summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTuomas Virtanen <katajakasa@gmail.com>2016-01-17 22:46:18 +0200
committerTuomas Virtanen <katajakasa@gmail.com>2016-01-17 22:46:18 +0200
commit766a346e516a58fd110caca311ecaec68b47bc12 (patch)
tree189acf02cec11701e48d7a94e9748c83deb46baa
parentdefe24601e2338a6a6d3145220e25c56bf1c3147 (diff)
Add support for libass subtitles
-rw-r--r--CMakeLists.txt5
-rw-r--r--README.md1
-rw-r--r--cmake/FindSDL2.cmake12
-rw-r--r--cmake/Findass.cmake43
-rw-r--r--cmake/Findcunit.cmake4
-rw-r--r--cmake/Findffmpeg.cmake6
-rw-r--r--include/kitchensink/internal/kitlibstate.h14
-rw-r--r--include/kitchensink/kitchensink.h25
-rw-r--r--include/kitchensink/kitlib.h33
-rw-r--r--include/kitchensink/kitplayer.h4
-rw-r--r--src/kitchensink.c34
-rw-r--r--src/kitlib.c59
-rw-r--r--src/kitlibstate.c7
-rw-r--r--src/kitlist.c1
-rw-r--r--src/kitplayer.c243
15 files changed, 397 insertions, 94 deletions
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 <ass/ass.h>
+#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 <libavformat/avformat.h>
-#include <assert.h>
-
-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 <libavformat/avformat.h>
+#include <ass/ass.h>
+#include <assert.h>
+
+// 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 <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
@@ -11,8 +12,10 @@
#include <libavutil/pixfmt.h>
#include <libavutil/time.h>
#include <libavutil/samplefmt.h>
+#include "libavutil/avstring.h"
#include <SDL2/SDL.h>
+#include <ass/ass.h>
#include <stdlib.h>
#include <string.h>
@@ -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);
}
}