diff options
-rw-r--r-- | CMakeLists.txt | 49 | ||||
-rw-r--r-- | examples/example_audio.c | 140 | ||||
-rw-r--r-- | examples/example_video.c (renamed from examples/example_play.c) | 7 | ||||
-rw-r--r-- | include/kitchensink/kitplayer.h | 7 | ||||
-rw-r--r-- | src/kitchensink.c | 14 | ||||
-rw-r--r-- | src/kiterror.c | 26 | ||||
-rw-r--r-- | src/kitplayer.c | 301 | ||||
-rw-r--r-- | src/kitsource.c | 172 | ||||
-rw-r--r-- | tests/test_source.c | 32 |
9 files changed, 479 insertions, 269 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 072761c..dbe64dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,13 +6,13 @@ set(VERSION_MAJOR "0") set(VERSION_MINOR "0") set(VERSION_PATCH "1") add_definitions( - -DKIT_VERSION_MAJOR=${VERSION_MAJOR} - -DKIT_VERSION_MINOR=${VERSION_MINOR} - -DKIT_VERSION_PATCH=${VERSION_PATCH} + -DKIT_VERSION_MAJOR=${VERSION_MAJOR} + -DKIT_VERSION_MINOR=${VERSION_MINOR} + -DKIT_VERSION_PATCH=${VERSION_PATCH} ) 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_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -pedantic -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") @@ -28,9 +28,9 @@ if(BUILD_TESTS) endif() include_directories( - include/ - ${SDL2_INCLUDE_DIRS} - ${FFMPEG_INCLUDE_DIRS} + include/ + ${SDL2_INCLUDE_DIRS} + ${FFMPEG_INCLUDE_DIRS} ) FILE(GLOB SOURCES "src/*.c") @@ -45,29 +45,36 @@ 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} - ${FFMPEG_LIBRARIES} + ${SDL2_LIBRARIES} + ${FFMPEG_LIBRARIES} ) if(BUILD_EXAMPLES) - add_executable(exampleplay examples/example_play.c) + add_executable(exampleaudio examples/example_audio.c) + add_executable(examplevideo examples/example_video.c) - if(MINGW) - target_link_libraries(exampleplay mingw32) - endif() + if(MINGW) + target_link_libraries(exampleaudio mingw32) + target_link_libraries(examplevideo mingw32) + endif() - target_link_libraries(exampleplay - SDL_kitchensink_static - ${SDL2_LIBRARIES} - ${FFMPEG_LIBRARIES} - ) + target_link_libraries(exampleaudio + SDL_kitchensink_static + ${SDL2_LIBRARIES} + ${FFMPEG_LIBRARIES} + ) + target_link_libraries(examplevideo + SDL_kitchensink_static + ${SDL2_LIBRARIES} + ${FFMPEG_LIBRARIES} + ) endif() # Installation INSTALL(FILES ${HEADERS} DESTINATION include/kitchensink/) INSTALL(TARGETS SDL_kitchensink SDL_kitchensink_static - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib ) diff --git a/examples/example_audio.c b/examples/example_audio.c new file mode 100644 index 0000000..d7fdd2a --- /dev/null +++ b/examples/example_audio.c @@ -0,0 +1,140 @@ +#include <kitchensink/kitchensink.h> +#include <SDL2/SDL.h> +#include <stdio.h> +#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", + "KIT_STREAMTYPE_AUDIO", + "KIT_STREAMTYPE_DATA", + "KIT_STREAMTYPE_SUBTITLE", + "KIT_STREAMTYPE_ATTACHMENT" +}; + +int main(int argc, char *argv[]) { + int err = 0, ret = 0; + const char* filename = NULL; + + // Events + 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_AUDIO); + if(err != 0) { + fprintf(stderr, "Unable to initialize SDL!\n"); + return 1; + } + + Kit_Init(KIT_INIT_FORMATS|KIT_INIT_NETWORK); + + // Open up the sourcefile. + src = Kit_CreateSourceFromUrl(filename); + if(src == NULL) { + fprintf(stderr, "Unable to load file '%s': %s\n", filename, Kit_GetError()); + return 1; + } + + // Print stream information + Kit_StreamInfo sinfo; + fprintf(stderr, "Source streams:\n"); + for(int i = 0; i < Kit_GetSourceStreamCount(src); 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[sinfo.type]); + } + + // 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); + + if(!pinfo.audio.is_enabled) { + fprintf(stderr, "File contains no audio!\n"); + return 1; + } + + 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"); + + // 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); + + // Start playback + Kit_PlayerPlay(player); + + while(run) { + if(Kit_GetPlayerState(player) == KIT_STOPPED) { + run = false; + continue; + } + + // Refresh audio + ret = SDL_GetQueuedAudioSize(audio_dev); + if(ret < AUDIOBUFFER_SIZE) { + ret = Kit_GetAudioData(player, (unsigned char*)audiobuf, AUDIOBUFFER_SIZE); + if(ret > 0) { + SDL_LockAudio(); + SDL_QueueAudio(audio_dev, audiobuf, ret); + SDL_UnlockAudio(); + SDL_PauseAudioDevice(audio_dev, 0); + } + } + + SDL_Delay(1); + } + + SDL_CloseAudioDevice(audio_dev); + + Kit_ClosePlayer(player); + Kit_CloseSource(src); + + Kit_Quit(); + SDL_Quit(); + return 0; +} diff --git a/examples/example_play.c b/examples/example_video.c index 1f31a6a..1afe957 100644 --- a/examples/example_play.c +++ b/examples/example_video.c @@ -132,6 +132,12 @@ int main(int argc, char *argv[]) { // Print some information Kit_PlayerInfo pinfo; Kit_GetPlayerInfo(player, &pinfo); + + if(!pinfo.video.is_enabled) { + fprintf(stderr, "File contains no video!\n"); + return 1; + } + fprintf(stderr, "Media information:\n"); fprintf(stderr, " * Audio: %s (%s), %dHz, %dch, %db, %s\n", pinfo.acodec, @@ -170,6 +176,7 @@ int main(int argc, char *argv[]) { // Set logical size for the renderer. This way when we scale, we keep aspect ratio. SDL_RenderSetLogicalSize(renderer, pinfo.video.width, pinfo.video.height); + // Start playback Kit_PlayerPlay(player); while(run) { diff --git a/include/kitchensink/kitplayer.h b/include/kitchensink/kitplayer.h index 8cae811..8f67a9b 100644 --- a/include/kitchensink/kitplayer.h +++ b/include/kitchensink/kitplayer.h @@ -3,11 +3,12 @@ #include "kitchensink/kitsource.h" #include "kitchensink/kitconfig.h" -#include <stdbool.h> #include <SDL2/SDL_render.h> #include <SDL2/SDL_thread.h> +#include <stdbool.h> + #ifdef __cplusplus extern "C" { #endif @@ -25,13 +26,15 @@ typedef enum Kit_PlayerState { } Kit_PlayerState; typedef struct Kit_AudioFormat { - int bytes; + bool is_enabled; bool is_signed; + int bytes; int samplerate; int channels; } Kit_AudioFormat; typedef struct Kit_VideoFormat { + bool is_enabled; unsigned int format; int width; int height; diff --git a/src/kitchensink.c b/src/kitchensink.c index a494d62..eb8deb1 100644 --- a/src/kitchensink.c +++ b/src/kitchensink.c @@ -5,17 +5,19 @@ static unsigned int _init_flags = 0; int Kit_Init(unsigned int flags) { - if(flags & KIT_INIT_NETWORK) - avformat_network_init(); - if(flags & KIT_INIT_FORMATS) - av_register_all(); + if(flags & KIT_INIT_NETWORK) { + avformat_network_init(); + } + if(flags & KIT_INIT_FORMATS) { + av_register_all(); + } _init_flags = flags; - return 0; + return 0; } void Kit_Quit() { if(_init_flags & KIT_INIT_NETWORK) { - avformat_network_deinit(); + avformat_network_deinit(); } _init_flags = 0; } diff --git a/src/kiterror.c b/src/kiterror.c index eed779b..2c87414 100644 --- a/src/kiterror.c +++ b/src/kiterror.c @@ -12,23 +12,23 @@ static char _error_available = false; static char _error_message[KIT_ERRBUFSIZE] = "\0"; const char* Kit_GetError() { - if(_error_available) { - _error_available = false; - return _error_message; - } - return NULL; + if(_error_available) { + _error_available = false; + return _error_message; + } + return NULL; } void Kit_SetError(const char* fmt, ...) { - assert(fmt != NULL); - va_list args; - va_start(args, fmt); - vsnprintf(_error_message, KIT_ERRBUFSIZE, (char*)fmt, args); - va_end(args); - _error_available = true; + assert(fmt != NULL); + va_list args; + va_start(args, fmt); + vsnprintf(_error_message, KIT_ERRBUFSIZE, (char*)fmt, args); + va_end(args); + _error_available = true; } void Kit_ClearError() { - _error_message[0] = 0; - _error_available = false; + _error_message[0] = 0; + _error_available = false; } diff --git a/src/kitplayer.c b/src/kitplayer.c index 0e03c18..8849aab 100644 --- a/src/kitplayer.c +++ b/src/kitplayer.c @@ -28,56 +28,59 @@ static int _InitCodecs(Kit_Player *player, const Kit_Source *src) { 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"); + // Make sure index seems correct + if(src->astream_idx >= (int)format_ctx->nb_streams) { + Kit_SetError("Invalid audio stream index: %d", src->astream_idx); goto exit_0; - } + } else if(src->astream_idx >= 0) { + // 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_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; - } + // 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_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; + // Create an audio decoder context + if(avcodec_open2(acodec_ctx, acodec, NULL) < 0) { + Kit_SetError("Unable to allocate audio 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"); + // Make sure index seems correct + if(src->vstream_idx >= (int)format_ctx->nb_streams) { + fprintf(stderr, "%d >= %d\n", src->vstream_idx, format_ctx->nb_streams); + Kit_SetError("Invalid video stream index: %d", src->vstream_idx); goto exit_2; - } + } else if(src->vstream_idx >= 0) { + // 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_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; - } + // 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_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; + // Create a video decoder context + if(avcodec_open2(vcodec_ctx, vcodec, NULL) < 0) { + Kit_SetError("Unable to allocate video codec context"); + goto exit_3; + } } player->acodec_ctx = acodec_ctx; @@ -86,11 +89,11 @@ static int _InitCodecs(Kit_Player *player, const Kit_Source *src) { return 0; exit_3: - avcodec_free_context(&acodec_ctx); + avcodec_free_context(&vcodec_ctx); exit_2: - avcodec_close(vcodec_ctx); + avcodec_close(acodec_ctx); exit_1: - avcodec_free_context(&vcodec_ctx); + avcodec_free_context(&acodec_ctx); exit_0: return 1; } @@ -282,14 +285,14 @@ static int _UpdatePlayer(Kit_Player *player) { int ret; // If either buffer is full, just stop here for now. - if(SDL_LockMutex(player->vmutex) == 0) { + if(player->vcodec_ctx != NULL && SDL_LockMutex(player->vmutex) == 0) { ret = Kit_IsBufferFull(player->vbuffer); SDL_UnlockMutex(player->vmutex); if(ret) { return 0; } } - if(SDL_LockMutex(player->amutex) == 0) { + if(player->acodec_ctx != NULL && SDL_LockMutex(player->amutex) == 0) { ret = Kit_GetRingBufferFree(player->abuffer); SDL_UnlockMutex(player->amutex); if(ret < 16384) { @@ -306,10 +309,10 @@ static int _UpdatePlayer(Kit_Player *player) { } // Check if this is a packet we need to handle and pass it on - if(packet.stream_index == player->src->vstream_idx) { + if(player->vcodec_ctx != NULL && packet.stream_index == player->src->vstream_idx) { _HandleVideoPacket(player, &packet); } - if(packet.stream_index == player->src->astream_idx) { + if(player->acodec_ctx != NULL && packet.stream_index == player->src->astream_idx) { _HandleAudioPacket(player, &packet); } @@ -346,101 +349,137 @@ Kit_Player* Kit_CreatePlayer(const Kit_Source *src) { assert(src != NULL); Kit_Player *player = calloc(1, sizeof(Kit_Player)); + if(player == NULL) { + Kit_SetError("Unable to allocate player"); + return NULL; + } + + AVCodecContext *acodec_ctx = NULL; + AVCodecContext *vcodec_ctx = NULL; // Initialize codecs if(_InitCodecs(player, src) != 0) { - goto exit_0; - } + goto error; + } + + // Init audio codec information if audio codec is initialized + acodec_ctx = (AVCodecContext*)player->acodec_ctx; + if(acodec_ctx != NULL) { + player->aformat.samplerate = acodec_ctx->sample_rate; + player->aformat.channels = acodec_ctx->channels; + player->aformat.is_enabled = true; + _FindAudioFormat(acodec_ctx->sample_fmt, &player->aformat.bytes, &player->aformat.is_signed); + + player->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); + if(swr_init((struct SwrContext *)player->swr) != 0) { + Kit_SetError("Unable to initialize audio converter context"); + goto error; + } - 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); - if(swr_init(swr) != 0) { - Kit_SetError("Unable to initialize audio converter context"); - goto exit_0; - } + player->abuffer = Kit_CreateRingBuffer(KIT_ABUFFERSIZE); + if(player->abuffer == NULL) { + Kit_SetError("Unable to initialize audio ringbuffer"); + goto error; + } - // 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); - if(sws == NULL) { - Kit_SetError("Unable to initialize video converter context"); - goto exit_1; - } - - player->abuffer = Kit_CreateRingBuffer(KIT_ABUFFERSIZE); - if(player->abuffer == NULL) { - Kit_SetError("Unable to initialize audio ringbuffer"); - goto exit_2; + player->tmp_aframe = av_frame_alloc(); + if(player->tmp_aframe == NULL) { + Kit_SetError("Unable to initialize temporary audio frame"); + goto error; + } } - player->vbuffer = Kit_CreateBuffer(KIT_VBUFFERSIZE); - if(player->vbuffer == NULL) { - Kit_SetError("Unable to initialize video ringbuffer"); - goto exit_3; + // Initialize video codec information is initialized + vcodec_ctx = (AVCodecContext*)player->vcodec_ctx; + if(vcodec_ctx != NULL) { + player->vformat.is_enabled = true; + player->vformat.width = vcodec_ctx->width; + player->vformat.height = vcodec_ctx->height; + _FindPixelFormat(vcodec_ctx->pix_fmt, &player->vformat.format); + + player->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); + if((struct SwsContext *)player->sws == NULL) { + Kit_SetError("Unable to initialize video converter context"); + goto error; + } + + player->vbuffer = Kit_CreateBuffer(KIT_VBUFFERSIZE); + if(player->vbuffer == NULL) { + Kit_SetError("Unable to initialize video ringbuffer"); + goto error; + } + + player->tmp_vframe = av_frame_alloc(); + if(player->tmp_vframe == NULL) { + Kit_SetError("Unable to initialize temporary video frame"); + goto error; + } } - player->tmp_vframe = av_frame_alloc(); - if(player->tmp_vframe == NULL) { - Kit_SetError("Unable to initialize temporary video frame"); - goto exit_4; + player->vmutex = SDL_CreateMutex(); + if(player->vmutex == NULL) { + Kit_SetError("Unable to allocate video mutex"); + goto error; } - player->tmp_aframe = av_frame_alloc(); - if(player->tmp_aframe == NULL) { - Kit_SetError("Unable to initialize temporary audio frame"); - goto exit_5; + player->amutex = SDL_CreateMutex(); + if(player->amutex == NULL) { + Kit_SetError("Unable to allocate audio mutex"); + goto error; } player->dec_thread = SDL_CreateThread(_DecoderThread, "Kit Decoder Thread", player); if(player->dec_thread == NULL) { Kit_SetError("Unable to create a decoder thread: %s", SDL_GetError()); - goto exit_6; + goto error; } - player->vmutex = SDL_CreateMutex(); - player->amutex = SDL_CreateMutex(); - player->swr = swr; - player->sws = sws; return player; -exit_6: - av_frame_free((AVFrame**)&player->tmp_aframe); -exit_5: - av_frame_free((AVFrame**)&player->tmp_vframe); -exit_4: - Kit_DestroyBuffer((Kit_Buffer*)player->vbuffer); -exit_3: - Kit_DestroyRingBuffer((Kit_RingBuffer*)player->abuffer); -exit_2: - sws_freeContext((struct SwsContext *)sws); -exit_1: - swr_free((struct SwrContext **)swr); -exit_0: - free(player); +error: + if(player->amutex != NULL) { + SDL_DestroyMutex(player->amutex); + } + if(player->vmutex != NULL) { + SDL_DestroyMutex(player->vmutex); + } + if(player->tmp_aframe != NULL) { + av_frame_free((AVFrame**)&player->tmp_aframe); + } + if(player->tmp_vframe != NULL) { + av_frame_free((AVFrame**)&player->tmp_vframe); + } + if(player->vbuffer != NULL) { + Kit_DestroyBuffer((Kit_Buffer*)player->vbuffer); + } + if(player->abuffer != NULL) { + Kit_DestroyRingBuffer((Kit_RingBuffer*)player->abuffer); + } + if(player->sws != NULL) { + sws_freeContext((struct SwsContext *)player->sws); + } + if(player->swr != NULL) { + swr_free((struct SwrContext **)player->swr); + } + if(player != NULL) { + free(player); + } return NULL; } @@ -552,13 +591,19 @@ void Kit_GetPlayerInfo(const Kit_Player *player, Kit_PlayerInfo *info) { 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); + // Reset everything to 0. We might not fill all fields. + memset(info, 0, sizeof(Kit_PlayerInfo)); - memcpy(&info->video, &player->vformat, sizeof(Kit_VideoFormat)); - memcpy(&info->audio, &player->aformat, sizeof(Kit_AudioFormat)); + if(acodec_ctx != NULL) { + strncpy(info->acodec, acodec_ctx->codec->name, KIT_CODECMAX); + strncpy(info->acodec_name, acodec_ctx->codec->long_name, KIT_CODECNAMEMAX); + memcpy(&info->audio, &player->aformat, sizeof(Kit_AudioFormat)); + } + if(vcodec_ctx != NULL) { + 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)); + } } KIT_API Kit_PlayerState Kit_GetPlayerState(const Kit_Player *player) { diff --git a/src/kitsource.c b/src/kitsource.c index d8cfe53..b79207e 100644 --- a/src/kitsource.c +++ b/src/kitsource.c @@ -9,108 +9,114 @@ #include <assert.h> Kit_Source* Kit_CreateSourceFromUrl(const char *url) { - assert(url != NULL); - - AVFormatContext *format_ctx = NULL; - - // Attempt to open source - if(avformat_open_input(&format_ctx, url, NULL, NULL) < 0) { - Kit_SetError("Unable to open source Url"); - goto exit_0; - } - - // Fetch stream information. This may potentially take a while. - if(avformat_find_stream_info(format_ctx, NULL) < 0) { - Kit_SetError("Unable to fetch source information"); - goto exit_1; - } - - // Find best streams for defaults - int best_astream_idx = av_find_best_stream(format_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); - int best_vstream_idx = av_find_best_stream(format_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); - - // Allocate and return a new source - // TODO: Check allocation errors - Kit_Source *out = calloc(1, sizeof(Kit_Source)); - out->format_ctx = format_ctx; - out->astream_idx = best_astream_idx; - out->vstream_idx = best_vstream_idx; - return out; + assert(url != NULL); + + Kit_Source *src = calloc(1, sizeof(Kit_Source)); + if(src == NULL) { + Kit_SetError("Unable to allocate source"); + return NULL; + } + + // Attempt to open source + if(avformat_open_input((AVFormatContext **)&src->format_ctx, url, NULL, NULL) < 0) { + Kit_SetError("Unable to open source Url"); + goto exit_0; + } + + // Fetch stream information. This may potentially take a while. + if(avformat_find_stream_info((AVFormatContext *)src->format_ctx, NULL) < 0) { + Kit_SetError("Unable to fetch source information"); + goto exit_1; + } + + // Find best streams for defaults + src->astream_idx = Kit_GetBestSourceStream(src, KIT_STREAMTYPE_AUDIO); + src->vstream_idx = Kit_GetBestSourceStream(src, KIT_STREAMTYPE_VIDEO); + return src; exit_1: - avformat_close_input(&format_ctx); + avformat_close_input((AVFormatContext **)&src->format_ctx); exit_0: - return NULL; + free(src); + return NULL; } void Kit_CloseSource(Kit_Source *src) { - assert(src != NULL); - avformat_close_input((AVFormatContext **)&src->format_ctx); - free(src); + assert(src != NULL); + avformat_close_input((AVFormatContext **)&src->format_ctx); + free(src); } int Kit_GetSourceStreamInfo(const Kit_Source *src, Kit_StreamInfo *info, int index) { - 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"); - return 1; - } - - AVStream *stream = format_ctx->streams[index]; - switch(stream->codec->codec_type) { - case AVMEDIA_TYPE_UNKNOWN: info->type = KIT_STREAMTYPE_UNKNOWN; break; - case AVMEDIA_TYPE_DATA: info->type = KIT_STREAMTYPE_DATA; break; - case AVMEDIA_TYPE_VIDEO: info->type = KIT_STREAMTYPE_VIDEO; break; - case AVMEDIA_TYPE_AUDIO: info->type = KIT_STREAMTYPE_AUDIO; break; - case AVMEDIA_TYPE_SUBTITLE: info->type = KIT_STREAMTYPE_SUBTITLE; break; - case AVMEDIA_TYPE_ATTACHMENT: info->type = KIT_STREAMTYPE_ATTACHMENT; break; - default: - Kit_SetError("Unknown native stream type"); - return 1; - } - - info->index = index; - return 0; + 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"); + return 1; + } + + AVStream *stream = format_ctx->streams[index]; + switch(stream->codec->codec_type) { + case AVMEDIA_TYPE_UNKNOWN: info->type = KIT_STREAMTYPE_UNKNOWN; break; + case AVMEDIA_TYPE_DATA: info->type = KIT_STREAMTYPE_DATA; break; + case AVMEDIA_TYPE_VIDEO: info->type = KIT_STREAMTYPE_VIDEO; break; + case AVMEDIA_TYPE_AUDIO: info->type = KIT_STREAMTYPE_AUDIO; break; + case AVMEDIA_TYPE_SUBTITLE: info->type = KIT_STREAMTYPE_SUBTITLE; break; + case AVMEDIA_TYPE_ATTACHMENT: info->type = KIT_STREAMTYPE_ATTACHMENT; break; + default: + Kit_SetError("Unknown native stream type"); + return 1; + } + + info->index = index; + return 0; } int Kit_GetBestSourceStream(const Kit_Source *src, const Kit_streamtype type) { - assert(src != NULL); - int avmedia_type = 0; - switch(type) { - case KIT_STREAMTYPE_VIDEO: avmedia_type = AVMEDIA_TYPE_VIDEO; break; - case KIT_STREAMTYPE_AUDIO: avmedia_type = AVMEDIA_TYPE_AUDIO; break; - default: return -1; - } - return av_find_best_stream((AVFormatContext *)src->format_ctx, avmedia_type, -1, -1, NULL, 0); + assert(src != NULL); + int avmedia_type = 0; + switch(type) { + case KIT_STREAMTYPE_VIDEO: avmedia_type = AVMEDIA_TYPE_VIDEO; break; + case KIT_STREAMTYPE_AUDIO: avmedia_type = AVMEDIA_TYPE_AUDIO; break; + default: return -1; + } + int ret = av_find_best_stream((AVFormatContext *)src->format_ctx, avmedia_type, -1, -1, NULL, 0); + if(ret == AVERROR_STREAM_NOT_FOUND) { + return -1; + } + if(ret == AVERROR_DECODER_NOT_FOUND) { + Kit_SetError("Unable to find a decoder for the stream"); + return 1; + } + return ret; } int Kit_SetSourceStream(Kit_Source *src, const Kit_streamtype type, int index) { - assert(src != NULL); - switch(type) { - case KIT_STREAMTYPE_AUDIO: src->astream_idx = index; break; - case KIT_STREAMTYPE_VIDEO: src->vstream_idx = index; break; - default: - Kit_SetError("Invalid stream type"); - return 1; - } - return 0; + assert(src != NULL); + switch(type) { + case KIT_STREAMTYPE_AUDIO: src->astream_idx = index; break; + case KIT_STREAMTYPE_VIDEO: src->vstream_idx = index; break; + default: + Kit_SetError("Invalid stream type"); + return 1; + } + return 0; } int Kit_GetSourceStream(const Kit_Source *src, const Kit_streamtype type) { - assert(src != NULL); - switch(type) { - case KIT_STREAMTYPE_AUDIO: return src->astream_idx; - case KIT_STREAMTYPE_VIDEO: return src->vstream_idx; - default: - break; - } - return -1; + assert(src != NULL); + switch(type) { + case KIT_STREAMTYPE_AUDIO: return src->astream_idx; + case KIT_STREAMTYPE_VIDEO: return src->vstream_idx; + default: + break; + } + return -1; } int Kit_GetSourceStreamCount(const Kit_Source *src) { - assert(src != NULL); - return ((AVFormatContext *)src->format_ctx)->nb_streams; + assert(src != NULL); + return ((AVFormatContext *)src->format_ctx)->nb_streams; } diff --git a/tests/test_source.c b/tests/test_source.c index f2349a4..0f70635 100644 --- a/tests/test_source.c +++ b/tests/test_source.c @@ -5,37 +5,37 @@ Kit_Source *src = NULL; void test_Kit_CreateSourceFromUrl(void) { - CU_ASSERT_PTR_NULL(Kit_CreateSourceFromUrl("nonexistent")); - src = Kit_CreateSourceFromUrl("../../tests/data/CEP140_512kb.mp4"); - CU_ASSERT_PTR_NOT_NULL(src); + CU_ASSERT_PTR_NULL(Kit_CreateSourceFromUrl("nonexistent")); + src = Kit_CreateSourceFromUrl("../../tests/data/CEP140_512kb.mp4"); + CU_ASSERT_PTR_NOT_NULL(src); } 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(src, KIT_STREAMTYPE_UNKNOWN) == -1); - CU_ASSERT(Kit_GetBestSourceStream(src, KIT_STREAMTYPE_DATA) == -1); - CU_ASSERT(Kit_GetBestSourceStream(src, KIT_STREAMTYPE_ATTACHMENT) == -1); - CU_ASSERT(Kit_GetBestSourceStream(src, KIT_STREAMTYPE_SUBTITLE) == -1); + CU_ASSERT(Kit_GetBestSourceStream(src, KIT_STREAMTYPE_VIDEO) == 0); + CU_ASSERT(Kit_GetBestSourceStream(src, 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); + CU_ASSERT(Kit_GetBestSourceStream(src, KIT_STREAMTYPE_SUBTITLE) == -1); } void test_Kit_GetSourceStreamCount(void) { - CU_ASSERT(Kit_GetSourceStreamCount(src) == 2); + CU_ASSERT(Kit_GetSourceStreamCount(src) == 2); } void test_Kit_SetSourceStream(void) { - CU_ASSERT(Kit_SetSourceStream(src, KIT_STREAMTYPE_VIDEO, 0) == 0); - CU_ASSERT(Kit_SetSourceStream(src, KIT_STREAMTYPE_UNKNOWN, 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(src, KIT_STREAMTYPE_VIDEO) == 0); - CU_ASSERT(Kit_GetSourceStream(src, KIT_STREAMTYPE_AUDIO) == 1); - CU_ASSERT(Kit_GetSourceStream(src, KIT_STREAMTYPE_UNKNOWN) == -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); } void test_Kit_CloseSource(void) { - Kit_CloseSource(src); + Kit_CloseSource(src); } void source_test_suite(CU_pSuite suite) { |