From cce3ee047e712f48059ed28b284cf5c194c34de8 Mon Sep 17 00:00:00 2001 From: Tuomas Virtanen Date: Fri, 8 Jan 2016 01:29:36 +0200 Subject: Code dump. Playback & decoding works, but no sync yet. --- CMakeLists.txt | 28 +- README.md | 2 +- examples/example_play.c | 154 +++++++++- include/kitchensink/internal/kitbuffer.h | 20 ++ include/kitchensink/internal/kitringbuffer.h | 22 ++ include/kitchensink/kitchensink.h | 19 +- include/kitchensink/kitconfig.h | 26 ++ include/kitchensink/kiterror.h | 8 +- include/kitchensink/kitplayer.h | 56 +++- include/kitchensink/kitsource.h | 23 +- src/kitbuffer.c | 46 +++ src/kitchensink.c | 6 +- src/kiterror.c | 4 + src/kitplayer.c | 441 ++++++++++++++++++++++++++- src/kitringbuffer.c | 158 ++++++++++ src/kitsource.c | 125 +------- tests/test_source.c | 5 - 17 files changed, 962 insertions(+), 181 deletions(-) create mode 100644 include/kitchensink/internal/kitbuffer.h create mode 100644 include/kitchensink/internal/kitringbuffer.h create mode 100644 include/kitchensink/kitconfig.h create mode 100644 src/kitbuffer.c create mode 100644 src/kitringbuffer.c 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 /* +* 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 \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 #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 + +#include #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 +#include + +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 +#include -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 #include +#include #include +#include #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 +#include "kitchensink/kitplayer.h" +#include "kitchensink/kiterror.h" +#include "kitchensink/internal/kitbuffer.h" +#include "kitchensink/internal/kitringbuffer.h" +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +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 +#include +#include +#include + +/** + * 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 #include +#include 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); -- cgit v1.2.3