From eea30df7dbafd7301b1707bf5f69f22537ebda13 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Sat, 16 Jul 2011 03:36:28 +0000 Subject: [PATCH] Kill in-repo libass and ffms and clean up some old cruft in the configure script Originally committed to SVN as r5480. --- aegisub/LICENCE | 6 - aegisub/Makefile | 8 - aegisub/Makefile.inc.in | 27 +- aegisub/Makefile.target | 2 +- aegisub/configure.in | 224 +- aegisub/libass/COPYING | 11 - aegisub/libass/Makefile | 24 - aegisub/libass/ass.c | 1238 ------- aegisub/libass/ass.h | 383 -- aegisub/libass/ass_bitmap.c | 531 --- aegisub/libass/ass_bitmap.h | 56 - aegisub/libass/ass_cache.c | 385 -- aegisub/libass/ass_cache.h | 117 - aegisub/libass/ass_cache_template.h | 122 - aegisub/libass/ass_drawing.c | 492 --- aegisub/libass/ass_drawing.h | 80 - aegisub/libass/ass_font.c | 701 ---- aegisub/libass/ass_font.h | 66 - aegisub/libass/ass_fontconfig.c | 531 --- aegisub/libass/ass_fontconfig.h | 45 - aegisub/libass/ass_library.c | 146 - aegisub/libass/ass_library.h | 41 - aegisub/libass/ass_parse.c | 943 ----- aegisub/libass/ass_parse.h | 38 - aegisub/libass/ass_render.c | 2538 ------------- aegisub/libass/ass_render.h | 264 -- aegisub/libass/ass_render_api.c | 139 - aegisub/libass/ass_strtod.c | 249 -- aegisub/libass/ass_types.h | 123 - aegisub/libass/ass_utils.c | 206 -- aegisub/libass/ass_utils.h | 145 - aegisub/libass/config.h | 12 - aegisub/libass/help_mp.h | 55 - aegisub/libass/msvc/enca.h | 175 - aegisub/libass/msvc/inttypes.h | 305 -- aegisub/libass/msvc/stdint.h | 247 -- aegisub/libass/msvc/strings.h | 3 - aegisub/libass/msvc/unistd.h | 0 aegisub/libass/wscript | 26 - aegisub/libffms/Makefile | 30 - aegisub/libffms/include/ffms.h | 294 -- aegisub/libffms/include/ffmscompat.h | 85 - aegisub/libffms/src/core/audiosource.cpp | 304 -- aegisub/libffms/src/core/audiosource.h | 162 - aegisub/libffms/src/core/config.h | 0 aegisub/libffms/src/core/ffms.cpp | 469 --- aegisub/libffms/src/core/indexing.cpp | 603 ---- aegisub/libffms/src/core/indexing.h | 195 - aegisub/libffms/src/core/lavfaudio.cpp | 86 - aegisub/libffms/src/core/lavfindexer.cpp | 160 - aegisub/libffms/src/core/lavfvideo.cpp | 264 -- aegisub/libffms/src/core/matroskaaudio.cpp | 75 - aegisub/libffms/src/core/matroskaindexer.cpp | 160 - aegisub/libffms/src/core/matroskaparser.c | 3371 ------------------ aegisub/libffms/src/core/matroskaparser.h | 396 -- aegisub/libffms/src/core/matroskavideo.cpp | 234 -- aegisub/libffms/src/core/stdiostream.c | 116 - aegisub/libffms/src/core/stdiostream.h | 84 - aegisub/libffms/src/core/utils.cpp | 758 ---- aegisub/libffms/src/core/utils.h | 232 -- aegisub/libffms/src/core/videosource.cpp | 296 -- aegisub/libffms/src/core/videosource.h | 151 - aegisub/libffms/src/core/wave64writer.cpp | 112 - aegisub/libffms/src/core/wave64writer.h | 57 - aegisub/libffms/wscript | 26 - aegisub/src/Makefile | 12 +- 66 files changed, 27 insertions(+), 19409 deletions(-) delete mode 100644 aegisub/libass/COPYING delete mode 100644 aegisub/libass/Makefile delete mode 100644 aegisub/libass/ass.c delete mode 100644 aegisub/libass/ass.h delete mode 100644 aegisub/libass/ass_bitmap.c delete mode 100644 aegisub/libass/ass_bitmap.h delete mode 100644 aegisub/libass/ass_cache.c delete mode 100644 aegisub/libass/ass_cache.h delete mode 100644 aegisub/libass/ass_cache_template.h delete mode 100644 aegisub/libass/ass_drawing.c delete mode 100644 aegisub/libass/ass_drawing.h delete mode 100644 aegisub/libass/ass_font.c delete mode 100644 aegisub/libass/ass_font.h delete mode 100644 aegisub/libass/ass_fontconfig.c delete mode 100644 aegisub/libass/ass_fontconfig.h delete mode 100644 aegisub/libass/ass_library.c delete mode 100644 aegisub/libass/ass_library.h delete mode 100644 aegisub/libass/ass_parse.c delete mode 100644 aegisub/libass/ass_parse.h delete mode 100644 aegisub/libass/ass_render.c delete mode 100644 aegisub/libass/ass_render.h delete mode 100644 aegisub/libass/ass_render_api.c delete mode 100644 aegisub/libass/ass_strtod.c delete mode 100644 aegisub/libass/ass_types.h delete mode 100644 aegisub/libass/ass_utils.c delete mode 100644 aegisub/libass/ass_utils.h delete mode 100644 aegisub/libass/config.h delete mode 100644 aegisub/libass/help_mp.h delete mode 100644 aegisub/libass/msvc/enca.h delete mode 100644 aegisub/libass/msvc/inttypes.h delete mode 100644 aegisub/libass/msvc/stdint.h delete mode 100644 aegisub/libass/msvc/strings.h delete mode 100644 aegisub/libass/msvc/unistd.h delete mode 100644 aegisub/libass/wscript delete mode 100644 aegisub/libffms/Makefile delete mode 100644 aegisub/libffms/include/ffms.h delete mode 100644 aegisub/libffms/include/ffmscompat.h delete mode 100644 aegisub/libffms/src/core/audiosource.cpp delete mode 100644 aegisub/libffms/src/core/audiosource.h delete mode 100644 aegisub/libffms/src/core/config.h delete mode 100644 aegisub/libffms/src/core/ffms.cpp delete mode 100644 aegisub/libffms/src/core/indexing.cpp delete mode 100644 aegisub/libffms/src/core/indexing.h delete mode 100644 aegisub/libffms/src/core/lavfaudio.cpp delete mode 100644 aegisub/libffms/src/core/lavfindexer.cpp delete mode 100644 aegisub/libffms/src/core/lavfvideo.cpp delete mode 100644 aegisub/libffms/src/core/matroskaaudio.cpp delete mode 100644 aegisub/libffms/src/core/matroskaindexer.cpp delete mode 100644 aegisub/libffms/src/core/matroskaparser.c delete mode 100644 aegisub/libffms/src/core/matroskaparser.h delete mode 100644 aegisub/libffms/src/core/matroskavideo.cpp delete mode 100644 aegisub/libffms/src/core/stdiostream.c delete mode 100644 aegisub/libffms/src/core/stdiostream.h delete mode 100644 aegisub/libffms/src/core/utils.cpp delete mode 100644 aegisub/libffms/src/core/utils.h delete mode 100644 aegisub/libffms/src/core/videosource.cpp delete mode 100644 aegisub/libffms/src/core/videosource.h delete mode 100644 aegisub/libffms/src/core/wave64writer.cpp delete mode 100644 aegisub/libffms/src/core/wave64writer.h delete mode 100644 aegisub/libffms/wscript diff --git a/aegisub/LICENCE b/aegisub/LICENCE index db85fd642..0ae4666a7 100644 --- a/aegisub/LICENCE +++ b/aegisub/LICENCE @@ -33,12 +33,6 @@ covered by another license in the file itself. The following directories and file are covered by their respective licenses as follows: -libass/ - - ISC license. See libass/COPYING. - -libffms/ - - MIT license. - src/boost/ - Boost Software License Version 1.0 see src/boost/LICENSE_1_0.txt. diff --git a/aegisub/Makefile b/aegisub/Makefile index c12b44621..5244a7771 100644 --- a/aegisub/Makefile +++ b/aegisub/Makefile @@ -1,13 +1,5 @@ include Makefile.inc -ifeq (yes, $(WITH_LIBASS)) -SUBDIRS += libass -endif - -ifeq (yes, $(HAVE_PROVIDER_FFMPEGSOURCE)) -SUBDIRS += libffms -endif - SUBDIRS += \ universalchardet \ libaegisub \ diff --git a/aegisub/Makefile.inc.in b/aegisub/Makefile.inc.in index e26efc520..9795658ec 100644 --- a/aegisub/Makefile.inc.in +++ b/aegisub/Makefile.inc.in @@ -4,18 +4,15 @@ HAVE_ALSA = @with_alsa@ HAVE_AUTO4_LUA = @with_auto4_lua@ HAVE_AUTOMATION = @with_automation@ -HAVE_FFMPEG = @agi_cv_with_ffmpeg@ HAVE_HUNSPELL = @with_hunspell@ HAVE_OPENAL = @with_openal@ HAVE_OPENMP = @with_openmp@ HAVE_OSS = @with_oss@ HAVE_PORTAUDIO = @with_portaudio@ -HAVE_PROVIDER_FFMPEGSOURCE = @with_provider_ffmpegsource@ +HAVE_FFMS = @with_ffms@ HAVE_PULSEAUDIO = @with_pulseaudio@ -WITH_EXTERNAL_LIBASS = @with_external_libass@ -WITH_LIBASS = @with_libass@ +HAVE_LIBASS = @with_libass@ FOUND_AUDIO_PLAYER = @found_audio_player@ -FOUND_VIDEO_PROVIDER = @found_video_provider@ ################### @@ -93,18 +90,12 @@ CPPFLAGS_WX = @WX_CPPFLAGS@ CFLAGS_FONTCONFIG = @FONTCONFIG_CFLAGS@ CFLAGS_FREETYPE = @FREETYPE_CFLAGS@ CFLAGS_GL = @GL_CFLAGS@ -CFLAGS_FFMPEGSOURCE = -I../libffms/include +CFLAGS_FFMS = @FFMS_CFLAGS@ CFLAGS_HUNSPELL = @HUNSPELL_CFLAGS@ CFLAGS_ICONV = @ICONV_CFLAGS@ -CFLAGS_LIBASS = -I../libass -#CFLAGS_LIBASS = @LIBASS_CFLAGS@ -CFLAGS_LIBAVCODEC = @LIBAVCODEC_CFLAGS@ -CFLAGS_LIBAVFORMAT = @LIBAVFORMAT_CFLAGS@ -CFLAGS_LIBAVUTIL = @LIBAVUTIL_CFLAGS@ +CFLAGS_LIBASS = @LIBASS_CFLAGS@ CFLAGS_LIBCURL = @LIBCURL_CFLAGS@ -CFLAGS_LIBPOSTPROC = @LIBPOSTPROC_CFLAGS@ CFLAGS_LIBPULSE = @LIBPULSE_CFLAGS@ -CFLAGS_LIBSWSCALE = @LIBSWSCALE_CFLAGS@ CFLAGS_LUA = @LUA_CFLAGS@ CFLAGS_OPENAL = @OPENAL_CFLAGS@ CFLAGS_PORTAUDIO = @PORTAUDIO_CFLAGS@ @@ -116,18 +107,12 @@ LDFLAGS_ALSA = @ALSA_LDFLAGS@ LDFLAGS_FONTCONFIG = @FONTCONFIG_LIBS@ LDFLAGS_FREETYPE = @FREETYPE_LIBS@ LDFLAGS_GL = @GL_LIBS@ -LDFLAGS_FFMPEGSOURCE = ../libffms/libffmpegsource_aegisub.a +LDFLAGS_FFMS = @FFMS_LIBS@ LDFLAGS_HUNSPELL = @HUNSPELL_LIBS@ LDFLAGS_ICONV = @ICONV_LDFLAGS@ -LDFLAGS_LIBASS = ../libass/libass_aegisub.a -#LDFLAGS_LIBASS = @LIBASS_LIBS@ -LDFLAGS_LIBAVCODEC = @LIBAVCODEC_LIBS@ -LDFLAGS_LIBAVFORMAT = @LIBAVFORMAT_LIBS@ -LDFLAGS_LIBAVUTIL = @LIBAVUTIL_LIBS@ +LDFLAGS_LIBASS = @LIBASS_LIBS@ LDFLAGS_LIBCURL = @LIBCURL_LIBS@ -LDFLAGS_LIBPOSTPROC = @LIBPOSTPROC_LIBS@ LDFLAGS_LIBPULSE = @LIBPULSE_LIBS@ -LDFLAGS_LIBSWSCALE = @LIBSWSCALE_LIBS@ LDFLAGS_LUA = @LUA_LDFLAGS@ LDFLAGS_OPENAL = @OPENAL_LIBS@ LDFLAGS_PTHREAD = @PTHREAD_LIBS@ diff --git a/aegisub/Makefile.target b/aegisub/Makefile.target index e6e349312..e411122d0 100644 --- a/aegisub/Makefile.target +++ b/aegisub/Makefile.target @@ -121,7 +121,7 @@ $(SUBDIRS): $(MAKE) -C $@ $(MAKECMDGOALS) # Set relations to ensure dependencies are built before their targets during parallel builds. -src: universalchardet libass libffms tools libaegisub +src: universalchardet tools libaegisub tests: libaegisub reporter: src command: libresrc diff --git a/aegisub/configure.in b/aegisub/configure.in index 8b89a097b..9eaf9f4f9 100644 --- a/aegisub/configure.in +++ b/aegisub/configure.in @@ -16,12 +16,6 @@ m4_define([aegisub_version_data], [aegisub_version_major.aegisub_version_minor]) ################### # Required packages ################### -m4_define([libavcodec_required_version], [52.27.0]) # (r18642) -m4_define([libavformat_required_version], [52.32.0]) # (r18642) -m4_define([libavutil_required_version], [50.3.0]) # (r18642) -m4_define([libpostproc_required_version], [51.2.0]) # (r18642) -m4_define([libswscale_required_version], [0.7.1]) # (r18642) - m4_define([lua_auto4_required_version], [5.1]) m4_define([portaudio_required_version], [19]) m4_define([pulseaudio_required_version], [0.5]) @@ -31,6 +25,7 @@ m4_define([freetype_required_version], [9.7.0]) m4_define([pkgconfig_required_version], [0.20]) m4_define([wx_required_version], [2.9.0]) m4_define([libass_required_version], [0.9.7]) +m4_define([ffms_required_version], [2.1.15]) ####### @@ -681,154 +676,24 @@ AC_SUBST(with_oss) ######################### # Video / Audio Providers ######################### +AC_ARG_WITH(ffms,[ --without-ffms build without ffms2 A/V provider. (default: auto)], ffms_disabled="(disabled)") -AC_ARG_WITH(ffmpeg, [ --without-ffmpeg build without FFMPEG support. - Disables FFMPEG and FFmpegSource A/V providers. - (default: auto)], [ffmpeg_disabled="(disabled)"; with_ffmpeg="no"]) - -if test "$with_ffmpeg" != "no"; then - PKG_CHECK_MODULES(LIBAVCODEC, libavcodec >= libavcodec_required_version, [], [with_ffmpeg="no"]) - PKG_CHECK_MODULES(LIBAVFORMAT, libavformat >= libavformat_required_version, [], [with_ffmpeg="no"]) - PKG_CHECK_MODULES(LIBSWSCALE, libswscale >= libswscale_required_version, [], [with_ffmpeg="no"]) - PKG_CHECK_MODULES(LIBAVUTIL, libavutil >= libavutil_required_version, [], [with_ffmpeg="no"]) +if test "$with_ffms" != "no"; then + PKG_CHECK_MODULES(FFMS, ffms2 >= ffms_required_version, [with_ffms="yes"], [with_ffms="no"]) fi -if test "$with_ffmpeg" != "no" && test "$enable_old_ffmpeg" != "yes"; then - AC_AGI_COMPILE([FFMPEG], [ffmpeg], [$LIBSWSCALE_CFLAGS $LIBAVCODEC_CFLAGS $LIBAVFORMAT_CFLAGS $LIBAVUTIL_CFLAGS], [$LIBSWSCALE_LIBS $LIBAVCODEC_LIBS $LIBAVFORMAT_LIBS $LIBAVUTIL_LIBS],[ -#define __STDC_CONSTANT_MACROS -extern "C" { -#include -#include -#include -} - -int main (void) { - AVFormatContext *fc = NULL; - AVCodec *c = NULL; - SwsContext *swsc = NULL; - - av_register_all(); - fc = avformat_alloc_context(); - if (fc == NULL) { - printf("avformat failure\n"); - return 1; - } - av_free(fc); - - avcodec_init(); - avcodec_register_all(); - c = avcodec_find_decoder(CODEC_ID_PCM_S16LE); - if (c == NULL) { - printf("avcodec failure\n"); - return 1; - } - - swsc = sws_getContext(704, 480, PIX_FMT_RGB32, 704, 480, PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL); - if (swsc == NULL) { - printf("swscale failure\n"); - return 1; - } - sws_freeContext(swsc); - - return 0; -} ]) -fi - -if test "$agi_cv_with_ffmpeg" = "no" && test "$with_ffmpeg" != "no"; then - AC_MSG_WARN([FFMPEG detected, but it doesn't work...]) - with_ffmpeg="no" -fi - -if test "$agi_cv_with_ffmpeg" = "yes" && test "$with_ffmpeg" != "no"; then - with_ffmpeg="yes" -fi - -if test "$agi_cv_with_ffmpeg" = "no" || test "$with_ffmpeg" = "no"; then - ffmpegsource_provider_disabled="(disabled, requires FFMPEG)" - ffmpeg_provider_disabled="(disabled, requires FFMPEG)" - with_ffmpeg="no" -fi - -AC_SUBST(agi_cv_with_ffmpeg) -AC_SUBST(LIBAVFORMAT_LIBS) -AC_SUBST(LIBAVFORMAT_CFLAGS) -AC_SUBST(LIBAVCODEC_LIBS) -AC_SUBST(LIBAVCODEC_CFLAGS) -AC_SUBST(LIBSWSCALE_LIBS) -AC_SUBST(LIBSWSCALE_CFLAGS) -AC_SUBST(LIBAVUTIL_LIBS) -AC_SUBST(LIBAVUTIL_CFLAGS) - -AC_ARG_WITH(provider-ffmpegsource, [ --without-provider-ffmpegsource - build without FFmpegSource A/V provider. - (default: auto)], ffmpegsource_provider_disabled="(disabled)", with_provider_ffmpegsource="yes") - -if test "$agi_cv_with_ffmpeg" = "yes" && test "$with_provider_ffmpegsource" = "yes"; then - PKG_CHECK_MODULES(LIBPOSTPROC, libpostproc >= libpostproc_required_version, [], [with_libpostproc="no"]) - - AC_AGI_COMPILE([postproc], [postproc], [$LIBPOSTPROC_CFLAGS], [$LIBPOSTPROC_LIBS],[ -extern "C" { -#define __STDC_CONSTANT_MACROS -#include -} - -int main (void) { - pp_context_t *PPContext = pp_get_context(704,480, 0); - - if (!PPContext) return 1; - pp_free_context(PPContext); - return 0; -} ]) - - if test "$agi_cv_with_postproc" = "yes"; then - found_video_provider="yes" - AC_DEFINE(WITH_FFMPEGSOURCE, 1, [Enable FFmpegSource2 Video Provider]) - else - with_provider_ffmpegsource="no" - fi -else - with_provider_ffmpegsource="no" -fi - -AC_SUBST(with_provider_ffmpegsource) -AC_SUBST(LIBPOSTPROC_LIBS) -AC_SUBST(LIBPOSTPROC_CFLAGS) - +AC_SUBST(with_ffms) ################### # Subtitle Provider ################### +AC_ARG_WITH(libass,[ --without-libass build without libass support (default: auto)], libass_disabled="(disabled)") -AC_ARG_ENABLE(libass, [ --disable-libass disable libass support (default=enabled)], libass_disabled="(disabled)") -AC_ARG_WITH(external-libass, [ --with-external-libass link to external libass (default=use internal)]) - -if test "$enable_libass" != "no"; then - if test "$with_external_libass" != "yes"; then - with_external_libass="no" - - if test "$agi_cv_with_iconv" = "yes"; then - LIBASS_LIBS="-L../libass -lass_aegisub" - LIBASS_CFLAGS="-I../libass" - with_libass="yes" - else - AC_MSG_WARN([libiconv is required for libass support.]) - with_libass="no" - fi - else - PKG_CHECK_MODULES(LIBASS, libass >= libass_required_version, [with_libass="yes"], [with_libass="no"]) - fi -fi - -if test "$with_libass" = "yes" || test "$enable_libass" != "no"; then - AC_DEFINE(WITH_LIBASS, 1, [Enable libass Subtitle Provider]) -else - with_libass="no" +if test "$with_libass" != "no"; then + PKG_CHECK_MODULES(LIBASS, libass >= libass_required_version, [with_libass="yes"], [with_libass="no"]) fi AC_SUBST(with_libass) -AC_SUBST(with_external_libass) -AC_SUBST(LIBASS_LIBS) -AC_SUBST(LIBASS_CFLAGS) ########### @@ -841,10 +706,7 @@ if test "$with_hunspell" != "no"; then AC_AGI_COMPILE([Hunspell], [hunspell], [$HUNSPELL_CFLAGS], [$HUNSPELL_LIBS],[ #include int main(void) { - Hunspell *hunspell; - hunspell = new Hunspell(".", "."); - if (!hunspell) return 1; - return 0; + return !(new Hunspell(".", ".")); } ]) fi @@ -1163,44 +1025,10 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", # it above. #################################################################### -AC_ARG_WITH(provider-video, [ --with-provider-video=(ffmpegsource) - Default Video Provider. (default: ffmpegsource)]) -AC_ARG_WITH(provider-audio, [ --with-provider-audio=(ffmpegsource) - Default Audio Provider. (default: ffmpegsource)]) AC_ARG_WITH(player-audio, [ --with-player-audio=(alsa|openal|portaudio|pulseaudio) Default Audio Player (default: Linux/ALSA, Darwin/OpenAL, 1:*/OSS, 2:*/PortAudio.]) -# Default Video Provider. -if ! test -z "$with_provider_video"; then - default_provider_video="$with_provider_video" -else - if test "$with_provider_ffmpegsource" = "yes"; then - default_provider_video="ffmpegsource" - fi -fi -AC_DEFINE_UNQUOTED([DEFAULT_PROVIDER_VIDEO], ["$default_provider_video"], [Default Video Provider.]) - -# Default Audio Provider. -if ! test -z "$with_provider_audio"; then - default_provider_audio="$with_provider_audio" -else - if test "$with_provider_ffmpegsource" = "yes"; then - default_provider_audio="ffmpegsource" - fi -fi -AC_DEFINE_UNQUOTED([DEFAULT_PROVIDER_AUDIO], ["$default_provider_audio"], [Default Video Provider.]) - -# Default Subtitle Provider. -if ! test -z "$with_provider_subtitle"; then - default_provider_subtitle="$with_provider_subtitle" -else - if test "$with_libass" = "yes"; then - default_provider_subtitle="libass" - fi -fi -AC_DEFINE_UNQUOTED([DEFAULT_PROVIDER_SUBTITLE], ["$default_provider_subtitle"], [Default Subtitle Provider.]) - # Default audio player. if ! test -z "$with_player_audio"; then default_player_audio="$with_player_audio" @@ -1221,31 +1049,15 @@ AC_DEFINE_UNQUOTED([DEFAULT_PLAYER_AUDIO], ["$default_player_audio"], [Default a # Set some friendly strings if some of the above aren't detected. -if test -z "$default_provider_video"; then - default_provider_video="NONE (requires ffmpeg)" -fi - -if test -z "$default_provider_audio"; then - default_provider_audio="NONE (requires ffmpeg)" -fi - -if test -z "$default_provider_subtitle"; then - default_provider_subtitle="NONE" -fi - if test -z "$default_player_audio"; then default_player_audio="NONE" fi - - ############### # Misc settings ############### AC_SUBST(found_audio_player) -AC_SUBST(found_audio_provider) -AC_SUBST(found_video_provider) # Files that need substitution. AC_CONFIG_FILES([ @@ -1273,7 +1085,7 @@ if test -z "$found_audio_player"; then ]) fi -if test -z "$found_video_provider"; then +if test "$with_ffms" != "yes"; then AC_MSG_NOTICE([ *********************************************************************** @@ -1284,8 +1096,8 @@ if test -z "$found_video_provider"; then * virtual video clip with subtitles overlaid. * Currently we only support one video/audio provider on non-Windows * systems: -* - FFmpeg (libavcodec + libavformat) -* * http://ffmpeg.mplayerhq.hu/ +* - FFMS2 +* * http://code.google.com/p/ffmpegsource/ *********************************************************************** ]) fi @@ -1300,9 +1112,6 @@ Configure settings LDFLAGS $LDFLAGS Default Settings - Video Provider: $default_provider_video - Audio Provider: $default_provider_audio - Subtitle Provider: $default_provider_subtitle Audio Player: $default_player_audio Scripting Engines @@ -1315,16 +1124,11 @@ Audio Players PortAudio: $with_portaudio $portaudio_disabled PulseAudio: $with_pulseaudio $pulseaudio_disabled -A/V Support - FFMPEG: $with_ffmpeg $ffmpeg_disabled - (required for video providers) - A/V Providers - FFmpegSource: $with_provider_ffmpegsource $ffmpegsource_provider_disabled + FFMS2: $with_ffms $ffms_disabled Subtitle Providers: - libASS $with_libass $libass_disabled $libass_default - (both require iconv and fontconfig) + libass $with_libass $libass_disabled Misc Packages Hunspell: $with_hunspell $with_hunspell_version $hunspell_disabled diff --git a/aegisub/libass/COPYING b/aegisub/libass/COPYING deleted file mode 100644 index 8351a30e3..000000000 --- a/aegisub/libass/COPYING +++ /dev/null @@ -1,11 +0,0 @@ -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/aegisub/libass/Makefile b/aegisub/libass/Makefile deleted file mode 100644 index fd44720b6..000000000 --- a/aegisub/libass/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -include ../Makefile.inc - -CXXFLAGS += -DCONFIG_ICONV -DCONFIG_FONTCONFIG $(CFLAGS_FREETYPE) $(CFLAGS_ICONV) $(CFLAGS_FONTCONFIG) - -LIB = libass_aegisub.a - -SRC = \ - ass.c \ - ass_bitmap.c \ - ass_cache.c \ - ass_drawing.c \ - ass_font.c \ - ass_fontconfig.c \ - ass_library.c \ - ass_parse.c \ - ass_render.c \ - ass_render_api.c \ - ass_strtod.c \ - ass_utils.c - -HEADER = *.h - -include ../Makefile.target --include *.d diff --git a/aegisub/libass/ass.c b/aegisub/libass/ass.c deleted file mode 100644 index 368377251..000000000 --- a/aegisub/libass/ass.c +++ /dev/null @@ -1,1238 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_ICONV -#include -#endif - -#include "ass.h" -#include "ass_utils.h" -#include "ass_library.h" - -#define ass_atof(STR) (ass_strtod((STR),NULL)) - -typedef enum { - PST_UNKNOWN = 0, - PST_INFO, - PST_STYLES, - PST_EVENTS, - PST_FONTS -} ParserState; - -struct parser_priv { - ParserState state; - char *fontname; - char *fontdata; - int fontdata_size; - int fontdata_used; -}; - -#define ASS_STYLES_ALLOC 20 -#define ASS_EVENTS_ALLOC 200 - -void ass_free_track(ASS_Track *track) -{ - int i; - - if (track->parser_priv) { - free(track->parser_priv->fontname); - free(track->parser_priv->fontdata); - free(track->parser_priv); - } - free(track->style_format); - free(track->event_format); - if (track->styles) { - for (i = 0; i < track->n_styles; ++i) - ass_free_style(track, i); - } - free(track->styles); - if (track->events) { - for (i = 0; i < track->n_events; ++i) - ass_free_event(track, i); - } - free(track->events); - free(track->name); - free(track); -} - -/// \brief Allocate a new style struct -/// \param track track -/// \return style id -int ass_alloc_style(ASS_Track *track) -{ - int sid; - - assert(track->n_styles <= track->max_styles); - - if (track->n_styles == track->max_styles) { - track->max_styles += ASS_STYLES_ALLOC; - track->styles = - (ASS_Style *) realloc(track->styles, - sizeof(ASS_Style) * - track->max_styles); - } - - sid = track->n_styles++; - memset(track->styles + sid, 0, sizeof(ASS_Style)); - return sid; -} - -/// \brief Allocate a new event struct -/// \param track track -/// \return event id -int ass_alloc_event(ASS_Track *track) -{ - int eid; - - assert(track->n_events <= track->max_events); - - if (track->n_events == track->max_events) { - track->max_events += ASS_EVENTS_ALLOC; - track->events = - (ASS_Event *) realloc(track->events, - sizeof(ASS_Event) * - track->max_events); - } - - eid = track->n_events++; - memset(track->events + eid, 0, sizeof(ASS_Event)); - return eid; -} - -void ass_free_event(ASS_Track *track, int eid) -{ - ASS_Event *event = track->events + eid; - - free(event->Name); - free(event->Effect); - free(event->Text); - free(event->render_priv); -} - -void ass_free_style(ASS_Track *track, int sid) -{ - ASS_Style *style = track->styles + sid; - - free(style->Name); - free(style->FontName); -} - -// ============================================================================================== - -static void skip_spaces(char **str) -{ - char *p = *str; - while ((*p == ' ') || (*p == '\t')) - ++p; - *str = p; -} - -static void rskip_spaces(char **str, char *limit) -{ - char *p = *str; - while ((p >= limit) && ((*p == ' ') || (*p == '\t'))) - --p; - *str = p; -} - -/** - * \brief find style by name - * \param track track - * \param name style name - * \return index in track->styles - * Returnes 0 if no styles found => expects at least 1 style. - * Parsing code always adds "Default" style in the end. - */ -static int lookup_style(ASS_Track *track, char *name) -{ - int i; - if (*name == '*') - ++name; // FIXME: what does '*' really mean ? - for (i = track->n_styles - 1; i >= 0; --i) { - // FIXME: mb strcasecmp ? - if (strcmp(track->styles[i].Name, name) == 0) - return i; - } - i = track->default_style; - ass_msg(track->library, MSGL_WARN, - "[%p]: Warning: no style named '%s' found, using '%s'", - track, name, track->styles[i].Name); - return i; // use the first style -} - -static uint32_t string2color(ASS_Library *library, char *p) -{ - uint32_t tmp; - (void) strtocolor(library, &p, &tmp, 0); - return tmp; -} - -static long long string2timecode(ASS_Library *library, char *p) -{ - unsigned h, m, s, ms; - long long tm; - int res = sscanf(p, "%1d:%2d:%2d.%2d", &h, &m, &s, &ms); - if (res < 4) { - ass_msg(library, MSGL_WARN, "Bad timestamp"); - return 0; - } - tm = ((h * 60 + m) * 60 + s) * 1000 + ms * 10; - return tm; -} - -/** - * \brief converts numpad-style align to align. - */ -static int numpad2align(int val) -{ - int res, v; - v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment - if (v != 0) - v = 3 - v; - res = ((val - 1) % 3) + 1; // horizontal alignment - res += v * 4; - return res; -} - -#define NEXT(str,token) \ - token = next_token(&str); \ - if (!token) break; - -#define ANYVAL(name,func) \ - } else if (strcasecmp(tname, #name) == 0) { \ - target->name = func(token); \ - ass_msg(track->library, MSGL_DBG2, "%s = %s", #name, token); - -#define STRVAL(name) \ - } else if (strcasecmp(tname, #name) == 0) { \ - if (target->name != NULL) free(target->name); \ - target->name = strdup(token); \ - ass_msg(track->library, MSGL_DBG2, "%s = %s", #name, token); - -#define COLORVAL(name) \ - } else if (strcasecmp(tname, #name) == 0) { \ - target->name = string2color(track->library, token); \ - ass_msg(track->library, MSGL_DBG2, "%s = %s", #name, token); - -#define INTVAL(name) ANYVAL(name,atoi) -#define FPVAL(name) ANYVAL(name,ass_atof) -#define TIMEVAL(name) \ - } else if (strcasecmp(tname, #name) == 0) { \ - target->name = string2timecode(track->library, token); \ - ass_msg(track->library, MSGL_DBG2, "%s = %s", #name, token); - -#define STYLEVAL(name) \ - } else if (strcasecmp(tname, #name) == 0) { \ - target->name = lookup_style(track, token); \ - ass_msg(track->library, MSGL_DBG2, "%s = %s", #name, token); - -#define ALIAS(alias,name) \ - if (strcasecmp(tname, #alias) == 0) {tname = #name;} - -static char *next_token(char **str) -{ - char *p = *str; - char *start; - skip_spaces(&p); - if (*p == '\0') { - *str = p; - return 0; - } - start = p; // start of the token - for (; (*p != '\0') && (*p != ','); ++p) { - } - if (*p == '\0') { - *str = p; // eos found, str will point to '\0' at exit - } else { - *p = '\0'; - *str = p + 1; // ',' found, str will point to the next char (beginning of the next token) - } - --p; // end of current token - rskip_spaces(&p, start); - if (p < start) - p = start; // empty token - else - ++p; // the first space character, or '\0' - *p = '\0'; - return start; -} - -/** - * \brief Parse the tail of Dialogue line - * \param track track - * \param event parsed data goes here - * \param str string to parse, zero-terminated - * \param n_ignored number of format options to skip at the beginning -*/ -static int process_event_tail(ASS_Track *track, ASS_Event *event, - char *str, int n_ignored) -{ - char *token; - char *tname; - char *p = str; - int i; - ASS_Event *target = event; - - char *format = strdup(track->event_format); - char *q = format; // format scanning pointer - - if (track->n_styles == 0) { - // add "Default" style to the end - // will be used if track does not contain a default style (or even does not contain styles at all) - int sid = ass_alloc_style(track); - track->styles[sid].Name = strdup("Default"); - track->styles[sid].FontName = strdup("Arial"); - } - - for (i = 0; i < n_ignored; ++i) { - NEXT(q, tname); - } - - while (1) { - NEXT(q, tname); - if (strcasecmp(tname, "Text") == 0) { - char *last; - event->Text = strdup(p); - if (*event->Text != 0) { - last = event->Text + strlen(event->Text) - 1; - if (last >= event->Text && *last == '\r') - *last = 0; - } - ass_msg(track->library, MSGL_DBG2, "Text = %s", event->Text); - event->Duration -= event->Start; - free(format); - return 0; // "Text" is always the last - } - NEXT(p, token); - - ALIAS(End, Duration) // temporarily store end timecode in event->Duration - if (0) { // cool ;) - INTVAL(Layer) - STYLEVAL(Style) - STRVAL(Name) - STRVAL(Effect) - INTVAL(MarginL) - INTVAL(MarginR) - INTVAL(MarginV) - TIMEVAL(Start) - TIMEVAL(Duration) - } - } - free(format); - return 1; -} - -/** - * \brief Parse command line style overrides (--ass-force-style option) - * \param track track to apply overrides to - * The format for overrides is [StyleName.]Field=Value - */ -void ass_process_force_style(ASS_Track *track) -{ - char **fs, *eq, *dt, *style, *tname, *token; - ASS_Style *target; - int sid; - char **list = track->library->style_overrides; - - if (!list) - return; - - for (fs = list; *fs; ++fs) { - eq = strrchr(*fs, '='); - if (!eq) - continue; - *eq = '\0'; - token = eq + 1; - - if (!strcasecmp(*fs, "PlayResX")) - track->PlayResX = atoi(token); - else if (!strcasecmp(*fs, "PlayResY")) - track->PlayResY = atoi(token); - else if (!strcasecmp(*fs, "Timer")) - track->Timer = ass_atof(token); - else if (!strcasecmp(*fs, "WrapStyle")) - track->WrapStyle = atoi(token); - else if (!strcasecmp(*fs, "ScaledBorderAndShadow")) - track->ScaledBorderAndShadow = parse_bool(token); - else if (!strcasecmp(*fs, "Kerning")) - track->Kerning = parse_bool(token); - - dt = strrchr(*fs, '.'); - if (dt) { - *dt = '\0'; - style = *fs; - tname = dt + 1; - } else { - style = NULL; - tname = *fs; - } - for (sid = 0; sid < track->n_styles; ++sid) { - if (style == NULL - || strcasecmp(track->styles[sid].Name, style) == 0) { - target = track->styles + sid; - if (0) { - STRVAL(FontName) - COLORVAL(PrimaryColour) - COLORVAL(SecondaryColour) - COLORVAL(OutlineColour) - COLORVAL(BackColour) - FPVAL(FontSize) - INTVAL(Bold) - INTVAL(Italic) - INTVAL(Underline) - INTVAL(StrikeOut) - FPVAL(Spacing) - INTVAL(Angle) - INTVAL(BorderStyle) - INTVAL(Alignment) - INTVAL(MarginL) - INTVAL(MarginR) - INTVAL(MarginV) - INTVAL(Encoding) - FPVAL(ScaleX) - FPVAL(ScaleY) - FPVAL(Outline) - FPVAL(Shadow) - } - } - } - *eq = '='; - if (dt) - *dt = '.'; - } -} - -/** - * \brief Parse the Style line - * \param track track - * \param str string to parse, zero-terminated - * Allocates a new style struct. -*/ -static int process_style(ASS_Track *track, char *str) -{ - - char *token; - char *tname; - char *p = str; - char *format; - char *q; // format scanning pointer - int sid; - ASS_Style *style; - ASS_Style *target; - - if (!track->style_format) { - // no style format header - // probably an ancient script version - if (track->track_type == TRACK_TYPE_SSA) - track->style_format = - strdup - ("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour," - "TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline," - "Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding"); - else - track->style_format = - strdup - ("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour," - "OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut," - "ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow," - "Alignment, MarginL, MarginR, MarginV, Encoding"); - } - - q = format = strdup(track->style_format); - - ass_msg(track->library, MSGL_V, "[%p] Style: %s", track, str); - - sid = ass_alloc_style(track); - - style = track->styles + sid; - target = style; - - // fill style with some default values - style->ScaleX = 100.; - style->ScaleY = 100.; - - while (1) { - NEXT(q, tname); - NEXT(p, token); - - if (0) { // cool ;) - STRVAL(Name) - if ((strcmp(target->Name, "Default") == 0) - || (strcmp(target->Name, "*Default") == 0)) - track->default_style = sid; - STRVAL(FontName) - COLORVAL(PrimaryColour) - COLORVAL(SecondaryColour) - COLORVAL(OutlineColour) // TertiaryColor - COLORVAL(BackColour) - // SSA uses BackColour for both outline and shadow - // this will destroy SSA's TertiaryColour, but i'm not going to use it anyway - if (track->track_type == TRACK_TYPE_SSA) - target->OutlineColour = target->BackColour; - FPVAL(FontSize) - INTVAL(Bold) - INTVAL(Italic) - INTVAL(Underline) - INTVAL(StrikeOut) - FPVAL(Spacing) - INTVAL(Angle) - INTVAL(BorderStyle) - INTVAL(Alignment) - if (track->track_type == TRACK_TYPE_ASS) - target->Alignment = numpad2align(target->Alignment); - INTVAL(MarginL) - INTVAL(MarginR) - INTVAL(MarginV) - INTVAL(Encoding) - FPVAL(ScaleX) - FPVAL(ScaleY) - FPVAL(Outline) - FPVAL(Shadow) - } - } - style->ScaleX /= 100.; - style->ScaleY /= 100.; - style->Bold = !!style->Bold; - style->Italic = !!style->Italic; - style->Underline = !!style->Underline; - if (!style->Name) - style->Name = strdup("Default"); - if (!style->FontName) - style->FontName = strdup("Arial"); - free(format); - return 0; - -} - -static int process_styles_line(ASS_Track *track, char *str) -{ - if (!strncmp(str, "Format:", 7)) { - char *p = str + 7; - skip_spaces(&p); - track->style_format = strdup(p); - ass_msg(track->library, MSGL_DBG2, "Style format: %s", - track->style_format); - } else if (!strncmp(str, "Style:", 6)) { - char *p = str + 6; - skip_spaces(&p); - process_style(track, p); - } - return 0; -} - -static int process_info_line(ASS_Track *track, char *str) -{ - if (!strncmp(str, "PlayResX:", 9)) { - track->PlayResX = atoi(str + 9); - } else if (!strncmp(str, "PlayResY:", 9)) { - track->PlayResY = atoi(str + 9); - } else if (!strncmp(str, "Timer:", 6)) { - track->Timer = ass_atof(str + 6); - } else if (!strncmp(str, "WrapStyle:", 10)) { - track->WrapStyle = atoi(str + 10); - } else if (!strncmp(str, "ScaledBorderAndShadow:", 22)) { - track->ScaledBorderAndShadow = parse_bool(str + 22); - } else if (!strncmp(str, "Kerning:", 8)) { - track->Kerning = parse_bool(str + 8); - } - return 0; -} - -static void event_format_fallback(ASS_Track *track) -{ - track->parser_priv->state = PST_EVENTS; - if (track->track_type == TRACK_TYPE_SSA) - track->event_format = strdup("Format: Marked, Start, End, Style, " - "Name, MarginL, MarginR, MarginV, Effect, Text"); - else - track->event_format = strdup("Format: Layer, Start, End, Style, " - "Actor, MarginL, MarginR, MarginV, Effect, Text"); - ass_msg(track->library, MSGL_V, - "No event format found, using fallback"); -} - -static int process_events_line(ASS_Track *track, char *str) -{ - if (!strncmp(str, "Format:", 7)) { - char *p = str + 7; - skip_spaces(&p); - free(track->event_format); - track->event_format = strdup(p); - ass_msg(track->library, MSGL_DBG2, "Event format: %s", track->event_format); - } else if (!strncmp(str, "Dialogue:", 9)) { - // This should never be reached for embedded subtitles. - // They have slightly different format and are parsed in ass_process_chunk, - // called directly from demuxer - int eid; - ASS_Event *event; - - str += 9; - skip_spaces(&str); - - eid = ass_alloc_event(track); - event = track->events + eid; - - // We can't parse events with event_format - if (!track->event_format) - event_format_fallback(track); - - process_event_tail(track, event, str, 0); - } else { - ass_msg(track->library, MSGL_V, "Not understood: '%.30s'", str); - } - return 0; -} - -// Copied from mkvtoolnix -static unsigned char *decode_chars(unsigned char c1, unsigned char c2, - unsigned char c3, unsigned char c4, - unsigned char *dst, int cnt) -{ - uint32_t value; - unsigned char bytes[3]; - int i; - - value = - ((c1 - 33) << 18) + ((c2 - 33) << 12) + ((c3 - 33) << 6) + (c4 - - 33); - bytes[2] = value & 0xff; - bytes[1] = (value & 0xff00) >> 8; - bytes[0] = (value & 0xff0000) >> 16; - - for (i = 0; i < cnt; ++i) - *dst++ = bytes[i]; - return dst; -} - -static int decode_font(ASS_Track *track) -{ - unsigned char *p; - unsigned char *q; - int i; - int size; // original size - int dsize; // decoded size - unsigned char *buf = 0; - - ass_msg(track->library, MSGL_V, "Font: %d bytes encoded data", - track->parser_priv->fontdata_used); - size = track->parser_priv->fontdata_used; - if (size % 4 == 1) { - ass_msg(track->library, MSGL_ERR, "Bad encoded data size"); - goto error_decode_font; - } - buf = malloc(size / 4 * 3 + 2); - q = buf; - for (i = 0, p = (unsigned char *) track->parser_priv->fontdata; - i < size / 4; i++, p += 4) { - q = decode_chars(p[0], p[1], p[2], p[3], q, 3); - } - if (size % 4 == 2) { - q = decode_chars(p[0], p[1], 0, 0, q, 1); - } else if (size % 4 == 3) { - q = decode_chars(p[0], p[1], p[2], 0, q, 2); - } - dsize = q - buf; - assert(dsize <= size / 4 * 3 + 2); - - if (track->library->extract_fonts) { - ass_add_font(track->library, track->parser_priv->fontname, - (char *) buf, dsize); - } - -error_decode_font: - free(buf); - free(track->parser_priv->fontname); - free(track->parser_priv->fontdata); - track->parser_priv->fontname = 0; - track->parser_priv->fontdata = 0; - track->parser_priv->fontdata_size = 0; - track->parser_priv->fontdata_used = 0; - return 0; -} - -static int process_fonts_line(ASS_Track *track, char *str) -{ - int len; - - if (!strncmp(str, "fontname:", 9)) { - char *p = str + 9; - skip_spaces(&p); - if (track->parser_priv->fontname) { - decode_font(track); - } - track->parser_priv->fontname = strdup(p); - ass_msg(track->library, MSGL_V, "Fontname: %s", - track->parser_priv->fontname); - return 0; - } - - if (!track->parser_priv->fontname) { - ass_msg(track->library, MSGL_V, "Not understood: '%s'", str); - return 0; - } - - len = strlen(str); - if (len > 80) { - ass_msg(track->library, MSGL_WARN, "Font line too long: %d, %s", - len, str); - return 0; - } - if (track->parser_priv->fontdata_used + len > - track->parser_priv->fontdata_size) { - track->parser_priv->fontdata_size += 100 * 1024; - track->parser_priv->fontdata = - realloc(track->parser_priv->fontdata, - track->parser_priv->fontdata_size); - } - memcpy(track->parser_priv->fontdata + track->parser_priv->fontdata_used, - str, len); - track->parser_priv->fontdata_used += len; - - return 0; -} - -/** - * \brief Parse a header line - * \param track track - * \param str string to parse, zero-terminated -*/ -static int process_line(ASS_Track *track, char *str) -{ - if (!strncasecmp(str, "[Script Info]", 13)) { - track->parser_priv->state = PST_INFO; - } else if (!strncasecmp(str, "[V4 Styles]", 11)) { - track->parser_priv->state = PST_STYLES; - track->track_type = TRACK_TYPE_SSA; - } else if (!strncasecmp(str, "[V4+ Styles]", 12)) { - track->parser_priv->state = PST_STYLES; - track->track_type = TRACK_TYPE_ASS; - } else if (!strncasecmp(str, "[Events]", 8)) { - track->parser_priv->state = PST_EVENTS; - } else if (!strncasecmp(str, "[Fonts]", 7)) { - track->parser_priv->state = PST_FONTS; - } else { - switch (track->parser_priv->state) { - case PST_INFO: - process_info_line(track, str); - break; - case PST_STYLES: - process_styles_line(track, str); - break; - case PST_EVENTS: - process_events_line(track, str); - break; - case PST_FONTS: - process_fonts_line(track, str); - break; - default: - break; - } - } - - // there is no explicit end-of-font marker in ssa/ass - if ((track->parser_priv->state != PST_FONTS) - && (track->parser_priv->fontname)) - decode_font(track); - - return 0; -} - -static int process_text(ASS_Track *track, char *str) -{ - char *p = str; - while (1) { - char *q; - while (1) { - if ((*p == '\r') || (*p == '\n')) - ++p; - else if (p[0] == '\xef' && p[1] == '\xbb' && p[2] == '\xbf') - p += 3; // U+FFFE (BOM) - else - break; - } - for (q = p; ((*q != '\0') && (*q != '\r') && (*q != '\n')); ++q) { - }; - if (q == p) - break; - if (*q != '\0') - *(q++) = '\0'; - process_line(track, p); - if (*q == '\0') - break; - p = q; - } - return 0; -} - -/** - * \brief Process a chunk of subtitle stream data. - * \param track track - * \param data string to parse - * \param size length of data -*/ -void ass_process_data(ASS_Track *track, char *data, int size) -{ - char *str = malloc(size + 1); - - memcpy(str, data, size); - str[size] = '\0'; - - ass_msg(track->library, MSGL_V, "Event: %s", str); - process_text(track, str); - free(str); -} - -/** - * \brief Process CodecPrivate section of subtitle stream - * \param track track - * \param data string to parse - * \param size length of data - CodecPrivate section contains [Stream Info] and [V4+ Styles] ([V4 Styles] for SSA) sections -*/ -void ass_process_codec_private(ASS_Track *track, char *data, int size) -{ - ass_process_data(track, data, size); - - // probably an mkv produced by ancient mkvtoolnix - // such files don't have [Events] and Format: headers - if (!track->event_format) - event_format_fallback(track); - - ass_process_force_style(track); -} - -static int check_duplicate_event(ASS_Track *track, int ReadOrder) -{ - int i; - for (i = 0; i < track->n_events - 1; ++i) // ignoring last event, it is the one we are comparing with - if (track->events[i].ReadOrder == ReadOrder) - return 1; - return 0; -} - -/** - * \brief Process a chunk of subtitle stream data. In Matroska, this contains exactly 1 event (or a commentary). - * \param track track - * \param data string to parse - * \param size length of data - * \param timecode starting time of the event (milliseconds) - * \param duration duration of the event (milliseconds) -*/ -void ass_process_chunk(ASS_Track *track, char *data, int size, - long long timecode, long long duration) -{ - char *str; - int eid; - char *p; - char *token; - ASS_Event *event; - - if (!track->event_format) { - ass_msg(track->library, MSGL_WARN, "Event format header missing"); - return; - } - - str = malloc(size + 1); - memcpy(str, data, size); - str[size] = '\0'; - ass_msg(track->library, MSGL_V, "Event at %" PRId64 ", +%" PRId64 ": %s", - (int64_t) timecode, (int64_t) duration, str); - - eid = ass_alloc_event(track); - event = track->events + eid; - - p = str; - - do { - NEXT(p, token); - event->ReadOrder = atoi(token); - if (check_duplicate_event(track, event->ReadOrder)) - break; - - NEXT(p, token); - event->Layer = atoi(token); - - process_event_tail(track, event, p, 3); - - event->Start = timecode; - event->Duration = duration; - - free(str); - return; -// dump_events(tid); - } while (0); - // some error - ass_free_event(track, eid); - track->n_events--; - free(str); -} - -/** - * \brief Flush buffered events. - * \param track track -*/ -void ass_flush_events(ASS_Track *track) -{ - if (track->events) { - int eid; - for (eid = 0; eid < track->n_events; eid++) - ass_free_event(track, eid); - track->n_events = 0; - } -} - -#ifdef CONFIG_ICONV -/** \brief recode buffer to utf-8 - * constraint: codepage != 0 - * \param data pointer to text buffer - * \param size buffer size - * \return a pointer to recoded buffer, caller is responsible for freeing it -**/ -static char *sub_recode(ASS_Library *library, char *data, size_t size, - char *codepage) -{ - iconv_t icdsc; - char *tocp = "UTF-8"; - char *outbuf; - assert(codepage); - - { - const char *cp_tmp = codepage; -#ifdef CONFIG_ENCA - char enca_lang[3], enca_fallback[100]; - if (sscanf(codepage, "enca:%2s:%99s", enca_lang, enca_fallback) == 2 - || sscanf(codepage, "ENCA:%2s:%99s", enca_lang, - enca_fallback) == 2) { - cp_tmp = - ass_guess_buffer_cp(library, (unsigned char *) data, size, - enca_lang, enca_fallback); - } -#endif - if ((icdsc = iconv_open(tocp, cp_tmp)) != (iconv_t) (-1)) { - ass_msg(library, MSGL_V, "Opened iconv descriptor"); - } else - ass_msg(library, MSGL_ERR, "Error opening iconv descriptor"); - } - - { - size_t osize = size; - size_t ileft = size; - size_t oleft = size - 1; - char *ip; - char *op; - size_t rc; - int clear = 0; - - outbuf = malloc(osize); - ip = data; - op = outbuf; - - while (1) { - if (ileft) - rc = iconv(icdsc, &ip, &ileft, &op, &oleft); - else { // clear the conversion state and leave - clear = 1; - rc = iconv(icdsc, NULL, NULL, &op, &oleft); - } - if (rc == (size_t) (-1)) { - if (errno == E2BIG) { - size_t offset = op - outbuf; - outbuf = (char *) realloc(outbuf, osize + size); - op = outbuf + offset; - osize += size; - oleft += size; - } else { - ass_msg(library, MSGL_WARN, "Error recoding file"); - return NULL; - } - } else if (clear) - break; - } - outbuf[osize - oleft - 1] = 0; - } - - if (icdsc != (iconv_t) (-1)) { - (void) iconv_close(icdsc); - icdsc = (iconv_t) (-1); - ass_msg(library, MSGL_V, "Closed iconv descriptor"); - } - - return outbuf; -} -#endif // ICONV - -/** - * \brief read file contents into newly allocated buffer - * \param fname file name - * \param bufsize out: file size - * \return pointer to file contents. Caller is responsible for its deallocation. - */ -static char *read_file(ASS_Library *library, char *fname, size_t *bufsize) -{ - int res; - long sz; - long bytes_read; - char *buf; - - FILE *fp = fopen(fname, "rb"); - if (!fp) { - ass_msg(library, MSGL_WARN, - "ass_read_file(%s): fopen failed", fname); - return 0; - } - res = fseek(fp, 0, SEEK_END); - if (res == -1) { - ass_msg(library, MSGL_WARN, - "ass_read_file(%s): fseek failed", fname); - fclose(fp); - return 0; - } - - sz = ftell(fp); - rewind(fp); - - ass_msg(library, MSGL_V, "File size: %ld", sz); - - buf = malloc(sz + 1); - assert(buf); - bytes_read = 0; - do { - res = fread(buf + bytes_read, 1, sz - bytes_read, fp); - if (res <= 0) { - ass_msg(library, MSGL_INFO, "Read failed, %d: %s", errno, - strerror(errno)); - fclose(fp); - free(buf); - return 0; - } - bytes_read += res; - } while (sz - bytes_read > 0); - buf[sz] = '\0'; - fclose(fp); - - if (bufsize) - *bufsize = sz; - return buf; -} - -/* - * \param buf pointer to subtitle text in utf-8 - */ -static ASS_Track *parse_memory(ASS_Library *library, char *buf) -{ - ASS_Track *track; - int i; - - track = ass_new_track(library); - - // process header - process_text(track, buf); - - // external SSA/ASS subs does not have ReadOrder field - for (i = 0; i < track->n_events; ++i) - track->events[i].ReadOrder = i; - - // there is no explicit end-of-font marker in ssa/ass - if (track->parser_priv->fontname) - decode_font(track); - - if (track->track_type == TRACK_TYPE_UNKNOWN) { - ass_free_track(track); - return 0; - } - - ass_process_force_style(track); - - return track; -} - -/** - * \brief Read subtitles from memory. - * \param library libass library object - * \param buf pointer to subtitles text - * \param bufsize size of buffer - * \param codepage recode buffer contents from given codepage - * \return newly allocated track -*/ -ASS_Track *ass_read_memory(ASS_Library *library, char *buf, - size_t bufsize, char *codepage) -{ - ASS_Track *track; - int need_free = 0; - - if (!buf) - return 0; - -#ifdef CONFIG_ICONV - if (codepage) { - buf = sub_recode(library, buf, bufsize, codepage); - if (!buf) - return 0; - else - need_free = 1; - } -#endif - track = parse_memory(library, buf); - if (need_free) - free(buf); - if (!track) - return 0; - - ass_msg(library, MSGL_INFO, "Added subtitle file: " - " (%d styles, %d events)", - track->n_styles, track->n_events); - return track; -} - -static char *read_file_recode(ASS_Library *library, char *fname, - char *codepage, size_t *size) -{ - char *buf; - size_t bufsize; - - buf = read_file(library, fname, &bufsize); - if (!buf) - return 0; -#ifdef CONFIG_ICONV - if (codepage) { - char *tmpbuf = sub_recode(library, buf, bufsize, codepage); - free(buf); - buf = tmpbuf; - } - if (!buf) - return 0; -#endif - *size = bufsize; - return buf; -} - -/** - * \brief Read subtitles from file. - * \param library libass library object - * \param fname file name - * \param codepage recode buffer contents from given codepage - * \return newly allocated track -*/ -ASS_Track *ass_read_file(ASS_Library *library, char *fname, - char *codepage) -{ - char *buf; - ASS_Track *track; - size_t bufsize; - - buf = read_file_recode(library, fname, codepage, &bufsize); - if (!buf) - return 0; - track = parse_memory(library, buf); - free(buf); - if (!track) - return 0; - - track->name = strdup(fname); - - ass_msg(library, MSGL_INFO, - "Added subtitle file: '%s' (%d styles, %d events)", - fname, track->n_styles, track->n_events); - - return track; -} - -/** - * \brief read styles from file into already initialized track - */ -int ass_read_styles(ASS_Track *track, char *fname, char *codepage) -{ - char *buf; - ParserState old_state; - size_t sz; - - buf = read_file(track->library, fname, &sz); - if (!buf) - return 1; -#ifdef CONFIG_ICONV - if (codepage) { - char *tmpbuf; - tmpbuf = sub_recode(track->library, buf, sz, codepage); - free(buf); - buf = tmpbuf; - } - if (!buf) - return 0; -#endif - - old_state = track->parser_priv->state; - track->parser_priv->state = PST_STYLES; - process_text(track, buf); - track->parser_priv->state = old_state; - - return 0; -} - -long long ass_step_sub(ASS_Track *track, long long now, int movement) -{ - int i; - - if (movement == 0) - return 0; - if (track->n_events == 0) - return 0; - - if (movement < 0) - for (i = 0; - (i < track->n_events) - && - ((long long) (track->events[i].Start + - track->events[i].Duration) <= now); ++i) { - } else - for (i = track->n_events - 1; - (i >= 0) && ((long long) (track->events[i].Start) > now); - --i) { - } - - // -1 and n_events are ok - assert(i >= -1); - assert(i <= track->n_events); - i += movement; - if (i < 0) - i = 0; - if (i >= track->n_events) - i = track->n_events - 1; - return ((long long) track->events[i].Start) - now; -} - -ASS_Track *ass_new_track(ASS_Library *library) -{ - ASS_Track *track = calloc(1, sizeof(ASS_Track)); - track->library = library; - track->ScaledBorderAndShadow = 1; - track->parser_priv = calloc(1, sizeof(ASS_ParserPriv)); - return track; -} diff --git a/aegisub/libass/ass.h b/aegisub/libass/ass.h deleted file mode 100644 index 3762bffe4..000000000 --- a/aegisub/libass/ass.h +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef LIBASS_ASS_H -#define LIBASS_ASS_H - -#include -#include -#include "ass_types.h" - -#define LIBASS_VERSION 0x00911000 - -/* - * A linked list of images produced by an ass renderer. - * - * These images have to be rendered in-order for the correct screen - * composition. The libass renderer clips these bitmaps to the frame size. - * w/h can be zero, in this case the bitmap should not be rendered at all. - * The last bitmap row is not guaranteed to be padded up to stride size, - * e.g. in the worst case a bitmap has the size stride * (h - 1) + w. - */ -typedef struct ass_image { - int w, h; // Bitmap width/height - int stride; // Bitmap stride - unsigned char *bitmap; // 1bpp stride*h alpha buffer - // Note: the last row may not be padded to - // bitmap stride! - uint32_t color; // Bitmap color and alpha, RGBA - int dst_x, dst_y; // Bitmap placement inside the video frame - - struct ass_image *next; // Next image, or NULL -} ASS_Image; - -/* - * Hinting type. (see ass_set_hinting below) - * - * FreeType's native hinter is still buggy sometimes and it is recommended - * to use the light autohinter, ASS_HINTING_LIGHT, instead. For best - * compatibility with problematic fonts, disable hinting. - */ -typedef enum { - ASS_HINTING_NONE = 0, - ASS_HINTING_LIGHT, - ASS_HINTING_NORMAL, - ASS_HINTING_NATIVE -} ASS_Hinting; - -/** - * \brief Initialize the library. - * \return library handle or NULL if failed - */ -ASS_Library *ass_library_init(void); - -/** - * \brief Finalize the library - * \param priv library handle - */ -void ass_library_done(ASS_Library *priv); - -/** - * \brief Set additional fonts directory. - * Optional directory that will be scanned for fonts recursively. The fonts - * found are used for font lookup. - * NOTE: A valid font directory is not needed to support embedded fonts. - * - * \param priv library handle - * \param fonts_dir directory with additional fonts - */ -void ass_set_fonts_dir(ASS_Library *priv, const char *fonts_dir); - -/** - * \brief Whether fonts should be extracted from track data. - * \param priv library handle - * \param extract whether to extract fonts - */ -void ass_set_extract_fonts(ASS_Library *priv, int extract); - -/** - * \brief Register style overrides with a library instance. - * The overrides should have the form [Style.]Param=Value, e.g. - * SomeStyle.Font=Arial - * ScaledBorderAndShadow=yes - * - * \param priv library handle - * \param list NULL-terminated list of strings - */ -void ass_set_style_overrides(ASS_Library *priv, char **list); - -/** - * \brief Explicitly process style overrides for a track. - * \param track track handle - */ -void ass_process_force_style(ASS_Track *track); - -/** - * \brief Register a callback for debug/info messages. - * If a callback is registered, it is called for every message emitted by - * libass. The callback receives a format string and a list of arguments, - * to be used for the printf family of functions. Additionally, a log level - * from 0 (FATAL errors) to 7 (verbose DEBUG) is passed. Usually, level 5 - * should be used by applications. - * If no callback is set, all messages level < 5 are printed to stderr, - * prefixed with [ass]. - * - * \param priv library handle - * \param msg_cb pointer to callback function - * \param data additional data, will be passed to callback - */ -void ass_set_message_cb(ASS_Library *priv, void (*msg_cb) - (int level, const char *fmt, va_list args, void *data), - void *data); - -/** - * \brief Initialize the renderer. - * \param priv library handle - * \return renderer handle or NULL if failed - */ -ASS_Renderer *ass_renderer_init(ASS_Library *); - -/** - * \brief Finalize the renderer. - * \param priv renderer handle - */ -void ass_renderer_done(ASS_Renderer *priv); - -/** - * \brief Set the frame size in pixels, including margins. - * \param priv renderer handle - * \param w width - * \param h height - */ -void ass_set_frame_size(ASS_Renderer *priv, int w, int h); - -/** - * \brief Set frame margins. These values may be negative if pan-and-scan - * is used. - * \param priv renderer handle - * \param t top margin - * \param b bottom margin - * \param l left margin - * \param r right margin - */ -void ass_set_margins(ASS_Renderer *priv, int t, int b, int l, int r); - -/** - * \brief Whether margins should be used for placing regular events. - * \param priv renderer handle - * \param use whether to use the margins - */ -void ass_set_use_margins(ASS_Renderer *priv, int use); - -/** - * \brief Set aspect ratio parameters. - * \param priv renderer handle - * \param dar display aspect ratio (DAR), prescaled for output PAR - * \param sar storage aspect ratio (SAR) - */ -void ass_set_aspect_ratio(ASS_Renderer *priv, double dar, double sar); - -/** - * \brief Set a fixed font scaling factor. - * \param priv renderer handle - * \param font_scale scaling factor, default is 1.0 - */ -void ass_set_font_scale(ASS_Renderer *priv, double font_scale); - -/** - * \brief Set font hinting method. - * \param priv renderer handle - * \param ht hinting method - */ -void ass_set_hinting(ASS_Renderer *priv, ASS_Hinting ht); - -/** - * \brief Set line spacing. Will not be scaled with frame size. - * \param priv renderer handle - * \param line_spacing line spacing in pixels - */ -void ass_set_line_spacing(ASS_Renderer *priv, double line_spacing); - -/** - * \brief Set font lookup defaults. - * \param default_font path to default font to use. Must be supplied if - * fontconfig is disabled or unavailable. - * \param default_family fallback font family for fontconfig, or NULL - * \param fc whether to use fontconfig - * \param config path to fontconfig configuration file, or NULL. Only relevant - * if fontconfig is used. - * \param update whether fontconfig cache should be built/updated now. Only - * relevant if fontconfig is used. - * - * NOTE: font lookup must be configured before an ASS_Renderer can be used. - */ -void ass_set_fonts(ASS_Renderer *priv, const char *default_font, - const char *default_family, int fc, const char *config, - int update); - -/** - * \brief Update/build font cache. This needs to be called if it was - * disabled when ass_set_fonts was set. - * - * \param priv renderer handle - * \return success - */ -int ass_fonts_update(ASS_Renderer *priv); - -/** - * \brief Set hard cache limits. Do not set, or set to zero, for reasonable - * defaults. - * - * \param priv renderer handle - * \param glyph_max maximum number of cached glyphs - * \param bitmap_max_size maximum bitmap cache size (in MB) - */ -void ass_set_cache_limits(ASS_Renderer *priv, int glyph_max, - int bitmap_max_size); - -/** - * \brief Render a frame, producing a list of ASS_Image. - * \param priv renderer handle - * \param track subtitle track - * \param now video timestamp in milliseconds - * \param detect_change will be set to 1 if a change occured compared - * to the last invocation - */ -ASS_Image *ass_render_frame(ASS_Renderer *priv, ASS_Track *track, - long long now, int *detect_change); - - -/* - * The following functions operate on track objects and do not need - * an ass_renderer - */ - -/** - * \brief Allocate a new empty track object. - * \param library handle - * \return pointer to empty track - */ -ASS_Track *ass_new_track(ASS_Library *); - -/** - * \brief Deallocate track and all its child objects (styles and events). - * \param track track to deallocate - */ -void ass_free_track(ASS_Track *track); - -/** - * \brief Allocate new style. - * \param track track - * \return newly allocated style id - */ -int ass_alloc_style(ASS_Track *track); - -/** - * \brief Allocate new event. - * \param track track - * \return newly allocated event id - */ -int ass_alloc_event(ASS_Track *track); - -/** - * \brief Delete a style. - * \param track track - * \param sid style id - * Deallocates style data. Does not modify track->n_styles. - */ -void ass_free_style(ASS_Track *track, int sid); - -/** - * \brief Delete an event. - * \param track track - * \param eid event id - * Deallocates event data. Does not modify track->n_events. - */ -void ass_free_event(ASS_Track *track, int eid); - -/** - * \brief Parse a chunk of subtitle stream data. - * \param track track - * \param data string to parse - * \param size length of data - */ -void ass_process_data(ASS_Track *track, char *data, int size); - -/** - * \brief Parse Codec Private section of the subtitle stream, in Matroska - * format. See the Matroska specification for details. - * \param track target track - * \param data string to parse - * \param size length of data - */ -void ass_process_codec_private(ASS_Track *track, char *data, int size); - -/** - * \brief Parse a chunk of subtitle stream data. A chunk contains exactly one - * event in Matroska format. See the Matroska specification for details. - * \param track track - * \param data string to parse - * \param size length of data - * \param timecode starting time of the event (milliseconds) - * \param duration duration of the event (milliseconds) - */ -void ass_process_chunk(ASS_Track *track, char *data, int size, - long long timecode, long long duration); - -/** - * \brief Flush buffered events. - * \param track track -*/ -void ass_flush_events(ASS_Track *track); - -/** - * \brief Read subtitles from file. - * \param library library handle - * \param fname file name - * \param codepage encoding (iconv format) - * \return newly allocated track -*/ -ASS_Track *ass_read_file(ASS_Library *library, char *fname, - char *codepage); - -/** - * \brief Read subtitles from memory. - * \param library library handle - * \param buf pointer to subtitles text - * \param bufsize size of buffer - * \param codepage encoding (iconv format) - * \return newly allocated track -*/ -ASS_Track *ass_read_memory(ASS_Library *library, char *buf, - size_t bufsize, char *codepage); -/** - * \brief Read styles from file into already initialized track. - * \param fname file name - * \param codepage encoding (iconv format) - * \return 0 on success - */ -int ass_read_styles(ASS_Track *track, char *fname, char *codepage); - -/** - * \brief Add a memory font. - * \param library library handle - * \param name attachment name - * \param data binary font data - * \param data_size data size -*/ -void ass_add_font(ASS_Library *library, char *name, char *data, - int data_size); - -/** - * \brief Remove all fonts stored in an ass_library object. - * \param library library handle - */ -void ass_clear_fonts(ASS_Library *library); - -/** - * \brief Calculates timeshift from now to the start of some other subtitle - * event, depending on movement parameter. - * \param track subtitle track - * \param now current time in milliseconds - * \param movement how many events to skip from the one currently displayed - * +2 means "the one after the next", -1 means "previous" - * \return timeshift in milliseconds - */ -long long ass_step_sub(ASS_Track *track, long long now, int movement); - -#endif /* LIBASS_ASS_H */ diff --git a/aegisub/libass/ass_bitmap.c b/aegisub/libass/ass_bitmap.c deleted file mode 100644 index 4865974cd..000000000 --- a/aegisub/libass/ass_bitmap.c +++ /dev/null @@ -1,531 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include FT_GLYPH_H - -#include "ass_utils.h" -#include "ass_bitmap.h" - -struct ass_synth_priv { - int tmp_w, tmp_h; - unsigned short *tmp; - - int g_r; - int g_w; - - unsigned *g; - unsigned *gt2; - - double radius; -}; - -static const unsigned int maxcolor = 255; -static const unsigned base = 256; - -static int generate_tables(ASS_SynthPriv *priv, double radius) -{ - double A = log(1.0 / base) / (radius * radius * 2); - int mx, i; - double volume_diff, volume_factor = 0; - unsigned volume; - - if (priv->radius == radius) - return 0; - else - priv->radius = radius; - - priv->g_r = ceil(radius); - priv->g_w = 2 * priv->g_r + 1; - - if (priv->g_r) { - priv->g = realloc(priv->g, priv->g_w * sizeof(unsigned)); - priv->gt2 = realloc(priv->gt2, 256 * priv->g_w * sizeof(unsigned)); - if (priv->g == NULL || priv->gt2 == NULL) { - return -1; - } - } - - if (priv->g_r) { - // gaussian curve with volume = 256 - for (volume_diff = 10000000; volume_diff > 0.0000001; - volume_diff *= 0.5) { - volume_factor += volume_diff; - volume = 0; - for (i = 0; i < priv->g_w; ++i) { - priv->g[i] = - (unsigned) (exp(A * (i - priv->g_r) * (i - priv->g_r)) * - volume_factor + .5); - volume += priv->g[i]; - } - if (volume > 256) - volume_factor -= volume_diff; - } - volume = 0; - for (i = 0; i < priv->g_w; ++i) { - priv->g[i] = - (unsigned) (exp(A * (i - priv->g_r) * (i - priv->g_r)) * - volume_factor + .5); - volume += priv->g[i]; - } - - // gauss table: - for (mx = 0; mx < priv->g_w; mx++) { - for (i = 0; i < 256; i++) { - priv->gt2[mx + i * priv->g_w] = i * priv->g[mx]; - } - } - } - - return 0; -} - -static void resize_tmp(ASS_SynthPriv *priv, int w, int h) -{ - if (priv->tmp_w >= w && priv->tmp_h >= h) - return; - if (priv->tmp_w == 0) - priv->tmp_w = 64; - if (priv->tmp_h == 0) - priv->tmp_h = 64; - while (priv->tmp_w < w) - priv->tmp_w *= 2; - while (priv->tmp_h < h) - priv->tmp_h *= 2; - free(priv->tmp); - priv->tmp = malloc((priv->tmp_w + 1) * priv->tmp_h * sizeof(short)); -} - -ASS_SynthPriv *ass_synth_init(double radius) -{ - ASS_SynthPriv *priv = calloc(1, sizeof(ASS_SynthPriv)); - generate_tables(priv, radius); - return priv; -} - -void ass_synth_done(ASS_SynthPriv *priv) -{ - free(priv->tmp); - free(priv->g); - free(priv->gt2); - free(priv); -} - -static Bitmap *alloc_bitmap(int w, int h) -{ - Bitmap *bm; - bm = malloc(sizeof(Bitmap)); - bm->buffer = calloc(w, h); - bm->w = w; - bm->h = h; - bm->left = bm->top = 0; - return bm; -} - -void ass_free_bitmap(Bitmap *bm) -{ - if (bm) - free(bm->buffer); - free(bm); -} - -static Bitmap *copy_bitmap(const Bitmap *src) -{ - Bitmap *dst = alloc_bitmap(src->w, src->h); - dst->left = src->left; - dst->top = src->top; - memcpy(dst->buffer, src->buffer, src->w * src->h); - return dst; -} - -int check_glyph_area(ASS_Library *library, FT_Glyph glyph) -{ - FT_BBox bbox; - long long dx, dy; - FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox); - dx = bbox.xMax - bbox.xMin; - dy = bbox.yMax - bbox.yMin; - if (dx * dy > 8000000) { - ass_msg(library, MSGL_WARN, "Glyph bounding box too large: %dx%dpx", - (int) dx, (int) dy); - return 1; - } else - return 0; -} - -static Bitmap *glyph_to_bitmap_internal(ASS_Library *library, - FT_Glyph glyph, int bord) -{ - FT_BitmapGlyph bg; - FT_Bitmap *bit; - Bitmap *bm; - int w, h; - unsigned char *src; - unsigned char *dst; - int i; - int error; - - if (check_glyph_area(library, glyph)) - return 0; - error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 0); - if (error) { - ass_msg(library, MSGL_WARN, "FT_Glyph_To_Bitmap error %d", - error); - return 0; - } - - bg = (FT_BitmapGlyph) glyph; - bit = &(bg->bitmap); - if (bit->pixel_mode != FT_PIXEL_MODE_GRAY) { - ass_msg(library, MSGL_WARN, "Unsupported pixel mode: %d", - (int) (bit->pixel_mode)); - FT_Done_Glyph(glyph); - return 0; - } - - w = bit->width; - h = bit->rows; - bm = alloc_bitmap(w + 2 * bord, h + 2 * bord); - bm->left = bg->left - bord; - bm->top = -bg->top - bord; - - src = bit->buffer; - dst = bm->buffer + bord + bm->w * bord; - for (i = 0; i < h; ++i) { - memcpy(dst, src, w); - src += bit->pitch; - dst += bm->w; - } - - FT_Done_Glyph(glyph); - return bm; -} - -/** - * \brief fix outline bitmap - * - * The glyph bitmap is subtracted from outline bitmap. This way looks much - * better in some cases. - */ -static void fix_outline(Bitmap *bm_g, Bitmap *bm_o) -{ - int x, y; - const int l = bm_o->left > bm_g->left ? bm_o->left : bm_g->left; - const int t = bm_o->top > bm_g->top ? bm_o->top : bm_g->top; - const int r = - bm_o->left + bm_o->w < - bm_g->left + bm_g->w ? bm_o->left + bm_o->w : bm_g->left + bm_g->w; - const int b = - bm_o->top + bm_o->h < - bm_g->top + bm_g->h ? bm_o->top + bm_o->h : bm_g->top + bm_g->h; - - unsigned char *g = - bm_g->buffer + (t - bm_g->top) * bm_g->w + (l - bm_g->left); - unsigned char *o = - bm_o->buffer + (t - bm_o->top) * bm_o->w + (l - bm_o->left); - - for (y = 0; y < b - t; ++y) { - for (x = 0; x < r - l; ++x) { - unsigned char c_g, c_o; - c_g = g[x]; - c_o = o[x]; - o[x] = (c_o > c_g) ? c_o - (c_g / 2) : 0; - } - g += bm_g->w; - o += bm_o->w; - } -} - -/** - * \brief Shift a bitmap by the fraction of a pixel in x and y direction - * expressed in 26.6 fixed point - */ -static void shift_bitmap(unsigned char *buf, int w, int h, int shift_x, - int shift_y) -{ - int x, y, b; - - // Shift in x direction - if (shift_x > 0) { - for (y = 0; y < h; y++) { - for (x = w - 1; x > 0; x--) { - b = (buf[x + y * w - 1] * shift_x) >> 6; - buf[x + y * w - 1] -= b; - buf[x + y * w] += b; - } - } - } else if (shift_x < 0) { - shift_x = -shift_x; - for (y = 0; y < h; y++) { - for (x = 0; x < w - 1; x++) { - b = (buf[x + y * w + 1] * shift_x) >> 6; - buf[x + y * w + 1] -= b; - buf[x + y * w] += b; - } - } - } - - // Shift in y direction - if (shift_y > 0) { - for (x = 0; x < w; x++) { - for (y = h - 1; y > 0; y--) { - b = (buf[x + (y - 1) * w] * shift_y) >> 6; - buf[x + (y - 1) * w] -= b; - buf[x + y * w] += b; - } - } - } else if (shift_y < 0) { - shift_y = -shift_y; - for (x = 0; x < w; x++) { - for (y = 0; y < h - 1; y++) { - b = (buf[x + (y + 1) * w] * shift_y) >> 6; - buf[x + (y + 1) * w] -= b; - buf[x + y * w] += b; - } - } - } -} - -/* - * Gaussian blur. An fast pure C implementation from MPlayer. - */ -static void ass_gauss_blur(unsigned char *buffer, unsigned short *tmp2, - int width, int height, int stride, int *m2, - int r, int mwidth) -{ - - int x, y; - - unsigned char *s = buffer; - unsigned short *t = tmp2 + 1; - for (y = 0; y < height; y++) { - memset(t - 1, 0, (width + 1) * sizeof(short)); - - for (x = 0; x < r; x++) { - const int src = s[x]; - if (src) { - register unsigned short *dstp = t + x - r; - int mx; - unsigned *m3 = (unsigned *) (m2 + src * mwidth); - for (mx = r - x; mx < mwidth; mx++) { - dstp[mx] += m3[mx]; - } - } - } - - for (; x < width - r; x++) { - const int src = s[x]; - if (src) { - register unsigned short *dstp = t + x - r; - int mx; - unsigned *m3 = (unsigned *) (m2 + src * mwidth); - for (mx = 0; mx < mwidth; mx++) { - dstp[mx] += m3[mx]; - } - } - } - - for (; x < width; x++) { - const int src = s[x]; - if (src) { - register unsigned short *dstp = t + x - r; - int mx; - const int x2 = r + width - x; - unsigned *m3 = (unsigned *) (m2 + src * mwidth); - for (mx = 0; mx < x2; mx++) { - dstp[mx] += m3[mx]; - } - } - } - - s += stride; - t += width + 1; - } - - t = tmp2; - for (x = 0; x < width; x++) { - for (y = 0; y < r; y++) { - unsigned short *srcp = t + y * (width + 1) + 1; - int src = *srcp; - if (src) { - register unsigned short *dstp = srcp - 1 + width + 1; - const int src2 = (src + 128) >> 8; - unsigned *m3 = (unsigned *) (m2 + src2 * mwidth); - - int mx; - *srcp = 128; - for (mx = r - 1; mx < mwidth; mx++) { - *dstp += m3[mx]; - dstp += width + 1; - } - } - } - for (; y < height - r; y++) { - unsigned short *srcp = t + y * (width + 1) + 1; - int src = *srcp; - if (src) { - register unsigned short *dstp = srcp - 1 - r * (width + 1); - const int src2 = (src + 128) >> 8; - unsigned *m3 = (unsigned *) (m2 + src2 * mwidth); - - int mx; - *srcp = 128; - for (mx = 0; mx < mwidth; mx++) { - *dstp += m3[mx]; - dstp += width + 1; - } - } - } - for (; y < height; y++) { - unsigned short *srcp = t + y * (width + 1) + 1; - int src = *srcp; - if (src) { - const int y2 = r + height - y; - register unsigned short *dstp = srcp - 1 - r * (width + 1); - const int src2 = (src + 128) >> 8; - unsigned *m3 = (unsigned *) (m2 + src2 * mwidth); - - int mx; - *srcp = 128; - for (mx = 0; mx < y2; mx++) { - *dstp += m3[mx]; - dstp += width + 1; - } - } - } - t++; - } - - t = tmp2; - s = buffer; - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - s[x] = t[x] >> 8; - } - s += stride; - t += width + 1; - } -} - -/** - * \brief Blur with [[1,2,1]. [2,4,2], [1,2,1]] kernel - * This blur is the same as the one employed by vsfilter. - */ -static void be_blur(unsigned char *buf, int w, int h) -{ - unsigned int x, y; - unsigned int old_sum, new_sum; - - for (y = 0; y < h; y++) { - old_sum = 2 * buf[y * w]; - for (x = 0; x < w - 1; x++) { - new_sum = buf[y * w + x] + buf[y * w + x + 1]; - buf[y * w + x] = (old_sum + new_sum) >> 2; - old_sum = new_sum; - } - } - - for (x = 0; x < w; x++) { - old_sum = 2 * buf[x]; - for (y = 0; y < h - 1; y++) { - new_sum = buf[y * w + x] + buf[(y + 1) * w + x]; - buf[y * w + x] = (old_sum + new_sum) >> 2; - old_sum = new_sum; - } - } -} - -int glyph_to_bitmap(ASS_Library *library, ASS_SynthPriv *priv_blur, - FT_Glyph glyph, FT_Glyph outline_glyph, - Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s, - int be, double blur_radius, FT_Vector shadow_offset, - int border_style) -{ - int bbord; - int gbord; - int bord; - blur_radius *= 2; - bbord = be > 0 ? sqrt(2 * be) : 0; - gbord = blur_radius > 0.0 ? blur_radius + 1 : 0; - bord = FFMAX(bbord, gbord); - if (bord == 0 && (shadow_offset.x || shadow_offset.y)) - bord = 1; - - assert(bm_g && bm_o && bm_s); - - *bm_g = *bm_o = *bm_s = 0; - - if (glyph) - *bm_g = glyph_to_bitmap_internal(library, glyph, bord); - if (!*bm_g) - return 1; - - if (outline_glyph) { - *bm_o = glyph_to_bitmap_internal(library, outline_glyph, bord); - if (!*bm_o) { - return 1; - } - } - - // Apply box blur (multiple passes, if requested) - while (be--) { - if (*bm_o) - be_blur((*bm_o)->buffer, (*bm_o)->w, (*bm_o)->h); - else - be_blur((*bm_g)->buffer, (*bm_g)->w, (*bm_g)->h); - } - - // Apply gaussian blur - if (blur_radius > 0.0) { - if (*bm_o) - resize_tmp(priv_blur, (*bm_o)->w, (*bm_o)->h); - else - resize_tmp(priv_blur, (*bm_g)->w, (*bm_g)->h); - generate_tables(priv_blur, blur_radius); - if (*bm_o) - ass_gauss_blur((*bm_o)->buffer, priv_blur->tmp, - (*bm_o)->w, (*bm_o)->h, (*bm_o)->w, - (int *) priv_blur->gt2, priv_blur->g_r, - priv_blur->g_w); - else - ass_gauss_blur((*bm_g)->buffer, priv_blur->tmp, - (*bm_g)->w, (*bm_g)->h, (*bm_g)->w, - (int *) priv_blur->gt2, priv_blur->g_r, - priv_blur->g_w); - } - - // Create shadow and fix outline as needed - if (*bm_o && border_style != 3) { - *bm_s = copy_bitmap(*bm_o); - fix_outline(*bm_g, *bm_o); - } else if (*bm_o) { - *bm_s = copy_bitmap(*bm_o); - } else - *bm_s = copy_bitmap(*bm_g); - - assert(bm_s); - - shift_bitmap((*bm_s)->buffer, (*bm_s)->w,(*bm_s)->h, - shadow_offset.x, shadow_offset.y); - - return 0; -} diff --git a/aegisub/libass/ass_bitmap.h b/aegisub/libass/ass_bitmap.h deleted file mode 100644 index 287b63828..000000000 --- a/aegisub/libass/ass_bitmap.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef LIBASS_BITMAP_H -#define LIBASS_BITMAP_H - -#include -#include FT_GLYPH_H - -#include "ass.h" - -typedef struct ass_synth_priv ASS_SynthPriv; - -ASS_SynthPriv *ass_synth_init(double); -void ass_synth_done(ASS_SynthPriv *priv); - -typedef struct { - int left, top; - int w, h; // width, height - unsigned char *buffer; // w x h buffer -} Bitmap; - -/** - * \brief perform glyph rendering - * \param glyph original glyph - * \param outline_glyph "border" glyph, produced from original by FreeType's glyph stroker - * \param bm_g out: pointer to the bitmap of original glyph is returned here - * \param bm_o out: pointer to the bitmap of outline (border) glyph is returned here - * \param bm_g out: pointer to the bitmap of glyph shadow is returned here - * \param be 1 = produces blurred bitmaps, 0 = normal bitmaps - */ -int glyph_to_bitmap(ASS_Library *library, ASS_SynthPriv *priv_blur, - FT_Glyph glyph, FT_Glyph outline_glyph, - Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s, - int be, double blur_radius, FT_Vector shadow_offset, - int border_style); - -void ass_free_bitmap(Bitmap *bm); -int check_glyph_area(ASS_Library *library, FT_Glyph glyph); - -#endif /* LIBASS_BITMAP_H */ diff --git a/aegisub/libass/ass_cache.c b/aegisub/libass/ass_cache.c deleted file mode 100644 index 46c2478f8..000000000 --- a/aegisub/libass/ass_cache.c +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include FT_FREETYPE_H -#include FT_GLYPH_H - -#include - -#include "ass_utils.h" -#include "ass.h" -#include "ass_fontconfig.h" -#include "ass_font.h" -#include "ass_bitmap.h" -#include "ass_cache.h" - -static unsigned hashmap_hash(void *buf, size_t len) -{ - return fnv_32a_buf(buf, len, FNV1_32A_INIT); -} - -static int hashmap_key_compare(void *a, void *b, size_t size) -{ - return memcmp(a, b, size) == 0; -} - -static void hashmap_item_dtor(void *key, size_t key_size, void *value, - size_t value_size) -{ - free(key); - free(value); -} - -Hashmap *hashmap_init(ASS_Library *library, size_t key_size, - size_t value_size, int nbuckets, - HashmapItemDtor item_dtor, - HashmapKeyCompare key_compare, - HashmapHash hash) -{ - Hashmap *map = calloc(1, sizeof(Hashmap)); - map->library = library; - map->nbuckets = nbuckets; - map->key_size = key_size; - map->value_size = value_size; - map->root = calloc(nbuckets, sizeof(hashmap_item_p)); - map->item_dtor = item_dtor ? item_dtor : hashmap_item_dtor; - map->key_compare = key_compare ? key_compare : hashmap_key_compare; - map->hash = hash ? hash : hashmap_hash; - return map; -} - -void hashmap_done(Hashmap *map) -{ - int i; - // print stats - if (map->count > 0 || map->hit_count + map->miss_count > 0) - ass_msg(map->library, MSGL_V, - "cache statistics: \n total accesses: %d\n hits: %d\n " - "misses: %d\n object count: %d", - map->hit_count + map->miss_count, map->hit_count, - map->miss_count, map->count); - - for (i = 0; i < map->nbuckets; ++i) { - HashmapItem *item = map->root[i]; - while (item) { - HashmapItem *next = item->next; - map->item_dtor(item->key, map->key_size, item->value, - map->value_size); - free(item); - item = next; - } - } - free(map->root); - free(map); -} - -// does nothing if key already exists -void *hashmap_insert(Hashmap *map, void *key, void *value) -{ - unsigned hash = map->hash(key, map->key_size); - HashmapItem **next = map->root + (hash % map->nbuckets); - while (*next) { - if (map->key_compare(key, (*next)->key, map->key_size)) - return (*next)->value; - next = &((*next)->next); - assert(next); - } - (*next) = malloc(sizeof(HashmapItem)); - (*next)->key = malloc(map->key_size); - (*next)->value = malloc(map->value_size); - memcpy((*next)->key, key, map->key_size); - memcpy((*next)->value, value, map->value_size); - (*next)->next = 0; - - map->count++; - return (*next)->value; -} - -void *hashmap_find(Hashmap *map, void *key) -{ - unsigned hash = map->hash(key, map->key_size); - HashmapItem *item = map->root[hash % map->nbuckets]; - while (item) { - if (map->key_compare(key, item->key, map->key_size)) { - map->hit_count++; - return item->value; - } - item = item->next; - } - map->miss_count++; - return 0; -} - -//--------------------------------- -// font cache - -static unsigned font_desc_hash(void *buf, size_t len) -{ - ASS_FontDesc *desc = buf; - unsigned hval; - hval = fnv_32a_str(desc->family, FNV1_32A_INIT); - hval = fnv_32a_buf(&desc->bold, sizeof(desc->bold), hval); - hval = fnv_32a_buf(&desc->italic, sizeof(desc->italic), hval); - return hval; -} - -static int font_compare(void *key1, void *key2, size_t key_size) -{ - ASS_FontDesc *a = key1; - ASS_FontDesc *b = key2; - if (strcmp(a->family, b->family) != 0) - return 0; - if (a->bold != b->bold) - return 0; - if (a->italic != b->italic) - return 0; - if (a->treat_family_as_pattern != b->treat_family_as_pattern) - return 0; - if (a->vertical != b->vertical) - return 0; - return 1; -} - -static void font_hash_dtor(void *key, size_t key_size, void *value, - size_t value_size) -{ - ass_font_free(value); - free(key); -} - -ASS_Font *ass_font_cache_find(Hashmap *font_cache, - ASS_FontDesc *desc) -{ - return hashmap_find(font_cache, desc); -} - -/** - * \brief Add a face struct to cache. - * \param font font struct -*/ -void *ass_font_cache_add(Hashmap *font_cache, ASS_Font *font) -{ - return hashmap_insert(font_cache, &(font->desc), font); -} - -Hashmap *ass_font_cache_init(ASS_Library *library) -{ - Hashmap *font_cache; - font_cache = hashmap_init(library, sizeof(ASS_FontDesc), - sizeof(ASS_Font), - 1000, - font_hash_dtor, font_compare, font_desc_hash); - return font_cache; -} - -void ass_font_cache_done(Hashmap *font_cache) -{ - hashmap_done(font_cache); -} - - -// Create hash/compare functions for bitmap and glyph -#define CREATE_HASH_FUNCTIONS -#include "ass_cache_template.h" -#define CREATE_COMPARISON_FUNCTIONS -#include "ass_cache_template.h" - -//--------------------------------- -// bitmap cache - -static void bitmap_hash_dtor(void *key, size_t key_size, void *value, - size_t value_size) -{ - BitmapHashValue *v = value; - if (v->bm) - ass_free_bitmap(v->bm); - if (v->bm_o) - ass_free_bitmap(v->bm_o); - if (v->bm_s) - ass_free_bitmap(v->bm_s); - free(key); - free(value); -} - -void *cache_add_bitmap(Hashmap *bitmap_cache, BitmapHashKey *key, - BitmapHashValue *val) -{ - // Note: this is only an approximation - if (val->bm_o) - bitmap_cache->cache_size += val->bm_o->w * val->bm_o->h * 3; - else if (val->bm) - bitmap_cache->cache_size += val->bm->w * val->bm->h * 3; - - return hashmap_insert(bitmap_cache, key, val); -} - -/** - * \brief Get a bitmap from bitmap cache. - * \param key hash key - * \return requested hash val or 0 if not found -*/ -BitmapHashValue *cache_find_bitmap(Hashmap *bitmap_cache, - BitmapHashKey *key) -{ - return hashmap_find(bitmap_cache, key); -} - -Hashmap *ass_bitmap_cache_init(ASS_Library *library) -{ - Hashmap *bitmap_cache; - bitmap_cache = hashmap_init(library, - sizeof(BitmapHashKey), - sizeof(BitmapHashValue), - 0xFFFF + 13, - bitmap_hash_dtor, bitmap_compare, - bitmap_hash); - return bitmap_cache; -} - -void ass_bitmap_cache_done(Hashmap *bitmap_cache) -{ - hashmap_done(bitmap_cache); -} - -Hashmap *ass_bitmap_cache_reset(Hashmap *bitmap_cache) -{ - ASS_Library *lib = bitmap_cache->library; - - ass_bitmap_cache_done(bitmap_cache); - return ass_bitmap_cache_init(lib); -} - -//--------------------------------- -// glyph cache - -static void glyph_hash_dtor(void *key, size_t key_size, void *value, - size_t value_size) -{ - GlyphHashValue *v = value; - if (v->glyph) - FT_Done_Glyph(v->glyph); - if (v->outline_glyph) - FT_Done_Glyph(v->outline_glyph); - free(key); - free(value); -} - -void *cache_add_glyph(Hashmap *glyph_cache, GlyphHashKey *key, - GlyphHashValue *val) -{ - if (val->glyph && val->glyph->format == FT_GLYPH_FORMAT_BITMAP) { - FT_Bitmap *bitmap = &((FT_BitmapGlyph) val->glyph)->bitmap; - glyph_cache->cache_size += bitmap->rows * bitmap->pitch; - } - - return hashmap_insert(glyph_cache, key, val); -} - -/** - * \brief Get a glyph from glyph cache. - * \param key hash key - * \return requested hash val or 0 if not found -*/ -GlyphHashValue *cache_find_glyph(Hashmap *glyph_cache, - GlyphHashKey *key) -{ - return hashmap_find(glyph_cache, key); -} - -Hashmap *ass_glyph_cache_init(ASS_Library *library) -{ - Hashmap *glyph_cache; - glyph_cache = hashmap_init(library, sizeof(GlyphHashKey), - sizeof(GlyphHashValue), - 0xFFFF + 13, - glyph_hash_dtor, glyph_compare, glyph_hash); - return glyph_cache; -} - -void ass_glyph_cache_done(Hashmap *glyph_cache) -{ - hashmap_done(glyph_cache); -} - -Hashmap *ass_glyph_cache_reset(Hashmap *glyph_cache) -{ - ASS_Library *lib = glyph_cache->library; - - ass_glyph_cache_done(glyph_cache); - return ass_glyph_cache_init(lib); -} - - -//--------------------------------- -// composite cache - -static void composite_hash_dtor(void *key, size_t key_size, void *value, - size_t value_size) -{ - CompositeHashValue *v = value; - free(v->a); - free(v->b); - free(key); - free(value); -} - -void *cache_add_composite(Hashmap *composite_cache, - CompositeHashKey *key, - CompositeHashValue *val) -{ - return hashmap_insert(composite_cache, key, val); -} - -/** - * \brief Get a composite bitmap from composite cache. - * \param key hash key - * \return requested hash val or 0 if not found -*/ -CompositeHashValue *cache_find_composite(Hashmap *composite_cache, - CompositeHashKey *key) -{ - return hashmap_find(composite_cache, key); -} - -Hashmap *ass_composite_cache_init(ASS_Library *library) -{ - Hashmap *composite_cache; - composite_cache = hashmap_init(library, sizeof(CompositeHashKey), - sizeof(CompositeHashValue), - 0xFFFF + 13, - composite_hash_dtor, composite_compare, - composite_hash); - return composite_cache; -} - -void ass_composite_cache_done(Hashmap *composite_cache) -{ - hashmap_done(composite_cache); -} - -Hashmap *ass_composite_cache_reset(Hashmap *composite_cache) -{ - ASS_Library *lib = composite_cache->library; - - ass_composite_cache_done(composite_cache); - return ass_composite_cache_init(lib); -} diff --git a/aegisub/libass/ass_cache.h b/aegisub/libass/ass_cache.h deleted file mode 100644 index 472bf359b..000000000 --- a/aegisub/libass/ass_cache.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef LIBASS_CACHE_H -#define LIBASS_CACHE_H - -#include "ass.h" -#include "ass_font.h" -#include "ass_bitmap.h" - -typedef void (*HashmapItemDtor) (void *key, size_t key_size, - void *value, size_t value_size); -typedef int (*HashmapKeyCompare) (void *key1, void *key2, - size_t key_size); -typedef unsigned (*HashmapHash) (void *key, size_t key_size); - -typedef struct hashmap_item { - void *key; - void *value; - struct hashmap_item *next; -} HashmapItem; -typedef HashmapItem *hashmap_item_p; - -typedef struct { - int nbuckets; - size_t key_size, value_size; - hashmap_item_p *root; - HashmapItemDtor item_dtor; // a destructor for hashmap key/value pairs - HashmapKeyCompare key_compare; - HashmapHash hash; - size_t cache_size; - // stats - int hit_count; - int miss_count; - int count; - ASS_Library *library; -} Hashmap; - -Hashmap *hashmap_init(ASS_Library *library, size_t key_size, - size_t value_size, int nbuckets, - HashmapItemDtor item_dtor, - HashmapKeyCompare key_compare, - HashmapHash hash); -void hashmap_done(Hashmap *map); -void *hashmap_insert(Hashmap *map, void *key, void *value); -void *hashmap_find(Hashmap *map, void *key); - -Hashmap *ass_font_cache_init(ASS_Library *library); -ASS_Font *ass_font_cache_find(Hashmap *, ASS_FontDesc *desc); -void *ass_font_cache_add(Hashmap *, ASS_Font *font); -void ass_font_cache_done(Hashmap *); - -// Create definitions for bitmap_hash_key and glyph_hash_key -#define CREATE_STRUCT_DEFINITIONS -#include "ass_cache_template.h" - -typedef struct { - Bitmap *bm; // the actual bitmaps - Bitmap *bm_o; - Bitmap *bm_s; -} BitmapHashValue; - -Hashmap *ass_bitmap_cache_init(ASS_Library *library); -void *cache_add_bitmap(Hashmap *, BitmapHashKey *key, - BitmapHashValue *val); -BitmapHashValue *cache_find_bitmap(Hashmap *bitmap_cache, - BitmapHashKey *key); -Hashmap *ass_bitmap_cache_reset(Hashmap *bitmap_cache); -void ass_bitmap_cache_done(Hashmap *bitmap_cache); - - -typedef struct { - unsigned char *a; - unsigned char *b; -} CompositeHashValue; - -Hashmap *ass_composite_cache_init(ASS_Library *library); -void *cache_add_composite(Hashmap *, CompositeHashKey *key, - CompositeHashValue *val); -CompositeHashValue *cache_find_composite(Hashmap *composite_cache, - CompositeHashKey *key); -Hashmap *ass_composite_cache_reset(Hashmap *composite_cache); -void ass_composite_cache_done(Hashmap *composite_cache); - - -typedef struct { - FT_Glyph glyph; - FT_Glyph outline_glyph; - FT_BBox bbox_scaled; // bbox after scaling, but before rotation - FT_Vector advance; // 26.6, advance distance to the next bitmap in line - int asc, desc; // ascender/descender of a drawing -} GlyphHashValue; - -Hashmap *ass_glyph_cache_init(ASS_Library *library); -void *cache_add_glyph(Hashmap *, GlyphHashKey *key, - GlyphHashValue *val); -GlyphHashValue *cache_find_glyph(Hashmap *glyph_cache, - GlyphHashKey *key); -Hashmap *ass_glyph_cache_reset(Hashmap *glyph_cache); -void ass_glyph_cache_done(Hashmap *glyph_cache); - -#endif /* LIBASS_CACHE_H */ diff --git a/aegisub/libass/ass_cache_template.h b/aegisub/libass/ass_cache_template.h deleted file mode 100644 index f335c6b27..000000000 --- a/aegisub/libass/ass_cache_template.h +++ /dev/null @@ -1,122 +0,0 @@ -#ifdef CREATE_STRUCT_DEFINITIONS -#undef CREATE_STRUCT_DEFINITIONS -#define START(funcname, structname) \ - typedef struct structname { -#define GENERIC(type, member) \ - type member; -#define FTVECTOR(member) \ - FT_Vector member; -#define BITMAPHASHKEY(member) \ - BitmapHashKey member; -#define END(typedefnamename) \ - } typedefnamename; - -#elif defined(CREATE_COMPARISON_FUNCTIONS) -#undef CREATE_COMPARISON_FUNCTIONS -#define START(funcname, structname) \ - static int funcname##_compare(void *key1, void *key2, size_t key_size) \ - { \ - struct structname *a = key1; \ - struct structname *b = key2; \ - return // conditions follow -#define GENERIC(type, member) \ - a->member == b->member && -#define FTVECTOR(member) \ - a->member.x == b->member.x && a->member.y == b->member.y && -#define BITMAPHASHKEY(member) \ - bitmap_compare(&a->member, &b->member, sizeof(a->member)) && -#define END(typedefname) \ - 1; \ - } - -#elif defined(CREATE_HASH_FUNCTIONS) -#undef CREATE_HASH_FUNCTIONS -#define START(funcname, structname) \ - static unsigned funcname##_hash(void *buf, size_t len) \ - { \ - struct structname *p = buf; \ - unsigned hval = FNV1_32A_INIT; -#define GENERIC(type, member) \ - hval = fnv_32a_buf(&p->member, sizeof(p->member), hval); -#define FTVECTOR(member) GENERIC(, member.x); GENERIC(, member.y); -#define BITMAPHASHKEY(member) { \ - unsigned temp = bitmap_hash(&p->member, sizeof(p->member)); \ - hval = fnv_32a_buf(&temp, sizeof(temp), hval); \ - } -#define END(typedefname) \ - return hval; \ - } - -#else -#error missing defines -#endif - - - -// describes a bitmap; bitmaps with equivalents structs are considered identical -START(bitmap, bitmap_hash_key) - GENERIC(char, bitmap) // bool : true = bitmap, false = outline - GENERIC(ASS_Font *, font) - GENERIC(double, size) // font size - GENERIC(uint32_t, ch) // character code - FTVECTOR(outline) // border width, 16.16 fixed point value - GENERIC(int, bold) - GENERIC(int, italic) - GENERIC(char, be) // blur edges - GENERIC(double, blur) // gaussian blur - GENERIC(unsigned, scale_x) // 16.16 - GENERIC(unsigned, scale_y) // 16.16 - GENERIC(int, frx) // signed 16.16 - GENERIC(int, fry) // signed 16.16 - GENERIC(int, frz) // signed 16.16 - GENERIC(int, fax) // signed 16.16 - GENERIC(int, fay) // signed 16.16 - // shift vector that was added to glyph before applying rotation - // = 0, if frx = fry = frx = 0 - // = (glyph base point) - (rotation origin), otherwise - GENERIC(int, shift_x) - GENERIC(int, shift_y) - FTVECTOR(advance) // subpixel shift vector - FTVECTOR(shadow_offset) // shadow subpixel shift - GENERIC(unsigned, drawing_hash) // hashcode of a drawing - GENERIC(unsigned, flags) // glyph decoration - GENERIC(unsigned, border_style) -END(BitmapHashKey) - -// describes an outline glyph -START(glyph, glyph_hash_key) - GENERIC(ASS_Font *, font) - GENERIC(double, size) // font size - GENERIC(uint32_t, ch) // character code - GENERIC(int, bold) - GENERIC(int, italic) - GENERIC(unsigned, scale_x) // 16.16 - GENERIC(unsigned, scale_y) // 16.16 - FTVECTOR(outline) // border width, 16.16 - GENERIC(unsigned, drawing_hash) // hashcode of a drawing - GENERIC(unsigned, flags) // glyph decoration flags - GENERIC(unsigned, border_style) -END(GlyphHashKey) - -// Cache for composited bitmaps -START(composite, composite_hash_key) - GENERIC(int, aw) - GENERIC(int, ah) - GENERIC(int, bw) - GENERIC(int, bh) - GENERIC(int, ax) - GENERIC(int, ay) - GENERIC(int, bx) - GENERIC(int, by) - GENERIC(int, as) - GENERIC(int, bs) - GENERIC(unsigned char *, a) - GENERIC(unsigned char *, b) -END(CompositeHashKey) - - -#undef START -#undef GENERIC -#undef FTVECTOR -#undef BITMAPHASHKEY -#undef END diff --git a/aegisub/libass/ass_drawing.c b/aegisub/libass/ass_drawing.c deleted file mode 100644 index be430ddd9..000000000 --- a/aegisub/libass/ass_drawing.c +++ /dev/null @@ -1,492 +0,0 @@ -/* - * Copyright (C) 2009 Grigori Goronzy - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "config.h" - -#include -#include FT_GLYPH_H -#include FT_OUTLINE_H -#include FT_BBOX_H -#include - -#include "ass_utils.h" -#include "ass_font.h" -#include "ass_drawing.h" - -#define CURVE_ACCURACY 64.0 -#define GLYPH_INITIAL_POINTS 100 -#define GLYPH_INITIAL_CONTOURS 5 - -/* - * \brief Get and prepare a FreeType glyph - */ -static void drawing_make_glyph(ASS_Drawing *drawing, void *fontconfig_priv, - ASS_Font *font) -{ - FT_OutlineGlyph glyph; - - // This is hacky... - glyph = (FT_OutlineGlyph) ass_font_get_glyph(fontconfig_priv, font, - (uint32_t) ' ', 0, 0); - if (glyph) { - FT_Outline_Done(drawing->ftlibrary, &glyph->outline); - FT_Outline_New(drawing->ftlibrary, GLYPH_INITIAL_POINTS, - GLYPH_INITIAL_CONTOURS, &glyph->outline); - - glyph->outline.n_contours = 0; - glyph->outline.n_points = 0; - glyph->root.advance.x = glyph->root.advance.y = 0; - } - drawing->glyph = glyph; -} - -/* - * \brief Add a single point to a contour. - */ -static inline void drawing_add_point(ASS_Drawing *drawing, - FT_Vector *point) -{ - FT_Outline *ol = &drawing->glyph->outline; - - if (ol->n_points >= drawing->max_points) { - drawing->max_points *= 2; - ol->points = realloc(ol->points, sizeof(FT_Vector) * - drawing->max_points); - ol->tags = realloc(ol->tags, drawing->max_points); - } - - ol->points[ol->n_points].x = point->x; - ol->points[ol->n_points].y = point->y; - ol->tags[ol->n_points] = 1; - ol->n_points++; -} - -/* - * \brief Close a contour and check glyph size overflow. - */ -static inline void drawing_close_shape(ASS_Drawing *drawing) -{ - FT_Outline *ol = &drawing->glyph->outline; - - if (ol->n_contours >= drawing->max_contours) { - drawing->max_contours *= 2; - ol->contours = realloc(ol->contours, sizeof(short) * - drawing->max_contours); - } - - if (ol->n_points) { - ol->contours[ol->n_contours] = ol->n_points - 1; - ol->n_contours++; - } -} - -/* - * \brief Prepare drawing for parsing. This just sets a few parameters. - */ -static void drawing_prepare(ASS_Drawing *drawing) -{ - // Scaling parameters - drawing->point_scale_x = drawing->scale_x * - 64.0 / (1 << (drawing->scale - 1)); - drawing->point_scale_y = drawing->scale_y * - 64.0 / (1 << (drawing->scale - 1)); -} - -/* - * \brief Finish a drawing. This only sets the horizontal advance according - * to the glyph's bbox at the moment. - */ -static void drawing_finish(ASS_Drawing *drawing, int raw_mode) -{ - int i, offset; - FT_BBox bbox = drawing->cbox; - FT_Outline *ol = &drawing->glyph->outline; - - // Close the last contour - drawing_close_shape(drawing); - - ass_msg(drawing->library, MSGL_V, - "Parsed drawing with %d points and %d contours", ol->n_points, - ol->n_contours); - - if (raw_mode) - return; - - drawing->glyph->root.advance.x = d6_to_d16(bbox.xMax - bbox.xMin); - - drawing->desc = double_to_d6(-drawing->pbo * drawing->scale_y); - drawing->asc = bbox.yMax - bbox.yMin + drawing->desc; - - // Place it onto the baseline - offset = (bbox.yMax - bbox.yMin) + double_to_d6(-drawing->pbo * - drawing->scale_y); - for (i = 0; i < ol->n_points; i++) - ol->points[i].y += offset; -} - -/* - * \brief Check whether a number of items on the list is available - */ -static int token_check_values(ASS_DrawingToken *token, int i, int type) -{ - int j; - for (j = 0; j < i; j++) { - if (!token || token->type != type) return 0; - token = token->next; - } - - return 1; -} - -/* - * \brief Tokenize a drawing string into a list of ASS_DrawingToken - * This also expands points for closing b-splines - */ -static ASS_DrawingToken *drawing_tokenize(char *str) -{ - char *p = str; - int i, val, type = -1, is_set = 0; - FT_Vector point = {0, 0}; - - ASS_DrawingToken *root = NULL, *tail = NULL, *spline_start = NULL; - - while (*p) { - if (*p == 'c' && spline_start) { - // Close b-splines: add the first three points of the b-spline - // back to the end - if (token_check_values(spline_start->next, 2, TOKEN_B_SPLINE)) { - for (i = 0; i < 3; i++) { - tail->next = calloc(1, sizeof(ASS_DrawingToken)); - tail->next->prev = tail; - tail = tail->next; - tail->type = TOKEN_B_SPLINE; - tail->point = spline_start->point; - spline_start = spline_start->next; - } - spline_start = NULL; - } - } else if (!is_set && mystrtoi(&p, &val)) { - point.x = val; - is_set = 1; - p--; - } else if (is_set == 1 && mystrtoi(&p, &val)) { - point.y = val; - is_set = 2; - p--; - } else if (*p == 'm') - type = TOKEN_MOVE; - else if (*p == 'n') - type = TOKEN_MOVE_NC; - else if (*p == 'l') - type = TOKEN_LINE; - else if (*p == 'b') - type = TOKEN_CUBIC_BEZIER; - else if (*p == 'q') - type = TOKEN_CONIC_BEZIER; - else if (*p == 's') - type = TOKEN_B_SPLINE; - // We're simply ignoring TOKEN_EXTEND_B_SPLINE here. - // This is not harmful at all, since it can be ommitted with - // similar result (the spline is extended anyway). - - if (type != -1 && is_set == 2) { - if (root) { - tail->next = calloc(1, sizeof(ASS_DrawingToken)); - tail->next->prev = tail; - tail = tail->next; - } else - root = tail = calloc(1, sizeof(ASS_DrawingToken)); - tail->type = type; - tail->point = point; - is_set = 0; - if (type == TOKEN_B_SPLINE && !spline_start) - spline_start = tail->prev; - } - p++; - } - - return root; -} - -/* - * \brief Free a list of tokens - */ -static void drawing_free_tokens(ASS_DrawingToken *token) -{ - while (token) { - ASS_DrawingToken *at = token; - token = token->next; - free(at); - } -} - -/* - * \brief Update drawing cbox - */ -static inline void update_cbox(ASS_Drawing *drawing, FT_Vector *point) -{ - FT_BBox *box = &drawing->cbox; - - box->xMin = FFMIN(box->xMin, point->x); - box->xMax = FFMAX(box->xMax, point->x); - box->yMin = FFMIN(box->yMin, point->y); - box->yMax = FFMAX(box->yMax, point->y); -} - -/* - * \brief Translate and scale a point coordinate according to baseline - * offset and scale. - */ -static inline void translate_point(ASS_Drawing *drawing, FT_Vector *point) -{ - point->x = drawing->point_scale_x * point->x; - point->y = drawing->point_scale_y * -point->y; - - update_cbox(drawing, point); -} - -/* - * \brief Evaluate a curve into lines - * This curve evaluator is also used in VSFilter (RTS.cpp); it's a simple - * implementation of the De Casteljau algorithm. - */ -static void drawing_evaluate_curve(ASS_Drawing *drawing, - ASS_DrawingToken *token, char spline, - int started) -{ - double cx3, cx2, cx1, cx0, cy3, cy2, cy1, cy0; - double t, h, max_accel, max_accel1, max_accel2; - FT_Vector cur = {0, 0}; - int x0, x1, x2, x3; - int y0, y1, y2, y3; - - cur = token->point; - translate_point(drawing, &cur); - x0 = cur.x; - y0 = cur.y; - token = token->next; - cur = token->point; - translate_point(drawing, &cur); - x1 = cur.x; - y1 = cur.y; - token = token->next; - cur = token->point; - translate_point(drawing, &cur); - x2 = cur.x; - y2 = cur.y; - token = token->next; - cur = token->point; - translate_point(drawing, &cur); - x3 = cur.x; - y3 = cur.y; - - if (spline) { - // 1 [-1 +3 -3 +1] - // - * [+3 -6 +3 0] - // 6 [-3 0 +3 0] - // [+1 +4 +1 0] - - double div6 = 1.0/6.0; - - cx3 = div6*(- x0+3*x1-3*x2+x3); - cx2 = div6*( 3*x0-6*x1+3*x2); - cx1 = div6*(-3*x0 +3*x2); - cx0 = div6*( x0+4*x1+1*x2); - - cy3 = div6*(- y0+3*y1-3*y2+y3); - cy2 = div6*( 3*y0-6*y1+3*y2); - cy1 = div6*(-3*y0 +3*y2); - cy0 = div6*( y0+4*y1+1*y2); - } else { - // [-1 +3 -3 +1] - // [+3 -6 +3 0] - // [-3 +3 0 0] - // [+1 0 0 0] - - cx3 = - x0+3*x1-3*x2+x3; - cx2 = 3*x0-6*x1+3*x2; - cx1 = -3*x0+3*x1; - cx0 = x0; - - cy3 = - y0+3*y1-3*y2+y3; - cy2 = 3*y0-6*y1+3*y2; - cy1 = -3*y0+3*y1; - cy0 = y0; - } - - max_accel1 = fabs(2 * cy2) + fabs(6 * cy3); - max_accel2 = fabs(2 * cx2) + fabs(6 * cx3); - - max_accel = FFMAX(max_accel1, max_accel2); - h = 1.0; - - if (max_accel > CURVE_ACCURACY) - h = sqrt(CURVE_ACCURACY / max_accel); - - if (!started) { - cur.x = cx0; - cur.y = cy0; - drawing_add_point(drawing, &cur); - } - - for (t = 0; t < 1.0; t += h) { - cur.x = cx0 + t * (cx1 + t * (cx2 + t * cx3)); - cur.y = cy0 + t * (cy1 + t * (cy2 + t * cy3)); - drawing_add_point(drawing, &cur); - } - - cur.x = cx0 + cx1 + cx2 + cx3; - cur.y = cy0 + cy1 + cy2 + cy3; - drawing_add_point(drawing, &cur); -} - -/* - * \brief Create and initialize a new drawing and return it - */ -ASS_Drawing *ass_drawing_new(void *fontconfig_priv, ASS_Font *font, - FT_Library lib) -{ - ASS_Drawing *drawing; - - drawing = calloc(1, sizeof(*drawing)); - drawing->text = calloc(1, DRAWING_INITIAL_SIZE); - drawing->size = DRAWING_INITIAL_SIZE; - drawing->cbox.xMin = drawing->cbox.yMin = INT_MAX; - drawing->cbox.xMax = drawing->cbox.yMax = INT_MIN; - drawing->fontconfig_priv = fontconfig_priv; - drawing->font = font; - drawing->ftlibrary = lib; - drawing->library = font->library; - - drawing->scale_x = 1.; - drawing->scale_y = 1.; - drawing->max_contours = GLYPH_INITIAL_CONTOURS; - drawing->max_points = GLYPH_INITIAL_POINTS; - - return drawing; -} - -/* - * \brief Free a drawing - */ -void ass_drawing_free(ASS_Drawing* drawing) -{ - if (drawing) { - free(drawing->text); - } - free(drawing); -} - -/* - * \brief Add one ASCII character to the drawing text buffer - */ -void ass_drawing_add_char(ASS_Drawing* drawing, char symbol) -{ - drawing->text[drawing->i++] = symbol; - drawing->text[drawing->i] = 0; - - if (drawing->i + 1 >= drawing->size) { - drawing->size *= 2; - drawing->text = realloc(drawing->text, drawing->size); - } -} - -/* - * \brief Create a hashcode for the drawing - * XXX: To avoid collisions a better hash algorithm might be useful. - */ -void ass_drawing_hash(ASS_Drawing* drawing) -{ - drawing->hash = fnv_32a_str(drawing->text, FNV1_32A_INIT); -} - -/* - * \brief Convert token list to outline. Calls the line and curve evaluators. - */ -FT_OutlineGlyph *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode) -{ - int started = 0; - ASS_DrawingToken *token; - FT_Vector pen = {0, 0}; - - if (drawing->font) - drawing_make_glyph(drawing, drawing->fontconfig_priv, drawing->font); - if (!drawing->glyph) - return NULL; - - drawing->tokens = drawing_tokenize(drawing->text); - drawing_prepare(drawing); - - token = drawing->tokens; - while (token) { - // Draw something according to current command - switch (token->type) { - case TOKEN_MOVE_NC: - pen = token->point; - translate_point(drawing, &pen); - token = token->next; - break; - case TOKEN_MOVE: - pen = token->point; - translate_point(drawing, &pen); - if (started) { - drawing_close_shape(drawing); - started = 0; - } - token = token->next; - break; - case TOKEN_LINE: { - FT_Vector to; - to = token->point; - translate_point(drawing, &to); - if (!started) drawing_add_point(drawing, &pen); - drawing_add_point(drawing, &to); - started = 1; - token = token->next; - break; - } - case TOKEN_CUBIC_BEZIER: - if (token_check_values(token, 3, TOKEN_CUBIC_BEZIER) && - token->prev) { - drawing_evaluate_curve(drawing, token->prev, 0, started); - token = token->next; - token = token->next; - token = token->next; - started = 1; - } else - token = token->next; - break; - case TOKEN_B_SPLINE: - if (token_check_values(token, 3, TOKEN_B_SPLINE) && - token->prev) { - drawing_evaluate_curve(drawing, token->prev, 1, started); - token = token->next; - started = 1; - } else - token = token->next; - break; - default: - token = token->next; - break; - } - } - - drawing_finish(drawing, raw_mode); - drawing_free_tokens(drawing->tokens); - return &drawing->glyph; -} diff --git a/aegisub/libass/ass_drawing.h b/aegisub/libass/ass_drawing.h deleted file mode 100644 index f677fcddd..000000000 --- a/aegisub/libass/ass_drawing.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2009 Grigori Goronzy - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef LIBASS_DRAWING_H -#define LIBASS_DRAWING_H - -#include -#include FT_GLYPH_H - -#include "ass.h" - -#define DRAWING_INITIAL_SIZE 256 - -typedef enum { - TOKEN_MOVE, - TOKEN_MOVE_NC, - TOKEN_LINE, - TOKEN_CUBIC_BEZIER, - TOKEN_CONIC_BEZIER, - TOKEN_B_SPLINE, - TOKEN_EXTEND_SPLINE, - TOKEN_CLOSE -} ASS_TokenType; - -typedef struct ass_drawing_token { - ASS_TokenType type; - FT_Vector point; - struct ass_drawing_token *next; - struct ass_drawing_token *prev; -} ASS_DrawingToken; - -typedef struct { - char *text; // drawing string - int i; // text index - int scale; // scale (1-64) for subpixel accuracy - double pbo; // drawing will be shifted in y direction by this amount - double scale_x; // FontScaleX - double scale_y; // FontScaleY - int asc; // ascender - int desc; // descender - FT_OutlineGlyph glyph; // the "fake" glyph created for later rendering - int hash; // hash value (for caching) - - // private - FT_Library ftlibrary; // needed for font ops - ASS_Font *font; // dito - void *fontconfig_priv; // dito - ASS_Library *library; - int size; // current buffer size - ASS_DrawingToken *tokens; // tokenized drawing - int max_points; // current maximum size - int max_contours; - double point_scale_x; - double point_scale_y; - FT_BBox cbox; // bounding box, or let's say... VSFilter's idea of it -} ASS_Drawing; - -ASS_Drawing *ass_drawing_new(void *fontconfig_priv, ASS_Font *font, - FT_Library lib); -void ass_drawing_free(ASS_Drawing* drawing); -void ass_drawing_add_char(ASS_Drawing* drawing, char symbol); -void ass_drawing_hash(ASS_Drawing* drawing); -FT_OutlineGlyph *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode); - -#endif /* LIBASS_DRAWING_H */ diff --git a/aegisub/libass/ass_font.c b/aegisub/libass/ass_font.c deleted file mode 100644 index 0b8816a1b..000000000 --- a/aegisub/libass/ass_font.c +++ /dev/null @@ -1,701 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include FT_FREETYPE_H -#include FT_SYNTHESIS_H -#include FT_GLYPH_H -#include FT_TRUETYPE_TABLES_H -#include FT_OUTLINE_H -#include - -#include "ass.h" -#include "ass_library.h" -#include "ass_font.h" -#include "ass_bitmap.h" -#include "ass_cache.h" -#include "ass_fontconfig.h" -#include "ass_utils.h" - -#define VERTICAL_LOWER_BOUND 0x02f1 - -/** - * Select a good charmap, prefer Microsoft Unicode charmaps. - * Otherwise, let FreeType decide. - */ -static void charmap_magic(ASS_Library *library, FT_Face face) -{ - int i; - int ms_cmap = -1; - - // Search for a Microsoft Unicode cmap - for (i = 0; i < face->num_charmaps; ++i) { - FT_CharMap cmap = face->charmaps[i]; - unsigned pid = cmap->platform_id; - unsigned eid = cmap->encoding_id; - if (pid == 3 /*microsoft */ - && (eid == 1 /*unicode bmp */ - || eid == 10 /*full unicode */ )) { - FT_Set_Charmap(face, cmap); - return; - } else if (pid == 3 && ms_cmap < 0) - ms_cmap = i; - } - - // Try the first Microsoft cmap if no Microsoft Unicode cmap was found - if (ms_cmap >= 0) { - FT_CharMap cmap = face->charmaps[ms_cmap]; - FT_Set_Charmap(face, cmap); - return; - } - - if (!face->charmap) { - if (face->num_charmaps == 0) { - ass_msg(library, MSGL_WARN, "Font face with no charmaps"); - return; - } - ass_msg(library, MSGL_WARN, - "No charmap autodetected, trying the first one"); - FT_Set_Charmap(face, face->charmaps[0]); - return; - } -} - -/** - * \brief find a memory font by name - */ -static int find_font(ASS_Library *library, char *name) -{ - int i; - for (i = 0; i < library->num_fontdata; ++i) - if (strcasecmp(name, library->fontdata[i].name) == 0) - return i; - return -1; -} - -static void face_set_size(FT_Face face, double size); - -static void buggy_font_workaround(FT_Face face) -{ - // Some fonts have zero Ascender/Descender fields in 'hhea' table. - // In this case, get the information from 'os2' table or, as - // a last resort, from face.bbox. - if (face->ascender + face->descender == 0 || face->height == 0) { - TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); - if (os2) { - face->ascender = os2->sTypoAscender; - face->descender = os2->sTypoDescender; - face->height = face->ascender - face->descender; - } else { - face->ascender = face->bbox.yMax; - face->descender = face->bbox.yMin; - face->height = face->ascender - face->descender; - } - } -} - -/** - * \brief Select a face with the given charcode and add it to ASS_Font - * \return index of the new face in font->faces, -1 if failed - */ -static int add_face(void *fc_priv, ASS_Font *font, uint32_t ch) -{ - char *path; - int index; - FT_Face face; - int error; - int mem_idx; - - if (font->n_faces == ASS_FONT_MAX_FACES) - return -1; - - path = - fontconfig_select(font->library, fc_priv, font->desc.family, - font->desc.treat_family_as_pattern, - font->desc.bold, font->desc.italic, &index, ch); - if (!path) - return -1; - - mem_idx = find_font(font->library, path); - if (mem_idx >= 0) { - error = - FT_New_Memory_Face(font->ftlibrary, - (unsigned char *) font->library-> - fontdata[mem_idx].data, - font->library->fontdata[mem_idx].size, index, - &face); - if (error) { - ass_msg(font->library, MSGL_WARN, - "Error opening memory font: '%s'", path); - free(path); - return -1; - } - } else { - error = FT_New_Face(font->ftlibrary, path, index, &face); - if (error) { - ass_msg(font->library, MSGL_WARN, - "Error opening font: '%s', %d", path, index); - free(path); - return -1; - } - } - charmap_magic(font->library, face); - buggy_font_workaround(face); - - font->faces[font->n_faces++] = face; - face_set_size(face, font->size); - free(path); - return font->n_faces - 1; -} - -/** - * \brief Create a new ASS_Font according to "desc" argument - */ -ASS_Font *ass_font_new(void *font_cache, ASS_Library *library, - FT_Library ftlibrary, void *fc_priv, - ASS_FontDesc *desc) -{ - int error; - ASS_Font *fontp; - ASS_Font font; - - fontp = ass_font_cache_find((Hashmap *) font_cache, desc); - if (fontp) - return fontp; - - font.library = library; - font.ftlibrary = ftlibrary; - font.n_faces = 0; - font.desc.family = strdup(desc->family); - font.desc.treat_family_as_pattern = desc->treat_family_as_pattern; - font.desc.bold = desc->bold; - font.desc.italic = desc->italic; - font.desc.vertical = desc->vertical; - - font.scale_x = font.scale_y = 1.; - font.v.x = font.v.y = 0; - font.size = 0.; - - error = add_face(fc_priv, &font, 0); - if (error == -1) { - free(font.desc.family); - return 0; - } else - return ass_font_cache_add((Hashmap *) font_cache, &font); -} - -/** - * \brief Set font transformation matrix and shift vector - **/ -void ass_font_set_transform(ASS_Font *font, double scale_x, - double scale_y, FT_Vector *v) -{ - font->scale_x = scale_x; - font->scale_y = scale_y; - if (v) { - font->v.x = v->x; - font->v.y = v->y; - } -} - -static void face_set_size(FT_Face face, double size) -{ - TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea); - TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); - double mscale = 1.; - FT_Size_RequestRec rq; - FT_Size_Metrics *m = &face->size->metrics; - // VSFilter uses metrics from TrueType OS/2 table - // The idea was borrowed from asa (http://asa.diac24.net) - if (hori && os2) { - int hori_height = hori->Ascender - hori->Descender; - int os2_height = os2->usWinAscent + os2->usWinDescent; - if (hori_height && os2_height) - mscale = (double) hori_height / os2_height; - } - memset(&rq, 0, sizeof(rq)); - rq.type = FT_SIZE_REQUEST_TYPE_REAL_DIM; - rq.width = 0; - rq.height = double_to_d6(size * mscale); - rq.horiResolution = rq.vertResolution = 0; - FT_Request_Size(face, &rq); - m->ascender /= mscale; - m->descender /= mscale; - m->height /= mscale; -} - -/** - * \brief Set font size - **/ -void ass_font_set_size(ASS_Font *font, double size) -{ - int i; - if (font->size != size) { - font->size = size; - for (i = 0; i < font->n_faces; ++i) - face_set_size(font->faces[i], size); - } -} - -/** - * \brief Get maximal font ascender and descender. - * \param ch character code - * The values are extracted from the font face that provides glyphs for the given character - **/ -void ass_font_get_asc_desc(ASS_Font *font, uint32_t ch, int *asc, - int *desc) -{ - int i; - for (i = 0; i < font->n_faces; ++i) { - FT_Face face = font->faces[i]; - TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); - if (FT_Get_Char_Index(face, ch)) { - int y_scale = face->size->metrics.y_scale; - if (os2) { - *asc = FT_MulFix(os2->usWinAscent, y_scale); - *desc = FT_MulFix(os2->usWinDescent, y_scale); - } else { - *asc = FT_MulFix(face->ascender, y_scale); - *desc = FT_MulFix(-face->descender, y_scale); - } - if (font->desc.vertical && ch >= VERTICAL_LOWER_BOUND) { - *asc = FT_MulFix(face->max_advance_width, y_scale); - } - return; - } - } - - *asc = *desc = 0; -} - -/* - * Strike a glyph with a horizontal line; it's possible to underline it - * and/or strike through it. For the line's position and size, truetype - * tables are consulted. Obviously this relies on the data in the tables - * being accurate. - * - */ -static int ass_strike_outline_glyph(FT_Face face, ASS_Font *font, - FT_Glyph glyph, int under, int through) -{ - TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); - TT_Postscript *ps = FT_Get_Sfnt_Table(face, ft_sfnt_post); - FT_Outline *ol = &((FT_OutlineGlyph) glyph)->outline; - int bear, advance, y_scale, i, dir; - - if (!under && !through) - return 0; - - // Grow outline - i = (under ? 4 : 0) + (through ? 4 : 0); - ol->points = realloc(ol->points, sizeof(FT_Vector) * - (ol->n_points + i)); - ol->tags = realloc(ol->tags, ol->n_points + i); - i = !!under + !!through; - ol->contours = realloc(ol->contours, sizeof(short) * - (ol->n_contours + i)); - - // If the bearing is negative, the glyph starts left of the current - // pen position - bear = FFMIN(face->glyph->metrics.horiBearingX, 0); - // We're adding half a pixel to avoid small gaps - advance = d16_to_d6(glyph->advance.x) + 32; - y_scale = face->size->metrics.y_scale; - - // Reverse drawing direction for non-truetype fonts - dir = FT_Outline_Get_Orientation(ol); - - // Add points to the outline - if (under && ps) { - int pos = FT_MulFix(ps->underlinePosition, y_scale * font->scale_y); - int size = FT_MulFix(ps->underlineThickness, - y_scale * font->scale_y / 2); - - FT_Vector points[4]; - - if (pos > 0 || size <= 0) - return 1; - - points[0].x = bear; - points[0].y = pos + size; - points[1].x = advance; - points[1].y = pos + size; - points[2].x = advance; - points[2].y = pos - size; - points[3].x = bear; - points[3].y = pos - size; - - if (dir == FT_ORIENTATION_TRUETYPE) { - for (i = 0; i < 4; i++) { - ol->points[ol->n_points] = points[i]; - ol->tags[ol->n_points++] = 1; - } - } else { - for (i = 3; i >= 0; i--) { - ol->points[ol->n_points] = points[i]; - ol->tags[ol->n_points++] = 1; - } - } - - ol->contours[ol->n_contours++] = ol->n_points - 1; - } - - if (through && os2) { - int pos = FT_MulFix(os2->yStrikeoutPosition, y_scale * font->scale_y); - int size = FT_MulFix(os2->yStrikeoutSize, y_scale * font->scale_y / 2); - FT_Vector points[4]; - - if (pos < 0 || size <= 0) - return 1; - - points[0].x = bear; - points[0].y = pos + size; - points[1].x = advance; - points[1].y = pos + size; - points[2].x = advance; - points[2].y = pos - size; - points[3].x = bear; - points[3].y = pos - size; - - if (dir == FT_ORIENTATION_TRUETYPE) { - for (i = 0; i < 4; i++) { - ol->points[ol->n_points] = points[i]; - ol->tags[ol->n_points++] = 1; - } - } else { - for (i = 3; i >= 0; i--) { - ol->points[ol->n_points] = points[i]; - ol->tags[ol->n_points++] = 1; - } - } - - ol->contours[ol->n_contours++] = ol->n_points - 1; - } - - return 0; -} - -/** - * Slightly embold a glyph without touching its metrics - */ -static void ass_glyph_embolden(FT_GlyphSlot slot) -{ - int str; - - if (slot->format != FT_GLYPH_FORMAT_OUTLINE) - return; - - str = FT_MulFix(slot->face->units_per_EM, - slot->face->size->metrics.y_scale) / 64; - - FT_Outline_Embolden(&slot->outline, str); -} - -/** - * \brief Get a glyph - * \param ch character code - **/ -FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font, - uint32_t ch, ASS_Hinting hinting, int deco) -{ - int error; - int index = 0; - int i; - FT_Glyph glyph; - FT_Face face = 0; - int flags = 0; - int vertical = font->desc.vertical; - - if (ch < 0x20) - return 0; - // Handle NBSP like a regular space when rendering the glyph - if (ch == 0xa0) - ch = ' '; - if (font->n_faces == 0) - return 0; - - for (i = 0; i < font->n_faces; ++i) { - face = font->faces[i]; - index = FT_Get_Char_Index(face, ch); - if (index) - break; - } - -#ifdef CONFIG_FONTCONFIG - if (index == 0) { - int face_idx; - ass_msg(font->library, MSGL_INFO, - "Glyph 0x%X not found, selecting one more " - "font for (%s, %d, %d)", ch, font->desc.family, - font->desc.bold, font->desc.italic); - face_idx = add_face(fontconfig_priv, font, ch); - if (face_idx >= 0) { - face = font->faces[face_idx]; - index = FT_Get_Char_Index(face, ch); - if (index == 0 && face->num_charmaps > 0) { - FT_CharMap cur = face->charmap; - ass_msg(font->library, MSGL_WARN, - "Glyph 0x%X not found, falling back to first charmap", ch); - FT_Set_Charmap(face, face->charmaps[0]); - index = FT_Get_Char_Index(face, ch); - FT_Set_Charmap(face, cur); - } - if (index == 0) { - ass_msg(font->library, MSGL_ERR, - "Glyph 0x%X not found in font for (%s, %d, %d)", - ch, font->desc.family, font->desc.bold, - font->desc.italic); - } - } - } -#endif - - flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH - | FT_LOAD_IGNORE_TRANSFORM; - switch (hinting) { - case ASS_HINTING_NONE: - flags |= FT_LOAD_NO_HINTING; - break; - case ASS_HINTING_LIGHT: - flags |= FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT; - break; - case ASS_HINTING_NORMAL: - flags |= FT_LOAD_FORCE_AUTOHINT; - break; - case ASS_HINTING_NATIVE: - break; - } - - error = FT_Load_Glyph(face, index, flags); - if (error) { - ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d", - index); - return 0; - } - if (!(face->style_flags & FT_STYLE_FLAG_ITALIC) && - (font->desc.italic > 55)) { - FT_GlyphSlot_Oblique(face->glyph); - } - - if (!(face->style_flags & FT_STYLE_FLAG_BOLD) && - (font->desc.bold > 80)) { - ass_glyph_embolden(face->glyph); - } - error = FT_Get_Glyph(face->glyph, &glyph); - if (error) { - ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d", - index); - return 0; - } - - // Rotate glyph, if needed - if (vertical && ch >= VERTICAL_LOWER_BOUND) { - FT_Matrix m = { 0, double_to_d16(-1.0), double_to_d16(1.0), 0 }; - FT_Outline_Transform(&((FT_OutlineGlyph) glyph)->outline, &m); - FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline, - face->glyph->metrics.vertAdvance, - 0); - glyph->advance.x = face->glyph->linearVertAdvance; - } - - // Apply scaling and shift - { - FT_Matrix scale = { double_to_d16(font->scale_x), 0, 0, - double_to_d16(font->scale_y) }; - FT_Outline *outl = &((FT_OutlineGlyph) glyph)->outline; - FT_Outline_Transform(outl, &scale); - FT_Outline_Translate(outl, font->v.x, font->v.y); - glyph->advance.x *= font->scale_x; - - ass_strike_outline_glyph(face, font, glyph, deco & DECO_UNDERLINE, - deco & DECO_STRIKETHROUGH); - } - - return glyph; -} - -/** - * \brief Get kerning for the pair of glyphs. - **/ -FT_Vector ass_font_get_kerning(ASS_Font *font, uint32_t c1, uint32_t c2) -{ - FT_Vector v = { 0, 0 }; - int i; - - if (font->desc.vertical) - return v; - - for (i = 0; i < font->n_faces; ++i) { - FT_Face face = font->faces[i]; - int i1 = FT_Get_Char_Index(face, c1); - int i2 = FT_Get_Char_Index(face, c2); - if (i1 && i2) { - if (FT_HAS_KERNING(face)) - FT_Get_Kerning(face, i1, i2, FT_KERNING_DEFAULT, &v); - return v; - } - if (i1 || i2) // these glyphs are from different font faces, no kerning information - return v; - } - return v; -} - -/** - * \brief Deallocate ASS_Font - **/ -void ass_font_free(ASS_Font *font) -{ - int i; - for (i = 0; i < font->n_faces; ++i) - if (font->faces[i]) - FT_Done_Face(font->faces[i]); - free(font->desc.family); - free(font); -} - -/** - * \brief Calculate the cbox of a series of points - */ -static void -get_contour_cbox(FT_BBox *box, FT_Vector *points, int start, int end) -{ - int i; - box->xMin = box->yMin = INT_MAX; - box->xMax = box->yMax = INT_MIN; - - for (i = start; i <= end; i++) { - box->xMin = (points[i].x < box->xMin) ? points[i].x : box->xMin; - box->xMax = (points[i].x > box->xMax) ? points[i].x : box->xMax; - box->yMin = (points[i].y < box->yMin) ? points[i].y : box->yMin; - box->yMax = (points[i].y > box->yMax) ? points[i].y : box->yMax; - } -} - -/** - * \brief Determine winding direction of a contour - * \return direction; 0 = clockwise - */ -static int get_contour_direction(FT_Vector *points, int start, int end) -{ - int i; - long long sum = 0; - int x = points[start].x; - int y = points[start].y; - for (i = start + 1; i <= end; i++) { - sum += x * (points[i].y - y) - y * (points[i].x - x); - x = points[i].x; - y = points[i].y; - } - sum += x * (points[start].y - y) - y * (points[start].x - x); - return sum > 0; -} - -/** - * \brief Fix-up stroker result for huge borders by removing inside contours - * that would reverse in size - */ -void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, int border_y) -{ - int nc = glyph->outline.n_contours; - int begin, stop; - char modified = 0; - char *valid_cont = malloc(nc); - int start = 0; - int end = -1; - FT_BBox *boxes = malloc(nc * sizeof(FT_BBox)); - int i, j; - int inside_direction; - - inside_direction = FT_Outline_Get_Orientation(&glyph->outline) == - FT_ORIENTATION_TRUETYPE; - - // create a list of cboxes of the contours - for (i = 0; i < nc; i++) { - start = end + 1; - end = glyph->outline.contours[i]; - get_contour_cbox(&boxes[i], glyph->outline.points, start, end); - } - - // for each contour, check direction and whether it's "outside" - // or contained in another contour - end = -1; - for (i = 0; i < nc; i++) { - int dir; - start = end + 1; - end = glyph->outline.contours[i]; - dir = get_contour_direction(glyph->outline.points, start, end); - valid_cont[i] = 1; - if (dir == inside_direction) { - for (j = 0; j < nc; j++) { - if (i == j) - continue; - if (boxes[i].xMin >= boxes[j].xMin && - boxes[i].xMax <= boxes[j].xMax && - boxes[i].yMin >= boxes[j].yMin && - boxes[i].yMax <= boxes[j].yMax) - goto check_inside; - } - /* "inside" contour but we can't find anything it could be - * inside of - assume the font is buggy and it should be - * an "outside" contour, and reverse it */ - for (j = 0; j < (end + 1 - start) / 2; j++) { - FT_Vector temp = glyph->outline.points[start + j]; - char temp2 = glyph->outline.tags[start + j]; - glyph->outline.points[start + j] = glyph->outline.points[end - j]; - glyph->outline.points[end - j] = temp; - glyph->outline.tags[start + j] = glyph->outline.tags[end - j]; - glyph->outline.tags[end - j] = temp2; - } - dir ^= 1; - } - check_inside: - if (dir == inside_direction) { - FT_BBox box; - int width, height; - get_contour_cbox(&box, glyph->outline.points, start, end); - width = box.xMax - box.xMin; - height = box.yMax - box.yMin; - if (width < border_x * 2 || height < border_y * 2) { - valid_cont[i] = 0; - modified = 1; - } - } - } - - // zero-out contours that can be removed; much simpler than copying - if (modified) { - for (i = 0; i < nc; i++) { - if (valid_cont[i]) - continue; - begin = (i == 0) ? 0 : glyph->outline.contours[i - 1] + 1; - stop = glyph->outline.contours[i]; - for (j = begin; j <= stop; j++) { - glyph->outline.points[j].x = 0; - glyph->outline.points[j].y = 0; - glyph->outline.tags[j] = 0; - } - } - } - - free(boxes); - free(valid_cont); -} - diff --git a/aegisub/libass/ass_font.h b/aegisub/libass/ass_font.h deleted file mode 100644 index ab4054813..000000000 --- a/aegisub/libass/ass_font.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef LIBASS_FONT_H -#define LIBASS_FONT_H - -#include -#include -#include FT_GLYPH_H -#include "ass.h" -#include "ass_types.h" - -#define ASS_FONT_MAX_FACES 10 -#define DECO_UNDERLINE 1 -#define DECO_STRIKETHROUGH 2 - -typedef struct { - char *family; - unsigned bold; - unsigned italic; - int treat_family_as_pattern; - int vertical; // @font vertical layout -} ASS_FontDesc; - -typedef struct { - ASS_FontDesc desc; - ASS_Library *library; - FT_Library ftlibrary; - FT_Face faces[ASS_FONT_MAX_FACES]; - int n_faces; - double scale_x, scale_y; // current transform - FT_Vector v; // current shift - double size; -} ASS_Font; - -// FIXME: passing the hashmap via a void pointer is very ugly. -ASS_Font *ass_font_new(void *font_cache, ASS_Library *library, - FT_Library ftlibrary, void *fc_priv, - ASS_FontDesc *desc); -void ass_font_set_transform(ASS_Font *font, double scale_x, - double scale_y, FT_Vector *v); -void ass_font_set_size(ASS_Font *font, double size); -void ass_font_get_asc_desc(ASS_Font *font, uint32_t ch, int *asc, - int *desc); -FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font, - uint32_t ch, ASS_Hinting hinting, int flags); -FT_Vector ass_font_get_kerning(ASS_Font *font, uint32_t c1, uint32_t c2); -void ass_font_free(ASS_Font *font); -void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, int border_y); - -#endif /* LIBASS_FONT_H */ diff --git a/aegisub/libass/ass_fontconfig.c b/aegisub/libass/ass_fontconfig.c deleted file mode 100644 index 2571739ff..000000000 --- a/aegisub/libass/ass_fontconfig.c +++ /dev/null @@ -1,531 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include FT_FREETYPE_H - -#include "ass_utils.h" -#include "ass.h" -#include "ass_library.h" -#include "ass_fontconfig.h" - -#ifdef CONFIG_FONTCONFIG -#include -#include -#endif - -struct fc_instance { -#ifdef CONFIG_FONTCONFIG - FcConfig *config; -#endif - char *family_default; - char *path_default; - int index_default; -}; - -#ifdef CONFIG_FONTCONFIG - -/** - * \brief Case-insensitive match ASS/SSA font family against full name. (also - * known as "name for humans") - * - * \param lib library instance - * \param priv fontconfig instance - * \param family font fullname - * \param bold weight attribute - * \param italic italic attribute - * \return font set - */ -static FcFontSet * -match_fullname(ASS_Library *lib, FCInstance *priv, const char *family, - unsigned bold, unsigned italic) -{ - FcFontSet *sets[2]; - FcFontSet *result = FcFontSetCreate(); - int nsets = 0; - int i, fi; - - if ((sets[nsets] = FcConfigGetFonts(priv->config, FcSetSystem))) - nsets++; - if ((sets[nsets] = FcConfigGetFonts(priv->config, FcSetApplication))) - nsets++; - - // Run over font sets and patterns and try to match against full name - for (i = 0; i < nsets; i++) { - FcFontSet *set = sets[i]; - for (fi = 0; fi < set->nfont; fi++) { - FcPattern *pat = set->fonts[fi]; - char *fullname; - int pi = 0, at; - FcBool ol; - while (FcPatternGetString(pat, FC_FULLNAME, pi++, - (FcChar8 **) &fullname) == FcResultMatch) { - if (FcPatternGetBool(pat, FC_OUTLINE, 0, &ol) != FcResultMatch - || ol != FcTrue) - continue; - if (FcPatternGetInteger(pat, FC_SLANT, 0, &at) != FcResultMatch - || at < italic) - continue; - if (FcPatternGetInteger(pat, FC_WEIGHT, 0, &at) != FcResultMatch - || at < bold) - continue; - if (strcasecmp(fullname, family) == 0) { - FcFontSetAdd(result, FcPatternDuplicate(pat)); - break; - } - } - } - } - - return result; -} - -/** - * \brief Low-level font selection. - * \param priv private data - * \param family font family - * \param treat_family_as_pattern treat family as fontconfig pattern - * \param bold font weight value - * \param italic font slant value - * \param index out: font index inside a file - * \param code: the character that should be present in the font, can be 0 - * \return font file path -*/ -static char *select_font(ASS_Library *library, FCInstance *priv, - const char *family, int treat_family_as_pattern, - unsigned bold, unsigned italic, int *index, - uint32_t code) -{ - FcBool rc; - FcResult result; - FcPattern *pat = NULL, *rpat = NULL; - int r_index, r_slant, r_weight; - FcChar8 *r_family, *r_style, *r_file, *r_fullname; - FcBool r_outline, r_embolden; - FcCharSet *r_charset; - FcFontSet *ffullname = NULL, *fsorted = NULL, *fset = NULL; - int curf; - char *retval = NULL; - int family_cnt = 0; - - *index = 0; - - if (treat_family_as_pattern) - pat = FcNameParse((const FcChar8 *) family); - else - pat = FcPatternCreate(); - - if (!pat) - goto error; - - if (!treat_family_as_pattern) { - FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *) family); - - // In SSA/ASS fonts are sometimes referenced by their "full name", - // which is usually a concatenation of family name and font - // style (ex. Ottawa Bold). Full name is available from - // FontConfig pattern element FC_FULLNAME, but it is never - // used for font matching. - // Therefore, I'm removing words from the end of the name one - // by one, and adding shortened names to the pattern. It seems - // that the first value (full name in this case) has - // precedence in matching. - // An alternative approach could be to reimplement FcFontSort - // using FC_FULLNAME instead of FC_FAMILY. - family_cnt = 1; - { - char *s = strdup(family); - char *p = s + strlen(s); - while (--p > s) - if (*p == ' ' || *p == '-') { - *p = '\0'; - FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *) s); - ++family_cnt; - } - free(s); - } - } - FcPatternAddBool(pat, FC_OUTLINE, FcTrue); - FcPatternAddInteger(pat, FC_SLANT, italic); - FcPatternAddInteger(pat, FC_WEIGHT, bold); - - FcDefaultSubstitute(pat); - - rc = FcConfigSubstitute(priv->config, pat, FcMatchPattern); - if (!rc) - goto error; - - fsorted = FcFontSort(priv->config, pat, FcTrue, NULL, &result); - ffullname = match_fullname(library, priv, family, bold, italic); - if (!fsorted || !ffullname) - goto error; - - fset = FcFontSetCreate(); - for (curf = 0; curf < ffullname->nfont; ++curf) { - FcPattern *curp = ffullname->fonts[curf]; - FcPatternReference(curp); - FcFontSetAdd(fset, curp); - } - for (curf = 0; curf < fsorted->nfont; ++curf) { - FcPattern *curp = fsorted->fonts[curf]; - FcPatternReference(curp); - FcFontSetAdd(fset, curp); - } - - for (curf = 0; curf < fset->nfont; ++curf) { - FcPattern *curp = fset->fonts[curf]; - - result = FcPatternGetBool(curp, FC_OUTLINE, 0, &r_outline); - if (result != FcResultMatch) - continue; - if (r_outline != FcTrue) - continue; - if (!code) - break; - result = FcPatternGetCharSet(curp, FC_CHARSET, 0, &r_charset); - if (result != FcResultMatch) - continue; - if (FcCharSetHasChar(r_charset, code)) - break; - } - - if (curf >= fset->nfont) - goto error; - - if (!treat_family_as_pattern) { - // Remove all extra family names from original pattern. - // After this, FcFontRenderPrepare will select the most relevant family - // name in case there are more than one of them. - for (; family_cnt > 1; --family_cnt) - FcPatternRemove(pat, FC_FAMILY, family_cnt - 1); - } - - rpat = FcFontRenderPrepare(priv->config, pat, fset->fonts[curf]); - if (!rpat) - goto error; - - result = FcPatternGetInteger(rpat, FC_INDEX, 0, &r_index); - if (result != FcResultMatch) - goto error; - *index = r_index; - - result = FcPatternGetString(rpat, FC_FILE, 0, &r_file); - if (result != FcResultMatch) - goto error; - retval = strdup((const char *) r_file); - - result = FcPatternGetString(rpat, FC_FAMILY, 0, &r_family); - if (result != FcResultMatch) - r_family = NULL; - - result = FcPatternGetString(rpat, FC_FULLNAME, 0, &r_fullname); - if (result != FcResultMatch) - r_fullname = NULL; - - if (!treat_family_as_pattern && - !(r_family && strcasecmp((const char *) r_family, family) == 0) && - !(r_fullname && strcasecmp((const char *) r_fullname, family) == 0)) - ass_msg(library, MSGL_WARN, - "fontconfig: Selected font is not the requested one: " - "'%s' != '%s'", - (const char *) (r_fullname ? r_fullname : r_family), family); - - result = FcPatternGetString(rpat, FC_STYLE, 0, &r_style); - if (result != FcResultMatch) - r_style = NULL; - - result = FcPatternGetInteger(rpat, FC_SLANT, 0, &r_slant); - if (result != FcResultMatch) - r_slant = 0; - - result = FcPatternGetInteger(rpat, FC_WEIGHT, 0, &r_weight); - if (result != FcResultMatch) - r_weight = 0; - - result = FcPatternGetBool(rpat, FC_EMBOLDEN, 0, &r_embolden); - if (result != FcResultMatch) - r_embolden = 0; - - ass_msg(library, MSGL_V, - "Font info: family '%s', style '%s', fullname '%s'," - " slant %d, weight %d%s", (const char *) r_family, - (const char *) r_style, (const char *) r_fullname, r_slant, - r_weight, r_embolden ? ", embolden" : ""); - - error: - if (pat) - FcPatternDestroy(pat); - if (rpat) - FcPatternDestroy(rpat); - if (fsorted) - FcFontSetDestroy(fsorted); - if (ffullname) - FcFontSetDestroy(ffullname); - if (fset) - FcFontSetDestroy(fset); - return retval; -} - -/** - * \brief Find a font. Use default family or path if necessary. - * \param priv_ private data - * \param family font family - * \param treat_family_as_pattern treat family as fontconfig pattern - * \param bold font weight value - * \param italic font slant value - * \param index out: font index inside a file - * \param code: the character that should be present in the font, can be 0 - * \return font file path -*/ -char *fontconfig_select(ASS_Library *library, FCInstance *priv, - const char *family, int treat_family_as_pattern, - unsigned bold, unsigned italic, int *index, - uint32_t code) -{ - char *res = 0; - if (!priv->config) { - *index = priv->index_default; - res = priv->path_default ? strdup(priv->path_default) : 0; - return res; - } - if (family && *family) - res = - select_font(library, priv, family, treat_family_as_pattern, - bold, italic, index, code); - if (!res && priv->family_default) { - res = - select_font(library, priv, priv->family_default, 0, bold, - italic, index, code); - if (res) - ass_msg(library, MSGL_WARN, "fontconfig_select: Using default " - "font family: (%s, %d, %d) -> %s, %d", - family, bold, italic, res, *index); - } - if (!res && priv->path_default) { - res = strdup(priv->path_default); - *index = priv->index_default; - ass_msg(library, MSGL_WARN, "fontconfig_select: Using default font: " - "(%s, %d, %d) -> %s, %d", family, bold, italic, - res, *index); - } - if (!res) { - res = select_font(library, priv, "Arial", 0, bold, italic, - index, code); - if (res) - ass_msg(library, MSGL_WARN, "fontconfig_select: Using 'Arial' " - "font family: (%s, %d, %d) -> %s, %d", family, bold, - italic, res, *index); - } - if (res) - ass_msg(library, MSGL_V, - "fontconfig_select: (%s, %d, %d) -> %s, %d", family, bold, - italic, res, *index); - return res; -} - -/** - * \brief Process memory font. - * \param priv private data - * \param library library object - * \param ftlibrary freetype library object - * \param idx index of the processed font in library->fontdata - * - * Builds a font pattern in memory via FT_New_Memory_Face/FcFreeTypeQueryFace. -*/ -static void process_fontdata(FCInstance *priv, ASS_Library *library, - FT_Library ftlibrary, int idx) -{ - int rc; - const char *name = library->fontdata[idx].name; - const char *data = library->fontdata[idx].data; - int data_size = library->fontdata[idx].size; - - FT_Face face; - FcPattern *pattern; - FcFontSet *fset; - FcBool res; - int face_index, num_faces = 1; - - for (face_index = 0; face_index < num_faces; ++face_index) { - rc = FT_New_Memory_Face(ftlibrary, (unsigned char *) data, - data_size, face_index, &face); - if (rc) { - ass_msg(library, MSGL_WARN, "Error opening memory font: %s", - name); - return; - } - num_faces = face->num_faces; - - pattern = - FcFreeTypeQueryFace(face, (unsigned char *) name, face_index, - FcConfigGetBlanks(priv->config)); - if (!pattern) { - ass_msg(library, MSGL_WARN, "%s failed", "FcFreeTypeQueryFace"); - FT_Done_Face(face); - return; - } - - fset = FcConfigGetFonts(priv->config, FcSetSystem); // somehow it failes when asked for FcSetApplication - if (!fset) { - ass_msg(library, MSGL_WARN, "%s failed", "FcConfigGetFonts"); - FT_Done_Face(face); - return; - } - - res = FcFontSetAdd(fset, pattern); - if (!res) { - ass_msg(library, MSGL_WARN, "%s failed", "FcFontSetAdd"); - FT_Done_Face(face); - return; - } - - FT_Done_Face(face); - } -} - -/** - * \brief Init fontconfig. - * \param library libass library object - * \param ftlibrary freetype library object - * \param family default font family - * \param path default font path - * \param fc whether fontconfig should be used - * \param config path to a fontconfig configuration file, or NULL - * \param update whether the fontconfig cache should be built/updated - * \return pointer to fontconfig private data -*/ -FCInstance *fontconfig_init(ASS_Library *library, - FT_Library ftlibrary, const char *family, - const char *path, int fc, const char *config, - int update) -{ - int rc; - FCInstance *priv = calloc(1, sizeof(FCInstance)); - const char *dir = library->fonts_dir; - int i; - - if (!fc) { - ass_msg(library, MSGL_WARN, - "Fontconfig disabled, only default font will be used."); - goto exit; - } - - priv->config = FcConfigCreate(); - rc = FcConfigParseAndLoad(priv->config, (unsigned char *) config, FcTrue); - if (!rc) { - ass_msg(library, MSGL_WARN, "No usable fontconfig configuration " - "file found, using fallback."); - FcConfigDestroy(priv->config); - priv->config = FcInitLoadConfig(); - rc++; - } - if (rc && update) { - FcConfigBuildFonts(priv->config); - } - - if (!rc || !priv->config) { - ass_msg(library, MSGL_FATAL, - "No valid fontconfig configuration found!"); - FcConfigDestroy(priv->config); - goto exit; - } - - for (i = 0; i < library->num_fontdata; ++i) - process_fontdata(priv, library, ftlibrary, i); - - if (dir) { - ass_msg(library, MSGL_V, "Updating font cache"); - - rc = FcConfigAppFontAddDir(priv->config, (const FcChar8 *) dir); - if (!rc) { - ass_msg(library, MSGL_WARN, "%s failed", "FcConfigAppFontAddDir"); - } - } - - priv->family_default = family ? strdup(family) : NULL; -exit: - priv->path_default = path ? strdup(path) : NULL; - priv->index_default = 0; - - return priv; -} - -int fontconfig_update(FCInstance *priv) -{ - return FcConfigBuildFonts(priv->config); -} - -#else /* CONFIG_FONTCONFIG */ - -char *fontconfig_select(ASS_Library *library, FCInstance *priv, - const char *family, int treat_family_as_pattern, - unsigned bold, unsigned italic, int *index, - uint32_t code) -{ - *index = priv->index_default; - char* res = priv->path_default ? strdup(priv->path_default) : 0; - return res; -} - -FCInstance *fontconfig_init(ASS_Library *library, - FT_Library ftlibrary, const char *family, - const char *path, int fc, const char *config, - int update) -{ - FCInstance *priv; - - ass_msg(library, MSGL_WARN, - "Fontconfig disabled, only default font will be used."); - - priv = calloc(1, sizeof(FCInstance)); - - priv->path_default = path ? strdup(path) : 0; - priv->index_default = 0; - return priv; -} - -int fontconfig_update(FCInstance *priv) -{ - // Do nothing - return 1; -} - -#endif - -void fontconfig_done(FCInstance *priv) -{ - - if (priv) { -#ifdef CONFIG_FONTCONFIG - FcConfigDestroy(priv->config); -#endif - free(priv->path_default); - free(priv->family_default); - } - free(priv); -} diff --git a/aegisub/libass/ass_fontconfig.h b/aegisub/libass/ass_fontconfig.h deleted file mode 100644 index 396fb72d0..000000000 --- a/aegisub/libass/ass_fontconfig.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef LIBASS_FONTCONFIG_H -#define LIBASS_FONTCONFIG_H - -#include -#include "ass_types.h" -#include "ass.h" -#include -#include FT_FREETYPE_H - -#ifdef CONFIG_FONTCONFIG -#include -#endif - -typedef struct fc_instance FCInstance; - -FCInstance *fontconfig_init(ASS_Library *library, - FT_Library ftlibrary, const char *family, - const char *path, int fc, const char *config, - int update); -char *fontconfig_select(ASS_Library *library, FCInstance *priv, - const char *family, int treat_family_as_pattern, - unsigned bold, unsigned italic, int *index, - uint32_t code); -void fontconfig_done(FCInstance *priv); -int fontconfig_update(FCInstance *priv); - -#endif /* LIBASS_FONTCONFIG_H */ diff --git a/aegisub/libass/ass_library.c b/aegisub/libass/ass_library.c deleted file mode 100644 index 5bca64485..000000000 --- a/aegisub/libass/ass_library.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#include "ass.h" -#include "ass_library.h" -#include "ass_utils.h" - -static void ass_msg_handler(int level, const char *fmt, va_list va, void *data) -{ - if (level > MSGL_INFO) - return; - fprintf(stderr, "[ass] "); - vfprintf(stderr, fmt, va); - fprintf(stderr, "\n"); -} - -ASS_Library *ass_library_init(void) -{ - ASS_Library* lib = calloc(1, sizeof(*lib)); - lib->msg_callback = ass_msg_handler; - - return lib; -} - -void ass_library_done(ASS_Library *priv) -{ - if (priv) { - ass_set_fonts_dir(priv, NULL); - ass_set_style_overrides(priv, NULL); - ass_clear_fonts(priv); - free(priv); - } -} - -void ass_set_fonts_dir(ASS_Library *priv, const char *fonts_dir) -{ - free(priv->fonts_dir); - - priv->fonts_dir = fonts_dir ? strdup(fonts_dir) : 0; -} - -void ass_set_extract_fonts(ASS_Library *priv, int extract) -{ - priv->extract_fonts = !!extract; -} - -void ass_set_style_overrides(ASS_Library *priv, char **list) -{ - char **p; - char **q; - int cnt; - - if (priv->style_overrides) { - for (p = priv->style_overrides; *p; ++p) - free(*p); - } - free(priv->style_overrides); - - if (!list) - return; - - for (p = list, cnt = 0; *p; ++p, ++cnt) { - } - - priv->style_overrides = malloc((cnt + 1) * sizeof(char *)); - for (p = list, q = priv->style_overrides; *p; ++p, ++q) - *q = strdup(*p); - priv->style_overrides[cnt] = NULL; -} - -static void grow_array(void **array, int nelem, size_t elsize) -{ - if (!(nelem & 31)) - *array = realloc(*array, (nelem + 32) * elsize); -} - -void ass_add_font(ASS_Library *priv, char *name, char *data, int size) -{ - int idx = priv->num_fontdata; - if (!name || !data || !size) - return; - grow_array((void **) &priv->fontdata, priv->num_fontdata, - sizeof(*priv->fontdata)); - - priv->fontdata[idx].name = strdup(name); - - priv->fontdata[idx].data = malloc(size); - memcpy(priv->fontdata[idx].data, data, size); - - priv->fontdata[idx].size = size; - - priv->num_fontdata++; -} - -void ass_clear_fonts(ASS_Library *priv) -{ - int i; - for (i = 0; i < priv->num_fontdata; ++i) { - free(priv->fontdata[i].name); - free(priv->fontdata[i].data); - } - free(priv->fontdata); - priv->fontdata = NULL; - priv->num_fontdata = 0; -} - -/* - * Register a message callback function with libass. Without setting one, - * a default handler is used which prints everything with MSGL_INFO or - * higher to the standard output. - * - * \param msg_cb the callback function - * \param data additional data that will be passed to the callback - */ -void ass_set_message_cb(ASS_Library *priv, - void (*msg_cb)(int, const char *, va_list, void *), - void *data) -{ - if (msg_cb) { - priv->msg_callback = msg_cb; - priv->msg_callback_data = data; - } -} diff --git a/aegisub/libass/ass_library.h b/aegisub/libass/ass_library.h deleted file mode 100644 index 8faf15e93..000000000 --- a/aegisub/libass/ass_library.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef LIBASS_LIBRARY_H -#define LIBASS_LIBRARY_H - -#include - -typedef struct { - char *name; - char *data; - int size; -} ASS_Fontdata; - -struct ass_library { - char *fonts_dir; - int extract_fonts; - char **style_overrides; - - ASS_Fontdata *fontdata; - int num_fontdata; - void (*msg_callback)(int, const char *, va_list, void *); - void *msg_callback_data; -}; - -#endif /* LIBASS_LIBRARY_H */ diff --git a/aegisub/libass/ass_parse.c b/aegisub/libass/ass_parse.c deleted file mode 100644 index 40aaf0430..000000000 --- a/aegisub/libass/ass_parse.c +++ /dev/null @@ -1,943 +0,0 @@ -/* - * Copyright (C) 2009 Grigori Goronzy - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include - -#include "ass_render.h" -#include "ass_parse.h" - -#define MAX_BE 127 -#define NBSP 0xa0 // unicode non-breaking space character - -#define skip_to(x) while ((*p != (x)) && (*p != '}') && (*p != 0)) { ++p;} -#define skip(x) if (*p == (x)) ++p; else { return p; } -#define skipopt(x) if (*p == (x)) { ++p; } - -/** - * \brief Check if starting part of (*p) matches sample. - * If true, shift p to the first symbol after the matching part. - */ -static inline int mystrcmp(char **p, const char *sample) -{ - int len = strlen(sample); - if (strncmp(*p, sample, len) == 0) { - (*p) += len; - return 1; - } else - return 0; -} - -static void change_font_size(ASS_Renderer *render_priv, double sz) -{ - double size = sz * render_priv->font_scale; - - if (size < 1) - size = 1; - else if (size > render_priv->height * 2) - size = render_priv->height * 2; - - ass_font_set_size(render_priv->state.font, size); - - render_priv->state.font_size = sz; -} - -/** - * \brief Change current font, using setting from render_priv->state. - */ -void update_font(ASS_Renderer *render_priv) -{ - unsigned val; - ASS_FontDesc desc; - desc.treat_family_as_pattern = render_priv->state.treat_family_as_pattern; - - if (render_priv->state.family[0] == '@') { - desc.vertical = 1; - desc.family = strdup(render_priv->state.family + 1); - } else { - desc.vertical = 0; - desc.family = strdup(render_priv->state.family); - } - - val = render_priv->state.bold; - // 0 = normal, 1 = bold, >1 = exact weight - if (val == 1 || val == -1) - val = 200; // bold - else if (val <= 0) - val = 80; // normal - desc.bold = val; - - val = render_priv->state.italic; - if (val == 1 || val == -1) - val = 110; // italic - else if (val <= 0) - val = 0; // normal - desc.italic = val; - - render_priv->state.font = - ass_font_new(render_priv->cache.font_cache, render_priv->library, - render_priv->ftlibrary, render_priv->fontconfig_priv, - &desc); - free(desc.family); - - if (render_priv->state.font) - change_font_size(render_priv, render_priv->state.font_size); -} - -/** - * \brief Change border width - * negative value resets border to style value - */ -void change_border(ASS_Renderer *render_priv, double border_x, - double border_y) -{ - int bord; - if (!render_priv->state.font) - return; - - if (border_x < 0 && border_y < 0) { - if (render_priv->state.style->BorderStyle == 1 || - render_priv->state.style->BorderStyle == 3) - border_x = border_y = render_priv->state.style->Outline; - else - border_x = border_y = 1.; - } - - render_priv->state.border_x = border_x; - render_priv->state.border_y = border_y; - - bord = 64 * border_x * render_priv->border_scale; - if (bord > 0 && border_x == border_y) { - if (!render_priv->state.stroker) { - int error; - error = - FT_Stroker_New(render_priv->ftlibrary, - &render_priv->state.stroker); - if (error) { - ass_msg(render_priv->library, MSGL_V, - "failed to get stroker"); - render_priv->state.stroker = 0; - } - } - if (render_priv->state.stroker) - FT_Stroker_Set(render_priv->state.stroker, bord, - FT_STROKER_LINECAP_ROUND, - FT_STROKER_LINEJOIN_ROUND, 0); - } else { - FT_Stroker_Done(render_priv->state.stroker); - render_priv->state.stroker = 0; - } -} - -/** - * \brief Calculate a weighted average of two colors - * calculates c1*(1-a) + c2*a, but separately for each component except alpha - */ -static void change_color(uint32_t *var, uint32_t new, double pwr) -{ - (*var) = ((uint32_t) (_r(*var) * (1 - pwr) + _r(new) * pwr) << 24) + - ((uint32_t) (_g(*var) * (1 - pwr) + _g(new) * pwr) << 16) + - ((uint32_t) (_b(*var) * (1 - pwr) + _b(new) * pwr) << 8) + _a(*var); -} - -// like change_color, but for alpha component only -inline void change_alpha(uint32_t *var, uint32_t new, double pwr) -{ - *var = - (_r(*var) << 24) + (_g(*var) << 16) + (_b(*var) << 8) + - (uint32_t) (_a(*var) * (1 - pwr) + _a(new) * pwr); -} - -/** - * \brief Multiply two alpha values - * \param a first value - * \param b second value - * \return result of multiplication - * Parameters and result are limited by 0xFF. - */ -inline uint32_t mult_alpha(uint32_t a, uint32_t b) -{ - return 0xFF - (0xFF - a) * (0xFF - b) / 0xFF; -} - -/** - * \brief Calculate alpha value by piecewise linear function - * Used for \fad, \fade implementation. - */ -static unsigned -interpolate_alpha(long long now, long long t1, long long t2, long long t3, - long long t4, unsigned a1, unsigned a2, unsigned a3) -{ - unsigned a; - double cf; - if (now <= t1) { - a = a1; - } else if (now >= t4) { - a = a3; - } else if (now < t2) { // and > t1 - cf = ((double) (now - t1)) / (t2 - t1); - a = a1 * (1 - cf) + a2 * cf; - } else if (now > t3) { - cf = ((double) (now - t3)) / (t4 - t3); - a = a2 * (1 - cf) + a3 * cf; - } else { // t2 <= now <= t3 - a = a2; - } - - return a; -} - -/** - * Parse a vector clip into an outline, using the proper scaling - * parameters. Translate it to correct for screen borders, if needed. - */ -static char *parse_vector_clip(ASS_Renderer *render_priv, char *p) -{ - int scale = 1; - int res = 0; - ASS_Drawing *drawing = render_priv->state.clip_drawing; - - if (drawing && drawing->glyph) - FT_Done_Glyph((FT_Glyph) drawing->glyph); - ass_drawing_free(drawing); - render_priv->state.clip_drawing = ass_drawing_new( - render_priv->fontconfig_priv, - render_priv->state.font, - render_priv->ftlibrary); - drawing = render_priv->state.clip_drawing; - skipopt('('); - res = mystrtoi(&p, &scale); - skipopt(',') - if (!res) - scale = 1; - drawing->scale = scale; - drawing->scale_x = render_priv->font_scale_x * render_priv->font_scale; - drawing->scale_y = render_priv->font_scale; - while (*p != ')' && *p != '}' && p != 0) - ass_drawing_add_char(drawing, *p++); - skipopt(')'); - - return p; -} - -/** - * \brief Parse style override tag. - * \param p string to parse - * \param pwr multiplier for some tag effects (comes from \t tags) - */ -static char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr) -{ - skip_to('\\'); - skip('\\'); - if ((*p == '}') || (*p == 0)) - return p; - - // New tags introduced in vsfilter 2.39 - if (mystrcmp(&p, "xbord")) { - double val; - if (mystrtod(&p, &val)) - val = render_priv->state.border_x * (1 - pwr) + val * pwr; - else - val = -1.; - change_border(render_priv, val, render_priv->state.border_y); - } else if (mystrcmp(&p, "ybord")) { - double val; - if (mystrtod(&p, &val)) - val = render_priv->state.border_y * (1 - pwr) + val * pwr; - else - val = -1.; - change_border(render_priv, render_priv->state.border_x, val); - } else if (mystrcmp(&p, "xshad")) { - double val; - if (mystrtod(&p, &val)) - val = render_priv->state.shadow_x * (1 - pwr) + val * pwr; - else - val = 0.; - render_priv->state.shadow_x = val; - } else if (mystrcmp(&p, "yshad")) { - double val; - if (mystrtod(&p, &val)) - val = render_priv->state.shadow_y * (1 - pwr) + val * pwr; - else - val = 0.; - render_priv->state.shadow_y = val; - } else if (mystrcmp(&p, "fax")) { - double val; - if (mystrtod(&p, &val)) - render_priv->state.fax = - val * pwr + render_priv->state.fax * (1 - pwr); - else - render_priv->state.fax = 0.; - } else if (mystrcmp(&p, "fay")) { - double val; - if (mystrtod(&p, &val)) - render_priv->state.fay = - val * pwr + render_priv->state.fay * (1 - pwr); - else - render_priv->state.fay = 0.; - } else if (mystrcmp(&p, "iclip")) { - int x0, y0, x1, y1; - int res = 1; - char *start = p; - skipopt('('); - res &= mystrtoi(&p, &x0); - skipopt(','); - res &= mystrtoi(&p, &y0); - skipopt(','); - res &= mystrtoi(&p, &x1); - skipopt(','); - res &= mystrtoi(&p, &y1); - skipopt(')'); - if (res) { - render_priv->state.clip_x0 = - render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr; - render_priv->state.clip_x1 = - render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr; - render_priv->state.clip_y0 = - render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr; - render_priv->state.clip_y1 = - render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr; - render_priv->state.clip_mode = 1; - } else if (!render_priv->state.clip_drawing) { - p = parse_vector_clip(render_priv, start); - render_priv->state.clip_drawing_mode = 1; - } else - render_priv->state.clip_mode = 0; - } else if (mystrcmp(&p, "blur")) { - double val; - if (mystrtod(&p, &val)) { - val = render_priv->state.blur * (1 - pwr) + val * pwr; - val = (val < 0) ? 0 : val; - val = (val > BLUR_MAX_RADIUS) ? BLUR_MAX_RADIUS : val; - render_priv->state.blur = val; - } else - render_priv->state.blur = 0.0; - // ASS standard tags - } else if (mystrcmp(&p, "fsc")) { - char tp = *p++; - double val; - if (tp == 'x') { - if (mystrtod(&p, &val)) { - val /= 100; - render_priv->state.scale_x = - render_priv->state.scale_x * (1 - pwr) + val * pwr; - } else - render_priv->state.scale_x = - render_priv->state.style->ScaleX; - } else if (tp == 'y') { - if (mystrtod(&p, &val)) { - val /= 100; - render_priv->state.scale_y = - render_priv->state.scale_y * (1 - pwr) + val * pwr; - } else - render_priv->state.scale_y = - render_priv->state.style->ScaleY; - } - } else if (mystrcmp(&p, "fsp")) { - double val; - if (mystrtod(&p, &val)) - render_priv->state.hspacing = - render_priv->state.hspacing * (1 - pwr) + val * pwr; - else - render_priv->state.hspacing = render_priv->state.style->Spacing; - } else if (mystrcmp(&p, "fs+")) { - double val; - if (mystrtod(&p, &val)) { - val = render_priv->state.font_size + pwr * val; - } else - val = render_priv->state.style->FontSize; - if (render_priv->state.font) - change_font_size(render_priv, val); - } else if (mystrcmp(&p, "fs-")) { - double val; - if (mystrtod(&p, &val)) - val = render_priv->state.font_size - pwr * val; - else - val = render_priv->state.style->FontSize; - if (render_priv->state.font) - change_font_size(render_priv, val); - } else if (mystrcmp(&p, "fs")) { - double val; - if (mystrtod(&p, &val)) - val = render_priv->state.font_size * (1 - pwr) + val * pwr; - else - val = render_priv->state.style->FontSize; - if (render_priv->state.font) - change_font_size(render_priv, val); - } else if (mystrcmp(&p, "bord")) { - double val; - if (mystrtod(&p, &val)) { - if (render_priv->state.border_x == render_priv->state.border_y) - val = render_priv->state.border_x * (1 - pwr) + val * pwr; - } else - val = -1.; // reset to default - change_border(render_priv, val, val); - } else if (mystrcmp(&p, "move")) { - double x1, x2, y1, y2; - long long t1, t2, delta_t, t; - double x, y; - double k; - skip('('); - mystrtod(&p, &x1); - skip(','); - mystrtod(&p, &y1); - skip(','); - mystrtod(&p, &x2); - skip(','); - mystrtod(&p, &y2); - if (*p == ',') { - skip(','); - mystrtoll(&p, &t1); - skip(','); - mystrtoll(&p, &t2); - ass_msg(render_priv->library, MSGL_DBG2, - "movement6: (%f, %f) -> (%f, %f), (%" PRId64 " .. %" - PRId64 ")\n", x1, y1, x2, y2, (int64_t) t1, - (int64_t) t2); - } else { - t1 = 0; - t2 = render_priv->state.event->Duration; - ass_msg(render_priv->library, MSGL_DBG2, - "movement: (%f, %f) -> (%f, %f)", x1, y1, x2, y2); - } - skip(')'); - delta_t = t2 - t1; - t = render_priv->time - render_priv->state.event->Start; - if (t < t1) - k = 0.; - else if (t > t2) - k = 1.; - else - k = ((double) (t - t1)) / delta_t; - x = k * (x2 - x1) + x1; - y = k * (y2 - y1) + y1; - if (render_priv->state.evt_type != EVENT_POSITIONED) { - render_priv->state.pos_x = x; - render_priv->state.pos_y = y; - render_priv->state.detect_collisions = 0; - render_priv->state.evt_type = EVENT_POSITIONED; - } - } else if (mystrcmp(&p, "frx")) { - double val; - if (mystrtod(&p, &val)) { - val *= M_PI / 180; - render_priv->state.frx = - val * pwr + render_priv->state.frx * (1 - pwr); - } else - render_priv->state.frx = 0.; - } else if (mystrcmp(&p, "fry")) { - double val; - if (mystrtod(&p, &val)) { - val *= M_PI / 180; - render_priv->state.fry = - val * pwr + render_priv->state.fry * (1 - pwr); - } else - render_priv->state.fry = 0.; - } else if (mystrcmp(&p, "frz") || mystrcmp(&p, "fr")) { - double val; - if (mystrtod(&p, &val)) { - val *= M_PI / 180; - render_priv->state.frz = - val * pwr + render_priv->state.frz * (1 - pwr); - } else - render_priv->state.frz = - M_PI * render_priv->state.style->Angle / 180.; - } else if (mystrcmp(&p, "fn")) { - char *start = p; - char *family; - skip_to('\\'); - if (p > start) { - family = malloc(p - start + 1); - strncpy(family, start, p - start); - family[p - start] = '\0'; - } else - family = strdup(render_priv->state.style->FontName); - free(render_priv->state.family); - render_priv->state.family = family; - update_font(render_priv); - } else if (mystrcmp(&p, "alpha")) { - uint32_t val; - int i; - int hex = render_priv->track->track_type == TRACK_TYPE_ASS; - if (strtocolor(render_priv->library, &p, &val, hex)) { - unsigned char a = val >> 24; - for (i = 0; i < 4; ++i) - change_alpha(&render_priv->state.c[i], a, pwr); - } else { - change_alpha(&render_priv->state.c[0], - render_priv->state.style->PrimaryColour, pwr); - change_alpha(&render_priv->state.c[1], - render_priv->state.style->SecondaryColour, pwr); - change_alpha(&render_priv->state.c[2], - render_priv->state.style->OutlineColour, pwr); - change_alpha(&render_priv->state.c[3], - render_priv->state.style->BackColour, pwr); - } - // FIXME: simplify - } else if (mystrcmp(&p, "an")) { - int val; - if (mystrtoi(&p, &val) && val) { - int v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment - ass_msg(render_priv->library, MSGL_DBG2, "an %d", val); - if (v != 0) - v = 3 - v; - val = ((val - 1) % 3) + 1; // horizontal alignment - val += v * 4; - ass_msg(render_priv->library, MSGL_DBG2, "align %d", val); - render_priv->state.alignment = val; - } else - render_priv->state.alignment = - render_priv->state.style->Alignment; - } else if (mystrcmp(&p, "a")) { - int val; - if (mystrtoi(&p, &val) && val) - // take care of a vsfilter quirk: handle illegal \a8 like \a5 - render_priv->state.alignment = (val == 8) ? 5 : val; - else - render_priv->state.alignment = - render_priv->state.style->Alignment; - } else if (mystrcmp(&p, "pos")) { - double v1, v2; - skip('('); - mystrtod(&p, &v1); - skip(','); - mystrtod(&p, &v2); - skip(')'); - ass_msg(render_priv->library, MSGL_DBG2, "pos(%f, %f)", v1, v2); - if (render_priv->state.evt_type == EVENT_POSITIONED) { - ass_msg(render_priv->library, MSGL_V, "Subtitle has a new \\pos " - "after \\move or \\pos, ignoring"); - } else { - render_priv->state.evt_type = EVENT_POSITIONED; - render_priv->state.detect_collisions = 0; - render_priv->state.pos_x = v1; - render_priv->state.pos_y = v2; - } - } else if (mystrcmp(&p, "fad")) { - int a1, a2, a3; - long long t1, t2, t3, t4; - if (*p == 'e') - ++p; // either \fad or \fade - skip('('); - mystrtoi(&p, &a1); - skip(','); - mystrtoi(&p, &a2); - if (*p == ')') { - // 2-argument version (\fad, according to specs) - // a1 and a2 are fade-in and fade-out durations - t1 = 0; - t4 = render_priv->state.event->Duration; - t2 = a1; - t3 = t4 - a2; - a1 = 0xFF; - a2 = 0; - a3 = 0xFF; - } else { - // 6-argument version (\fade) - // a1 and a2 (and a3) are opacity values - skip(','); - mystrtoi(&p, &a3); - skip(','); - mystrtoll(&p, &t1); - skip(','); - mystrtoll(&p, &t2); - skip(','); - mystrtoll(&p, &t3); - skip(','); - mystrtoll(&p, &t4); - } - skip(')'); - render_priv->state.fade = - interpolate_alpha(render_priv->time - - render_priv->state.event->Start, t1, t2, - t3, t4, a1, a2, a3); - } else if (mystrcmp(&p, "org")) { - int v1, v2; - skip('('); - mystrtoi(&p, &v1); - skip(','); - mystrtoi(&p, &v2); - skip(')'); - ass_msg(render_priv->library, MSGL_DBG2, "org(%d, %d)", v1, v2); - if (!render_priv->state.have_origin) { - render_priv->state.org_x = v1; - render_priv->state.org_y = v2; - render_priv->state.have_origin = 1; - render_priv->state.detect_collisions = 0; - } - } else if (mystrcmp(&p, "t")) { - double v[3]; - int v1, v2; - double v3; - int cnt; - long long t1, t2, t, delta_t; - double k; - skip('('); - for (cnt = 0; cnt < 3; ++cnt) { - if (*p == '\\') - break; - mystrtod(&p, &v[cnt]); - skip(','); - } - if (cnt == 3) { - v1 = v[0]; - v2 = (v[1] < v1) ? render_priv->state.event->Duration : v[1]; - v3 = v[2]; - } else if (cnt == 2) { - v1 = v[0]; - v2 = (v[1] < v1) ? render_priv->state.event->Duration : v[1]; - v3 = 1.; - } else if (cnt == 1) { - v1 = 0; - v2 = render_priv->state.event->Duration; - v3 = v[0]; - } else { // cnt == 0 - v1 = 0; - v2 = render_priv->state.event->Duration; - v3 = 1.; - } - render_priv->state.detect_collisions = 0; - t1 = v1; - t2 = v2; - delta_t = v2 - v1; - if (v3 < 0.) - v3 = 0.; - t = render_priv->time - render_priv->state.event->Start; // FIXME: move to render_context - if (t <= t1) - k = 0.; - else if (t >= t2) - k = 1.; - else { - assert(delta_t != 0.); - k = pow(((double) (t - t1)) / delta_t, v3); - } - while (*p == '\\') - p = parse_tag(render_priv, p, k); // maybe k*pwr ? no, specs forbid nested \t's - skip_to(')'); // in case there is some unknown tag or a comment - skip(')'); - } else if (mystrcmp(&p, "clip")) { - char *start = p; - int x0, y0, x1, y1; - int res = 1; - skipopt('('); - res &= mystrtoi(&p, &x0); - skipopt(','); - res &= mystrtoi(&p, &y0); - skipopt(','); - res &= mystrtoi(&p, &x1); - skipopt(','); - res &= mystrtoi(&p, &y1); - skipopt(')'); - if (res) { - render_priv->state.clip_x0 = - render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr; - render_priv->state.clip_x1 = - render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr; - render_priv->state.clip_y0 = - render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr; - render_priv->state.clip_y1 = - render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr; - // Might be a vector clip - } else if (!render_priv->state.clip_drawing) { - p = parse_vector_clip(render_priv, start); - render_priv->state.clip_drawing_mode = 0; - } else { - render_priv->state.clip_x0 = 0; - render_priv->state.clip_y0 = 0; - render_priv->state.clip_x1 = render_priv->track->PlayResX; - render_priv->state.clip_y1 = render_priv->track->PlayResY; - } - } else if (mystrcmp(&p, "c")) { - uint32_t val; - int hex = render_priv->track->track_type == TRACK_TYPE_ASS; - if (!strtocolor(render_priv->library, &p, &val, hex)) - val = render_priv->state.style->PrimaryColour; - ass_msg(render_priv->library, MSGL_DBG2, "color: %X", val); - change_color(&render_priv->state.c[0], val, pwr); - } else if ((*p >= '1') && (*p <= '4') && (++p) - && (mystrcmp(&p, "c") || mystrcmp(&p, "a"))) { - char n = *(p - 2); - int cidx = n - '1'; - char cmd = *(p - 1); - uint32_t val; - int hex = render_priv->track->track_type == TRACK_TYPE_ASS; - assert((n >= '1') && (n <= '4')); - if (!strtocolor(render_priv->library, &p, &val, hex)) - switch (n) { - case '1': - val = render_priv->state.style->PrimaryColour; - break; - case '2': - val = render_priv->state.style->SecondaryColour; - break; - case '3': - val = render_priv->state.style->OutlineColour; - break; - case '4': - val = render_priv->state.style->BackColour; - break; - default: - val = 0; - break; // impossible due to assert; avoid compilation warning - } - switch (cmd) { - case 'c': - change_color(render_priv->state.c + cidx, val, pwr); - break; - case 'a': - change_alpha(render_priv->state.c + cidx, val >> 24, pwr); - break; - default: - ass_msg(render_priv->library, MSGL_WARN, "Bad command: %c%c", - n, cmd); - break; - } - ass_msg(render_priv->library, MSGL_DBG2, "single c/a at %f: %c%c = %X", - pwr, n, cmd, render_priv->state.c[cidx]); - } else if (mystrcmp(&p, "r")) { - reset_render_context(render_priv); - } else if (mystrcmp(&p, "be")) { - int val; - if (mystrtoi(&p, &val)) { - // Clamp to a safe upper limit, since high values need excessive CPU - val = (val < 0) ? 0 : val; - val = (val > MAX_BE) ? MAX_BE : val; - render_priv->state.be = val; - } else - render_priv->state.be = 0; - } else if (mystrcmp(&p, "b")) { - int b; - if (mystrtoi(&p, &b)) { - if (pwr >= .5) - render_priv->state.bold = b; - } else - render_priv->state.bold = render_priv->state.style->Bold; - update_font(render_priv); - } else if (mystrcmp(&p, "i")) { - int i; - if (mystrtoi(&p, &i)) { - if (pwr >= .5) - render_priv->state.italic = i; - } else - render_priv->state.italic = render_priv->state.style->Italic; - update_font(render_priv); - } else if (mystrcmp(&p, "kf") || mystrcmp(&p, "K")) { - int val = 0; - mystrtoi(&p, &val); - render_priv->state.effect_type = EF_KARAOKE_KF; - if (render_priv->state.effect_timing) - render_priv->state.effect_skip_timing += - render_priv->state.effect_timing; - render_priv->state.effect_timing = val * 10; - } else if (mystrcmp(&p, "ko")) { - int val = 0; - mystrtoi(&p, &val); - render_priv->state.effect_type = EF_KARAOKE_KO; - if (render_priv->state.effect_timing) - render_priv->state.effect_skip_timing += - render_priv->state.effect_timing; - render_priv->state.effect_timing = val * 10; - } else if (mystrcmp(&p, "k")) { - int val = 0; - mystrtoi(&p, &val); - render_priv->state.effect_type = EF_KARAOKE; - if (render_priv->state.effect_timing) - render_priv->state.effect_skip_timing += - render_priv->state.effect_timing; - render_priv->state.effect_timing = val * 10; - } else if (mystrcmp(&p, "shad")) { - double val; - if (mystrtod(&p, &val)) { - if (render_priv->state.shadow_x == render_priv->state.shadow_y) - val = render_priv->state.shadow_x * (1 - pwr) + val * pwr; - } else - val = 0.; - render_priv->state.shadow_x = render_priv->state.shadow_y = val; - } else if (mystrcmp(&p, "s")) { - int val; - if (mystrtoi(&p, &val) && val) - render_priv->state.flags |= DECO_STRIKETHROUGH; - else - render_priv->state.flags &= ~DECO_STRIKETHROUGH; - } else if (mystrcmp(&p, "u")) { - int val; - if (mystrtoi(&p, &val) && val) - render_priv->state.flags |= DECO_UNDERLINE; - else - render_priv->state.flags &= ~DECO_UNDERLINE; - } else if (mystrcmp(&p, "pbo")) { - double val = 0; - if (mystrtod(&p, &val)) - render_priv->state.drawing->pbo = val; - } else if (mystrcmp(&p, "p")) { - int val; - if (!mystrtoi(&p, &val)) - val = 0; - if (val) - render_priv->state.drawing->scale = val; - render_priv->state.drawing_mode = !!val; - } else if (mystrcmp(&p, "q")) { - int val; - if (!mystrtoi(&p, &val)) - val = render_priv->track->WrapStyle; - render_priv->state.wrap_style = val; - } - - return p; -} - -void apply_transition_effects(ASS_Renderer *render_priv, ASS_Event *event) -{ - int v[4]; - int cnt; - char *p = event->Effect; - - if (!p || !*p) - return; - - cnt = 0; - while (cnt < 4 && (p = strchr(p, ';'))) { - v[cnt++] = atoi(++p); - } - - if (strncmp(event->Effect, "Banner;", 7) == 0) { - int delay; - if (cnt < 1) { - ass_msg(render_priv->library, MSGL_V, - "Error parsing effect: '%s'", event->Effect); - return; - } - if (cnt >= 2 && v[1] == 0) // right-to-left - render_priv->state.scroll_direction = SCROLL_RL; - else // left-to-right - render_priv->state.scroll_direction = SCROLL_LR; - - delay = v[0]; - if (delay == 0) - delay = 1; // ? - render_priv->state.scroll_shift = - (render_priv->time - render_priv->state.event->Start) / delay; - render_priv->state.evt_type = EVENT_HSCROLL; - return; - } - - if (strncmp(event->Effect, "Scroll up;", 10) == 0) { - render_priv->state.scroll_direction = SCROLL_BT; - } else if (strncmp(event->Effect, "Scroll down;", 12) == 0) { - render_priv->state.scroll_direction = SCROLL_TB; - } else { - ass_msg(render_priv->library, MSGL_DBG2, - "Unknown transition effect: '%s'", event->Effect); - return; - } - // parse scroll up/down parameters - { - int delay; - int y0, y1; - if (cnt < 3) { - ass_msg(render_priv->library, MSGL_V, - "Error parsing effect: '%s'", event->Effect); - return; - } - delay = v[2]; - if (delay == 0) - delay = 1; // ? - render_priv->state.scroll_shift = - (render_priv->time - render_priv->state.event->Start) / delay; - if (v[0] < v[1]) { - y0 = v[0]; - y1 = v[1]; - } else { - y0 = v[1]; - y1 = v[0]; - } - if (y1 == 0) - y1 = render_priv->track->PlayResY; // y0=y1=0 means fullscreen scrolling - render_priv->state.clip_y0 = y0; - render_priv->state.clip_y1 = y1; - render_priv->state.evt_type = EVENT_VSCROLL; - render_priv->state.detect_collisions = 0; - } - -} - -/** - * \brief Get next ucs4 char from string, parsing and executing style overrides - * \param str string pointer - * \return ucs4 code of the next char - * On return str points to the unparsed part of the string - */ -unsigned get_next_char(ASS_Renderer *render_priv, char **str) -{ - char *p = *str; - unsigned chr; - if (*p == '{') { // '\0' goes here - p++; - while (1) { - p = parse_tag(render_priv, p, 1.); - if (*p == '}') { // end of tag - p++; - if (*p == '{') { - p++; - continue; - } else - break; - } else if (*p != '\\') - ass_msg(render_priv->library, MSGL_V, - "Unable to parse: '%.30s'", p); - if (*p == 0) - break; - } - } - if (*p == '\t') { - ++p; - *str = p; - return ' '; - } - if (*p == '\\') { - if ((p[1] == 'N') || ((p[1] == 'n') && - (render_priv->state.wrap_style == 2))) { - p += 2; - *str = p; - return '\n'; - } else if (p[1] == 'n') { - p += 2; - *str = p; - return ' '; - } else if (p[1] == 'h') { - p += 2; - *str = p; - return NBSP; - } else if (p[1] == '{') { - p += 2; - *str = p; - return '{'; - } else if (p[1] == '}') { - p += 2; - *str = p; - return '}'; - } - } - chr = ass_utf8_get_char((char **) &p); - *str = p; - return chr; -} diff --git a/aegisub/libass/ass_parse.h b/aegisub/libass/ass_parse.h deleted file mode 100644 index c65b56515..000000000 --- a/aegisub/libass/ass_parse.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2009 Grigori Goronzy - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef LIBASS_PARSE_H -#define LIBASS_PARSE_H - -#define BLUR_MAX_RADIUS 100.0 - -#define _r(c) ((c) >> 24) -#define _g(c) (((c) >> 16) & 0xFF) -#define _b(c) (((c) >> 8) & 0xFF) -#define _a(c) ((c) & 0xFF) - -void update_font(ASS_Renderer *render_priv); -void change_border(ASS_Renderer *render_priv, double border_x, - double border_y); -void apply_transition_effects(ASS_Renderer *render_priv, ASS_Event *event); -unsigned get_next_char(ASS_Renderer *render_priv, char **str); -extern void change_alpha(uint32_t *var, uint32_t new, double pwr); -extern uint32_t mult_alpha(uint32_t a, uint32_t b); - - -#endif /* LIBASS_PARSE_H */ diff --git a/aegisub/libass/ass_render.c b/aegisub/libass/ass_render.c deleted file mode 100644 index 21170667a..000000000 --- a/aegisub/libass/ass_render.c +++ /dev/null @@ -1,2538 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "config.h" - -#include -#include - -#include "ass_render.h" -#include "ass_parse.h" - -#define MAX_GLYPHS_INITIAL 1024 -#define MAX_LINES_INITIAL 64 -#define SUBPIXEL_MASK 63 -#define SUBPIXEL_ACCURACY 7 - -static void ass_lazy_track_init(ASS_Renderer *render_priv) -{ - ASS_Track *track = render_priv->track; - - if (track->PlayResX && track->PlayResY) - return; - if (!track->PlayResX && !track->PlayResY) { - ass_msg(render_priv->library, MSGL_WARN, - "Neither PlayResX nor PlayResY defined. Assuming 384x288"); - track->PlayResX = 384; - track->PlayResY = 288; - } else { - if (!track->PlayResY && track->PlayResX == 1280) { - track->PlayResY = 1024; - ass_msg(render_priv->library, MSGL_WARN, - "PlayResY undefined, setting to %d", track->PlayResY); - } else if (!track->PlayResY) { - track->PlayResY = track->PlayResX * 3 / 4; - ass_msg(render_priv->library, MSGL_WARN, - "PlayResY undefined, setting to %d", track->PlayResY); - } else if (!track->PlayResX && track->PlayResY == 1024) { - track->PlayResX = 1280; - ass_msg(render_priv->library, MSGL_WARN, - "PlayResX undefined, setting to %d", track->PlayResX); - } else if (!track->PlayResX) { - track->PlayResX = track->PlayResY * 4 / 3; - ass_msg(render_priv->library, MSGL_WARN, - "PlayResX undefined, setting to %d", track->PlayResX); - } - } -} - -ASS_Renderer *ass_renderer_init(ASS_Library *library) -{ - int error; - FT_Library ft; - ASS_Renderer *priv = 0; - int vmajor, vminor, vpatch; - - error = FT_Init_FreeType(&ft); - if (error) { - ass_msg(library, MSGL_FATAL, "%s failed", "FT_Init_FreeType"); - goto ass_init_exit; - } - - FT_Library_Version(ft, &vmajor, &vminor, &vpatch); - ass_msg(library, MSGL_V, "FreeType library version: %d.%d.%d", - vmajor, vminor, vpatch); - ass_msg(library, MSGL_V, "FreeType headers version: %d.%d.%d", - FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH); - - priv = calloc(1, sizeof(ASS_Renderer)); - if (!priv) { - FT_Done_FreeType(ft); - goto ass_init_exit; - } - - priv->synth_priv = ass_synth_init(BLUR_MAX_RADIUS); - - priv->library = library; - priv->ftlibrary = ft; - // images_root and related stuff is zero-filled in calloc - - priv->cache.font_cache = ass_font_cache_init(library); - priv->cache.bitmap_cache = ass_bitmap_cache_init(library); - priv->cache.composite_cache = ass_composite_cache_init(library); - priv->cache.glyph_cache = ass_glyph_cache_init(library); - priv->cache.glyph_max = GLYPH_CACHE_MAX; - priv->cache.bitmap_max_size = BITMAP_CACHE_MAX_SIZE; - - priv->text_info.max_glyphs = MAX_GLYPHS_INITIAL; - priv->text_info.max_lines = MAX_LINES_INITIAL; - priv->text_info.glyphs = calloc(MAX_GLYPHS_INITIAL, sizeof(GlyphInfo)); - priv->text_info.lines = calloc(MAX_LINES_INITIAL, sizeof(LineInfo)); - - priv->settings.font_size_coeff = 1.; - - ass_init_exit: - if (priv) - ass_msg(library, MSGL_V, "Init"); - else - ass_msg(library, MSGL_ERR, "Init failed"); - - return priv; -} - -static void free_list_clear(ASS_Renderer *render_priv) -{ - if (render_priv->free_head) { - FreeList *item = render_priv->free_head; - while(item) { - FreeList *oi = item; - free(item->object); - item = item->next; - free(oi); - } - render_priv->free_head = NULL; - } -} - -void ass_renderer_done(ASS_Renderer *render_priv) -{ - ass_font_cache_done(render_priv->cache.font_cache); - ass_bitmap_cache_done(render_priv->cache.bitmap_cache); - ass_composite_cache_done(render_priv->cache.composite_cache); - ass_glyph_cache_done(render_priv->cache.glyph_cache); - - ass_free_images(render_priv->images_root); - ass_free_images(render_priv->prev_images_root); - - if (render_priv->state.stroker) { - FT_Stroker_Done(render_priv->state.stroker); - render_priv->state.stroker = 0; - } - if (render_priv->ftlibrary) - FT_Done_FreeType(render_priv->ftlibrary); - if (render_priv->fontconfig_priv) - fontconfig_done(render_priv->fontconfig_priv); - if (render_priv->synth_priv) - ass_synth_done(render_priv->synth_priv); - free(render_priv->eimg); - free(render_priv->text_info.glyphs); - free(render_priv->text_info.lines); - - free(render_priv->settings.default_font); - free(render_priv->settings.default_family); - - free_list_clear(render_priv); - free(render_priv); -} - -/** - * \brief Create a new ASS_Image - * Parameters are the same as ASS_Image fields. - */ -static ASS_Image *my_draw_bitmap(unsigned char *bitmap, int bitmap_w, - int bitmap_h, int stride, int dst_x, - int dst_y, uint32_t color) -{ - ASS_Image *img = malloc(sizeof(ASS_Image)); - - if (img) { - img->w = bitmap_w; - img->h = bitmap_h; - img->stride = stride; - img->bitmap = bitmap; - img->color = color; - img->dst_x = dst_x; - img->dst_y = dst_y; - } - - return img; -} - -/** - * \brief Mapping between script and screen coordinates - */ -static double x2scr(ASS_Renderer *render_priv, double x) -{ - return x * render_priv->orig_width_nocrop / render_priv->font_scale_x / - render_priv->track->PlayResX + - FFMAX(render_priv->settings.left_margin, 0); -} -static double x2scr_pos(ASS_Renderer *render_priv, double x) -{ - return x * render_priv->orig_width / render_priv->font_scale_x / render_priv->track->PlayResX + - render_priv->settings.left_margin; -} -static double x2scr_scaled(ASS_Renderer *render_priv, double x) -{ - return x * render_priv->orig_width_nocrop / - render_priv->track->PlayResX + - FFMAX(render_priv->settings.left_margin, 0); -} -static double x2scr_pos_scaled(ASS_Renderer *render_priv, double x) -{ - return x * render_priv->orig_width / render_priv->track->PlayResX + - render_priv->settings.left_margin; -} -/** - * \brief Mapping between script and screen coordinates - */ -static double y2scr(ASS_Renderer *render_priv, double y) -{ - return y * render_priv->orig_height_nocrop / - render_priv->track->PlayResY + - FFMAX(render_priv->settings.top_margin, 0); -} -static double y2scr_pos(ASS_Renderer *render_priv, double y) -{ - return y * render_priv->orig_height / render_priv->track->PlayResY + - render_priv->settings.top_margin; -} - -// the same for toptitles -static double y2scr_top(ASS_Renderer *render_priv, double y) -{ - if (render_priv->settings.use_margins) - return y * render_priv->orig_height_nocrop / - render_priv->track->PlayResY; - else - return y * render_priv->orig_height_nocrop / - render_priv->track->PlayResY + - FFMAX(render_priv->settings.top_margin, 0); -} -// the same for subtitles -static double y2scr_sub(ASS_Renderer *render_priv, double y) -{ - if (render_priv->settings.use_margins) - return y * render_priv->orig_height_nocrop / - render_priv->track->PlayResY + - FFMAX(render_priv->settings.top_margin, 0) - + FFMAX(render_priv->settings.bottom_margin, 0); - else - return y * render_priv->orig_height_nocrop / - render_priv->track->PlayResY + - FFMAX(render_priv->settings.top_margin, 0); -} - -/* - * \brief Convert bitmap glyphs into ASS_Image list with inverse clipping - * - * Inverse clipping with the following strategy: - * - find rectangle from (x0, y0) to (cx0, y1) - * - find rectangle from (cx0, y0) to (cx1, cy0) - * - find rectangle from (cx0, cy1) to (cx1, y1) - * - find rectangle from (cx1, y0) to (x1, y1) - * These rectangles can be invalid and in this case are discarded. - * Afterwards, they are clipped against the screen coordinates. - * In an additional pass, the rectangles need to be split up left/right for - * karaoke effects. This can result in a lot of bitmaps (6 to be exact). - */ -static ASS_Image **render_glyph_i(ASS_Renderer *render_priv, - Bitmap *bm, int dst_x, int dst_y, - uint32_t color, uint32_t color2, int brk, - ASS_Image **tail) -{ - int i, j, x0, y0, x1, y1, cx0, cy0, cx1, cy1, sx, sy, zx, zy; - Rect r[4]; - ASS_Image *img; - - dst_x += bm->left; - dst_y += bm->top; - - // we still need to clip against screen boundaries - zx = x2scr_pos_scaled(render_priv, 0); - zy = y2scr_pos(render_priv, 0); - sx = x2scr_pos_scaled(render_priv, render_priv->track->PlayResX); - sy = y2scr_pos(render_priv, render_priv->track->PlayResY); - - x0 = 0; - y0 = 0; - x1 = bm->w; - y1 = bm->h; - cx0 = render_priv->state.clip_x0 - dst_x; - cy0 = render_priv->state.clip_y0 - dst_y; - cx1 = render_priv->state.clip_x1 - dst_x; - cy1 = render_priv->state.clip_y1 - dst_y; - - // calculate rectangles and discard invalid ones while we're at it. - i = 0; - r[i].x0 = x0; - r[i].y0 = y0; - r[i].x1 = (cx0 > x1) ? x1 : cx0; - r[i].y1 = y1; - if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++; - r[i].x0 = (cx0 < 0) ? x0 : cx0; - r[i].y0 = y0; - r[i].x1 = (cx1 > x1) ? x1 : cx1; - r[i].y1 = (cy0 > y1) ? y1 : cy0; - if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++; - r[i].x0 = (cx0 < 0) ? x0 : cx0; - r[i].y0 = (cy1 < 0) ? y0 : cy1; - r[i].x1 = (cx1 > x1) ? x1 : cx1; - r[i].y1 = y1; - if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++; - r[i].x0 = (cx1 < 0) ? x0 : cx1; - r[i].y0 = y0; - r[i].x1 = x1; - r[i].y1 = y1; - if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++; - - // clip each rectangle to screen coordinates - for (j = 0; j < i; j++) { - r[j].x0 = (r[j].x0 + dst_x < zx) ? zx - dst_x : r[j].x0; - r[j].y0 = (r[j].y0 + dst_y < zy) ? zy - dst_y : r[j].y0; - r[j].x1 = (r[j].x1 + dst_x > sx) ? sx - dst_x : r[j].x1; - r[j].y1 = (r[j].y1 + dst_y > sy) ? sy - dst_y : r[j].y1; - } - - // draw the rectangles - for (j = 0; j < i; j++) { - int lbrk = brk; - // kick out rectangles that are invalid now - if (r[j].x1 <= r[j].x0 || r[j].y1 <= r[j].y0) - continue; - // split up into left and right for karaoke, if needed - if (lbrk > r[j].x0) { - if (lbrk > r[j].x1) lbrk = r[j].x1; - img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->w + r[j].x0, - lbrk - r[j].x0, r[j].y1 - r[j].y0, - bm->w, dst_x + r[j].x0, dst_y + r[j].y0, color); - if (!img) break; - *tail = img; - tail = &img->next; - } - if (lbrk < r[j].x1) { - if (lbrk < r[j].x0) lbrk = r[j].x0; - img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->w + lbrk, - r[j].x1 - lbrk, r[j].y1 - r[j].y0, - bm->w, dst_x + lbrk, dst_y + r[j].y0, color2); - if (!img) break; - *tail = img; - tail = &img->next; - } - } - - return tail; -} - -/** - * \brief convert bitmap glyph into ASS_Image struct(s) - * \param bit freetype bitmap glyph, FT_PIXEL_MODE_GRAY - * \param dst_x bitmap x coordinate in video frame - * \param dst_y bitmap y coordinate in video frame - * \param color first color, RGBA - * \param color2 second color, RGBA - * \param brk x coordinate relative to glyph origin, color is used to the left of brk, color2 - to the right - * \param tail pointer to the last image's next field, head of the generated list should be stored here - * \return pointer to the new list tail - * Performs clipping. Uses my_draw_bitmap for actual bitmap convertion. - */ -static ASS_Image ** -render_glyph(ASS_Renderer *render_priv, Bitmap *bm, int dst_x, int dst_y, - uint32_t color, uint32_t color2, int brk, ASS_Image **tail) -{ - // brk is relative to dst_x - // color = color left of brk - // color2 = color right of brk - int b_x0, b_y0, b_x1, b_y1; // visible part of the bitmap - int clip_x0, clip_y0, clip_x1, clip_y1; - int tmp; - ASS_Image *img; - - // Inverse clipping in use? - if (render_priv->state.clip_mode) - return render_glyph_i(render_priv, bm, dst_x, dst_y, color, color2, - brk, tail); - - dst_x += bm->left; - dst_y += bm->top; - brk -= bm->left; - - // clipping - clip_x0 = FFMINMAX(render_priv->state.clip_x0, 0, render_priv->width); - clip_y0 = FFMINMAX(render_priv->state.clip_y0, 0, render_priv->height); - clip_x1 = FFMINMAX(render_priv->state.clip_x1, 0, render_priv->width); - clip_y1 = FFMINMAX(render_priv->state.clip_y1, 0, render_priv->height); - b_x0 = 0; - b_y0 = 0; - b_x1 = bm->w; - b_y1 = bm->h; - - tmp = dst_x - clip_x0; - if (tmp < 0) { - ass_msg(render_priv->library, MSGL_DBG2, "clip left"); - b_x0 = -tmp; - } - tmp = dst_y - clip_y0; - if (tmp < 0) { - ass_msg(render_priv->library, MSGL_DBG2, "clip top"); - b_y0 = -tmp; - } - tmp = clip_x1 - dst_x - bm->w; - if (tmp < 0) { - ass_msg(render_priv->library, MSGL_DBG2, "clip right"); - b_x1 = bm->w + tmp; - } - tmp = clip_y1 - dst_y - bm->h; - if (tmp < 0) { - ass_msg(render_priv->library, MSGL_DBG2, "clip bottom"); - b_y1 = bm->h + tmp; - } - - if ((b_y0 >= b_y1) || (b_x0 >= b_x1)) - return tail; - - if (brk > b_x0) { // draw left part - if (brk > b_x1) - brk = b_x1; - img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + b_x0, - brk - b_x0, b_y1 - b_y0, bm->w, - dst_x + b_x0, dst_y + b_y0, color); - if (!img) return tail; - *tail = img; - tail = &img->next; - } - if (brk < b_x1) { // draw right part - if (brk < b_x0) - brk = b_x0; - img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + brk, - b_x1 - brk, b_y1 - b_y0, bm->w, - dst_x + brk, dst_y + b_y0, color2); - if (!img) return tail; - *tail = img; - tail = &img->next; - } - return tail; -} - -/** - * \brief Replace the bitmap buffer in ASS_Image with a copy - * \param img ASS_Image to operate on - * \return pointer to old bitmap buffer - */ -static unsigned char *clone_bitmap_buffer(ASS_Image *img) -{ - unsigned char *old_bitmap = img->bitmap; - int size = img->stride * (img->h - 1) + img->w; - img->bitmap = malloc(size); - memcpy(img->bitmap, old_bitmap, size); - return old_bitmap; -} - -/** - * \brief Calculate overlapping area of two consecutive bitmaps and in case they - * overlap, blend them together - * Mainly useful for translucent glyphs and especially borders, to avoid the - * luminance adding up where they overlap (which looks ugly) - */ -static void -render_overlap(ASS_Renderer *render_priv, ASS_Image **last_tail, - ASS_Image **tail) -{ - int left, top, bottom, right; - int old_left, old_top, w, h, cur_left, cur_top; - int x, y, opos, cpos; - char m; - CompositeHashKey hk; - CompositeHashValue *hv; - CompositeHashValue chv; - int ax = (*last_tail)->dst_x; - int ay = (*last_tail)->dst_y; - int aw = (*last_tail)->w; - int as = (*last_tail)->stride; - int ah = (*last_tail)->h; - int bx = (*tail)->dst_x; - int by = (*tail)->dst_y; - int bw = (*tail)->w; - int bs = (*tail)->stride; - int bh = (*tail)->h; - unsigned char *a; - unsigned char *b; - - if ((*last_tail)->bitmap == (*tail)->bitmap) - return; - - if ((*last_tail)->color != (*tail)->color) - return; - - // Calculate overlap coordinates - left = (ax > bx) ? ax : bx; - top = (ay > by) ? ay : by; - right = ((ax + aw) < (bx + bw)) ? (ax + aw) : (bx + bw); - bottom = ((ay + ah) < (by + bh)) ? (ay + ah) : (by + bh); - if ((right <= left) || (bottom <= top)) - return; - old_left = left - ax; - old_top = top - ay; - w = right - left; - h = bottom - top; - cur_left = left - bx; - cur_top = top - by; - - // Query cache - hk.a = (*last_tail)->bitmap; - hk.b = (*tail)->bitmap; - hk.aw = aw; - hk.ah = ah; - hk.bw = bw; - hk.bh = bh; - hk.ax = ax; - hk.ay = ay; - hk.bx = bx; - hk.by = by; - hk.as = as; - hk.bs = bs; - hv = cache_find_composite(render_priv->cache.composite_cache, &hk); - if (hv) { - (*last_tail)->bitmap = hv->a; - (*tail)->bitmap = hv->b; - return; - } - // Allocate new bitmaps and copy over data - a = clone_bitmap_buffer(*last_tail); - b = clone_bitmap_buffer(*tail); - - // Blend overlapping area - for (y = 0; y < h; y++) - for (x = 0; x < w; x++) { - opos = (old_top + y) * (as) + (old_left + x); - cpos = (cur_top + y) * (bs) + (cur_left + x); - m = FFMIN(a[opos] + b[cpos], 0xff); - (*last_tail)->bitmap[opos] = 0; - (*tail)->bitmap[cpos] = m; - } - - // Insert bitmaps into the cache - chv.a = (*last_tail)->bitmap; - chv.b = (*tail)->bitmap; - cache_add_composite(render_priv->cache.composite_cache, &hk, &chv); -} - -static void free_list_add(ASS_Renderer *render_priv, void *object) -{ - if (!render_priv->free_head) { - render_priv->free_head = calloc(1, sizeof(FreeList)); - render_priv->free_head->object = object; - render_priv->free_tail = render_priv->free_head; - } else { - FreeList *l = calloc(1, sizeof(FreeList)); - l->object = object; - render_priv->free_tail->next = l; - render_priv->free_tail = render_priv->free_tail->next; - } -} - -/** - * Iterate through a list of bitmaps and blend with clip vector, if - * applicable. The blended bitmaps are added to a free list which is freed - * at the start of a new frame. - */ -static void blend_vector_clip(ASS_Renderer *render_priv, - ASS_Image *head) -{ - FT_Glyph glyph; - FT_BitmapGlyph clip_bm; - ASS_Image *cur; - ASS_Drawing *drawing = render_priv->state.clip_drawing; - GlyphHashKey key; - GlyphHashValue *val; - int error; - - if (!drawing) - return; - - // Try to get mask from cache - ass_drawing_hash(drawing); - memset(&key, 0, sizeof(key)); - key.ch = -2; - key.drawing_hash = drawing->hash; - val = cache_find_glyph(render_priv->cache.glyph_cache, &key); - - if (val) { - clip_bm = (FT_BitmapGlyph) val->glyph; - } else { - GlyphHashValue v; - - // Not found in cache, parse and rasterize it - glyph = (FT_Glyph) *ass_drawing_parse(drawing, 1); - if (!glyph) { - ass_msg(render_priv->library, MSGL_WARN, - "Clip vector parsing failed. Skipping."); - goto blend_vector_error; - } - - // We need to translate the clip according to screen borders - if (render_priv->settings.left_margin != 0 || - render_priv->settings.top_margin != 0) { - FT_Vector trans = { - int_to_d6(render_priv->settings.left_margin), - -int_to_d6(render_priv->settings.top_margin), - }; - FT_Outline_Translate(&drawing->glyph->outline, - trans.x, trans.y); - } - - // Check glyph bounding box size - if (check_glyph_area(render_priv->library, glyph)) { - FT_Done_Glyph(glyph); - glyph = 0; - goto blend_vector_error; - } - - ass_msg(render_priv->library, MSGL_DBG2, - "Parsed vector clip: scales (%f, %f) string [%s]\n", - drawing->scale_x, drawing->scale_y, drawing->text); - - error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1); - if (error) { - ass_msg(render_priv->library, MSGL_WARN, - "Clip vector rasterization failed: %d. Skipping.", error); - FT_Done_Glyph(glyph); - glyph = 0; - } - -blend_vector_error: - clip_bm = (FT_BitmapGlyph) glyph; - - // Add to cache - memset(&v, 0, sizeof(v)); - v.glyph = glyph; - cache_add_glyph(render_priv->cache.glyph_cache, &key, &v); - } - - if (!clip_bm) goto blend_vector_exit; - - // Iterate through bitmaps and blend/clip them - for (cur = head; cur; cur = cur->next) { - int left, top, right, bottom, apos, bpos, y, x, w, h; - int ax, ay, aw, ah, as; - int bx, by, bw, bh, bs; - int aleft, atop, bleft, btop; - unsigned char *abuffer, *bbuffer, *nbuffer; - - abuffer = cur->bitmap; - bbuffer = clip_bm->bitmap.buffer; - ax = cur->dst_x; - ay = cur->dst_y; - aw = cur->w; - ah = cur->h; - as = cur->stride; - bx = clip_bm->left; - by = -clip_bm->top; - bw = clip_bm->bitmap.width; - bh = clip_bm->bitmap.rows; - bs = clip_bm->bitmap.pitch; - - // Calculate overlap coordinates - left = (ax > bx) ? ax : bx; - top = (ay > by) ? ay : by; - right = ((ax + aw) < (bx + bw)) ? (ax + aw) : (bx + bw); - bottom = ((ay + ah) < (by + bh)) ? (ay + ah) : (by + bh); - aleft = left - ax; - atop = top - ay; - w = right - left; - h = bottom - top; - bleft = left - bx; - btop = top - by; - - if (render_priv->state.clip_drawing_mode) { - // Inverse clip - if (ax + aw < bx || ay + ah < by || ax > bx + bw || - ay > by + bh) { - continue; - } - - // Allocate new buffer and add to free list - nbuffer = malloc(as * ah); - if (!nbuffer) goto blend_vector_exit; - free_list_add(render_priv, nbuffer); - - // Blend together - memcpy(nbuffer, abuffer, as * (ah - 1) + aw); - for (y = 0; y < h; y++) - for (x = 0; x < w; x++) { - apos = (atop + y) * as + aleft + x; - bpos = (btop + y) * bs + bleft + x; - nbuffer[apos] = FFMAX(0, abuffer[apos] - bbuffer[bpos]); - } - } else { - // Regular clip - if (ax + aw < bx || ay + ah < by || ax > bx + bw || - ay > by + bh) { - cur->w = cur->h = 0; - continue; - } - - // Allocate new buffer and add to free list - nbuffer = calloc(as, ah); - if (!nbuffer) goto blend_vector_exit; - free_list_add(render_priv, nbuffer); - - // Blend together - for (y = 0; y < h; y++) - for (x = 0; x < w; x++) { - apos = (atop + y) * as + aleft + x; - bpos = (btop + y) * bs + bleft + x; - nbuffer[apos] = (abuffer[apos] * bbuffer[bpos] + 255) >> 8; - } - } - cur->bitmap = nbuffer; - } - -blend_vector_exit: - ass_drawing_free(render_priv->state.clip_drawing); - render_priv->state.clip_drawing = 0; -} - -/** - * \brief Convert TextInfo struct to ASS_Image list - * Splits glyphs in halves when needed (for \kf karaoke). - */ -static ASS_Image *render_text(ASS_Renderer *render_priv, int dst_x, int dst_y) -{ - int pen_x, pen_y; - int i; - Bitmap *bm; - ASS_Image *head; - ASS_Image **tail = &head; - ASS_Image **last_tail = 0; - ASS_Image **here_tail = 0; - TextInfo *text_info = &render_priv->text_info; - - for (i = 0; i < text_info->length; ++i) { - GlyphInfo *info = text_info->glyphs + i; - if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_s - || (info->shadow_x == 0 && info->shadow_y == 0) || info->skip) - continue; - - pen_x = - dst_x + (info->pos.x >> 6) + - (int) (info->shadow_x * render_priv->border_scale); - pen_y = - dst_y + (info->pos.y >> 6) + - (int) (info->shadow_y * render_priv->border_scale); - bm = info->bm_s; - - here_tail = tail; - tail = - render_glyph(render_priv, bm, pen_x, pen_y, info->c[3], 0, - 1000000, tail); - if (last_tail && tail != here_tail && ((info->c[3] & 0xff) > 0)) - render_overlap(render_priv, last_tail, here_tail); - - last_tail = here_tail; - } - - last_tail = 0; - for (i = 0; i < text_info->length; ++i) { - GlyphInfo *info = text_info->glyphs + i; - if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_o - || info->skip) - continue; - - pen_x = dst_x + (info->pos.x >> 6); - pen_y = dst_y + (info->pos.y >> 6); - bm = info->bm_o; - - if ((info->effect_type == EF_KARAOKE_KO) - && (info->effect_timing <= (info->bbox.xMax >> 6))) { - // do nothing - } else { - here_tail = tail; - tail = - render_glyph(render_priv, bm, pen_x, pen_y, info->c[2], - 0, 1000000, tail); - if (last_tail && tail != here_tail && ((info->c[2] & 0xff) > 0)) - render_overlap(render_priv, last_tail, here_tail); - - last_tail = here_tail; - } - } - - for (i = 0; i < text_info->length; ++i) { - GlyphInfo *info = text_info->glyphs + i; - if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm - || info->skip) - continue; - - pen_x = dst_x + (info->pos.x >> 6); - pen_y = dst_y + (info->pos.y >> 6); - bm = info->bm; - - if ((info->effect_type == EF_KARAOKE) - || (info->effect_type == EF_KARAOKE_KO)) { - if (info->effect_timing > (info->bbox.xMax >> 6)) - tail = - render_glyph(render_priv, bm, pen_x, pen_y, - info->c[0], 0, 1000000, tail); - else - tail = - render_glyph(render_priv, bm, pen_x, pen_y, - info->c[1], 0, 1000000, tail); - } else if (info->effect_type == EF_KARAOKE_KF) { - tail = - render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], - info->c[1], info->effect_timing, tail); - } else - tail = - render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], - 0, 1000000, tail); - } - - *tail = 0; - blend_vector_clip(render_priv, head); - - return head; -} - -static void compute_string_bbox(TextInfo *info, DBBox *bbox) -{ - int i; - - if (info->length > 0) { - bbox->xMin = 32000; - bbox->xMax = -32000; - bbox->yMin = -1 * info->lines[0].asc + d6_to_double(info->glyphs[0].pos.y); - bbox->yMax = info->height - info->lines[0].asc + - d6_to_double(info->glyphs[0].pos.y); - - for (i = 0; i < info->length; ++i) { - double s, e; - if (info->glyphs[i].skip) continue; - s = d6_to_double(info->glyphs[i].pos.x); - e = s + d6_to_double(info->glyphs[i].advance.x); - bbox->xMin = FFMIN(bbox->xMin, s); - bbox->xMax = FFMAX(bbox->xMax, e); - } - } else - bbox->xMin = bbox->xMax = bbox->yMin = bbox->yMax = 0.; -} - -/** - * \brief partially reset render_context to style values - * Works like {\r}: resets some style overrides - */ -void reset_render_context(ASS_Renderer *render_priv) -{ - render_priv->state.c[0] = render_priv->state.style->PrimaryColour; - render_priv->state.c[1] = render_priv->state.style->SecondaryColour; - render_priv->state.c[2] = render_priv->state.style->OutlineColour; - render_priv->state.c[3] = render_priv->state.style->BackColour; - render_priv->state.flags = - (render_priv->state.style->Underline ? DECO_UNDERLINE : 0) | - (render_priv->state.style->StrikeOut ? DECO_STRIKETHROUGH : 0); - render_priv->state.font_size = render_priv->state.style->FontSize; - - free(render_priv->state.family); - render_priv->state.family = NULL; - render_priv->state.family = strdup(render_priv->state.style->FontName); - render_priv->state.treat_family_as_pattern = - render_priv->state.style->treat_fontname_as_pattern; - render_priv->state.bold = render_priv->state.style->Bold; - render_priv->state.italic = render_priv->state.style->Italic; - update_font(render_priv); - - change_border(render_priv, -1., -1.); - render_priv->state.scale_x = render_priv->state.style->ScaleX; - render_priv->state.scale_y = render_priv->state.style->ScaleY; - render_priv->state.hspacing = render_priv->state.style->Spacing; - render_priv->state.be = 0; - render_priv->state.blur = 0.0; - render_priv->state.shadow_x = render_priv->state.style->Shadow; - render_priv->state.shadow_y = render_priv->state.style->Shadow; - render_priv->state.frx = render_priv->state.fry = 0.; - render_priv->state.frz = M_PI * render_priv->state.style->Angle / 180.; - render_priv->state.fax = render_priv->state.fay = 0.; - render_priv->state.wrap_style = render_priv->track->WrapStyle; -} - -/** - * \brief Start new event. Reset render_priv->state. - */ -static void -init_render_context(ASS_Renderer *render_priv, ASS_Event *event) -{ - render_priv->state.event = event; - render_priv->state.style = render_priv->track->styles + event->Style; - - reset_render_context(render_priv); - - render_priv->state.evt_type = EVENT_NORMAL; - render_priv->state.alignment = render_priv->state.style->Alignment; - render_priv->state.pos_x = 0; - render_priv->state.pos_y = 0; - render_priv->state.org_x = 0; - render_priv->state.org_y = 0; - render_priv->state.have_origin = 0; - render_priv->state.clip_x0 = 0; - render_priv->state.clip_y0 = 0; - render_priv->state.clip_x1 = render_priv->track->PlayResX; - render_priv->state.clip_y1 = render_priv->track->PlayResY; - render_priv->state.clip_mode = 0; - render_priv->state.detect_collisions = 1; - render_priv->state.fade = 0; - render_priv->state.drawing_mode = 0; - render_priv->state.effect_type = EF_NONE; - render_priv->state.effect_timing = 0; - render_priv->state.effect_skip_timing = 0; - ass_drawing_free(render_priv->state.drawing); - render_priv->state.drawing = ass_drawing_new(render_priv->fontconfig_priv, - render_priv->state.font, - render_priv->ftlibrary); - - apply_transition_effects(render_priv, event); -} - -static void free_render_context(ASS_Renderer *render_priv) -{ - free(render_priv->state.family); - ass_drawing_free(render_priv->state.drawing); - - render_priv->state.family = NULL; - render_priv->state.drawing = NULL; -} - -/* - * Replace the outline of a glyph by a contour which makes up a simple - * opaque rectangle. - */ -static void draw_opaque_box(ASS_Renderer *render_priv, uint32_t ch, - FT_Glyph glyph, int sx, int sy) -{ - int asc = 0, desc = 0; - int i; - int adv = d16_to_d6(glyph->advance.x); - double scale_y = render_priv->state.scale_y; - double scale_x = render_priv->state.scale_x; - FT_OutlineGlyph og = (FT_OutlineGlyph) glyph; - FT_Outline *ol; - - // to avoid gaps - sx = FFMAX(64, sx); - sy = FFMAX(64, sy); - - if (ch == -1) { - asc = render_priv->state.drawing->asc; - desc = render_priv->state.drawing->desc; - } else { - ass_font_get_asc_desc(render_priv->state.font, ch, &asc, &desc); - asc *= scale_y; - desc *= scale_y; - } - - // Emulate the WTFish behavior of VSFilter, i.e. double-scale - // the sizes of the opaque box. - adv += double_to_d6(render_priv->state.hspacing * render_priv->font_scale - * scale_x); - adv *= scale_x; - sx *= scale_x; - sy *= scale_y; - desc *= scale_y; - desc += asc * (scale_y - 1.0); - - { - FT_Vector points[4] = { - { -sx, asc + sy }, - { adv + sx, asc + sy }, - { adv + sx, -desc - sy }, - { -sx, -desc - sy }, - }; - - FT_Outline_Done(render_priv->ftlibrary, &og->outline); - FT_Outline_New(render_priv->ftlibrary, 4, 1, &og->outline); - - ol = &og->outline; - ol->n_points = ol->n_contours = 0; - for (i = 0; i < 4; i++) { - ol->points[ol->n_points] = points[i]; - ol->tags[ol->n_points++] = 1; - } - } - ol->contours[ol->n_contours++] = ol->n_points - 1; -} - -/* - * Stroke an outline glyph in x/y direction. Applies various fixups to get - * around limitations of the FreeType stroker. - */ -static void stroke_outline_glyph(ASS_Renderer *render_priv, - FT_OutlineGlyph *glyph, int sx, int sy) -{ - if (sx <= 0 && sy <= 0) - return; - - fix_freetype_stroker(*glyph, sx, sy); - - // Borders are equal; use the regular stroker - if (sx == sy && render_priv->state.stroker) { - int error; - error = FT_Glyph_StrokeBorder((FT_Glyph *) glyph, - render_priv->state.stroker, 0, 1); - if (error) - ass_msg(render_priv->library, MSGL_WARN, - "FT_Glyph_Stroke error: %d", error); - - // "Stroke" with the outline emboldener in two passes. - // The outlines look uglier, but the emboldening never adds any points - } else { - int i; - FT_Outline *ol = &(*glyph)->outline; - FT_Outline nol; - FT_Outline_New(render_priv->ftlibrary, ol->n_points, - ol->n_contours, &nol); - FT_Outline_Copy(ol, &nol); - - FT_Outline_Embolden(ol, sx * 2); - FT_Outline_Translate(ol, -sx, -sx); - FT_Outline_Embolden(&nol, sy * 2); - FT_Outline_Translate(&nol, -sy, -sy); - - for (i = 0; i < ol->n_points; i++) - ol->points[i].y = nol.points[i].y; - - FT_Outline_Done(render_priv->ftlibrary, &nol); - } -} - -/** - * \brief Prepare glyph hash - */ -static void -fill_glyph_hash(ASS_Renderer *priv, GlyphHashKey *key, - ASS_Drawing *drawing, uint32_t ch) -{ - if (drawing->hash) { - key->scale_x = double_to_d16(priv->state.scale_x); - key->scale_y = double_to_d16(priv->state.scale_y); - key->outline.x = priv->state.border_x * 0xFFFF; - key->outline.y = priv->state.border_y * 0xFFFF; - key->border_style = priv->state.style->BorderStyle; - key->drawing_hash = drawing->hash; - // not very clean, but works - key->size = drawing->scale; - key->ch = -1; - } else { - key->font = priv->state.font; - key->size = priv->state.font_size; - key->ch = ch; - key->bold = priv->state.bold; - key->italic = priv->state.italic; - key->scale_x = double_to_d16(priv->state.scale_x); - key->scale_y = double_to_d16(priv->state.scale_y); - key->outline.x = priv->state.border_x * 0xFFFF; - key->outline.y = priv->state.border_y * 0xFFFF; - key->flags = priv->state.flags; - key->border_style = priv->state.style->BorderStyle; - } -} - -/** - * \brief Get normal and outline (border) glyphs - * \param symbol ucs4 char - * \param info out: struct filled with extracted data - * Tries to get both glyphs from cache. - * If they can't be found, gets a glyph from font face, generates outline with FT_Stroker, - * and add them to cache. - * The glyphs are returned in info->glyph and info->outline_glyph - */ -static void -get_outline_glyph(ASS_Renderer *render_priv, int symbol, GlyphInfo *info, - ASS_Drawing *drawing) -{ - GlyphHashValue *val; - GlyphHashKey key; - - memset(&key, 0, sizeof(key)); - memset(info, 0, sizeof(GlyphInfo)); - - fill_glyph_hash(render_priv, &key, drawing, symbol); - val = cache_find_glyph(render_priv->cache.glyph_cache, &key); - if (val) { - info->glyph = val->glyph; - info->outline_glyph = val->outline_glyph; - info->bbox = val->bbox_scaled; - info->advance.x = val->advance.x; - info->advance.y = val->advance.y; - if (drawing->hash) { - drawing->asc = val->asc; - drawing->desc = val->desc; - } - } else { - GlyphHashValue v; - if (drawing->hash) { - if(!ass_drawing_parse(drawing, 0)) - return; - info->glyph = (FT_Glyph) drawing->glyph; - } else { - info->glyph = - ass_font_get_glyph(render_priv->fontconfig_priv, - render_priv->state.font, symbol, - render_priv->settings.hinting, - render_priv->state.flags); - } - if (!info->glyph) - return; - - info->advance.x = d16_to_d6(info->glyph->advance.x); - info->advance.y = d16_to_d6(info->glyph->advance.y); - FT_Glyph_Get_CBox(info->glyph, FT_GLYPH_BBOX_SUBPIXELS, &info->bbox); - - if (render_priv->state.style->BorderStyle == 3 && - (render_priv->state.border_x > 0|| - render_priv->state.border_y > 0)) { - FT_Glyph_Copy(info->glyph, &info->outline_glyph); - draw_opaque_box(render_priv, symbol, info->outline_glyph, - double_to_d6(render_priv->state.border_x * - render_priv->border_scale), - double_to_d6(render_priv->state.border_y * - render_priv->border_scale)); - } else if ((render_priv->state.border_x > 0 - || render_priv->state.border_y > 0) - && key.scale_x && key.scale_y) { - - FT_Glyph_Copy(info->glyph, &info->outline_glyph); - stroke_outline_glyph(render_priv, - (FT_OutlineGlyph *) &info->outline_glyph, - double_to_d6(render_priv->state.border_x * - render_priv->border_scale), - double_to_d6(render_priv->state.border_y * - render_priv->border_scale)); - } - - memset(&v, 0, sizeof(v)); - v.glyph = info->glyph; - v.outline_glyph = info->outline_glyph; - v.advance = info->advance; - v.bbox_scaled = info->bbox; - if (drawing->hash) { - v.asc = drawing->asc; - v.desc = drawing->desc; - } - cache_add_glyph(render_priv->cache.glyph_cache, &key, &v); - } -} - -/** - * \brief Apply transformation to outline points of a glyph - * Applies rotations given by frx, fry and frz and projects the points back - * onto the screen plane. - */ -static void -transform_3d_points(FT_Vector shift, FT_Glyph glyph, double frx, double fry, - double frz, double fax, double fay, double scale, - int yshift) -{ - double sx = sin(frx); - double sy = sin(fry); - double sz = sin(frz); - double cx = cos(frx); - double cy = cos(fry); - double cz = cos(frz); - FT_Outline *outline = &((FT_OutlineGlyph) glyph)->outline; - FT_Vector *p = outline->points; - double x, y, z, xx, yy, zz; - int i, dist; - - dist = 20000 * scale; - for (i = 0; i < outline->n_points; i++) { - x = (double) p[i].x + shift.x + (fax * (yshift - p[i].y)); - y = (double) p[i].y + shift.y + (-fay * p[i].x); - z = 0.; - - xx = x * cz + y * sz; - yy = -(x * sz - y * cz); - zz = z; - - x = xx; - y = yy * cx + zz * sx; - z = yy * sx - zz * cx; - - xx = x * cy + z * sy; - yy = y; - zz = x * sy - z * cy; - - zz = FFMAX(zz, 1000 - dist); - - x = (xx * dist) / (zz + dist); - y = (yy * dist) / (zz + dist); - p[i].x = x - shift.x + 0.5; - p[i].y = y - shift.y + 0.5; - } -} - -/** - * \brief Apply 3d transformation to several objects - * \param shift FreeType vector - * \param glyph FreeType glyph - * \param glyph2 FreeType glyph - * \param frx x-axis rotation angle - * \param fry y-axis rotation angle - * \param frz z-axis rotation angle - * Rotates both glyphs by frx, fry and frz. Shift vector is added before rotation and subtracted after it. - */ -static void -transform_3d(FT_Vector shift, FT_Glyph *glyph, FT_Glyph *glyph2, - double frx, double fry, double frz, double fax, double fay, - double scale, int yshift) -{ - frx = -frx; - frz = -frz; - if (frx != 0. || fry != 0. || frz != 0. || fax != 0. || fay != 0.) { - if (glyph && *glyph) - transform_3d_points(shift, *glyph, frx, fry, frz, - fax, fay, scale, yshift); - - if (glyph2 && *glyph2) - transform_3d_points(shift, *glyph2, frx, fry, frz, - fax, fay, scale, yshift); - } -} - -/** - * \brief Get bitmaps for a glyph - * \param info glyph info - * Tries to get glyph bitmaps from bitmap cache. - * If they can't be found, they are generated by rotating and rendering the glyph. - * After that, bitmaps are added to the cache. - * They are returned in info->bm (glyph), info->bm_o (outline) and info->bm_s (shadow). - */ -static void -get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info) -{ - BitmapHashValue *val; - BitmapHashKey *key = &info->hash_key; - - val = cache_find_bitmap(render_priv->cache.bitmap_cache, key); - - if (val) { - info->bm = val->bm; - info->bm_o = val->bm_o; - info->bm_s = val->bm_s; - } else { - FT_Vector shift; - BitmapHashValue hash_val; - int error; - double fax_scaled, fay_scaled; - info->bm = info->bm_o = info->bm_s = 0; - if (info->glyph && info->symbol != '\n' && info->symbol != 0 - && !info->skip) { - FT_Glyph glyph; - FT_Glyph outline; - double scale_x = render_priv->font_scale_x; - - // PAR correction scaling - FT_Matrix m = { double_to_d16(scale_x), 0, 0, double_to_d16(1.0) }; - - FT_Glyph_Copy(info->glyph, &glyph); - FT_Glyph_Copy(info->outline_glyph, &outline); - // calculating rotation shift vector (from rotation origin to the glyph basepoint) - shift.x = key->shift_x; - shift.y = key->shift_y; - fax_scaled = info->fax * - render_priv->state.scale_x; - fay_scaled = info->fay * render_priv->state.scale_y; - // apply rotation - transform_3d(shift, &glyph, &outline, - info->frx, info->fry, info->frz, fax_scaled, - fay_scaled, render_priv->font_scale, info->asc); - - // subpixel shift - if (glyph) { - FT_Outline *outl = &((FT_OutlineGlyph) glyph)->outline; - if (scale_x != 1.0) - FT_Outline_Transform(outl, &m); - FT_Outline_Translate(outl, key->advance.x, -key->advance.y); - } - if (outline) { - FT_Outline *outl = &((FT_OutlineGlyph) outline)->outline; - if (scale_x != 1.0) - FT_Outline_Transform(outl, &m); - FT_Outline_Translate(outl, key->advance.x, -key->advance.y); - } - // render glyph - error = glyph_to_bitmap(render_priv->library, - render_priv->synth_priv, - glyph, outline, - &info->bm, &info->bm_o, - &info->bm_s, info->be, - info->blur * render_priv->border_scale, - key->shadow_offset, key->border_style); - if (error) - info->symbol = 0; - - // add bitmaps to cache - hash_val.bm_o = info->bm_o; - hash_val.bm = info->bm; - hash_val.bm_s = info->bm_s; - cache_add_bitmap(render_priv->cache.bitmap_cache, key, &hash_val); - - FT_Done_Glyph(glyph); - FT_Done_Glyph(outline); - } - } -} - -/** - * This function goes through text_info and calculates text parameters. - * The following text_info fields are filled: - * height - * lines[].height - * lines[].asc - * lines[].desc - */ -static void measure_text(ASS_Renderer *render_priv) -{ - TextInfo *text_info = &render_priv->text_info; - int cur_line = 0; - double max_asc = 0., max_desc = 0.; - GlyphInfo *last = NULL; - int i; - int empty_line = 1; - text_info->height = 0.; - for (i = 0; i < text_info->length + 1; ++i) { - if ((i == text_info->length) || text_info->glyphs[i].linebreak) { - if (empty_line && cur_line > 0 && last && i < text_info->length) { - max_asc = d6_to_double(last->asc) / 2.0; - max_desc = d6_to_double(last->desc) / 2.0; - } - text_info->lines[cur_line].asc = max_asc; - text_info->lines[cur_line].desc = max_desc; - text_info->height += max_asc + max_desc; - cur_line++; - max_asc = max_desc = 0.; - empty_line = 1; - } else - empty_line = 0; - if (i < text_info->length) { - GlyphInfo *cur = text_info->glyphs + i; - if (d6_to_double(cur->asc) > max_asc) - max_asc = d6_to_double(cur->asc); - if (d6_to_double(cur->desc) > max_desc) - max_desc = d6_to_double(cur->desc); - if (cur->symbol != '\n' && cur->symbol != 0) - last = cur; - } - } - text_info->height += - (text_info->n_lines - - 1) * render_priv->settings.line_spacing; -} - -/** - * Mark extra whitespace for later removal. - */ -#define IS_WHITESPACE(x) ((x->symbol == ' ' || x->symbol == '\n') \ - && !x->linebreak) -static void trim_whitespace(ASS_Renderer *render_priv) -{ - int i, j; - GlyphInfo *cur; - TextInfo *ti = &render_priv->text_info; - - // Mark trailing spaces - i = ti->length - 1; - cur = ti->glyphs + i; - while (i && IS_WHITESPACE(cur)) { - cur->skip++; - cur = ti->glyphs + --i; - } - - // Mark leading whitespace - i = 0; - cur = ti->glyphs; - while (i < ti->length && IS_WHITESPACE(cur)) { - cur->skip++; - cur = ti->glyphs + ++i; - } - - // Mark all extraneous whitespace inbetween - for (i = 0; i < ti->length; ++i) { - cur = ti->glyphs + i; - if (cur->linebreak) { - // Mark whitespace before - j = i - 1; - cur = ti->glyphs + j; - while (j && IS_WHITESPACE(cur)) { - cur->skip++; - cur = ti->glyphs + --j; - } - // A break itself can contain a whitespace, too - cur = ti->glyphs + i; - if (cur->symbol == ' ') - cur->skip++; - // Mark whitespace after - j = i + 1; - cur = ti->glyphs + j; - while (j < ti->length && IS_WHITESPACE(cur)) { - cur->skip++; - cur = ti->glyphs + ++j; - } - i = j - 1; - } - } -} -#undef IS_WHITESPACE - -/** - * \brief rearrange text between lines - * \param max_text_width maximal text line width in pixels - * The algo is similar to the one in libvo/sub.c: - * 1. Place text, wrapping it when current line is full - * 2. Try moving words from the end of a line to the beginning of the next one while it reduces - * the difference in lengths between this two lines. - * The result may not be optimal, but usually is good enough. - * - * FIXME: implement style 0 and 3 correctly - */ -static void -wrap_lines_smart(ASS_Renderer *render_priv, double max_text_width) -{ - int i; - GlyphInfo *cur, *s1, *e1, *s2, *s3, *w; - int last_space; - int break_type; - int exit; - double pen_shift_x; - double pen_shift_y; - int cur_line; - TextInfo *text_info = &render_priv->text_info; - - last_space = -1; - text_info->n_lines = 1; - break_type = 0; - s1 = text_info->glyphs; // current line start - for (i = 0; i < text_info->length; ++i) { - int break_at; - double s_offset, len; - cur = text_info->glyphs + i; - break_at = -1; - s_offset = d6_to_double(s1->bbox.xMin + s1->pos.x); - len = d6_to_double(cur->bbox.xMax + cur->pos.x) - s_offset; - - if (cur->symbol == '\n') { - break_type = 2; - break_at = i; - ass_msg(render_priv->library, MSGL_DBG2, - "forced line break at %d", break_at); - } - - if ((len >= max_text_width) - && (render_priv->state.wrap_style != 2)) { - break_type = 1; - break_at = last_space; - if (break_at >= 0) - ass_msg(render_priv->library, MSGL_DBG2, "line break at %d", - break_at); - } - - if (break_at != -1) { - // need to use one more line - // marking break_at+1 as start of a new line - int lead = break_at + 1; // the first symbol of the new line - if (text_info->n_lines >= text_info->max_lines) { - // Raise maximum number of lines - text_info->max_lines *= 2; - text_info->lines = realloc(text_info->lines, - sizeof(LineInfo) * - text_info->max_lines); - } - if (lead < text_info->length) - text_info->glyphs[lead].linebreak = break_type; - last_space = -1; - s1 = text_info->glyphs + lead; - s_offset = d6_to_double(s1->bbox.xMin + s1->pos.x); - text_info->n_lines++; - } - - if (cur->symbol == ' ') - last_space = i; - - // make sure the hard linebreak is not forgotten when - // there was a new soft linebreak just inserted - if (cur->symbol == '\n' && break_type == 1) - i--; - } -#define DIFF(x,y) (((x) < (y)) ? (y - x) : (x - y)) - exit = 0; - while (!exit && render_priv->state.wrap_style != 1) { - exit = 1; - w = s3 = text_info->glyphs; - s1 = s2 = 0; - for (i = 0; i <= text_info->length; ++i) { - cur = text_info->glyphs + i; - if ((i == text_info->length) || cur->linebreak) { - s1 = s2; - s2 = s3; - s3 = cur; - if (s1 && (s2->linebreak == 1)) { // have at least 2 lines, and linebreak is 'soft' - double l1, l2, l1_new, l2_new; - - w = s2; - do { - --w; - } while ((w > s1) && (w->symbol == ' ')); - while ((w > s1) && (w->symbol != ' ')) { - --w; - } - e1 = w; - while ((e1 > s1) && (e1->symbol == ' ')) { - --e1; - } - if (w->symbol == ' ') - ++w; - - l1 = d6_to_double(((s2 - 1)->bbox.xMax + (s2 - 1)->pos.x) - - (s1->bbox.xMin + s1->pos.x)); - l2 = d6_to_double(((s3 - 1)->bbox.xMax + (s3 - 1)->pos.x) - - (s2->bbox.xMin + s2->pos.x)); - l1_new = d6_to_double( - (e1->bbox.xMax + e1->pos.x) - - (s1->bbox.xMin + s1->pos.x)); - l2_new = d6_to_double( - ((s3 - 1)->bbox.xMax + (s3 - 1)->pos.x) - - (w->bbox.xMin + w->pos.x)); - - if (DIFF(l1_new, l2_new) < DIFF(l1, l2)) { - w->linebreak = 1; - s2->linebreak = 0; - exit = 0; - } - } - } - if (i == text_info->length) - break; - } - - } - assert(text_info->n_lines >= 1); -#undef DIFF - - measure_text(render_priv); - trim_whitespace(render_priv); - - pen_shift_x = 0.; - pen_shift_y = 0.; - cur_line = 1; - - i = 0; - cur = text_info->glyphs + i; - while (i < text_info->length && cur->skip) - cur = text_info->glyphs + ++i; - pen_shift_x = d6_to_double(-cur->pos.x); - - for (i = 0; i < text_info->length; ++i) { - cur = text_info->glyphs + i; - if (cur->linebreak) { - double height; - while (i < text_info->length && cur->skip && cur->symbol != '\n') - cur = text_info->glyphs + ++i; - height = - text_info->lines[cur_line - 1].desc + - text_info->lines[cur_line].asc; - cur_line++; - pen_shift_x = d6_to_double(-cur->pos.x); - pen_shift_y += height + render_priv->settings.line_spacing; - ass_msg(render_priv->library, MSGL_DBG2, - "shifting from %d to %d by (%f, %f)", i, - text_info->length - 1, pen_shift_x, pen_shift_y); - } - cur->pos.x += double_to_d6(pen_shift_x); - cur->pos.y += double_to_d6(pen_shift_y); - } -} - -/** - * \brief determine karaoke effects - * Karaoke effects cannot be calculated during parse stage (get_next_char()), - * so they are done in a separate step. - * Parse stage: when karaoke style override is found, its parameters are stored in the next glyph's - * (the first glyph of the karaoke word)'s effect_type and effect_timing. - * This function: - * 1. sets effect_type for all glyphs in the word (_karaoke_ word) - * 2. sets effect_timing for all glyphs to x coordinate of the border line between the left and right karaoke parts - * (left part is filled with PrimaryColour, right one - with SecondaryColour). - */ -static void process_karaoke_effects(ASS_Renderer *render_priv) -{ - GlyphInfo *cur, *cur2; - GlyphInfo *s1, *e1; // start and end of the current word - GlyphInfo *s2; // start of the next word - int i; - int timing; // current timing - int tm_start, tm_end; // timings at start and end of the current word - int tm_current; - double dt; - int x; - int x_start, x_end; - - tm_current = render_priv->time - render_priv->state.event->Start; - timing = 0; - s1 = s2 = 0; - for (i = 0; i <= render_priv->text_info.length; ++i) { - cur = render_priv->text_info.glyphs + i; - if ((i == render_priv->text_info.length) - || (cur->effect_type != EF_NONE)) { - s1 = s2; - s2 = cur; - if (s1) { - e1 = s2 - 1; - tm_start = timing + s1->effect_skip_timing; - tm_end = tm_start + s1->effect_timing; - timing = tm_end; - x_start = 1000000; - x_end = -1000000; - for (cur2 = s1; cur2 <= e1; ++cur2) { - x_start = FFMIN(x_start, d6_to_int(cur2->bbox.xMin + cur2->pos.x)); - x_end = FFMAX(x_end, d6_to_int(cur2->bbox.xMax + cur2->pos.x)); - } - - dt = (tm_current - tm_start); - if ((s1->effect_type == EF_KARAOKE) - || (s1->effect_type == EF_KARAOKE_KO)) { - if (dt > 0) - x = x_end + 1; - else - x = x_start; - } else if (s1->effect_type == EF_KARAOKE_KF) { - dt /= (tm_end - tm_start); - x = x_start + (x_end - x_start) * dt; - } else { - ass_msg(render_priv->library, MSGL_ERR, - "Unknown effect type"); - continue; - } - - for (cur2 = s1; cur2 <= e1; ++cur2) { - cur2->effect_type = s1->effect_type; - cur2->effect_timing = x - d6_to_int(cur2->pos.x); - } - } - } - } -} - -/** - * \brief Calculate base point for positioning and rotation - * \param bbox text bbox - * \param alignment alignment - * \param bx, by out: base point coordinates - */ -static void get_base_point(DBBox *bbox, int alignment, double *bx, double *by) -{ - const int halign = alignment & 3; - const int valign = alignment & 12; - if (bx) - switch (halign) { - case HALIGN_LEFT: - *bx = bbox->xMin; - break; - case HALIGN_CENTER: - *bx = (bbox->xMax + bbox->xMin) / 2.0; - break; - case HALIGN_RIGHT: - *bx = bbox->xMax; - break; - } - if (by) - switch (valign) { - case VALIGN_TOP: - *by = bbox->yMin; - break; - case VALIGN_CENTER: - *by = (bbox->yMax + bbox->yMin) / 2.0; - break; - case VALIGN_SUB: - *by = bbox->yMax; - break; - } -} - -/** - * Prepare bitmap hash key of a glyph - */ -static void -fill_bitmap_hash(ASS_Renderer *priv, BitmapHashKey *hash_key, - ASS_Drawing *drawing, FT_Vector pen, uint32_t code) -{ - if (!drawing->hash) { - hash_key->font = priv->state.font; - hash_key->size = priv->state.font_size; - hash_key->bold = priv->state.bold; - hash_key->italic = priv->state.italic; - } else { - hash_key->drawing_hash = drawing->hash; - hash_key->size = drawing->scale; - } - hash_key->ch = code; - hash_key->outline.x = double_to_d16(priv->state.border_x); - hash_key->outline.y = double_to_d16(priv->state.border_y); - hash_key->scale_x = double_to_d16(priv->state.scale_x); - hash_key->scale_y = double_to_d16(priv->state.scale_y); - hash_key->frx = rot_key(priv->state.frx); - hash_key->fry = rot_key(priv->state.fry); - hash_key->frz = rot_key(priv->state.frz); - hash_key->fax = double_to_d16(priv->state.fax); - hash_key->fay = double_to_d16(priv->state.fay); - hash_key->be = priv->state.be; - hash_key->blur = priv->state.blur; - hash_key->border_style = priv->state.style->BorderStyle; - hash_key->shadow_offset.x = double_to_d6( - priv->state.shadow_x * priv->border_scale - - (int) (priv->state.shadow_x * priv->border_scale)); - hash_key->shadow_offset.y = double_to_d6( - priv->state.shadow_y * priv->border_scale - - (int) (priv->state.shadow_y * priv->border_scale)); - hash_key->flags = priv->state.flags; -} - -/** - * \brief Main ass rendering function, glues everything together - * \param event event to render - * \param event_images struct containing resulting images, will also be initialized - * Process event, appending resulting ASS_Image's to images_root. - */ -static int -ass_render_event(ASS_Renderer *render_priv, ASS_Event *event, - EventImages *event_images) -{ - char *p; - FT_UInt previous; - FT_UInt num_glyphs; - FT_Vector pen; - unsigned code; - DBBox bbox; - int i, j; - int MarginL, MarginR, MarginV; - int last_break; - int alignment, halign, valign; - int kern = render_priv->track->Kerning; - double device_x = 0; - double device_y = 0; - TextInfo *text_info = &render_priv->text_info; - GlyphInfo *glyphs = render_priv->text_info.glyphs; - ASS_Drawing *drawing; - - if (event->Style >= render_priv->track->n_styles) { - ass_msg(render_priv->library, MSGL_WARN, "No style found"); - return 1; - } - if (!event->Text) { - ass_msg(render_priv->library, MSGL_WARN, "Empty event"); - return 1; - } - - init_render_context(render_priv, event); - - drawing = render_priv->state.drawing; - text_info->length = 0; - pen.x = 0; - pen.y = 0; - previous = 0; - num_glyphs = 0; - p = event->Text; - // Event parsing. - while (1) { - // get next char, executing style override - // this affects render_context - do { - code = get_next_char(render_priv, &p); - if (render_priv->state.drawing_mode && code) - ass_drawing_add_char(drawing, (char) code); - } while (code && render_priv->state.drawing_mode); // skip everything in drawing mode - - // Parse drawing - if (drawing->i) { - drawing->scale_x = render_priv->state.scale_x * - render_priv->font_scale; - drawing->scale_y = render_priv->state.scale_y * - render_priv->font_scale; - ass_drawing_hash(drawing); - p--; - code = -1; - } - - // face could have been changed in get_next_char - if (!render_priv->state.font) { - free_render_context(render_priv); - return 1; - } - - if (code == 0) - break; - - if (text_info->length >= text_info->max_glyphs) { - // Raise maximum number of glyphs - text_info->max_glyphs *= 2; - text_info->glyphs = glyphs = - realloc(text_info->glyphs, - sizeof(GlyphInfo) * text_info->max_glyphs); - } - - // Add kerning to pen - if (kern && previous && code && !drawing->hash) { - FT_Vector delta; - delta = - ass_font_get_kerning(render_priv->state.font, previous, - code); - pen.x += delta.x * render_priv->state.scale_x; - pen.y += delta.y * render_priv->state.scale_y; - } - - ass_font_set_transform(render_priv->state.font, - render_priv->state.scale_x, - render_priv->state.scale_y, NULL); - - get_outline_glyph(render_priv, code, - glyphs + text_info->length, drawing); - - // Add additional space after italic to non-italic style changes - if (text_info->length && - glyphs[text_info->length - 1].hash_key.italic && - !render_priv->state.italic) { - int back = text_info->length - 1; - GlyphInfo *og = &glyphs[back]; - while (back && og->bbox.xMax - og->bbox.xMin == 0 - && og->hash_key.italic) - og = &glyphs[--back]; - if (og->bbox.xMax > og->advance.x) { - // The FreeType oblique slants by 6/16 - pen.x += og->bbox.yMax * 0.375; - } - } - - glyphs[text_info->length].pos.x = pen.x; - glyphs[text_info->length].pos.y = pen.y; - - pen.x += glyphs[text_info->length].advance.x; - pen.x += double_to_d6(render_priv->state.hspacing * - render_priv->font_scale - * render_priv->state.scale_x); - pen.y += glyphs[text_info->length].advance.y; - pen.y += (render_priv->state.fay * render_priv->state.scale_y) * - glyphs[text_info->length].advance.x; - - previous = code; - - glyphs[text_info->length].symbol = code; - glyphs[text_info->length].linebreak = 0; - for (i = 0; i < 4; ++i) { - uint32_t clr = render_priv->state.c[i]; - change_alpha(&clr, - mult_alpha(_a(clr), render_priv->state.fade), 1.); - glyphs[text_info->length].c[i] = clr; - } - glyphs[text_info->length].effect_type = render_priv->state.effect_type; - glyphs[text_info->length].effect_timing = - render_priv->state.effect_timing; - glyphs[text_info->length].effect_skip_timing = - render_priv->state.effect_skip_timing; - glyphs[text_info->length].be = render_priv->state.be; - glyphs[text_info->length].blur = render_priv->state.blur; - glyphs[text_info->length].shadow_x = render_priv->state.shadow_x; - glyphs[text_info->length].shadow_y = render_priv->state.shadow_y; - glyphs[text_info->length].frx = render_priv->state.frx; - glyphs[text_info->length].fry = render_priv->state.fry; - glyphs[text_info->length].frz = render_priv->state.frz; - glyphs[text_info->length].fax = render_priv->state.fax; - glyphs[text_info->length].fay = render_priv->state.fay; - if (drawing->hash) { - glyphs[text_info->length].asc = drawing->asc; - glyphs[text_info->length].desc = drawing->desc; - } else { - ass_font_get_asc_desc(render_priv->state.font, code, - &glyphs[text_info->length].asc, - &glyphs[text_info->length].desc); - - glyphs[text_info->length].asc *= render_priv->state.scale_y; - glyphs[text_info->length].desc *= render_priv->state.scale_y; - } - - // fill bitmap hash - fill_bitmap_hash(render_priv, &glyphs[text_info->length].hash_key, - drawing, pen, code); - - text_info->length++; - - render_priv->state.effect_type = EF_NONE; - render_priv->state.effect_timing = 0; - render_priv->state.effect_skip_timing = 0; - - if (drawing->hash) { - ass_drawing_free(drawing); - drawing = render_priv->state.drawing = - ass_drawing_new(render_priv->fontconfig_priv, - render_priv->state.font, - render_priv->ftlibrary); - } - } - - - if (text_info->length == 0) { - // no valid symbols in the event; this can be smth like {comment} - free_render_context(render_priv); - return 1; - } - - // depends on glyph x coordinates being monotonous, so it should be done before line wrap - process_karaoke_effects(render_priv); - - // alignments - alignment = render_priv->state.alignment; - halign = alignment & 3; - valign = alignment & 12; - - MarginL = - (event->MarginL) ? event->MarginL : render_priv->state.style->MarginL; - MarginR = - (event->MarginR) ? event->MarginR : render_priv->state.style->MarginR; - MarginV = - (event->MarginV) ? event->MarginV : render_priv->state.style->MarginV; - - if (render_priv->state.evt_type != EVENT_HSCROLL) { - double max_text_width; - - // calculate max length of a line - max_text_width = - x2scr(render_priv, - render_priv->track->PlayResX - MarginR) - - x2scr(render_priv, MarginL); - - // rearrange text in several lines - wrap_lines_smart(render_priv, max_text_width); - - // align text - last_break = -1; - for (i = 1; i < text_info->length + 1; ++i) { // (text_info->length + 1) is the end of the last line - if ((i == text_info->length) - || glyphs[i].linebreak) { - double width, shift = 0; - GlyphInfo *first_glyph = - glyphs + last_break + 1; - GlyphInfo *last_glyph = glyphs + i - 1; - - while (first_glyph < last_glyph && first_glyph->skip) - first_glyph++; - - while ((last_glyph > first_glyph) - && ((last_glyph->symbol == '\n') - || (last_glyph->symbol == 0) - || (last_glyph->skip))) - last_glyph--; - - width = d6_to_double( - last_glyph->pos.x + last_glyph->advance.x - - first_glyph->pos.x); - if (halign == HALIGN_LEFT) { // left aligned, no action - shift = 0; - } else if (halign == HALIGN_RIGHT) { // right aligned - shift = max_text_width - width; - } else if (halign == HALIGN_CENTER) { // centered - shift = (max_text_width - width) / 2.0; - } - for (j = last_break + 1; j < i; ++j) { - glyphs[j].pos.x += double_to_d6(shift); - } - last_break = i - 1; - } - } - } else { // render_priv->state.evt_type == EVENT_HSCROLL - measure_text(render_priv); - } - - // determing text bounding box - compute_string_bbox(text_info, &bbox); - - // determine device coordinates for text - - // x coordinate for everything except positioned events - if (render_priv->state.evt_type == EVENT_NORMAL || - render_priv->state.evt_type == EVENT_VSCROLL) { - device_x = x2scr(render_priv, MarginL); - } else if (render_priv->state.evt_type == EVENT_HSCROLL) { - if (render_priv->state.scroll_direction == SCROLL_RL) - device_x = - x2scr(render_priv, - render_priv->track->PlayResX - - render_priv->state.scroll_shift); - else if (render_priv->state.scroll_direction == SCROLL_LR) - device_x = - x2scr(render_priv, - render_priv->state.scroll_shift) - (bbox.xMax - - bbox.xMin); - } - - // y coordinate for everything except positioned events - if (render_priv->state.evt_type == EVENT_NORMAL || - render_priv->state.evt_type == EVENT_HSCROLL) { - if (valign == VALIGN_TOP) { // toptitle - device_y = - y2scr_top(render_priv, - MarginV) + text_info->lines[0].asc; - } else if (valign == VALIGN_CENTER) { // midtitle - double scr_y = - y2scr(render_priv, render_priv->track->PlayResY / 2.0); - device_y = scr_y - (bbox.yMax + bbox.yMin) / 2.0; - } else { // subtitle - double scr_y; - if (valign != VALIGN_SUB) - ass_msg(render_priv->library, MSGL_V, - "Invalid valign, assuming 0 (subtitle)"); - scr_y = - y2scr_sub(render_priv, - render_priv->track->PlayResY - MarginV); - device_y = scr_y; - device_y -= text_info->height; - device_y += text_info->lines[0].asc; - } - } else if (render_priv->state.evt_type == EVENT_VSCROLL) { - if (render_priv->state.scroll_direction == SCROLL_TB) - device_y = - y2scr(render_priv, - render_priv->state.clip_y0 + - render_priv->state.scroll_shift) - (bbox.yMax - - bbox.yMin); - else if (render_priv->state.scroll_direction == SCROLL_BT) - device_y = - y2scr(render_priv, - render_priv->state.clip_y1 - - render_priv->state.scroll_shift); - } - - // positioned events are totally different - if (render_priv->state.evt_type == EVENT_POSITIONED) { - double base_x = 0; - double base_y = 0; - ass_msg(render_priv->library, MSGL_DBG2, "positioned event at %f, %f", - render_priv->state.pos_x, render_priv->state.pos_y); - get_base_point(&bbox, alignment, &base_x, &base_y); - device_x = - x2scr_pos(render_priv, render_priv->state.pos_x) - base_x; - device_y = - y2scr_pos(render_priv, render_priv->state.pos_y) - base_y; - } - - // fix clip coordinates (they depend on alignment) - if (render_priv->state.evt_type == EVENT_NORMAL || - render_priv->state.evt_type == EVENT_HSCROLL || - render_priv->state.evt_type == EVENT_VSCROLL) { - render_priv->state.clip_x0 = - x2scr_scaled(render_priv, render_priv->state.clip_x0); - render_priv->state.clip_x1 = - x2scr_scaled(render_priv, render_priv->state.clip_x1); - if (valign == VALIGN_TOP) { - render_priv->state.clip_y0 = - y2scr_top(render_priv, render_priv->state.clip_y0); - render_priv->state.clip_y1 = - y2scr_top(render_priv, render_priv->state.clip_y1); - } else if (valign == VALIGN_CENTER) { - render_priv->state.clip_y0 = - y2scr(render_priv, render_priv->state.clip_y0); - render_priv->state.clip_y1 = - y2scr(render_priv, render_priv->state.clip_y1); - } else if (valign == VALIGN_SUB) { - render_priv->state.clip_y0 = - y2scr_sub(render_priv, render_priv->state.clip_y0); - render_priv->state.clip_y1 = - y2scr_sub(render_priv, render_priv->state.clip_y1); - } - } else if (render_priv->state.evt_type == EVENT_POSITIONED) { - render_priv->state.clip_x0 = - x2scr_pos_scaled(render_priv, render_priv->state.clip_x0); - render_priv->state.clip_x1 = - x2scr_pos_scaled(render_priv, render_priv->state.clip_x1); - render_priv->state.clip_y0 = - y2scr_pos(render_priv, render_priv->state.clip_y0); - render_priv->state.clip_y1 = - y2scr_pos(render_priv, render_priv->state.clip_y1); - } - - // calculate rotation parameters - { - DVector center; - - if (render_priv->state.have_origin) { - center.x = x2scr(render_priv, render_priv->state.org_x); - center.y = y2scr(render_priv, render_priv->state.org_y); - } else { - double bx = 0., by = 0.; - get_base_point(&bbox, alignment, &bx, &by); - center.x = device_x + bx; - center.y = device_y + by; - } - - for (i = 0; i < text_info->length; ++i) { - GlyphInfo *info = glyphs + i; - - if (info->hash_key.frx || info->hash_key.fry - || info->hash_key.frz || info->hash_key.fax - || info->hash_key.fay) { - info->hash_key.shift_x = info->pos.x + double_to_d6(device_x - center.x); - info->hash_key.shift_y = - -(info->pos.y + double_to_d6(device_y - center.y)); - } else { - info->hash_key.shift_x = 0; - info->hash_key.shift_y = 0; - } - } - } - - // convert glyphs to bitmaps - device_x *= render_priv->font_scale_x; - for (i = 0; i < text_info->length; ++i) { - GlyphInfo *g = glyphs + i; - g->pos.x *= render_priv->font_scale_x; - g->hash_key.advance.x = - double_to_d6(device_x - (int) device_x + - d6_to_double(g->pos.x & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY; - g->hash_key.advance.y = - double_to_d6(device_y - (int) device_y + - d6_to_double(g->pos.y & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY; - get_bitmap_glyph(render_priv, glyphs + i); - } - - memset(event_images, 0, sizeof(*event_images)); - event_images->top = device_y - text_info->lines[0].asc; - event_images->height = text_info->height; - event_images->left = - (device_x + bbox.xMin * render_priv->font_scale_x) + 0.5; - event_images->width = - (bbox.xMax - bbox.xMin) * render_priv->font_scale_x + 0.5; - event_images->detect_collisions = render_priv->state.detect_collisions; - event_images->shift_direction = (valign == VALIGN_TOP) ? 1 : -1; - event_images->event = event; - event_images->imgs = render_text(render_priv, (int) device_x, (int) device_y); - - free_render_context(render_priv); - - return 0; -} - -/** - * \brief deallocate image list - * \param img list pointer - */ -void ass_free_images(ASS_Image *img) -{ - while (img) { - ASS_Image *next = img->next; - free(img); - img = next; - } -} - -/** - * \brief Check cache limits and reset cache if they are exceeded - */ -static void check_cache_limits(ASS_Renderer *priv, CacheStore *cache) -{ - if (cache->bitmap_cache->cache_size > cache->bitmap_max_size) { - ass_msg(priv->library, MSGL_V, - "Hitting hard bitmap cache limit (was: %ld bytes), " - "resetting.", (long) cache->bitmap_cache->cache_size); - cache->bitmap_cache = ass_bitmap_cache_reset(cache->bitmap_cache); - cache->composite_cache = ass_composite_cache_reset( - cache->composite_cache); - ass_free_images(priv->prev_images_root); - priv->prev_images_root = 0; - } - - if (cache->glyph_cache->count > cache->glyph_max - || cache->glyph_cache->cache_size > cache->bitmap_max_size) { - ass_msg(priv->library, MSGL_V, - "Hitting hard glyph cache limit (was: %d glyphs, %ld bytes), " - "resetting.", - cache->glyph_cache->count, (long) cache->glyph_cache->cache_size); - cache->glyph_cache = ass_glyph_cache_reset(cache->glyph_cache); - } -} - -/** - * \brief Start a new frame - */ -static int -ass_start_frame(ASS_Renderer *render_priv, ASS_Track *track, - long long now) -{ - ASS_Settings *settings_priv = &render_priv->settings; - - if (!render_priv->settings.frame_width - && !render_priv->settings.frame_height) - return 1; // library not initialized - - if (render_priv->library != track->library) - return 1; - - if (!render_priv->fontconfig_priv) - return 1; - - free_list_clear(render_priv); - - if (track->n_events == 0) - return 1; // nothing to do - - render_priv->track = track; - render_priv->time = now; - - ass_lazy_track_init(render_priv); - - render_priv->font_scale = settings_priv->font_size_coeff * - render_priv->orig_height / render_priv->track->PlayResY; - if (render_priv->track->ScaledBorderAndShadow) - render_priv->border_scale = - ((double) render_priv->orig_height) / - render_priv->track->PlayResY; - else - render_priv->border_scale = 1.; - - // PAR correction - render_priv->font_scale_x = render_priv->settings.aspect / - render_priv->settings.storage_aspect; - - render_priv->prev_images_root = render_priv->images_root; - render_priv->images_root = 0; - - check_cache_limits(render_priv, &render_priv->cache); - - return 0; -} - -static int cmp_event_layer(const void *p1, const void *p2) -{ - ASS_Event *e1 = ((EventImages *) p1)->event; - ASS_Event *e2 = ((EventImages *) p2)->event; - if (e1->Layer < e2->Layer) - return -1; - if (e1->Layer > e2->Layer) - return 1; - if (e1->ReadOrder < e2->ReadOrder) - return -1; - if (e1->ReadOrder > e2->ReadOrder) - return 1; - return 0; -} - -static ASS_RenderPriv *get_render_priv(ASS_Renderer *render_priv, - ASS_Event *event) -{ - if (!event->render_priv) - event->render_priv = calloc(1, sizeof(ASS_RenderPriv)); - if (render_priv->render_id != event->render_priv->render_id) { - memset(event->render_priv, 0, sizeof(ASS_RenderPriv)); - event->render_priv->render_id = render_priv->render_id; - } - - return event->render_priv; -} - -static int overlap(Segment *s1, Segment *s2) -{ - if (s1->a >= s2->b || s2->a >= s1->b || - s1->ha >= s2->hb || s2->ha >= s1->hb) - return 0; - return 1; -} - -static int cmp_segment(const void *p1, const void *p2) -{ - return ((Segment *) p1)->a - ((Segment *) p2)->a; -} - -static void -shift_event(ASS_Renderer *render_priv, EventImages *ei, int shift) -{ - ASS_Image *cur = ei->imgs; - while (cur) { - cur->dst_y += shift; - // clip top and bottom - if (cur->dst_y < 0) { - int clip = -cur->dst_y; - cur->h -= clip; - cur->bitmap += clip * cur->stride; - cur->dst_y = 0; - } - if (cur->dst_y + cur->h >= render_priv->height) { - int clip = cur->dst_y + cur->h - render_priv->height; - cur->h -= clip; - } - if (cur->h <= 0) { - cur->h = 0; - cur->dst_y = 0; - } - cur = cur->next; - } - ei->top += shift; -} - -// dir: 1 - move down -// -1 - move up -static int fit_segment(Segment *s, Segment *fixed, int *cnt, int dir) -{ - int i; - int shift = 0; - - if (dir == 1) // move down - for (i = 0; i < *cnt; ++i) { - if (s->b + shift <= fixed[i].a || s->a + shift >= fixed[i].b || - s->hb <= fixed[i].ha || s->ha >= fixed[i].hb) - continue; - shift = fixed[i].b - s->a; - } else // dir == -1, move up - for (i = *cnt - 1; i >= 0; --i) { - if (s->b + shift <= fixed[i].a || s->a + shift >= fixed[i].b || - s->hb <= fixed[i].ha || s->ha >= fixed[i].hb) - continue; - shift = fixed[i].a - s->b; - } - - fixed[*cnt].a = s->a + shift; - fixed[*cnt].b = s->b + shift; - fixed[*cnt].ha = s->ha; - fixed[*cnt].hb = s->hb; - (*cnt)++; - qsort(fixed, *cnt, sizeof(Segment), cmp_segment); - - return shift; -} - -static void -fix_collisions(ASS_Renderer *render_priv, EventImages *imgs, int cnt) -{ - Segment *used = malloc(cnt * sizeof(*used)); - int cnt_used = 0; - int i, j; - - // fill used[] with fixed events - for (i = 0; i < cnt; ++i) { - ASS_RenderPriv *priv; - if (!imgs[i].detect_collisions) - continue; - priv = get_render_priv(render_priv, imgs[i].event); - if (priv->height > 0) { // it's a fixed event - Segment s; - s.a = priv->top; - s.b = priv->top + priv->height; - s.ha = priv->left; - s.hb = priv->left + priv->width; - if (priv->height != imgs[i].height) { // no, it's not - ass_msg(render_priv->library, MSGL_WARN, - "Event height has changed"); - priv->top = 0; - priv->height = 0; - priv->left = 0; - priv->width = 0; - } - for (j = 0; j < cnt_used; ++j) - if (overlap(&s, used + j)) { // no, it's not - priv->top = 0; - priv->height = 0; - priv->left = 0; - priv->width = 0; - } - if (priv->height > 0) { // still a fixed event - used[cnt_used].a = priv->top; - used[cnt_used].b = priv->top + priv->height; - used[cnt_used].ha = priv->left; - used[cnt_used].hb = priv->left + priv->width; - cnt_used++; - shift_event(render_priv, imgs + i, priv->top - imgs[i].top); - } - } - } - qsort(used, cnt_used, sizeof(Segment), cmp_segment); - - // try to fit other events in free spaces - for (i = 0; i < cnt; ++i) { - ASS_RenderPriv *priv; - if (!imgs[i].detect_collisions) - continue; - priv = get_render_priv(render_priv, imgs[i].event); - if (priv->height == 0) { // not a fixed event - int shift; - Segment s; - s.a = imgs[i].top; - s.b = imgs[i].top + imgs[i].height; - s.ha = imgs[i].left; - s.hb = imgs[i].left + imgs[i].width; - shift = fit_segment(&s, used, &cnt_used, imgs[i].shift_direction); - if (shift) - shift_event(render_priv, imgs + i, shift); - // make it fixed - priv->top = imgs[i].top; - priv->height = imgs[i].height; - priv->left = imgs[i].left; - priv->width = imgs[i].width; - } - - } - - free(used); -} - -/** - * \brief compare two images - * \param i1 first image - * \param i2 second image - * \return 0 if identical, 1 if different positions, 2 if different content - */ -static int ass_image_compare(ASS_Image *i1, ASS_Image *i2) -{ - if (i1->w != i2->w) - return 2; - if (i1->h != i2->h) - return 2; - if (i1->stride != i2->stride) - return 2; - if (i1->color != i2->color) - return 2; - if (i1->bitmap != i2->bitmap) - return 2; - if (i1->dst_x != i2->dst_x) - return 1; - if (i1->dst_y != i2->dst_y) - return 1; - return 0; -} - -/** - * \brief compare current and previous image list - * \param priv library handle - * \return 0 if identical, 1 if different positions, 2 if different content - */ -static int ass_detect_change(ASS_Renderer *priv) -{ - ASS_Image *img, *img2; - int diff; - - img = priv->prev_images_root; - img2 = priv->images_root; - diff = 0; - while (img && diff < 2) { - ASS_Image *next, *next2; - next = img->next; - if (img2) { - int d = ass_image_compare(img, img2); - if (d > diff) - diff = d; - next2 = img2->next; - } else { - // previous list is shorter - diff = 2; - break; - } - img = next; - img2 = next2; - } - - // is the previous list longer? - if (img2) - diff = 2; - - return diff; -} - -/** - * \brief render a frame - * \param priv library handle - * \param track track - * \param now current video timestamp (ms) - * \param detect_change a value describing how the new images differ from the previous ones will be written here: - * 0 if identical, 1 if different positions, 2 if different content. - * Can be NULL, in that case no detection is performed. - */ -ASS_Image *ass_render_frame(ASS_Renderer *priv, ASS_Track *track, - long long now, int *detect_change) -{ - int i, cnt, rc; - EventImages *last; - ASS_Image **tail; - - // init frame - rc = ass_start_frame(priv, track, now); - if (rc != 0) - return 0; - - // render events separately - cnt = 0; - for (i = 0; i < track->n_events; ++i) { - ASS_Event *event = track->events + i; - if ((event->Start <= now) - && (now < (event->Start + event->Duration))) { - if (cnt >= priv->eimg_size) { - priv->eimg_size += 100; - priv->eimg = - realloc(priv->eimg, - priv->eimg_size * sizeof(EventImages)); - } - rc = ass_render_event(priv, event, priv->eimg + cnt); - if (!rc) - ++cnt; - } - } - - // sort by layer - qsort(priv->eimg, cnt, sizeof(EventImages), cmp_event_layer); - - // call fix_collisions for each group of events with the same layer - last = priv->eimg; - for (i = 1; i < cnt; ++i) - if (last->event->Layer != priv->eimg[i].event->Layer) { - fix_collisions(priv, last, priv->eimg + i - last); - last = priv->eimg + i; - } - if (cnt > 0) - fix_collisions(priv, last, priv->eimg + cnt - last); - - // concat lists - tail = &priv->images_root; - for (i = 0; i < cnt; ++i) { - ASS_Image *cur = priv->eimg[i].imgs; - while (cur) { - *tail = cur; - tail = &cur->next; - cur = cur->next; - } - } - - if (detect_change) - *detect_change = ass_detect_change(priv); - - // free the previous image list - ass_free_images(priv->prev_images_root); - priv->prev_images_root = 0; - - return priv->images_root; -} diff --git a/aegisub/libass/ass_render.h b/aegisub/libass/ass_render.h deleted file mode 100644 index 89bffb012..000000000 --- a/aegisub/libass/ass_render.h +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * Copyright (C) 2009 Grigori Goronzy - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef LIBASS_RENDER_H -#define LIBASS_RENDER_H - -#include -#include -#include FT_FREETYPE_H -#include FT_STROKER_H -#include FT_GLYPH_H -#include FT_SYNTHESIS_H - -#include "ass.h" -#include "ass_font.h" -#include "ass_bitmap.h" -#include "ass_cache.h" -#include "ass_utils.h" -#include "ass_fontconfig.h" -#include "ass_library.h" -#include "ass_drawing.h" - -#define GLYPH_CACHE_MAX 1000 -#define BITMAP_CACHE_MAX_SIZE 30 * 1048576 - -typedef struct { - double xMin; - double xMax; - double yMin; - double yMax; -} DBBox; - -typedef struct { - double x; - double y; -} DVector; - -typedef struct free_list { - void *object; - struct free_list *next; -} FreeList; - -typedef struct { - int frame_width; - int frame_height; - double font_size_coeff; // font size multiplier - double line_spacing; // additional line spacing (in frame pixels) - int top_margin; // height of top margin. Everything except toptitles is shifted down by top_margin. - int bottom_margin; // height of bottom margin. (frame_height - top_margin - bottom_margin) is original video height. - int left_margin; - int right_margin; - int use_margins; // 0 - place all subtitles inside original frame - // 1 - use margins for placing toptitles and subtitles - double aspect; // frame aspect ratio, d_width / d_height. - double storage_aspect; // pixel ratio of the source image - ASS_Hinting hinting; - - char *default_font; - char *default_family; -} ASS_Settings; - -// a rendered event -typedef struct { - ASS_Image *imgs; - int top, height, left, width; - int detect_collisions; - int shift_direction; - ASS_Event *event; -} EventImages; - -typedef enum { - EF_NONE = 0, - EF_KARAOKE, - EF_KARAOKE_KF, - EF_KARAOKE_KO -} Effect; - -// describes a glyph -// GlyphInfo and TextInfo are used for text centering and word-wrapping operations -typedef struct { - unsigned symbol; - unsigned skip; // skip glyph when layouting text - FT_Glyph glyph; - FT_Glyph outline_glyph; - Bitmap *bm; // glyph bitmap - Bitmap *bm_o; // outline bitmap - Bitmap *bm_s; // shadow bitmap - FT_BBox bbox; - FT_Vector pos; - char linebreak; // the first (leading) glyph of some line ? - uint32_t c[4]; // colors - FT_Vector advance; // 26.6 - Effect effect_type; - int effect_timing; // time duration of current karaoke word - // after process_karaoke_effects: distance in pixels from the glyph origin. - // part of the glyph to the left of it is displayed in a different color. - int effect_skip_timing; // delay after the end of last karaoke word - int asc, desc; // font max ascender and descender - int be; // blur edges - double blur; // gaussian blur - double shadow_x; - double shadow_y; - double frx, fry, frz; // rotation - double fax, fay; // text shearing - - BitmapHashKey hash_key; -} GlyphInfo; - -typedef struct { - double asc, desc; -} LineInfo; - -typedef struct { - GlyphInfo *glyphs; - int length; - LineInfo *lines; - int n_lines; - double height; - int max_glyphs; - int max_lines; -} TextInfo; - -// Renderer state. -// Values like current font face, color, screen position, clipping and so on are stored here. -typedef struct { - ASS_Event *event; - ASS_Style *style; - - ASS_Font *font; - char *font_path; - double font_size; - int flags; // decoration flags (underline/strike-through) - - FT_Stroker stroker; - int alignment; // alignment overrides go here; if zero, style value will be used - double frx, fry, frz; - double fax, fay; // text shearing - enum { - EVENT_NORMAL, // "normal" top-, sub- or mid- title - EVENT_POSITIONED, // happens after pos(,), margins are ignored - EVENT_HSCROLL, // "Banner" transition effect, text_width is unlimited - EVENT_VSCROLL // "Scroll up", "Scroll down" transition effects - } evt_type; - double pos_x, pos_y; // position - double org_x, org_y; // origin - char have_origin; // origin is explicitly defined; if 0, get_base_point() is used - double scale_x, scale_y; - double hspacing; // distance between letters, in pixels - double border_x; // outline width - double border_y; - uint32_t c[4]; // colors(Primary, Secondary, so on) in RGBA - int clip_x0, clip_y0, clip_x1, clip_y1; - char clip_mode; // 1 = iclip - char detect_collisions; - uint32_t fade; // alpha from \fad - char be; // blur edges - double blur; // gaussian blur - double shadow_x; - double shadow_y; - int drawing_mode; // not implemented; when != 0 text is discarded, except for style override tags - ASS_Drawing *drawing; // current drawing - ASS_Drawing *clip_drawing; // clip vector - int clip_drawing_mode; // 0 = regular clip, 1 = inverse clip - - Effect effect_type; - int effect_timing; - int effect_skip_timing; - - enum { - SCROLL_LR, // left-to-right - SCROLL_RL, - SCROLL_TB, // top-to-bottom - SCROLL_BT - } scroll_direction; // for EVENT_HSCROLL, EVENT_VSCROLL - int scroll_shift; - - // face properties - char *family; - unsigned bold; - unsigned italic; - int treat_family_as_pattern; - int wrap_style; -} RenderContext; - -typedef struct { - Hashmap *font_cache; - Hashmap *glyph_cache; - Hashmap *bitmap_cache; - Hashmap *composite_cache; - size_t glyph_max; - size_t bitmap_max_size; -} CacheStore; - -struct ass_renderer { - ASS_Library *library; - FT_Library ftlibrary; - FCInstance *fontconfig_priv; - ASS_Settings settings; - int render_id; - ASS_SynthPriv *synth_priv; - - ASS_Image *images_root; // rendering result is stored here - ASS_Image *prev_images_root; - - EventImages *eimg; // temporary buffer for sorting rendered events - int eimg_size; // allocated buffer size - - // frame-global data - int width, height; // screen dimensions - int orig_height; // frame height ( = screen height - margins ) - int orig_width; // frame width ( = screen width - margins ) - int orig_height_nocrop; // frame height ( = screen height - margins + cropheight) - int orig_width_nocrop; // frame width ( = screen width - margins + cropwidth) - ASS_Track *track; - long long time; // frame's timestamp, ms - double font_scale; - double font_scale_x; // x scale applied to all glyphs to preserve text aspect ratio - double border_scale; - - RenderContext state; - TextInfo text_info; - CacheStore cache; - - FreeList *free_head; - FreeList *free_tail; -}; - -typedef struct render_priv { - int top, height, left, width; - int render_id; -} RenderPriv; - -typedef struct { - int x0; - int y0; - int x1; - int y1; -} Rect; - -typedef struct { - int a, b; // top and height - int ha, hb; // left and width -} Segment; - -void reset_render_context(ASS_Renderer *render_priv); -void ass_free_images(ASS_Image *img); - -#endif /* LIBASS_RENDER_H */ diff --git a/aegisub/libass/ass_render_api.c b/aegisub/libass/ass_render_api.c deleted file mode 100644 index 65cfa58d0..000000000 --- a/aegisub/libass/ass_render_api.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * Copyright (C) 2010 Grigori Goronzy - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "config.h" -#include "ass_render.h" - -static void ass_reconfigure(ASS_Renderer *priv) -{ - ASS_Settings *settings = &priv->settings; - - priv->render_id++; - priv->cache.glyph_cache = - ass_glyph_cache_reset(priv->cache.glyph_cache); - priv->cache.bitmap_cache = - ass_bitmap_cache_reset(priv->cache.bitmap_cache); - priv->cache.composite_cache = - ass_composite_cache_reset(priv->cache.composite_cache); - ass_free_images(priv->prev_images_root); - priv->prev_images_root = 0; - - priv->width = settings->frame_width; - priv->height = settings->frame_height; - priv->orig_width = settings->frame_width - settings->left_margin - - settings->right_margin; - priv->orig_height = settings->frame_height - settings->top_margin - - settings->bottom_margin; - priv->orig_width_nocrop = - settings->frame_width - FFMAX(settings->left_margin, 0) - - FFMAX(settings->right_margin, 0); - priv->orig_height_nocrop = - settings->frame_height - FFMAX(settings->top_margin, 0) - - FFMAX(settings->bottom_margin, 0); -} - -void ass_set_frame_size(ASS_Renderer *priv, int w, int h) -{ - if (priv->settings.frame_width != w || priv->settings.frame_height != h) { - priv->settings.frame_width = w; - priv->settings.frame_height = h; - if (priv->settings.aspect == 0.) { - priv->settings.aspect = ((double) w) / h; - priv->settings.storage_aspect = ((double) w) / h; - } - ass_reconfigure(priv); - } -} - -void ass_set_margins(ASS_Renderer *priv, int t, int b, int l, int r) -{ - if (priv->settings.left_margin != l || priv->settings.right_margin != r || - priv->settings.top_margin != t || priv->settings.bottom_margin != b) { - priv->settings.left_margin = l; - priv->settings.right_margin = r; - priv->settings.top_margin = t; - priv->settings.bottom_margin = b; - ass_reconfigure(priv); - } -} - -void ass_set_use_margins(ASS_Renderer *priv, int use) -{ - priv->settings.use_margins = use; -} - -void ass_set_aspect_ratio(ASS_Renderer *priv, double dar, double sar) -{ - if (priv->settings.aspect != dar || priv->settings.storage_aspect != sar) { - priv->settings.aspect = dar; - priv->settings.storage_aspect = sar; - ass_reconfigure(priv); - } -} - -void ass_set_font_scale(ASS_Renderer *priv, double font_scale) -{ - if (priv->settings.font_size_coeff != font_scale) { - priv->settings.font_size_coeff = font_scale; - ass_reconfigure(priv); - } -} - -void ass_set_hinting(ASS_Renderer *priv, ASS_Hinting ht) -{ - if (priv->settings.hinting != ht) { - priv->settings.hinting = ht; - ass_reconfigure(priv); - } -} - -void ass_set_line_spacing(ASS_Renderer *priv, double line_spacing) -{ - priv->settings.line_spacing = line_spacing; -} - -void ass_set_fonts(ASS_Renderer *priv, const char *default_font, - const char *default_family, int fc, const char *config, - int update) -{ - free(priv->settings.default_font); - free(priv->settings.default_family); - priv->settings.default_font = default_font ? strdup(default_font) : 0; - priv->settings.default_family = - default_family ? strdup(default_family) : 0; - - if (priv->fontconfig_priv) - fontconfig_done(priv->fontconfig_priv); - priv->fontconfig_priv = - fontconfig_init(priv->library, priv->ftlibrary, default_family, - default_font, fc, config, update); -} - -int ass_fonts_update(ASS_Renderer *render_priv) -{ - return fontconfig_update(render_priv->fontconfig_priv); -} - -void ass_set_cache_limits(ASS_Renderer *render_priv, int glyph_max, - int bitmap_max) -{ - render_priv->cache.glyph_max = glyph_max ? glyph_max : GLYPH_CACHE_MAX; - render_priv->cache.bitmap_max_size = bitmap_max ? 1048576 * bitmap_max : - BITMAP_CACHE_MAX_SIZE; -} diff --git a/aegisub/libass/ass_strtod.c b/aegisub/libass/ass_strtod.c deleted file mode 100644 index b26cdd205..000000000 --- a/aegisub/libass/ass_strtod.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright (c) 1988-1993 The Regents of the University of California. - * Copyright (c) 1994 Sun Microsystems, Inc. - * - * Permission to use, copy, modify, and distribute this - * software and its documentation for any purpose and without - * fee is hereby granted, provided that the above copyright - * notice appear in all copies. The University of California - * makes no representations about the suitability of this - * software for any purpose. It is provided "as is" without - * express or implied warranty. - * - */ - -#include -#include -#include - -const -static int maxExponent = 511; /* Largest possible base 10 exponent. Any - * exponent larger than this will already - * produce underflow or overflow, so there's - * no need to worry about additional digits. - */ - -const -static double powersOf10[] = { /* Table giving binary powers of 10. Entry */ - 10., /* is 10^2^i. Used to convert decimal */ - 100., /* exponents into floating-point numbers. */ - 1.0e4, - 1.0e8, - 1.0e16, - 1.0e32, - 1.0e64, - 1.0e128, - 1.0e256 -}; - -/* - *---------------------------------------------------------------------- - * - * strtod -- - * - * This procedure converts a floating-point number from an ASCII - * decimal representation to internal double-precision format. - * - * Results: - * The return value is the double-precision floating-point - * representation of the characters in string. If endPtr isn't - * NULL, then *endPtr is filled in with the address of the - * next character after the last one that was part of the - * floating-point number. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -double -ass_strtod(string, endPtr) - const char *string; /* A decimal ASCII floating-point number, - * optionally preceded by white space. - * Must have form "-I.FE-X", where I is the - * integer part of the mantissa, F is the - * fractional part of the mantissa, and X - * is the exponent. Either of the signs - * may be "+", "-", or omitted. Either I - * or F may be omitted, or both. The decimal - * point isn't necessary unless F is present. - * The "E" may actually be an "e". E and X - * may both be omitted (but not just one). - */ - char **endPtr; /* If non-NULL, store terminating character's - * address here. */ -{ - int sign, expSign = 0; - double fraction, dblExp, *d; - register const char *p; - register int c; - int exp = 0; /* Exponent read from "EX" field. */ - int fracExp = 0; /* Exponent that derives from the fractional - * part. Under normal circumstatnces, it is - * the negative of the number of digits in F. - * However, if I is very long, the last digits - * of I get dropped (otherwise a long I with a - * large negative exponent could cause an - * unnecessary overflow on I alone). In this - * case, fracExp is incremented one for each - * dropped digit. */ - int mantSize; /* Number of digits in mantissa. */ - int decPt; /* Number of mantissa digits BEFORE decimal - * point. */ - const char *pExp; /* Temporarily holds location of exponent - * in string. */ - - /* - * Strip off leading blanks and check for a sign. - */ - - p = string; - while (isascii(*p) && isspace(*p)) { - p += 1; - } - if (*p == '-') { - sign = 1; - p += 1; - } else { - if (*p == '+') { - p += 1; - } - sign = 0; - } - - /* - * Count the number of digits in the mantissa (including the decimal - * point), and also locate the decimal point. - */ - - decPt = -1; - for (mantSize = 0; ; mantSize += 1) - { - c = *p; - if (!isascii(*p) || !isdigit(c)) { - if ((c != '.') || (decPt >= 0)) { - break; - } - decPt = mantSize; - } - p += 1; - } - - /* - * Now suck up the digits in the mantissa. Use two integers to - * collect 9 digits each (this is faster than using floating-point). - * If the mantissa has more than 18 digits, ignore the extras, since - * they can't affect the value anyway. - */ - - pExp = p; - p -= mantSize; - if (decPt < 0) { - decPt = mantSize; - } else { - mantSize -= 1; /* One of the digits was the point. */ - } - if (mantSize > 18) { - fracExp = decPt - 18; - mantSize = 18; - } else { - fracExp = decPt - mantSize; - } - if (mantSize == 0) { - fraction = 0.0; - p = string; - goto done; - } else { - int frac1, frac2; - frac1 = 0; - for ( ; mantSize > 9; mantSize -= 1) - { - c = *p; - p += 1; - if (c == '.') { - c = *p; - p += 1; - } - frac1 = 10*frac1 + (c - '0'); - } - frac2 = 0; - for (; mantSize > 0; mantSize -= 1) - { - c = *p; - p += 1; - if (c == '.') { - c = *p; - p += 1; - } - frac2 = 10*frac2 + (c - '0'); - } - fraction = (1.0e9 * frac1) + frac2; - } - - /* - * Skim off the exponent. - */ - - p = pExp; - if ((*p == 'E') || (*p == 'e')) { - p += 1; - if (*p == '-') { - expSign = 1; - p += 1; - } else { - if (*p == '+') { - p += 1; - } - expSign = 0; - } - while (isascii(*p) && isdigit(*p)) { - exp = exp * 10 + (*p - '0'); - p += 1; - } - } - if (expSign) { - exp = fracExp - exp; - } else { - exp = fracExp + exp; - } - - /* - * Generate a floating-point number that represents the exponent. - * Do this by processing the exponent one bit at a time to combine - * many powers of 2 of 10. Then combine the exponent with the - * fraction. - */ - - if (exp < 0) { - expSign = 1; - exp = -exp; - } else { - expSign = 0; - } - if (exp > maxExponent) { - exp = maxExponent; - errno = ERANGE; - } - dblExp = 1.0; - for (d = (double *) powersOf10; exp != 0; exp >>= 1, d += 1) { - if (exp & 01) { - dblExp *= *d; - } - } - if (expSign) { - fraction /= dblExp; - } else { - fraction *= dblExp; - } - -done: - if (endPtr != NULL) { - *endPtr = (char *) p; - } - - if (sign) { - return -fraction; - } - return fraction; -} diff --git a/aegisub/libass/ass_types.h b/aegisub/libass/ass_types.h deleted file mode 100644 index 6a6f1ae86..000000000 --- a/aegisub/libass/ass_types.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef LIBASS_TYPES_H -#define LIBASS_TYPES_H - -#include - -#define VALIGN_SUB 0 -#define VALIGN_CENTER 8 -#define VALIGN_TOP 4 -#define HALIGN_LEFT 1 -#define HALIGN_CENTER 2 -#define HALIGN_RIGHT 3 - -/* Opaque objects internally used by libass. Contents are private. */ -typedef struct ass_renderer ASS_Renderer; -typedef struct render_priv ASS_RenderPriv; -typedef struct parser_priv ASS_ParserPriv; -typedef struct ass_library ASS_Library; - -/* ASS Style: line */ -typedef struct ass_style { - char *Name; - char *FontName; - double FontSize; - uint32_t PrimaryColour; - uint32_t SecondaryColour; - uint32_t OutlineColour; - uint32_t BackColour; - int Bold; - int Italic; - int Underline; - int StrikeOut; - double ScaleX; - double ScaleY; - double Spacing; - int Angle; - int BorderStyle; - double Outline; - double Shadow; - int Alignment; - int MarginL; - int MarginR; - int MarginV; - int Encoding; - int treat_fontname_as_pattern; -} ASS_Style; - -/* - * ASS_Event corresponds to a single Dialogue line; - * text is stored as-is, style overrides will be parsed later. - */ -typedef struct ass_event { - long long Start; // ms - long long Duration; // ms - - int ReadOrder; - int Layer; - int Style; - char *Name; - int MarginL; - int MarginR; - int MarginV; - char *Effect; - char *Text; - - ASS_RenderPriv *render_priv; -} ASS_Event; - -/* - * ass track represent either an external script or a matroska subtitle stream - * (no real difference between them); it can be used in rendering after the - * headers are parsed (i.e. events format line read). - */ -typedef struct ass_track { - int n_styles; // amount used - int max_styles; // amount allocated - int n_events; - int max_events; - ASS_Style *styles; // array of styles, max_styles length, n_styles used - ASS_Event *events; // the same as styles - - char *style_format; // style format line (everything after "Format: ") - char *event_format; // event format line - - enum { - TRACK_TYPE_UNKNOWN = 0, - TRACK_TYPE_ASS, - TRACK_TYPE_SSA - } track_type; - - // Script header fields - int PlayResX; - int PlayResY; - double Timer; - int WrapStyle; - int ScaledBorderAndShadow; - int Kerning; - - int default_style; // index of default style - char *name; // file name in case of external subs, 0 for streams - - ASS_Library *library; - ASS_ParserPriv *parser_priv; -} ASS_Track; - -#endif /* LIBASS_TYPES_H */ diff --git a/aegisub/libass/ass_utils.c b/aegisub/libass/ass_utils.c deleted file mode 100644 index 4c9d4bcc6..000000000 --- a/aegisub/libass/ass_utils.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include FT_GLYPH_H -#include - -#include "ass_library.h" -#include "ass.h" -#include "ass_utils.h" - -int mystrtoi(char **p, int *res) -{ - double temp_res; - char *start = *p; - temp_res = ass_strtod(*p, p); - *res = (int) (temp_res + (temp_res > 0 ? 0.5 : -0.5)); - if (*p != start) - return 1; - else - return 0; -} - -int mystrtoll(char **p, long long *res) -{ - double temp_res; - char *start = *p; - temp_res = ass_strtod(*p, p); - *res = (int) (temp_res + (temp_res > 0 ? 0.5 : -0.5)); - if (*p != start) - return 1; - else - return 0; -} - -int mystrtou32(char **p, int base, uint32_t *res) -{ - char *start = *p; - *res = strtoll(*p, p, base); - if (*p != start) - return 1; - else - return 0; -} - -int mystrtod(char **p, double *res) -{ - char *start = *p; - *res = ass_strtod(*p, p); - if (*p != start) - return 1; - else - return 0; -} - -int strtocolor(ASS_Library *library, char **q, uint32_t *res, int hex) -{ - uint32_t color = 0; - int result; - char *p = *q; - int base = hex ? 16 : 10; - - if (*p == '&') - ++p; - else - ass_msg(library, MSGL_DBG2, "suspicious color format: \"%s\"\n", p); - - if (*p == 'H' || *p == 'h') { - ++p; - result = mystrtou32(&p, 16, &color); - } else { - result = mystrtou32(&p, base, &color); - } - - { - unsigned char *tmp = (unsigned char *) (&color); - unsigned char b; - b = tmp[0]; - tmp[0] = tmp[3]; - tmp[3] = b; - b = tmp[1]; - tmp[1] = tmp[2]; - tmp[2] = b; - } - if (*p == '&') - ++p; - *q = p; - - *res = color; - return result; -} - -// Return a boolean value for a string -char parse_bool(char *str) -{ - while (*str == ' ' || *str == '\t') - str++; - if (!strncasecmp(str, "yes", 3)) - return 1; - else if (strtol(str, NULL, 10) > 0) - return 1; - return 0; -} - -void ass_msg(ASS_Library *priv, int lvl, char *fmt, ...) -{ - va_list va; - va_start(va, fmt); - priv->msg_callback(lvl, fmt, va, priv->msg_callback_data); - va_end(va); -} - -unsigned ass_utf8_get_char(char **str) -{ - uint8_t *strp = (uint8_t *) * str; - unsigned c = *strp++; - unsigned mask = 0x80; - int len = -1; - while (c & mask) { - mask >>= 1; - len++; - } - if (len <= 0 || len > 4) - goto no_utf8; - c &= mask - 1; - while ((*strp & 0xc0) == 0x80) { - if (len-- <= 0) - goto no_utf8; - c = (c << 6) | (*strp++ & 0x3f); - } - if (len) - goto no_utf8; - *str = (char *) strp; - return c; - - no_utf8: - strp = (uint8_t *) * str; - c = *strp++; - *str = (char *) strp; - return c; -} - -#ifdef CONFIG_ENCA -void *ass_guess_buffer_cp(ASS_Library *library, unsigned char *buffer, - int buflen, char *preferred_language, - char *fallback) -{ - const char **languages; - size_t langcnt; - EncaAnalyser analyser; - EncaEncoding encoding; - char *detected_sub_cp = NULL; - int i; - - languages = enca_get_languages(&langcnt); - ass_msg(library, MSGL_V, "ENCA supported languages"); - for (i = 0; i < langcnt; i++) { - ass_msg(library, MSGL_V, "lang %s", languages[i]); - } - - for (i = 0; i < langcnt; i++) { - const char *tmp; - - if (strcasecmp(languages[i], preferred_language) != 0) - continue; - analyser = enca_analyser_alloc(languages[i]); - encoding = enca_analyse_const(analyser, buffer, buflen); - tmp = enca_charset_name(encoding.charset, ENCA_NAME_STYLE_ICONV); - if (tmp && encoding.charset != ENCA_CS_UNKNOWN) { - detected_sub_cp = strdup(tmp); - ass_msg(library, MSGL_INFO, "ENCA detected charset: %s", tmp); - } - enca_analyser_free(analyser); - } - - free(languages); - - if (!detected_sub_cp) { - detected_sub_cp = strdup(fallback); - ass_msg(library, MSGL_INFO, - "ENCA detection failed: fallback to %s", fallback); - } - - return detected_sub_cp; -} -#endif diff --git a/aegisub/libass/ass_utils.h b/aegisub/libass/ass_utils.h deleted file mode 100644 index 327bb79cf..000000000 --- a/aegisub/libass/ass_utils.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * This file is part of libass. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef LIBASS_UTILS_H -#define LIBASS_UTILS_H - -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_ENCA -#include -#endif - -#include "ass.h" - -#define MSGL_FATAL 0 -#define MSGL_ERR 1 -#define MSGL_WARN 2 -#define MSGL_INFO 4 -#define MSGL_V 6 -#define MSGL_DBG2 7 - -#define FFMAX(a,b) ((a) > (b) ? (a) : (b)) -#define FFMIN(a,b) ((a) > (b) ? (b) : (a)) -#define FFMINMAX(c,a,b) FFMIN(FFMAX(c, a), b) - -int mystrtoi(char **p, int *res); -int mystrtoll(char **p, long long *res); -int mystrtou32(char **p, int base, uint32_t *res); -int mystrtod(char **p, double *res); -int strtocolor(ASS_Library *library, char **q, uint32_t *res, int hex); -char parse_bool(char *str); -unsigned ass_utf8_get_char(char **str); -void ass_msg(ASS_Library *priv, int lvl, char *fmt, ...); -#ifdef CONFIG_ENCA -void *ass_guess_buffer_cp(ASS_Library *library, unsigned char *buffer, - int buflen, char *preferred_language, - char *fallback); -#endif - -/* defined in ass_strtod.c */ -double ass_strtod(const char *string, char **endPtr); - -static inline int d6_to_int(int x) -{ - return (x + 32) >> 6; -} -static inline int d16_to_int(int x) -{ - return (x + 32768) >> 16; -} -static inline int int_to_d6(int x) -{ - return x << 6; -} -static inline int int_to_d16(int x) -{ - return x << 16; -} -static inline int d16_to_d6(int x) -{ - return (x + 512) >> 10; -} -static inline int d6_to_d16(int x) -{ - return x << 10; -} -static inline double d6_to_double(int x) -{ - return x / 64.; -} -static inline int double_to_d6(double x) -{ - return (int) (x * 64); -} -static inline double d16_to_double(int x) -{ - return ((double) x) / 0x10000; -} -static inline int double_to_d16(double x) -{ - return (int) (x * 0x10000); -} -static inline double d22_to_double(int x) -{ - return ((double) x) / 0x400000; -} -static inline int double_to_d22(double x) -{ - return (int) (x * 0x400000); -} - -// Calculate cache key for a rotational angle in degrees -static inline int rot_key(double a) -{ - const int m = double_to_d22(360.0); - return double_to_d22(a) % m; -} - -#define FNV1_32A_INIT (unsigned)0x811c9dc5 - -static inline unsigned fnv_32a_buf(void *buf, size_t len, unsigned hval) -{ - unsigned char *bp = buf; - unsigned char *be = bp + len; - while (bp < be) { - hval ^= (unsigned) *bp++; - hval += - (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + - (hval << 24); - } - return hval; -} -static inline unsigned fnv_32a_str(char *str, unsigned hval) -{ - unsigned char *s = (unsigned char *) str; - while (*s) { - hval ^= (unsigned) *s++; - hval += - (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + - (hval << 24); - } - return hval; -} - -#endif /* LIBASS_UTILS_H */ diff --git a/aegisub/libass/config.h b/aegisub/libass/config.h deleted file mode 100644 index d6bb4217e..000000000 --- a/aegisub/libass/config.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifdef _MSC_VER -#define CONFIG_FONTCONFIG 1 -#define CONFIG_ICONV 1 - -#define inline __inline - -#define strtoll(p, e, b) _strtoi64(p, e, b) - -#define M_PI 3.1415926535897932384626433832795 - -#pragma warning(disable : 4996) -#endif diff --git a/aegisub/libass/help_mp.h b/aegisub/libass/help_mp.h deleted file mode 100644 index 4e235f4c3..000000000 --- a/aegisub/libass/help_mp.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef __LIBASS_HELP_MP_H__ -#define __LIBASS_HELP_MP_H__ -#define MSGTR_LIBASS_FT_Glyph_To_BitmapError "[ass] FT_Glyph_To_Bitmap error %d \n" -#define MSGTR_LIBASS_UnsupportedPixelMode "[ass] Unsupported pixel mode: %d\n" -#define MSGTR_LIBASS_GlyphBBoxTooLarge "[ass] Glyph bounding box too large: %dx%dpx\n" -#define MSGTR_LIBASS_NoStyleNamedXFoundUsingY "[ass] [%p] Warning: no style named '%s' found, using '%s'\n" -#define MSGTR_LIBASS_BadTimestamp "[ass] bad timestamp\n" -#define MSGTR_LIBASS_BadEncodedDataSize "[ass] bad encoded data size\n" -#define MSGTR_LIBASS_FontLineTooLong "[ass] Font line too long: %d, %s\n" -#define MSGTR_LIBASS_EventFormatHeaderMissing "[ass] Event format header missing\n" -#define MSGTR_LIBASS_ErrorOpeningIconvDescriptor "[ass] error opening iconv descriptor.\n" -#define MSGTR_LIBASS_ErrorRecodingFile "[ass] error recoding file.\n" -#define MSGTR_LIBASS_FopenFailed "[ass] ass_read_file(%s): fopen failed\n" -#define MSGTR_LIBASS_FseekFailed "[ass] ass_read_file(%s): fseek failed\n" -#define MSGTR_LIBASS_RefusingToLoadSubtitlesLargerThan10M "[ass] ass_read_file(%s): Refusing to load subtitles larger than 10M\n" -#define MSGTR_LIBASS_ReadFailed "Read failed, %d: %s\n" -#define MSGTR_LIBASS_AddedSubtitleFileMemory "[ass] Added subtitle file: (%d styles, %d events)\n" -#define MSGTR_LIBASS_AddedSubtitleFileFname "[ass] Added subtitle file: %s (%d styles, %d events)\n" -#define MSGTR_LIBASS_FailedToCreateDirectory "[ass] Failed to create directory %s\n" -#define MSGTR_LIBASS_NotADirectory "[ass] Not a directory: %s\n" -#define MSGTR_LIBASS_TooManyFonts "[ass] Too many fonts\n" -#define MSGTR_LIBASS_ErrorOpeningFont "[ass] Error opening font: %s, %d\n" -#define MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne "[ass] fontconfig: Selected font is not the requested one: '%s' != '%s'\n" -#define MSGTR_LIBASS_UsingDefaultFontFamily "[ass] fontconfig_select: Using default font family: (%s, %d, %d) -> %s, %d\n" -#define MSGTR_LIBASS_UsingDefaultFont "[ass] fontconfig_select: Using default font: (%s, %d, %d) -> %s, %d\n" -#define MSGTR_LIBASS_UsingArialFontFamily "[ass] fontconfig_select: Using 'Arial' font family: (%s, %d, %d) -> %s, %d\n" -#define MSGTR_LIBASS_FcInitLoadConfigAndFontsFailed "[ass] FcInitLoadConfigAndFonts failed.\n" -#define MSGTR_LIBASS_UpdatingFontCache "[ass] Updating font cache.\n" -#define MSGTR_LIBASS_BetaVersionsOfFontconfigAreNotSupported "[ass] Beta versions of fontconfig are not supported.\n[ass] Update before reporting any bugs.\n" -#define MSGTR_LIBASS_FcStrSetAddFailed "[ass] FcStrSetAdd failed.\n" -#define MSGTR_LIBASS_FcDirScanFailed "[ass] FcDirScan failed.\n" -#define MSGTR_LIBASS_FcDirSave "[ass] FcDirSave failed.\n" -#define MSGTR_LIBASS_FcConfigAppFontAddDirFailed "[ass] FcConfigAppFontAddDir failed\n" -#define MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed "[ass] Fontconfig disabled, only default font will be used.\n" -#define MSGTR_LIBASS_FunctionCallFailed "[ass] %s failed\n" -#define MSGTR_LIBASS_NeitherPlayResXNorPlayResYDefined "[ass] Neither PlayResX nor PlayResY defined. Assuming 384x288.\n" -#define MSGTR_LIBASS_PlayResYUndefinedSettingY "[ass] PlayResY undefined, setting %d.\n" -#define MSGTR_LIBASS_PlayResXUndefinedSettingX "[ass] PlayResX undefined, setting %d.\n" -#define MSGTR_LIBASS_FT_Init_FreeTypeFailed "[ass] FT_Init_FreeType failed.\n" -#define MSGTR_LIBASS_Init "[ass] Init\n" -#define MSGTR_LIBASS_InitFailed "[ass] Init failed.\n" -#define MSGTR_LIBASS_BadCommand "[ass] Bad command: %c%c\n" -#define MSGTR_LIBASS_ErrorLoadingGlyph "[ass] Error loading glyph.\n" -#define MSGTR_LIBASS_FT_Glyph_Stroke_Error "[ass] FT_Glyph_Stroke error %d \n" -#define MSGTR_LIBASS_UnknownEffectType_InternalError "[ass] Unknown effect type (internal error)\n" -#define MSGTR_LIBASS_NoStyleFound "[ass] No style found!\n" -#define MSGTR_LIBASS_EmptyEvent "[ass] Empty event!\n" -#define MSGTR_LIBASS_MAX_GLYPHS_Reached "[ass] MAX_GLYPHS reached: event %d, start = %llu, duration = %llu\n Text = %s\n" -#define MSGTR_LIBASS_EventHeightHasChanged "[ass] Warning! Event height has changed! \n" -#define MSGTR_LIBASS_GlyphNotFoundReselectingFont "[ass] Glyph 0x%X not found, selecting one more font for (%s, %d, %d)\n" -#define MSGTR_LIBASS_GlyphNotFound "[ass] Glyph 0x%X not found in font for (%s, %d, %d)\n" -#define MSGTR_LIBASS_ErrorOpeningMemoryFont "[ass] Error opening memory font: %s\n" -#define MSGTR_LIBASS_NoCharmaps "[ass] font face with no charmaps\n" -#define MSGTR_LIBASS_NoCharmapAutodetected "[ass] no charmap autodetected, trying the first one\n" -#endif diff --git a/aegisub/libass/msvc/enca.h b/aegisub/libass/msvc/enca.h deleted file mode 100644 index cfb5f550c..000000000 --- a/aegisub/libass/msvc/enca.h +++ /dev/null @@ -1,175 +0,0 @@ -/* @(#) $Id: enca.h,v 1.11 2005/02/27 12:08:55 yeti Exp $ */ -/* This header file is in the public domain. */ -#ifndef ENCA_H -#define ENCA_H - -#include -/* According to autoconf stdlib may not be enough for size_t */ -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* Enumerated types */ - -typedef enum { /*< flags >*/ - ENCA_SURFACE_EOL_CR = 1 << 0, - ENCA_SURFACE_EOL_LF = 1 << 1, - ENCA_SURFACE_EOL_CRLF = 1 << 2, - ENCA_SURFACE_EOL_MIX = 1 << 3, - ENCA_SURFACE_EOL_BIN = 1 << 4, - ENCA_SURFACE_MASK_EOL = (ENCA_SURFACE_EOL_CR - | ENCA_SURFACE_EOL_LF - | ENCA_SURFACE_EOL_CRLF - | ENCA_SURFACE_EOL_MIX - | ENCA_SURFACE_EOL_BIN), - ENCA_SURFACE_PERM_21 = 1 << 5, - ENCA_SURFACE_PERM_4321 = 1 << 6, - ENCA_SURFACE_PERM_MIX = 1 << 7, - ENCA_SURFACE_MASK_PERM = (ENCA_SURFACE_PERM_21 - | ENCA_SURFACE_PERM_4321 - | ENCA_SURFACE_PERM_MIX), - ENCA_SURFACE_QP = 1 << 8, - ENCA_SURFACE_REMOVE = 1 << 13, - ENCA_SURFACE_UNKNOWN = 1 << 14, - ENCA_SURFACE_MASK_ALL = (ENCA_SURFACE_MASK_EOL - | ENCA_SURFACE_MASK_PERM - | ENCA_SURFACE_QP - | ENCA_SURFACE_REMOVE) -} EncaSurface; - -typedef enum { - ENCA_NAME_STYLE_ENCA, - ENCA_NAME_STYLE_RFC1345, - ENCA_NAME_STYLE_CSTOCS, - ENCA_NAME_STYLE_ICONV, - ENCA_NAME_STYLE_HUMAN, - ENCA_NAME_STYLE_MIME -} EncaNameStyle; - -typedef enum { /*< flags >*/ - ENCA_CHARSET_7BIT = 1 << 0, - ENCA_CHARSET_8BIT = 1 << 1, - ENCA_CHARSET_16BIT = 1 << 2, - ENCA_CHARSET_32BIT = 1 << 3, - ENCA_CHARSET_FIXED = 1 << 4, - ENCA_CHARSET_VARIABLE = 1 << 5, - ENCA_CHARSET_BINARY = 1 << 6, - ENCA_CHARSET_REGULAR = 1 << 7, - ENCA_CHARSET_MULTIBYTE = 1 << 8 -} EncaCharsetFlags; - -typedef enum { - ENCA_EOK = 0, - ENCA_EINVALUE, - ENCA_EEMPTY, - ENCA_EFILTERED, - ENCA_ENOCS8, - ENCA_ESIGNIF, - ENCA_EWINNER, - ENCA_EGARBAGE -} EncaErrno; - -#define ENCA_CS_UNKNOWN (-1) - -#define ENCA_NOT_A_CHAR 0xffff - -/* Published (opaque) typedefs */ -typedef struct _EncaAnalyserState *EncaAnalyser; - -/* Public (transparent) typedefs */ -typedef struct _EncaEncoding EncaEncoding; - -struct _EncaEncoding { int charset; EncaSurface surface; }; - -/* Basic interface. */ -EncaAnalyser enca_analyser_alloc (const char *langname); -void enca_analyser_free (EncaAnalyser analyser); -EncaEncoding enca_analyse (EncaAnalyser analyser, - unsigned char *buffer, - size_t size); -EncaEncoding enca_analyse_const (EncaAnalyser analyser, - const unsigned char *buffer, - size_t size); -int enca_double_utf8_check (EncaAnalyser analyser, - const unsigned char *buffer, - size_t size); -int* enca_double_utf8_get_candidates (EncaAnalyser analyser); -int enca_errno (EncaAnalyser analyser); -const char* enca_strerror (EncaAnalyser analyser, - int errnum); - -/* Options. */ -void enca_set_multibyte (EncaAnalyser analyser, - int multibyte); -int enca_get_multibyte (EncaAnalyser analyser); -void enca_set_interpreted_surfaces (EncaAnalyser analyser, - int interpreted_surfaces); -int enca_get_interpreted_surfaces (EncaAnalyser analyser); -void enca_set_ambiguity (EncaAnalyser analyser, - int ambiguity); -int enca_get_ambiguity (EncaAnalyser analyser); -void enca_set_filtering (EncaAnalyser analyser, - int filtering); -int enca_get_filtering (EncaAnalyser analyser); -void enca_set_garbage_test (EncaAnalyser analyser, - int garabage_test); -int enca_get_garbage_test (EncaAnalyser analyser); -void enca_set_termination_strictness (EncaAnalyser analyser, - int termination_strictness); -int enca_get_termination_strictness (EncaAnalyser analyser); -int enca_set_significant (EncaAnalyser analyser, - size_t significant); -size_t enca_get_significant (EncaAnalyser analyser); -int enca_set_threshold (EncaAnalyser analyser, - double threshold); -double enca_get_threshold (EncaAnalyser analyser); - -/* Names and properties. */ -const char* enca_charset_name (int charset, - EncaNameStyle whatname); -const char** enca_get_charset_aliases (int charset, - size_t *n); -char* enca_get_surface_name (EncaSurface surface, - EncaNameStyle whatname); -EncaEncoding enca_parse_encoding_name (const char *name); -EncaSurface enca_charset_natural_surface (int charset); -EncaCharsetFlags enca_charset_properties (int charset); - -#define enca_charset_is_known(cs) \ - ((cs) != ENCA_CS_UNKNOWN) -#define enca_charset_is_7bit(cs) \ - (enca_charset_properties(cs) & ENCA_CHARSET_7BIT) -#define enca_charset_is_8bit(cs) \ - (enca_charset_properties(cs) & ENCA_CHARSET_8BIT) -#define enca_charset_is_16bit(cs) \ - (enca_charset_properties(cs) & ENCA_CHARSET_16BIT) -#define enca_charset_is_32bit(cs) \ - (enca_charset_properties(cs) & ENCA_CHARSET_32BIT) -#define enca_charset_is_fixed(cs) \ - (enca_charset_properties(cs) & ENCA_CHARSET_FIXED) -#define enca_charset_is_variable(cs) \ - (enca_charset_properties(cs) & ENCA_CHARSET_VARIABLE) -#define enca_charset_is_binary(cs) \ - (enca_charset_properties(cs) & ENCA_CHARSET_BINARY) -#define enca_charset_is_regular(cs) \ - (enca_charset_properties(cs) & ENCA_CHARSET_REGULAR) -#define enca_charset_is_multibyte(cs) \ - (enca_charset_properties(cs) & ENCA_CHARSET_MULTIBYTE) - -/* Auxiliary functions. */ -int enca_charset_has_ucs2_map (int charset); -int enca_charset_ucs2_map (int charset, - unsigned int *buffer); -size_t enca_number_of_charsets (void); -const char* enca_analyser_language (EncaAnalyser analyser); -const char* enca_language_english_name (const char *lang); -const char** enca_get_languages (size_t *n); -int* enca_get_language_charsets (const char *langname, - size_t *n); -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif diff --git a/aegisub/libass/msvc/inttypes.h b/aegisub/libass/msvc/inttypes.h deleted file mode 100644 index 4b3828a21..000000000 --- a/aegisub/libass/msvc/inttypes.h +++ /dev/null @@ -1,305 +0,0 @@ -// ISO C9x compliant inttypes.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// -// Copyright (c) 2006 Alexander Chemeris -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. The name of the author may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef _MSC_VER // [ -#error "Use this header only with Microsoft Visual C++ compilers!" -#endif // _MSC_VER ] - -#ifndef _MSC_INTTYPES_H_ // [ -#define _MSC_INTTYPES_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif - -#include "stdint.h" - -// 7.8 Format conversion of integer types - -typedef struct { - intmax_t quot; - intmax_t rem; -} imaxdiv_t; - -// 7.8.1 Macros for format specifiers - -#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 - -// The fprintf macros for signed integers are: -#define PRId8 "d" -#define PRIi8 "i" -#define PRIdLEAST8 "d" -#define PRIiLEAST8 "i" -#define PRIdFAST8 "d" -#define PRIiFAST8 "i" - -#define PRId16 "hd" -#define PRIi16 "hi" -#define PRIdLEAST16 "hd" -#define PRIiLEAST16 "hi" -#define PRIdFAST16 "hd" -#define PRIiFAST16 "hi" - -#define PRId32 "I32d" -#define PRIi32 "I32i" -#define PRIdLEAST32 "I32d" -#define PRIiLEAST32 "I32i" -#define PRIdFAST32 "I32d" -#define PRIiFAST32 "I32i" - -#define PRId64 "I64d" -#define PRIi64 "I64i" -#define PRIdLEAST64 "I64d" -#define PRIiLEAST64 "I64i" -#define PRIdFAST64 "I64d" -#define PRIiFAST64 "I64i" - -#define PRIdMAX "I64d" -#define PRIiMAX "I64i" - -#define PRIdPTR "Id" -#define PRIiPTR "Ii" - -// The fprintf macros for unsigned integers are: -#define PRIo8 "o" -#define PRIu8 "u" -#define PRIx8 "x" -#define PRIX8 "X" -#define PRIoLEAST8 "o" -#define PRIuLEAST8 "u" -#define PRIxLEAST8 "x" -#define PRIXLEAST8 "X" -#define PRIoFAST8 "o" -#define PRIuFAST8 "u" -#define PRIxFAST8 "x" -#define PRIXFAST8 "X" - -#define PRIo16 "ho" -#define PRIu16 "hu" -#define PRIx16 "hx" -#define PRIX16 "hX" -#define PRIoLEAST16 "ho" -#define PRIuLEAST16 "hu" -#define PRIxLEAST16 "hx" -#define PRIXLEAST16 "hX" -#define PRIoFAST16 "ho" -#define PRIuFAST16 "hu" -#define PRIxFAST16 "hx" -#define PRIXFAST16 "hX" - -#define PRIo32 "I32o" -#define PRIu32 "I32u" -#define PRIx32 "I32x" -#define PRIX32 "I32X" -#define PRIoLEAST32 "I32o" -#define PRIuLEAST32 "I32u" -#define PRIxLEAST32 "I32x" -#define PRIXLEAST32 "I32X" -#define PRIoFAST32 "I32o" -#define PRIuFAST32 "I32u" -#define PRIxFAST32 "I32x" -#define PRIXFAST32 "I32X" - -#define PRIo64 "I64o" -#define PRIu64 "I64u" -#define PRIx64 "I64x" -#define PRIX64 "I64X" -#define PRIoLEAST64 "I64o" -#define PRIuLEAST64 "I64u" -#define PRIxLEAST64 "I64x" -#define PRIXLEAST64 "I64X" -#define PRIoFAST64 "I64o" -#define PRIuFAST64 "I64u" -#define PRIxFAST64 "I64x" -#define PRIXFAST64 "I64X" - -#define PRIoMAX "I64o" -#define PRIuMAX "I64u" -#define PRIxMAX "I64x" -#define PRIXMAX "I64X" - -#define PRIoPTR "Io" -#define PRIuPTR "Iu" -#define PRIxPTR "Ix" -#define PRIXPTR "IX" - -// The fscanf macros for signed integers are: -#define SCNd8 "d" -#define SCNi8 "i" -#define SCNdLEAST8 "d" -#define SCNiLEAST8 "i" -#define SCNdFAST8 "d" -#define SCNiFAST8 "i" - -#define SCNd16 "hd" -#define SCNi16 "hi" -#define SCNdLEAST16 "hd" -#define SCNiLEAST16 "hi" -#define SCNdFAST16 "hd" -#define SCNiFAST16 "hi" - -#define SCNd32 "ld" -#define SCNi32 "li" -#define SCNdLEAST32 "ld" -#define SCNiLEAST32 "li" -#define SCNdFAST32 "ld" -#define SCNiFAST32 "li" - -#define SCNd64 "I64d" -#define SCNi64 "I64i" -#define SCNdLEAST64 "I64d" -#define SCNiLEAST64 "I64i" -#define SCNdFAST64 "I64d" -#define SCNiFAST64 "I64i" - -#define SCNdMAX "I64d" -#define SCNiMAX "I64i" - -#ifdef _WIN64 // [ -# define SCNdPTR "I64d" -# define SCNiPTR "I64i" -#else // _WIN64 ][ -# define SCNdPTR "ld" -# define SCNiPTR "li" -#endif // _WIN64 ] - -// The fscanf macros for unsigned integers are: -#define SCNo8 "o" -#define SCNu8 "u" -#define SCNx8 "x" -#define SCNX8 "X" -#define SCNoLEAST8 "o" -#define SCNuLEAST8 "u" -#define SCNxLEAST8 "x" -#define SCNXLEAST8 "X" -#define SCNoFAST8 "o" -#define SCNuFAST8 "u" -#define SCNxFAST8 "x" -#define SCNXFAST8 "X" - -#define SCNo16 "ho" -#define SCNu16 "hu" -#define SCNx16 "hx" -#define SCNX16 "hX" -#define SCNoLEAST16 "ho" -#define SCNuLEAST16 "hu" -#define SCNxLEAST16 "hx" -#define SCNXLEAST16 "hX" -#define SCNoFAST16 "ho" -#define SCNuFAST16 "hu" -#define SCNxFAST16 "hx" -#define SCNXFAST16 "hX" - -#define SCNo32 "lo" -#define SCNu32 "lu" -#define SCNx32 "lx" -#define SCNX32 "lX" -#define SCNoLEAST32 "lo" -#define SCNuLEAST32 "lu" -#define SCNxLEAST32 "lx" -#define SCNXLEAST32 "lX" -#define SCNoFAST32 "lo" -#define SCNuFAST32 "lu" -#define SCNxFAST32 "lx" -#define SCNXFAST32 "lX" - -#define SCNo64 "I64o" -#define SCNu64 "I64u" -#define SCNx64 "I64x" -#define SCNX64 "I64X" -#define SCNoLEAST64 "I64o" -#define SCNuLEAST64 "I64u" -#define SCNxLEAST64 "I64x" -#define SCNXLEAST64 "I64X" -#define SCNoFAST64 "I64o" -#define SCNuFAST64 "I64u" -#define SCNxFAST64 "I64x" -#define SCNXFAST64 "I64X" - -#define SCNoMAX "I64o" -#define SCNuMAX "I64u" -#define SCNxMAX "I64x" -#define SCNXMAX "I64X" - -#ifdef _WIN64 // [ -# define SCNoPTR "I64o" -# define SCNuPTR "I64u" -# define SCNxPTR "I64x" -# define SCNXPTR "I64X" -#else // _WIN64 ][ -# define SCNoPTR "lo" -# define SCNuPTR "lu" -# define SCNxPTR "lx" -# define SCNXPTR "lX" -#endif // _WIN64 ] - -#endif // __STDC_FORMAT_MACROS ] - -// 7.8.2 Functions for greatest-width integer types - -// 7.8.2.1 The imaxabs function -#define imaxabs _abs64 - -// 7.8.2.2 The imaxdiv function - -// This is modified version of div() function from Microsoft's div.c found -// in %MSVC.NET%\crt\src\div.c -#ifdef STATIC_IMAXDIV // [ -static -#else // STATIC_IMAXDIV ][ -_inline -#endif // STATIC_IMAXDIV ] -imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) -{ - imaxdiv_t result; - - result.quot = numer / denom; - result.rem = numer % denom; - - if (numer < 0 && result.rem > 0) { - // did division wrong; must fix up - ++result.quot; - result.rem -= denom; - } - - return result; -} - -// 7.8.2.3 The strtoimax and strtoumax functions -#define strtoimax _strtoi64 -#define strtoumax _strtoui64 - -// 7.8.2.4 The wcstoimax and wcstoumax functions -#define wcstoimax _wcstoi64 -#define wcstoumax _wcstoui64 - - -#endif // _MSC_INTTYPES_H_ ] diff --git a/aegisub/libass/msvc/stdint.h b/aegisub/libass/msvc/stdint.h deleted file mode 100644 index d02608a59..000000000 --- a/aegisub/libass/msvc/stdint.h +++ /dev/null @@ -1,247 +0,0 @@ -// ISO C9x compliant stdint.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// -// Copyright (c) 2006-2008 Alexander Chemeris -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. The name of the author may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef _MSC_VER // [ -#error "Use this header only with Microsoft Visual C++ compilers!" -#endif // _MSC_VER ] - -#ifndef _MSC_STDINT_H_ // [ -#define _MSC_STDINT_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif - -#include - -// For Visual Studio 6 in C++ mode and for many Visual Studio versions when -// compiling for ARM we should wrap include with 'extern "C++" {}' -// or compiler give many errors like this: -// error C2733: second C linkage of overloaded function 'wmemchr' not allowed -#ifdef __cplusplus -extern "C" { -#endif -# include -#ifdef __cplusplus -} -#endif - -// Define _W64 macros to mark types changing their size, like intptr_t. -#ifndef _W64 -# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 -# define _W64 __w64 -# else -# define _W64 -# endif -#endif - - -// 7.18.1 Integer types - -// 7.18.1.1 Exact-width integer types - -// Visual Studio 6 and Embedded Visual C++ 4 doesn't -// realize that, e.g. char has the same size as __int8 -// so we give up on __intX for them. -#if (_MSC_VER < 1300) - typedef signed char int8_t; - typedef signed short int16_t; - typedef signed int int32_t; - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned int uint32_t; -#else - typedef signed __int8 int8_t; - typedef signed __int16 int16_t; - typedef signed __int32 int32_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; -#endif -typedef signed __int64 int64_t; -typedef unsigned __int64 uint64_t; - - -// 7.18.1.2 Minimum-width integer types -typedef int8_t int_least8_t; -typedef int16_t int_least16_t; -typedef int32_t int_least32_t; -typedef int64_t int_least64_t; -typedef uint8_t uint_least8_t; -typedef uint16_t uint_least16_t; -typedef uint32_t uint_least32_t; -typedef uint64_t uint_least64_t; - -// 7.18.1.3 Fastest minimum-width integer types -typedef int8_t int_fast8_t; -typedef int16_t int_fast16_t; -typedef int32_t int_fast32_t; -typedef int64_t int_fast64_t; -typedef uint8_t uint_fast8_t; -typedef uint16_t uint_fast16_t; -typedef uint32_t uint_fast32_t; -typedef uint64_t uint_fast64_t; - -// 7.18.1.4 Integer types capable of holding object pointers -#ifdef _WIN64 // [ - typedef signed __int64 intptr_t; - typedef unsigned __int64 uintptr_t; -#else // _WIN64 ][ - typedef _W64 signed int intptr_t; - typedef _W64 unsigned int uintptr_t; -#endif // _WIN64 ] - -// 7.18.1.5 Greatest-width integer types -typedef int64_t intmax_t; -typedef uint64_t uintmax_t; - - -// 7.18.2 Limits of specified-width integer types - -#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 - -// 7.18.2.1 Limits of exact-width integer types -#define INT8_MIN ((int8_t)_I8_MIN) -#define INT8_MAX _I8_MAX -#define INT16_MIN ((int16_t)_I16_MIN) -#define INT16_MAX _I16_MAX -#define INT32_MIN ((int32_t)_I32_MIN) -#define INT32_MAX _I32_MAX -#define INT64_MIN ((int64_t)_I64_MIN) -#define INT64_MAX _I64_MAX -#define UINT8_MAX _UI8_MAX -#define UINT16_MAX _UI16_MAX -#define UINT32_MAX _UI32_MAX -#define UINT64_MAX _UI64_MAX - -// 7.18.2.2 Limits of minimum-width integer types -#define INT_LEAST8_MIN INT8_MIN -#define INT_LEAST8_MAX INT8_MAX -#define INT_LEAST16_MIN INT16_MIN -#define INT_LEAST16_MAX INT16_MAX -#define INT_LEAST32_MIN INT32_MIN -#define INT_LEAST32_MAX INT32_MAX -#define INT_LEAST64_MIN INT64_MIN -#define INT_LEAST64_MAX INT64_MAX -#define UINT_LEAST8_MAX UINT8_MAX -#define UINT_LEAST16_MAX UINT16_MAX -#define UINT_LEAST32_MAX UINT32_MAX -#define UINT_LEAST64_MAX UINT64_MAX - -// 7.18.2.3 Limits of fastest minimum-width integer types -#define INT_FAST8_MIN INT8_MIN -#define INT_FAST8_MAX INT8_MAX -#define INT_FAST16_MIN INT16_MIN -#define INT_FAST16_MAX INT16_MAX -#define INT_FAST32_MIN INT32_MIN -#define INT_FAST32_MAX INT32_MAX -#define INT_FAST64_MIN INT64_MIN -#define INT_FAST64_MAX INT64_MAX -#define UINT_FAST8_MAX UINT8_MAX -#define UINT_FAST16_MAX UINT16_MAX -#define UINT_FAST32_MAX UINT32_MAX -#define UINT_FAST64_MAX UINT64_MAX - -// 7.18.2.4 Limits of integer types capable of holding object pointers -#ifdef _WIN64 // [ -# define INTPTR_MIN INT64_MIN -# define INTPTR_MAX INT64_MAX -# define UINTPTR_MAX UINT64_MAX -#else // _WIN64 ][ -# define INTPTR_MIN INT32_MIN -# define INTPTR_MAX INT32_MAX -# define UINTPTR_MAX UINT32_MAX -#endif // _WIN64 ] - -// 7.18.2.5 Limits of greatest-width integer types -#define INTMAX_MIN INT64_MIN -#define INTMAX_MAX INT64_MAX -#define UINTMAX_MAX UINT64_MAX - -// 7.18.3 Limits of other integer types - -#ifdef _WIN64 // [ -# define PTRDIFF_MIN _I64_MIN -# define PTRDIFF_MAX _I64_MAX -#else // _WIN64 ][ -# define PTRDIFF_MIN _I32_MIN -# define PTRDIFF_MAX _I32_MAX -#endif // _WIN64 ] - -#define SIG_ATOMIC_MIN INT_MIN -#define SIG_ATOMIC_MAX INT_MAX - -#ifndef SIZE_MAX // [ -# ifdef _WIN64 // [ -# define SIZE_MAX _UI64_MAX -# else // _WIN64 ][ -# define SIZE_MAX _UI32_MAX -# endif // _WIN64 ] -#endif // SIZE_MAX ] - -// WCHAR_MIN and WCHAR_MAX are also defined in -#ifndef WCHAR_MIN // [ -# define WCHAR_MIN 0 -#endif // WCHAR_MIN ] -#ifndef WCHAR_MAX // [ -# define WCHAR_MAX _UI16_MAX -#endif // WCHAR_MAX ] - -#define WINT_MIN 0 -#define WINT_MAX _UI16_MAX - -#endif // __STDC_LIMIT_MACROS ] - - -// 7.18.4 Limits of other integer types - -#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 - -// 7.18.4.1 Macros for minimum-width integer constants - -#define INT8_C(val) val##i8 -#define INT16_C(val) val##i16 -#define INT32_C(val) val##i32 -#define INT64_C(val) val##i64 - -#define UINT8_C(val) val##ui8 -#define UINT16_C(val) val##ui16 -#define UINT32_C(val) val##ui32 -#define UINT64_C(val) val##ui64 - -// 7.18.4.2 Macros for greatest-width integer constants -#define INTMAX_C INT64_C -#define UINTMAX_C UINT64_C - -#endif // __STDC_CONSTANT_MACROS ] - - -#endif // _MSC_STDINT_H_ ] diff --git a/aegisub/libass/msvc/strings.h b/aegisub/libass/msvc/strings.h deleted file mode 100644 index 8279fd78d..000000000 --- a/aegisub/libass/msvc/strings.h +++ /dev/null @@ -1,3 +0,0 @@ -#define strncasecmp _strnicmp -#define strcasecmp _stricmp - diff --git a/aegisub/libass/msvc/unistd.h b/aegisub/libass/msvc/unistd.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/aegisub/libass/wscript b/aegisub/libass/wscript deleted file mode 100644 index fb470d90a..000000000 --- a/aegisub/libass/wscript +++ /dev/null @@ -1,26 +0,0 @@ -#! /usr/bin/env python -# encoding: utf-8 -# -# Copyright (c) 2009, Kevin Ollivier -# -# Permission to use, copy, modify, and distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -def build(bld): - obj = bld.new_task_gen( - features = 'cc cstaticlib', - target = 'ass_aegisub', - defines = 'CONFIG_ICONV CONFIG_FONTCONFIG', - includes = '.', - uselib = 'FREETYPE2') - - obj.find_sources_in_dirs('.') diff --git a/aegisub/libffms/Makefile b/aegisub/libffms/Makefile deleted file mode 100644 index 72362a441..000000000 --- a/aegisub/libffms/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -include ../Makefile.inc - -CXXFLAGS = -Icore -Iinclude -D__UNIX__ -DFFMS_EXPORTS -DHAVE_STRLCPY -D__STDC_CONSTANT_MACROS -fPIC -CXXFLAGS += $(CFLAGS_LIBAVFORMAT) $(CFLAGS_LIBAVCODEC) $(CFLAGS_LIBAVUTIL) $(CFLAGS_LIBPOSTPROC) - -LIB = libffmpegsource_aegisub.a - -SRC = \ - src/core/audiosource.cpp \ - src/core/ffms.cpp \ - src/core/indexing.cpp \ - src/core/lavfaudio.cpp \ - src/core/lavfindexer.cpp \ - src/core/lavfvideo.cpp \ - src/core/matroskaaudio.cpp \ - src/core/matroskaindexer.cpp \ - src/core/matroskaparser.c \ - src/core/matroskavideo.cpp \ - src/core/stdiostream.c \ - src/core/utils.cpp \ - src/core/videosource.cpp \ - src/core/wave64writer.cpp - -HEADER = \ - src/core/*.h \ - include/*.h - -include ../Makefile.target --include src/core/*.d - diff --git a/aegisub/libffms/include/ffms.h b/aegisub/libffms/include/ffms.h deleted file mode 100644 index 54df8ccb5..000000000 --- a/aegisub/libffms/include/ffms.h +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#ifndef FFMS_H -#define FFMS_H - -// Version format: major - minor - micro - bump -#define FFMS_VERSION ((2 << 24) | (14 << 16) | (1 << 8) | 1) - -#include - -#ifdef __cplusplus -# define FFMS_EXTERN_C extern "C" -# define FFMS_CLASS_TYPE class -#else -# define FFMS_EXTERN_C -# define FFMS_CLASS_TYPE struct -#endif - -#ifdef _WIN32 -# define FFMS_CC __stdcall -# ifdef _MSC_VER -# ifdef FFMS_EXPORTS -# define FFMS_API(ret) FFMS_EXTERN_C __declspec(dllexport) ret FFMS_CC -# else -# define FFMS_API(ret) FFMS_EXTERN_C __declspec(dllimport) ret FFMS_CC -# endif -# else -# define FFMS_API(ret) FFMS_EXTERN_C ret FFMS_CC -# endif -#else -# define FFMS_CC -# define FFMS_API(ret) FFMS_EXTERN_C ret FFMS_CC -#endif - -typedef struct { - int ErrorType; - int SubType; - int BufferSize; - char *Buffer; -} FFMS_ErrorInfo; - -typedef FFMS_CLASS_TYPE FFMS_VideoSource FFMS_VideoSource; -typedef FFMS_CLASS_TYPE FFMS_AudioSource FFMS_AudioSource; -typedef FFMS_CLASS_TYPE FFMS_Indexer FFMS_Indexer; -typedef FFMS_CLASS_TYPE FFMS_Index FFMS_Index; -typedef FFMS_CLASS_TYPE FFMS_Track FFMS_Track; - -enum FFMS_Errors { - // No error - FFMS_ERROR_SUCCESS = 0, - - // Main types - where the error occurred - FFMS_ERROR_INDEX = 1, - FFMS_ERROR_INDEXING, - FFMS_ERROR_POSTPROCESSING, - FFMS_ERROR_SCALING, - FFMS_ERROR_DECODING, - FFMS_ERROR_SEEKING, - FFMS_ERROR_PARSER, - FFMS_ERROR_TRACK, - FFMS_ERROR_WAVE_WRITER, - FFMS_ERROR_CANCELLED, - - // Subtypes - what caused the error - FFMS_ERROR_UNKNOWN = 20, - FFMS_ERROR_UNSUPPORTED, - FFMS_ERROR_FILE_READ, - FFMS_ERROR_FILE_WRITE, - FFMS_ERROR_NO_FILE, - FFMS_ERROR_VERSION, - FFMS_ERROR_ALLOCATION_FAILED, - FFMS_ERROR_INVALID_ARGUMENT, - FFMS_ERROR_CODEC, - FFMS_ERROR_NOT_AVAILABLE, - FFMS_ERROR_FILE_MISMATCH, - FFMS_ERROR_USER -}; - -enum FFMS_Sources { - FFMS_SOURCE_LAVF = 0x01, - FFMS_SOURCE_MATROSKA = 0x02, - FFMS_SOURCE_HAALIMPEG = 0x04, - FFMS_SOURCE_HAALIOGG = 0x08 -}; - -enum FFMS_CPUFeatures { - FFMS_CPU_CAPS_MMX = 0x01, - FFMS_CPU_CAPS_MMX2 = 0x02, - FFMS_CPU_CAPS_3DNOW = 0x04, - FFMS_CPU_CAPS_ALTIVEC = 0x08, - FFMS_CPU_CAPS_BFIN = 0x10, - FFMS_CPU_CAPS_SSE2 = 0x20 -}; - -enum FFMS_SeekMode { - FFMS_SEEK_LINEAR_NO_RW = -1, - FFMS_SEEK_LINEAR = 0, - FFMS_SEEK_NORMAL = 1, - FFMS_SEEK_UNSAFE = 2, - FFMS_SEEK_AGGRESSIVE = 3 -}; - -enum FFMS_IndexErrorHandling { - FFMS_IEH_ABORT = 0, - FFMS_IEH_CLEAR_TRACK = 1, - FFMS_IEH_STOP_TRACK = 2, - FFMS_IEH_IGNORE = 3 -}; - -enum FFMS_TrackType { - FFMS_TYPE_UNKNOWN = -1, - FFMS_TYPE_VIDEO, - FFMS_TYPE_AUDIO, - FFMS_TYPE_DATA, - FFMS_TYPE_SUBTITLE, - FFMS_TYPE_ATTACHMENT -}; - -enum FFMS_SampleFormat { - FFMS_FMT_U8 = 0, - FFMS_FMT_S16, - FFMS_FMT_S32, - FFMS_FMT_FLT, - FFMS_FMT_DBL -}; - -enum FFMS_AudioChannel { - FFMS_CH_FRONT_LEFT = 0x00000001, - FFMS_CH_FRONT_RIGHT = 0x00000002, - FFMS_CH_FRONT_CENTER = 0x00000004, - FFMS_CH_LOW_FREQUENCY = 0x00000008, - FFMS_CH_BACK_LEFT = 0x00000010, - FFMS_CH_BACK_RIGHT = 0x00000020, - FFMS_CH_FRONT_LEFT_OF_CENTER = 0x00000040, - FFMS_CH_FRONT_RIGHT_OF_CENTER = 0x00000080, - FFMS_CH_BACK_CENTER = 0x00000100, - FFMS_CH_SIDE_LEFT = 0x00000200, - FFMS_CH_SIDE_RIGHT = 0x00000400, - FFMS_CH_TOP_CENTER = 0x00000800, - FFMS_CH_TOP_FRONT_LEFT = 0x00001000, - FFMS_CH_TOP_FRONT_CENTER = 0x00002000, - FFMS_CH_TOP_FRONT_RIGHT = 0x00004000, - FFMS_CH_TOP_BACK_LEFT = 0x00008000, - FFMS_CH_TOP_BACK_CENTER = 0x00010000, - FFMS_CH_TOP_BACK_RIGHT = 0x00020000, - FFMS_CH_STEREO_LEFT = 0x20000000, - FFMS_CH_STEREO_RIGHT = 0x40000000 -}; - -enum FFMS_Resizers { - FFMS_RESIZER_FAST_BILINEAR = 0x0001, - FFMS_RESIZER_BILINEAR = 0x0002, - FFMS_RESIZER_BICUBIC = 0x0004, - FFMS_RESIZER_X = 0x0008, - FFMS_RESIZER_POINT = 0x0010, - FFMS_RESIZER_AREA = 0x0020, - FFMS_RESIZER_BICUBLIN = 0x0040, - FFMS_RESIZER_GAUSS = 0x0080, - FFMS_RESIZER_SINC = 0x0100, - FFMS_RESIZER_LANCZOS = 0x0200, - FFMS_RESIZER_SPLINE = 0x0400 -}; - -enum FFMS_AudioDelayModes { - FFMS_DELAY_NO_SHIFT = -3, - FFMS_DELAY_TIME_ZERO = -2, - FFMS_DELAY_FIRST_VIDEO_TRACK = -1 -}; - -typedef struct { - uint8_t *Data[4]; - int Linesize[4]; - int EncodedWidth; - int EncodedHeight; - int EncodedPixelFormat; - int ScaledWidth; - int ScaledHeight; - int ConvertedPixelFormat; - int KeyFrame; - int RepeatPict; - int InterlacedFrame; - int TopFieldFirst; - char PictType; -} FFMS_Frame; - -typedef struct { - int64_t Num; - int64_t Den; -} FFMS_TrackTimeBase; - -#define FFMS_FRAMEINFO_COMMON int64_t PTS; int RepeatPict; int KeyFrame; - -typedef struct { - FFMS_FRAMEINFO_COMMON -} FFMS_FrameInfo; - -typedef struct { - int FPSDenominator; - int FPSNumerator; - int RFFDenominator; - int RFFNumerator; - int NumFrames; - int SARNum; - int SARDen; - int CropTop; - int CropBottom; - int CropLeft; - int CropRight; - int TopFieldFirst; - int ColorSpace; // same as in the MPEG-2 specs, see AVColorSpace in avcodec.h - int ColorRange; // 0=unspecified, 1=16-235, 2=0-255 - double FirstTime; - double LastTime; -} FFMS_VideoProperties; - -typedef struct { - int SampleFormat; - int SampleRate; - int BitsPerSample; - int Channels; - int64_t ChannelLayout; - int64_t NumSamples; - double FirstTime; - double LastTime; -} FFMS_AudioProperties; - -typedef int (FFMS_CC *TIndexCallback)(int64_t Current, int64_t Total, void *ICPrivate); -typedef int (FFMS_CC *TAudioNameCallback)(const char *SourceFile, int Track, const FFMS_AudioProperties *AP, char *FileName, int FNSize, void *Private); - -// Most functions return 0 on success -// Functions without error message output can be assumed to never fail in a graceful way -FFMS_API(void) FFMS_Init(int CPUFeatures, int UseUTF8Paths); -FFMS_API(int) FFMS_GetLogLevel(); -FFMS_API(void) FFMS_SetLogLevel(int Level); -FFMS_API(FFMS_VideoSource *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, int SeekMode, FFMS_ErrorInfo *ErrorInfo); -FFMS_API(FFMS_AudioSource *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFMS_Index *Index, int DelayMode, FFMS_ErrorInfo *ErrorInfo); -FFMS_API(void) FFMS_DestroyVideoSource(FFMS_VideoSource *V); -FFMS_API(void) FFMS_DestroyAudioSource(FFMS_AudioSource *A); -FFMS_API(const FFMS_VideoProperties *) FFMS_GetVideoProperties(FFMS_VideoSource *V); -FFMS_API(const FFMS_AudioProperties *) FFMS_GetAudioProperties(FFMS_AudioSource *A); -FFMS_API(const FFMS_Frame *) FFMS_GetFrame(FFMS_VideoSource *V, int n, FFMS_ErrorInfo *ErrorInfo); -FFMS_API(const FFMS_Frame *) FFMS_GetFrameByTime(FFMS_VideoSource *V, double Time, FFMS_ErrorInfo *ErrorInfo); -FFMS_API(int) FFMS_GetAudio(FFMS_AudioSource *A, void *Buf, int64_t Start, int64_t Count, FFMS_ErrorInfo *ErrorInfo); -FFMS_API(int) FFMS_SetOutputFormatV(FFMS_VideoSource *V, int64_t TargetFormats, int Width, int Height, int Resizer, FFMS_ErrorInfo *ErrorInfo); -FFMS_API(void) FFMS_ResetOutputFormatV(FFMS_VideoSource *V); -FFMS_API(int) FFMS_SetPP(FFMS_VideoSource *V, const char *PP, FFMS_ErrorInfo *ErrorInfo); -FFMS_API(void) FFMS_ResetPP(FFMS_VideoSource *V); -FFMS_API(void) FFMS_DestroyIndex(FFMS_Index *Index); -FFMS_API(int) FFMS_GetSourceType(FFMS_Index *Index); -FFMS_API(int) FFMS_GetFirstTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo); -FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo); -FFMS_API(int) FFMS_GetNumTracks(FFMS_Index *Index); -FFMS_API(int) FFMS_GetNumTracksI(FFMS_Indexer *Indexer); -FFMS_API(int) FFMS_GetTrackType(FFMS_Track *T); -FFMS_API(int) FFMS_GetTrackTypeI(FFMS_Indexer *Indexer, int Track); -FFMS_API(const char *) FFMS_GetCodecNameI(FFMS_Indexer *Indexer, int Track); -FFMS_API(int) FFMS_GetNumFrames(FFMS_Track *T); -FFMS_API(const FFMS_FrameInfo *) FFMS_GetFrameInfo(FFMS_Track *T, int Frame); -FFMS_API(FFMS_Track *) FFMS_GetTrackFromIndex(FFMS_Index *Index, int Track); -FFMS_API(FFMS_Track *) FFMS_GetTrackFromVideo(FFMS_VideoSource *V); -FFMS_API(FFMS_Track *) FFMS_GetTrackFromAudio(FFMS_AudioSource *A); -FFMS_API(const FFMS_TrackTimeBase *) FFMS_GetTimeBase(FFMS_Track *T); -FFMS_API(int) FFMS_WriteTimecodes(FFMS_Track *T, const char *TimecodeFile, FFMS_ErrorInfo *ErrorInfo); -FFMS_API(FFMS_Index *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, int ErrorHandling, TIndexCallback IC, void *ICPrivate, FFMS_ErrorInfo *ErrorInfo); -FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const FFMS_AudioProperties *AP, char *FileName, int FNSize, void *Private); -FFMS_API(FFMS_Indexer *) FFMS_CreateIndexer(const char *SourceFile, FFMS_ErrorInfo *ErrorInfo); -FFMS_API(FFMS_Index *) FFMS_DoIndexing(FFMS_Indexer *Indexer, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, int ErrorHandling, TIndexCallback IC, void *ICPrivate, FFMS_ErrorInfo *ErrorInfo); -FFMS_API(void) FFMS_CancelIndexing(FFMS_Indexer *Indexer); -FFMS_API(FFMS_Index *) FFMS_ReadIndex(const char *IndexFile, FFMS_ErrorInfo *ErrorInfo); -FFMS_API(int) FFMS_IndexBelongsToFile(FFMS_Index *Index, const char *SourceFile, FFMS_ErrorInfo *ErrorInfo); -FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo); -FFMS_API(int) FFMS_GetPixFmt(const char *Name); -FFMS_API(int) FFMS_GetPresentSources(); -FFMS_API(int) FFMS_GetEnabledSources(); - -#endif diff --git a/aegisub/libffms/include/ffmscompat.h b/aegisub/libffms/include/ffmscompat.h deleted file mode 100644 index 3e21fb396..000000000 --- a/aegisub/libffms/include/ffmscompat.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#ifndef FFMSCOMPAT_H -#define FFMSCOMPAT_H - -#ifdef _WIN32 -# define snprintf _snprintf -# ifdef __MINGW32__ -# define fseeko fseeko64 -# define ftello ftello64 -# else -# define fseeko _fseeki64 -# define ftello _ftelli64 -# endif -#endif - -// Compatibility with older/newer ffmpegs -#ifdef LIBAVFORMAT_VERSION_INT -# if (LIBAVFORMAT_VERSION_INT) < (AV_VERSION_INT(52,34,1)) -# define ff_codec_bmp_tags codec_bmp_tags -# define ff_codec_movvideo_tags codec_movvideo_tags -# define ff_codec_wav_tags codec_wav_tags -# endif -#endif - -#ifdef LIBAVCODEC_VERSION_INT -# if (LIBAVCODEC_VERSION_INT) >= (AV_VERSION_INT(52,29,0)) -# define FFMS_HAVE_FFMPEG_COLORSPACE_INFO -# else -# define AVCOL_RANGE_JPEG 2 -# ifdef _MSC_VER -# pragma message("WARNING: Your FFmpeg is too old to support reporting colorspace and luma range information. The corresponding fields of FFMS_VideoProperties will be set to 0. Please update FFmpeg to get rid of this warning.") -# else -# warning "Your FFmpeg is too old to support reporting colorspace and luma range information. The corresponding fields of FFMS_VideoProperties will be set to 0. Please update FFmpeg to get rid of this warning." -# endif -# endif -# if (LIBAVCODEC_VERSION_INT) < (AV_VERSION_INT(52,30,2)) -# define AV_PKT_FLAG_KEY PKT_FLAG_KEY -# endif -# if (LIBAVCODEC_VERSION_INT) >= (AV_VERSION_INT(52,94,3)) // there are ~3 revisions where this will break but fixing that is :effort: -# undef SampleFormat -# else -# define AVSampleFormat SampleFormat -# define av_get_bits_per_sample_fmt av_get_bits_per_sample_format -# define AV_SAMPLE_FMT_U8 SAMPLE_FMT_U8 -# define AV_SAMPLE_FMT_S16 SAMPLE_FMT_S16 -# define AV_SAMPLE_FMT_S32 SAMPLE_FMT_S32 -# define AV_SAMPLE_FMT_FLT SAMPLE_FMT_FLT -# define AV_SAMPLE_FMT_DBL SAMPLE_FMT_DBL -# endif -#endif - -#ifdef LIBAVUTIL_VERSION_INT -# if (LIBAVUTIL_VERSION_INT) < (AV_VERSION_INT(50, 8, 0)) -# define av_get_pix_fmt avcodec_get_pix_fmt -# endif -#endif - -#ifdef LIBSWSCALE_VERSION_INT -# if (LIBSWSCALE_VERSION_INT) < (AV_VERSION_INT(0,8,0)) -# define FFMS_SWS_CONST_PARAM -# else -# define FFMS_SWS_CONST_PARAM const -# endif -#endif - -#endif // FFMSCOMPAT_H diff --git a/aegisub/libffms/src/core/audiosource.cpp b/aegisub/libffms/src/core/audiosource.cpp deleted file mode 100644 index 136d5b93b..000000000 --- a/aegisub/libffms/src/core/audiosource.cpp +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright (c) 2010 Thomas Goyne -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include "audiosource.h" - -#include -#include - -FFMS_AudioSource::FFMS_AudioSource(const char *SourceFile, FFMS_Index &Index, int Track) -: Delay(0) -, MaxCacheBlocks(50) -, BytesPerSample(0) -, Decoded(0) -, CurrentSample(-1) -, PacketNumber(0) -, CurrentFrame(NULL) -, TrackNumber(Track) -, SeekOffset(0) -, DecodingBuffer(AVCODEC_MAX_AUDIO_FRAME_SIZE * 10) -{ - if (Track < 0 || Track >= static_cast(Index.size())) - throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, - "Out of bounds track index selected"); - - if (Index[Track].TT != FFMS_TYPE_AUDIO) - throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, - "Not an audio track"); - - if (Index[Track].empty()) - throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, - "Audio track contains no audio frames"); - - Frames = Index[Track]; - - if (!Index.CompareFileSignature(SourceFile)) - throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH, - "The index does not match the source file"); -} -void FFMS_AudioSource::Init(FFMS_Index &Index, int DelayMode) { - // The first packet after a seek is often decoded incorrectly, which - // makes it impossible to ever correctly seek back to the beginning, so - // store the first block now - - // In addition, anything with the same PTS as the first packet can't be - // distinguished from the first packet and so can't be seeked to, so - // store those as well - - // Some of LAVF's splitters don't like to seek to the beginning of the - // file (ts and?), so cache a few blocks even if PTSes are unique - // Packet 7 is the last packet I've had be unseekable to, so cache up to - // 10 for a bit of an extra buffer - CacheIterator end = Cache.end(); - while (PacketNumber < Frames.size() && - ((Frames[0].PTS != ffms_av_nopts_value && Frames[PacketNumber].PTS == Frames[0].PTS) || - Cache.size() < 10)) { - - DecodeNextBlock(); - if (Decoded) - CacheBlock(end, CurrentSample, Decoded, &DecodingBuffer[0]); - } - // Store the iterator to the last element of the cache which is used for - // correctness rather than speed, so that when looking for one to delete - // we know how much to skip - CacheNoDelete = Cache.end(); - --CacheNoDelete; - - // Read properties of the audio which may not be available until the first - // frame has been decoded - FillAP(AP, CodecContext, Frames); - - if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) - throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, - "Codec returned zero size audio"); - - if (DelayMode < FFMS_DELAY_NO_SHIFT) - throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, - "Bad audio delay compensation mode"); - - if (DelayMode == FFMS_DELAY_NO_SHIFT) return; - - if (DelayMode > (signed)Index.size()) - throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, - "Out of bounds track index selected for audio delay compensation"); - - if (DelayMode >= 0 && Index[DelayMode].TT != FFMS_TYPE_VIDEO) - throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, - "Audio delay compensation must be relative to a video track"); - - double AdjustRelative = 0; - if (DelayMode != FFMS_DELAY_TIME_ZERO) { - if (DelayMode == FFMS_DELAY_FIRST_VIDEO_TRACK) { - for (size_t i = 0; i < Index.size(); ++i) { - if (Index[i].TT == FFMS_TYPE_VIDEO) { - DelayMode = i; - break; - } - } - } - - if (DelayMode >= 0) { - const FFMS_Track &VTrack = Index[DelayMode]; - AdjustRelative = VTrack[0].PTS * VTrack.TB.Num / (double)VTrack.TB.Den / 1000.; - } - } - - Delay = static_cast((AdjustRelative - Frames[0].PTS) * AP.SampleRate + .5); - AP.NumSamples -= Delay; -} - -void FFMS_AudioSource::CacheBlock(CacheIterator &pos, int64_t Start, size_t Samples, uint8_t *SrcData) { - Cache.insert(pos, AudioBlock(Start, Samples, SrcData, Samples * BytesPerSample)); - - if (Cache.size() >= MaxCacheBlocks) { - // Kill the oldest one - CacheIterator min = CacheNoDelete; - // Never drop the first one as the first packet decoded after a seek - // is often decoded incorrectly and we can't seek to before the first one - ++min; - for (CacheIterator it = min; it != Cache.end(); ++it) - if (it->Age < min->Age) min = it; - if (min == pos) ++pos; - Cache.erase(min); - } -} - -void FFMS_AudioSource::DecodeNextBlock() { - if (BytesPerSample == 0) BytesPerSample = (av_get_bits_per_sample_fmt(CodecContext->sample_fmt) * CodecContext->channels) / 8; - - CurrentFrame = &Frames[PacketNumber]; - - AVPacket Packet; - if (!ReadPacket(&Packet)) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_UNKNOWN, "ReadPacket unexpectedly failed to read a packet"); - - // ReadPacket may have changed the packet number - CurrentFrame = &Frames[PacketNumber]; - CurrentSample = CurrentFrame->SampleStart; - ++PacketNumber; - - uint8_t *Buf = &DecodingBuffer[0]; - uint8_t *Data = Packet.data; - while (Packet.size > 0) { - int TempOutputBufSize = AVCODEC_MAX_AUDIO_FRAME_SIZE * 10 - (Buf - &DecodingBuffer[0]); - int Ret = avcodec_decode_audio3(CodecContext, (int16_t *)Buf, &TempOutputBufSize, &Packet); - - // Should only ever happen if the user chose to ignore decoding errors - // during indexing, so continue to just ignore decoding errors - if (Ret < 0) break; - - if (Ret > 0) { - Packet.size -= Ret; - Packet.data += Ret; - Buf += TempOutputBufSize; - } - } - Packet.data = Data; - FreePacket(&Packet); - - Decoded = (Buf - &DecodingBuffer[0]) / BytesPerSample; - if (Decoded == 0) { - // zero sample packets aren't included in the index so we didn't - // actually move to the next packet - --PacketNumber; - } -} - -static bool SampleStartComp(const TFrameInfo &a, const TFrameInfo &b) { - return a.SampleStart < b.SampleStart; -} - -void FFMS_AudioSource::GetAudio(void *Buf, int64_t Start, int64_t Count) { - if (Start < 0 || Start + Count > AP.NumSamples || Count < 0) - throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_INVALID_ARGUMENT, - "Out of bounds audio samples requested"); - - uint8_t *Dst = static_cast(Buf); - - // Apply audio delay (if any) and fill any samples before the start time with zero - Start -= Delay; - if (Start < 0) { - size_t Bytes = static_cast(BytesPerSample * FFMIN(-Start, Count)); - memset(Dst, 0, Bytes); - - Count += Start; - // Entire request was before the start of the audio - if (Count <= 0) return; - - Start = 0; - Dst += Bytes; - } - - CacheIterator it = Cache.begin(); - - while (Count > 0) { - // Find first useful cache block - while (it != Cache.end() && it->Start + it->Samples <= Start) ++it; - - // Cache has the next block we want - if (it != Cache.end() && it->Start <= Start) { - int64_t SrcOffset = FFMAX(0, Start - it->Start); - int64_t DstOffset = FFMAX(0, it->Start - Start); - int64_t CopySamples = FFMIN(it->Samples - SrcOffset, Count - DstOffset); - size_t Bytes = static_cast(CopySamples * BytesPerSample); - - memcpy(Dst + DstOffset * BytesPerSample, &it->Data[SrcOffset * BytesPerSample], Bytes); - Start += CopySamples; - Count -= CopySamples; - Dst += Bytes; - ++it; - } - // Decode another block - else { - if (Start < CurrentSample && SeekOffset == -1) - throw FFMS_Exception(FFMS_ERROR_SEEKING, FFMS_ERROR_CODEC, "Audio stream is not seekable"); - - if (SeekOffset >= 0 && (Start < CurrentSample || Start > CurrentSample + Decoded * 5)) { - TFrameInfo f; - f.SampleStart = Start; - int NewPacketNumber = std::distance(Frames.begin(), std::lower_bound(Frames.begin(), Frames.end(), f, SampleStartComp)); - NewPacketNumber = FFMAX(0, NewPacketNumber - SeekOffset - 15); - while (NewPacketNumber > 0 && !Frames[NewPacketNumber].KeyFrame) --NewPacketNumber; - - // Only seek forward if it'll actually result in moving forward - if (Start < CurrentSample || static_cast(NewPacketNumber) > PacketNumber) { - PacketNumber = NewPacketNumber; - Decoded = 0; - CurrentSample = -1; - avcodec_flush_buffers(CodecContext); - Seek(); - } - } - - // Decode everything between the last keyframe and the block we want - while (CurrentSample + Decoded <= Start) DecodeNextBlock(); - if (CurrentSample > Start) - throw FFMS_Exception(FFMS_ERROR_SEEKING, FFMS_ERROR_CODEC, "Seeking is severely broken"); - - CacheBlock(it, CurrentSample, Decoded, &DecodingBuffer[0]); - - size_t FirstSample = static_cast(Start - CurrentSample); - size_t Samples = static_cast(Decoded - FirstSample); - size_t Bytes = FFMIN(Samples, static_cast(Count)) * BytesPerSample; - - memcpy(Dst, &DecodingBuffer[FirstSample * BytesPerSample], Bytes); - - Start += Samples; - Count -= Samples; - Dst += Bytes; - } - } -} - -size_t GetSeekablePacketNumber(FFMS_Track const& Frames, size_t PacketNumber) { - // Packets don't always have unique PTSes, so we may not be able to - // uniquely identify the packet we want. This function attempts to find - // a PTS we can seek to which will let us figure out which packet we're - // on before we get to the packet we actually wanted - - // MatroskaAudioSource doesn't need this, as it seeks by byte offset - // rather than PTS. LAVF theoretically can seek by byte offset, but we - // don't use it as not all demuxers support it and it's broken in some of - // those that claim to support it - - // However much we might wish to, we can't seek to before packet zero - if (PacketNumber == 0) return PacketNumber; - - // Desired packet's PTS is unique, so don't do anything - if (Frames[PacketNumber].PTS != Frames[PacketNumber - 1].PTS && - (PacketNumber + 1 == Frames.size() || Frames[PacketNumber].PTS != Frames[PacketNumber + 1].PTS)) - return PacketNumber; - - // When decoding, we only reliably know what packet we're at when the - // newly parsed packet has a different PTS from the previous one. As such, - // we walk backwards until we hit a different PTS and then seek to there, - // so that we can then decode until we hit the PTS group we actually wanted - // (and thereby know that we're at the first packet in the group rather - // than whatever the splitter happened to choose) - - // This doesn't work if our desired packet has the same PTS as the first - // packet, but this scenario should never come up anyway; we permanently - // cache the decoded results from those packets, so there's no need to ever - // seek to them - int64_t PTS = Frames[PacketNumber].PTS; - while (PacketNumber > 0 && PTS == Frames[PacketNumber].PTS) - --PacketNumber; - return PacketNumber; -} diff --git a/aegisub/libffms/src/core/audiosource.h b/aegisub/libffms/src/core/audiosource.h deleted file mode 100644 index 3d2d8806c..000000000 --- a/aegisub/libffms/src/core/audiosource.h +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#ifndef FFAUDIOSOURCE_H -#define FFAUDIOSOURCE_H - -extern "C" { -#include -#include -} - -#include -#include -#include -#include "indexing.h" -#include "utils.h" -#include "ffms.h" - -#ifdef HAALISOURCE -# define WIN32_LEAN_AND_MEAN -# define _WIN32_DCOM -# include -# include -# include -# include -# include -# include "CoParser.h" -# include "guids.h" -#endif - -class FFMS_AudioSource { - struct AudioBlock { - int64_t Age; - int64_t Start; - int64_t Samples; - std::vector Data; - - AudioBlock(int64_t Start, int64_t Samples, uint8_t *SrcData, size_t SrcBytes) - : Start(Start) - , Samples(Samples) - , Data(SrcData, SrcData + SrcBytes) - { - static int64_t Now = 0; - Age = Now++; - } - }; - typedef std::list::iterator CacheIterator; - - // delay in samples to apply to the audio - int64_t Delay; - // cache of decoded audio blocks - std::list Cache; - // max size of the cache in blocks - size_t MaxCacheBlocks; - // pointer to last element of the cache which should never be deleted - CacheIterator CacheNoDelete; - // bytes per sample * number of channels - size_t BytesPerSample; - // Number of samples stored in the decoding buffer - size_t Decoded; - - // Insert a block into the cache - void CacheBlock(CacheIterator &pos, int64_t Start, size_t Samples, uint8_t *SrcData); - - // Called after seeking - virtual void Seek() { }; - // Read the next packet from the file - virtual bool ReadPacket(AVPacket *) = 0; - virtual void FreePacket(AVPacket *) { } -protected: - // First sample which is stored in the decoding buffer - int64_t CurrentSample; - // Next packet to be read - size_t PacketNumber; - // Current audio frame - TFrameInfo *CurrentFrame; - // Track which this corresponds to - int TrackNumber; - // Number of packets which the demuxer requires to know where it is - // If -1, seeking is assumed to be impossible - int SeekOffset; - - // Buffer which audio is decoded into - AlignedBuffer DecodingBuffer; - FFMS_Track Frames; - FFCodecContext CodecContext; - FFMS_AudioProperties AP; - - void DecodeNextBlock(); - // Initialization which has to be done after the codec is opened - void Init(FFMS_Index &Index, int DelayMode); - - FFMS_AudioSource(const char *SourceFile, FFMS_Index &Index, int Track); - -public: - virtual ~FFMS_AudioSource() { }; - FFMS_Track *GetTrack() { return &Frames; } - const FFMS_AudioProperties& GetAudioProperties() const { return AP; } - void GetAudio(void *Buf, int64_t Start, int64_t Count); -}; - -class FFLAVFAudio : public FFMS_AudioSource { - AVFormatContext *FormatContext; - - bool ReadPacket(AVPacket *); - void FreePacket(AVPacket *); - void Seek(); - -public: - FFLAVFAudio(const char *SourceFile, int Track, FFMS_Index &Index, int DelayMode); - ~FFLAVFAudio(); -}; - -class FFMatroskaAudio : public FFMS_AudioSource { - MatroskaFile *MF; - MatroskaReaderContext MC; - TrackInfo *TI; - std::auto_ptr TCC; - char ErrorMessage[256]; - - bool ReadPacket(AVPacket *); - -public: - FFMatroskaAudio(const char *SourceFile, int Track, FFMS_Index &Index, int DelayMode); - ~FFMatroskaAudio(); -}; - -#ifdef HAALISOURCE - -class FFHaaliAudio : public FFMS_AudioSource { - CComPtr pMMC; - CComPtr pMMF; - - bool ReadPacket(AVPacket *); - void Seek(); - -public: - FFHaaliAudio(const char *SourceFile, int Track, FFMS_Index &Index, enum FFMS_Sources SourceMode, int DelayMode); -}; - -#endif // HAALISOURCE - -size_t GetSeekablePacketNumber(FFMS_Track const& Frames, size_t PacketNumber); - -#endif diff --git a/aegisub/libffms/src/core/config.h b/aegisub/libffms/src/core/config.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/aegisub/libffms/src/core/ffms.cpp b/aegisub/libffms/src/core/ffms.cpp deleted file mode 100644 index 57a49b2aa..000000000 --- a/aegisub/libffms/src/core/ffms.cpp +++ /dev/null @@ -1,469 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include -#include -#include "ffms.h" -#include "videosource.h" -#include "audiosource.h" -#include "indexing.h" - -extern "C" { -#include -} - - -#ifdef FFMS_WIN_DEBUG -# include -#endif - -static bool FFmpegInited = false; -bool HasHaaliMPEG = false; -bool HasHaaliOGG = false; -int CPUFeatures = 0; -bool GlobalUseUTF8Paths = false; - - -#ifdef FFMS_WIN_DEBUG - -extern "C" int av_log_level; - -void av_log_windebug_callback(void* ptr, int level, const char* fmt, va_list vl) { - static int print_prefix=1; - static int count; - static char line[1024] = {0}, prev[1024] = {0}; - AVClass* avc = ptr ? *(AVClass**)ptr : NULL; - if(level > av_log_level) - return; - - int written = 0; - if(print_prefix && avc) { - written = snprintf(line, sizeof(line), "[%s @ %p]", avc->item_name(ptr), ptr); - } - - written += vsnprintf(line + written, sizeof(line) - written, fmt, vl); - - print_prefix = line[written-1] == '\n'; - line[sizeof(line) - 1] = 0; - if(print_prefix && !strcmp(line, prev)){ - count++; - return; - } - if(count > 0){ - std::stringstream ss; - ss << " Last message repeated " << count << " times\n"; - OutputDebugStringA(ss.str().c_str()); - count = 0; - } - OutputDebugStringA(line); - strcpy(prev, line); -} - -#endif - -FFMS_API(void) FFMS_Init(int CPUFeatures, int UseUTF8Paths) { - if (!FFmpegInited) { - av_register_all(); -#ifdef _WIN32 - if (UseUTF8Paths) { - ffms_patch_lavf_file_open(); - GlobalUseUTF8Paths = true; - } - else { - GlobalUseUTF8Paths = false; - } -#else - GlobalUseUTF8Paths = false; -#endif -#ifdef FFMS_WIN_DEBUG - av_log_set_callback(av_log_windebug_callback); - av_log_set_level(AV_LOG_INFO); -#else - av_log_set_level(AV_LOG_QUIET); -#endif - ::CPUFeatures = CPUFeatures; -#ifdef HAALISOURCE - CComPtr pMMC; - HasHaaliMPEG = !FAILED(pMMC.CoCreateInstance(HAALI_MPEG_PARSER)); - pMMC = NULL; - HasHaaliOGG = !FAILED(pMMC.CoCreateInstance(HAALI_OGG_PARSER)); - pMMC = NULL; -#endif - FFmpegInited = true; - } -} - -FFMS_API(int) FFMS_GetLogLevel() { - return av_log_get_level(); -} - -FFMS_API(void) FFMS_SetLogLevel(int Level) { - av_log_set_level(Level); -} - -FFMS_API(FFMS_VideoSource *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, int SeekMode, FFMS_ErrorInfo *ErrorInfo) { - if (Threads < 1) - Threads = 1; - try { - switch (Index->Decoder) { - case FFMS_SOURCE_LAVF: - return new FFLAVFVideo(SourceFile, Track, Index, Threads, SeekMode); - case FFMS_SOURCE_MATROSKA: - return new FFMatroskaVideo(SourceFile, Track, Index, Threads); -#ifdef HAALISOURCE - case FFMS_SOURCE_HAALIMPEG: - if (HasHaaliMPEG) - return new FFHaaliVideo(SourceFile, Track, Index, Threads, FFMS_SOURCE_HAALIMPEG); - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali MPEG/TS source unavailable"); - case FFMS_SOURCE_HAALIOGG: - if (HasHaaliOGG) - return new FFHaaliVideo(SourceFile, Track, Index, Threads, FFMS_SOURCE_HAALIOGG); - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali OGG/OGM source unavailable"); -#endif - default: - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Unsupported format"); - } - } catch (FFMS_Exception &e) { - e.CopyOut(ErrorInfo); - return NULL; - } -} - -FFMS_API(FFMS_AudioSource *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFMS_Index *Index, int DelayMode, FFMS_ErrorInfo *ErrorInfo) { - try { - switch (Index->Decoder) { - case FFMS_SOURCE_LAVF: - return new FFLAVFAudio(SourceFile, Track, *Index, DelayMode); - case FFMS_SOURCE_MATROSKA: - return new FFMatroskaAudio(SourceFile, Track, *Index, DelayMode); -#ifdef HAALISOURCE - case FFMS_SOURCE_HAALIMPEG: - if (HasHaaliMPEG) - return new FFHaaliAudio(SourceFile, Track, *Index, FFMS_SOURCE_HAALIMPEG, DelayMode); - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali MPEG/TS source unavailable"); - case FFMS_SOURCE_HAALIOGG: - if (HasHaaliOGG) - return new FFHaaliAudio(SourceFile, Track, *Index, FFMS_SOURCE_HAALIOGG, DelayMode); - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali OGG/OGM source unavailable"); -#endif - default: - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Unsupported format"); - } - } catch (FFMS_Exception &e) { - e.CopyOut(ErrorInfo); - return NULL; - } -} - -FFMS_API(void) FFMS_DestroyVideoSource(FFMS_VideoSource *V) { - delete V; -} - -FFMS_API(void) FFMS_DestroyAudioSource(FFMS_AudioSource *A) { - delete A; -} - -FFMS_API(const FFMS_VideoProperties *) FFMS_GetVideoProperties(FFMS_VideoSource *V) { - return &V->GetVideoProperties(); -} - -FFMS_API(const FFMS_AudioProperties *) FFMS_GetAudioProperties(FFMS_AudioSource *A) { - return &A->GetAudioProperties(); -} - -FFMS_API(const FFMS_Frame *) FFMS_GetFrame(FFMS_VideoSource *V, int n, FFMS_ErrorInfo *ErrorInfo) { - ClearErrorInfo(ErrorInfo); - try { - return V->GetFrame(n); - } catch (FFMS_Exception &e) { - e.CopyOut(ErrorInfo); - return NULL; - } -} - -FFMS_API(const FFMS_Frame *) FFMS_GetFrameByTime(FFMS_VideoSource *V, double Time, FFMS_ErrorInfo *ErrorInfo) { - ClearErrorInfo(ErrorInfo); - try { - return (FFMS_Frame *)V->GetFrameByTime(Time); - } catch (FFMS_Exception &e) { - e.CopyOut(ErrorInfo); - return NULL; - } -} - -FFMS_API(int) FFMS_GetAudio(FFMS_AudioSource *A, void *Buf, int64_t Start, int64_t Count, FFMS_ErrorInfo *ErrorInfo) { - ClearErrorInfo(ErrorInfo); - try { - A->GetAudio(Buf, Start, Count); - } catch (FFMS_Exception &e) { - return e.CopyOut(ErrorInfo); - } - return FFMS_ERROR_SUCCESS; -} - -FFMS_API(int) FFMS_SetOutputFormatV(FFMS_VideoSource *V, int64_t TargetFormats, int Width, int Height, int Resizer, FFMS_ErrorInfo *ErrorInfo) { - ClearErrorInfo(ErrorInfo); - try { - V->SetOutputFormat(TargetFormats, Width, Height, Resizer); - } catch (FFMS_Exception &e) { - return e.CopyOut(ErrorInfo); - } - return FFMS_ERROR_SUCCESS; -} - -FFMS_API(void) FFMS_ResetOutputFormatV(FFMS_VideoSource *V) { - V->ResetOutputFormat(); -} - -FFMS_API(int) FFMS_SetPP(FFMS_VideoSource *V, const char *PP, FFMS_ErrorInfo *ErrorInfo) { - ClearErrorInfo(ErrorInfo); - try { - V->SetPP(PP); - } catch (FFMS_Exception &e) { - return e.CopyOut(ErrorInfo); - } - return FFMS_ERROR_SUCCESS; -} - -FFMS_API(void) FFMS_ResetPP(FFMS_VideoSource *V) { - V->ResetPP(); -} - -FFMS_API(void) FFMS_DestroyIndex(FFMS_Index *Index) { - delete Index; -} - -FFMS_API(int) FFMS_GetSourceType(FFMS_Index *Index) { - return Index->Decoder; -} - -FFMS_API(int) FFMS_GetFirstTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo) { - ClearErrorInfo(ErrorInfo); - for (int i = 0; i < static_cast(Index->size()); i++) - if ((*Index)[i].TT == TrackType) - return i; - - try { - throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_NOT_AVAILABLE, - "No suitable, indexed track found"); - } catch (FFMS_Exception &e) { - e.CopyOut(ErrorInfo); - return -1; - } -} - -FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo) { - ClearErrorInfo(ErrorInfo); - for (int i = 0; i < static_cast(Index->size()); i++) - if ((*Index)[i].TT == TrackType && (*Index)[i].size() > 0) - return i; - try { - throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_NOT_AVAILABLE, - "No suitable, indexed track found"); - } catch (FFMS_Exception &e) { - e.CopyOut(ErrorInfo); - return -1; - } -} - -FFMS_API(int) FFMS_GetNumTracks(FFMS_Index *Index) { - return Index->size(); -} - -FFMS_API(int) FFMS_GetNumTracksI(FFMS_Indexer *Indexer) { - return Indexer->GetNumberOfTracks(); -} - -FFMS_API(int) FFMS_GetTrackType(FFMS_Track *T) { - return T->TT; -} - -FFMS_API(int) FFMS_GetTrackTypeI(FFMS_Indexer *Indexer, int Track) { - return Indexer->GetTrackType(Track); -} - -FFMS_API(const char *) FFMS_GetCodecNameI(FFMS_Indexer *Indexer, int Track) { - return Indexer->GetTrackCodec(Track); -} - -FFMS_API(int) FFMS_GetNumFrames(FFMS_Track *T) { - return T->size(); -} - -FFMS_API(const FFMS_FrameInfo *) FFMS_GetFrameInfo(FFMS_Track *T, int Frame) { - return reinterpret_cast(&(*T)[Frame]); -} - -FFMS_API(FFMS_Track *) FFMS_GetTrackFromIndex(FFMS_Index *Index, int Track) { - return &(*Index)[Track]; -} - -FFMS_API(FFMS_Track *) FFMS_GetTrackFromVideo(FFMS_VideoSource *V) { - return V->GetTrack(); -} - -FFMS_API(FFMS_Track *) FFMS_GetTrackFromAudio(FFMS_AudioSource *A) { - return A->GetTrack(); -} - -FFMS_API(const FFMS_TrackTimeBase *) FFMS_GetTimeBase(FFMS_Track *T) { - return &T->TB; -} - -FFMS_API(int) FFMS_WriteTimecodes(FFMS_Track *T, const char *TimecodeFile, FFMS_ErrorInfo *ErrorInfo) { - ClearErrorInfo(ErrorInfo); - try { - T->WriteTimecodes(TimecodeFile); - } catch (FFMS_Exception &e) { - return e.CopyOut(ErrorInfo); - } - return FFMS_ERROR_SUCCESS; -} - -FFMS_API(FFMS_Index *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, int ErrorHandling, TIndexCallback IC, void *ICPrivate, FFMS_ErrorInfo *ErrorInfo) { - FFMS_Indexer *Indexer = FFMS_CreateIndexer(SourceFile, ErrorInfo); - if (!Indexer) - return NULL; - return FFMS_DoIndexing(Indexer, IndexMask, DumpMask, ANC, ANCPrivate, ErrorHandling, IC, ICPrivate, ErrorInfo); -} - -/* Used by FFMS_DefaultAudioFilename */ - -static std::string IntToStr(int i, int zp = 0) { - std::stringstream s; - s.fill('0'); - s.width(zp); - s << i; - return s.str(); -} - -static void ReplaceString(std::string &s, std::string from, std::string to) { - int idx; - while ((idx = s.find(from)) != std::string::npos) - s.replace(idx, from.length(), to); -} - -FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const FFMS_AudioProperties *AP, char *FileName, int FNSize, void *Private) { - std::string s = static_cast(Private); - - ReplaceString(s, "%sourcefile%", SourceFile); - ReplaceString(s, "%trackn%", IntToStr(Track)); - ReplaceString(s, "%trackzn%", IntToStr(Track, 2)); - ReplaceString(s, "%samplerate%", IntToStr(AP->SampleRate)); - ReplaceString(s, "%channels%", IntToStr(AP->Channels)); - ReplaceString(s, "%bps%", IntToStr(AP->BitsPerSample)); - ReplaceString(s, "%delay%", IntToStr(static_cast(AP->FirstTime))); - - if (FileName != NULL) - strcpy(FileName, s.c_str()); - - return s.length() + 1; -} - -FFMS_API(FFMS_Indexer *) FFMS_CreateIndexer(const char *SourceFile, FFMS_ErrorInfo *ErrorInfo) { - ClearErrorInfo(ErrorInfo); - try { - return FFMS_Indexer::CreateIndexer(SourceFile); - } catch (FFMS_Exception &e) { - e.CopyOut(ErrorInfo); - return NULL; - } -} - -FFMS_API(FFMS_Index *) FFMS_DoIndexing(FFMS_Indexer *Indexer, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, int ErrorHandling, TIndexCallback IC, void *ICPrivate, FFMS_ErrorInfo *ErrorInfo) { - ClearErrorInfo(ErrorInfo); - - Indexer->SetIndexMask(IndexMask | DumpMask); - Indexer->SetDumpMask(DumpMask); - Indexer->SetErrorHandling(ErrorHandling); - Indexer->SetProgressCallback(IC, ICPrivate); - Indexer->SetAudioNameCallback(ANC, ANCPrivate); - - FFMS_Index *Index = NULL; - try { - Index = Indexer->DoIndexing(); - } catch (FFMS_Exception &e) { - e.CopyOut(ErrorInfo); - } - delete Indexer; - return Index; -} - -FFMS_API(void) FFMS_CancelIndexing(FFMS_Indexer *Indexer) { - delete Indexer; -} - -FFMS_API(FFMS_Index *) FFMS_ReadIndex(const char *IndexFile, FFMS_ErrorInfo *ErrorInfo) { - ClearErrorInfo(ErrorInfo); - FFMS_Index *Index = new FFMS_Index(); - try { - Index->ReadIndex(IndexFile); - } catch (FFMS_Exception &e) { - delete Index; - e.CopyOut(ErrorInfo); - return NULL; - } - return Index; -} - -FFMS_API(int) FFMS_IndexBelongsToFile(FFMS_Index *Index, const char *SourceFile, FFMS_ErrorInfo *ErrorInfo) { - ClearErrorInfo(ErrorInfo); - try { - if (!Index->CompareFileSignature(SourceFile)) - throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH, - "The index does not belong to the file"); - } catch (FFMS_Exception &e) { - return e.CopyOut(ErrorInfo); - } - return FFMS_ERROR_SUCCESS; -} - -FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo) { - ClearErrorInfo(ErrorInfo); - try { - Index->WriteIndex(IndexFile); - } catch (FFMS_Exception &e) { - return e.CopyOut(ErrorInfo); - } - return FFMS_ERROR_SUCCESS; -} - -FFMS_API(int) FFMS_GetPixFmt(const char *Name) { - return av_get_pix_fmt(Name); -} - -FFMS_API(int) FFMS_GetPresentSources() { - int Sources = FFMS_SOURCE_LAVF | FFMS_SOURCE_MATROSKA; -#ifdef HAALISOURCE - Sources |= FFMS_SOURCE_HAALIMPEG | FFMS_SOURCE_HAALIOGG; -#endif - return Sources; -} - -FFMS_API(int) FFMS_GetEnabledSources() { - if (!FFmpegInited) - return 0; - int Sources = FFMS_SOURCE_LAVF | FFMS_SOURCE_MATROSKA; - if (HasHaaliMPEG) - Sources |= FFMS_SOURCE_HAALIMPEG; - if (HasHaaliOGG) - Sources |= FFMS_SOURCE_HAALIOGG; - return Sources; -} diff --git a/aegisub/libffms/src/core/indexing.cpp b/aegisub/libffms/src/core/indexing.cpp deleted file mode 100644 index 150bf4464..000000000 --- a/aegisub/libffms/src/core/indexing.cpp +++ /dev/null @@ -1,603 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include "indexing.h" - -#include -#include -#include -#include - - -extern "C" { -#include -#include -} - -#undef max - -#define INDEXID 0x53920873 - -extern bool HasHaaliMPEG; -extern bool HasHaaliOGG; - -#ifndef FFMS_USE_POSTPROC -unsigned postproc_version() { return 0; } // ugly workaround to avoid lots of ifdeffing -#endif // FFMS_USE_POSTPROC - -struct IndexHeader { - uint32_t Id; - uint32_t Version; - uint32_t Tracks; - uint32_t Decoder; - uint32_t LAVUVersion; - uint32_t LAVFVersion; - uint32_t LAVCVersion; - uint32_t LSWSVersion; - uint32_t LPPVersion; - int64_t FileSize; - uint8_t FileSignature[20]; -}; - -struct TrackHeader { - uint32_t TT; - uint32_t Frames; - int64_t Num; - int64_t Den; - uint32_t UseDTS; -}; - - -SharedVideoContext::SharedVideoContext(bool FreeCodecContext) { - CodecContext = NULL; - Parser = NULL; - this->FreeCodecContext = FreeCodecContext; - TCC = NULL; -} - -SharedVideoContext::~SharedVideoContext() { - if (CodecContext) { - avcodec_close(CodecContext); - if (FreeCodecContext) - av_freep(&CodecContext); - } - av_parser_close(Parser); - delete TCC; -} - -SharedAudioContext::SharedAudioContext(bool FreeCodecContext) { - W64Writer = NULL; - CodecContext = NULL; - CurrentSample = 0; - TCC = NULL; - this->FreeCodecContext = FreeCodecContext; -} - -SharedAudioContext::~SharedAudioContext() { - delete W64Writer; - if (CodecContext) { - avcodec_close(CodecContext); - if (FreeCodecContext) - av_freep(&CodecContext); - } - delete TCC; -} - -TFrameInfo::TFrameInfo() { -} - -TFrameInfo::TFrameInfo(int64_t PTS, int64_t SampleStart, unsigned int SampleCount, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) { - this->PTS = PTS; - this->RepeatPict = RepeatPict; - this->KeyFrame = KeyFrame; - this->SampleStart = SampleStart; - this->SampleCount = SampleCount; - this->FilePos = FilePos; - this->FrameSize = FrameSize; - this->OriginalPos = 0; -} - -TFrameInfo TFrameInfo::VideoFrameInfo(int64_t PTS, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) { - return TFrameInfo(PTS, 0, 0, RepeatPict, KeyFrame, FilePos, FrameSize); -} - -TFrameInfo TFrameInfo::AudioFrameInfo(int64_t PTS, int64_t SampleStart, int64_t SampleCount, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) { - return TFrameInfo(PTS, SampleStart, static_cast(SampleCount), 0, KeyFrame, FilePos, FrameSize); -} - -void FFMS_Track::WriteTimecodes(const char *TimecodeFile) { - ffms_fstream Timecodes(TimecodeFile, std::ios::out | std::ios::trunc); - - if (!Timecodes.is_open()) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - std::string("Failed to open '") + TimecodeFile + "' for writing"); - - Timecodes << "# timecode format v2\n"; - - for (iterator Cur = begin(); Cur != end(); ++Cur) - Timecodes << std::fixed << ((Cur->PTS * TB.Num) / (double)TB.Den) << "\n"; -} - -int FFMS_Track::FrameFromPTS(int64_t PTS) { - for (int i = 0; i < static_cast(size()); i++) - if (at(i).PTS == PTS) - return i; - return -1; -} - -static bool PTSComparison(TFrameInfo FI1, TFrameInfo FI2) { - return FI1.PTS < FI2.PTS; -} - -int FFMS_Track::ClosestFrameFromPTS(int64_t PTS) { - TFrameInfo F; - F.PTS = PTS; - - iterator Pos = std::lower_bound(begin(), end(), F, PTSComparison); - int Frame = std::distance(begin(), Pos); - if ((Pos + 1) != end() && FFABS(Pos->PTS - PTS) > FFABS((Pos + 1)->PTS - PTS)) - Frame += 1; - return Frame; -} - -int FFMS_Track::FindClosestVideoKeyFrame(int Frame) { - Frame = FFMIN(FFMAX(Frame, 0), static_cast(size()) - 1); - for (; Frame > 0 && !at(Frame).KeyFrame; Frame--) ; - for (; Frame > 0 && !at(at(Frame).OriginalPos).KeyFrame; Frame--) ; - return Frame; -} - -FFMS_Track::FFMS_Track() { - this->TT = FFMS_TYPE_UNKNOWN; - this->TB.Num = 0; - this->TB.Den = 0; - this->UseDTS = false; -} - -FFMS_Track::FFMS_Track(int64_t Num, int64_t Den, FFMS_TrackType TT, bool UseDTS) { - this->TT = TT; - this->TB.Num = Num; - this->TB.Den = Den; - this->UseDTS = UseDTS; -} - -void FFMS_Index::CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20]) { - FILE *SFile = ffms_fopen(Filename,"rb"); - if (!SFile) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - std::string("Failed to open '") + Filename + "' for hashing"); - - std::vector FileBuffer(1024*1024, 0); - std::vector ctxmem(av_sha1_size); - AVSHA1 *ctx = (AVSHA1 *)(&ctxmem[0]); - av_sha1_init(ctx); - - try { - fread(&FileBuffer[0], 1, FileBuffer.size(), SFile); - if (ferror(SFile) && !feof(SFile)) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - std::string("Failed to read '") + Filename + "' for hashing"); - - av_sha1_update(ctx, &FileBuffer[0], FileBuffer.size()); - - fseeko(SFile, -(int)FileBuffer.size(), SEEK_END); - std::fill(FileBuffer.begin(), FileBuffer.end(), 0); - fread(&FileBuffer[0], 1, FileBuffer.size(), SFile); - if (ferror(SFile) && !feof(SFile)) { - std::ostringstream buf; - buf << "Failed to seek with offset " << FileBuffer.size() << " from file end in '" << Filename << "' for hashing"; - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, buf.str()); - } - - av_sha1_update(ctx, &FileBuffer[0], FileBuffer.size()); - - fseeko(SFile, 0, SEEK_END); - if (ferror(SFile)) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - std::string("Failed to seek to end of '") + Filename + "' for hashing"); - - *Filesize = ftello(SFile); - } - catch (...) { - fclose(SFile); - av_sha1_final(ctx, Digest); - throw; - } - fclose(SFile); - av_sha1_final(ctx, Digest); -} - -void FFMS_Index::Sort() { - for (FFMS_Index::iterator Cur = begin(); Cur != end(); ++Cur) { - if (Cur->size() > 2 && Cur->front().PTS >= Cur->back().PTS) Cur->pop_back(); - - for (size_t i = 0; i < Cur->size(); i++) - Cur->at(i).OriginalPos = i; - - std::sort(Cur->begin(), Cur->end(), PTSComparison); - - std::vector ReorderTemp; - ReorderTemp.resize(Cur->size()); - - for (size_t i = 0; i < Cur->size(); i++) - ReorderTemp[i] = Cur->at(i).OriginalPos; - - for (size_t i = 0; i < Cur->size(); i++) - Cur->at(ReorderTemp[i]).OriginalPos = i; - } -} - -bool FFMS_Index::CompareFileSignature(const char *Filename) { - int64_t CFilesize; - uint8_t CDigest[20]; - CalculateFileSignature(Filename, &CFilesize, CDigest); - return (CFilesize == Filesize && !memcmp(CDigest, Digest, sizeof(Digest))); -} - -#define CHUNK 65536 - -static unsigned int z_def(ffms_fstream *IndexStream, z_stream *stream, void *in, size_t in_sz, int finish) { - unsigned int total = 0, have; - int ret; - char out[CHUNK]; - - if (!finish && (in_sz == 0 || in == NULL)) return 0; - - stream->next_in = (Bytef*) in; - stream->avail_in = in_sz; - do { - do { - stream->avail_out = CHUNK; - stream->next_out = (Bytef*) out; - ret = deflate(stream, finish ? Z_FINISH : Z_NO_FLUSH); - have = CHUNK - stream->avail_out; - if (have) IndexStream->write(out, have); - total += have; - } while (stream->avail_out == 0); - } while (finish && ret != Z_STREAM_END); - if (finish) deflateEnd(stream); - return total; -} - -void FFMS_Index::WriteIndex(const char *IndexFile) { - ffms_fstream IndexStream(IndexFile, std::ios::out | std::ios::binary | std::ios::trunc); - - if (!IndexStream.is_open()) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - std::string("Failed to open '") + IndexFile + "' for writing"); - - z_stream stream; - memset(&stream, 0, sizeof(z_stream)); - if (deflateInit(&stream, 9) != Z_OK) { - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to initialize zlib"); - } - - // Write the index file header - IndexHeader IH; - IH.Id = INDEXID; - IH.Version = FFMS_VERSION; - IH.Tracks = size(); - IH.Decoder = Decoder; - IH.LAVUVersion = avutil_version(); - IH.LAVFVersion = avformat_version(); - IH.LAVCVersion = avcodec_version(); - IH.LSWSVersion = swscale_version(); - IH.LPPVersion = postproc_version(); - IH.FileSize = Filesize; - memcpy(IH.FileSignature, Digest, sizeof(Digest)); - - z_def(&IndexStream, &stream, &IH, sizeof(IndexHeader), 0); - - for (unsigned int i = 0; i < IH.Tracks; i++) { - FFMS_Track &ctrack = at(i); - TrackHeader TH; - TH.TT = ctrack.TT; - TH.Frames = ctrack.size(); - TH.Num = ctrack.TB.Num;; - TH.Den = ctrack.TB.Den; - TH.UseDTS = ctrack.UseDTS; - - FFMS_Track temptrack; - temptrack.resize(TH.Frames); - - if (TH.Frames) - temptrack[0] = ctrack[0]; - - for (size_t j = 1; j < ctrack.size(); j++) { - temptrack[j] = ctrack[j]; - temptrack[j].FilePos = ctrack[j].FilePos - ctrack[j - 1].FilePos; - temptrack[j].OriginalPos = ctrack[j].OriginalPos - ctrack[j - 1].OriginalPos; - temptrack[j].PTS = ctrack[j].PTS - ctrack[j - 1].PTS; - temptrack[j].SampleStart = ctrack[j].SampleStart - ctrack[j - 1].SampleStart; - } - - z_def(&IndexStream, &stream, &TH, sizeof(TrackHeader), 0); - if (TH.Frames) - z_def(&IndexStream, &stream, FFMS_GET_VECTOR_PTR(temptrack), TH.Frames * sizeof(TFrameInfo), 0); - } - z_def(&IndexStream, &stream, NULL, 0, 1); -} - -static unsigned int z_inf(ffms_fstream *Index, z_stream *stream, void *in, size_t in_sz, void *out, size_t out_sz) { - if (out_sz == 0 || out == 0) return 0; - stream->next_out = (Bytef*) out; - stream->avail_out = out_sz; - - do { - if (stream->avail_in) memmove(in, stream->next_in, stream->avail_in); - Index->read(((char*)in) + stream->avail_in, in_sz - stream->avail_in); - stream->next_in = (Bytef*) in; - stream->avail_in += Index->gcount(); - - switch (inflate(stream, Z_SYNC_FLUSH)) { - case Z_NEED_DICT: - inflateEnd(stream); - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to read data: Dictionary error."); - case Z_DATA_ERROR: - inflateEnd(stream); - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to read data: Data error."); - case Z_MEM_ERROR: - inflateEnd(stream); - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to read data: Memory error."); - case Z_STREAM_END: - inflateEnd(stream); - return out_sz - stream->avail_out; - } - - } while (stream->avail_out); - return out_sz; -} - -void FFMS_Index::ReadIndex(const char *IndexFile) { - ffms_fstream Index(IndexFile, std::ios::in | std::ios::binary); - - if (!Index.is_open()) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - std::string("Failed to open '") + IndexFile + "' for reading"); - - z_stream stream; - memset(&stream, 0, sizeof(z_stream)); - unsigned char in[CHUNK]; - if (inflateInit(&stream) != Z_OK) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - "Failed to initialize zlib"); - - // Read the index file header - IndexHeader IH; - z_inf(&Index, &stream, &in, CHUNK, &IH, sizeof(IndexHeader)); - - if (IH.Id != INDEXID) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - std::string("'") + IndexFile + "' is not a valid index file"); - - if (IH.Version != FFMS_VERSION) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - std::string("'") + IndexFile + "' is the expected index version"); - - if (IH.LAVUVersion != avutil_version() || IH.LAVFVersion != avformat_version() || - IH.LAVCVersion != avcodec_version() || IH.LSWSVersion != swscale_version() || - IH.LPPVersion != postproc_version()) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - std::string("A different FFmpeg build was used to create '") + IndexFile + "'"); - - if (!(IH.Decoder & FFMS_GetEnabledSources())) - throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_NOT_AVAILABLE, - "The source which this index was created with is not available"); - - Decoder = IH.Decoder; - Filesize = IH.FileSize; - memcpy(Digest, IH.FileSignature, sizeof(Digest)); - - try { - for (unsigned int i = 0; i < IH.Tracks; i++) { - TrackHeader TH; - z_inf(&Index, &stream, &in, CHUNK, &TH, sizeof(TrackHeader)); - push_back(FFMS_Track(TH.Num, TH.Den, static_cast(TH.TT), TH.UseDTS != 0)); - FFMS_Track &ctrack = at(i); - - if (TH.Frames) { - ctrack.resize(TH.Frames); - z_inf(&Index, &stream, &in, CHUNK, FFMS_GET_VECTOR_PTR(ctrack), TH.Frames * sizeof(TFrameInfo)); - } - - for (size_t j = 1; j < ctrack.size(); j++) { - ctrack[j].FilePos = ctrack[j].FilePos + ctrack[j - 1].FilePos; - ctrack[j].OriginalPos = ctrack[j].OriginalPos + ctrack[j - 1].OriginalPos; - ctrack[j].PTS = ctrack[j].PTS + ctrack[j - 1].PTS; - ctrack[j].SampleStart = ctrack[j].SampleStart + ctrack[j - 1].SampleStart; - } - } - } - catch (FFMS_Exception const&) { - throw; - } - catch (...) { - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - std::string("Unknown error while reading index information in '") + IndexFile + "'"); - } -} - -FFMS_Index::FFMS_Index() { -} - -FFMS_Index::FFMS_Index(int64_t Filesize, uint8_t Digest[20]) { - this->Filesize = Filesize; - memcpy(this->Digest, Digest, sizeof(this->Digest)); -} - -void FFMS_Indexer::SetIndexMask(int IndexMask) { - this->IndexMask = IndexMask; -} - -void FFMS_Indexer::SetDumpMask(int DumpMask) { - this->DumpMask = DumpMask; -} - -void FFMS_Indexer::SetErrorHandling(int ErrorHandling) { - if (ErrorHandling != FFMS_IEH_ABORT && ErrorHandling != FFMS_IEH_CLEAR_TRACK && - ErrorHandling != FFMS_IEH_STOP_TRACK && ErrorHandling != FFMS_IEH_IGNORE) - throw FFMS_Exception(FFMS_ERROR_INDEXING, FFMS_ERROR_INVALID_ARGUMENT, - "Invalid error handling mode specified"); - this->ErrorHandling = ErrorHandling; -} - -void FFMS_Indexer::SetProgressCallback(TIndexCallback IC, void *ICPrivate) { - this->IC = IC; - this->ICPrivate = ICPrivate; -} - -void FFMS_Indexer::SetAudioNameCallback(TAudioNameCallback ANC, void *ANCPrivate) { - this->ANC = ANC; - this->ANCPrivate = ANCPrivate; -} - -FFMS_Indexer *FFMS_Indexer::CreateIndexer(const char *Filename) { - AVFormatContext *FormatContext = NULL; - - if (av_open_input_file(&FormatContext, Filename, NULL, 0, NULL) != 0) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - std::string("Can't open '") + Filename + "'"); - - // Do matroska indexing instead? - if (!strncmp(FormatContext->iformat->name, "matroska", 8)) { - av_close_input_file(FormatContext); - return new FFMatroskaIndexer(Filename); - } - -#ifdef HAALISOURCE - // Do haali ts indexing instead? - if (HasHaaliMPEG && (!strcmp(FormatContext->iformat->name, "mpeg") || !strcmp(FormatContext->iformat->name, "mpegts"))) { - av_close_input_file(FormatContext); - return new FFHaaliIndexer(Filename, FFMS_SOURCE_HAALIMPEG); - } - - if (HasHaaliOGG && !strcmp(FormatContext->iformat->name, "ogg")) { - av_close_input_file(FormatContext); - return new FFHaaliIndexer(Filename, FFMS_SOURCE_HAALIOGG); - } -#endif - - return new FFLAVFIndexer(Filename, FormatContext); -} - -FFMS_Indexer::FFMS_Indexer(const char *Filename) : DecodingBuffer(AVCODEC_MAX_AUDIO_FRAME_SIZE * 10) { - IndexMask = 0; - DumpMask = 0; - ErrorHandling = FFMS_IEH_CLEAR_TRACK; - IC = NULL; - ICPrivate = NULL; - ANC = NULL; - ANCPrivate = NULL; - - FFMS_Index::CalculateFileSignature(Filename, &Filesize, Digest); -} - -FFMS_Indexer::~FFMS_Indexer() { - -} - -void FFMS_Indexer::WriteAudio(SharedAudioContext &AudioContext, FFMS_Index *Index, int Track, int DBSize) { - // Delay writer creation until after an audio frame has been decoded. This ensures that all parameters are known when writing the headers. - if (DBSize <= 0) return; - - if (!AudioContext.W64Writer) { - FFMS_AudioProperties AP; - FillAP(AP, AudioContext.CodecContext, (*Index)[Track]); - int FNSize = (*ANC)(SourceFile, Track, &AP, NULL, 0, ANCPrivate); - if (FNSize <= 0) { - DumpMask = DumpMask & ~(1 << Track); - return; - } - - std::vector WName(FNSize); - (*ANC)(SourceFile, Track, &AP, &WName[0], FNSize, ANCPrivate); - std::string WN(&WName[0]); - try { - AudioContext.W64Writer = - new Wave64Writer(WN.c_str(), - av_get_bits_per_sample_fmt(AudioContext.CodecContext->sample_fmt), - AudioContext.CodecContext->channels, - AudioContext.CodecContext->sample_rate, - (AudioContext.CodecContext->sample_fmt == AV_SAMPLE_FMT_FLT) || (AudioContext.CodecContext->sample_fmt == AV_SAMPLE_FMT_DBL)); - } catch (...) { - throw FFMS_Exception(FFMS_ERROR_WAVE_WRITER, FFMS_ERROR_FILE_WRITE, - "Failed to write wave data"); - } - } - - AudioContext.W64Writer->WriteData(&DecodingBuffer[0], DBSize); -} - -int64_t FFMS_Indexer::IndexAudioPacket(int Track, AVPacket *Packet, SharedAudioContext &Context, FFMS_Index &TrackIndices) { - AVCodecContext *CodecContext = Context.CodecContext; - int64_t StartSample = Context.CurrentSample; - int Read = 0; - while (Packet->size > 0) { - int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10; - int Ret = avcodec_decode_audio3(CodecContext, (int16_t *)&DecodingBuffer[0], &dbsize, Packet); - if (Ret < 0) { - if (ErrorHandling == FFMS_IEH_ABORT) { - throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, "Audio decoding error"); - } else if (ErrorHandling == FFMS_IEH_CLEAR_TRACK) { - TrackIndices[Track].clear(); - IndexMask &= ~(1 << Track); - } else if (ErrorHandling == FFMS_IEH_STOP_TRACK) { - IndexMask &= ~(1 << Track); - } - break; - } - Packet->size -= Ret; - Packet->data += Ret; - Read += Ret; - - CheckAudioProperties(Track, CodecContext); - - if (dbsize > 0) - Context.CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_fmt(CodecContext->sample_fmt) * CodecContext->channels); - - if (DumpMask & (1 << Track)) - WriteAudio(Context, &TrackIndices, Track, dbsize); - } - Packet->size += Read; - Packet->data -= Read; - return Context.CurrentSample - StartSample; -} - -void FFMS_Indexer::CheckAudioProperties(int Track, AVCodecContext *Context) { - std::map::iterator it = LastAudioProperties.find(Track); - if (it == LastAudioProperties.end()) { - FFMS_AudioProperties &AP = LastAudioProperties[Track]; - AP.SampleRate = Context->sample_rate; - AP.SampleFormat = Context->sample_fmt; - AP.Channels = Context->channels; - } - else if (it->second.SampleRate != Context->sample_rate || - it->second.SampleFormat != Context->sample_fmt || - it->second.Channels != Context->channels) { - std::ostringstream buf; - buf << - "Audio format change detected. This is currently unsupported." - << " Channels: " << it->second.Channels << " -> " << Context->channels << ";" - << " Sample rate: " << it->second.SampleRate << " -> " << Context->sample_rate << ";" - << " Sample format: " << GetLAVCSampleFormatName((AVSampleFormat)it->second.SampleFormat) << " -> " - << GetLAVCSampleFormatName(Context->sample_fmt); - throw FFMS_Exception(FFMS_ERROR_UNSUPPORTED, FFMS_ERROR_DECODING, buf.str()); - } -} diff --git a/aegisub/libffms/src/core/indexing.h b/aegisub/libffms/src/core/indexing.h deleted file mode 100644 index 9758b62b7..000000000 --- a/aegisub/libffms/src/core/indexing.h +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#ifndef INDEXING_H -#define INDEXING_H - -#include -#include -#include "utils.h" -#include "wave64writer.h" - -#ifdef HAALISOURCE -# define WIN32_LEAN_AND_MEAN -# define _WIN32_DCOM -# include -# include -# include -# include -# include -# include "CoParser.h" -# include "guids.h" -#endif - - -class SharedVideoContext { -private: - bool FreeCodecContext; -public: - AVCodecContext *CodecContext; - AVCodecParserContext *Parser; - TrackCompressionContext *TCC; - - SharedVideoContext(bool FreeCodecContext); - ~SharedVideoContext(); -}; - -class SharedAudioContext { -private: - bool FreeCodecContext; -public: - AVCodecContext *CodecContext; - Wave64Writer *W64Writer; - int64_t CurrentSample; - TrackCompressionContext *TCC; - - SharedAudioContext(bool FreeCodecContext); - ~SharedAudioContext(); -}; - -struct TFrameInfo { -public: - FFMS_FRAMEINFO_COMMON - int64_t SampleStart; - unsigned int SampleCount; - int64_t FilePos; - unsigned int FrameSize; - size_t OriginalPos; - - TFrameInfo(); - static TFrameInfo VideoFrameInfo(int64_t PTS, int RepeatPict, bool KeyFrame, int64_t FilePos = 0, unsigned int FrameSize = 0); - static TFrameInfo AudioFrameInfo(int64_t PTS, int64_t SampleStart, int64_t SampleCount, bool KeyFrame, int64_t FilePos = 0, unsigned int FrameSize = 0); -private: - TFrameInfo(int64_t PTS, int64_t SampleStart, unsigned int SampleCount, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize); -}; - -class FFMS_Track : public std::vector { -public: - FFMS_TrackType TT; - FFMS_TrackTimeBase TB; - bool UseDTS; - - int FindClosestVideoKeyFrame(int Frame); - int FrameFromPTS(int64_t PTS); - int ClosestFrameFromPTS(int64_t PTS); - void WriteTimecodes(const char *TimecodeFile); - - FFMS_Track(); - FFMS_Track(int64_t Num, int64_t Den, FFMS_TrackType TT, bool UseDTS = false); -}; - -class FFMS_Index : public std::vector { -public: - static void CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20]); - - int Decoder; - int64_t Filesize; - uint8_t Digest[20]; - - void Sort(); - bool CompareFileSignature(const char *Filename); - void WriteIndex(const char *IndexFile); - void ReadIndex(const char *IndexFile); - - FFMS_Index(); - FFMS_Index(int64_t Filesize, uint8_t Digest[20]); -}; - -class FFMS_Indexer { - std::map LastAudioProperties; -protected: - int IndexMask; - int DumpMask; - int ErrorHandling; - TIndexCallback IC; - void *ICPrivate; - TAudioNameCallback ANC; - void *ANCPrivate; - const char *SourceFile; - AlignedBuffer DecodingBuffer; - - int64_t Filesize; - uint8_t Digest[20]; - - void WriteAudio(SharedAudioContext &AudioContext, FFMS_Index *Index, int Track, int DBSize); - void CheckAudioProperties(int Track, AVCodecContext *Context); - int64_t IndexAudioPacket(int Track, AVPacket *Packet, SharedAudioContext &Context, FFMS_Index &TrackIndices); -public: - static FFMS_Indexer *CreateIndexer(const char *Filename); - FFMS_Indexer(const char *Filename); - virtual ~FFMS_Indexer(); - void SetIndexMask(int IndexMask); - void SetDumpMask(int DumpMask); - void SetErrorHandling(int ErrorHandling); - void SetProgressCallback(TIndexCallback IC, void *ICPrivate); - void SetAudioNameCallback(TAudioNameCallback ANC, void *ANCPrivate); - virtual FFMS_Index *DoIndexing() = 0; - virtual int GetNumberOfTracks() = 0; - virtual FFMS_TrackType GetTrackType(int Track) = 0; - virtual const char *GetTrackCodec(int Track) = 0; -}; - -class FFLAVFIndexer : public FFMS_Indexer { - AVFormatContext *FormatContext; - void ReadTS(const AVPacket &Packet, int64_t &TS, bool &UseDTS); -public: - FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext); - ~FFLAVFIndexer(); - FFMS_Index *DoIndexing(); - int GetNumberOfTracks(); - FFMS_TrackType GetTrackType(int Track); - const char *GetTrackCodec(int Track); -}; - -class FFMatroskaIndexer : public FFMS_Indexer { -private: - MatroskaFile *MF; - MatroskaReaderContext MC; - AVCodec *Codec[32]; -public: - FFMatroskaIndexer(const char *Filename); - ~FFMatroskaIndexer(); - FFMS_Index *DoIndexing(); - int GetNumberOfTracks(); - FFMS_TrackType GetTrackType(int Track); - const char *GetTrackCodec(int Track); -}; - -#ifdef HAALISOURCE - -class FFHaaliIndexer : public FFMS_Indexer { -private: - int SourceMode; - CComPtr pMMC; - int NumTracks; - FFMS_TrackType TrackType[32]; - CComQIPtr PropertyBags[32]; - int64_t Duration; -public: - FFHaaliIndexer(const char *Filename, enum FFMS_Sources SourceMode); - FFMS_Index *DoIndexing(); - int GetNumberOfTracks(); - FFMS_TrackType GetTrackType(int Track); - const char *GetTrackCodec(int Track); -}; - -#endif // HAALISOURCE - -#endif diff --git a/aegisub/libffms/src/core/lavfaudio.cpp b/aegisub/libffms/src/core/lavfaudio.cpp deleted file mode 100644 index ac1fde6d0..000000000 --- a/aegisub/libffms/src/core/lavfaudio.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2011 Thomas Goyne -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include "audiosource.h" -#include - -FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFMS_Index &Index, int DelayMode) -: FFMS_AudioSource(SourceFile, Index, Track) -, FormatContext(NULL) -{ - LAVFOpenFile(SourceFile, FormatContext); - - CodecContext.reset(FormatContext->streams[TrackNumber]->codec); - assert(CodecContext); - - AVCodec *Codec = avcodec_find_decoder(CodecContext->codec_id); - try { - if (!Codec) - throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, - "Audio codec not found"); - - if (avcodec_open(CodecContext, Codec) < 0) - throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, - "Could not open audio codec"); - } - catch (...) { - av_close_input_file(FormatContext); - throw; - } - - if (Frames.back().PTS == Frames.front().PTS) - SeekOffset = -1; - else - SeekOffset = 10; - Init(Index, DelayMode); -} - -FFLAVFAudio::~FFLAVFAudio() { - av_close_input_file(FormatContext); -} - -void FFLAVFAudio::Seek() { - size_t TargetPacket = GetSeekablePacketNumber(Frames, PacketNumber); - - if (av_seek_frame(FormatContext, TrackNumber, Frames[TargetPacket].PTS, AVSEEK_FLAG_BACKWARD) < 0) - av_seek_frame(FormatContext, TrackNumber, Frames[TargetPacket].PTS, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY); - - if (TargetPacket != PacketNumber) { - // Decode until the PTS changes so we know where we are - int64_t LastPTS = Frames[PacketNumber].PTS; - while (LastPTS == Frames[PacketNumber].PTS) DecodeNextBlock(); - } -} - -bool FFLAVFAudio::ReadPacket(AVPacket *Packet) { - InitNullPacket(*Packet); - - while (av_read_frame(FormatContext, Packet) >= 0) { - if (Packet->stream_index == TrackNumber) { - while (Frames[PacketNumber].PTS < Packet->pts) ++PacketNumber; - return true; - } - av_free_packet(Packet); - } - return false; -} -void FFLAVFAudio::FreePacket(AVPacket *Packet) { - av_free_packet(Packet); -} diff --git a/aegisub/libffms/src/core/lavfindexer.cpp b/aegisub/libffms/src/core/lavfindexer.cpp deleted file mode 100644 index c48dfb682..000000000 --- a/aegisub/libffms/src/core/lavfindexer.cpp +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include "indexing.h" - - - -FFLAVFIndexer::FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext) : FFMS_Indexer(Filename) { - SourceFile = Filename; - this->FormatContext = FormatContext; - - if (av_find_stream_info(FormatContext) < 0) { - av_close_input_file(FormatContext); - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - "Couldn't find stream information"); - } -} - -FFLAVFIndexer::~FFLAVFIndexer() { - av_close_input_file(FormatContext); -} - -FFMS_Index *FFLAVFIndexer::DoIndexing() { - std::vector AudioContexts(FormatContext->nb_streams, SharedAudioContext(false)); - std::vector VideoContexts(FormatContext->nb_streams, SharedVideoContext(false)); - - std::auto_ptr TrackIndices(new FFMS_Index(Filesize, Digest)); - TrackIndices->Decoder = FFMS_SOURCE_LAVF; - - for (unsigned int i = 0; i < FormatContext->nb_streams; i++) { - TrackIndices->push_back(FFMS_Track((int64_t)FormatContext->streams[i]->time_base.num * 1000, - FormatContext->streams[i]->time_base.den, - static_cast(FormatContext->streams[i]->codec->codec_type))); - - if (FormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) { - AVCodec *VideoCodec = avcodec_find_decoder(FormatContext->streams[i]->codec->codec_id); - if (!VideoCodec) - throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED, - "Video codec not found"); - - if (avcodec_open(FormatContext->streams[i]->codec, VideoCodec) < 0) - throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, - "Could not open video codec"); - - VideoContexts[i].CodecContext = FormatContext->streams[i]->codec; - VideoContexts[i].Parser = av_parser_init(FormatContext->streams[i]->codec->codec_id); - if (VideoContexts[i].Parser) - VideoContexts[i].Parser->flags = PARSER_FLAG_COMPLETE_FRAMES; - IndexMask |= 1 << i; - } - else if (IndexMask & (1 << i) && FormatContext->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO) { - AVCodecContext *AudioCodecContext = FormatContext->streams[i]->codec; - - AVCodec *AudioCodec = avcodec_find_decoder(AudioCodecContext->codec_id); - if (AudioCodec == NULL) - throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED, - "Audio codec not found"); - - if (avcodec_open(AudioCodecContext, AudioCodec) < 0) - throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, - "Could not open audio codec"); - - AudioContexts[i].CodecContext = AudioCodecContext; - } else { - IndexMask &= ~(1 << i); - } - } - - AVPacket Packet; - InitNullPacket(Packet); - std::vector LastValidTS; - LastValidTS.resize(FormatContext->nb_streams, ffms_av_nopts_value); - - while (av_read_frame(FormatContext, &Packet) >= 0) { - // Update progress - // FormatContext->pb can apparently be NULL when opening images. - if (IC && FormatContext->pb) { - if ((*IC)(FormatContext->pb->pos, FormatContext->file_size, ICPrivate)) - throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER, - "Cancelled by user"); - } - if (!(IndexMask & (1 << Packet.stream_index))) { - av_free_packet(&Packet); - continue; - } - - int Track = Packet.stream_index; - bool KeyFrame = !!(Packet.flags & AV_PKT_FLAG_KEY); - ReadTS(Packet, LastValidTS[Track], (*TrackIndices)[Track].UseDTS); - - if (FormatContext->streams[Track]->codec->codec_type == CODEC_TYPE_VIDEO) { - if (LastValidTS[Track] == ffms_av_nopts_value) - throw FFMS_Exception(FFMS_ERROR_INDEXING, FFMS_ERROR_PARSER, - "Invalid initial pts and dts"); - - int RepeatPict = -1; - - if (VideoContexts[Track].Parser) { - uint8_t *OB; - int OBSize; - av_parser_parse2(VideoContexts[Track].Parser, VideoContexts[Track].CodecContext, &OB, &OBSize, Packet.data, Packet.size, Packet.pts, Packet.dts, Packet.pos); - RepeatPict = VideoContexts[Track].Parser->repeat_pict; - } - - (*TrackIndices)[Track].push_back(TFrameInfo::VideoFrameInfo(LastValidTS[Track], RepeatPict, KeyFrame, Packet.pos)); - } - else if (FormatContext->streams[Track]->codec->codec_type == CODEC_TYPE_AUDIO) { - int64_t StartSample = AudioContexts[Track].CurrentSample; - int64_t SampleCount = IndexAudioPacket(Track, &Packet, AudioContexts[Track], *TrackIndices); - - if (SampleCount != 0) - (*TrackIndices)[Track].push_back(TFrameInfo::AudioFrameInfo(LastValidTS[Track], - StartSample, SampleCount, KeyFrame, Packet.pos)); - } - - av_free_packet(&Packet); - } - - TrackIndices->Sort(); - return TrackIndices.release(); -} - -void FFLAVFIndexer::ReadTS(const AVPacket &Packet, int64_t &TS, bool &UseDTS) { - if (!UseDTS && Packet.pts != ffms_av_nopts_value) - TS = Packet.pts; - if (TS == ffms_av_nopts_value) - UseDTS = true; - if (UseDTS && Packet.dts != ffms_av_nopts_value) - TS = Packet.dts; -} - -int FFLAVFIndexer::GetNumberOfTracks() { - return FormatContext->nb_streams; -} - -FFMS_TrackType FFLAVFIndexer::GetTrackType(int Track) { - return static_cast(FormatContext->streams[Track]->codec->codec_type); -} - -const char *FFLAVFIndexer::GetTrackCodec(int Track) { - AVCodec *codec = avcodec_find_decoder(FormatContext->streams[Track]->codec->codec_id); - return codec ? codec->name : NULL; -} diff --git a/aegisub/libffms/src/core/lavfvideo.cpp b/aegisub/libffms/src/core/lavfvideo.cpp deleted file mode 100644 index cb2b9bdb2..000000000 --- a/aegisub/libffms/src/core/lavfvideo.cpp +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include "videosource.h" - - - -void FFLAVFVideo::Free(bool CloseCodec) { - if (CloseCodec) - avcodec_close(CodecContext); - av_close_input_file(FormatContext); -} - -FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFMS_Index *Index, - int Threads, int SeekMode) - : Res(FFSourceResources(this)), FFMS_VideoSource(SourceFile, Index, Track) { - - FormatContext = NULL; - AVCodec *Codec = NULL; - this->SeekMode = SeekMode; - VideoTrack = Track; - Frames = (*Index)[VideoTrack]; - - LAVFOpenFile(SourceFile, FormatContext); - - if (SeekMode >= 0 && av_seek_frame(FormatContext, VideoTrack, Frames[0].PTS, AVSEEK_FLAG_BACKWARD) < 0) - throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, - "Video track is unseekable"); - - CodecContext = FormatContext->streams[VideoTrack]->codec; - if (avcodec_thread_init(CodecContext, Threads)) - CodecContext->thread_count = 1; - - Codec = avcodec_find_decoder(CodecContext->codec_id); - if (Codec == NULL) - throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, - "Video codec not found"); - - if (avcodec_open(CodecContext, Codec) < 0) - throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, - "Could not open video codec"); - - Res.CloseCodec(true); - - // Always try to decode a frame to make sure all required parameters are known - int64_t Dummy; - DecodeNextFrame(&Dummy); - - //VP.image_type = VideoInfo::IT_TFF; - VP.FPSDenominator = FormatContext->streams[VideoTrack]->time_base.num; - VP.FPSNumerator = FormatContext->streams[VideoTrack]->time_base.den; - VP.RFFDenominator = CodecContext->time_base.num; - VP.RFFNumerator = CodecContext->time_base.den; - if (CodecContext->codec_id == CODEC_ID_H264) { - if (VP.RFFNumerator & 1) - VP.RFFDenominator *= 2; - else - VP.RFFNumerator /= 2; - } - VP.NumFrames = Frames.size(); - VP.TopFieldFirst = DecodeFrame->top_field_first; -#ifdef FFMS_HAVE_FFMPEG_COLORSPACE_INFO - VP.ColorSpace = CodecContext->colorspace; - VP.ColorRange = CodecContext->color_range; -#else - VP.ColorSpace = 0; - VP.ColorRange = 0; -#endif - // these pixfmt's are deprecated but still used - if ( - CodecContext->pix_fmt == PIX_FMT_YUVJ420P - || CodecContext->pix_fmt == PIX_FMT_YUVJ422P - || CodecContext->pix_fmt == PIX_FMT_YUVJ444P - ) - VP.ColorRange = AVCOL_RANGE_JPEG; - - - VP.FirstTime = ((Frames.front().PTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; - VP.LastTime = ((Frames.back().PTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; - - if (CodecContext->width <= 0 || CodecContext->height <= 0) - throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, - "Codec returned zero size video"); - - // sanity check framerate - if (VP.FPSDenominator > VP.FPSNumerator || VP.FPSDenominator <= 0 || VP.FPSNumerator <= 0) { - VP.FPSDenominator = 1; - VP.FPSNumerator = 30; - } - - // Calculate the average framerate - if (Frames.size() >= 2) { - double PTSDiff = (double)(Frames.back().PTS - Frames.front().PTS); - double TD = (double)(Frames.TB.Den); - double TN = (double)(Frames.TB.Num); - VP.FPSDenominator = (unsigned int)(((double)1000000) / (double)((VP.NumFrames - 1) / ((PTSDiff * TN/TD) / (double)1000))); - VP.FPSNumerator = 1000000; - } - - // attempt to correct framerate to the proper NTSC fraction, if applicable - CorrectNTSCRationalFramerate(&VP.FPSNumerator, &VP.FPSDenominator); - // correct the timebase, if necessary - CorrectTimebase(&VP, &Frames.TB); - - // Cannot "output" to PPFrame without doing all other initialization - // This is the additional mess required for seekmode=-1 to work in a reasonable way - OutputFrame(DecodeFrame); - - // Set AR variables - VP.SARNum = CodecContext->sample_aspect_ratio.num; - VP.SARDen = CodecContext->sample_aspect_ratio.den; -} - -void FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime) { - AVPacket Packet; - InitNullPacket(Packet); - int FrameFinished = 0; - *AStartTime = -1; - - if (InitialDecode == -1) { - if (DelayCounter > CodecContext->has_b_frames) { - DelayCounter--; - goto Done; - } else { - InitialDecode = 0; - } - } - - while (av_read_frame(FormatContext, &Packet) >= 0) { - if (Packet.stream_index == VideoTrack) { - if (*AStartTime < 0) { - if (Frames.UseDTS) - *AStartTime = Packet.dts; - else - *AStartTime = Packet.pts; - } - - avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet); - - if (!FrameFinished) - DelayCounter++; - if (DelayCounter > CodecContext->has_b_frames && !InitialDecode) { - av_free_packet(&Packet); - goto Done; - } - } - - av_free_packet(&Packet); - - if (FrameFinished) - goto Done; - } - - // Flush the last frames - if (CodecContext->has_b_frames) { - AVPacket NullPacket; - InitNullPacket(NullPacket); - avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket); - } - - if (!FrameFinished) - goto Error; - -Error: -Done: - if (InitialDecode == 1) InitialDecode = -1; -} - -FFMS_Frame *FFLAVFVideo::GetFrame(int n) { - GetFrameCheck(n); - - if (LastFrameNum == n) - return &LocalFrame; - - bool HasSeeked = false; - int SeekOffset = 0; - - int ClosestKF = 0; - if (SeekMode >= 0) { - ClosestKF = Frames.FindClosestVideoKeyFrame(n); - - if (SeekMode == 0) { - if (n < CurrentFrame) { - av_seek_frame(FormatContext, VideoTrack, Frames[0].PTS, AVSEEK_FLAG_BACKWARD); - avcodec_flush_buffers(CodecContext); - CurrentFrame = 0; - DelayCounter = 0; - InitialDecode = 1; - } - } else { - // 10 frames is used as a margin to prevent excessive seeking since the predicted best keyframe isn't always selected by avformat - if (n < CurrentFrame || ClosestKF > CurrentFrame + 10 || (SeekMode == 3 && n > CurrentFrame + 10)) { -ReSeek: - av_seek_frame(FormatContext, VideoTrack, - (SeekMode == 3) ? Frames[n].PTS : Frames[ClosestKF + SeekOffset].PTS, - AVSEEK_FLAG_BACKWARD); - avcodec_flush_buffers(CodecContext); - HasSeeked = true; - DelayCounter = 0; - InitialDecode = 1; - } - } - } else if (n < CurrentFrame) { - throw FFMS_Exception(FFMS_ERROR_SEEKING, FFMS_ERROR_INVALID_ARGUMENT, - "Non-linear access attempted"); - } - - do { - int64_t StartTime; - if (CurrentFrame+CodecContext->has_b_frames >= n) - CodecContext->skip_frame = AVDISCARD_DEFAULT; - else - CodecContext->skip_frame = AVDISCARD_NONREF; - DecodeNextFrame(&StartTime); - - if (HasSeeked) { - HasSeeked = false; - - // Is the seek destination time known? Does it belong to a frame? - if (StartTime < 0 || (CurrentFrame = Frames.FrameFromPTS(StartTime)) < 0) { - switch (SeekMode) { - case 1: - // No idea where we are so go back a bit further - if (ClosestKF + SeekOffset == 0) - throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, - "Frame accurate seeking is not possible in this file"); - - - SeekOffset -= FFMIN(10, ClosestKF + SeekOffset); - goto ReSeek; - case 2: - case 3: - CurrentFrame = Frames.ClosestFrameFromPTS(StartTime); - break; - default: - throw FFMS_Exception(FFMS_ERROR_SEEKING, FFMS_ERROR_UNKNOWN, - "Failed assertion"); - } - } - } - - CurrentFrame++; - } while (CurrentFrame <= n); - - LastFrameNum = n; - return OutputFrame(DecodeFrame); -} diff --git a/aegisub/libffms/src/core/matroskaaudio.cpp b/aegisub/libffms/src/core/matroskaaudio.cpp deleted file mode 100644 index 95d45cdc5..000000000 --- a/aegisub/libffms/src/core/matroskaaudio.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2011 Thomas Goyne -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include "audiosource.h" -#include - -FFMatroskaAudio::FFMatroskaAudio(const char *SourceFile, int Track, FFMS_Index &Index, int DelayMode) -: FFMS_AudioSource(SourceFile, Index, Track) -, TI(NULL) -{ - if (!(MC.ST.fp = ffms_fopen(SourceFile, "rb"))) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - std::string("Can't open '") + SourceFile + "': " + strerror(errno)); - - setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE); - - if (!(MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage)))) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - std::string("Can't parse Matroska file: ") + ErrorMessage); - - TI = mkv_GetTrackInfo(MF, Track); - assert(TI); - - if (TI->CompEnabled) - TCC.reset(new TrackCompressionContext(MF, TI, Track)); - - CodecContext.reset(avcodec_alloc_context(), DeleteMatroskaCodecContext); - assert(CodecContext); - - AVCodec *Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate, 0, TI->AV.Audio.BitDepth)); - if (!Codec) { - mkv_Close(MF); - throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, "Audio codec not found"); - } - - InitializeCodecContextFromMatroskaTrackInfo(TI, CodecContext); - - if (avcodec_open(CodecContext, Codec) < 0) { - mkv_Close(MF); - throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, "Could not open audio codec"); - } - - Init(Index, DelayMode); -} - -FFMatroskaAudio::~FFMatroskaAudio() { - mkv_Close(MF); -} - -bool FFMatroskaAudio::ReadPacket(AVPacket *Packet) { - ReadFrame(CurrentFrame->FilePos, CurrentFrame->FrameSize, TCC.get(), MC); - InitNullPacket(*Packet); - Packet->data = MC.Buffer; - Packet->size = CurrentFrame->FrameSize + ((TCC.get() && TCC->CompressionMethod == COMP_PREPEND) ? TCC->CompressedPrivateDataSize : 0); - Packet->flags = CurrentFrame->KeyFrame ? AV_PKT_FLAG_KEY : 0; - - return true; -} diff --git a/aegisub/libffms/src/core/matroskaindexer.cpp b/aegisub/libffms/src/core/matroskaindexer.cpp deleted file mode 100644 index d9dfdde94..000000000 --- a/aegisub/libffms/src/core/matroskaindexer.cpp +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include "indexing.h" -#include "matroskaparser.h" - - - -FFMatroskaIndexer::FFMatroskaIndexer(const char *Filename) : FFMS_Indexer(Filename) { - SourceFile = Filename; - char ErrorMessage[256]; - for (int i = 0; i < 32; i++) { - Codec[i] = NULL; - } - - InitStdIoStream(&MC.ST); - MC.ST.fp = ffms_fopen(SourceFile, "rb"); - if (MC.ST.fp == NULL) { - std::ostringstream buf; - buf << "Can't open '" << SourceFile << "': " << strerror(errno); - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, buf.str()); - } - - setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE); - - MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage)); - if (MF == NULL) { - std::ostringstream buf; - buf << "Can't parse Matroska file: " << ErrorMessage; - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, buf.str()); - } - - for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) { - TrackInfo *TI = mkv_GetTrackInfo(MF, i); - Codec[i] = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate, 0, TI->AV.Audio.BitDepth)); - } -} - -FFMatroskaIndexer::~FFMatroskaIndexer() { - mkv_Close(MF); -} - -FFMS_Index *FFMatroskaIndexer::DoIndexing() { - std::vector AudioContexts(mkv_GetNumTracks(MF), SharedAudioContext(true)); - std::vector VideoContexts(mkv_GetNumTracks(MF), SharedVideoContext(true)); - - std::auto_ptr TrackIndices(new FFMS_Index(Filesize, Digest)); - TrackIndices->Decoder = FFMS_SOURCE_MATROSKA; - - for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) { - TrackInfo *TI = mkv_GetTrackInfo(MF, i); - TrackIndices->push_back(FFMS_Track(mkv_TruncFloat(mkv_GetTrackInfo(MF, i)->TimecodeScale), 1000000, HaaliTrackTypeToFFTrackType(mkv_GetTrackInfo(MF, i)->Type))); - - if (!Codec[i]) continue; - - AVCodecContext *CodecContext = avcodec_alloc_context(); - InitializeCodecContextFromMatroskaTrackInfo(TI, CodecContext); - - try { - if (TI->Type == TT_VIDEO && (VideoContexts[i].Parser = av_parser_init(Codec[i]->id))) { - if (avcodec_open(CodecContext, Codec[i]) < 0) - throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, - "Could not open video codec"); - - if (TI->CompEnabled) - VideoContexts[i].TCC = new TrackCompressionContext(MF, TI, i); - - VideoContexts[i].CodecContext = CodecContext; - VideoContexts[i].Parser->flags = PARSER_FLAG_COMPLETE_FRAMES; - } - else if (IndexMask & (1 << i) && TI->Type == TT_AUDIO) { - if (avcodec_open(CodecContext, Codec[i]) < 0) - throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, - "Could not open audio codec"); - - if (TI->CompEnabled) - AudioContexts[i].TCC = new TrackCompressionContext(MF, TI, i); - - AudioContexts[i].CodecContext = CodecContext; - } else { - IndexMask &= ~(1 << i); - av_freep(&CodecContext); - } - } - catch (...) { - av_freep(&CodecContext); - throw; - } - } - - ulonglong StartTime, EndTime, FilePos; - unsigned int Track, FrameFlags, FrameSize; - AVPacket TempPacket; - InitNullPacket(TempPacket); - - while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0) { - // Update progress - if (IC && (*IC)(ftello(MC.ST.fp), Filesize, ICPrivate)) - throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER, "Cancelled by user"); - - if (mkv_GetTrackInfo(MF, Track)->Type == TT_VIDEO) { - uint8_t *OB; - int OBSize; - int RepeatPict = -1; - - if (VideoContexts[Track].Parser) { - av_parser_parse2(VideoContexts[Track].Parser, VideoContexts[Track].CodecContext, &OB, &OBSize, TempPacket.data, TempPacket.size, ffms_av_nopts_value, ffms_av_nopts_value, ffms_av_nopts_value); - RepeatPict = VideoContexts[Track].Parser->repeat_pict; - } - - (*TrackIndices)[Track].push_back(TFrameInfo::VideoFrameInfo(StartTime, RepeatPict, (FrameFlags & FRAME_KF) != 0, FilePos, FrameSize)); - } else if (mkv_GetTrackInfo(MF, Track)->Type == TT_AUDIO && (IndexMask & (1 << Track))) { - TrackCompressionContext *TCC = AudioContexts[Track].TCC; - unsigned int CompressedFrameSize = FrameSize; - ReadFrame(FilePos, FrameSize, TCC, MC); - TempPacket.data = MC.Buffer; - TempPacket.size = (TCC && TCC->CompressionMethod == COMP_PREPEND) ? FrameSize + TCC->CompressedPrivateDataSize : FrameSize; - TempPacket.flags = FrameFlags & FRAME_KF ? AV_PKT_FLAG_KEY : 0; - - int64_t StartSample = AudioContexts[Track].CurrentSample; - int64_t SampleCount = IndexAudioPacket(Track, &TempPacket, AudioContexts[Track], *TrackIndices); - - if (SampleCount != 0) - (*TrackIndices)[Track].push_back(TFrameInfo::AudioFrameInfo(StartTime, StartSample, - SampleCount, (FrameFlags & FRAME_KF) != 0, FilePos, CompressedFrameSize)); - } - } - - TrackIndices->Sort(); - return TrackIndices.release(); -} - -int FFMatroskaIndexer::GetNumberOfTracks() { - return mkv_GetNumTracks(MF); -} - -FFMS_TrackType FFMatroskaIndexer::GetTrackType(int Track) { - return HaaliTrackTypeToFFTrackType(mkv_GetTrackInfo(MF, Track)->Type); -} - -const char *FFMatroskaIndexer::GetTrackCodec(int Track) { - return Codec[Track] ? Codec[Track]->name : NULL; -} diff --git a/aegisub/libffms/src/core/matroskaparser.c b/aegisub/libffms/src/core/matroskaparser.c deleted file mode 100644 index 627d70d08..000000000 --- a/aegisub/libffms/src/core/matroskaparser.c +++ /dev/null @@ -1,3371 +0,0 @@ -/* - * Copyright (c) 2004-2008 Mike Matsnev. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice immediately at the beginning of the file, without modification, - * this list of conditions, and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Absolutely no warranty of function or purpose is made by the author - * Mike Matsnev. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id: MatroskaParser.c,v 1.73 2010/03/05 22:41:47 mike Exp $ - * - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#ifdef _MSC_VER -// MS names some functions differently -#define alloca _alloca -#define inline __inline -#else /* _MSC_VER */ -// support for building with MinGW on Windows -#include -#endif /* _MSC_VER */ -#include -#endif /* _WIN32 */ - -#ifdef HAVE_ALLOCA_H -#include -#endif - -#ifndef EVCBUG -#define EVCBUG -#endif - -#include "matroskaparser.h" - -#ifdef MATROSKA_COMPRESSION_SUPPORT -#include -#endif - -#define EBML_VERSION 1 -#define EBML_MAX_ID_LENGTH 4 -#define EBML_MAX_SIZE_LENGTH 8 -#define MATROSKA_VERSION 2 -#define MATROSKA_DOCTYPE "matroska" - -#define MAX_STRING_LEN 1023 -#define QSEGSIZE 512 -#define MAX_TRACKS 32 -#define MAX_READAHEAD (256*1024) - -#define MAXCLUSTER (64*1048576) -#define MAXFRAME (4*1048576) - -#ifdef _MSC_VER -#define LL(x) x##i64 -#define ULL(x) x##ui64 -#else -#define LL(x) x##ll -#define ULL(x) x##ull -#endif - -#define MAXU64 ULL(0xffffffffffffffff) -#define ONE ULL(1) - -// compatibility -static char *mystrdup(struct InputStream *is,const char *src) { - size_t len; - char *dst; - - if (src==NULL) - return NULL; - - len = strlen(src); - dst = is->memalloc(is,len+1); - if (dst==NULL) - return NULL; - - memcpy(dst,src,len+1); - - return dst; -} - -static void mystrlcpy(char *dst,const char *src,unsigned size) { - unsigned i; - - for (i=0;i+1 0) - *dest = '\0'; - return; - } - - while (*fmt && dest < de) - switch (state) { - case 0: - if (*fmt == '%') { - ++fmt; - state = 1; - width = zero = neg = ll = 0; - } else - *dest++ = *fmt++; - break; - case 1: - if (*fmt == '-') { - neg = 1; - ++fmt; - state = 2; - break; - } - if (*fmt == '0') - zero = 1; - state = 2; - case 2: - if (*fmt >= '0' && *fmt <= '9') { - width = width * 10 + *fmt++ - '0'; - break; - } - state = 3; - case 3: - if (*fmt == 'l') { - ++ll; - ++fmt; - break; - } - state = 4; - case 4: - switch (*fmt) { - case 's': - myvsnprintf_string(&dest,de,va_arg(ap,const char *)); - break; - case 'd': - switch (ll) { - case 0: - myvsnprintf_int(&dest,de,width,zero,neg,10,'a',va_arg(ap,int)); - break; - case 1: - myvsnprintf_int(&dest,de,width,zero,neg,10,'a',va_arg(ap,long)); - break; - case 2: - myvsnprintf_int(&dest,de,width,zero,neg,10,'a',va_arg(ap,longlong)); - break; - } - break; - case 'u': - switch (ll) { - case 0: - myvsnprintf_uint(&dest,de,width,zero,neg,10,'a',va_arg(ap,unsigned int)); - break; - case 1: - myvsnprintf_uint(&dest,de,width,zero,neg,10,'a',va_arg(ap,unsigned long)); - break; - case 2: - myvsnprintf_uint(&dest,de,width,zero,neg,10,'a',va_arg(ap,ulonglong)); - break; - } - break; - case 'x': - switch (ll) { - case 0: - myvsnprintf_uint(&dest,de,width,zero,neg,16,'a',va_arg(ap,unsigned int)); - break; - case 1: - myvsnprintf_uint(&dest,de,width,zero,neg,16,'a',va_arg(ap,unsigned long)); - break; - case 2: - myvsnprintf_uint(&dest,de,width,zero,neg,16,'a',va_arg(ap,ulonglong)); - break; - } - break; - case 'X': - switch (ll) { - case 0: - myvsnprintf_uint(&dest,de,width,zero,neg,16,'A',va_arg(ap,unsigned int)); - break; - case 1: - myvsnprintf_uint(&dest,de,width,zero,neg,16,'A',va_arg(ap,unsigned long)); - break; - case 2: - myvsnprintf_uint(&dest,de,width,zero,neg,16,'A',va_arg(ap,ulonglong)); - break; - } - break; - default: - break; - } - ++fmt; - state = 0; - break; - default: - state = 0; - break; - } - *dest = '\0'; -} - -static void errorjmp(MatroskaFile *mf,const char *fmt, ...) { - va_list ap; - - va_start(ap, fmt); - myvsnprintf(mf->errmsg,sizeof(mf->errmsg),fmt,ap); - va_end(ap); - - mf->flags |= MPF_ERROR; - - longjmp(mf->jb,1); -} - -/////////////////////////////////////////////////////////////////////////// -// arrays -static void *ArrayAlloc(MatroskaFile *mf,void **base, - unsigned *cur,unsigned *max,unsigned elem_size) -{ - if (*cur>=*max) { - void *np; - unsigned newsize = *max * 2; - if (newsize==0) - newsize = 1; - - np = mf->cache->memrealloc(mf->cache,*base,newsize*elem_size); - if (np==NULL) - errorjmp(mf,"Out of memory in ArrayAlloc"); - - *base = np; - *max = newsize; - } - - return (char*)*base + elem_size * (*cur)++; -} - -static void ArrayReleaseMemory(MatroskaFile *mf,void **base, - unsigned cur,unsigned *max,unsigned elem_size) -{ - if (cur<*max) { - void *np = mf->cache->memrealloc(mf->cache,*base,cur*elem_size); - *base = np; - *max = cur; - } -} - - -#define ASGET(f,s,name) ArrayAlloc((f),(void**)&(s)->name,&(s)->n##name,&(s)->n##name##Size,sizeof(*((s)->name))) -#define AGET(f,name) ArrayAlloc((f),(void**)&(f)->name,&(f)->n##name,&(f)->n##name##Size,sizeof(*((f)->name))) -#define ARELEASE(f,s,name) ArrayReleaseMemory((f),(void**)&(s)->name,(s)->n##name,&(s)->n##name##Size,sizeof(*((s)->name))) - -/////////////////////////////////////////////////////////////////////////// -// queues -static struct QueueEntry *QPut(struct Queue *q,struct QueueEntry *qe) { - if (q->tail) - q->tail->next = qe; - qe->next = NULL; - q->tail = qe; - if (q->head==NULL) - q->head = qe; - - return qe; -} - -static struct QueueEntry *QGet(struct Queue *q) { - struct QueueEntry *qe = q->head; - if (qe == NULL) - return NULL; - q->head = qe->next; - if (q->tail == qe) - q->tail = NULL; - return qe; -} - -static struct QueueEntry *QAlloc(MatroskaFile *mf) { - struct QueueEntry *qe,**qep; - if (mf->QFreeList == NULL) { - unsigned i; - - qep = AGET(mf,QBlocks); - - *qep = mf->cache->memalloc(mf->cache,QSEGSIZE * sizeof(*qe)); - if (*qep == NULL) - errorjmp(mf,"Ouf of memory"); - - qe = *qep; - - for (i=0;iQFreeList = qe; - } - - qe = mf->QFreeList; - mf->QFreeList = qe->next; - - return qe; -} - -static inline void QFree(MatroskaFile *mf,struct QueueEntry *qe) { - qe->next = mf->QFreeList; - mf->QFreeList = qe; -} - -// fill the buffer at current position -static void fillbuf(MatroskaFile *mf) { - int rd; - - // advance buffer pointers - mf->bufbase += mf->buflen; - mf->buflen = mf->bufpos = 0; - - // get the relevant page - rd = mf->cache->read(mf->cache, mf->bufbase, mf->inbuf, IBSZ); - if (rd<0) - errorjmp(mf,"I/O Error: %s",mf->cache->geterror(mf->cache)); - - mf->buflen = rd; -} - -// fill the buffer and return next char -static int nextbuf(MatroskaFile *mf) { - fillbuf(mf); - - if (mf->bufpos < mf->buflen) - return (unsigned char)(mf->inbuf[mf->bufpos++]); - - return EOF; -} - -static inline int readch(MatroskaFile *mf) { - return mf->bufpos < mf->buflen ? (unsigned char)(mf->inbuf[mf->bufpos++]) : nextbuf(mf); -} - -static inline ulonglong filepos(MatroskaFile *mf) { - return mf->bufbase + mf->bufpos; -} - -static void readbytes(MatroskaFile *mf,void *buffer,int len) { - char *cp = buffer; - int nb = mf->buflen - mf->bufpos; - - if (nb > len) - nb = len; - - memcpy(cp, mf->inbuf + mf->bufpos, nb); - mf->bufpos += nb; - len -= nb; - cp += nb; - - if (len>0) { - mf->bufbase += mf->buflen; - mf->bufpos = mf->buflen = 0; - - nb = mf->cache->read(mf->cache, mf->bufbase, cp, len); - if (nb<0) - errorjmp(mf,"I/O Error: %s",mf->cache->geterror(mf->cache)); - if (nb != len) - errorjmp(mf,"Short read: got %d bytes of %d",nb,len); - mf->bufbase += len; - } -} - -static void skipbytes(MatroskaFile *mf,ulonglong len) { - int nb = mf->buflen - mf->bufpos; - - if (nb > len) - nb = (int)len; - - mf->bufpos += nb; - len -= nb; - - if (len>0) { - mf->bufbase += mf->buflen; - mf->bufpos = mf->buflen = 0; - - mf->bufbase += len; - } -} - -static void seek(MatroskaFile *mf,ulonglong pos) { - // see if pos is inside buffer - if (pos>=mf->bufbase && posbufbase+mf->buflen) - mf->bufpos = (unsigned)(pos - mf->bufbase); - else { - // invalidate buffer and set pointer - mf->bufbase = pos; - mf->buflen = mf->bufpos = 0; - } -} - -/////////////////////////////////////////////////////////////////////////// -// floating point -static inline MKFLOAT mkfi(int i) { -#ifdef MATROSKA_INTEGER_ONLY - MKFLOAT f; - f.v = (longlong)i << 32; - return f; -#else - return i; -#endif -} - -static inline longlong mul3(MKFLOAT scale,longlong tc) { -#ifdef MATROSKA_INTEGER_ONLY - // x1 x0 - // y1 y0 - // -------------- - // x0*y0 - // x1*y0 - // x0*y1 - // x1*y1 - // -------------- - // .. r1 r0 .. - // - // r = ((x0*y0) >> 32) + (x1*y0) + (x0*y1) + ((x1*y1) << 32) - unsigned x0,x1,y0,y1; - ulonglong p; - char sign = 0; - - if (scale.v < 0) - sign = !sign, scale.v = -scale.v; - if (tc < 0) - sign = !sign, tc = -tc; - - x0 = (unsigned)scale.v; - x1 = (unsigned)((ulonglong)scale.v >> 32); - y0 = (unsigned)tc; - y1 = (unsigned)((ulonglong)tc >> 32); - - p = (ulonglong)x0*y0 >> 32; - p += (ulonglong)x0*y1; - p += (ulonglong)x1*y0; - p += (ulonglong)(x1*y1) << 32; - - return p; -#else - return (longlong)(scale * tc); -#endif -} - -/////////////////////////////////////////////////////////////////////////// -// EBML support -static int readID(MatroskaFile *mf) { - int c1,c2,c3,c4; - - c1 = readch(mf); - if (c1 == EOF) - return EOF; - - if (c1 & 0x80) - return c1; - - if ((c1 & 0xf0) == 0) - errorjmp(mf,"Invalid first byte of EBML ID: %02X",c1); - - c2 = readch(mf); - if (c2 == EOF) -fail: - errorjmp(mf,"Got EOF while reading EBML ID"); - - if ((c1 & 0xc0) == 0x40) - return (c1<<8) | c2; - - c3 = readch(mf); - if (c3 == EOF) - goto fail; - - if ((c1 & 0xe0) == 0x20) - return (c1<<16) | (c2<<8) | c3; - - c4 = readch(mf); - if (c4 == EOF) - goto fail; - - if ((c1 & 0xf0) == 0x10) - return (c1<<24) | (c2<<16) | (c3<<8) | c4; - - return 0; // NOT REACHED -} - -static ulonglong readVLUIntImp(MatroskaFile *mf,int *mask) { - int c,d,m; - ulonglong v = 0; - - c = readch(mf); - if (c == EOF) - return 0; // XXX should errorjmp()? - - if (c == 0) - errorjmp(mf,"Invalid first byte of EBML integer: 0"); - - for (m=0;;++m) { - if (c & (0x80 >> m)) { - c &= 0x7f >> m; - if (mask) - *mask = m; - return v | ((ulonglong)c << m*8); - } - d = readch(mf); - if (d == EOF) - errorjmp(mf,"Got EOF while reading EBML unsigned integer"); - v = (v<<8) | d; - } - // NOT REACHED -} - -static inline ulonglong readVLUInt(MatroskaFile *mf) { - return readVLUIntImp(mf,NULL); -} - -static ulonglong readSizeUnspec(MatroskaFile *mf) { - int m; - ulonglong v = readVLUIntImp(mf,&m); - - // see if it's unspecified - if (v == (MAXU64 >> (57-m*7))) - return MAXU64; - - return v; -} - -static ulonglong readSize(MatroskaFile *mf) { - ulonglong v = readSizeUnspec(mf); - - // see if it's unspecified - if (v == MAXU64) - errorjmp(mf,"Unspecified element size is not supported here."); - - return v; -} - -static inline longlong readVLSInt(MatroskaFile *mf) { - static longlong bias[8] = { (ONE<<6)-1, (ONE<<13)-1, (ONE<<20)-1, (ONE<<27)-1, - (ONE<<34)-1, (ONE<<41)-1, (ONE<<48)-1, (ONE<<55)-1 }; - - int m; - longlong v = readVLUIntImp(mf,&m); - - return v - bias[m]; -} - -static ulonglong readUInt(MatroskaFile *mf,unsigned int len) { - int c; - unsigned int m = len; - ulonglong v = 0; - - if (len==0) - return v; - if (len>8) - errorjmp(mf,"Unsupported integer size in readUInt: %u",len); - - do { - c = readch(mf); - if (c == EOF) - errorjmp(mf,"Got EOF while reading EBML unsigned integer"); - v = (v<<8) | c; - } while (--m); - - return v; -} - -static inline longlong readSInt(MatroskaFile *mf,unsigned int len) { - longlong v = readUInt(mf,(unsigned)len); - int s = 64 - (len<<3); - return (v << s) >> s; -} - -static MKFLOAT readFloat(MatroskaFile *mf,unsigned int len) { -#ifdef MATROSKA_INTEGER_ONLY - MKFLOAT f; - int shift; -#else - union { - unsigned int ui; - ulonglong ull; - float f; - double d; - } u; -#endif - - if (len!=4 && len!=8) - errorjmp(mf,"Invalid float size in readFloat: %u",len); - -#ifdef MATROSKA_INTEGER_ONLY - if (len == 4) { - unsigned ui = (unsigned)readUInt(mf,(unsigned)len); - f.v = (ui & 0x7fffff) | 0x800000; - if (ui & 0x80000000) - f.v = -f.v; - shift = (ui >> 23) & 0xff; - if (shift == 0) // assume 0 -zero: - shift = 0, f.v = 0; - else if (shift == 255) -inf: - if (ui & 0x80000000) - f.v = LL(0x8000000000000000); - else - f.v = LL(0x7fffffffffffffff); - else { - shift += -127 + 9; - if (shift > 39) - goto inf; -shift: - if (shift < 0) - f.v = f.v >> -shift; - else if (shift > 0) - f.v = f.v << shift; - } - } else if (len == 8) { - ulonglong ui = readUInt(mf,(unsigned)len); - f.v = (ui & LL(0xfffffffffffff)) | LL(0x10000000000000); - if (ui & 0x80000000) - f.v = -f.v; - shift = (int)((ui >> 52) & 0x7ff); - if (shift == 0) // assume 0 - goto zero; - else if (shift == 2047) - goto inf; - else { - shift += -1023 - 20; - if (shift > 10) - goto inf; - goto shift; - } - } - - return f; -#else - if (len==4) { - u.ui = (unsigned int)readUInt(mf,(unsigned)len); - return u.f; - } - - if (len==8) { - u.ull = readUInt(mf,(unsigned)len); - return u.d; - } - - return 0; -#endif -} - -static void readString(MatroskaFile *mf,ulonglong len,char *buffer,int buflen) { - int nread; - - if (buflen<1) - errorjmp(mf,"Invalid buffer size in readString: %d",buflen); - - nread = buflen - 1; - - if (nread > len) - nread = (int)len; - - readbytes(mf,buffer,nread); - len -= nread; - - if (len>0) - skipbytes(mf,len); - - buffer[nread] = '\0'; -} - -static void readLangCC(MatroskaFile *mf, ulonglong len, char lcc[4]) { - unsigned todo = len > 3 ? 3 : (int)len; - - lcc[0] = lcc[1] = lcc[2] = lcc[3] = 0; - readbytes(mf, lcc, todo); - skipbytes(mf, len - todo); -} - -/////////////////////////////////////////////////////////////////////////// -// file parser -#define FOREACH2(f,tl,clid) \ - { \ - ulonglong tmplen = (tl); \ - { \ - ulonglong start = filepos(f); \ - ulonglong cur,len; \ - int id; \ - for (;;) { \ - cur = filepos(mf); \ - if (tmplen != MAXU64 && cur == start + tmplen) \ - break; \ - id = readID(f); \ - if (id==EOF) \ - errorjmp(mf,"Unexpected EOF while reading EBML container"); \ - len = id == clid ? readSizeUnspec(mf) : readSize(mf); \ - switch (id) { - -#define FOREACH(f,tl) FOREACH2(f,tl,EOF) - -#define RESTART() (tmplen=len),(start=cur) - -#define ENDFOR1(f) \ - default: \ - skipbytes(f,len); \ - break; \ - } -#define ENDFOR2() \ - } \ - } \ - } - -#define ENDFOR(f) ENDFOR1(f) ENDFOR2() - -#define myalloca(f,c) alloca(c) -#define STRGETF(f,v,len,func) \ - { \ - char *TmpVal; \ - unsigned TmpLen = (len)>MAX_STRING_LEN ? MAX_STRING_LEN : (unsigned)(len); \ - TmpVal = func(f->cache,TmpLen+1); \ - if (TmpVal == NULL) \ - errorjmp(mf,"Out of memory"); \ - readString(f,len,TmpVal,TmpLen+1); \ - (v) = TmpVal; \ - } - -#define STRGETA(f,v,len) STRGETF(f,v,len,myalloca) -#define STRGETM(f,v,len) STRGETF(f,v,len,f->cache->memalloc) - -static int IsWritingApp(MatroskaFile *mf,const char *str) { - const char *cp = mf->Seg.WritingApp; - if (!cp) - return 0; - - while (*str && *str++==*cp++) ; - - return !*str; -} - -static void parseEBML(MatroskaFile *mf,ulonglong toplen) { - ulonglong v; - char buf[32]; - - FOREACH(mf,toplen) - case 0x4286: // Version - v = readUInt(mf,(unsigned)len); - break; - case 0x42f7: // ReadVersion - v = readUInt(mf,(unsigned)len); - if (v > EBML_VERSION) - errorjmp(mf,"File requires version %d EBML parser",(int)v); - break; - case 0x42f2: // MaxIDLength - v = readUInt(mf,(unsigned)len); - if (v > EBML_MAX_ID_LENGTH) - errorjmp(mf,"File has identifiers longer than %d",(int)v); - break; - case 0x42f3: // MaxSizeLength - v = readUInt(mf,(unsigned)len); - if (v > EBML_MAX_SIZE_LENGTH) - errorjmp(mf,"File has integers longer than %d",(int)v); - break; - case 0x4282: // DocType - readString(mf,len,buf,sizeof(buf)); - if (strcmp(buf,MATROSKA_DOCTYPE)) - errorjmp(mf,"Unsupported DocType: %s",buf); - break; - case 0x4287: // DocTypeVersion - v = readUInt(mf,(unsigned)len); - break; - case 0x4285: // DocTypeReadVersion - v = readUInt(mf,(unsigned)len); - if (v > MATROSKA_VERSION) - errorjmp(mf,"File requires version %d Matroska parser",(int)v); - break; - ENDFOR(mf); -} - -static void parseSeekEntry(MatroskaFile *mf,ulonglong toplen) { - int seekid = 0; - ulonglong pos = (ulonglong)-1; - - FOREACH(mf,toplen) - case 0x53ab: // SeekID - if (len>EBML_MAX_ID_LENGTH) - errorjmp(mf,"Invalid ID size in parseSeekEntry: %d\n",(int)len); - seekid = (int)readUInt(mf,(unsigned)len); - break; - case 0x53ac: // SeekPos - pos = readUInt(mf,(unsigned)len); - break; - ENDFOR(mf); - - if (pos == (ulonglong)-1) - errorjmp(mf,"Invalid element position in parseSeekEntry"); - - pos += mf->pSegment; - switch (seekid) { - case 0x114d9b74: // next SeekHead - if (mf->pSeekHead) - errorjmp(mf,"SeekHead contains more than one SeekHead pointer"); - mf->pSeekHead = pos; - break; - case 0x1549a966: // SegmentInfo - mf->pSegmentInfo = pos; - break; - case 0x1f43b675: // Cluster - if (!mf->pCluster) - mf->pCluster = pos; - break; - case 0x1654ae6b: // Tracks - mf->pTracks = pos; - break; - case 0x1c53bb6b: // Cues - mf->pCues = pos; - break; - case 0x1941a469: // Attachments - mf->pAttachments = pos; - break; - case 0x1043a770: // Chapters - mf->pChapters = pos; - break; - case 0x1254c367: // tags - mf->pTags = pos; - break; - } -} - -static void parseSeekHead(MatroskaFile *mf,ulonglong toplen) { - FOREACH(mf,toplen) - case 0x4dbb: - parseSeekEntry(mf,len); - break; - ENDFOR(mf); -} - -static void parseSegmentInfo(MatroskaFile *mf,ulonglong toplen) { - MKFLOAT duration = mkfi(0); - - if (mf->seen.SegmentInfo) { - skipbytes(mf,toplen); - return; - } - - mf->seen.SegmentInfo = 1; - mf->Seg.TimecodeScale = 1000000; // Default value - - FOREACH(mf,toplen) - case 0x73a4: // SegmentUID - if (len!=sizeof(mf->Seg.UID)) - errorjmp(mf,"SegmentUID size is not %d bytes",mf->Seg.UID); - readbytes(mf,mf->Seg.UID,sizeof(mf->Seg.UID)); - break; - case 0x7384: // SegmentFilename - STRGETM(mf,mf->Seg.Filename,len); - break; - case 0x3cb923: // PrevUID - if (len!=sizeof(mf->Seg.PrevUID)) - errorjmp(mf,"PrevUID size is not %d bytes",mf->Seg.PrevUID); - readbytes(mf,mf->Seg.PrevUID,sizeof(mf->Seg.PrevUID)); - break; - case 0x3c83ab: // PrevFilename - STRGETM(mf,mf->Seg.PrevFilename,len); - break; - case 0x3eb923: // NextUID - if (len!=sizeof(mf->Seg.NextUID)) - errorjmp(mf,"NextUID size is not %d bytes",mf->Seg.NextUID); - readbytes(mf,mf->Seg.NextUID,sizeof(mf->Seg.NextUID)); - break; - case 0x3e83bb: // NextFilename - STRGETM(mf,mf->Seg.NextFilename,len); - break; - case 0x2ad7b1: // TimecodeScale - mf->Seg.TimecodeScale = readUInt(mf,(unsigned)len); - if (mf->Seg.TimecodeScale == 0) - errorjmp(mf,"Segment timecode scale is zero"); - break; - case 0x4489: // Duration - duration = readFloat(mf,(unsigned)len); - break; - case 0x4461: // DateUTC - mf->Seg.DateUTC = readUInt(mf,(unsigned)len); - mf->Seg.DateUTCValid = 1; - break; - case 0x7ba9: // Title - STRGETM(mf,mf->Seg.Title,len); - break; - case 0x4d80: // MuxingApp - STRGETM(mf,mf->Seg.MuxingApp,len); - break; - case 0x5741: // WritingApp - STRGETM(mf,mf->Seg.WritingApp,len); - break; - ENDFOR(mf); - - mf->Seg.Duration = mul3(duration,mf->Seg.TimecodeScale); -} - -static void parseFirstCluster(MatroskaFile *mf,ulonglong toplen) { - int seenTimecode = 0, seenBlock = 0; - longlong tc; - ulonglong clstart = filepos(mf); - - mf->seen.Cluster = 1; - mf->firstTimecode = 0; - - FOREACH2(mf,toplen,0x1f43b675) - case 0xe7: // Timecode - tc = readUInt(mf,(unsigned)len); - if (!seenTimecode) { - seenTimecode = 1; - mf->firstTimecode += tc; - } - - if (seenBlock) { -out: - if (toplen != MAXU64) - skipbytes(mf,clstart + toplen - filepos(mf)); - else if (len != MAXU64) - skipbytes(mf,cur + len - filepos(mf)); - return; - } - break; - case 0xa3: // BlockEx - readVLUInt(mf); // track number - tc = readSInt(mf, 2); - if (!seenBlock) { - seenBlock = 1; - mf->firstTimecode += tc; - } - - if (seenTimecode) - goto out; - break; - case 0xa0: // BlockGroup - FOREACH(mf,len) - case 0xa1: // Block - readVLUInt(mf); // track number - tc = readSInt(mf,2); - if (!seenBlock) { - seenBlock = 1; - mf->firstTimecode += tc; - } - - if (seenTimecode) - goto out; - ENDFOR(mf); - break; - case 0x1f43b675: - return; - ENDFOR(mf); -} - -static void parseVideoInfo(MatroskaFile *mf,ulonglong toplen,struct TrackInfo *ti) { - ulonglong v; - char dW = 0, dH = 0; - - FOREACH(mf,toplen) - case 0x9a: // FlagInterlaced - ti->AV.Video.Interlaced = readUInt(mf,(unsigned)len)!=0; - break; - case 0x53b8: // StereoMode - v = readUInt(mf,(unsigned)len); - if (v>3) - errorjmp(mf,"Invalid stereo mode"); - ti->AV.Video.StereoMode = (unsigned char)v; - break; - case 0xb0: // PixelWidth - v = readUInt(mf,(unsigned)len); - if (v>0xffffffff) - errorjmp(mf,"PixelWidth is too large"); - ti->AV.Video.PixelWidth = (unsigned)v; - if (!dW) - ti->AV.Video.DisplayWidth = ti->AV.Video.PixelWidth; - break; - case 0xba: // PixelHeight - v = readUInt(mf,(unsigned)len); - if (v>0xffffffff) - errorjmp(mf,"PixelHeight is too large"); - ti->AV.Video.PixelHeight = (unsigned)v; - if (!dH) - ti->AV.Video.DisplayHeight = ti->AV.Video.PixelHeight; - break; - case 0x54b0: // DisplayWidth - v = readUInt(mf,(unsigned)len); - if (v>0xffffffff) - errorjmp(mf,"DisplayWidth is too large"); - ti->AV.Video.DisplayWidth = (unsigned)v; - dW = 1; - break; - case 0x54ba: // DisplayHeight - v = readUInt(mf,(unsigned)len); - if (v>0xffffffff) - errorjmp(mf,"DisplayHeight is too large"); - ti->AV.Video.DisplayHeight = (unsigned)v; - dH = 1; - break; - case 0x54b2: // DisplayUnit - v = readUInt(mf,(unsigned)len); - if (v>2) - errorjmp(mf,"Invalid DisplayUnit: %d",(int)v); - ti->AV.Video.DisplayUnit = (unsigned char)v; - break; - case 0x54b3: // AspectRatioType - v = readUInt(mf,(unsigned)len); - if (v>2) - errorjmp(mf,"Invalid AspectRatioType: %d",(int)v); - ti->AV.Video.AspectRatioType = (unsigned char)v; - break; - case 0x54aa: // PixelCropBottom - v = readUInt(mf,(unsigned)len); - if (v>0xffffffff) - errorjmp(mf,"PixelCropBottom is too large"); - ti->AV.Video.CropB = (unsigned)v; - break; - case 0x54bb: // PixelCropTop - v = readUInt(mf,(unsigned)len); - if (v>0xffffffff) - errorjmp(mf,"PixelCropTop is too large"); - ti->AV.Video.CropT = (unsigned)v; - break; - case 0x54cc: // PixelCropLeft - v = readUInt(mf,(unsigned)len); - if (v>0xffffffff) - errorjmp(mf,"PixelCropLeft is too large"); - ti->AV.Video.CropL = (unsigned)v; - break; - case 0x54dd: // PixelCropRight - v = readUInt(mf,(unsigned)len); - if (v>0xffffffff) - errorjmp(mf,"PixelCropRight is too large"); - ti->AV.Video.CropR = (unsigned)v; - break; - case 0x2eb524: // ColourSpace - ti->AV.Video.ColourSpace = (unsigned)readUInt(mf,4); - break; - case 0x2fb523: // GammaValue - ti->AV.Video.GammaValue = readFloat(mf,(unsigned)len); - break; - ENDFOR(mf); -} - -static void parseAudioInfo(MatroskaFile *mf,ulonglong toplen,struct TrackInfo *ti) { - ulonglong v; - - FOREACH(mf,toplen) - case 0xb5: // SamplingFrequency - ti->AV.Audio.SamplingFreq = readFloat(mf,(unsigned)len); - break; - case 0x78b5: // OutputSamplingFrequency - ti->AV.Audio.OutputSamplingFreq = readFloat(mf,(unsigned)len); - break; - case 0x9f: // Channels - v = readUInt(mf,(unsigned)len); - if (v<1 || v>255) - errorjmp(mf,"Invalid Channels value"); - ti->AV.Audio.Channels = (unsigned char)v; - break; - case 0x7d7b: // ChannelPositions - skipbytes(mf,len); - break; - case 0x6264: // BitDepth - v = readUInt(mf,(unsigned)len); -#if 0 - if ((v<1 || v>255) && !IsWritingApp(mf,"AVI-Mux GUI")) - errorjmp(mf,"Invalid BitDepth: %d",(int)v); -#endif - ti->AV.Audio.BitDepth = (unsigned char)v; - break; - ENDFOR(mf); - - if (ti->AV.Audio.Channels == 0) - ti->AV.Audio.Channels = 1; - if (mkv_TruncFloat(ti->AV.Audio.SamplingFreq) == 0) - ti->AV.Audio.SamplingFreq = mkfi(8000); - if (mkv_TruncFloat(ti->AV.Audio.OutputSamplingFreq)==0) - ti->AV.Audio.OutputSamplingFreq = ti->AV.Audio.SamplingFreq; -} - -static void CopyStr(char **src,char **dst) { - size_t l; - - if (!*src) - return; - - l = strlen(*src)+1; - memcpy(*dst,*src,l); - *src = *dst; - *dst += l; -} - -static void parseTrackEntry(MatroskaFile *mf,ulonglong toplen) { - struct TrackInfo t,*tp,**tpp; - ulonglong v; - char *cp = NULL, *cs = NULL; - size_t cplen = 0, cslen = 0, cpadd = 0; - unsigned CompScope, num_comp = 0; - - if (mf->nTracks >= MAX_TRACKS) - errorjmp(mf,"Too many tracks."); - - // clear track info - memset(&t,0,sizeof(t)); - - // fill default values - t.Enabled = 1; - t.Default = 1; - t.Lacing = 1; - t.TimecodeScale = mkfi(1); - t.DecodeAll = 1; - - FOREACH(mf,toplen) - case 0xd7: // TrackNumber - v = readUInt(mf,(unsigned)len); - if (v>255) - errorjmp(mf,"Track number is >255 (%d)",(int)v); - t.Number = (unsigned char)v; - break; - case 0x73c5: // TrackUID - t.UID = readUInt(mf,(unsigned)len); - break; - case 0x83: // TrackType - v = readUInt(mf,(unsigned)len); - if (v<1 || v>254) - errorjmp(mf,"Invalid track type: %d",(int)v); - t.Type = (unsigned char)v; - break; - case 0xb9: // Enabled - t.Enabled = readUInt(mf,(unsigned)len)!=0; - break; - case 0x88: // Default - t.Default = readUInt(mf,(unsigned)len)!=0; - break; - case 0x9c: // Lacing - t.Lacing = readUInt(mf,(unsigned)len)!=0; - break; - case 0x6de7: // MinCache - v = readUInt(mf,(unsigned)len); - if (v > 0xffffffff) - errorjmp(mf,"MinCache is too large"); - t.MinCache = (unsigned)v; - break; - case 0x6df8: // MaxCache - v = readUInt(mf,(unsigned)len); - if (v > 0xffffffff) - errorjmp(mf,"MaxCache is too large"); - t.MaxCache = (unsigned)v; - break; - case 0x23e383: // DefaultDuration - t.DefaultDuration = readUInt(mf,(unsigned)len); - break; - case 0x23314f: // TrackTimecodeScale - t.TimecodeScale = readFloat(mf,(unsigned)len); - break; - case 0x55ee: // MaxBlockAdditionID - t.MaxBlockAdditionID = (unsigned)readUInt(mf,(unsigned)len); - break; - case 0x536e: // Name - if (t.Name) - errorjmp(mf,"Duplicate Track Name"); - STRGETA(mf,t.Name,len); - break; - case 0x22b59c: // Language - readLangCC(mf, len, t.Language); - break; - case 0x86: // CodecID - if (t.CodecID) - errorjmp(mf,"Duplicate CodecID"); - STRGETA(mf,t.CodecID,len); - break; - case 0x63a2: // CodecPrivate - if (cp) - errorjmp(mf,"Duplicate CodecPrivate"); - if (len>262144) // 256KB - errorjmp(mf,"CodecPrivate is too large: %d",(int)len); - cplen = (unsigned)len; - cp = alloca(cplen); - readbytes(mf,cp,(int)cplen); - break; - case 0x258688: // CodecName - skipbytes(mf,len); - break; - case 0x3a9697: // CodecSettings - skipbytes(mf,len); - break; - case 0x3b4040: // CodecInfoURL - skipbytes(mf,len); - break; - case 0x26b240: // CodecDownloadURL - skipbytes(mf,len); - break; - case 0xaa: // CodecDecodeAll - t.DecodeAll = readUInt(mf,(unsigned)len)!=0; - break; - case 0x6fab: // TrackOverlay - v = readUInt(mf,(unsigned)len); - if (v>255) - errorjmp(mf,"Track number in TrackOverlay is too large: %d",(int)v); - t.TrackOverlay = (unsigned char)v; - break; - case 0xe0: // VideoInfo - parseVideoInfo(mf,len,&t); - break; - case 0xe1: // AudioInfo - parseAudioInfo(mf,len,&t); - break; - case 0x6d80: // ContentEncodings - FOREACH(mf,len) - case 0x6240: // ContentEncoding - // fill in defaults - t.CompEnabled = 1; - t.CompMethod = COMP_ZLIB; - CompScope = 1; - if (++num_comp > 1) - return; // only one compression layer supported - FOREACH(mf,len) - case 0x5031: // ContentEncodingOrder - readUInt(mf,(unsigned)len); - break; - case 0x5032: // ContentEncodingScope - CompScope = (unsigned)readUInt(mf,(unsigned)len); - break; - case 0x5033: // ContentEncodingType - if (readUInt(mf,(unsigned)len) != 0) - return; // encryption is not supported - break; - case 0x5034: // ContentCompression - FOREACH(mf,len) - case 0x4254: // ContentCompAlgo - v = readUInt(mf,(unsigned)len); - t.CompEnabled = 1; - switch (v) { - case 0: // Zlib - t.CompMethod = COMP_ZLIB; - break; - case 3: // prepend fixed data - t.CompMethod = COMP_PREPEND; - break; - default: - return; // unsupported compression, skip track - } - break; - case 0x4255: // ContentCompSettings - if (len > 256) - return; - cslen = (unsigned)len; - cs = alloca(cslen); - readbytes(mf, cs, (int)cslen); - break; - ENDFOR(mf); - break; - // TODO Implement Encryption/Signatures - ENDFOR(mf); - break; - ENDFOR(mf); - break; - ENDFOR(mf); - - // validate track info - if (!t.CodecID) - errorjmp(mf,"Track has no Codec ID"); - - if (t.UID != 0) { - unsigned i; - for (i = 0; i < mf->nTracks; ++i) - if (mf->Tracks[i]->UID == t.UID) // duplicate track entry - return; - } - -#ifdef MATROSKA_COMPRESSION_SUPPORT - // handle compressed CodecPrivate - if (t.CompEnabled && t.CompMethod == COMP_ZLIB && (CompScope & 2) && cplen > 0) { - z_stream zs; - Bytef tmp[64], *ncp; - int code; - uLong ncplen; - - memset(&zs,0,sizeof(zs)); - if (inflateInit(&zs) != Z_OK) - errorjmp(mf, "inflateInit failed"); - - zs.next_in = (Bytef *)cp; - zs.avail_in = (uInt)cplen; - - do { - zs.next_out = tmp; - zs.avail_out = sizeof(tmp); - - code = inflate(&zs, Z_NO_FLUSH); - } while (code == Z_OK); - - if (code != Z_STREAM_END) - errorjmp(mf, "invalid compressed data in CodecPrivate"); - - ncplen = zs.total_out; - ncp = alloca(ncplen); - - inflateReset(&zs); - - zs.next_in = (Bytef *)cp; - zs.avail_in = (uInt)cplen; - zs.next_out = ncp; - zs.avail_out = ncplen; - - if (inflate(&zs, Z_FINISH) != Z_STREAM_END) - errorjmp(mf, "inflate failed"); - - inflateEnd(&zs); - - cp = (char *)ncp; - cplen = ncplen; - } -#endif - - if (t.CompEnabled && !(CompScope & 1)) { - t.CompEnabled = 0; - cslen = 0; - } - - // allocate new track - tpp = AGET(mf,Tracks); - - // copy strings - if (t.Name) - cpadd += strlen(t.Name)+1; - if (t.CodecID) - cpadd += strlen(t.CodecID)+1; - - tp = mf->cache->memalloc(mf->cache,sizeof(*tp) + cplen + cslen + cpadd); - if (tp == NULL) - errorjmp(mf,"Out of memory"); - - memcpy(tp,&t,sizeof(*tp)); - if (cplen) { - tp->CodecPrivate = tp+1; - tp->CodecPrivateSize = (unsigned)cplen; - memcpy(tp->CodecPrivate,cp,cplen); - } - if (cslen) { - tp->CompMethodPrivate = (char *)(tp+1) + cplen; - tp->CompMethodPrivateSize = (unsigned)cslen; - memcpy(tp->CompMethodPrivate, cs, cslen); - } - - cp = (char*)(tp+1) + cplen + cslen; - CopyStr(&tp->Name,&cp); - CopyStr(&tp->CodecID,&cp); - - // set default language - if (!tp->Language[0]) - memcpy(tp->Language, "eng", 4); - - *tpp = tp; -} - -static void parseTracks(MatroskaFile *mf,ulonglong toplen) { - mf->seen.Tracks = 1; - FOREACH(mf,toplen) - case 0xae: // TrackEntry - parseTrackEntry(mf,len); - break; - ENDFOR(mf); -} - -static void addCue(MatroskaFile *mf,ulonglong pos,ulonglong timecode) { - struct Cue *cc = AGET(mf,Cues); - cc->Time = timecode; - cc->Position = pos; - cc->Track = 0; - cc->Block = 0; -} - -static void fixupCues(MatroskaFile *mf) { - // adjust cues, shift cues if file does not start at 0 - unsigned i; - longlong adjust = mf->firstTimecode * mf->Seg.TimecodeScale; - - for (i=0;inCues;++i) { - mf->Cues[i].Time *= mf->Seg.TimecodeScale; - mf->Cues[i].Time -= adjust; - } -} - -static void parseCues(MatroskaFile *mf,ulonglong toplen) { - jmp_buf jb; - ulonglong v; - struct Cue cc; - unsigned i,j,k; - - mf->seen.Cues = 1; - mf->nCues = 0; - cc.Block = 0; - - memcpy(&jb,&mf->jb,sizeof(jb)); - - if (setjmp(mf->jb)) { - memcpy(&mf->jb,&jb,sizeof(jb)); - mf->nCues = 0; - mf->seen.Cues = 0; - return; - } - - FOREACH(mf,toplen) - case 0xbb: // CuePoint - FOREACH(mf,len) - case 0xb3: // CueTime - cc.Time = readUInt(mf,(unsigned)len); - break; - case 0xb7: // CueTrackPositions - FOREACH(mf,len) - case 0xf7: // CueTrack - v = readUInt(mf,(unsigned)len); - if (v>255) - errorjmp(mf,"CueTrack points to an invalid track: %d",(int)v); - cc.Track = (unsigned char)v; - break; - case 0xf1: // CueClusterPosition - cc.Position = readUInt(mf,(unsigned)len); - break; - case 0x5378: // CueBlockNumber - cc.Block = readUInt(mf,(unsigned)len); - break; - case 0xea: // CodecState - readUInt(mf,(unsigned)len); - break; - case 0xdb: // CueReference - FOREACH(mf,len) - case 0x96: // CueRefTime - readUInt(mf,(unsigned)len); - break; - case 0x97: // CueRefCluster - readUInt(mf,(unsigned)len); - break; - case 0x535f: // CueRefNumber - readUInt(mf,(unsigned)len); - break; - case 0xeb: // CueRefCodecState - readUInt(mf,(unsigned)len); - break; - ENDFOR(mf); - break; - ENDFOR(mf); - break; - ENDFOR(mf); - - if (mf->nCues == 0 && mf->pCluster - mf->pSegment != cc.Position) - addCue(mf,mf->pCluster - mf->pSegment,mf->firstTimecode); - - memcpy(AGET(mf,Cues),&cc,sizeof(cc)); - break; - ENDFOR(mf); - - memcpy(&mf->jb,&jb,sizeof(jb)); - - ARELEASE(mf,mf,Cues); - - // bubble sort the cues and fuck the losers that write unordered cues - if (mf->nCues > 0) - for (i = mf->nCues - 1, k = 1; i > 0 && k > 0; --i) - for (j = k = 0; j < i; ++j) - if (mf->Cues[j].Time > mf->Cues[j+1].Time) { - struct Cue tmp = mf->Cues[j+1]; - mf->Cues[j+1] = mf->Cues[j]; - mf->Cues[j] = tmp; - ++k; - } -} - -static void parseAttachment(MatroskaFile *mf,ulonglong toplen) { - struct Attachment a,*pa; - - memset(&a,0,sizeof(a)); - FOREACH(mf,toplen) - case 0x467e: // Description - STRGETA(mf,a.Description,len); - break; - case 0x466e: // Name - STRGETA(mf,a.Name,len); - break; - case 0x4660: // MimeType - STRGETA(mf,a.MimeType,len); - break; - case 0x46ae: // UID - a.UID = readUInt(mf,(unsigned)len); - break; - case 0x465c: // Data - a.Position = filepos(mf); - a.Length = len; - skipbytes(mf,len); - break; - ENDFOR(mf); - - if (!a.Position) - return; - - pa = AGET(mf,Attachments); - memcpy(pa,&a,sizeof(a)); - - if (a.Description) - pa->Description = mystrdup(mf->cache,a.Description); - if (a.Name) - pa->Name = mystrdup(mf->cache,a.Name); - if (a.MimeType) - pa->MimeType = mystrdup(mf->cache,a.MimeType); -} - -static void parseAttachments(MatroskaFile *mf,ulonglong toplen) { - mf->seen.Attachments = 1; - - FOREACH(mf,toplen) - case 0x61a7: // AttachedFile - parseAttachment(mf,len); - break; - ENDFOR(mf); -} - -static void parseChapter(MatroskaFile *mf,ulonglong toplen,struct Chapter *parent) { - struct ChapterDisplay *disp; - struct ChapterProcess *proc; - struct ChapterCommand *cmd; - struct Chapter *ch = ASGET(mf,parent,Children); - - memset(ch,0,sizeof(*ch)); - - ch->Enabled = 1; - - FOREACH(mf,toplen) - case 0x73c4: // ChapterUID - ch->UID = readUInt(mf,(unsigned)len); - break; - case 0x6e67: // ChapterSegmentUID - if (len != sizeof(ch->SegmentUID)) - skipbytes(mf, len); - else - readbytes(mf, ch->SegmentUID, sizeof(ch->SegmentUID)); - break; - case 0x91: // ChapterTimeStart - ch->Start = readUInt(mf,(unsigned)len); - break; - case 0x92: // ChapterTimeEnd - ch->End = readUInt(mf,(unsigned)len); - break; - case 0x98: // ChapterFlagHidden - ch->Hidden = readUInt(mf,(unsigned)len)!=0; - break; - case 0x4598: // ChapterFlagEnabled - ch->Enabled = readUInt(mf,(unsigned)len)!=0; - break; - case 0x8f: // ChapterTrack - FOREACH(mf,len) - case 0x89: // ChapterTrackNumber - *(ulonglong*)(ASGET(mf,ch,Tracks)) = readUInt(mf,(unsigned)len); - break; - ENDFOR(mf); - break; - case 0x80: // ChapterDisplay - disp = NULL; - - FOREACH(mf,len) - case 0x85: // ChapterString - if (disp==NULL) { - disp = ASGET(mf,ch,Display); - memset(disp, 0, sizeof(*disp)); - } - if (disp->String) - skipbytes(mf,len); // Ignore duplicate string - else - STRGETM(mf,disp->String,len); - break; - case 0x437c: // ChapterLanguage - if (disp==NULL) { - disp = ASGET(mf,ch,Display); - memset(disp, 0, sizeof(*disp)); - } - readLangCC(mf, len, disp->Language); - break; - case 0x437e: // ChapterCountry - if (disp==NULL) { - disp = ASGET(mf,ch,Display); - memset(disp, 0, sizeof(*disp)); - } - readLangCC(mf, len, disp->Country); - break; - ENDFOR(mf); - - if (disp && !disp->String) - --ch->nDisplay; - break; - case 0x6944: // ChapProcess - proc = NULL; - - FOREACH(mf,len) - case 0x6955: // ChapProcessCodecID - if (proc == NULL) { - proc = ASGET(mf, ch, Process); - memset(proc, 0, sizeof(*proc)); - } - proc->CodecID = (unsigned)readUInt(mf,(unsigned)len); - break; - case 0x450d: // ChapProcessPrivate - if (proc == NULL) { - proc = ASGET(mf, ch, Process); - memset(proc, 0, sizeof(*proc)); - } - if (proc->CodecPrivate) - skipbytes(mf, len); - else { - proc->CodecPrivateLength = (unsigned)len; - STRGETM(mf,proc->CodecPrivate,len); - } - break; - case 0x6911: // ChapProcessCommand - if (proc == NULL) { - proc = ASGET(mf, ch, Process); - memset(proc, 0, sizeof(*proc)); - } - - cmd = NULL; - - FOREACH(mf,len) - case 0x6922: // ChapterCommandTime - if (cmd == NULL) { - cmd = ASGET(mf,proc,Commands); - memset(cmd, 0, sizeof(*cmd)); - } - cmd->Time = (unsigned)readUInt(mf,(unsigned)len); - break; - case 0x6933: // ChapterCommandString - if (cmd == NULL) { - cmd = ASGET(mf,proc,Commands); - memset(cmd, 0, sizeof(*cmd)); - } - if (cmd->Command) - skipbytes(mf,len); - else { - cmd->CommandLength = (unsigned)len; - STRGETM(mf,cmd->Command,len); - } - break; - ENDFOR(mf); - - if (cmd && !cmd->Command) - --proc->nCommands; - break; - ENDFOR(mf); - - if (proc && !proc->nCommands) - --ch->nProcess; - break; - case 0xb6: // Nested ChapterAtom - parseChapter(mf,len,ch); - break; - ENDFOR(mf); - - ARELEASE(mf,ch,Tracks); - ARELEASE(mf,ch,Display); - ARELEASE(mf,ch,Children); -} - -static void parseChapters(MatroskaFile *mf,ulonglong toplen) { - struct Chapter *ch; - - mf->seen.Chapters = 1; - - FOREACH(mf,toplen) - case 0x45b9: // EditionEntry - ch = AGET(mf,Chapters); - memset(ch, 0, sizeof(*ch)); - FOREACH(mf,len) - case 0x45bc: // EditionUID - ch->UID = readUInt(mf,(unsigned)len); - break; - case 0x45bd: // EditionFlagHidden - ch->Hidden = readUInt(mf,(unsigned)len)!=0; - break; - case 0x45db: // EditionFlagDefault - ch->Default = readUInt(mf,(unsigned)len)!=0; - break; - case 0x45dd: // EditionFlagOrdered - ch->Ordered = readUInt(mf,(unsigned)len)!=0; - break; - case 0xb6: // ChapterAtom - parseChapter(mf,len,ch); - break; - ENDFOR(mf); - break; - ENDFOR(mf); -} - -static void parseTags(MatroskaFile *mf,ulonglong toplen) { - struct Tag *tag; - struct Target *target; - struct SimpleTag *st; - - mf->seen.Tags = 1; - - FOREACH(mf,toplen) - case 0x7373: // Tag - tag = AGET(mf,Tags); - memset(tag,0,sizeof(*tag)); - - FOREACH(mf,len) - case 0x63c0: // Targets - FOREACH(mf,len) - case 0x63c5: // TrackUID - target = ASGET(mf,tag,Targets); - target->UID = readUInt(mf,(unsigned)len); - target->Type = TARGET_TRACK; - break; - case 0x63c4: // ChapterUID - target = ASGET(mf,tag,Targets); - target->UID = readUInt(mf,(unsigned)len); - target->Type = TARGET_CHAPTER; - break; - case 0x63c6: // AttachmentUID - target = ASGET(mf,tag,Targets); - target->UID = readUInt(mf,(unsigned)len); - target->Type = TARGET_ATTACHMENT; - break; - case 0x63c9: // EditionUID - target = ASGET(mf,tag,Targets); - target->UID = readUInt(mf,(unsigned)len); - target->Type = TARGET_EDITION; - break; - ENDFOR(mf); - break; - case 0x67c8: // SimpleTag - st = ASGET(mf,tag,SimpleTags); - memset(st,0,sizeof(*st)); - - FOREACH(mf,len) - case 0x45a3: // TagName - if (st->Name) - skipbytes(mf,len); - else - STRGETM(mf,st->Name,len); - break; - case 0x4487: // TagString - if (st->Value) - skipbytes(mf,len); - else - STRGETM(mf,st->Value,len); - break; - case 0x447a: // TagLanguage - readLangCC(mf, len, st->Language); - break; - case 0x4484: // TagDefault - st->Default = readUInt(mf,(unsigned)len)!=0; - break; - ENDFOR(mf); - - if (!st->Name || !st->Value) { - mf->cache->memfree(mf->cache,st->Name); - mf->cache->memfree(mf->cache,st->Value); - --tag->nSimpleTags; - } - break; - ENDFOR(mf); - break; - ENDFOR(mf); -} - -static void parseContainer(MatroskaFile *mf) { - ulonglong len; - int id = readID(mf); - if (id==EOF) - errorjmp(mf,"Unexpected EOF in parseContainer"); - - len = readSize(mf); - - switch (id) { - case 0x1549a966: // SegmentInfo - parseSegmentInfo(mf,len); - break; - case 0x1f43b675: // Cluster - parseFirstCluster(mf,len); - break; - case 0x1654ae6b: // Tracks - parseTracks(mf,len); - break; - case 0x1c53bb6b: // Cues - parseCues(mf,len); - break; - case 0x1941a469: // Attachments - parseAttachments(mf,len); - break; - case 0x1043a770: // Chapters - parseChapters(mf,len); - break; - case 0x1254c367: // Tags - parseTags(mf,len); - break; - } -} - -static void parseContainerPos(MatroskaFile *mf,ulonglong pos) { - seek(mf,pos); - parseContainer(mf); -} - -static void parsePointers(MatroskaFile *mf) { - jmp_buf jb; - - if (mf->pSegmentInfo && !mf->seen.SegmentInfo) - parseContainerPos(mf,mf->pSegmentInfo); - if (mf->pCluster && !mf->seen.Cluster) - parseContainerPos(mf,mf->pCluster); - if (mf->pTracks && !mf->seen.Tracks) - parseContainerPos(mf,mf->pTracks); - - memcpy(&jb,&mf->jb,sizeof(jb)); - - if (setjmp(mf->jb)) - mf->flags &= ~MPF_ERROR; // ignore errors - else { - if (mf->pCues && !mf->seen.Cues) - parseContainerPos(mf,mf->pCues); - if (mf->pAttachments && !mf->seen.Attachments) - parseContainerPos(mf,mf->pAttachments); - if (mf->pChapters && !mf->seen.Chapters) - parseContainerPos(mf,mf->pChapters); - if (mf->pTags && !mf->seen.Tags) - parseContainerPos(mf,mf->pTags); - } - - memcpy(&mf->jb,&jb,sizeof(jb)); -} - -static void parseSegment(MatroskaFile *mf,ulonglong toplen) { - ulonglong nextpos; - unsigned nSeekHeads = 0, dontstop = 0; - jmp_buf jb; - - memcpy(&jb,&mf->jb,sizeof(jb)); - - if (setjmp(mf->jb)) - mf->flags &= ~MPF_ERROR; - else { - // we want to read data until we find a seekhead or a trackinfo - FOREACH2(mf,toplen,0x1f43b675) - case 0x114d9b74: // SeekHead - if (mf->flags & MKVF_AVOID_SEEKS) { - skipbytes(mf,len); - break; - } - - nextpos = filepos(mf) + len; - do { - mf->pSeekHead = 0; - parseSeekHead(mf,len); - ++nSeekHeads; - if (mf->pSeekHead) { // this is possibly a chained SeekHead - seek(mf,mf->pSeekHead); - id = readID(mf); - if (id==EOF) // chained SeekHead points to EOF? - break; - if (id != 0x114d9b74) // chained SeekHead doesnt point to a SeekHead? - break; - len = readSize(mf); - } - } while (mf->pSeekHead && nSeekHeads < 10); - seek(mf,nextpos); // resume reading segment - break; - case 0x1549a966: // SegmentInfo - mf->pSegmentInfo = cur; - parseSegmentInfo(mf,len); - break; - case 0x1f43b675: // Cluster - if (!mf->pCluster) - mf->pCluster = cur; - if (mf->seen.Cluster) { - if (len != MAXU64) - skipbytes(mf,len); - } else - parseFirstCluster(mf,len); - break; - case 0x1654ae6b: // Tracks - mf->pTracks = cur; - parseTracks(mf,len); - break; - case 0x1c53bb6b: // Cues - mf->pCues = cur; - parseCues(mf,len); - break; - case 0x1941a469: // Attachments - mf->pAttachments = cur; - parseAttachments(mf,len); - break; - case 0x1043a770: // Chapters - mf->pChapters = cur; - parseChapters(mf,len); - break; - case 0x1254c367: // Tags - mf->pTags = cur; - parseTags(mf,len); - break; - ENDFOR1(mf); - // if we have pointers to all key elements - if (!dontstop && mf->pSegmentInfo && mf->pTracks && mf->pCluster) - break; - ENDFOR2(); - } - - memcpy(&mf->jb,&jb,sizeof(jb)); - - parsePointers(mf); -} - -static void parseBlockAdditions(MatroskaFile *mf, ulonglong toplen, ulonglong timecode, unsigned track) { - ulonglong add_id = 1, add_pos, add_len; - unsigned char have_add; - - FOREACH(mf, toplen) - case 0xa6: // BlockMore - have_add = 0; - FOREACH(mf, len) - case 0xee: // BlockAddId - add_id = readUInt(mf, (unsigned)len); - break; - case 0xa5: // BlockAddition - add_pos = filepos(mf); - add_len = len; - skipbytes(mf, len); - ++have_add; - break; - ENDFOR(mf); - if (have_add == 1 && id > 0 && id < 255) { - struct QueueEntry *qe = QAlloc(mf); - qe->Start = qe->End = timecode; - qe->Position = add_pos; - qe->Length = (unsigned)add_len; - qe->flags = FRAME_UNKNOWN_START | FRAME_UNKNOWN_END | - (((unsigned)add_id << FRAME_STREAM_SHIFT) & FRAME_STREAM_MASK); - - QPut(&mf->Queues[track],qe); - } - break; - ENDFOR(mf); -} - -static void parseBlockGroup(MatroskaFile *mf,ulonglong toplen,ulonglong timecode, int blockex) { - ulonglong v; - ulonglong duration = 0; - ulonglong dpos; - struct QueueEntry *qe,*qf = NULL; - unsigned char have_duration = 0, have_block = 0; - unsigned char gap = 0; - unsigned char lacing = 0; - unsigned char ref = 0; - unsigned char trackid; - unsigned tracknum = 0; - int c; - unsigned nframes = 0,i; - unsigned *sizes; - signed short block_timecode; - - if (blockex) - goto blockex; - - FOREACH(mf,toplen) - case 0xfb: // ReferenceBlock - readSInt(mf,(unsigned)len); - ref = 1; - break; -blockex: - cur = start = filepos(mf); - len = tmplen = toplen; - case 0xa1: // Block - have_block = 1; - - dpos = filepos(mf); - - v = readVLUInt(mf); - if (v>255) - errorjmp(mf,"Invalid track number in Block: %d",(int)v); - trackid = (unsigned char)v; - - for (tracknum=0;tracknumnTracks;++tracknum) - if (mf->Tracks[tracknum]->Number == trackid) { - if (mf->trackMask & (1<Tracks[tracknum]->TimecodeScale, - (timecode - mf->firstTimecode + block_timecode) * mf->Seg.TimecodeScale); - - c = readch(mf); - if (c==EOF) - errorjmp(mf,"Unexpected EOF while reading Block flags"); - - if (blockex) - ref = (unsigned char)!(c & 0x80); - - gap = (unsigned char)(c & 0x1); - lacing = (unsigned char)((c >> 1) & 3); - - if (lacing) { - c = readch(mf); - if (c == EOF) - errorjmp(mf,"Unexpected EOF while reading lacing data"); - nframes = c+1; - } else - nframes = 1; - sizes = alloca(nframes*sizeof(*sizes)); - - switch (lacing) { - case 0: // No lacing - sizes[0] = (unsigned)(len - filepos(mf) + dpos); - break; - case 1: // Xiph lacing - sizes[nframes-1] = 0; - for (i=0;i1) - sizes[nframes-1] = (unsigned)(len - filepos(mf) + dpos) - sizes[0] - sizes[nframes-1]; - break; - case 2: // Fixed lacing - sizes[0] = (unsigned)(len - filepos(mf) + dpos)/nframes; - for (i=1;iStart = timecode; - qe->End = timecode; - qe->Position = v; - qe->Length = sizes[i]; - qe->flags = FRAME_UNKNOWN_END | FRAME_KF; - if (i == nframes-1 && gap) - qe->flags |= FRAME_GAP; - if (i > 0) - qe->flags |= FRAME_UNKNOWN_START; - - QPut(&mf->Queues[tracknum],qe); - - v += sizes[i]; - } - - // we want to still load these bytes into cache - for (v = filepos(mf) & ~0x3fff; v < len + dpos; v += 0x4000) - mf->cache->read(mf->cache,v,NULL,0); // touch page - - skipbytes(mf,len - filepos(mf) + dpos); - - if (blockex) - goto out; - break; - case 0x9b: // BlockDuration - duration = readUInt(mf,(unsigned)len); - have_duration = 1; - break; - case 0x75a1: // BlockAdditions - if (nframes > 0) // have some frames - parseBlockAdditions(mf, len, timecode, tracknum); - else - skipbytes(mf, len); - break; - ENDFOR(mf); - -out: - if (!have_block) - errorjmp(mf,"Found a BlockGroup without Block"); - - if (nframes > 1) { - ulonglong defd = mf->Tracks[tracknum]->DefaultDuration; - v = qf->Start; - - if (have_duration) { - duration = mul3(mf->Tracks[tracknum]->TimecodeScale, - duration * mf->Seg.TimecodeScale); - - for (qe = qf; nframes > 1; --nframes, qe = qe->next) { - qe->Start = v; - v += defd; - duration -= defd; - qe->End = v; -#if 0 - qe->flags &= ~(FRAME_UNKNOWN_START|FRAME_UNKNOWN_END); -#endif - } - qe->Start = v; - qe->End = v + duration; - qe->flags &= ~FRAME_UNKNOWN_END; - } else if (mf->Tracks[tracknum]->DefaultDuration) { - for (qe = qf; nframes > 0; --nframes, qe = qe->next) { - qe->Start = v; - v += defd; - qe->End = v; - qe->flags &= ~(FRAME_UNKNOWN_START|FRAME_UNKNOWN_END); - } - } - } else if (nframes == 1) { - if (have_duration) { - qf->End = qf->Start + mul3(mf->Tracks[tracknum]->TimecodeScale, - duration * mf->Seg.TimecodeScale); - qf->flags &= ~FRAME_UNKNOWN_END; - } else if (mf->Tracks[tracknum]->DefaultDuration) { - qf->End = qf->Start + mf->Tracks[tracknum]->DefaultDuration; - qf->flags &= ~FRAME_UNKNOWN_END; - } - } - - if (ref) - while (qf) { - qf->flags &= ~FRAME_KF; - qf = qf->next; - } -} - -static void ClearQueue(MatroskaFile *mf,struct Queue *q) { - struct QueueEntry *qe,*qn; - - for (qe=q->head;qe;qe=qn) { - qn = qe->next; - qe->next = mf->QFreeList; - mf->QFreeList = qe; - } - - q->head = NULL; - q->tail = NULL; -} - -static void EmptyQueues(MatroskaFile *mf) { - unsigned i; - - for (i=0;inTracks;++i) - ClearQueue(mf,&mf->Queues[i]); -} - -static int readMoreBlocks(MatroskaFile *mf) { - ulonglong toplen, cstop; - longlong cp; - int cid, ret = 0; - jmp_buf jb; - volatile unsigned retries = 0; - - if (mf->readPosition >= mf->pSegmentTop) - return EOF; - - memcpy(&jb,&mf->jb,sizeof(jb)); - - if (setjmp(mf->jb)) { // something evil happened here, try to resync - // always advance read position no matter what so - // we don't get caught in an endless loop - mf->readPosition = filepos(mf); - - ret = EOF; - - if (++retries > 3) // don't try too hard - goto ex; - - for (;;) { - if (filepos(mf) >= mf->pSegmentTop) - goto ex; - - cp = mf->cache->scan(mf->cache,filepos(mf),0x1f43b675); // cluster - - if (cp < 0 || (ulonglong)cp >= mf->pSegmentTop) - goto ex; - - seek(mf,cp); - - cid = readID(mf); - if (cid == EOF) - goto ex; - if (cid == 0x1f43b675) { - toplen = readSizeUnspec(mf); - if (toplen < MAXCLUSTER || toplen == MAXU64) { - // reset error flags - mf->flags &= ~MPF_ERROR; - ret = RBRESYNC; - break; - } - } - } - - mf->readPosition = cp; - } - - cstop = mf->cache->getcachesize(mf->cache)>>1; - if (cstop > MAX_READAHEAD) - cstop = MAX_READAHEAD; - cstop += mf->readPosition; - - seek(mf,mf->readPosition); - - while (filepos(mf) < mf->pSegmentTop) { - cid = readID(mf); - if (cid == EOF) { - ret = EOF; - break; - } - toplen = cid == 0x1f43b675 ? readSizeUnspec(mf) : readSize(mf); - - if (cid == 0x1f43b675) { // Cluster - unsigned char have_timecode = 0; - - FOREACH2(mf,toplen,0x1f43b675) - case 0x1f43b675: - RESTART(); - break; - case 0xe7: // Timecode - mf->tcCluster = readUInt(mf,(unsigned)len); - have_timecode = 1; - break; - case 0xa7: // Position - readUInt(mf,(unsigned)len); - break; - case 0xab: // PrevSize - readUInt(mf,(unsigned)len); - break; - case 0x5854: { // SilentTracks - unsigned stmask = 0, i, trk; - FOREACH(mf, len) - case 0x58d7: // SilentTrackNumber - trk = (unsigned)readUInt(mf, (unsigned)len); - for (i = 0; i < mf->nTracks; ++i) - if (mf->Tracks[i]->Number == trk) { - stmask |= 1 << i; - break; - } - break; - ENDFOR(mf); - // TODO pass stmask to reading app - break; } - case 0xa0: // BlockGroup - if (!have_timecode) - errorjmp(mf,"Found BlockGroup before cluster TimeCode"); - parseBlockGroup(mf,len,mf->tcCluster, 0); - goto out; - case 0xa3: // BlockEx - if (!have_timecode) - errorjmp(mf,"Found BlockGroup before cluster TimeCode"); - parseBlockGroup(mf, len, mf->tcCluster, 1); - goto out; - ENDFOR(mf); -out:; - } else { - if (toplen > MAXFRAME) - errorjmp(mf,"Element in a cluster is too large around %llu, %X [%u]",filepos(mf),cid,(unsigned)toplen); - if (cid == 0xa0) // BlockGroup - parseBlockGroup(mf,toplen,mf->tcCluster, 0); - else if (cid == 0xa3) // BlockEx - parseBlockGroup(mf, toplen, mf->tcCluster, 1); - else - skipbytes(mf,toplen); - } - - if ((mf->readPosition = filepos(mf)) > cstop) - break; - } - - mf->readPosition = filepos(mf); - -ex: - memcpy(&mf->jb,&jb,sizeof(jb)); - - return ret; -} - -// this is almost the same as readMoreBlocks, except it ensures -// there are no partial frames queued, however empty queues are ok -static int fillQueues(MatroskaFile *mf,unsigned int mask) { - unsigned i,j; - int ret = 0; - - for (;;) { - j = 0; - - for (i=0;inTracks;++i) - if (mf->Queues[i].head && !(mask & (1<0) // have at least some frames - return ret; - - if ((ret = readMoreBlocks(mf)) < 0) { - j = 0; - for (i=0;inTracks;++i) - if (mf->Queues[i].head && !(mask & (1<pCluster; - ulonglong step = 10*1024*1024; - ulonglong size, tc, isize; - longlong next_cluster; - int id, have_tc, bad; - struct Cue *cue; - - if (pos >= mf->pSegmentTop) - return; - - if (pos + step * 10 > mf->pSegmentTop) - step = (mf->pSegmentTop - pos) / 10; - if (step == 0) - step = 1; - - memcpy(&jb,&mf->jb,sizeof(jb)); - - // remove all cues - mf->nCues = 0; - - bad = 0; - - while (pos < mf->pSegmentTop) { - if (!mf->cache->progress(mf->cache,pos,mf->pSegmentTop)) - break; - - if (++bad > 50) { - pos += step; - bad = 0; - continue; - } - - // find next cluster header - next_cluster = mf->cache->scan(mf->cache,pos,0x1f43b675); // cluster - if (next_cluster < 0 || (ulonglong)next_cluster >= mf->pSegmentTop) - break; - - pos = next_cluster + 4; // prevent endless loops - - if (setjmp(mf->jb)) // something evil happened while reindexing - continue; - - seek(mf,next_cluster); - - id = readID(mf); - if (id == EOF) - break; - if (id != 0x1f43b675) // shouldn't happen - continue; - - size = readSizeUnspec(mf); - if (size == MAXU64) - break; - - if (size >= MAXCLUSTER || size < 1024) - continue; - - have_tc = 0; - size += filepos(mf); - - while (filepos(mf) < (ulonglong)next_cluster + 1024) { - id = readID(mf); - if (id == EOF) - break; - - isize = readVLUInt(mf); - - if (id == 0xe7) { // cluster timecode - tc = readUInt(mf,(unsigned)isize); - have_tc = 1; - break; - } - - skipbytes(mf,isize); - } - - if (!have_tc) - continue; - - seek(mf,size); - id = readID(mf); - - if (id == EOF) - break; - - if (id != 0x1f43b675) // cluster - continue; - - // good cluster, remember it - cue = AGET(mf,Cues); - cue->Time = tc; - cue->Position = next_cluster - mf->pSegment; - cue->Block = 0; - cue->Track = 0; - - // advance to the next point - pos = next_cluster + step; - if (pos < size) - pos = size; - - bad = 0; - } - - fixupCues(mf); - - if (mf->nCues == 0) { - cue = AGET(mf,Cues); - cue->Time = mf->firstTimecode; - cue->Position = mf->pCluster - mf->pSegment; - cue->Block = 0; - cue->Track = 0; - } - - mf->cache->progress(mf->cache,0,0); - - memcpy(&mf->jb,&jb,sizeof(jb)); -} - -static void fixupChapter(ulonglong adj, struct Chapter *ch) { - unsigned i; - - if (ch->Start != 0) - ch->Start -= adj; - if (ch->End != 0) - ch->End -= adj; - - for (i=0;inChildren;++i) - fixupChapter(adj,&ch->Children[i]); -} - -static longlong findLastTimecode(MatroskaFile *mf) { - ulonglong nd = 0; - unsigned n,vtrack; - - if (mf->nTracks == 0) - return -1; - - for (n=vtrack=0;nnTracks;++n) - if (mf->Tracks[n]->Type == TT_VIDEO) { - vtrack = n; - goto ok; - } - - return -1; -ok: - - EmptyQueues(mf); - - if (mf->nCues == 0) { - mf->readPosition = mf->pCluster + 13000000 > mf->pSegmentTop ? mf->pCluster : mf->pSegmentTop - 13000000; - mf->tcCluster = 0; - } else { - mf->readPosition = mf->Cues[mf->nCues - 1].Position + mf->pSegment; - mf->tcCluster = mf->Cues[mf->nCues - 1].Time / mf->Seg.TimecodeScale; - } - mf->trackMask = ~(1 << vtrack); - - do - while (mf->Queues[vtrack].head) - { - ulonglong tc = mf->Queues[vtrack].head->flags & FRAME_UNKNOWN_END ? - mf->Queues[vtrack].head->Start : mf->Queues[vtrack].head->End; - if (nd < tc) - nd = tc; - QFree(mf,QGet(&mf->Queues[vtrack])); - } - while (fillQueues(mf,0) != EOF); - - mf->trackMask = 0; - - EmptyQueues(mf); - - // there may have been an error, but at this point we will ignore it - if (mf->flags & MPF_ERROR) { - mf->flags &= ~MPF_ERROR; - if (nd == 0) - return -1; - } - - return nd; -} - -static void parseFile(MatroskaFile *mf) { - ulonglong len = filepos(mf), adjust; - unsigned i; - int id = readID(mf); - - if (id==EOF) - errorjmp(mf,"Unexpected EOF at start of file"); - - // files with multiple concatenated segments can have only - // one EBML prolog - if (len > 0 && id == 0x18538067) - goto segment; - - if (id!=0x1a45dfa3) - errorjmp(mf,"First element in file is not EBML"); - - parseEBML(mf,readSize(mf)); - - // next we need to find the first segment - for (;;) { - id = readID(mf); - if (id==EOF) - errorjmp(mf,"No segments found in the file"); -segment: - len = readSizeUnspec(mf); - if (id == 0x18538067) // Segment - break; - if (len == MAXU64) - errorjmp(mf,"No segments found in the file"); - skipbytes(mf,len); - } - - // found it - mf->pSegment = filepos(mf); - if (len == MAXU64) { - mf->pSegmentTop = MAXU64; - if (mf->cache->getfilesize) { - longlong seglen = mf->cache->getfilesize(mf->cache); - if (seglen > 0) - mf->pSegmentTop = seglen; - } - } else - mf->pSegmentTop = mf->pSegment + len; - parseSegment(mf,len); - - // check if we got all data - if (!mf->seen.SegmentInfo) - errorjmp(mf,"Couldn't find SegmentInfo"); - if (!mf->seen.Cluster) - mf->pCluster = mf->pSegmentTop; - - adjust = mf->firstTimecode * mf->Seg.TimecodeScale; - - for (i=0;inChapters;++i) - fixupChapter(adjust, &mf->Chapters[i]); - - fixupCues(mf); - - // release extra memory - ARELEASE(mf,mf,Tracks); - - // initialize reader - mf->Queues = mf->cache->memalloc(mf->cache,mf->nTracks * sizeof(*mf->Queues)); - if (mf->Queues == NULL) - errorjmp(mf, "Ouf of memory"); - memset(mf->Queues, 0, mf->nTracks * sizeof(*mf->Queues)); - - // try to detect real duration - if (!(mf->flags & MKVF_AVOID_SEEKS)) { - longlong nd = findLastTimecode(mf); - if (nd > 0) - mf->Seg.Duration = nd; - } - - // move to first frame - mf->readPosition = mf->pCluster; - mf->tcCluster = mf->firstTimecode; -} - -static void DeleteChapter(MatroskaFile *mf,struct Chapter *ch) { - unsigned i,j; - - for (i=0;inDisplay;++i) - mf->cache->memfree(mf->cache,ch->Display[i].String); - mf->cache->memfree(mf->cache,ch->Display); - mf->cache->memfree(mf->cache,ch->Tracks); - - for (i=0;inProcess;++i) { - for (j=0;jProcess[i].nCommands;++j) - mf->cache->memfree(mf->cache,ch->Process[i].Commands[j].Command); - mf->cache->memfree(mf->cache,ch->Process[i].Commands); - mf->cache->memfree(mf->cache,ch->Process[i].CodecPrivate); - } - mf->cache->memfree(mf->cache,ch->Process); - - for (i=0;inChildren;++i) - DeleteChapter(mf,&ch->Children[i]); - mf->cache->memfree(mf->cache,ch->Children); -} - -/////////////////////////////////////////////////////////////////////////// -// public interface -MatroskaFile *mkv_OpenEx(InputStream *io, - ulonglong base, - unsigned flags, - char *err_msg,unsigned msgsize) -{ - MatroskaFile *mf = io->memalloc(io,sizeof(*mf)); - if (mf == NULL) { - mystrlcpy(err_msg,"Out of memory",msgsize); - return NULL; - } - - memset(mf,0,sizeof(*mf)); - - mf->cache = io; - mf->flags = flags; - io->progress(io,0,0); - - if (setjmp(mf->jb)==0) { - seek(mf,base); - parseFile(mf); - } else { // parser error - mystrlcpy(err_msg,mf->errmsg,msgsize); - mkv_Close(mf); - return NULL; - } - - return mf; -} - -MatroskaFile *mkv_Open(InputStream *io, - char *err_msg,unsigned msgsize) -{ - return mkv_OpenEx(io,0,0,err_msg,msgsize); -} - -void mkv_Close(MatroskaFile *mf) { - unsigned i,j; - - if (mf==NULL) - return; - - for (i=0;inTracks;++i) - mf->cache->memfree(mf->cache,mf->Tracks[i]); - mf->cache->memfree(mf->cache,mf->Tracks); - - for (i=0;inQBlocks;++i) - mf->cache->memfree(mf->cache,mf->QBlocks[i]); - mf->cache->memfree(mf->cache,mf->QBlocks); - - mf->cache->memfree(mf->cache,mf->Queues); - - mf->cache->memfree(mf->cache,mf->Seg.Title); - mf->cache->memfree(mf->cache,mf->Seg.MuxingApp); - mf->cache->memfree(mf->cache,mf->Seg.WritingApp); - mf->cache->memfree(mf->cache,mf->Seg.Filename); - mf->cache->memfree(mf->cache,mf->Seg.NextFilename); - mf->cache->memfree(mf->cache,mf->Seg.PrevFilename); - - mf->cache->memfree(mf->cache,mf->Cues); - - for (i=0;inAttachments;++i) { - mf->cache->memfree(mf->cache,mf->Attachments[i].Description); - mf->cache->memfree(mf->cache,mf->Attachments[i].Name); - mf->cache->memfree(mf->cache,mf->Attachments[i].MimeType); - } - mf->cache->memfree(mf->cache,mf->Attachments); - - for (i=0;inChapters;++i) - DeleteChapter(mf,&mf->Chapters[i]); - mf->cache->memfree(mf->cache,mf->Chapters); - - for (i=0;inTags;++i) { - for (j=0;jTags[i].nSimpleTags;++j) { - mf->cache->memfree(mf->cache,mf->Tags[i].SimpleTags[j].Name); - mf->cache->memfree(mf->cache,mf->Tags[i].SimpleTags[j].Value); - } - mf->cache->memfree(mf->cache,mf->Tags[i].Targets); - mf->cache->memfree(mf->cache,mf->Tags[i].SimpleTags); - } - mf->cache->memfree(mf->cache,mf->Tags); - - mf->cache->memfree(mf->cache,mf); -} - -const char *mkv_GetLastError(MatroskaFile *mf) { - return mf->errmsg[0] ? mf->errmsg : NULL; -} - -SegmentInfo *mkv_GetFileInfo(MatroskaFile *mf) { - return &mf->Seg; -} - -unsigned int mkv_GetNumTracks(MatroskaFile *mf) { - return mf->nTracks; -} - -TrackInfo *mkv_GetTrackInfo(MatroskaFile *mf,unsigned track) { - if (track>mf->nTracks) - return NULL; - - return mf->Tracks[track]; -} - -void mkv_GetAttachments(MatroskaFile *mf,Attachment **at,unsigned *count) { - *at = mf->Attachments; - *count = mf->nAttachments; -} - -void mkv_GetChapters(MatroskaFile *mf,Chapter **ch,unsigned *count) { - *ch = mf->Chapters; - *count = mf->nChapters; -} - -void mkv_GetTags(MatroskaFile *mf,Tag **tag,unsigned *count) { - *tag = mf->Tags; - *count = mf->nTags; -} - -ulonglong mkv_GetSegmentTop(MatroskaFile *mf) { - return mf->pSegmentTop; -} - -#define IS_DELTA(f) (!((f)->flags & FRAME_KF) || ((f)->flags & FRAME_UNKNOWN_START)) - -void mkv_Seek(MatroskaFile *mf,ulonglong timecode,unsigned flags) { - int i,j,m,ret; - unsigned n,z,mask; - ulonglong m_kftime[MAX_TRACKS]; - unsigned char m_seendf[MAX_TRACKS]; - - if (mf->flags & MKVF_AVOID_SEEKS) - return; - - if (timecode == 0) { - EmptyQueues(mf); - mf->readPosition = mf->pCluster; - mf->tcCluster = mf->firstTimecode; - mf->flags &= ~MPF_ERROR; - - return; - } - - if (mf->nCues==0) - reindex(mf); - - if (mf->nCues==0) - return; - - mf->flags &= ~MPF_ERROR; - - i = 0; - j = mf->nCues - 1; - - for (;;) { - if (i>j) { - j = j>=0 ? j : 0; - - if (setjmp(mf->jb)!=0) - return; - - mkv_SetTrackMask(mf,mf->trackMask); - - if (flags & (MKVF_SEEK_TO_PREV_KEYFRAME | MKVF_SEEK_TO_PREV_KEYFRAME_STRICT)) { - // we do this in two stages - // a. find the last keyframes before the require position - // b. seek to them - - // pass 1 - for (;;) { - for (n=0;nnTracks;++n) { - m_kftime[n] = MAXU64; - m_seendf[n] = 0; - } - - EmptyQueues(mf); - - mf->readPosition = mf->Cues[j].Position + mf->pSegment; - mf->tcCluster = mf->Cues[j].Time; - - for (;;) { - if ((ret = fillQueues(mf,0)) < 0 || ret == RBRESYNC) - return; - - // drain queues until we get to the required timecode - for (n=0;nnTracks;++n) { - if (mf->Queues[n].head && (mf->Queues[n].head->StartQueues[n].head)) - m_seendf[n] = 1; - else - m_kftime[n] = mf->Queues[n].head->Start; - } - - while (mf->Queues[n].head && mf->Queues[n].head->StartQueues[n].head)) - m_seendf[n] = 1; - else - m_kftime[n] = mf->Queues[n].head->Start; - QFree(mf,QGet(&mf->Queues[n])); - } - - // We've drained the queue, so the frame at head is the next one past the requered point. - // In strict mode we are done, but when seeking is not strict we use the head frame - // if it's not an audio track (we accept preroll within a frame for audio), and the head frame - // is a keyframe - if (!(flags & MKVF_SEEK_TO_PREV_KEYFRAME_STRICT)) - if (mf->Queues[n].head && (mf->Tracks[n]->Type != TT_AUDIO || mf->Queues[n].head->Start<=timecode)) - if (!IS_DELTA(mf->Queues[n].head)) - m_kftime[n] = mf->Queues[n].head->Start; - } - - for (n=0;nnTracks;++n) - if (mf->Queues[n].head && mf->Queues[n].head->Start>=timecode) - goto found; - } -found: - - for (n=0;nnTracks;++n) - if (!(mf->trackMask & (1<0) - { - // we need to restart the search from prev cue - --j; - goto again; - } - - break; -again:; - } - } else - for (n=0;nnTracks;++n) - m_kftime[n] = timecode; - - // now seek to this timecode - EmptyQueues(mf); - - mf->readPosition = mf->Cues[j].Position + mf->pSegment; - mf->tcCluster = mf->Cues[j].Time; - - for (mask=0;;) { - if ((ret = fillQueues(mf,mask)) < 0 || ret == RBRESYNC) - return; - - // drain queues until we get to the required timecode - for (n=0;nnTracks;++n) { - struct QueueEntry *qe; - for (qe = mf->Queues[n].head;qe && qe->StartQueues[n].head) - QFree(mf,QGet(&mf->Queues[n])); - } - - for (n=z=0;nnTracks;++n) - if (m_kftime[n]==MAXU64 || (mf->Queues[n].head && mf->Queues[n].head->Start>=m_kftime[n])) { - ++z; - mask |= 1<nTracks) - return; - } - } - - m = (i+j)>>1; - - if (timecode < mf->Cues[m].Time) - j = m-1; - else - i = m+1; - } -} - -void mkv_SkipToKeyframe(MatroskaFile *mf) { - unsigned n,wait; - ulonglong ht; - - if (setjmp(mf->jb)!=0) - return; - - // remove delta frames from queues - do { - wait = 0; - - if (fillQueues(mf,0)<0) - return; - - for (n=0;nnTracks;++n) - if (mf->Queues[n].head && !(mf->Queues[n].head->flags & FRAME_KF)) { - ++wait; - QFree(mf,QGet(&mf->Queues[n])); - } - } while (wait); - - // find highest queued time - for (n=0,ht=0;nnTracks;++n) - if (mf->Queues[n].head && htQueues[n].head->Start) - ht = mf->Queues[n].head->Start; - - // ensure the time difference is less than 100ms - do { - wait = 0; - - if (fillQueues(mf,0)<0) - return; - - for (n=0;nnTracks;++n) - while (mf->Queues[n].head && mf->Queues[n].head->next && - (mf->Queues[n].head->next->flags & FRAME_KF) && - ht - mf->Queues[n].head->Start > 100000000) - { - ++wait; - QFree(mf,QGet(&mf->Queues[n])); - } - - } while (wait); -} - -ulonglong mkv_GetLowestQTimecode(MatroskaFile *mf) { - unsigned n,seen; - ulonglong t; - - // find the lowest queued timecode - for (n=seen=0,t=0;nnTracks;++n) - if (mf->Queues[n].head && (!seen || t > mf->Queues[n].head->Start)) - t = mf->Queues[n].head->Start, seen=1; - - return seen ? t : (ulonglong)LL(-1); -} - -int mkv_TruncFloat(MKFLOAT f) { -#ifdef MATROSKA_INTEGER_ONLY - return (int)(f.v >> 32); -#else - return (int)f; -#endif -} - -#define FTRACK 0xffffffff - -void mkv_SetTrackMask(MatroskaFile *mf,unsigned int mask) { - unsigned int i; - - if (mf->flags & MPF_ERROR) - return; - - mf->trackMask = mask; - - for (i=0;inTracks;++i) - if (mask & (1<Queues[i]); -} - -int mkv_ReadFrame(MatroskaFile *mf, - unsigned int mask,unsigned int *track, - ulonglong *StartTime,ulonglong *EndTime, - ulonglong *FilePos,unsigned int *FrameSize, - unsigned int *FrameFlags) -{ - unsigned int i,j; - struct QueueEntry *qe; - - if (setjmp(mf->jb)!=0) - return -1; - - do { - // extract required frame, use block with the lowest timecode - for (j=FTRACK,i=0;inTracks;++i) - if (!(mask & (1<Queues[i].head) { - j = i; - ++i; - break; - } - - for (;inTracks;++i) - if (!(mask & (1<Queues[i].head && - mf->Queues[j].head->Start > mf->Queues[i].head->Start) - j = i; - - if (j != FTRACK) { - qe = QGet(&mf->Queues[j]); - - *track = j; - *StartTime = qe->Start; - *EndTime = qe->End; - *FilePos = qe->Position; - *FrameSize = qe->Length; - *FrameFlags = qe->flags; - - QFree(mf,qe); - - return 0; - } - - if (mf->flags & MPF_ERROR) - return -1; - - } while (fillQueues(mf,mask)>=0); - - return EOF; -} - -#ifdef MATROSKA_COMPRESSION_SUPPORT -/************************************************************************* - * Compressed streams support - ************************************************************************/ -struct CompressedStream { - MatroskaFile *mf; - z_stream zs; - - /* current compressed frame */ - ulonglong frame_pos; - unsigned frame_size; - char frame_buffer[2048]; - - /* decoded data buffer */ - char decoded_buffer[2048]; - unsigned decoded_ptr; - unsigned decoded_size; - - /* error handling */ - char errmsg[128]; -}; - -CompressedStream *cs_Create(/* in */ MatroskaFile *mf, - /* in */ unsigned tracknum, - /* out */ char *errormsg, - /* in */ unsigned msgsize) -{ - CompressedStream *cs; - TrackInfo *ti; - int code; - - ti = mkv_GetTrackInfo(mf, tracknum); - if (ti == NULL) { - mystrlcpy(errormsg, "No such track.", msgsize); - return NULL; - } - - if (!ti->CompEnabled) { - mystrlcpy(errormsg, "Track is not compressed.", msgsize); - return NULL; - } - - if (ti->CompMethod != COMP_ZLIB) { - mystrlcpy(errormsg, "Unsupported compression method.", msgsize); - return NULL; - } - - cs = mf->cache->memalloc(mf->cache,sizeof(*cs)); - if (cs == NULL) { - mystrlcpy(errormsg, "Ouf of memory.", msgsize); - return NULL; - } - - memset(&cs->zs,0,sizeof(cs->zs)); - code = inflateInit(&cs->zs); - if (code != Z_OK) { - mystrlcpy(errormsg, "ZLib error.", msgsize); - mf->cache->memfree(mf->cache,cs); - return NULL; - } - - cs->frame_size = 0; - cs->decoded_ptr = cs->decoded_size = 0; - cs->mf = mf; - - return cs; -} - -void cs_Destroy(/* in */ CompressedStream *cs) { - if (cs == NULL) - return; - - inflateEnd(&cs->zs); - cs->mf->cache->memfree(cs->mf->cache,cs); -} - -/* advance to the next frame in matroska stream, you need to pass values returned - * by mkv_ReadFrame */ -void cs_NextFrame(/* in */ CompressedStream *cs, - /* in */ ulonglong pos, - /* in */ unsigned size) -{ - cs->zs.avail_in = 0; - inflateReset(&cs->zs); - cs->frame_pos = pos; - cs->frame_size = size; - cs->decoded_ptr = cs->decoded_size = 0; -} - -/* read and decode more data from current frame, return number of bytes decoded, - * 0 on end of frame, or -1 on error */ -int cs_ReadData(CompressedStream *cs,char *buffer,unsigned bufsize) -{ - char *cp = buffer; - unsigned rd = 0; - unsigned todo; - int code; - - do { - /* try to copy data from decoded buffer */ - if (cs->decoded_ptr < cs->decoded_size) { - todo = cs->decoded_size - cs->decoded_ptr;; - if (todo > bufsize - rd) - todo = bufsize - rd; - - memcpy(cp, cs->decoded_buffer + cs->decoded_ptr, todo); - - rd += todo; - cp += todo; - cs->decoded_ptr += todo; - } else { - /* setup output buffer */ - cs->zs.next_out = (Bytef *)cs->decoded_buffer; - cs->zs.avail_out = sizeof(cs->decoded_buffer); - - /* try to read more data */ - if (cs->zs.avail_in == 0 && cs->frame_size > 0) { - todo = cs->frame_size; - if (todo > sizeof(cs->frame_buffer)) - todo = sizeof(cs->frame_buffer); - - if (cs->mf->cache->read(cs->mf->cache, cs->frame_pos, cs->frame_buffer, todo) != (int)todo) { - mystrlcpy(cs->errmsg, "File read failed", sizeof(cs->errmsg)); - return -1; - } - - cs->zs.next_in = (Bytef *)cs->frame_buffer; - cs->zs.avail_in = todo; - - cs->frame_pos += todo; - cs->frame_size -= todo; - } - - /* try to decode more data */ - code = inflate(&cs->zs,Z_NO_FLUSH); - if (code != Z_OK && code != Z_STREAM_END) { - mystrlcpy(cs->errmsg, "ZLib error.", sizeof(cs->errmsg)); - return -1; - } - - /* handle decoded data */ - if (cs->zs.avail_out == sizeof(cs->decoded_buffer)) /* EOF */ - break; - - cs->decoded_ptr = 0; - cs->decoded_size = sizeof(cs->decoded_buffer) - cs->zs.avail_out; - } - } while (rd < bufsize); - - return rd; -} - -/* return error message for the last error */ -const char *cs_GetLastError(CompressedStream *cs) -{ - if (!cs->errmsg[0]) - return NULL; - return cs->errmsg; -} -#endif diff --git a/aegisub/libffms/src/core/matroskaparser.h b/aegisub/libffms/src/core/matroskaparser.h deleted file mode 100644 index b3e1f75ad..000000000 --- a/aegisub/libffms/src/core/matroskaparser.h +++ /dev/null @@ -1,396 +0,0 @@ -/* - * Copyright (c) 2004-2008 Mike Matsnev. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice immediately at the beginning of the file, without modification, - * this list of conditions, and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Absolutely no warranty of function or purpose is made by the author - * Mike Matsnev. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id: MatroskaParser.h,v 1.22 2008/04/29 21:03:09 mike Exp $ - * - */ - -#ifndef MATROSKA_PARSER_H -#define MATROSKA_PARSER_H - -/* Random notes: - * - * The parser does not process frame data in any way and does not read it into - * the queue. The app should read it via mkv_ReadData if it is interested. - * - * The code here is 64-bit clean and was tested on FreeBSD/sparc 64-bit big endian - * system - */ - -#ifdef MPDLLBUILD -#define X __declspec(dllexport) -#else -#ifdef MPDLL -#define X __declspec(dllimport) -#pragma comment(lib,"MatroskaParser") -#else -#define X -#endif -#endif - -#define MATROSKA_COMPRESSION_SUPPORT -#define MATROSKA_INTEGER_ONLY - -#ifdef __cplusplus -extern "C" { -#endif - -/* 64-bit integers */ -#ifdef _WIN32_WCE -typedef signed __int64 longlong; -typedef unsigned __int64 ulonglong; -#else -typedef signed long long longlong; -typedef unsigned long long ulonglong; -#endif - -/* MKFLOATing point */ -#ifdef MATROSKA_INTEGER_ONLY -typedef struct { - longlong v; -} MKFLOAT; -#else -typedef double MKFLOAT; -#endif - -/* generic I/O */ -struct InputStream { - /* read bytes from stream */ - int (*read)(struct InputStream *cc,ulonglong pos,void *buffer,int count); - /* scan for a four byte signature, bytes must be nonzero */ - longlong (*scan)(struct InputStream *cc,ulonglong start,unsigned signature); - /* get cache size, this is used to cap readahead */ - unsigned (*getcachesize)(struct InputStream *cc); - /* fetch last error message */ - const char *(*geterror)(struct InputStream *cc); - /* memory allocation */ - void *(*memalloc)(struct InputStream *cc,size_t size); - void *(*memrealloc)(struct InputStream *cc,void *mem,size_t newsize); - void (*memfree)(struct InputStream *cc,void *mem); - /* zero return causes parser to abort open */ - int (*progress)(struct InputStream *cc,ulonglong cur,ulonglong max); - /* get file size, optional, can be NULL or return -1 if filesize is unknown */ - longlong (*getfilesize)(struct InputStream *cc); -}; - -typedef struct InputStream InputStream; - -/* matroska file */ -struct MatroskaFile; /* opaque */ - -typedef struct MatroskaFile MatroskaFile; - -#define COMP_ZLIB 0 -#define COMP_BZIP 1 -#define COMP_LZO1X 2 -#define COMP_PREPEND 3 - -#define TT_VIDEO 1 -#define TT_AUDIO 2 -#define TT_SUB 17 - -struct TrackInfo { - unsigned char Number; - unsigned char Type; - unsigned char TrackOverlay; - ulonglong UID; - ulonglong MinCache; - ulonglong MaxCache; - ulonglong DefaultDuration; - MKFLOAT TimecodeScale; - void *CodecPrivate; - unsigned CodecPrivateSize; - unsigned CompMethod; - void *CompMethodPrivate; - unsigned CompMethodPrivateSize; - unsigned MaxBlockAdditionID; - - unsigned int Enabled:1; - unsigned int Default:1; - unsigned int Lacing:1; - unsigned int DecodeAll:1; - unsigned int CompEnabled:1; - - union { - struct { - unsigned char StereoMode; - unsigned char DisplayUnit; - unsigned char AspectRatioType; - unsigned int PixelWidth; - unsigned int PixelHeight; - unsigned int DisplayWidth; - unsigned int DisplayHeight; - unsigned int CropL, CropT, CropR, CropB; - unsigned int ColourSpace; - MKFLOAT GammaValue; - - unsigned int Interlaced:1; - } Video; - struct { - MKFLOAT SamplingFreq; - MKFLOAT OutputSamplingFreq; - unsigned char Channels; - unsigned char BitDepth; - } Audio; - } AV; - - /* various strings */ - char *Name; - char Language[4]; - char *CodecID; -}; - -typedef struct TrackInfo TrackInfo; - -struct SegmentInfo { - char UID[16]; - char PrevUID[16]; - char NextUID[16]; - char *Filename; - char *PrevFilename; - char *NextFilename; - char *Title; - char *MuxingApp; - char *WritingApp; - ulonglong TimecodeScale; - ulonglong Duration; - longlong DateUTC; - char DateUTCValid; -}; - -typedef struct SegmentInfo SegmentInfo; - -struct Attachment { - ulonglong Position; - ulonglong Length; - ulonglong UID; - char *Name; - char *Description; - char *MimeType; -}; - -typedef struct Attachment Attachment; - -struct ChapterDisplay { - char *String; - char Language[4]; - char Country[4]; -}; - -struct ChapterCommand { - unsigned Time; - unsigned CommandLength; - void *Command; -}; - -struct ChapterProcess { - unsigned CodecID; - unsigned CodecPrivateLength; - void *CodecPrivate; - unsigned nCommands,nCommandsSize; - struct ChapterCommand *Commands; -}; - -struct Chapter { - ulonglong UID; - ulonglong Start; - ulonglong End; - - unsigned nTracks,nTracksSize; - ulonglong *Tracks; - unsigned nDisplay,nDisplaySize; - struct ChapterDisplay *Display; - unsigned nChildren,nChildrenSize; - struct Chapter *Children; - unsigned nProcess,nProcessSize; - struct ChapterProcess *Process; - - char SegmentUID[16]; - - unsigned int Hidden:1; - unsigned int Enabled:1; - - // Editions - unsigned int Default:1; - unsigned int Ordered:1; -}; - -typedef struct Chapter Chapter; - -#define TARGET_TRACK 0 -#define TARGET_CHAPTER 1 -#define TARGET_ATTACHMENT 2 -#define TARGET_EDITION 3 -struct Target { - ulonglong UID; - unsigned Type; -}; - -struct SimpleTag { - char *Name; - char *Value; - char Language[4]; - unsigned Default:1; -}; - -struct Tag { - unsigned nTargets,nTargetsSize; - struct Target *Targets; - - unsigned nSimpleTags,nSimpleTagsSize; - struct SimpleTag *SimpleTags; -}; - -typedef struct Tag Tag; - -/* Open a matroska file - * io pointer is recorded inside MatroskaFile - */ -X MatroskaFile *mkv_Open(/* in */ InputStream *io, - /* out */ char *err_msg, - /* in */ unsigned msgsize); - -#define MKVF_AVOID_SEEKS 1 /* use sequential reading only */ - -X MatroskaFile *mkv_OpenEx(/* in */ InputStream *io, - /* in */ ulonglong base, - /* in */ unsigned flags, - /* out */ char *err_msg, - /* in */ unsigned msgsize); - -/* Close and deallocate mf - * NULL pointer is ok and is simply ignored - */ -X void mkv_Close(/* in */ MatroskaFile *mf); - -/* Fetch the error message of the last failed operation */ -X const char *mkv_GetLastError(/* in */ MatroskaFile *mf); - -/* Get file information */ -X SegmentInfo *mkv_GetFileInfo(/* in */ MatroskaFile *mf); - -/* Get track information */ -X unsigned int mkv_GetNumTracks(/* in */ MatroskaFile *mf); -X TrackInfo *mkv_GetTrackInfo(/* in */ MatroskaFile *mf,/* in */ unsigned track); - -/* chapters, tags and attachments */ -X void mkv_GetAttachments(/* in */ MatroskaFile *mf, - /* out */ Attachment **at, - /* out */ unsigned *count); -X void mkv_GetChapters(/* in */ MatroskaFile *mf, - /* out */ Chapter **ch, - /* out */ unsigned *count); -X void mkv_GetTags(/* in */ MatroskaFile *mf, - /* out */ Tag **tag, - /* out */ unsigned *count); - -X ulonglong mkv_GetSegmentTop(MatroskaFile *mf); - -/* Seek to specified timecode, - * if timecode is past end of file, - * all tracks are set to return EOF - * on next read - */ -#define MKVF_SEEK_TO_PREV_KEYFRAME 1 -#define MKVF_SEEK_TO_PREV_KEYFRAME_STRICT 2 - -X void mkv_Seek(/* in */ MatroskaFile *mf, - /* in */ ulonglong timecode /* in ns */, - /* in */ unsigned flags); - -X void mkv_SkipToKeyframe(MatroskaFile *mf); - -X ulonglong mkv_GetLowestQTimecode(MatroskaFile *mf); - -X int mkv_TruncFloat(MKFLOAT f); - -/************************************************************************* - * reading data, pull model - */ - -/* frame flags */ -#define FRAME_UNKNOWN_START 0x00000001 -#define FRAME_UNKNOWN_END 0x00000002 -#define FRAME_KF 0x00000004 -#define FRAME_GAP 0x00800000 -#define FRAME_STREAM_MASK 0xff000000 -#define FRAME_STREAM_SHIFT 24 - -/* This sets the masking flags for the parser, - * masked tracks [with 1s in their bit positions] - * will be ignored when reading file data. - * This call discards all parsed and queued frames - */ -X void mkv_SetTrackMask(/* in */ MatroskaFile *mf,/* in */ unsigned int mask); - -/* Read one frame from the queue. - * mask specifies what tracks to ignore. - * Returns -1 if there are no more frames in the specified - * set of tracks, 0 on success - */ -X int mkv_ReadFrame(/* in */ MatroskaFile *mf, - /* in */ unsigned int mask, - /* out */ unsigned int *track, - /* out */ ulonglong *StartTime /* in ns */, - /* out */ ulonglong *EndTime /* in ns */, - /* out */ ulonglong *FilePos /* in bytes from start of file */, - /* out */ unsigned int *FrameSize /* in bytes */, - /* out */ unsigned int *FrameFlags); - -#ifdef MATROSKA_COMPRESSION_SUPPORT -/* Compressed streams support */ -struct CompressedStream; - -typedef struct CompressedStream CompressedStream; - -X CompressedStream *cs_Create(/* in */ MatroskaFile *mf, - /* in */ unsigned tracknum, - /* out */ char *errormsg, - /* in */ unsigned msgsize); -X void cs_Destroy(/* in */ CompressedStream *cs); - -/* advance to the next frame in matroska stream, you need to pass values returned - * by mkv_ReadFrame */ -X void cs_NextFrame(/* in */ CompressedStream *cs, - /* in */ ulonglong pos, - /* in */ unsigned size); - -/* read and decode more data from current frame, return number of bytes decoded, - * 0 on end of frame, or -1 on error */ -X int cs_ReadData(CompressedStream *cs,char *buffer,unsigned bufsize); - -/* return error message for the last error */ -X const char *cs_GetLastError(CompressedStream *cs); -#endif - -#ifdef __cplusplus -} -#endif - -#undef X - -#endif diff --git a/aegisub/libffms/src/core/matroskavideo.cpp b/aegisub/libffms/src/core/matroskavideo.cpp deleted file mode 100644 index d2a7ddc07..000000000 --- a/aegisub/libffms/src/core/matroskavideo.cpp +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include "videosource.h" - - - -void FFMatroskaVideo::Free(bool CloseCodec) { - if (TCC) - delete TCC; - if (MC.ST.fp) { - mkv_Close(MF); - } - if (CloseCodec) - avcodec_close(CodecContext); - av_freep(&CodecContext); -} - -FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track, - FFMS_Index *Index, int Threads) - : Res(FFSourceResources(this)), FFMS_VideoSource(SourceFile, Index, Track) { - - AVCodec *Codec = NULL; - CodecContext = NULL; - TrackInfo *TI = NULL; - TCC = NULL; - PacketNumber = 0; - VideoTrack = Track; - Frames = (*Index)[VideoTrack]; - - MC.ST.fp = ffms_fopen(SourceFile, "rb"); - if (MC.ST.fp == NULL) { - std::ostringstream buf; - buf << "Can't open '" << SourceFile << "': " << strerror(errno); - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, buf.str()); - } - - setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE); - - MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage)); - if (MF == NULL) { - std::ostringstream buf; - buf << "Can't parse Matroska file: " << ErrorMessage; - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, buf.str()); - } - - TI = mkv_GetTrackInfo(MF, VideoTrack); - - if (TI->CompEnabled) - TCC = new TrackCompressionContext(MF, TI, VideoTrack); - - CodecContext = avcodec_alloc_context(); - if (avcodec_thread_init(CodecContext, Threads)) - CodecContext->thread_count = 1; - - Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate)); - if (Codec == NULL) - throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, - "Video codec not found"); - - InitializeCodecContextFromMatroskaTrackInfo(TI, CodecContext); - - if (avcodec_open(CodecContext, Codec) < 0) - throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, - "Could not open video codec"); - - Res.CloseCodec(true); - - // Always try to decode a frame to make sure all required parameters are known - DecodeNextFrame(); - - VP.FPSDenominator = 1; - VP.FPSNumerator = 30; - VP.RFFDenominator = CodecContext->time_base.num; - VP.RFFNumerator = CodecContext->time_base.den; - if (CodecContext->codec_id == CODEC_ID_H264) { - if (VP.RFFNumerator & 1) - VP.RFFDenominator *= 2; - else - VP.RFFNumerator /= 2; - } - VP.NumFrames = Frames.size(); - VP.TopFieldFirst = DecodeFrame->top_field_first; -#ifdef FFMS_HAVE_FFMPEG_COLORSPACE_INFO - VP.ColorSpace = CodecContext->colorspace; - VP.ColorRange = CodecContext->color_range; -#else - VP.ColorSpace = 0; - VP.ColorRange = 0; -#endif - // these pixfmt's are deprecated but still used - if ( - CodecContext->pix_fmt == PIX_FMT_YUVJ420P - || CodecContext->pix_fmt == PIX_FMT_YUVJ422P - || CodecContext->pix_fmt == PIX_FMT_YUVJ444P - ) - VP.ColorRange = AVCOL_RANGE_JPEG; - - VP.FirstTime = ((Frames.front().PTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; - VP.LastTime = ((Frames.back().PTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; - - if (CodecContext->width <= 0 || CodecContext->height <= 0) - throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, - "Codec returned zero size video"); - - // Calculate the average framerate - if (Frames.size() >= 2) { - double PTSDiff = (double)(Frames.back().PTS - Frames.front().PTS); - VP.FPSDenominator = (unsigned int)(PTSDiff * mkv_TruncFloat(TI->TimecodeScale) / (double)1000 / (double)(VP.NumFrames - 1) + 0.5); - VP.FPSNumerator = 1000000; - } - - // attempt to correct framerate to the proper NTSC fraction, if applicable - CorrectNTSCRationalFramerate(&VP.FPSNumerator, &VP.FPSDenominator); - // correct the timebase, if necessary - CorrectTimebase(&VP, &Frames.TB); - - // Output the already decoded frame so it isn't wasted - OutputFrame(DecodeFrame); - - // Set AR variables - VP.SARNum = TI->AV.Video.DisplayWidth * TI->AV.Video.PixelHeight; - VP.SARDen = TI->AV.Video.DisplayHeight * TI->AV.Video.PixelWidth; - - // Set crop variables - VP.CropLeft = TI->AV.Video.CropL; - VP.CropRight = TI->AV.Video.CropR; - VP.CropTop = TI->AV.Video.CropT; - VP.CropBottom = TI->AV.Video.CropB; -} - -void FFMatroskaVideo::DecodeNextFrame() { - int FrameFinished = 0; - AVPacket Packet; - InitNullPacket(Packet); - - unsigned int FrameSize; - - if (InitialDecode == -1) { - if (DelayCounter > CodecContext->has_b_frames) { - DelayCounter--; - goto Done; - } else { - InitialDecode = 0; - } - } - - while (PacketNumber < Frames.size()) { - // The additional indirection is because the packets are stored in - // presentation order and not decoding order, this is unnoticable - // in the other sources where less is done manually - const TFrameInfo &FI = Frames[Frames[PacketNumber].OriginalPos]; - FrameSize = FI.FrameSize; - ReadFrame(FI.FilePos, FrameSize, TCC, MC); - - Packet.data = MC.Buffer; - Packet.size = (TCC && TCC->CompressionMethod == COMP_PREPEND) ? FrameSize + TCC->CompressedPrivateDataSize : FrameSize; - if (FI.KeyFrame) - Packet.flags = AV_PKT_FLAG_KEY; - else - Packet.flags = 0; - - PacketNumber++; - - avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet); - - if (!FrameFinished) - DelayCounter++; - if (DelayCounter > CodecContext->has_b_frames && !InitialDecode) - goto Done; - - if (FrameFinished) - goto Done; - } - - // Flush the last frames - if (CodecContext->has_b_frames) { - AVPacket NullPacket; - InitNullPacket(NullPacket); - avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket); - } - - if (!FrameFinished) - goto Error; - -Error: -Done: - if (InitialDecode == 1) InitialDecode = -1; -} - -FFMS_Frame *FFMatroskaVideo::GetFrame(int n) { - GetFrameCheck(n); - - if (LastFrameNum == n) - return &LocalFrame; - - int ClosestKF = Frames.FindClosestVideoKeyFrame(n); - if (CurrentFrame > n || ClosestKF > CurrentFrame + 10) { - DelayCounter = 0; - InitialDecode = 1; - PacketNumber = ClosestKF; - CurrentFrame = ClosestKF; - avcodec_flush_buffers(CodecContext); - } - - do { - if (CurrentFrame+CodecContext->has_b_frames >= n) - CodecContext->skip_frame = AVDISCARD_DEFAULT; - else - CodecContext->skip_frame = AVDISCARD_NONREF; - DecodeNextFrame(); - CurrentFrame++; - } while (CurrentFrame <= n); - - LastFrameNum = n; - return OutputFrame(DecodeFrame); -} diff --git a/aegisub/libffms/src/core/stdiostream.c b/aegisub/libffms/src/core/stdiostream.c deleted file mode 100644 index 9fdde8b15..000000000 --- a/aegisub/libffms/src/core/stdiostream.c +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include "stdiostream.h" -#include "ffmscompat.h" -#include - -/* StdIoStream methods */ - -/* read count bytes into buffer starting at file position pos - * return the number of bytes read, -1 on error or 0 on EOF - */ -int StdIoRead(StdIoStream *st, ulonglong pos, void *buffer, int count) { - size_t rd; - if (fseeko(st->fp, pos, SEEK_SET)) { - st->error = errno; - return -1; - } - rd = fread(buffer, 1, count, st->fp); - if (rd == 0) { - if (feof(st->fp)) - return 0; - st->error = errno; - return -1; - } - return rd; -} - -/* scan for a signature sig(big-endian) starting at file position pos - * return position of the first byte of signature or -1 if error/not found - */ -longlong StdIoScan(StdIoStream *st, ulonglong start, unsigned signature) { - int c; - unsigned cmp = 0; - FILE *fp = st->fp; - - if (fseeko(fp, start, SEEK_SET)) - return -1; - - while ((c = getc(fp)) != EOF) { - cmp = ((cmp << 8) | c) & 0xffffffff; - if (cmp == signature) - return ftello(fp) - 4; - } - - return -1; -} - -/* return cache size, this is used to limit readahead */ -unsigned StdIoGetCacheSize(StdIoStream *st) { - return CACHESIZE; -} - -/* return last error message */ -const char *StdIoGetLastError(StdIoStream *st) { - return strerror(st->error); -} - -/* memory allocation, this is done via stdlib */ -void *StdIoMalloc(StdIoStream *st, size_t size) { - return malloc(size); -} - -void *StdIoRealloc(StdIoStream *st, void *mem, size_t size) { - return realloc(mem,size); -} - -void StdIoFree(StdIoStream *st, void *mem) { - free(mem); -} - -/* progress report handler for lengthy operations - * returns 0 to abort operation, nonzero to continue - */ -int StdIoProgress(StdIoStream *st, ulonglong cur, ulonglong max) { - return 1; -} - -longlong StdIoGetFileSize(StdIoStream *st) { - longlong epos = 0; - longlong cpos = ftello(st->fp); - fseeko(st->fp, 0, SEEK_END); - epos = ftello(st->fp); - fseeko(st->fp, cpos, SEEK_SET); - return epos; -} - -void InitStdIoStream(StdIoStream *st) { - memset(st,0,sizeof(StdIoStream)); - st->base.read = (int (*)(InputStream *,ulonglong,void *,int))StdIoRead; - st->base.scan = (longlong (*)(InputStream *,ulonglong,unsigned int))StdIoScan; - st->base.getcachesize = (unsigned int (*)(InputStream *))StdIoGetCacheSize; - st->base.geterror = (const char *(*)(InputStream *))StdIoGetLastError; - st->base.memalloc = (void *(*)(InputStream *,size_t))StdIoMalloc; - st->base.memrealloc = (void *(*)(InputStream *,void *,size_t))StdIoRealloc; - st->base.memfree = (void (*)(InputStream *,void *))StdIoFree; - st->base.progress = (int (*)(InputStream *,ulonglong,ulonglong))StdIoProgress; - st->base.getfilesize = (longlong (*)(InputStream *))StdIoGetFileSize; -} diff --git a/aegisub/libffms/src/core/stdiostream.h b/aegisub/libffms/src/core/stdiostream.h deleted file mode 100644 index 116b0e089..000000000 --- a/aegisub/libffms/src/core/stdiostream.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#ifndef STDIOSTREAM_H -#define STDIOSTREAM_H - -#undef __STRICT_ANSI__ - -#include -#include -#include -#include "matroskaparser.h" - -#define CACHESIZE 65536 - -/************\ -* Structures * -\************/ - -/* first we need to create an I/O object that the parser will use to read the - * source file - */ -struct StdIoStream { - struct InputStream base; - FILE *fp; - int error; -}; - -typedef struct StdIoStream StdIoStream; - -/***********\ -* Functions * -\***********/ - -/* read count bytes into buffer starting at file position pos - * return the number of bytes read, -1 on error or 0 on EOF - */ -int StdIoRead(StdIoStream *st, ulonglong pos, void *buffer, int count); - -/* scan for a signature sig(big-endian) starting at file position pos - * return position of the first byte of signature or -1 if error/not found - */ -longlong StdIoScan(StdIoStream *st, ulonglong start, unsigned signature); - -/* return cache size, this is used to limit readahead */ -unsigned StdIoGetCacheSize(StdIoStream *st); - -/* return last error message */ -const char *StdIoGetLastError(StdIoStream *st); - -/* memory allocation, this is done via stdlib */ -void *StdIoMalloc(StdIoStream *st, size_t size); - -void *StdIoRealloc(StdIoStream *st, void *mem, size_t size); - -void StdIoFree(StdIoStream *st, void *mem); - -/* progress report handler for lengthy operations - * returns 0 to abort operation, nonzero to continue - */ -int StdIoProgress(StdIoStream *st, ulonglong cur, ulonglong max); - -longlong StdIoGetFileSize(StdIoStream *st); - -void InitStdIoStream(StdIoStream *st); - -#endif /* #ifndef STDIOSTREAM_H */ diff --git a/aegisub/libffms/src/core/utils.cpp b/aegisub/libffms/src/core/utils.cpp deleted file mode 100644 index a004dfa6c..000000000 --- a/aegisub/libffms/src/core/utils.cpp +++ /dev/null @@ -1,758 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include -#include -#include "utils.h" -#include "indexing.h" - -#ifdef _WIN32 -# define WIN32_LEAN_AND_MEAN -# include -# include -# include -extern "C" { -# include "libavutil/avstring.h" -} -#endif // _WIN32 - - -// Export the array but not its data type... fun... -typedef struct CodecTags{ - char str[20]; - enum CodecID id; -} CodecTags; - -extern "C" { -extern const AVCodecTag ff_codec_bmp_tags[]; -extern const CodecTags ff_mkv_codec_tags[]; -extern const AVCodecTag ff_codec_movvideo_tags[]; -extern const AVCodecTag ff_codec_wav_tags[]; - -/* if you have this, we'll assume you have a new enough libavutil too */ -#if LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(0, 12, 0) -# include -#endif -} - -extern int CPUFeatures; -extern bool GlobalUseUTF8Paths; - - - -FFMS_Exception::FFMS_Exception(int ErrorType, int SubType, const char *Message) : _ErrorType(ErrorType), _SubType(SubType), _Message(Message) { -} - -FFMS_Exception::FFMS_Exception(int ErrorType, int SubType, const std::string &Message) : _ErrorType(ErrorType), _SubType(SubType), _Message(Message) { -} - -FFMS_Exception::~FFMS_Exception() throw () { -} - -const std::string &FFMS_Exception::GetErrorMessage() const { - return _Message; -} - -int FFMS_Exception::CopyOut(FFMS_ErrorInfo *ErrorInfo) const { - if (ErrorInfo) { - ErrorInfo->ErrorType = _ErrorType; - ErrorInfo->SubType = _SubType; - - if (ErrorInfo->BufferSize > 0) { - memset(ErrorInfo->Buffer, 0, ErrorInfo->BufferSize); - _Message.copy(ErrorInfo->Buffer, ErrorInfo->BufferSize - 1); - } - } - - return (_ErrorType << 16) | _SubType; -} - -TrackCompressionContext::TrackCompressionContext(MatroskaFile *MF, TrackInfo *TI, unsigned int Track) { - CS = NULL; - CompressedPrivateData = NULL; - CompressedPrivateDataSize = 0; - CompressionMethod = TI->CompMethod; - - if (CompressionMethod == COMP_ZLIB) { - char ErrorMessage[512]; - CS = cs_Create(MF, Track, ErrorMessage, sizeof(ErrorMessage)); - if (CS == NULL) { - std::ostringstream buf; - buf << "Can't create MKV track decompressor: " << ErrorMessage; - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, buf.str()); - } - } else if (CompressionMethod == COMP_PREPEND) { - CompressedPrivateData = TI->CompMethodPrivate; - CompressedPrivateDataSize = TI->CompMethodPrivateSize; - } else { - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - "Can't create MKV track decompressor: unknown or unsupported compression method"); - } -} - -TrackCompressionContext::~TrackCompressionContext() { - if (CS) - cs_Destroy(CS); -} - -int64_t GetSWSCPUFlags() { - int64_t Flags = 0; - - if (CPUFeatures & FFMS_CPU_CAPS_MMX) - Flags |= SWS_CPU_CAPS_MMX; - if (CPUFeatures & FFMS_CPU_CAPS_MMX2) - Flags |= SWS_CPU_CAPS_MMX2; - if (CPUFeatures & FFMS_CPU_CAPS_3DNOW) - Flags |= SWS_CPU_CAPS_3DNOW; - if (CPUFeatures & FFMS_CPU_CAPS_ALTIVEC) - Flags |= SWS_CPU_CAPS_ALTIVEC; - if (CPUFeatures & FFMS_CPU_CAPS_BFIN) - Flags |= SWS_CPU_CAPS_BFIN; -#ifdef SWS_CPU_CAPS_SSE2 - if (CPUFeatures & FFMS_CPU_CAPS_SSE2) - Flags |= SWS_CPU_CAPS_SSE2; -#endif - - return Flags; -} - -static int handle_jpeg(PixelFormat *format) -{ - switch (*format) { - case PIX_FMT_YUVJ420P: *format = PIX_FMT_YUV420P; return 1; - case PIX_FMT_YUVJ422P: *format = PIX_FMT_YUV422P; return 1; - case PIX_FMT_YUVJ444P: *format = PIX_FMT_YUV444P; return 1; - case PIX_FMT_YUVJ440P: *format = PIX_FMT_YUV440P; return 1; - default: return 0; - } -} -SwsContext *GetSwsContext(int SrcW, int SrcH, PixelFormat SrcFormat, int DstW, int DstH, PixelFormat DstFormat, int64_t Flags, int ColorSpace) { -#if LIBSWSCALE_VERSION_INT < AV_VERSION_INT(0, 12, 0) - return sws_getContext(SrcW, SrcH, SrcFormat, DstW, DstH, DstFormat, Flags, 0, 0, 0); -#else - SwsContext *Context = sws_alloc_context(); - if (!Context) return 0; - - if (ColorSpace == -1) - ColorSpace = (SrcW > 1024 || SrcH >= 600) ? SWS_CS_ITU709 : SWS_CS_DEFAULT; - - int SrcRange = handle_jpeg(&SrcFormat); - int DstRange = handle_jpeg(&DstFormat); - - av_set_int(Context, "sws_flags", Flags); - av_set_int(Context, "srcw", SrcW); - av_set_int(Context, "srch", SrcH); - av_set_int(Context, "dstw", DstW); - av_set_int(Context, "dsth", DstH); - av_set_int(Context, "src_range", SrcRange); - av_set_int(Context, "dst_range", DstRange); - av_set_int(Context, "src_format", SrcFormat); - av_set_int(Context, "dst_format", DstFormat); - - sws_setColorspaceDetails(Context, sws_getCoefficients(ColorSpace), SrcRange, sws_getCoefficients(ColorSpace), DstRange, 0, 1<<16, 1<<16); - - if(sws_init_context(Context, 0, 0) < 0){ - sws_freeContext(Context); - return 0; - } - - return Context; -#endif - -} - - -int GetPPCPUFlags() { - int Flags = 0; - -#ifdef FFMS_USE_POSTPROC -// not exactly a pretty solution but it'll never get called anyway - if (CPUFeatures & FFMS_CPU_CAPS_MMX) - Flags |= PP_CPU_CAPS_MMX; - if (CPUFeatures & FFMS_CPU_CAPS_MMX2) - Flags |= PP_CPU_CAPS_MMX2; - if (CPUFeatures & FFMS_CPU_CAPS_3DNOW) - Flags |= PP_CPU_CAPS_3DNOW; - if (CPUFeatures & FFMS_CPU_CAPS_ALTIVEC) - Flags |= PP_CPU_CAPS_ALTIVEC; -#endif // FFMS_USE_POSTPROC - - return Flags; -} - -void ClearErrorInfo(FFMS_ErrorInfo *ErrorInfo) { - if (ErrorInfo) { - ErrorInfo->ErrorType = FFMS_ERROR_SUCCESS; - ErrorInfo->SubType = FFMS_ERROR_SUCCESS; - - if (ErrorInfo->BufferSize > 0) - ErrorInfo->Buffer[0] = 0; - } -} - -FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT) { - switch (TT) { - case TT_VIDEO: return FFMS_TYPE_VIDEO; break; - case TT_AUDIO: return FFMS_TYPE_AUDIO; break; - case TT_SUB: return FFMS_TYPE_SUBTITLE; break; - default: return FFMS_TYPE_UNKNOWN; - } -} - -const char *GetLAVCSampleFormatName(AVSampleFormat s) { - switch (s) { - case AV_SAMPLE_FMT_U8: return "8-bit unsigned integer"; - case AV_SAMPLE_FMT_S16: return "16-bit signed integer"; - case AV_SAMPLE_FMT_S32: return "32-bit signed integer"; - case AV_SAMPLE_FMT_FLT: return "Single-precision floating point"; - case AV_SAMPLE_FMT_DBL: return "Double-precision floating point"; - default: return "Unknown"; - } -} - -template static void safe_realloc(T *&ptr, size_t size) { - void *newalloc = realloc(ptr, size); - if (newalloc) { - ptr = static_cast(newalloc); - } - else { - free(ptr); - ptr = 0; - } -} - -void ReadFrame(uint64_t FilePos, unsigned int &FrameSize, TrackCompressionContext *TCC, MatroskaReaderContext &Context) { - if (TCC && TCC->CS) { - CompressedStream *CS = TCC->CS; - unsigned int DecompressedFrameSize = 0; - - cs_NextFrame(CS, FilePos, FrameSize); - - for (;;) { - int ReadBytes = cs_ReadData(CS, Context.CSBuffer, sizeof(Context.CSBuffer)); - if (ReadBytes < 0) { - std::ostringstream buf; - buf << "Error decompressing data: " << cs_GetLastError(CS); - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, buf.str()); - } - - if (ReadBytes == 0) { - FrameSize = DecompressedFrameSize; - memset(Context.Buffer + DecompressedFrameSize, 0, - Context.BufferSize + FF_INPUT_BUFFER_PADDING_SIZE - DecompressedFrameSize); - return; - } - - if (Context.BufferSize < DecompressedFrameSize + ReadBytes) { - Context.BufferSize = DecompressedFrameSize + ReadBytes; - safe_realloc(Context.Buffer, Context.BufferSize + FF_INPUT_BUFFER_PADDING_SIZE); - if (Context.Buffer == NULL) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED, - "Out of memory"); - } - - memcpy(Context.Buffer + DecompressedFrameSize, Context.CSBuffer, ReadBytes); - DecompressedFrameSize += ReadBytes; - } - } else { - if (fseeko(Context.ST.fp, FilePos, SEEK_SET)) { - std::ostringstream buf; - buf << "fseek(): " << strerror(errno); - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_SEEKING, buf.str()); - } - - if (TCC && TCC->CompressionMethod == COMP_PREPEND) { - unsigned ReqBufsize = FrameSize + TCC->CompressedPrivateDataSize + 16; - if (Context.BufferSize < ReqBufsize) { - Context.BufferSize = FrameSize + TCC->CompressedPrivateDataSize; - safe_realloc(Context.Buffer, ReqBufsize); - if (Context.Buffer == NULL) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED, "Out of memory"); - } - - /* // maybe faster? maybe not? - for (int i=0; i < TCC->CompressedPrivateDataSize; i++) - *(Context.Buffer)++ = ((uint8_t *)TCC->CompressedPrivateData)[i]; - */ - // screw it, memcpy and fuck the losers who use header compression - memcpy(Context.Buffer, TCC->CompressedPrivateData, TCC->CompressedPrivateDataSize); - } - else if (Context.BufferSize < FrameSize) { - Context.BufferSize = FrameSize; - safe_realloc(Context.Buffer, Context.BufferSize + 16); - if (Context.Buffer == NULL) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED, - "Out of memory"); - } - - uint8_t *TargetPtr = Context.Buffer; - if (TCC && TCC->CompressionMethod == COMP_PREPEND) - TargetPtr += TCC->CompressedPrivateDataSize; - - size_t ReadBytes = fread(TargetPtr, 1, FrameSize, Context.ST.fp); - if (ReadBytes != FrameSize) { - return; - if (ReadBytes == 0) { - if (feof(Context.ST.fp)) { - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - "Unexpected EOF while reading frame"); - } else { - std::ostringstream buf; - buf << "Error reading frame: " << strerror(errno); - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_SEEKING, buf.str()); - } - } else { - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - "Short read while reading frame"); - } - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - "Unknown read error"); - } - - return; - } -} - -void InitNullPacket(AVPacket &pkt) { - av_init_packet(&pkt); - pkt.data = NULL; - pkt.size = 0; -} - -void FillAP(FFMS_AudioProperties &AP, AVCodecContext *CTX, FFMS_Track &Frames) { - AP.SampleFormat = static_cast(CTX->sample_fmt); - AP.BitsPerSample = av_get_bits_per_sample_fmt(CTX->sample_fmt); - if (CTX->sample_fmt == AV_SAMPLE_FMT_S32 && CTX->bits_per_raw_sample) - AP.BitsPerSample = CTX->bits_per_raw_sample; - - AP.Channels = CTX->channels;; - AP.ChannelLayout = CTX->channel_layout; - AP.SampleRate = CTX->sample_rate; - if (Frames.size() > 0) { - AP.NumSamples = (Frames.back()).SampleStart + (Frames.back()).SampleCount; - AP.FirstTime = ((Frames.front().PTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; - AP.LastTime = ((Frames.back().PTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; - } -} - -#ifdef HAALISOURCE - -unsigned vtSize(VARIANT &vt) { - if (V_VT(&vt) != (VT_ARRAY | VT_UI1)) - return 0; - long lb,ub; - if (FAILED(SafeArrayGetLBound(V_ARRAY(&vt),1,&lb)) || - FAILED(SafeArrayGetUBound(V_ARRAY(&vt),1,&ub))) - return 0; - return ub - lb + 1; -} - -void vtCopy(VARIANT& vt,void *dest) { - unsigned sz = vtSize(vt); - if (sz > 0) { - void *vp; - if (SUCCEEDED(SafeArrayAccessData(V_ARRAY(&vt),&vp))) { - memcpy(dest,vp,sz); - SafeArrayUnaccessData(V_ARRAY(&vt)); - } - } -} - -#endif - -CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC, unsigned int BitsPerSample) { -/* Look up native codecs */ - for(int i = 0; ff_mkv_codec_tags[i].id != CODEC_ID_NONE; i++){ - if(!strncmp(ff_mkv_codec_tags[i].str, Codec, - strlen(ff_mkv_codec_tags[i].str))) { - - // Uncompressed and exotic format fixup - // This list is incomplete - CodecID CID = ff_mkv_codec_tags[i].id; - switch (CID) { - case CODEC_ID_PCM_S16LE: - switch (BitsPerSample) { - case 8: CID = CODEC_ID_PCM_S8; break; - case 16: CID = CODEC_ID_PCM_S16LE; break; - case 24: CID = CODEC_ID_PCM_S24LE; break; - case 32: CID = CODEC_ID_PCM_S32LE; break; - } - break; - case CODEC_ID_PCM_S16BE: - switch (BitsPerSample) { - case 8: CID = CODEC_ID_PCM_S8; break; - case 16: CID = CODEC_ID_PCM_S16BE; break; - case 24: CID = CODEC_ID_PCM_S24BE; break; - case 32: CID = CODEC_ID_PCM_S32BE; break; - } - break; - default: - break; - } - - return CID; - } - } - -/* Video codecs for "avi in mkv" mode */ - const AVCodecTag *const tags[] = { ff_codec_bmp_tags, 0 }; - - if (!strcmp(Codec, "V_MS/VFW/FOURCC")) { - FFMS_BITMAPINFOHEADER *b = reinterpret_cast(CodecPrivate); - return av_codec_get_id(tags, b->biCompression); - } - - if (!strcmp(Codec, "V_FOURCC")) { - return av_codec_get_id(tags, FourCC); - } - -// FIXME -/* Audio codecs for "acm in mkv" mode */ - //#include "Mmreg.h" - //((WAVEFORMATEX *)TI->CodecPrivate)->wFormatTag - -/* Fixup for uncompressed video formats */ - -/* Fixup for uncompressed audio formats */ - - return CODEC_ID_NONE; -} - -void InitializeCodecContextFromMatroskaTrackInfo(TrackInfo *TI, AVCodecContext *CodecContext) { - CodecContext->extradata = static_cast(TI->CodecPrivate); - CodecContext->extradata_size = TI->CodecPrivateSize; - - if (TI->Type == TT_VIDEO) { - CodecContext->coded_width = TI->AV.Video.PixelWidth; - CodecContext->coded_height = TI->AV.Video.PixelHeight; - } else if (TI->Type == TT_AUDIO) { - CodecContext->sample_rate = mkv_TruncFloat(TI->AV.Audio.SamplingFreq); - CodecContext->bits_per_coded_sample = TI->AV.Audio.BitDepth; - CodecContext->channels = TI->AV.Audio.Channels; - } -} - -#ifdef HAALISOURCE - -FFCodecContext InitializeCodecContextFromHaaliInfo(CComQIPtr pBag) { - CComVariant pV; - if (FAILED(pBag->Read(L"Type", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) - return FFCodecContext(); - - unsigned int TT = pV.uintVal; - - FFCodecContext CodecContext(avcodec_alloc_context(), DeleteHaaliCodecContext); - - unsigned int FourCC = 0; - if (TT == TT_VIDEO) { - pV.Clear(); - if (SUCCEEDED(pBag->Read(L"Video.PixelWidth", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) - CodecContext->coded_width = pV.uintVal; - - pV.Clear(); - if (SUCCEEDED(pBag->Read(L"Video.PixelHeight", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) - CodecContext->coded_height = pV.uintVal; - - pV.Clear(); - if (SUCCEEDED(pBag->Read(L"FOURCC", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) - FourCC = pV.uintVal; - - // Reconstruct the missing codec private part for VC1 - FFMS_BITMAPINFOHEADER bih; - memset(&bih, 0, sizeof bih); - bih.biSize = sizeof bih; - bih.biCompression = FourCC; - bih.biBitCount = 24; - bih.biPlanes = 1; - bih.biHeight = CodecContext->coded_height; - - pV.Clear(); - if (SUCCEEDED(pBag->Read(L"CodecPrivate", &pV, NULL))) { - bih.biSize += vtSize(pV); - CodecContext->extradata = static_cast(av_malloc(bih.biSize)); - memcpy(CodecContext->extradata, &bih, sizeof bih); - vtCopy(pV, CodecContext->extradata + sizeof bih); - } - else { - CodecContext->extradata = static_cast(av_malloc(bih.biSize)); - memcpy(CodecContext->extradata, &bih, sizeof bih); - } - CodecContext->extradata_size = bih.biSize; - } - else if (TT == TT_AUDIO) { - pV.Clear(); - if (SUCCEEDED(pBag->Read(L"CodecPrivate", &pV, NULL))) { - CodecContext->extradata_size = vtSize(pV); - CodecContext->extradata = static_cast(av_malloc(CodecContext->extradata_size)); - vtCopy(pV, CodecContext->extradata); - } - - pV.Clear(); - if (SUCCEEDED(pBag->Read(L"Audio.SamplingFreq", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) - CodecContext->sample_rate = pV.uintVal; - - pV.Clear(); - if (SUCCEEDED(pBag->Read(L"Audio.BitDepth", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) - CodecContext->bits_per_coded_sample = pV.uintVal; - - pV.Clear(); - if (SUCCEEDED(pBag->Read(L"Audio.Channels", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) - CodecContext->channels = pV.uintVal; - } - - pV.Clear(); - if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) { - char CodecStr[2048]; - wcstombs(CodecStr, pV.bstrVal, 2000); - - CodecContext->codec = avcodec_find_decoder(MatroskaToFFCodecID(CodecStr, CodecContext->extradata, FourCC, CodecContext->bits_per_coded_sample)); - } - return CodecContext; -} - -#endif - - -// All this filename chikanery that follows is supposed to make sure both local -// codepage (used by avisynth etc) and UTF8 (potentially used by API users) strings -// work correctly on Win32. -// It's a really ugly hack, and I blame Microsoft for it. -#ifdef _WIN32 -static wchar_t *dup_char_to_wchar(const char *s, unsigned int cp) { - wchar_t *w; - int l; - if (!(l = MultiByteToWideChar(cp, MB_ERR_INVALID_CHARS, s, -1, NULL, 0))) - return NULL; - if (!(w = (wchar_t *)malloc(l * sizeof(wchar_t)))) - return NULL; - if (MultiByteToWideChar(cp, MB_ERR_INVALID_CHARS, s, -1 , w, l) <= 0) { - free(w); - w = NULL; - } - return w; -} -#endif - -FILE *ffms_fopen(const char *filename, const char *mode) { -#ifdef _WIN32 - unsigned int codepage; - if (GlobalUseUTF8Paths) - codepage = CP_UTF8; - else - codepage = CP_ACP; - - FILE *ret; - wchar_t *filename_wide = dup_char_to_wchar(filename, codepage); - wchar_t *mode_wide = dup_char_to_wchar(mode, codepage); - if (filename_wide && mode_wide) - ret = _wfopen(filename_wide, mode_wide); - else - ret = fopen(filename, mode); - - free(filename_wide); - free(mode_wide); - - return ret; -#else - return fopen(filename, mode); -#endif /* _WIN32 */ -} - -size_t ffms_mbstowcs (wchar_t *wcstr, const char *mbstr, size_t max) { -#ifdef _WIN32 - // this is only called by HaaliOpenFile anyway, so I think this is safe - return static_cast(MultiByteToWideChar((GlobalUseUTF8Paths ? CP_UTF8 : CP_ACP), MB_ERR_INVALID_CHARS, mbstr, -1, wcstr, max)); -#else - return mbstowcs(wcstr, mbstr, max); -#endif -} - - -// ffms_fstream stuff -void ffms_fstream::open(const char *filename, std::ios_base::openmode mode) { - // Unlike MSVC, mingw's iostream library doesn't have an fstream overload - // that takes a wchar_t* filename, which means you can't open unicode - // filenames with it on Windows. gg. -#if defined(_WIN32) && !defined(__MINGW32__) - unsigned int codepage = GlobalUseUTF8Paths ? CP_UTF8 : CP_ACP; - - wchar_t *filename_wide = dup_char_to_wchar(filename, codepage); - if (filename_wide) - std::fstream::open(filename_wide, mode); - else - std::fstream::open(filename, mode); - - free(filename_wide); -#else // defined(_WIN32) && !defined(__MINGW32__) - std::fstream::open(filename, mode); -#endif // defined(_WIN32) && !defined(__MINGW32__) -} - -ffms_fstream::ffms_fstream(const char *filename, std::ios_base::openmode mode) { - open(filename, mode); -} - - -#ifdef _WIN32 -int ffms_wchar_open(const char *fname, int oflags, int pmode) { - wchar_t *wfname = dup_char_to_wchar(fname, CP_UTF8); - if (wfname) { - int ret = _wopen(wfname, oflags, pmode); - free(wfname); - return ret; - } - return -1; -} - -static int ffms_lavf_file_open(URLContext *h, const char *filename, int flags) { - int access; - int fd; - - av_strstart(filename, "file:", &filename); - - if (flags & URL_RDWR) { - access = _O_CREAT | _O_TRUNC | _O_RDWR; - } else if (flags & URL_WRONLY) { - access = _O_CREAT | _O_TRUNC | _O_WRONLY; - } else { - access = _O_RDONLY; - } -#ifdef _O_BINARY - access |= _O_BINARY; -#endif - fd = ffms_wchar_open(filename, access, 0666); - if (fd == -1) - return AVERROR(ENOENT); - h->priv_data = (void *) (intptr_t) fd; - return 0; -} - -// Hijack lavf's file protocol handler's open function and use our own instead. -// Hack by nielsm. -void ffms_patch_lavf_file_open() { - extern URLProtocol *first_protocol; - URLProtocol *proto = first_protocol; - while (proto != NULL) { - if (strcmp("file", proto->name) == 0) { - break; - } - proto = proto->next; - } - if (proto != NULL) { - proto->url_open = &ffms_lavf_file_open; - } -} -#endif // _WIN32 - -// End of filename hackery. - - -#ifdef HAALISOURCE - -CComPtr HaaliOpenFile(const char *SourceFile, enum FFMS_Sources SourceMode) { - CComPtr pMMC; - - CLSID clsid = HAALI_MPEG_PARSER; - if (SourceMode == FFMS_SOURCE_HAALIOGG) - clsid = HAALI_OGG_PARSER; - - if (FAILED(pMMC.CoCreateInstance(clsid))) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED, - "Can't create parser"); - - CComPtr pMA; - if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED, - "Can't create memory allocator"); - - CComPtr pMS; - if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED, - "Can't create disk file reader"); - - WCHAR WSourceFile[2048]; - ffms_mbstowcs(WSourceFile, SourceFile, 2000); - CComQIPtr pMSO(pMS); - if (FAILED(pMSO->Open(WSourceFile))) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - "Can't open file"); - - if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) { - if (SourceMode == FFMS_SOURCE_HAALIMPEG) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_INVALID_ARGUMENT, - "Can't parse file, most likely a transport stream not cut at packet boundaries"); - else - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_INVALID_ARGUMENT, - "Can't parse file"); - } - - return pMMC; -} - -#endif - -void LAVFOpenFile(const char *SourceFile, AVFormatContext *&FormatContext) { - if (av_open_input_file(&FormatContext, SourceFile, NULL, 0, NULL) != 0) - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - std::string("Couldn't open '") + SourceFile + "'"); - - if (av_find_stream_info(FormatContext) < 0) { - av_close_input_file(FormatContext); - FormatContext = NULL; - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, - "Couldn't find stream information"); - } -} - - -// attempt to correct framerate to the proper NTSC fraction, if applicable -// code stolen from Perian -void CorrectNTSCRationalFramerate(int *Num, int *Den) { - AVRational TempFPS; - TempFPS.den = *Num; // not a typo - TempFPS.num = *Den; // still not a typo - - av_reduce(&TempFPS.num, &TempFPS.den, TempFPS.num, TempFPS.den, INT_MAX); - - if (TempFPS.num == 1) { - *Num = TempFPS.den; - *Den = TempFPS.num; - } - else { - double FTimebase = av_q2d(TempFPS); - double NearestNTSC = floor(FTimebase * 1001.0 + 0.5) / 1001.0; - const double SmallInterval = 1.0/120.0; - - if (fabs(FTimebase - NearestNTSC) < SmallInterval) { - *Num = int((1001.0 / FTimebase) + 0.5); - *Den = 1001; - } - } -} - -// correct the timebase if it is invalid -void CorrectTimebase(FFMS_VideoProperties *VP, FFMS_TrackTimeBase *TTimebase) { - double Timebase = (double)TTimebase->Num / TTimebase->Den; - double FPS = (double)VP->FPSNumerator / VP->FPSDenominator; - if ((1000/Timebase) / FPS < 1) { - TTimebase->Den = VP->FPSNumerator; - TTimebase->Num = (int64_t)VP->FPSDenominator * 1000; - } -} diff --git a/aegisub/libffms/src/core/utils.h b/aegisub/libffms/src/core/utils.h deleted file mode 100644 index 0370a309b..000000000 --- a/aegisub/libffms/src/core/utils.h +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#ifndef UTILS_H -#define UTILS_H - -#include -#include -#include -#include -#include "ffms.h" -#include "matroskaparser.h" - -extern "C" { -#include "stdiostream.h" -#include -#include -#include -#include -#ifdef FFMS_USE_POSTPROC -#include -#endif // FFMS_USE_POSTPROC -} - -// must be included after ffmpeg headers -#include "ffmscompat.h" - -#ifdef HAALISOURCE -# define WIN32_LEAN_AND_MEAN -# define _WIN32_DCOM -# include -# include -# include -# include -# include -# include "CoParser.h" -# include "guids.h" -#endif - -#define FFMS_GET_VECTOR_PTR(v) (((v).size() ? &(v)[0] : NULL)) - -const int64_t ffms_av_nopts_value = static_cast(1) << 63; - -// used for matroska<->ffmpeg codec ID mapping to avoid Win32 dependency -typedef struct FFMS_BITMAPINFOHEADER { - uint32_t biSize; - int32_t biWidth; - int32_t biHeight; - uint16_t biPlanes; - uint16_t biBitCount; - uint32_t biCompression; - uint32_t biSizeImage; - int32_t biXPelsPerMeter; - int32_t biYPelsPerMeter; - uint32_t biClrUsed; - uint32_t biClrImportant; -} FFMS_BITMAPINFOHEADER; - -class FFMS_Exception : public std::exception { -private: - std::string _Message; - int _ErrorType; - int _SubType; -public: - FFMS_Exception(int ErrorType, int SubType, const char *Message = ""); - FFMS_Exception(int ErrorType, int SubType, const std::string &Message); - ~FFMS_Exception() throw (); - const std::string &GetErrorMessage() const; - int CopyOut(FFMS_ErrorInfo *ErrorInfo) const; -}; - -template -class FFSourceResources { -private: - T *_PrivClass; - bool _Enabled; - bool _Arg; -public: - FFSourceResources(T *Target) : _PrivClass(Target), _Enabled(true), _Arg(false) { - } - - ~FFSourceResources() { - if (_Enabled) - _PrivClass->Free(_Arg); - } - - void SetEnabled(bool Enabled) { - _Enabled = Enabled; - } - - void SetArg(bool Arg) { - _Arg = Arg; - } - - void CloseCodec(bool Arg) { - _Arg = Arg; - } -}; -// auto_ptr-ish holder for AVCodecContexts with overridable deleter -class FFCodecContext { - AVCodecContext *CodecContext; - void (*Deleter)(AVCodecContext *); -public: - FFCodecContext() : CodecContext(0), Deleter(0) { } - FFCodecContext(FFCodecContext &r) : CodecContext(r.CodecContext), Deleter(r.Deleter) { r.CodecContext = 0; } - FFCodecContext(AVCodecContext *c, void (*d)(AVCodecContext *)) : CodecContext(c), Deleter(d) { } - FFCodecContext& operator=(FFCodecContext r) { reset(r.CodecContext, r.Deleter); r.CodecContext = 0; return *this; } - ~FFCodecContext() { reset(); } - AVCodecContext* operator->() { return CodecContext; } - operator AVCodecContext*() { return CodecContext; } - void reset(AVCodecContext *c = 0, void (*d)(AVCodecContext *) = 0) { - if (CodecContext && Deleter) Deleter(CodecContext); - CodecContext = c; - Deleter = d; - } -}; - -inline void DeleteHaaliCodecContext(AVCodecContext *CodecContext) { - av_freep(&CodecContext->extradata); - av_freep(&CodecContext); -} -inline void DeleteMatroskaCodecContext(AVCodecContext *CodecContext) { - avcodec_close(CodecContext); - av_freep(&CodecContext); -} - -struct MatroskaReaderContext { -public: - StdIoStream ST; - uint8_t *Buffer; - unsigned int BufferSize; - char CSBuffer[4096]; - - MatroskaReaderContext() { - InitStdIoStream(&ST); - Buffer = NULL; - BufferSize = 0; - } - - ~MatroskaReaderContext() { - free(Buffer); - if (ST.fp) fclose(ST.fp); - } -}; - -class ffms_fstream : public std::fstream { -public: - void open(const char *filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out); - ffms_fstream(const char *filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out); -}; - -template -class AlignedBuffer { - T *buf; - -public: - AlignedBuffer(size_t n = 1) { - buf = (T*) av_malloc(sizeof(*buf) * n); - if (!buf) throw std::bad_alloc(); - } - - ~AlignedBuffer() { - av_free(buf); - buf = 0; - } - - const T &operator[] (size_t i) const { return buf[i]; } - T &operator[] (size_t i) { return buf[i]; } -}; - - -class TrackCompressionContext { -public: - CompressedStream *CS; - unsigned CompressionMethod; - void *CompressedPrivateData; - unsigned CompressedPrivateDataSize; - - TrackCompressionContext(MatroskaFile *MF, TrackInfo *TI, unsigned int Track); - ~TrackCompressionContext(); -}; - - -int64_t GetSWSCPUFlags(); -SwsContext *GetSwsContext(int SrcW, int SrcH, PixelFormat SrcFormat, int DstW, int DstH, PixelFormat DstFormat, int64_t Flags, int ColorSpace = -1); -int GetPPCPUFlags(); -void ClearErrorInfo(FFMS_ErrorInfo *ErrorInfo); -FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT); -void ReadFrame(uint64_t FilePos, unsigned int &FrameSize, TrackCompressionContext *TCC, MatroskaReaderContext &Context); -bool AudioFMTIsFloat(AVSampleFormat FMT); -void InitNullPacket(AVPacket &pkt); -void FillAP(FFMS_AudioProperties &AP, AVCodecContext *CTX, FFMS_Track &Frames); - -#ifdef HAALISOURCE -unsigned vtSize(VARIANT &vt); -void vtCopy(VARIANT& vt,void *dest); -FFCodecContext InitializeCodecContextFromHaaliInfo(CComQIPtr pBag); -#endif - -void InitializeCodecContextFromMatroskaTrackInfo(TrackInfo *TI, AVCodecContext *CodecContext); -CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC = 0, unsigned int BitsPerSample = 0); -FILE *ffms_fopen(const char *filename, const char *mode); -size_t ffms_mbstowcs (wchar_t *wcstr, const char *mbstr, size_t max); -#ifdef _WIN32 -void ffms_patch_lavf_file_open(); -#endif // _WIN32 -#ifdef HAALISOURCE -CComPtr HaaliOpenFile(const char *SourceFile, enum FFMS_Sources SourceMode); -#endif // HAALISOURCE -void LAVFOpenFile(const char *SourceFile, AVFormatContext *&FormatContext); -void CorrectNTSCRationalFramerate(int *Num, int *Den); -void CorrectTimebase(FFMS_VideoProperties *VP, FFMS_TrackTimeBase *TTimebase); -const char *GetLAVCSampleFormatName(AVSampleFormat s); - -#endif diff --git a/aegisub/libffms/src/core/videosource.cpp b/aegisub/libffms/src/core/videosource.cpp deleted file mode 100644 index 1c3d5dcdb..000000000 --- a/aegisub/libffms/src/core/videosource.cpp +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include "videosource.h" - -void FFMS_VideoSource::GetFrameCheck(int n) { - if (n < 0 || n >= VP.NumFrames) - throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_INVALID_ARGUMENT, - "Out of bounds frame requested"); -} - -void FFMS_VideoSource::SetPP(const char *PP) { - -#ifdef FFMS_USE_POSTPROC - if (PPMode) - pp_free_mode(PPMode); - PPMode = NULL; - - if (PP != NULL && strcmp(PP, "")) { - PPMode = pp_get_mode_by_name_and_quality(PP, PP_QUALITY_MAX); - if (!PPMode) { - ResetPP(); - throw FFMS_Exception(FFMS_ERROR_POSTPROCESSING, FFMS_ERROR_INVALID_ARGUMENT, - "Invalid postprocesing settings"); - } - - } - - ReAdjustPP(CodecContext->pix_fmt, CodecContext->width, CodecContext->height); - OutputFrame(DecodeFrame); -#else - throw FFMS_Exception(FFMS_ERROR_POSTPROCESSING, FFMS_ERROR_UNSUPPORTED, - "FFMS2 was not compiled with postprocessing support"); -#endif /* FFMS_USE_POSTPROC */ -} - -void FFMS_VideoSource::ResetPP() { -#ifdef FFMS_USE_POSTPROC - if (PPContext) - pp_free_context(PPContext); - PPContext = NULL; - - if (PPMode) - pp_free_mode(PPMode); - PPMode = NULL; - -#endif /* FFMS_USE_POSTPROC */ - OutputFrame(DecodeFrame); -} - -void FFMS_VideoSource::ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height) { -#ifdef FFMS_USE_POSTPROC - if (PPContext) - pp_free_context(PPContext); - PPContext = NULL; - - if (!PPMode) - return; - - int Flags = GetPPCPUFlags(); - - switch (VPixelFormat) { - case PIX_FMT_YUV420P: - case PIX_FMT_YUVJ420P: - Flags |= PP_FORMAT_420; break; - case PIX_FMT_YUV422P: - case PIX_FMT_YUVJ422P: - Flags |= PP_FORMAT_422; break; - case PIX_FMT_YUV411P: - Flags |= PP_FORMAT_411; break; - case PIX_FMT_YUV444P: - case PIX_FMT_YUVJ444P: - Flags |= PP_FORMAT_444; break; - default: - ResetPP(); - throw FFMS_Exception(FFMS_ERROR_POSTPROCESSING, FFMS_ERROR_UNSUPPORTED, - "The video does not have a colorspace suitable for postprocessing"); - } - - PPContext = pp_get_context(Width, Height, Flags); - - avpicture_free(&PPFrame); - avpicture_alloc(&PPFrame, VPixelFormat, Width, Height); -#else - return; -#endif /* FFMS_USE_POSTPROC */ - -} - - -static void CopyAVPictureFields(AVPicture &Picture, FFMS_Frame &Dst) { - for (int i = 0; i < 4; i++) { - Dst.Data[i] = Picture.data[i]; - Dst.Linesize[i] = Picture.linesize[i]; - } -} - -FFMS_Frame *FFMS_VideoSource::OutputFrame(AVFrame *Frame) { - if (LastFrameWidth != CodecContext->width || LastFrameHeight != CodecContext->height || LastFramePixelFormat != CodecContext->pix_fmt) { - ReAdjustPP(CodecContext->pix_fmt, CodecContext->width, CodecContext->height); - - if (TargetHeight > 0 && TargetWidth > 0 && TargetPixelFormats != 0) - ReAdjustOutputFormat(TargetPixelFormats, TargetWidth, TargetHeight, TargetResizer); - } - -#ifdef FFMS_USE_POSTPROC - if (PPMode) { - pp_postprocess(const_cast(Frame->data), Frame->linesize, PPFrame.data, PPFrame.linesize, CodecContext->width, CodecContext->height, Frame->qscale_table, Frame->qstride, PPMode, PPContext, Frame->pict_type | (Frame->qscale_type ? PP_PICT_TYPE_QP2 : 0)); - if (SWS) { - sws_scale(SWS, const_cast(PPFrame.data), PPFrame.linesize, 0, CodecContext->height, SWSFrame.data, SWSFrame.linesize); - CopyAVPictureFields(SWSFrame, LocalFrame); - } else { - CopyAVPictureFields(PPFrame, LocalFrame); - } - } else { - if (SWS) { - sws_scale(SWS, const_cast(Frame->data), Frame->linesize, 0, CodecContext->height, SWSFrame.data, SWSFrame.linesize); - CopyAVPictureFields(SWSFrame, LocalFrame); - } else { - // Special case to avoid ugly casts - for (int i = 0; i < 4; i++) { - LocalFrame.Data[i] = Frame->data[i]; - LocalFrame.Linesize[i] = Frame->linesize[i]; - } - } - } -#else // FFMS_USE_POSTPROC - if (SWS) { - sws_scale(SWS, const_cast(Frame->data), Frame->linesize, 0, CodecContext->height, SWSFrame.data, SWSFrame.linesize); - CopyAVPictureFields(SWSFrame, LocalFrame); - } else { - // Special case to avoid ugly casts - for (int i = 0; i < 4; i++) { - LocalFrame.Data[i] = Frame->data[i]; - LocalFrame.Linesize[i] = Frame->linesize[i]; - } - } -#endif // FFMS_USE_POSTPROC - - LocalFrame.EncodedWidth = CodecContext->width; - LocalFrame.EncodedHeight = CodecContext->height; - LocalFrame.EncodedPixelFormat = CodecContext->pix_fmt; - LocalFrame.ScaledWidth = TargetWidth; - LocalFrame.ScaledHeight = TargetHeight; - LocalFrame.ConvertedPixelFormat = OutputFormat; - LocalFrame.KeyFrame = Frame->key_frame; - LocalFrame.PictType = av_get_pict_type_char(Frame->pict_type); - LocalFrame.RepeatPict = Frame->repeat_pict; - LocalFrame.InterlacedFrame = Frame->interlaced_frame; - LocalFrame.TopFieldFirst = Frame->top_field_first; - - LastFrameHeight = CodecContext->height; - LastFrameWidth = CodecContext->width; - LastFramePixelFormat = CodecContext->pix_fmt; - - return &LocalFrame; -} - -FFMS_VideoSource::FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, int Track) { - if (Track < 0 || Track >= static_cast(Index->size())) - throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, - "Out of bounds track index selected"); - - if (Index->at(Track).TT != FFMS_TYPE_VIDEO) - throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, - "Not a video track"); - - if (Index->at(Track).size() == 0) - throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, - "Video track contains no frames"); - - if (!Index->CompareFileSignature(SourceFile)) - throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH, - "The index does not match the source file"); - - memset(&VP, 0, sizeof(VP)); -#ifdef FFMS_USE_POSTPROC - PPContext = NULL; - PPMode = NULL; -#endif // FFMS_USE_POSTPROC - SWS = NULL; - LastFrameNum = 0; - CurrentFrame = 1; - DelayCounter = 0; - InitialDecode = 1; - CodecContext = NULL; - LastFrameHeight = -1; - LastFrameWidth = -1; - LastFramePixelFormat = PIX_FMT_NONE; - TargetHeight = -1; - TargetWidth = -1; - TargetPixelFormats = 0; - TargetResizer = 0; - OutputFormat = PIX_FMT_NONE; - DecodeFrame = avcodec_alloc_frame(); - - // Dummy allocations so the unallocated case doesn't have to be handled later -#ifdef FFMS_USE_POSTPROC - avpicture_alloc(&PPFrame, PIX_FMT_GRAY8, 16, 16); -#endif // FFMS_USE_POSTPROC - avpicture_alloc(&SWSFrame, PIX_FMT_GRAY8, 16, 16); -} - -FFMS_VideoSource::~FFMS_VideoSource() { -#ifdef FFMS_USE_POSTPROC - if (PPMode) - pp_free_mode(PPMode); - - if (PPContext) - pp_free_context(PPContext); - - avpicture_free(&PPFrame); -#endif // FFMS_USE_POSTPROC - - if (SWS) - sws_freeContext(SWS); - - avpicture_free(&SWSFrame); - av_freep(&DecodeFrame); -} - -FFMS_Frame *FFMS_VideoSource::GetFrameByTime(double Time) { - int Frame = Frames.ClosestFrameFromPTS(static_cast((Time * 1000 * Frames.TB.Den) / Frames.TB.Num)); - return GetFrame(Frame); -} - -void FFMS_VideoSource::SetOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer) { - this->TargetWidth = Width; - this->TargetHeight = Height; - this->TargetPixelFormats = TargetFormats; - this->TargetResizer = Resizer; - ReAdjustOutputFormat(TargetFormats, Width, Height, Resizer); - OutputFrame(DecodeFrame); -} - -void FFMS_VideoSource::ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer) { - if (SWS) { - sws_freeContext(SWS); - SWS = NULL; - } - - int Loss; - OutputFormat = avcodec_find_best_pix_fmt(TargetFormats, - CodecContext->pix_fmt, 1 /* Required to prevent pointless RGB32 => RGB24 conversion */, &Loss); - if (OutputFormat == PIX_FMT_NONE) { - ResetOutputFormat(); - throw FFMS_Exception(FFMS_ERROR_SCALING, FFMS_ERROR_INVALID_ARGUMENT, - "No suitable output format found"); - } - - if (CodecContext->pix_fmt != OutputFormat || Width != CodecContext->width || Height != CodecContext->height) { - int ColorSpace = CodecContext->colorspace; - if (ColorSpace == AVCOL_SPC_UNSPECIFIED) ColorSpace = -1; - SWS = GetSwsContext(CodecContext->width, CodecContext->height, CodecContext->pix_fmt, Width, Height, - OutputFormat, GetSWSCPUFlags() | Resizer, ColorSpace); - if (SWS == NULL) { - ResetOutputFormat(); - throw FFMS_Exception(FFMS_ERROR_SCALING, FFMS_ERROR_INVALID_ARGUMENT, - "Failed to allocate SWScale context"); - } - } - - avpicture_free(&SWSFrame); - avpicture_alloc(&SWSFrame, OutputFormat, Width, Height); -} - -void FFMS_VideoSource::ResetOutputFormat() { - if (SWS) { - sws_freeContext(SWS); - SWS = NULL; - } - - TargetWidth = -1; - TargetHeight = -1; - TargetPixelFormats = 0; - OutputFormat = PIX_FMT_NONE; - - OutputFrame(DecodeFrame); -} diff --git a/aegisub/libffms/src/core/videosource.h b/aegisub/libffms/src/core/videosource.h deleted file mode 100644 index ee4f92dcc..000000000 --- a/aegisub/libffms/src/core/videosource.h +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#ifndef FFVIDEOSOURCE_H -#define FFVIDEOSOURCE_H - -extern "C" { -#include -#include -#include -#ifdef FFMS_USE_POSTPROC -#include -#endif // FFMS_USE_POSTPROC -} - -// must be included after ffmpeg headers -#include "ffmscompat.h" - -#include -#include -#include "indexing.h" -#include "utils.h" -#include "ffms.h" - -#ifdef HAALISOURCE -# define WIN32_LEAN_AND_MEAN -# define _WIN32_DCOM -# include -# include -# include -# include -# include -# include "CoParser.h" -# include "guids.h" -#endif - -class FFMS_VideoSource { -friend class FFSourceResources; -private: -#ifdef FFMS_USE_POSTPROC - pp_context_t *PPContext; - pp_mode_t *PPMode; -#endif // FFMS_USE_POSTPROC - SwsContext *SWS; - int LastFrameHeight; - int LastFrameWidth; - PixelFormat LastFramePixelFormat; - int TargetHeight; - int TargetWidth; - int64_t TargetPixelFormats; - int TargetResizer; - PixelFormat OutputFormat; - AVPicture PPFrame; - AVPicture SWSFrame; -protected: - FFMS_VideoProperties VP; - FFMS_Frame LocalFrame; - AVFrame *DecodeFrame; - int LastFrameNum; - FFMS_Track Frames; - int VideoTrack; - int CurrentFrame; - int DelayCounter; - int InitialDecode; - AVCodecContext *CodecContext; - - FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, int Track); - void ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height); - void ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer); - FFMS_Frame *OutputFrame(AVFrame *Frame); - virtual void Free(bool CloseCodec) = 0; -public: - virtual ~FFMS_VideoSource(); - const FFMS_VideoProperties& GetVideoProperties() { return VP; } - FFMS_Track *GetTrack() { return &Frames; } - virtual FFMS_Frame *GetFrame(int n) = 0; - void GetFrameCheck(int n); - FFMS_Frame *GetFrameByTime(double Time); - void SetPP(const char *PP); - void ResetPP(); - void SetOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer); - void ResetOutputFormat(); -}; - -class FFLAVFVideo : public FFMS_VideoSource { -private: - AVFormatContext *FormatContext; - int SeekMode; - FFSourceResources Res; - - void DecodeNextFrame(int64_t *PTS); -protected: - void Free(bool CloseCodec); -public: - FFLAVFVideo(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, int SeekMode); - FFMS_Frame *GetFrame(int n); -}; - -class FFMatroskaVideo : public FFMS_VideoSource { -private: - MatroskaFile *MF; - MatroskaReaderContext MC; - TrackCompressionContext *TCC; - char ErrorMessage[256]; - FFSourceResources Res; - size_t PacketNumber; - - void DecodeNextFrame(); -protected: - void Free(bool CloseCodec); -public: - FFMatroskaVideo(const char *SourceFile, int Track, FFMS_Index *Index, int Threads); - FFMS_Frame *GetFrame(int n); -}; - -#ifdef HAALISOURCE - -class FFHaaliVideo : public FFMS_VideoSource { - FFCodecContext HCodecContext; - CComPtr pMMC; - AVBitStreamFilterContext *BitStreamFilter; - FFSourceResources Res; - - void DecodeNextFrame(int64_t *AFirstStartTime); -protected: - void Free(bool CloseCodec); -public: - FFHaaliVideo(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, enum FFMS_Sources SourceMode); - FFMS_Frame *GetFrame(int n); -}; - -#endif // HAALISOURCE - -#endif diff --git a/aegisub/libffms/src/core/wave64writer.cpp b/aegisub/libffms/src/core/wave64writer.cpp deleted file mode 100644 index c2d82f202..000000000 --- a/aegisub/libffms/src/core/wave64writer.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include "wave64writer.h" -#include - -#define WAVE_FORMAT_IEEE_FLOAT 0x0003 -#define WAVE_FORMAT_PCM 1 - -static const uint8_t GuidRIFF[16]={ - // {66666972-912E-11CF-A5D6-28DB04C10000} - 0x72, 0x69, 0x66, 0x66, 0x2E, 0x91, 0xCF, 0x11, 0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00 -}; - -static const uint8_t GuidWAVE[16]={ - // {65766177-ACF3-11D3-8CD1-00C04F8EDB8A} - 0x77, 0x61, 0x76, 0x65, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A -}; - -static const uint8_t Guidfmt[16]={ - // {20746D66-ACF3-11D3-8CD1-00C04F8EDB8A} - 0x66, 0x6D, 0x74, 0x20, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A -}; - -static const uint8_t Guiddata[16]={ - // {61746164-ACF3-11D3-8CD1-00C04F8EDB8A} - 0x64, 0x61, 0x74, 0x61, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A -}; - -Wave64Writer::Wave64Writer(const char *Filename, uint16_t BitsPerSample, uint16_t Channels, uint32_t SamplesPerSec, bool IsFloat) : WavFile(Filename, std::ios::out | std::ios::binary | std::ios::trunc) { - BytesWritten = 0; - this->BitsPerSample = BitsPerSample; - this->Channels = Channels; - this->SamplesPerSec = SamplesPerSec; - this->IsFloat = IsFloat; - - if (!WavFile.is_open()) - throw "Failed to open destination file for writing"; - - WriteHeader(true, IsFloat); -} - -Wave64Writer::~Wave64Writer() { - WriteHeader(false, IsFloat); - WavFile.close(); -} - -void Wave64Writer::WriteHeader(bool Initial, bool IsFloat) { - FFMS_WAVEFORMATEX WFEX; - if (IsFloat) - WFEX.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; - else - WFEX.wFormatTag = WAVE_FORMAT_PCM; - WFEX.nChannels = Channels; - WFEX.nSamplesPerSec = SamplesPerSec; - WFEX.nAvgBytesPerSec = (BitsPerSample * Channels * SamplesPerSec) / 8; - WFEX.nBlockAlign = (BitsPerSample * Channels) / 8; - WFEX.wBitsPerSample = BitsPerSample; - WFEX.cbSize = 0; - - uint64_t Header[14]; - - memset(Header, 0, sizeof(Header)); - - memcpy(Header + 0, GuidRIFF, 16); - if (Initial) { - Header[2] = 0x7F00000000000000ull; - } else { - Header[2] = BytesWritten + sizeof(Header); - } - - memcpy(Header + 3, GuidWAVE, 16); - memcpy(Header + 5, Guidfmt, 16); - - Header[7] = 48; - - memcpy(Header + 8, &WFEX, sizeof(WFEX)); - memcpy(Header + 11, Guiddata, 16); - - if (Initial) - Header[13] = 0x7E00000000000000ull; - else - Header[13] = BytesWritten + 24; - - std::streampos CPos = WavFile.tellp(); - WavFile.seekp(0, std::ios::beg); - WavFile.write(reinterpret_cast(Header), sizeof(Header)); - if (!Initial) - WavFile.seekp(CPos, std::ios::beg); -} - -void Wave64Writer::WriteData(void *Data, std::streamsize Length) { - WavFile.write(reinterpret_cast(Data), Length); - BytesWritten += Length; -} diff --git a/aegisub/libffms/src/core/wave64writer.h b/aegisub/libffms/src/core/wave64writer.h deleted file mode 100644 index 3a5fddf3e..000000000 --- a/aegisub/libffms/src/core/wave64writer.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2007-2009 Fredrik Mellbin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#ifndef WAVE64WRITER_H -#define WAVE64WRITER_H - -#include -#include -#include -#include "utils.h" - -// this is to avoid depending on windows.h etc. -typedef struct FFMS_WAVEFORMATEX { - uint16_t wFormatTag; - uint16_t nChannels; - uint32_t nSamplesPerSec; - uint32_t nAvgBytesPerSec; - uint16_t nBlockAlign; - uint16_t wBitsPerSample; - uint16_t cbSize; -} FFMS_WAVEFORMATEX; - -class Wave64Writer { -public: - Wave64Writer(const char *Filename, uint16_t BitsPerSample, uint16_t Channels, uint32_t SamplesPerSec, bool IsFloat); - ~Wave64Writer(); - void WriteData(void *Data, std::streamsize Length); -private: - ffms_fstream WavFile; - int32_t BitsPerSample; - int32_t Channels; - uint32_t SamplesPerSec; - uint64_t BytesWritten; - uint32_t HeaderSize; - bool IsFloat; - - void WriteHeader(bool Initial, bool IsFloat); -}; - -#endif diff --git a/aegisub/libffms/wscript b/aegisub/libffms/wscript deleted file mode 100644 index 3dddeef0c..000000000 --- a/aegisub/libffms/wscript +++ /dev/null @@ -1,26 +0,0 @@ -#! /usr/bin/env python -# encoding: utf-8 -# -# Copyright (c) 2009, Kevin Ollivier -# -# Permission to use, copy, modify, and distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -def build(bld): - obj = bld.new_task_gen( - features = 'cc cxx cstaticlib', - target = 'ffmpegsource_aegisub', - defines = 'FFMS_EXPORTS HAVE_STRLCPY __STDC_CONSTANT_MACROS', - includes = 'include src/core', - uselib = 'FFMPEG') - - obj.find_sources_in_dirs('src/core') diff --git a/aegisub/src/Makefile b/aegisub/src/Makefile index badeeec04..1b5890b7e 100644 --- a/aegisub/src/Makefile +++ b/aegisub/src/Makefile @@ -55,12 +55,12 @@ endif ####################### # AUDIO / VIDEO SUPPORT ####################### -ifeq (yes, $(HAVE_PROVIDER_FFMPEGSOURCE)) +ifeq (yes, $(WITH_FFMS)) SRC_OPT += audio_provider_ffmpegsource.cpp video_provider_ffmpegsource.cpp ffmpegsource_common.cpp audio_provider_ffmpegsource.o video_provider_ffmpegsource.o ffmpegsource_common.o: \ - CXXFLAGS += $(CFLAGS_FFMPEGSOURCE) $(CFLAGS_LIBAVFORMAT) $(CFLAGS_LIBAVCODEC) $(CFLAGS_LIBSWSCALE) $(CFLAGS_LIBAVUTIL) $(CFLAGS_LIBPOSTPROC) + CXXFLAGS += $(CFLAGS_FFMS) ffmpegsource_common.o: CXXFLAGS += -D__STDC_FORMAT_MACROS -LDFLAGS += $(LDFLAGS_FFMPEGSOURCE) $(LDFLAGS_LIBAVFORMAT) $(LDFLAGS_LIBAVCODEC) $(LDFLAGS_LIBSWSCALE) $(LDFLAGS_LIBAVUTIL) $(LDFLAGS_LIBPOSTPROC) +LDFLAGS += $(LDFLAGS_FFMPEGSOURCE) LDFLAGS_POST += $(LDFLAGS_FFMPEGSOURCE) endif @@ -70,8 +70,8 @@ endif ########### ifeq (yes, $(WITH_LIBASS)) SRC_OPT += subtitles_provider_libass.cpp -subtitles_provider_libass.o: CXXFLAGS += $(CFLAGS_LIBASS) $(CFLAGS_ICONV) -LDFLAGS += $(LDFLAGS_LIBASS) $(LDFLAGS_ICONV) $(LDFLAGS_FONTCONFIG) +subtitles_provider_libass.o: CXXFLAGS += $(CFLAGS_LIBASS) +LDFLAGS += $(LDFLAGS_LIBASS) LDFLAGS_POST += $(LDFLAGS_LIBASS) endif @@ -106,7 +106,7 @@ charset_detect.o: CXXFLAGS += -D_X86_ font_file_lister_fontconfig.o: CXXFLAGS += $(CFLAGS_FONTCONFIG) $(CFLAGS_FREETYPE) font_file_lister.o: CXXFLAGS += $(CFLAGS_FREETYPE) text_file_reader.o: CXXFLAGS += -D_X86_ -video_provider_manager.o: CXXFLAGS += $(CFLAGS_FFMPEGSOURCE) +video_provider_manager.o: CXXFLAGS += $(CFLAGS_FFMS) # Ensure any optional source files above are added for compilation.