summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt28
-rw-r--r--README.md2
-rw-r--r--examples/example_play.c154
-rw-r--r--include/kitchensink/internal/kitbuffer.h20
-rw-r--r--include/kitchensink/internal/kitringbuffer.h22
-rw-r--r--include/kitchensink/kitchensink.h19
-rw-r--r--include/kitchensink/kitconfig.h26
-rw-r--r--include/kitchensink/kiterror.h8
-rw-r--r--include/kitchensink/kitplayer.h56
-rw-r--r--include/kitchensink/kitsource.h23
-rw-r--r--src/kitbuffer.c46
-rw-r--r--src/kitchensink.c6
-rw-r--r--src/kiterror.c4
-rw-r--r--src/kitplayer.c441
-rw-r--r--src/kitringbuffer.c158
-rw-r--r--src/kitsource.c125
-rw-r--r--tests/test_source.c5
17 files changed, 962 insertions, 181 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e784946..072761c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8)
+cmake_minimum_required(VERSION 3.0)
project(SDL_kitchensink C)
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
@@ -11,11 +11,11 @@ add_definitions(
-DKIT_VERSION_PATCH=${VERSION_PATCH}
)
-set(CMAKE_C_FLAGS "-Wall -std=c99")
-set(CMAKE_C_FLAGS_DEBUG "-ggdb -Werror -fno-omit-frame-pointer")
-set(CMAKE_C_FLAGS_RELWITHDEBINFO "-g -O2 -fno-omit-frame-pointer -DNDEBUG")
-set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG")
-set(CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -std=c99")
+set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -Werror -fno-omit-frame-pointer")
+set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -ggdb -O2 -fno-omit-frame-pointer -DNDEBUG")
+set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2 -DNDEBUG")
+set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} -Os -DNDEBUG")
option(BUILD_EXAMPLES "Build examples" OFF)
option(BUILD_TESTS "Build unittests" OFF)
@@ -33,18 +33,16 @@ include_directories(
${FFMPEG_INCLUDE_DIRS}
)
-set(SOURCES
- src/kitchensink.c
- src/kiterror.c
- src/kitplayer.c
- src/kitsource.c
-)
+FILE(GLOB SOURCES "src/*.c")
+FILE(GLOB HEADERS "include/kitchensink/*.h")
add_library(SDL_kitchensink SHARED ${SOURCES})
add_library(SDL_kitchensink_static STATIC ${SOURCES})
set_target_properties(SDL_kitchensink PROPERTIES DEBUG_POSTFIX "d")
set_target_properties(SDL_kitchensink_static PROPERTIES DEBUG_POSTFIX "d")
+target_compile_definitions(SDL_kitchensink PRIVATE "KIT_DLL;KIT_DLL_EXPORTS")
+target_compile_options(SDL_kitchensink PRIVATE "-fvisibility=hidden")
target_link_libraries(SDL_kitchensink
${SDL2_LIBRARIES}
@@ -53,9 +51,11 @@ target_link_libraries(SDL_kitchensink
if(BUILD_EXAMPLES)
add_executable(exampleplay examples/example_play.c)
+
if(MINGW)
target_link_libraries(exampleplay mingw32)
endif()
+
target_link_libraries(exampleplay
SDL_kitchensink_static
${SDL2_LIBRARIES}
@@ -64,8 +64,8 @@ if(BUILD_EXAMPLES)
endif()
# Installation
-FILE(GLOB H_FILES "include/kitchensink/*.h")
-INSTALL(FILES ${H_FILES} DESTINATION include/kitchensink/)
+
+INSTALL(FILES ${HEADERS} DESTINATION include/kitchensink/)
INSTALL(TARGETS SDL_kitchensink SDL_kitchensink_static
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
diff --git a/README.md b/README.md
index e7708d4..963aa6f 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ Build requirements:
* GCC (C99 support required)
Library requirements:
-* SDL2 (>=2.0.3)
+* SDL2 (>=2.0.3) (Note! Examples require 2.0.4!)
* FFMPEG (>=2.8)
* CUnit (optional, for unittests)
diff --git a/examples/example_play.c b/examples/example_play.c
index d5856c6..79294f3 100644
--- a/examples/example_play.c
+++ b/examples/example_play.c
@@ -4,10 +4,14 @@
#include <stdbool.h>
/*
+* Requires SDL2 2.0.4 !
+*
* Note! This example does not do proper error handling etc.
* It is for example use only!
*/
+#define AUDIOBUFFER_SIZE 8192
+
const char *stream_types[] = {
"KIT_STREAMTYPE_UNKNOWN",
"KIT_STREAMTYPE_VIDEO",
@@ -17,23 +21,80 @@ const char *stream_types[] = {
"KIT_STREAMTYPE_ATTACHMENT"
};
+const char* get_tex_type(unsigned int type) {
+ switch(type) {
+ case SDL_PIXELFORMAT_UNKNOWN: return "SDL_PIXELFORMAT_UNKNOWN";
+ case SDL_PIXELFORMAT_INDEX1LSB: return "SDL_PIXELFORMAT_INDEX1LSB";
+ case SDL_PIXELFORMAT_INDEX1MSB: return "SDL_PIXELFORMAT_INDEX1MSB";
+ case SDL_PIXELFORMAT_INDEX4LSB: return "SDL_PIXELFORMAT_INDEX4LSB";
+ case SDL_PIXELFORMAT_INDEX4MSB: return "SDL_PIXELFORMAT_INDEX4MSB";
+ case SDL_PIXELFORMAT_INDEX8: return "SDL_PIXELFORMAT_INDEX8";
+ case SDL_PIXELFORMAT_RGB332: return "SDL_PIXELFORMAT_RGB332";
+ case SDL_PIXELFORMAT_RGB444: return "SDL_PIXELFORMAT_RGB444";
+ case SDL_PIXELFORMAT_RGB555: return "SDL_PIXELFORMAT_RGB555";
+ case SDL_PIXELFORMAT_BGR555: return "SDL_PIXELFORMAT_BGR555";
+ case SDL_PIXELFORMAT_ARGB4444: return "SDL_PIXELFORMAT_ARGB4444";
+ case SDL_PIXELFORMAT_RGBA4444: return "SDL_PIXELFORMAT_RGBA4444";
+ case SDL_PIXELFORMAT_ABGR4444: return "SDL_PIXELFORMAT_ABGR4444";
+ case SDL_PIXELFORMAT_BGRA4444: return "SDL_PIXELFORMAT_BGRA4444";
+ case SDL_PIXELFORMAT_ARGB1555: return "SDL_PIXELFORMAT_ARGB1555";
+ case SDL_PIXELFORMAT_RGBA5551: return "SDL_PIXELFORMAT_RGBA5551";
+ case SDL_PIXELFORMAT_ABGR1555: return "SDL_PIXELFORMAT_ABGR1555";
+ case SDL_PIXELFORMAT_BGRA5551: return "SDL_PIXELFORMAT_BGRA5551";
+ case SDL_PIXELFORMAT_RGB565: return "SDL_PIXELFORMAT_RGB565";
+ case SDL_PIXELFORMAT_BGR565: return "SDL_PIXELFORMAT_BGR565";
+ case SDL_PIXELFORMAT_RGB24: return "SDL_PIXELFORMAT_RGB24";
+ case SDL_PIXELFORMAT_BGR24: return "SDL_PIXELFORMAT_BGR24";
+ case SDL_PIXELFORMAT_RGB888: return "SDL_PIXELFORMAT_RGB888";
+ case SDL_PIXELFORMAT_RGBX8888: return "SDL_PIXELFORMAT_RGBX8888";
+ case SDL_PIXELFORMAT_BGR888: return "SDL_PIXELFORMAT_BGR888";
+ case SDL_PIXELFORMAT_BGRX8888: return "SDL_PIXELFORMAT_BGRX8888";
+ case SDL_PIXELFORMAT_ARGB8888: return "SDL_PIXELFORMAT_ARGB8888";
+ case SDL_PIXELFORMAT_RGBA8888: return "SDL_PIXELFORMAT_RGBA8888";
+ case SDL_PIXELFORMAT_ABGR8888: return "SDL_PIXELFORMAT_ABGR8888";
+ case SDL_PIXELFORMAT_BGRA8888: return "SDL_PIXELFORMAT_BGRA8888";
+ case SDL_PIXELFORMAT_ARGB2101010: return "SDL_PIXELFORMAT_ARGB2101010";
+ case SDL_PIXELFORMAT_YV12: return "SDL_PIXELFORMAT_YV12";
+ case SDL_PIXELFORMAT_IYUV: return "SDL_PIXELFORMAT_IYUV";
+ case SDL_PIXELFORMAT_YUY2: return "SDL_PIXELFORMAT_YUY2";
+ case SDL_PIXELFORMAT_UYVY: return "SDL_PIXELFORMAT_UYVY";
+ case SDL_PIXELFORMAT_YVYU: return "SDL_PIXELFORMAT_YVYU";
+ default:
+ return "unknown";
+ }
+}
+
int main(int argc, char *argv[]) {
+ int err = 0, ret = 0;
+ const char* filename = NULL;
+
+ // Video
SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL;
- Kit_Source *src = NULL;
+
+ // Events
SDL_Event event;
- int err = 0;
- bool run = false;
- const char* filename = NULL;
+ bool run = true;
+
+ // Kitchensink
+ Kit_Source *src = NULL;
+ Kit_Player *player = NULL;
+ // Audio playback
+ SDL_AudioSpec wanted_spec, audio_spec;
+ SDL_AudioDeviceID audio_dev;
+ char audiobuf[AUDIOBUFFER_SIZE];
+
+ // Get filename to open
if(argc != 2) {
fprintf(stderr, "Usage: exampleplay <filename>\n");
return 0;
}
filename = argv[1];
+ // Init SDL
err = SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO);
- window = SDL_CreateWindow("Example Player", -1, -1, 1280, 800, SDL_WINDOW_SHOWN);
+ window = SDL_CreateWindow("Example Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 800, 0);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC);
Kit_Init(KIT_INIT_FORMATS|KIT_INIT_NETWORK);
@@ -50,20 +111,59 @@ int main(int argc, char *argv[]) {
}
// Print stream information
- Kit_StreamInfo info;
+ Kit_StreamInfo sinfo;
+ fprintf(stderr, "Source streams:\n");
for(int i = 0; i < Kit_GetSourceStreamCount(src); i++) {
- err = Kit_GetSourceStreamInfo(src, &info, i);
+ err = Kit_GetSourceStreamInfo(src, &sinfo, i);
if(err) {
fprintf(stderr, "Unable to fetch stream #%d information: %s.\n", i, Kit_GetError());
return 1;
}
- fprintf(stderr, "Stream #%d: %s\n", i, stream_types[info.type]);
+ fprintf(stderr, " * Stream #%d: %s\n", i, stream_types[sinfo.type]);
}
- // Initialize codecs.
- // We could choose streams before this if we wanted to; now we just use the defaults (best guesses)
- if(Kit_InitSourceCodecs(src)) {
- fprintf(stderr, "Error while initializing codecs: %s", Kit_GetError());
+ // Create the player
+ player = Kit_CreatePlayer(src);
+ if(player == NULL) {
+ fprintf(stderr, "Unable to create player: %s\n", Kit_GetError());
+ return 1;
+ }
+
+ // Print some information
+ Kit_PlayerInfo pinfo;
+ Kit_GetPlayerInfo(player, &pinfo);
+ fprintf(stderr, "Media information:\n");
+ fprintf(stderr, " * Audio: %s (%s), %dHz, %dch, %db, %s\n",
+ pinfo.acodec,
+ pinfo.acodec_name,
+ pinfo.audio.samplerate,
+ pinfo.audio.channels,
+ pinfo.audio.bytes,
+ pinfo.audio.is_signed ? "signed" : "unsigned");
+ fprintf(stderr, " * Video: %s (%s), %dx%d\n",
+ pinfo.vcodec,
+ pinfo.vcodec_name,
+ pinfo.video.width,
+ pinfo.video.height);
+
+ // Init audio
+ SDL_memset(&wanted_spec, 0, sizeof(wanted_spec));
+ wanted_spec.freq = pinfo.audio.samplerate;
+ wanted_spec.format = AUDIO_S16SYS;
+ wanted_spec.channels = pinfo.audio.channels;
+ audio_dev = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, &audio_spec, 0);
+ SDL_PauseAudioDevice(audio_dev, 0);
+
+ // Initialize texture
+ fprintf(stderr, "Texture type: %s\n", get_tex_type(pinfo.video.format));
+ SDL_Texture *tex = SDL_CreateTexture(
+ renderer,
+ pinfo.video.format,
+ SDL_TEXTUREACCESS_STREAMING,
+ pinfo.video.width,
+ pinfo.video.height);
+ if(tex == NULL) {
+ fprintf(stderr, "Error while attempting to create a texture\n");
return 1;
}
@@ -80,8 +180,38 @@ int main(int argc, char *argv[]) {
break;
}
}
+
+ // Run decoder
+ while((ret = Kit_UpdatePlayer(player)) == -1);
+ if(ret == 1) {
+ run = false;
+ }
+
+ // Refresh audio
+ ret = SDL_GetQueuedAudioSize(audio_dev);
+ if(ret < AUDIOBUFFER_SIZE) {
+ SDL_LockAudio();
+ ret = Kit_GetAudioData(player, (unsigned char*)audiobuf, AUDIOBUFFER_SIZE);
+ if(ret > 0) {
+ SDL_QueueAudio(audio_dev, audiobuf, ret);
+ printf("Queued: %d\n", ret);
+ SDL_UnlockAudio();
+ SDL_PauseAudioDevice(audio_dev, 0);
+ }
+ }
+
+ // Refresh video
+ Kit_RefreshTexture(player, tex);
+
+ SDL_RenderClear(renderer);
+ SDL_RenderCopy(renderer, tex, NULL, NULL);
+ SDL_RenderPresent(renderer);
}
+ SDL_DestroyTexture(tex);
+ SDL_CloseAudioDevice(audio_dev);
+
+ Kit_ClosePlayer(player);
Kit_CloseSource(src);
Kit_Quit();
diff --git a/include/kitchensink/internal/kitbuffer.h b/include/kitchensink/internal/kitbuffer.h
new file mode 100644
index 0000000..772edf1
--- /dev/null
+++ b/include/kitchensink/internal/kitbuffer.h
@@ -0,0 +1,20 @@
+#ifndef KITBUFFER_H
+#define KITBUFFER_H
+
+#include "kitchensink/kitconfig.h"
+
+typedef struct Kit_Buffer {
+ unsigned int read_p;
+ unsigned int write_p;
+ unsigned int size;
+ void **data;
+} Kit_Buffer;
+
+KIT_LOCAL Kit_Buffer* Kit_CreateBuffer(unsigned int size);
+KIT_LOCAL void Kit_DestroyBuffer(Kit_Buffer *buffer);
+
+KIT_LOCAL void* Kit_ReadBuffer(Kit_Buffer *buffer);
+KIT_LOCAL int Kit_WriteBuffer(Kit_Buffer *buffer, void *ptr);
+KIT_LOCAL int Kit_IsBufferFull(const Kit_Buffer *buffer);
+
+#endif // KITBUFFER_H
diff --git a/include/kitchensink/internal/kitringbuffer.h b/include/kitchensink/internal/kitringbuffer.h
new file mode 100644
index 0000000..773ba29
--- /dev/null
+++ b/include/kitchensink/internal/kitringbuffer.h
@@ -0,0 +1,22 @@
+#ifndef KITRINGBUFFER_H
+#define KITRINGBUFFER_H
+
+#include "kitchensink/kitconfig.h"
+
+typedef struct Kit_RingBuffer {
+ int size;
+ int len;
+ int wpos, rpos;
+ char* data;
+} Kit_RingBuffer;
+
+KIT_LOCAL Kit_RingBuffer* Kit_CreateRingBuffer(unsigned int size);
+KIT_LOCAL void Kit_DestroyRingBuffer(Kit_RingBuffer* rb);
+KIT_LOCAL int Kit_WriteRingBuffer(Kit_RingBuffer *rb, const char* data, int len);
+KIT_LOCAL int Kit_ReadRingBuffer(Kit_RingBuffer *rb, char* data, int len);
+KIT_LOCAL int Kit_PeekRingBuffer(const Kit_RingBuffer *rb, char* data, int len);
+KIT_LOCAL int Kit_AdvanceRingBuffer(Kit_RingBuffer *rb, int len);
+KIT_LOCAL int Kit_GetRingBufferLength(const Kit_RingBuffer *rb);
+KIT_LOCAL int Kit_GetRingBufferSize(const Kit_RingBuffer *rb);
+
+#endif // KITRINGBUFFER_H
diff --git a/include/kitchensink/kitchensink.h b/include/kitchensink/kitchensink.h
index 6ece77c..3120db7 100644
--- a/include/kitchensink/kitchensink.h
+++ b/include/kitchensink/kitchensink.h
@@ -1,24 +1,19 @@
#ifndef KITCHENSINK_H
#define KITCHENSINK_H
-#include <SDL2/SDL.h>
#include "kitchensink/kiterror.h"
#include "kitchensink/kitsource.h"
#include "kitchensink/kitplayer.h"
+#include "kitchensink/kitconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
-#define KIT_VERSION(x) \
- (x)->major = KIT_VERSION_MAJOR; \
- (x)->minor = KIT_VERSION_MINOR; \
- (x)->patch = KIT_VERSION_PATCH
-
typedef struct Kit_Version {
- Uint8 major;
- Uint8 minor;
- Uint8 patch;
+ unsigned char major;
+ unsigned char minor;
+ unsigned char patch;
} Kit_Version;
enum {
@@ -26,10 +21,10 @@ enum {
KIT_INIT_NETWORK = 0x2,
};
-int Kit_Init();
-void Kit_Quit();
+KIT_API int Kit_Init(unsigned int flags);
+KIT_API void Kit_Quit();
-void Kit_GetVersion(Kit_Version *version);
+KIT_API void Kit_GetVersion(Kit_Version *version);
#ifdef __cplusplus
}
diff --git a/include/kitchensink/kitconfig.h b/include/kitchensink/kitconfig.h
new file mode 100644
index 0000000..e8b2d01
--- /dev/null
+++ b/include/kitchensink/kitconfig.h
@@ -0,0 +1,26 @@
+#ifndef KITCONFIG_H
+#define KITCONFIG_H
+
+#if defined _WIN32 || defined __CYGWIN__
+ #define KIT_DLL_IMPORT __declspec(dllimport)
+ #define KIT_DLL_EXPORT __declspec(dllexport)
+ #define KIT_DLL_LOCAL
+#else
+ #define KIT_DLL_IMPORT __attribute__ ((visibility ("default")))
+ #define KIT_DLL_EXPORT __attribute__ ((visibility ("default")))
+ #define KIT_DLL_LOCAL __attribute__ ((visibility ("hidden")))
+#endif
+
+#ifdef KIT_DLL
+ #ifdef KIT_DLL_EXPORTS
+ #define KIT_API KIT_DLL_EXPORT
+ #else
+ #define KIT_API KIT_DLL_IMPORT
+ #endif
+ #define KIT_LOCAL KIT_DLL_LOCAL
+#else
+ #define KIT_API
+ #define KIT_LOCAL
+#endif
+
+#endif // KITCONFIG_H
diff --git a/include/kitchensink/kiterror.h b/include/kitchensink/kiterror.h
index 1b88f89..824ea8c 100644
--- a/include/kitchensink/kiterror.h
+++ b/include/kitchensink/kiterror.h
@@ -1,13 +1,15 @@
#ifndef KITERROR_H
#define KITERROR_H
+#include "kitchensink/kitconfig.h"
+
#ifdef __cplusplus
extern "C" {
#endif
-const char* Kit_GetError();
-void Kit_SetError(const char* fmt, ...);
-void Kit_ClearError();
+KIT_API const char* Kit_GetError();
+KIT_API void Kit_SetError(const char* fmt, ...);
+KIT_API void Kit_ClearError();
#ifdef __cplusplus
}
diff --git a/include/kitchensink/kitplayer.h b/include/kitchensink/kitplayer.h
index 3c88920..a4edb9b 100644
--- a/include/kitchensink/kitplayer.h
+++ b/include/kitchensink/kitplayer.h
@@ -2,19 +2,69 @@
#define KITPLAYER_H
#include "kitchensink/kitsource.h"
+#include "kitchensink/kitconfig.h"
+#include <stdbool.h>
+
+#include <SDL2/SDL_render.h>
#ifdef __cplusplus
extern "C" {
#endif
-typedef struct Kit_Player {
+#define KIT_CODECMAX 16
+#define KIT_CODECNAMEMAX 128
+#define KIT_VBUFFERSIZE 3
+#define KIT_ABUFFERSIZE 65536
+
+typedef enum Kit_PlayerState {
+ KIT_STOPPED = 0,
+ KIT_PLAYING,
+ KIT_FINISHED
+} Kit_PlayerState;
+typedef struct Kit_AudioFormat {
+ int bytes;
+ bool is_signed;
+ int samplerate;
+ int channels;
+} Kit_AudioFormat;
+
+typedef struct Kit_VideoFormat {
+ unsigned int format;
+ int width;
+ int height;
+} Kit_VideoFormat;
+
+typedef struct Kit_Player {
+ Kit_PlayerState state;
+ Kit_VideoFormat vformat;
+ Kit_AudioFormat aformat;
+ void *abuffer;
+ void *vbuffer;
+ void *vcodec_ctx;
+ void *acodec_ctx;
+ void *swr;
+ void *sws;
+ const Kit_Source *src;
} Kit_Player;
-Kit_Player* Kit_CreatePlayer(Kit_Source *src);
-void Kit_ClosePlayer(Kit_Player *player);
+typedef struct Kit_PlayerInfo {
+ char acodec[KIT_CODECMAX];
+ char acodec_name[KIT_CODECNAMEMAX];
+ char vcodec[KIT_CODECMAX];
+ char vcodec_name[KIT_CODECNAMEMAX];
+ Kit_VideoFormat video;
+ Kit_AudioFormat audio;
+} Kit_PlayerInfo;
+KIT_API Kit_Player* Kit_CreatePlayer(const Kit_Source *src);
+KIT_API void Kit_ClosePlayer(Kit_Player *player);
+KIT_API int Kit_UpdatePlayer(Kit_Player *player);
+KIT_API int Kit_RefreshTexture(Kit_Player *player, SDL_Texture *texture);
+KIT_API int Kit_GetAudioData(Kit_Player *player, unsigned char *buffer, size_t length);
+KIT_API int Kit_GetPlayerState(Kit_Player *player);
+KIT_API void Kit_GetPlayerInfo(const Kit_Player *player, Kit_PlayerInfo *info);
#ifdef __cplusplus
}
diff --git a/include/kitchensink/kitsource.h b/include/kitchensink/kitsource.h
index 2b72e88..43b9d2b 100644
--- a/include/kitchensink/kitsource.h
+++ b/include/kitchensink/kitsource.h
@@ -1,6 +1,8 @@
#ifndef KITSOURCE_H
#define KITSOURCE_H
+#include "kitchensink/kitconfig.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -21,28 +23,21 @@ typedef struct Kit_Source {
int astream_idx;
int vstream_idx;
void *format_ctx;
- void *vcodec_ctx;
- void *acodec_ctx;
- void *vcodec;
- void *acodec;
} Kit_Source;
typedef struct Kit_Stream {
int index;
Kit_streamtype type;
- int width;
- int height;
} Kit_StreamInfo;
-Kit_Source* Kit_CreateSourceFromUrl(const char *path);
-int Kit_InitSourceCodecs(Kit_Source *src);
-void Kit_CloseSource(Kit_Source *src);
+KIT_API Kit_Source* Kit_CreateSourceFromUrl(const char *path);
+KIT_API void Kit_CloseSource(Kit_Source *src);
-int Kit_GetSourceStreamInfo(const Kit_Source *src, Kit_StreamInfo *info, int index);
-int Kit_GetSourceStreamCount(const Kit_Source *src);
-int Kit_GetBestSourceStream(const Kit_Source *src, const Kit_streamtype type);
-int Kit_SetSourceStream(Kit_Source *src, const Kit_streamtype type, int index);
-int Kit_GetSourceStream(const Kit_Source *src, const Kit_streamtype type);
+KIT_API int Kit_GetSourceStreamInfo(const Kit_Source *src, Kit_StreamInfo *info, int index);
+KIT_API int Kit_GetSourceStreamCount(const Kit_Source *src);
+KIT_API int Kit_GetBestSourceStream(const Kit_Source *src, const Kit_streamtype type);
+KIT_API int Kit_SetSourceStream(Kit_Source *src, const Kit_streamtype type, int index);
+KIT_API int Kit_GetSourceStream(const Kit_Source *src, const Kit_streamtype type);
#ifdef __cplusplus
}
diff --git a/src/kitbuffer.c b/src/kitbuffer.c
new file mode 100644
index 0000000..769f9fd
--- /dev/null
+++ b/src/kitbuffer.c
@@ -0,0 +1,46 @@
+#include "kitchensink/internal/kitbuffer.h"
+
+#include <stdlib.h>
+#include <assert.h>
+
+Kit_Buffer* Kit_CreateBuffer(unsigned int size) {
+ Kit_Buffer *b = calloc(1, sizeof(Kit_Buffer));
+ b->size = size;
+ b->data = calloc(size, sizeof(void*));
+ return b;
+}
+
+void Kit_DestroyBuffer(Kit_Buffer *buffer) {
+ assert(buffer != NULL);
+}
+
+void* Kit_ReadBuffer(Kit_Buffer *buffer) {
+ assert(buffer != NULL);
+ if(buffer->read_p < buffer->write_p) {
+ void *out = buffer->data[buffer->read_p];
+ buffer->data[buffer->read_p] = NULL;
+ buffer->read_p++;
+ if(buffer->read_p >= buffer->size) {
+ buffer->read_p -= buffer->size;
+ buffer->write_p -= buffer->size;
+ }
+ return out;
+ }
+ return NULL;
+}
+
+int Kit_WriteBuffer(Kit_Buffer *buffer, void *ptr) {
+ assert(buffer != NULL);
+ assert(ptr != NULL);
+
+ if(!Kit_IsBufferFull(buffer)) {
+ buffer->data[buffer->write_p] = ptr;
+ buffer->write_p++;
+ return 0;
+ }
+ return 1;
+}
+
+int Kit_IsBufferFull(const Kit_Buffer *buffer) {
+ return (buffer->write_p - buffer->read_p >= buffer->size);
+} \ No newline at end of file
diff --git a/src/kitchensink.c b/src/kitchensink.c
index c2b526f..a494d62 100644
--- a/src/kitchensink.c
+++ b/src/kitchensink.c
@@ -1,9 +1,10 @@
#include "kitchensink/kitchensink.h"
#include <libavformat/avformat.h>
+#include <assert.h>
-static Uint32 _init_flags = 0;
+static unsigned int _init_flags = 0;
-int Kit_Init(Uint32 flags) {
+int Kit_Init(unsigned int flags) {
if(flags & KIT_INIT_NETWORK)
avformat_network_init();
if(flags & KIT_INIT_FORMATS)
@@ -20,6 +21,7 @@ void Kit_Quit() {
}
void Kit_GetVersion(Kit_Version *version) {
+ assert(version != NULL);
version->major = KIT_VERSION_MAJOR;
version->minor = KIT_VERSION_MINOR;
version->patch = KIT_VERSION_PATCH;
diff --git a/src/kiterror.c b/src/kiterror.c
index 948fda9..eed779b 100644
--- a/src/kiterror.c
+++ b/src/kiterror.c
@@ -1,7 +1,10 @@
#include "kitchensink/kitchensink.h"
+#include <stdlib.h>
#include <stdarg.h>
+#include <stdio.h>
#include <stdbool.h>
+#include <assert.h>
#define KIT_ERRBUFSIZE 1024
@@ -17,6 +20,7 @@ const char* Kit_GetError() {
}
void Kit_SetError(const char* fmt, ...) {
+ assert(fmt != NULL);
va_list args;
va_start(args, fmt);
vsnprintf(_error_message, KIT_ERRBUFSIZE, (char*)fmt, args);
diff --git a/src/kitplayer.c b/src/kitplayer.c
index 704317f..7822f0c 100644
--- a/src/kitplayer.c
+++ b/src/kitplayer.c
@@ -1,2 +1,441 @@
-#include <kitchensink/kitplayer.h>
+#include "kitchensink/kitplayer.h"
+#include "kitchensink/kiterror.h"
+#include "kitchensink/internal/kitbuffer.h"
+#include "kitchensink/internal/kitringbuffer.h"
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libswscale/swscale.h>
+#include <libswresample/swresample.h>
+#include <libavutil/pixfmt.h>
+
+#include <SDL2/SDL_types.h>
+#include <SDL2/SDL_pixels.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+typedef struct Kit_VideoPacket {
+ double pts;
+ AVFrame *frame;
+} Kit_VideoPacket;
+
+static int _InitCodecs(Kit_Player *player, const Kit_Source *src) {
+ assert(player != NULL);
+ assert(src != NULL);
+
+ AVCodecContext *acodec_ctx = NULL;
+ AVCodecContext *vcodec_ctx = NULL;
+ AVCodec *acodec = NULL;
+ AVCodec *vcodec = NULL;
+
+ // Make sure indexes seem correct
+ AVFormatContext *format_ctx = (AVFormatContext *)src->format_ctx;
+ if(src->astream_idx < 0 || src->astream_idx >= format_ctx->nb_streams) {
+ Kit_SetError("Invalid audio stream index");
+ return 1;
+ }
+ if(src->vstream_idx < 0 || src->vstream_idx >= format_ctx->nb_streams) {
+ Kit_SetError("Invalid video stream index");
+ return 1;
+ }
+
+ // Find video decoder
+ vcodec = avcodec_find_decoder(format_ctx->streams[src->vstream_idx]->codec->codec_id);
+ if(!vcodec) {
+ Kit_SetError("No suitable video decoder found");
+ goto exit_0;
+ }
+
+ // Copy the original video codec context
+ vcodec_ctx = avcodec_alloc_context3(vcodec);
+ if(avcodec_copy_context(vcodec_ctx, format_ctx->streams[src->vstream_idx]->codec) != 0) {
+ Kit_SetError("Unable to copy video codec context");
+ goto exit_0;
+ }
+
+ // Create a video decoder context
+ if(avcodec_open2(vcodec_ctx, vcodec, NULL) < 0) {
+ Kit_SetError("Unable to allocate video codec context");
+ goto exit_1;
+ }
+
+ // Find audio decoder
+ acodec = avcodec_find_decoder(format_ctx->streams[src->astream_idx]->codec->codec_id);
+ if(!acodec) {
+ Kit_SetError("No suitable audio decoder found");
+ goto exit_2;
+ }
+
+ // Copy the original audio codec context
+ acodec_ctx = avcodec_alloc_context3(acodec);
+ if(avcodec_copy_context(acodec_ctx, format_ctx->streams[src->astream_idx]->codec) != 0) {
+ Kit_SetError("Unable to copy audio codec context");
+ goto exit_2;
+ }
+
+ // Create an audio decoder context
+ if(avcodec_open2(acodec_ctx, acodec, NULL) < 0) {
+ Kit_SetError("Unable to allocate audio codec context");
+ goto exit_3;
+ }
+
+ player->acodec_ctx = acodec_ctx;
+ player->vcodec_ctx = vcodec_ctx;
+ player->src = src;
+ return 0;
+
+exit_3:
+ avcodec_free_context(&acodec_ctx);
+exit_2:
+ avcodec_close(vcodec_ctx);
+exit_1:
+ avcodec_free_context(&vcodec_ctx);
+exit_0:
+ return 1;
+}
+
+static void _FindPixelFormat(enum AVPixelFormat fmt, unsigned int *out_fmt) {
+ switch(fmt) {
+ case AV_PIX_FMT_YUV420P:
+ *out_fmt = SDL_PIXELFORMAT_YV12;
+ break;
+ case AV_PIX_FMT_YUYV422:
+ *out_fmt = SDL_PIXELFORMAT_YUY2;
+ break;
+ case AV_PIX_FMT_UYVY422:
+ *out_fmt = SDL_PIXELFORMAT_UYVY;
+ break;
+ default:
+ *out_fmt = SDL_PIXELFORMAT_ABGR8888;
+ break;
+ }
+}
+
+static void _FindAudioFormat(enum AVSampleFormat fmt, int *bytes, bool *is_signed) {
+ switch(fmt) {
+ case AV_SAMPLE_FMT_U8:
+ *bytes = 1;
+ *is_signed = false;
+ break;
+ case AV_SAMPLE_FMT_S16:
+ *bytes = 2;
+ *is_signed = true;
+ break;
+ case AV_SAMPLE_FMT_S32:
+ *bytes = 4;
+ *is_signed = true;
+ break;
+ default:
+ *bytes = 2;
+ *is_signed = true;
+ break;
+ }
+}
+
+static enum AVPixelFormat _FindAVPixelFormat(unsigned int fmt) {
+ switch(fmt) {
+ case SDL_PIXELFORMAT_IYUV: return AV_PIX_FMT_YUV420P;
+ case SDL_PIXELFORMAT_YV12: return AV_PIX_FMT_YUV420P;
+ case SDL_PIXELFORMAT_YUY2: return AV_PIX_FMT_YUYV422;
+ case SDL_PIXELFORMAT_UYVY: return AV_PIX_FMT_UYVY422;
+ case SDL_PIXELFORMAT_ARGB8888: return AV_PIX_FMT_BGRA;
+ case SDL_PIXELFORMAT_ABGR8888: return AV_PIX_FMT_RGBA;
+ default:
+ return AV_PIX_FMT_NONE;
+ }
+}
+
+static enum AVSampleFormat _FindAVSampleFormat(int bytes) {
+ switch(bytes) {
+ case 1: return AV_SAMPLE_FMT_U8;
+ case 2: return AV_SAMPLE_FMT_S16;
+ case 3: return AV_SAMPLE_FMT_S32;
+ default:
+ return AV_SAMPLE_FMT_NONE;
+ }
+}
+
+static unsigned int _FindAVChannelLayout(int channels) {
+ switch(channels) {
+ case 1: return AV_CH_LAYOUT_MONO;
+ default: return AV_CH_LAYOUT_STEREO;
+ }
+}
+
+Kit_Player* Kit_CreatePlayer(const Kit_Source *src) {
+ assert(src != NULL);
+
+ Kit_Player *player = calloc(1, sizeof(Kit_Player));
+
+ // Initialize codecs
+ if(_InitCodecs(player, src) != 0) {
+ goto exit_0;
+ }
+
+ AVCodecContext *acodec_ctx = (AVCodecContext*)player->acodec_ctx;
+ AVCodecContext *vcodec_ctx = (AVCodecContext*)player->vcodec_ctx;
+ player->vformat.width = vcodec_ctx->width;
+ player->vformat.height = vcodec_ctx->height;
+ _FindPixelFormat(vcodec_ctx->pix_fmt, &player->vformat.format);
+ player->aformat.samplerate = acodec_ctx->sample_rate;
+ player->aformat.channels = acodec_ctx->channels;
+ _FindAudioFormat(acodec_ctx->sample_fmt, &player->aformat.bytes, &player->aformat.is_signed);
+
+ // Audio converter
+ struct SwrContext *swr = swr_alloc_set_opts(
+ NULL,
+ _FindAVChannelLayout(player->aformat.channels), // Target channel layout
+ _FindAVSampleFormat(player->aformat.bytes), // Target fmt
+ player->aformat.samplerate, // Target samplerate
+ acodec_ctx->channel_layout, // Source channel layout
+ acodec_ctx->sample_fmt, // Source fmt
+ acodec_ctx->sample_rate, // Source samplerate
+ 0, NULL);
+ swr_init(swr);
+
+ // Video converter
+ struct SwsContext *sws = sws_getContext(
+ vcodec_ctx->width, // Source w
+ vcodec_ctx->height, // Source h
+ vcodec_ctx->pix_fmt, // Source fmt
+ vcodec_ctx->width, // Target w
+ vcodec_ctx->height, // Target h
+ _FindAVPixelFormat(player->vformat.format), // Target fmt
+ SWS_BICUBIC,
+ NULL, NULL, NULL);
+
+ player->swr = swr;
+ player->sws = sws;
+ player->abuffer = Kit_CreateRingBuffer(KIT_ABUFFERSIZE);
+ player->vbuffer = Kit_CreateBuffer(KIT_VBUFFERSIZE);
+ return player;
+
+exit_0:
+ free(player);
+ return NULL;
+}
+
+void Kit_ClosePlayer(Kit_Player *player) {
+ if(player == NULL) return;
+ sws_freeContext((struct SwsContext *)player->sws);
+ swr_free((struct SwrContext **)&player->swr);
+ avcodec_close((AVCodecContext*)player->acodec_ctx);
+ avcodec_close((AVCodecContext*)player->vcodec_ctx);
+ avcodec_free_context((AVCodecContext**)&player->acodec_ctx);
+ avcodec_free_context((AVCodecContext**)&player->vcodec_ctx);
+ Kit_DestroyRingBuffer((Kit_RingBuffer*)player->abuffer);
+ Kit_DestroyBuffer((Kit_Buffer*)player->vbuffer);
+ free(player);
+}
+
+void _HandleVideoPacket(Kit_Player *player, AVPacket *packet) {
+ assert(player != NULL);
+ assert(packet != NULL);
+
+ int frame_finished;
+ AVCodecContext *vcodec_ctx = (AVCodecContext*)player->vcodec_ctx;
+ AVFrame *vframe_raw = av_frame_alloc();
+ AVFrame *vframe_dec = av_frame_alloc();
+ AVPicture *iframe = (AVPicture*)vframe_raw;
+ AVPicture *oframe = (AVPicture*)vframe_dec;
+ int bytes = avpicture_get_size(_FindAVPixelFormat(player->vformat.format), vcodec_ctx->width, vcodec_ctx->height);
+ avpicture_fill(oframe, av_malloc(bytes), _FindAVPixelFormat(player->vformat.format), vcodec_ctx->width, vcodec_ctx->height);
+
+ avcodec_decode_video2(vcodec_ctx, vframe_raw, &frame_finished, packet);
+
+ if(frame_finished) {
+ sws_scale(
+ (struct SwsContext *)player->sws,
+ (const unsigned char * const *)iframe->data,
+ iframe->linesize,
+ 0,
+ vcodec_ctx->height,
+ oframe->data,
+ oframe->linesize);
+
+ Kit_VideoPacket *p = calloc(1, sizeof(Kit_VideoPacket));
+ p->frame = vframe_dec;
+ p->pts = 0;
+ Kit_WriteBuffer((Kit_Buffer*)player->vbuffer, p);
+ }
+
+ av_frame_free(&vframe_raw);
+}
+
+void _HandleAudioPacket(Kit_Player *player, AVPacket *packet) {
+ assert(player != NULL);
+ assert(packet != NULL);
+
+ int frame_finished;
+ int len, len2;
+ int dst_linesize;
+ int dst_nb_samples, dst_bufsize;
+ unsigned char **dst_data;
+ AVCodecContext *acodec_ctx = (AVCodecContext*)player->acodec_ctx;
+ struct SwrContext *swr = (struct SwrContext *)player->swr;
+ AVFrame *aframe = av_frame_alloc();
+
+ if(packet->size > 0) {
+ len = avcodec_decode_audio4(acodec_ctx, aframe, &frame_finished, packet);
+ if(len < 0) {
+ goto exit_1;
+ }
+
+ if(frame_finished) {
+ dst_nb_samples = av_rescale_rnd(
+ aframe->nb_samples,
+ player->aformat.samplerate,
+ acodec_ctx->sample_rate,
+ AV_ROUND_UP);
+
+ av_samples_alloc_array_and_samples(
+ &dst_data,
+ &dst_linesize,
+ player->aformat.channels,
+ dst_nb_samples,
+ _FindAVSampleFormat(player->aformat.bytes),
+ 0);
+
+ len2 = swr_convert(
+ swr,
+ dst_data,
+ aframe->nb_samples,
+ (const unsigned char **)aframe->extended_data,
+ aframe->nb_samples);
+
+ dst_bufsize = av_samples_get_buffer_size(
+ &dst_linesize,
+ player->aformat.channels,
+ len2,
+ _FindAVSampleFormat(player->aformat.bytes), 1);
+
+ Kit_WriteRingBuffer((Kit_RingBuffer*)player->abuffer, (char*)dst_data[0], dst_bufsize);
+ av_freep(&dst_data[0]);
+ }
+ }
+
+exit_1:
+ av_frame_free(&aframe);
+}
+
+// Return 0 if stream is good but nothing else to do for now
+// Return -1 if there is still work to be done
+// Return 1 if there was an error or stream end
+int Kit_UpdatePlayer(Kit_Player *player) {
+ assert(player != NULL);
+
+ // If either buffer is full, just stop here for now.
+ if(Kit_IsBufferFull(player->vbuffer)) {
+ return 0;
+ }
+
+ AVPacket packet;
+ AVFormatContext *format_ctx = (AVFormatContext*)player->src->format_ctx;
+
+ // Attempt to read frame. Just return here if it fails.
+ if(av_read_frame(format_ctx, &packet) < 0) {
+ return 1;
+ }
+
+ // Check if this is a packet we need to handle and pass it on
+ if(packet.stream_index == player->src->vstream_idx) {
+ _HandleVideoPacket(player, &packet);
+ }
+ if(packet.stream_index == player->src->astream_idx) {
+ _HandleAudioPacket(player, &packet);
+ }
+
+ // Free packet and that's that
+ av_free_packet(&packet);
+ return -1;
+}
+
+int Kit_RefreshTexture(Kit_Player *player, SDL_Texture *texture) {
+ assert(player != NULL);
+ assert(texture != NULL);
+
+ int retval = 1;
+
+ // Get texture information
+ unsigned int format;
+ int access, w, h;
+ SDL_QueryTexture(texture, &format, &access, &w, &h);
+
+ // Make sure the target texture looks correct
+ if(w != player->vformat.width || h != player->vformat.height) {
+ Kit_SetError("Incorrect target texture size: Is %dx%d, should be %dx%d",
+ w, h, player->vformat.width, player->vformat.height);
+ return 1;
+ }
+
+ // Make sure the texture format seems correct
+ if(format != player->vformat.format) {
+ Kit_SetError("Incorrect texture format");
+ return 1;
+ }
+
+ Kit_VideoPacket *packet = (Kit_VideoPacket*)Kit_ReadBuffer((Kit_Buffer*)player->vbuffer);
+ if(!packet) {
+ return 0;
+ }
+
+ if(format == SDL_PIXELFORMAT_YV12 || format == SDL_PIXELFORMAT_IYUV) {
+ SDL_UpdateYUVTexture(
+ texture, NULL,
+ packet->frame->data[0], packet->frame->linesize[0],
+ packet->frame->data[1], packet->frame->linesize[1],
+ packet->frame->data[2], packet->frame->linesize[2]);
+ }
+ else if(access == SDL_TEXTUREACCESS_STREAMING) {
+ unsigned char *pixels;
+ int pitch;
+
+ if(SDL_LockTexture(texture, NULL, (void**)&pixels, &pitch) == 0) {
+ memcpy(pixels, packet->frame->data[0], pitch * h);
+ SDL_UnlockTexture(texture);
+ } else {
+ Kit_SetError("Unable to lock texture for streaming access");
+ goto exit_0;
+ }
+ }
+ else {
+ Kit_SetError("Incorrect texture access: only streaming access is supported");
+ goto exit_0;
+ }
+
+ retval = 0;
+exit_0:
+ av_frame_free(&packet->frame);
+ free(packet);
+ return retval;
+}
+
+int Kit_GetAudioData(Kit_Player *player, unsigned char *buffer, size_t length) {
+ assert(player != NULL);
+ assert(buffer != NULL);
+
+ if(length == 0) {
+ return 0;
+ }
+
+ int ret = Kit_ReadRingBuffer((Kit_RingBuffer*)player->abuffer, (char*)buffer, length);
+ return ret;
+}
+
+void Kit_GetPlayerInfo(const Kit_Player *player, Kit_PlayerInfo *info) {
+ assert(player != NULL);
+ assert(info != NULL);
+
+ AVCodecContext *acodec_ctx = (AVCodecContext*)player->acodec_ctx;
+ AVCodecContext *vcodec_ctx = (AVCodecContext*)player->vcodec_ctx;
+
+ strncpy(info->acodec, acodec_ctx->codec->name, KIT_CODECMAX);
+ strncpy(info->acodec_name, acodec_ctx->codec->long_name, KIT_CODECNAMEMAX);
+ strncpy(info->vcodec, vcodec_ctx->codec->name, KIT_CODECMAX);
+ strncpy(info->vcodec_name, vcodec_ctx->codec->long_name, KIT_CODECNAMEMAX);
+
+ memcpy(&info->video, &player->vformat, sizeof(Kit_VideoFormat));
+ memcpy(&info->audio, &player->aformat, sizeof(Kit_AudioFormat));
+}
diff --git a/src/kitringbuffer.c b/src/kitringbuffer.c
new file mode 100644
index 0000000..7f97441
--- /dev/null
+++ b/src/kitringbuffer.c
@@ -0,0 +1,158 @@
+/*
+ * Ringbuffer
+ *
+ * Copyright (c) 2016, Tuomas Virtanen
+ * license: MIT; see LICENSE for details.
+*/
+
+#include "kitchensink/internal/kitringbuffer.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+
+/**
+ * Creates a new ringbuffer with the given size.
+ * @param size Size for the new ringbuffer
+ * @return Ringbuffer handle
+ */
+Kit_RingBuffer* Kit_CreateRingBuffer(unsigned int size) {
+ Kit_RingBuffer *rb = calloc(1, sizeof(Kit_RingBuffer));
+ rb->size = size;
+ rb->data = malloc(size);
+ return rb;
+}
+
+/**
+ * Deletes the given ringbuffer.
+ * @param rb Ringbuffer to be deleted.
+ */
+void Kit_DestroyRingBuffer(Kit_RingBuffer* rb) {
+ assert(rb != NULL);
+ free(rb->data);
+ free(rb);
+}
+
+/**
+ * Writes to the given ringbuffer. If given length is larger than the amount
+ * the ringbuffer can fit, only the data that fits will be written.
+ * @param rb Ringbuffer to write to.
+ * @param data Data to write
+ * @param len Data length
+ * @return Amount of data that was actually written.
+ */
+int Kit_WriteRingBuffer(Kit_RingBuffer *rb, const char* data, int len) {
+ int k;
+ len = (len > (rb->size - rb->len)) ? (rb->size - rb->len) : len;
+ if(rb->len < rb->size) {
+ if(len + rb->wpos > rb->size) {
+ k = (len + rb->wpos) % rb->size;
+ memcpy((rb->data + rb->wpos), data, len - k);
+ memcpy(rb->data, data+(len-k), k);
+ } else {
+ memcpy((rb->data + rb->wpos), data, len);
+ }
+ rb->len += len;
+ rb->wpos += len;
+ if(rb->wpos >= rb->size) {
+ rb->wpos = rb->wpos % rb->size;
+ }
+ return len;
+ }
+ return 0;
+}
+
+/**
+ * Reads data from ringbuffer. If ringbuffer has less data than was requested,
+ * only the available data will be read.
+ * @param rb Ringbuffer to read from.
+ * @param data Buffer to read into.
+ * @param len How much data do we want
+ * @return Amount of data that was actually read.
+ */
+int Kit_ReadRingBuffer(Kit_RingBuffer *rb, char* data, int len) {
+ int k;
+ len = (len > rb->len) ? rb->len : len;
+ if(rb->len > 0) {
+ if(len + rb->rpos > rb->size) {
+ k = (len + rb->rpos) % rb->size;
+ memcpy(data, (rb->data + rb->rpos), len-k);
+ memcpy(data+(len-k), (rb->data), k);
+ } else {
+ memcpy(data, (rb->data + rb->rpos), len);
+ }
+ rb->len -= len;
+ rb->rpos += len;
+ if(rb->rpos >= rb->size) {
+ rb->rpos = rb->rpos % rb->size;
+ }
+ return len;
+ }
+ return 0;
+}
+
+/**
+ * Peeks into the given ringbuffer. Technically same as rb_read, but does not advance
+ * the internal position pointer. In other words, you may peek as many times as you wish,
+ * and will always get the same data.
+ * @param rb Ringbuffer to peek into.
+ * @param data Buffer to read into
+ * @param len How much data do we need
+ * @return Amount of data actually read
+ */
+int Kit_PeekRingBuffer(const Kit_RingBuffer *rb, char* data, int len) {
+ int k;
+ len = (len > rb->len) ? rb->len : len;
+ if(rb->len > 0) {
+ if(len + rb->rpos > rb->size) {
+ k = (len + rb->rpos) % rb->size;
+ memcpy(data, (rb->data + rb->rpos), len-k);
+ memcpy(data+(len-k), (rb->data), k);
+ } else {
+ memcpy(data, (rb->data + rb->rpos), len);
+ }
+ return len;
+ }
+ return 0;
+}
+
+/**
+ * Advances the internal position counter by given amount. Note that counter can only be
+ * advanced by the amount of unreadable data in ringbuffer.
+ * @param rb Ringbuffer to handle
+ * @param len How much should the position counter be increased
+ * @return How much the position counter was actually increased.
+ */
+int Kit_AdvanceRingBuffer(Kit_RingBuffer *rb, int len) {
+ len = (len > rb->len) ? rb->len : len;
+ if(rb->len > 0) {
+ rb->len -= len;
+ rb->rpos += len;
+ if(rb->rpos >= rb->size) {
+ rb->rpos = rb->rpos % rb->size;
+ }
+ return len;
+ }
+ return 0;
+}
+
+/**
+ * Returns the current length of the Ringbuffer. In other words, how much data
+ * the ringbuffer contains
+ * @param rb Ringbuffer to handle
+ * @return Data in ringbuffer (in bytes).
+ */
+int Kit_GetRingBufferLength(const Kit_RingBuffer *rb) {
+ return rb->len;
+}
+
+/**
+ * Returns the size of the ringbuffer. In other words, the maximum amount of data
+ * the ringbuffer can hold.
+ * @param rb Ringbuffer to handle
+ * @return Size of the ringbuffer
+ */
+int Kit_GetRingBufferSize(const Kit_RingBuffer *rb) {
+ return rb->size;
+}
diff --git a/src/kitsource.c b/src/kitsource.c
index c947ea1..d8cfe53 100644
--- a/src/kitsource.c
+++ b/src/kitsource.c
@@ -6,14 +6,12 @@
#include <stdlib.h>
#include <string.h>
+#include <assert.h>
Kit_Source* Kit_CreateSourceFromUrl(const char *url) {
- AVFormatContext *format_ctx = NULL;
+ assert(url != NULL);
- if(url == NULL) {
- Kit_SetError("Source URL must not be NULL");
- return NULL;
- }
+ AVFormatContext *format_ctx = NULL;
// Attempt to open source
if(avformat_open_input(&format_ctx, url, NULL, NULL) < 0) {
@@ -45,103 +43,16 @@ exit_0:
return NULL;
}
-int Kit_InitSourceCodecs(Kit_Source *src) {
- AVCodecContext *acodec_ctx = NULL;
- AVCodecContext *vcodec_ctx = NULL;
- AVCodec *acodec = NULL;
- AVCodec *vcodec = NULL;
-
- if(src == NULL) {
- Kit_SetError("Source must not be NULL");
- return 1;
- }
- if(src->acodec_ctx || src->vcodec_ctx) {
- Kit_SetError("Source codecs already initialized");
- return 1;
- }
-
- // Make sure indexes seem correct
- AVFormatContext *format_ctx = (AVFormatContext *)src->format_ctx;
- if(src->astream_idx < 0 || src->astream_idx >= format_ctx->nb_streams) {
- Kit_SetError("Invalid audio stream index");
- return 1;
- }
- if(src->vstream_idx < 0 || src->vstream_idx >= format_ctx->nb_streams) {
- Kit_SetError("Invalid video stream index");
- return 1;
- }
-
- // Find video decoder
- vcodec = avcodec_find_decoder(format_ctx->streams[src->vstream_idx]->codec->codec_id);
- if(!vcodec) {
- Kit_SetError("No suitable video decoder found");
- goto exit_0;
- }
-
- // Copy the original video codec context
- vcodec_ctx = avcodec_alloc_context3(vcodec);
- if(avcodec_copy_context(vcodec_ctx, format_ctx->streams[src->vstream_idx]->codec) != 0) {
- Kit_SetError("Unable to copy video codec context");
- goto exit_0;
- }
-
- // Create a video decoder context
- if(avcodec_open2(vcodec_ctx, vcodec, NULL) < 0) {
- Kit_SetError("Unable to allocate video codec context");
- goto exit_1;
- }
-
- // Find audio decoder
- acodec = avcodec_find_decoder(format_ctx->streams[src->astream_idx]->codec->codec_id);
- if(!acodec) {
- Kit_SetError("No suitable audio decoder found");
- goto exit_2;
- }
-
- // Copy the original audio codec context
- acodec_ctx = avcodec_alloc_context3(acodec);
- if(avcodec_copy_context(acodec_ctx, format_ctx->streams[src->astream_idx]->codec) != 0) {
- Kit_SetError("Unable to copy audio codec context");
- goto exit_2;
- }
-
- // Create an audio decoder context
- if(avcodec_open2(acodec_ctx, acodec, NULL) < 0) {
- Kit_SetError("Unable to allocate audio codec context");
- goto exit_3;
- }
-
- src->acodec = acodec;
- src->vcodec = vcodec;
- src->acodec_ctx = acodec_ctx;
- src->vcodec_ctx = vcodec_ctx;
- return 0;
-
-exit_3:
- avcodec_free_context(&acodec_ctx);
-exit_2:
- avcodec_close(vcodec_ctx);
-exit_1:
- avcodec_free_context(&vcodec_ctx);
-exit_0:
- return 1;
-}
-
void Kit_CloseSource(Kit_Source *src) {
- if(src == NULL) return;
- avcodec_close((AVCodecContext*)src->acodec_ctx);
- avcodec_close((AVCodecContext*)src->vcodec_ctx);
- avcodec_free_context((AVCodecContext**)&src->acodec_ctx);
- avcodec_free_context((AVCodecContext**)&src->vcodec_ctx);
+ assert(src != NULL);
avformat_close_input((AVFormatContext **)&src->format_ctx);
free(src);
}
int Kit_GetSourceStreamInfo(const Kit_Source *src, Kit_StreamInfo *info, int index) {
- if(src == NULL) {
- Kit_SetError("Source must not be NULL");
- return 1;
- }
+ assert(src != NULL);
+ assert(info != NULL);
+
AVFormatContext *format_ctx = (AVFormatContext *)src->format_ctx;
if(index < 0 || index >= format_ctx->nb_streams) {
Kit_SetError("Invalid stream index");
@@ -162,16 +73,11 @@ int Kit_GetSourceStreamInfo(const Kit_Source *src, Kit_StreamInfo *info, int ind
}
info->index = index;
- info->width = stream->codec->width;
- info->height = stream->codec->height;
return 0;
}
int Kit_GetBestSourceStream(const Kit_Source *src, const Kit_streamtype type) {
- if(src == NULL) {
- Kit_SetError("Source must not be NULL");
- return -1;
- }
+ assert(src != NULL);
int avmedia_type = 0;
switch(type) {
case KIT_STREAMTYPE_VIDEO: avmedia_type = AVMEDIA_TYPE_VIDEO; break;
@@ -182,10 +88,7 @@ int Kit_GetBestSourceStream(const Kit_Source *src, const Kit_streamtype type) {
}
int Kit_SetSourceStream(Kit_Source *src, const Kit_streamtype type, int index) {
- if(src == NULL) {
- Kit_SetError("Source must not be NULL");
- return 1;
- }
+ assert(src != NULL);
switch(type) {
case KIT_STREAMTYPE_AUDIO: src->astream_idx = index; break;
case KIT_STREAMTYPE_VIDEO: src->vstream_idx = index; break;
@@ -197,10 +100,7 @@ int Kit_SetSourceStream(Kit_Source *src, const Kit_streamtype type, int index) {
}
int Kit_GetSourceStream(const Kit_Source *src, const Kit_streamtype type) {
- if(src == NULL) {
- Kit_SetError("Source must not be NULL");
- return -1;
- }
+ assert(src != NULL);
switch(type) {
case KIT_STREAMTYPE_AUDIO: return src->astream_idx;
case KIT_STREAMTYPE_VIDEO: return src->vstream_idx;
@@ -211,9 +111,6 @@ int Kit_GetSourceStream(const Kit_Source *src, const Kit_streamtype type) {
}
int Kit_GetSourceStreamCount(const Kit_Source *src) {
- if(src == NULL) {
- Kit_SetError("Source must not be NULL");
- return -1;
- }
+ assert(src != NULL);
return ((AVFormatContext *)src->format_ctx)->nb_streams;
}
diff --git a/tests/test_source.c b/tests/test_source.c
index 772ebfd..f2349a4 100644
--- a/tests/test_source.c
+++ b/tests/test_source.c
@@ -5,7 +5,6 @@
Kit_Source *src = NULL;
void test_Kit_CreateSourceFromUrl(void) {
- CU_ASSERT_PTR_NULL(Kit_CreateSourceFromUrl(NULL));
CU_ASSERT_PTR_NULL(Kit_CreateSourceFromUrl("nonexistent"));
src = Kit_CreateSourceFromUrl("../../tests/data/CEP140_512kb.mp4");
CU_ASSERT_PTR_NOT_NULL(src);
@@ -14,7 +13,6 @@ void test_Kit_CreateSourceFromUrl(void) {
void test_Kit_GetBestSourceStream(void) {
CU_ASSERT(Kit_GetBestSourceStream(src, KIT_STREAMTYPE_VIDEO) == 0);
CU_ASSERT(Kit_GetBestSourceStream(src, KIT_STREAMTYPE_AUDIO) == 1);
- CU_ASSERT(Kit_GetBestSourceStream(NULL, KIT_STREAMTYPE_AUDIO) == -1);
CU_ASSERT(Kit_GetBestSourceStream(src, KIT_STREAMTYPE_UNKNOWN) == -1);
CU_ASSERT(Kit_GetBestSourceStream(src, KIT_STREAMTYPE_DATA) == -1);
CU_ASSERT(Kit_GetBestSourceStream(src, KIT_STREAMTYPE_ATTACHMENT) == -1);
@@ -22,18 +20,15 @@ void test_Kit_GetBestSourceStream(void) {
}
void test_Kit_GetSourceStreamCount(void) {
- CU_ASSERT(Kit_GetSourceStreamCount(NULL) == -1);
CU_ASSERT(Kit_GetSourceStreamCount(src) == 2);
}
void test_Kit_SetSourceStream(void) {
- CU_ASSERT(Kit_SetSourceStream(NULL, KIT_STREAMTYPE_VIDEO, 0) == 1);
CU_ASSERT(Kit_SetSourceStream(src, KIT_STREAMTYPE_VIDEO, 0) == 0);
CU_ASSERT(Kit_SetSourceStream(src, KIT_STREAMTYPE_UNKNOWN, 0) == 1);
}
void test_Kit_GetSourceStream(void) {
- CU_ASSERT(Kit_GetSourceStream(NULL, KIT_STREAMTYPE_VIDEO) == -1);
CU_ASSERT(Kit_GetSourceStream(src, KIT_STREAMTYPE_VIDEO) == 0);
CU_ASSERT(Kit_GetSourceStream(src, KIT_STREAMTYPE_AUDIO) == 1);
CU_ASSERT(Kit_GetSourceStream(src, KIT_STREAMTYPE_UNKNOWN) == -1);