From 766a346e516a58fd110caca311ecaec68b47bc12 Mon Sep 17 00:00:00 2001 From: Tuomas Virtanen Date: Sun, 17 Jan 2016 22:46:18 +0200 Subject: Add support for libass subtitles --- CMakeLists.txt | 5 + README.md | 1 + cmake/FindSDL2.cmake | 12 +- cmake/Findass.cmake | 43 +++++ cmake/Findcunit.cmake | 4 +- cmake/Findffmpeg.cmake | 6 +- include/kitchensink/internal/kitlibstate.h | 14 ++ include/kitchensink/kitchensink.h | 25 +-- include/kitchensink/kitlib.h | 33 ++++ include/kitchensink/kitplayer.h | 4 + src/kitchensink.c | 34 ---- src/kitlib.c | 59 +++++++ src/kitlibstate.c | 7 + src/kitlist.c | 1 + src/kitplayer.c | 243 ++++++++++++++++++++++++++--- 15 files changed, 397 insertions(+), 94 deletions(-) create mode 100644 cmake/Findass.cmake create mode 100644 include/kitchensink/internal/kitlibstate.h create mode 100644 include/kitchensink/kitlib.h delete mode 100644 src/kitchensink.c create mode 100644 src/kitlib.c create mode 100644 src/kitlibstate.c diff --git a/CMakeLists.txt b/CMakeLists.txt index e8df027..7f05496 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ option(BUILD_EXAMPLES "Build examples" OFF) option(BUILD_TESTS "Build unittests" OFF) find_package(SDL2) +find_package(ass) find_package(ffmpeg COMPONENTS avcodec avformat avutil swscale swresample) if(BUILD_TESTS) @@ -31,6 +32,7 @@ include_directories( include/ ${SDL2_INCLUDE_DIRS} ${FFMPEG_INCLUDE_DIRS} + ${ASS_INCLUDE_DIRS} ) FILE(GLOB SOURCES "src/*.c") @@ -47,6 +49,7 @@ target_compile_options(SDL_kitchensink PRIVATE "-fvisibility=hidden") target_link_libraries(SDL_kitchensink ${SDL2_LIBRARIES} ${FFMPEG_LIBRARIES} + ${ASS_LIBRARIES} ) if(BUILD_EXAMPLES) @@ -62,11 +65,13 @@ if(BUILD_EXAMPLES) SDL_kitchensink_static ${SDL2_LIBRARIES} ${FFMPEG_LIBRARIES} + ${ASS_LIBRARIES} ) target_link_libraries(examplevideo SDL_kitchensink_static ${SDL2_LIBRARIES} ${FFMPEG_LIBRARIES} + ${ASS_LIBRARIES} ) endif() diff --git a/README.md b/README.md index f27dbc2..60a776c 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ Build requirements: Library requirements: * SDL2 (>=2.0.3) (Note! Examples require 2.0.4!) * FFmpeg (>=2.8) +* libass * CUnit (optional, for unittests) Note that Clang might work, but is not tested. Older SDL2 and FFmpeg library versions diff --git a/cmake/FindSDL2.cmake b/cmake/FindSDL2.cmake index c0b43bc..8102832 100644 --- a/cmake/FindSDL2.cmake +++ b/cmake/FindSDL2.cmake @@ -36,19 +36,19 @@ if(MINGW) PATHS ${SDL2_SEARCH_PATHS} ) else() - SET(SDL2MAIN_LIBRARY "") + set(SDL2MAIN_LIBRARY "") endif() if(SDL2_INCLUDE_DIR AND SDL2_LIBRARY) - SET(SDL2_FOUND TRUE) + set(SDL2_FOUND TRUE) endif() if(SDL2_FOUND) - SET(SDL2_LIBRARIES ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY}) - SET(SDL2_INCLUDE_DIRS ${SDL2_INCLUDE_DIR}) - MESSAGE(STATUS "Found SDL2: ${SDL2_LIBRARIES}") + set(SDL2_LIBRARIES ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY}) + set(SDL2_INCLUDE_DIRS ${SDL2_INCLUDE_DIR}) + message(STATUS "Found SDL2: ${SDL2_LIBRARIES}") else() - MESSAGE(WARNING "Could not find SDL2") + message(WARNING "Could not find SDL2") endif() mark_as_advanced(SDL2MAIN_LIBRARY SDL2_LIBRARY SDL2_INCLUDE_DIR SDL2_SEARCH_PATHS) diff --git a/cmake/Findass.cmake b/cmake/Findass.cmake new file mode 100644 index 0000000..bbc88d5 --- /dev/null +++ b/cmake/Findass.cmake @@ -0,0 +1,43 @@ +# A Simple libass Finder. +# (c) Tuomas Virtanen 2016 (Licensed under MIT license) +# Usage: +# find_package(ass) +# +# Declares: +# * ASS_FOUND +# * ASS_INCLUDE_DIRS +# * ASS_LIBRARIES +# + +set(ASS_SEARCH_PATHS + /usr/local/ + /usr/ + /opt +) + +find_path(ASS_INCLUDE_DIR ass/ass.h + HINTS + PATH_SUFFIXES include + PATHS ${ASS_SEARCH_PATHS} +) + +find_library(ASS_LIBRARY + NAMES ass + HINTS + PATH_SUFFIXES lib + PATHS ${ASS_SEARCH_PATHS} +) + +if(ASS_INCLUDE_DIR AND ASS_LIBRARY) + set(ASS_FOUND TRUE) +endif() + +if(ASS_FOUND) + set(ASS_LIBRARIES ${ASS_LIBRARY}) + set(ASS_INCLUDE_DIRS ${ASS_INCLUDE_DIR}) + message(STATUS "Found libass: ${ASS_LIBRARIES}") +else() + message(WARNING "Could not find libass") +endif() + +mark_as_advanced(ASS_LIBRARY ASS_INCLUDE_DIR ASS_SEARCH_PATHS) diff --git a/cmake/Findcunit.cmake b/cmake/Findcunit.cmake index ee982b0..ae81cf1 100644 --- a/cmake/Findcunit.cmake +++ b/cmake/Findcunit.cmake @@ -31,8 +31,8 @@ if(CUNIT_INCLUDE_DIR AND CUNIT_LIBRARY) endif() if(CUNIT_FOUND) - SET(CUNIT_LIBRARIES ${CUNIT_LIBRARY}) - SET(CUNIT_INCLUDE_DIRS ${CUNIT_INCLUDE_DIR}) + set(CUNIT_LIBRARIES ${CUNIT_LIBRARY}) + set(CUNIT_INCLUDE_DIRS ${CUNIT_INCLUDE_DIR}) message(STATUS "Found CUnit: ${CUNIT_LIBRARY}") else() message(WARNING "Could not find CUnit.") diff --git a/cmake/Findffmpeg.cmake b/cmake/Findffmpeg.cmake index eca94a3..fdb6246 100644 --- a/cmake/Findffmpeg.cmake +++ b/cmake/Findffmpeg.cmake @@ -57,7 +57,7 @@ foreach(comp ${FFMPEG_COMPONENTS}) else() set(FFMPEG_FOUND FALSE) set(${comp}_FOUND FALSE) - MESSAGE(WARNING "Could not find component: ${comp}") + message(WARNING "Could not find component: ${comp}") endif() # Mark the temporary variables as hidden in the ui @@ -66,9 +66,9 @@ foreach(comp ${FFMPEG_COMPONENTS}) endforeach() if(FFMPEG_FOUND) - MESSAGE(STATUS "Found FFMPEG: ${FFMPEG_LIBRARIES}") + message(STATUS "Found FFMPEG: ${FFMPEG_LIBRARIES}") else() - MESSAGE(WARNING "Could not find FFMPEG") + message(WARNING "Could not find FFMPEG") endif() mark_as_advanced(FFMPEG_COMPONENTS FFMPEG_SEARCH_PATHS) diff --git a/include/kitchensink/internal/kitlibstate.h b/include/kitchensink/internal/kitlibstate.h new file mode 100644 index 0000000..e16a5c9 --- /dev/null +++ b/include/kitchensink/internal/kitlibstate.h @@ -0,0 +1,14 @@ +#ifndef KITLIBSTATE_H +#define KITLIBSTATE_H + +#include +#include "kitchensink/kitconfig.h" + +typedef struct Kit_LibraryState { + unsigned int init_flags; + ASS_Library *libass_handle; +} Kit_LibraryState; + +KIT_LOCAL Kit_LibraryState* Kit_GetLibraryState(); + +#endif // KITLIBSTATE_H diff --git a/include/kitchensink/kitchensink.h b/include/kitchensink/kitchensink.h index 0d40442..be318a5 100644 --- a/include/kitchensink/kitchensink.h +++ b/include/kitchensink/kitchensink.h @@ -1,34 +1,11 @@ #ifndef KITCHENSINK_H #define KITCHENSINK_H +#include "kitchensink/kitlib.h" #include "kitchensink/kiterror.h" #include "kitchensink/kitsource.h" #include "kitchensink/kitplayer.h" #include "kitchensink/kitutils.h" #include "kitchensink/kitconfig.h" -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct Kit_Version { - unsigned char major; - unsigned char minor; - unsigned char patch; -} Kit_Version; - -enum { - KIT_INIT_FORMATS = 0x1, - KIT_INIT_NETWORK = 0x2, -}; - -KIT_API int Kit_Init(unsigned int flags); -KIT_API void Kit_Quit(); - -KIT_API void Kit_GetVersion(Kit_Version *version); - -#ifdef __cplusplus -} -#endif - #endif // KITCHENSINK_H diff --git a/include/kitchensink/kitlib.h b/include/kitchensink/kitlib.h new file mode 100644 index 0000000..9a827be --- /dev/null +++ b/include/kitchensink/kitlib.h @@ -0,0 +1,33 @@ +#ifndef KITLIB_H +#define KITLIB_H + +#include "kitchensink/kiterror.h" +#include "kitchensink/kitsource.h" +#include "kitchensink/kitplayer.h" +#include "kitchensink/kitutils.h" +#include "kitchensink/kitconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Kit_Version { + unsigned char major; + unsigned char minor; + unsigned char patch; +} Kit_Version; + +enum { + KIT_INIT_FORMATS = 0x1, + KIT_INIT_NETWORK = 0x2, +}; + +KIT_API int Kit_Init(unsigned int flags); +KIT_API void Kit_Quit(); +KIT_API void Kit_GetVersion(Kit_Version *version); + +#ifdef __cplusplus +} +#endif + +#endif // KITLIB_H diff --git a/include/kitchensink/kitplayer.h b/include/kitchensink/kitplayer.h index 1d1f5f9..5a6d9b4 100644 --- a/include/kitchensink/kitplayer.h +++ b/include/kitchensink/kitplayer.h @@ -82,6 +82,10 @@ typedef struct Kit_Player { void *swr; ///< FFmpeg: Audio resampler void *sws; ///< FFmpeg: Video converter + // libass + void *ass_renderer; + void *ass_track; + // Other uint8_t seek_flag; const Kit_Source *src; ///< Reference to Audio/Video source diff --git a/src/kitchensink.c b/src/kitchensink.c deleted file mode 100644 index afbfdac..0000000 --- a/src/kitchensink.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "kitchensink/kitchensink.h" -#include -#include - -static unsigned int _init_flags = 0; - -int Kit_Init(unsigned int flags) { - if(_init_flags != 0) { - Kit_SetError("Kitchensink is already initialized."); - return 1; - } - if(flags & KIT_INIT_NETWORK) { - avformat_network_init(); - } - if(flags & KIT_INIT_FORMATS) { - av_register_all(); - } - _init_flags = flags; - return 0; -} - -void Kit_Quit() { - if(_init_flags & KIT_INIT_NETWORK) { - avformat_network_deinit(); - } - _init_flags = 0; -} - -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/kitlib.c b/src/kitlib.c new file mode 100644 index 0000000..3c3eee3 --- /dev/null +++ b/src/kitlib.c @@ -0,0 +1,59 @@ +#include "kitchensink/kitchensink.h" +#include "kitchensink/internal/kitlibstate.h" +#include +#include +#include + +// No-op +void _libass_msg_callback(int level, const char *fmt, va_list va, void *data) {} + +int Kit_Init(unsigned int flags) { + Kit_LibraryState *state = Kit_GetLibraryState(); + + if(state->init_flags != 0) { + Kit_SetError("Kitchensink is already initialized."); + return 1; + } + if(flags & KIT_INIT_NETWORK) { + avformat_network_init(); + } + if(flags & KIT_INIT_FORMATS) { + av_register_all(); + } + + state->init_flags = flags; + + // Init libass + state->libass_handle = ass_library_init(); + + // Set up libass font directories + if(strcmp(SDL_GetPlatform(), "Windows") == 0) { + ass_set_fonts_dir(state->libass_handle, "C:/Windows/Fonts"); + } + else if(strcmp(SDL_GetPlatform(), "Linux") == 0) { + ass_set_fonts_dir(state->libass_handle, "/usr/share/fonts"); + } + + // Make libass message spam go away + ass_set_message_cb(state->libass_handle, _libass_msg_callback, NULL); + + return 0; +} + +void Kit_Quit() { + Kit_LibraryState *state = Kit_GetLibraryState(); + + if(state->init_flags & KIT_INIT_NETWORK) { + avformat_network_deinit(); + } + state->init_flags = 0; + + ass_library_done(state->libass_handle); +} + +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/kitlibstate.c b/src/kitlibstate.c new file mode 100644 index 0000000..0dd4e6b --- /dev/null +++ b/src/kitlibstate.c @@ -0,0 +1,7 @@ +#include "kitchensink/internal/kitlibstate.h" + +static Kit_LibraryState _librarystate = {0, NULL}; + +Kit_LibraryState* Kit_GetLibraryState() { + return &_librarystate; +} diff --git a/src/kitlist.c b/src/kitlist.c index 9926aa1..b6d861c 100644 --- a/src/kitlist.c +++ b/src/kitlist.c @@ -34,6 +34,7 @@ void Kit_ClearList(Kit_List *list) { list->data[i] = NULL; } } + list->length = 0; } void Kit_RemoveFromList(Kit_List *list, unsigned int iterator) { diff --git a/src/kitplayer.c b/src/kitplayer.c index 5024f6b..da62bcf 100644 --- a/src/kitplayer.c +++ b/src/kitplayer.c @@ -3,6 +3,7 @@ #include "kitchensink/internal/kitbuffer.h" #include "kitchensink/internal/kitringbuffer.h" #include "kitchensink/internal/kitlist.h" +#include "kitchensink/internal/kitlibstate.h" #include #include @@ -11,8 +12,10 @@ #include #include #include +#include "libavutil/avstring.h" #include +#include #include #include @@ -30,7 +33,7 @@ #define KIT_VBUFFERSIZE 3 #define KIT_ABUFFERSIZE 64 #define KIT_CBUFFERSIZE 8 -#define KIT_SBUFFERSIZE 16 +#define KIT_SBUFFERSIZE 48 typedef enum Kit_ControlPacketType { KIT_CONTROL_SEEK, @@ -170,6 +173,27 @@ exit_0: return 1; } +static int reset_libass_track(Kit_Player *player) { + Kit_LibraryState *state = Kit_GetLibraryState(); + AVCodecContext *scodec_ctx = player->scodec_ctx; + + // Initialize libass track + player->ass_track = ass_new_track(state->libass_handle); + if(player->ass_track == NULL) { + Kit_SetError("Unable to initialize libass track"); + return 1; + } + + // Set up libass track headers (ffmpeg provides these) + if(scodec_ctx->subtitle_header) { + ass_process_codec_private( + (ASS_Track*)player->ass_track, + (char*)scodec_ctx->subtitle_header, + scodec_ctx->subtitle_header_size); + } + return 0; +} + static void _FindPixelFormat(enum AVPixelFormat fmt, unsigned int *out_fmt) { switch(fmt) { case AV_PIX_FMT_YUV420P: @@ -451,7 +475,7 @@ static void _HandleAudioPacket(Kit_Player *player, AVPacket *packet) { } } -static Kit_SubtitlePacket* _HandleBitmapSubtitle(Kit_Player *player, double pts, AVSubtitle *sub, AVSubtitleRect *rect) { +static void _HandleBitmapSubtitle(Kit_SubtitlePacket** spackets, int *n, Kit_Player *player, double pts, AVSubtitle *sub, AVSubtitleRect *rect) { if(rect->nb_colors == 256) { // Paletted image based subtitles. Convert and set palette. SDL_Surface *s = SDL_CreateRGBSurfaceFrom( @@ -492,9 +516,85 @@ static Kit_SubtitlePacket* _HandleBitmapSubtitle(Kit_Player *player, double pts, end = pts + (sub->end_display_time / 1000.0f); } - return _CreateSubtitlePacket(start, end, dst_rect, tmp); + spackets[(*n)++] = _CreateSubtitlePacket(start, end, dst_rect, tmp); + } +} + +static void _ProcessAssSubtitleRect(Kit_Player *player, AVSubtitleRect *rect) { + ass_process_data((ASS_Track*)player->ass_track, rect->ass, strlen(rect->ass)); +} + +#define _r(c) ((c) >> 24) +#define _g(c) (((c) >> 16) & 0xFF) +#define _b(c) (((c) >> 8) & 0xFF) +#define _a(c) ((c) & 0xFF) + +static void _ProcessAssImage(SDL_Surface *surface, const ASS_Image *img) { + int x, y; + unsigned char opacity = 255 - _a(img->color); + unsigned char r = _r(img->color); + unsigned char g = _g(img->color); + unsigned char b = _b(img->color); + + unsigned char *src; + unsigned char *dst; + + src = img->bitmap; + dst = (unsigned char*)surface->pixels; + for(y = 0; y < img->h; y++) { + for(x = 0; x < img->w; x++) { + unsigned int k = ((unsigned) src[x]) * opacity / 255; + dst[x * 3] = (k * b + (255 - k) * dst[x * 3]) / 255; + dst[x * 3 + 1] = (k * g + (255 - k) * dst[x * 3 + 1]) / 255; + dst[x * 3 + 2] = (k * r + (255 - k) * dst[x * 3 + 2]) / 255; + } + src += img->stride; + dst += surface->pitch; + } +} + +static void _HandleAssSubtitle(Kit_SubtitlePacket** spackets, int *n, Kit_Player *player, double pts, AVSubtitle *sub) { + double start = pts + (sub->start_display_time / 1000.0f); + double end = pts + (sub->end_display_time / 1000.0f); + + // Process current chunk of data + unsigned int now = start * 1000; + int change = 0; + ASS_Image *images = ass_render_frame((ASS_Renderer*)player->ass_renderer, (ASS_Track*)player->ass_track, now, &change); + + // Convert to SDL_Surfaces + if(change > 0) { + ASS_Image *now = images; + if(now != NULL) { + do { + Uint32 rmask, gmask, bmask, amask; + #if SDL_BYTEORDER == SDL_BIG_ENDIAN + rmask = 0xff000000; + gmask = 0x00ff0000; + bmask = 0x0000ff00; + amask = 0x000000ff; + #else + rmask = 0x000000ff; + gmask = 0x0000ff00; + bmask = 0x00ff0000; + amask = 0xff000000; + #endif + SDL_Surface *tmp = SDL_CreateRGBSurface( + 0, now->w, now->h, 32, + rmask, gmask, bmask, amask); + + _ProcessAssImage(tmp, now); + + SDL_Rect *dst_rect = malloc(sizeof(SDL_Rect)); + dst_rect->x = now->dst_x; + dst_rect->y = now->dst_y; + dst_rect->w = now->w; + dst_rect->h = now->h; + + spackets[(*n)++] = _CreateSubtitlePacket(start, end, dst_rect, tmp); + } while((now = now->next) != NULL); + } } - return NULL; } static void _HandleSubtitlePacket(Kit_Player *player, AVPacket *packet) { @@ -505,6 +605,8 @@ static void _HandleSubtitlePacket(Kit_Player *player, AVPacket *packet) { int len; AVCodecContext *scodec_ctx = (AVCodecContext*)player->scodec_ctx; AVFormatContext *fmt_ctx = (AVFormatContext *)player->src->format_ctx; + Kit_SubtitlePacket *tmp = NULL; + unsigned int it; AVSubtitle sub; memset(&sub, 0, sizeof(AVSubtitle)); @@ -523,33 +625,53 @@ static void _HandleSubtitlePacket(Kit_Player *player, AVPacket *packet) { } // Convert subtitles to SDL_Surface and create a packet - Kit_SubtitlePacket *spacket = NULL; + Kit_SubtitlePacket *spackets[KIT_SBUFFERSIZE]; + memset(spackets, 0, sizeof(Kit_SubtitlePacket*) * KIT_SBUFFERSIZE); + + int n = 0; + bool has_ass = false; for(int r = 0; r < sub.num_rects; r++) { - AVSubtitleRect *rect = sub.rects[r]; - switch(rect->type) { - case SUBTITLE_BITMAP: spacket = _HandleBitmapSubtitle(player, pts, &sub, rect); break; - case SUBTITLE_TEXT: break; - case SUBTITLE_ASS: break; - case SUBTITLE_NONE: break; + switch(sub.rects[r]->type) { + case SUBTITLE_BITMAP: + _HandleBitmapSubtitle(spackets, &n, player, pts, &sub, sub.rects[r]); + break; + case SUBTITLE_ASS: + _ProcessAssSubtitleRect(player, sub.rects[r]); + has_ass = true; + break; + case SUBTITLE_TEXT: + break; + case SUBTITLE_NONE: + break; } } + // Process libass content + if(has_ass) { + _HandleAssSubtitle(spackets, &n, player, pts, &sub); + } + // Lock, write to subtitle buffer, unlock - bool done = false; if(SDL_LockMutex(player->smutex) == 0) { - // Clear out old subtitles that should only be valid until next (this) subtitle - unsigned int it = 0; - Kit_SubtitlePacket *tmp = NULL; - while((tmp = Kit_IterateList((Kit_List*)player->sbuffer, &it)) != NULL) { - if(tmp->pts_end < 0) { - Kit_RemoveFromList((Kit_List*)player->sbuffer, it); + if(has_ass) { + Kit_ClearList((Kit_List*)player->sbuffer); + } else { + // Clear out old subtitles that should only be valid until next (this) subtitle + it = 0; + while((tmp = Kit_IterateList((Kit_List*)player->sbuffer, &it)) != NULL) { + if(tmp->pts_end < 0) { + Kit_RemoveFromList((Kit_List*)player->sbuffer, it); + } } } // Add new subtitle - if(spacket != NULL) { - if(Kit_WriteList((Kit_List*)player->sbuffer, spacket) == 0) { - done = true; + for(int i = 0; i < KIT_SBUFFERSIZE; i++) { + Kit_SubtitlePacket *spacket = spackets[i]; + if(spacket != NULL) { + if(Kit_WriteList((Kit_List*)player->sbuffer, spacket) == 0) { + spackets[i] = NULL; + } } } @@ -558,10 +680,11 @@ static void _HandleSubtitlePacket(Kit_Player *player, AVPacket *packet) { } // Couldn't write packet, free memory - if(!done && spacket != NULL) { - _FreeSubtitlePacket(spacket); + for(int i = 0; i < KIT_SBUFFERSIZE; i++) { + if(spackets[i] != NULL) { + _FreeSubtitlePacket(spackets[i]); + } } - } } } @@ -598,6 +721,7 @@ static void _HandleFlushCommand(Kit_Player *player, Kit_ControlPacket *packet) { SDL_UnlockMutex(player->smutex); } } + reset_libass_track(player); } static void _HandleSeekCommand(Kit_Player *player, Kit_ControlPacket *packet) { @@ -727,6 +851,25 @@ static int _DecoderThread(void *ptr) { return 0; } +static const char * const font_mime[] = { + "application/x-font-ttf", + "application/x-truetype-font", + "application/vnd.ms-opentype", + NULL +}; + +static bool attachment_is_font(AVStream *stream) { + AVDictionaryEntry *tag = av_dict_get(stream->metadata, "mimetype", NULL, AV_DICT_MATCH_CASE); + if(tag) { + for(int n = 0; font_mime[n]; n++) { + if(av_strcasecmp(font_mime[n], tag->value) == 0) { + return true; + } + } + } + return false; +} + Kit_Player* Kit_CreatePlayer(const Kit_Source *src) { assert(src != NULL); @@ -823,11 +966,46 @@ Kit_Player* Kit_CreatePlayer(const Kit_Source *src) { player->sformat.is_enabled = true; player->sformat.stream_idx = src->sstream_idx; + // subtitle packet buffer player->sbuffer = Kit_CreateList(KIT_SBUFFERSIZE, _FreeSubtitlePacket); if(player->sbuffer == NULL) { Kit_SetError("Unable to initialize active subtitle list"); goto error; } + + // Initialize libass renderer + Kit_LibraryState *state = Kit_GetLibraryState(); + player->ass_renderer = ass_renderer_init(state->libass_handle); + if(player->ass_renderer == NULL) { + Kit_SetError("Unable to initialize libass renderer"); + goto error; + } + + // Read fonts from attachment streams and give them to libass + AVFormatContext *format_ctx = player->src->format_ctx; + for (int j = 0; j < format_ctx->nb_streams; j++) { + AVStream *st = format_ctx->streams[j]; + if(st->codec->codec_type == AVMEDIA_TYPE_ATTACHMENT && attachment_is_font(st)) { + const AVDictionaryEntry *tag = av_dict_get( + st->metadata, + "filename", + NULL, + AV_DICT_MATCH_CASE); + if(tag) { + ass_add_font( + state->libass_handle, + tag->value, + (char*)st->codec->extradata, + st->codec->extradata_size); + } + } + } + + // Init libass fonts and window frame size + ass_set_fonts(player->ass_renderer, NULL, NULL, 1, NULL, 1); + ass_set_frame_size(player->ass_renderer, vcodec_ctx->width, vcodec_ctx->height); + ass_set_font_scale(player->ass_renderer, 1.1f); + reset_libass_track(player); } player->cbuffer = Kit_CreateBuffer(KIT_CBUFFERSIZE, _FreeControlPacket); @@ -899,6 +1077,13 @@ error: if(player->swr != NULL) { swr_free((struct SwrContext **)player->swr); } + + if(player->ass_track != NULL) { + ass_free_track((ASS_Track*)player->ass_track); + } + if(player->ass_renderer != NULL) { + ass_renderer_done((ASS_Renderer *)player->ass_renderer); + } if(player != NULL) { free(player); } @@ -946,6 +1131,14 @@ void Kit_ClosePlayer(Kit_Player *player) { Kit_DestroyBuffer((Kit_Buffer*)player->vbuffer); Kit_DestroyList((Kit_List*)player->sbuffer); + // Free libass context + if(player->ass_track != NULL) { + ass_free_track((ASS_Track*)player->ass_track); + } + if(player->ass_renderer != NULL) { + ass_renderer_done((ASS_Renderer *)player->ass_renderer); + } + // Free the player structure itself free(player); } @@ -1061,7 +1254,7 @@ int Kit_GetSubtitleData(Kit_Player *player, SDL_Renderer *renderer) { // Check if refresh is required and remove old subtitles it = 0; while((packet = Kit_IterateList((Kit_List*)player->sbuffer, &it)) != NULL) { - if(packet->pts_end >= 0 && packet->pts_end + SUBTITLE_SYNC_THRESHOLD < cur_subtitle_ts) { + if(packet->pts_end >= 0 && packet->pts_end < cur_subtitle_ts) { Kit_RemoveFromList((Kit_List*)player->sbuffer, it); } } -- cgit v1.2.3