summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--examples/example_video.c3
-rw-r--r--include/kitchensink/kitplayer.h17
-rw-r--r--include/kitchensink/kitsource.h5
-rw-r--r--src/kitplayer.c75
-rw-r--r--src/kitsource.c4
6 files changed, 102 insertions, 4 deletions
diff --git a/README.md b/README.md
index ca4187b..f27dbc2 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
[![Build Status](https://travis-ci.org/katajakasa/SDL_kitchensink.svg?branch=master)](https://travis-ci.org/katajakasa/SDL_kitchensink)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/7585/badge.svg)](https://scan.coverity.com/projects/katajakasa-sdl_kitchensink)
-FFMPEG and SDL2 based C99 library for audio and video playback.
+FFmpeg and SDL2 based library for audio and video playback, written in C99.
This library is still very much todo, but it's slowly getting there.
diff --git a/examples/example_video.c b/examples/example_video.c
index a62fd2d..49725d2 100644
--- a/examples/example_video.c
+++ b/examples/example_video.c
@@ -152,6 +152,9 @@ int main(int argc, char *argv[]) {
pinfo.vcodec_name,
pinfo.video.width,
pinfo.video.height);
+ fprintf(stderr, " * Subtitle: %s (%s)\n",
+ pinfo.vcodec,
+ pinfo.vcodec_name);
fprintf(stderr, "Duration: %f seconds\n", Kit_GetPlayerDuration(player));
// Init audio
diff --git a/include/kitchensink/kitplayer.h b/include/kitchensink/kitplayer.h
index a0f97ad..ea86b31 100644
--- a/include/kitchensink/kitplayer.h
+++ b/include/kitchensink/kitplayer.h
@@ -24,6 +24,7 @@ typedef enum Kit_PlayerState {
} Kit_PlayerState;
typedef struct Kit_AudioFormat {
+ int stream_idx; ///< Stream index
bool is_enabled; ///< Is stream enabled
unsigned int format; ///< SDL Audio Format
bool is_signed; ///< Signedness
@@ -33,17 +34,24 @@ typedef struct Kit_AudioFormat {
} Kit_AudioFormat;
typedef struct Kit_VideoFormat {
- bool is_enabled; ///<Is stream enabled
+ int stream_idx; ///< Stream index
+ bool is_enabled; ///< Is stream enabled
unsigned int format; ///< SDL Pixel Format
int width; ///< Width in pixels
int height; ///< Height in pixels
} Kit_VideoFormat;
+typedef struct Kit_SubtitleFormat {
+ int stream_idx; ///< Stream index
+ bool is_enabled; ///< Is stream enabled
+} Kit_SubtitleFormat;
+
typedef struct Kit_Player {
// Local state
Kit_PlayerState state; ///< Playback state
Kit_VideoFormat vformat; ///< Video format information
Kit_AudioFormat aformat; ///< Audio format information
+ Kit_SubtitleFormat sformat; ///< Subtitle format information
// Synchronization
double clock_sync; ///< Clock sync point
@@ -54,18 +62,22 @@ typedef struct Kit_Player {
SDL_Thread *dec_thread; ///< Decoder thread
SDL_mutex *vmutex; ///< Video stream buffer lock
SDL_mutex *amutex; ///< Audio stream buffer lock
+ SDL_mutex *smutex; ///< Subtitle stream buffer lock
SDL_mutex *cmutex; ///< Control stream buffer lock
// Buffers
void *abuffer; ///<Audio stream buffer
void *vbuffer; ///< Video stream buffer
+ void *sbuffer; ///< Subtitle stream buffer
void *cbuffer; ///< Control stream buffer
// FFmpeg internal state
void *vcodec_ctx; ///< FFmpeg: Video codec context
void *acodec_ctx; ///< FFmpeg: Audio codec context
+ void *scodec_ctx; ///< FFmpeg: Subtitle codec context
void *tmp_vframe; ///< FFmpeg: Preallocated temporary video frame
void *tmp_aframe; ///< FFmpeg: Preallocated temporary audio frame
+ void *tmp_sframe; ///< FFmpeg: Preallocated temporary subtitle frame
void *swr; ///< FFmpeg: Audio resampler
void *sws; ///< FFmpeg: Video converter
@@ -79,8 +91,11 @@ typedef struct Kit_PlayerInfo {
char acodec_name[KIT_CODECNAMEMAX]; ///< Audio codec long, more descriptive name
char vcodec[KIT_CODECMAX]; ///< Video codec short name, eg. "x264"
char vcodec_name[KIT_CODECNAMEMAX]; ///< Video codec long, more descriptive name
+ char scodec[KIT_CODECMAX]; ///< Subtitle codec short name, eg. "ass"
+ char scodec_name[KIT_CODECNAMEMAX]; ///< Subtitle codec long, more descriptive name
Kit_VideoFormat video; ///< Video format information
Kit_AudioFormat audio; ///< Audio format information
+ Kit_SubtitleFormat subtitle; ///< Subtitle format information
} Kit_PlayerInfo;
KIT_API Kit_Player* Kit_CreatePlayer(const Kit_Source *src);
diff --git a/include/kitchensink/kitsource.h b/include/kitchensink/kitsource.h
index aef9764..ed1711f 100644
--- a/include/kitchensink/kitsource.h
+++ b/include/kitchensink/kitsource.h
@@ -20,8 +20,9 @@ typedef enum Kit_StreamType {
} Kit_StreamType;
typedef struct Kit_Source {
- int astream_idx; ///< Audiostream index
- int vstream_idx; ///< Videostream index
+ int astream_idx; ///< Audio stream index
+ int vstream_idx; ///< Video stream index
+ int sstream_idx; ///< Subtitle stream index
void *format_ctx; ///< FFmpeg: Videostream format context
} Kit_Source;
diff --git a/src/kitplayer.c b/src/kitplayer.c
index 0ae228e..f8fb2fc 100644
--- a/src/kitplayer.c
+++ b/src/kitplayer.c
@@ -28,6 +28,7 @@
#define KIT_VBUFFERSIZE 3
#define KIT_ABUFFERSIZE 64
#define KIT_CBUFFERSIZE 8
+#define KIT_SBUFFERSIZE 32
typedef enum Kit_ControlPacketType {
KIT_CONTROL_SEEK,
@@ -56,8 +57,10 @@ static int _InitCodecs(Kit_Player *player, const Kit_Source *src) {
AVCodecContext *acodec_ctx = NULL;
AVCodecContext *vcodec_ctx = NULL;
+ AVCodecContext *scodec_ctx = NULL;
AVCodec *acodec = NULL;
AVCodec *vcodec = NULL;
+ AVCodec *scodec = NULL;
AVFormatContext *format_ctx = (AVFormatContext *)src->format_ctx;
// Make sure index seems correct
@@ -112,11 +115,41 @@ static int _InitCodecs(Kit_Player *player, const Kit_Source *src) {
}
}
+ if(src->sstream_idx >= (int)format_ctx->nb_streams) {
+ Kit_SetError("Invalid subtitle stream index: %d", src->sstream_idx);
+ goto exit_2;
+ } else if(src->sstream_idx >= 0) {
+ // Find subtitle decoder
+ scodec = avcodec_find_decoder(format_ctx->streams[src->sstream_idx]->codec->codec_id);
+ if(!scodec) {
+ Kit_SetError("No suitable subtitle decoder found");
+ goto exit_4;
+ }
+
+ // Copy the original subtitle codec context
+ scodec_ctx = avcodec_alloc_context3(scodec);
+ if(avcodec_copy_context(scodec_ctx, format_ctx->streams[src->sstream_idx]->codec) != 0) {
+ Kit_SetError("Unable to copy subtitle codec context");
+ goto exit_4;
+ }
+
+ // Create a subtitle decoder context
+ if(avcodec_open2(scodec_ctx, scodec, NULL) < 0) {
+ Kit_SetError("Unable to allocate subtitle codec context");
+ goto exit_5;
+ }
+ }
+
player->acodec_ctx = acodec_ctx;
player->vcodec_ctx = vcodec_ctx;
+ player->scodec_ctx = scodec_ctx;
player->src = src;
return 0;
+exit_5:
+ avcodec_free_context(&scodec_ctx);
+exit_4:
+ avcodec_close(vcodec_ctx);
exit_3:
avcodec_free_context(&vcodec_ctx);
exit_2:
@@ -550,6 +583,7 @@ Kit_Player* Kit_CreatePlayer(const Kit_Source *src) {
AVCodecContext *acodec_ctx = NULL;
AVCodecContext *vcodec_ctx = NULL;
+ AVCodecContext *scodec_ctx = NULL;
// Initialize codecs
if(_InitCodecs(player, src) != 0) {
@@ -562,6 +596,7 @@ Kit_Player* Kit_CreatePlayer(const Kit_Source *src) {
player->aformat.samplerate = acodec_ctx->sample_rate;
player->aformat.channels = acodec_ctx->channels;
player->aformat.is_enabled = true;
+ player->aformat.stream_idx = src->astream_idx;
_FindAudioFormat(acodec_ctx->sample_fmt, &player->aformat.bytes, &player->aformat.is_signed, &player->aformat.format);
player->swr = swr_alloc_set_opts(
@@ -597,6 +632,7 @@ Kit_Player* Kit_CreatePlayer(const Kit_Source *src) {
player->vformat.is_enabled = true;
player->vformat.width = vcodec_ctx->width;
player->vformat.height = vcodec_ctx->height;
+ player->vformat.stream_idx = src->vstream_idx;
_FindPixelFormat(vcodec_ctx->pix_fmt, &player->vformat.format);
player->sws = sws_getContext(
@@ -626,6 +662,24 @@ Kit_Player* Kit_CreatePlayer(const Kit_Source *src) {
}
}
+ scodec_ctx = (AVCodecContext*)player->scodec_ctx;
+ if(scodec_ctx != NULL) {
+ player->sformat.is_enabled = true;
+ player->sformat.stream_idx = src->sstream_idx;
+
+ player->sbuffer = Kit_CreateBuffer(KIT_SBUFFERSIZE);
+ if(player->sbuffer == NULL) {
+ Kit_SetError("Unable to initialize subtitle ringbuffer");
+ goto error;
+ }
+
+ player->tmp_sframe = av_frame_alloc();
+ if(player->tmp_sframe == NULL) {
+ Kit_SetError("Unable to initialize temporary subtitle frame");
+ goto error;
+ }
+ }
+
player->cbuffer = Kit_CreateBuffer(KIT_CBUFFERSIZE);
if(player->cbuffer == NULL) {
Kit_SetError("Unable to initialize control ringbuffer");
@@ -674,12 +728,18 @@ error:
if(player->tmp_vframe != NULL) {
av_frame_free((AVFrame**)&player->tmp_vframe);
}
+ if(player->tmp_sframe != NULL) {
+ av_frame_free((AVFrame**)&player->tmp_sframe);
+ }
if(player->vbuffer != NULL) {
Kit_DestroyBuffer((Kit_Buffer*)player->vbuffer);
}
if(player->abuffer != NULL) {
Kit_DestroyBuffer((Kit_Buffer*)player->abuffer);
}
+ if(player->sbuffer != NULL) {
+ Kit_DestroyBuffer((Kit_Buffer*)player->sbuffer);
+ }
if(player->cbuffer != NULL) {
Kit_DestroyBuffer((Kit_Buffer*)player->cbuffer);
}
@@ -718,10 +778,13 @@ void Kit_ClosePlayer(Kit_Player *player) {
if(player->tmp_aframe != NULL) {
av_frame_free((AVFrame**)&player->tmp_aframe);
}
+
avcodec_close((AVCodecContext*)player->acodec_ctx);
avcodec_close((AVCodecContext*)player->vcodec_ctx);
+ avcodec_close((AVCodecContext*)player->scodec_ctx);
avcodec_free_context((AVCodecContext**)&player->acodec_ctx);
avcodec_free_context((AVCodecContext**)&player->vcodec_ctx);
+ avcodec_free_context((AVCodecContext**)&player->scodec_ctx);
// Free local audio buffers
if(player->cbuffer != NULL) {
@@ -735,6 +798,12 @@ void Kit_ClosePlayer(Kit_Player *player) {
Kit_DestroyBuffer((Kit_Buffer*)player->abuffer);
}
+ // Free local audio buffers
+ if(player->sbuffer != NULL) {
+ //_FlushSubtitleBuffer(player);
+ Kit_DestroyBuffer((Kit_Buffer*)player->sbuffer);
+ }
+
// Free local video buffers
if(player->vbuffer != NULL) {
_FlushVideoBuffer(player);
@@ -928,6 +997,7 @@ void Kit_GetPlayerInfo(const Kit_Player *player, Kit_PlayerInfo *info) {
AVCodecContext *acodec_ctx = (AVCodecContext*)player->acodec_ctx;
AVCodecContext *vcodec_ctx = (AVCodecContext*)player->vcodec_ctx;
+ AVCodecContext *scodec_ctx = (AVCodecContext*)player->scodec_ctx;
// Reset everything to 0. We might not fill all fields.
memset(info, 0, sizeof(Kit_PlayerInfo));
@@ -942,6 +1012,11 @@ void Kit_GetPlayerInfo(const Kit_Player *player, Kit_PlayerInfo *info) {
strncpy(info->vcodec_name, vcodec_ctx->codec->long_name, KIT_CODECNAMEMAX-1);
memcpy(&info->video, &player->vformat, sizeof(Kit_VideoFormat));
}
+ if(scodec_ctx != NULL) {
+ strncpy(info->scodec, scodec_ctx->codec->name, KIT_CODECMAX);
+ strncpy(info->scodec_name, scodec_ctx->codec->long_name, KIT_CODECNAMEMAX);
+ memcpy(&info->subtitle, &player->sformat, sizeof(Kit_SubtitleFormat));
+ }
}
Kit_PlayerState Kit_GetPlayerState(const Kit_Player *player) {
diff --git a/src/kitsource.c b/src/kitsource.c
index 9a7b739..270c84a 100644
--- a/src/kitsource.c
+++ b/src/kitsource.c
@@ -32,6 +32,7 @@ Kit_Source* Kit_CreateSourceFromUrl(const char *url) {
// Find best streams for defaults
src->astream_idx = Kit_GetBestSourceStream(src, KIT_STREAMTYPE_AUDIO);
src->vstream_idx = Kit_GetBestSourceStream(src, KIT_STREAMTYPE_VIDEO);
+ src->sstream_idx = Kit_GetBestSourceStream(src, KIT_STREAMTYPE_SUBTITLE);
return src;
exit_1:
@@ -80,6 +81,7 @@ int Kit_GetBestSourceStream(const Kit_Source *src, const Kit_StreamType type) {
switch(type) {
case KIT_STREAMTYPE_VIDEO: avmedia_type = AVMEDIA_TYPE_VIDEO; break;
case KIT_STREAMTYPE_AUDIO: avmedia_type = AVMEDIA_TYPE_AUDIO; break;
+ case KIT_STREAMTYPE_SUBTITLE: avmedia_type = AVMEDIA_TYPE_SUBTITLE; break;
default: return -1;
}
int ret = av_find_best_stream((AVFormatContext *)src->format_ctx, avmedia_type, -1, -1, NULL, 0);
@@ -98,6 +100,7 @@ int Kit_SetSourceStream(Kit_Source *src, const Kit_StreamType type, int index) {
switch(type) {
case KIT_STREAMTYPE_AUDIO: src->astream_idx = index; break;
case KIT_STREAMTYPE_VIDEO: src->vstream_idx = index; break;
+ case KIT_STREAMTYPE_SUBTITLE: src->sstream_idx = index; break;
default:
Kit_SetError("Invalid stream type");
return 1;
@@ -110,6 +113,7 @@ int Kit_GetSourceStream(const Kit_Source *src, const Kit_StreamType type) {
switch(type) {
case KIT_STREAMTYPE_AUDIO: return src->astream_idx;
case KIT_STREAMTYPE_VIDEO: return src->vstream_idx;
+ case KIT_STREAMTYPE_SUBTITLE: return src->sstream_idx;
default:
break;
}