From 20bfdbb781575fd6ee3c6e8624a09e314b65a162 Mon Sep 17 00:00:00 2001 From: Tuomas Virtanen Date: Fri, 15 Jan 2016 00:33:47 +0200 Subject: Initial work on subtitle streams --- README.md | 2 +- examples/example_video.c | 3 ++ include/kitchensink/kitplayer.h | 17 +++++++++- include/kitchensink/kitsource.h | 5 +-- src/kitplayer.c | 75 +++++++++++++++++++++++++++++++++++++++++ src/kitsource.c | 4 +++ 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; ///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; } -- cgit v1.2.3