summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTuomas Virtanen <katajakasa@gmail.com>2016-01-10 20:09:06 +0200
committerTuomas Virtanen <katajakasa@gmail.com>2016-01-10 20:09:06 +0200
commit8ee9b3e560a39d65c62a87fc797ddcc1279b3a78 (patch)
tree95fcffd406041b6ee09721e3d5d708588ab7de47
parentc29fb5865954310042aa833baf85064364883b28 (diff)
Cleanups + allow audio only playback
-rw-r--r--CMakeLists.txt49
-rw-r--r--examples/example_audio.c140
-rw-r--r--examples/example_video.c (renamed from examples/example_play.c)7
-rw-r--r--include/kitchensink/kitplayer.h7
-rw-r--r--src/kitchensink.c14
-rw-r--r--src/kiterror.c26
-rw-r--r--src/kitplayer.c301
-rw-r--r--src/kitsource.c172
-rw-r--r--tests/test_source.c32
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) {