Kill in-repo libass and ffms and clean up some old cruft in the configure script

Originally committed to SVN as r5480.
This commit is contained in:
Thomas Goyne 2011-07-16 03:36:28 +00:00
parent da3e7e4d88
commit eea30df7db
66 changed files with 27 additions and 19409 deletions

View file

@ -33,12 +33,6 @@ covered by another license in the file itself.
The following directories and file are covered by their respective licenses as The following directories and file are covered by their respective licenses as
follows: follows:
libass/
- ISC license. See libass/COPYING.
libffms/
- MIT license.
src/boost/ src/boost/
- Boost Software License Version 1.0 see src/boost/LICENSE_1_0.txt. - Boost Software License Version 1.0 see src/boost/LICENSE_1_0.txt.

View file

@ -1,13 +1,5 @@
include Makefile.inc include Makefile.inc
ifeq (yes, $(WITH_LIBASS))
SUBDIRS += libass
endif
ifeq (yes, $(HAVE_PROVIDER_FFMPEGSOURCE))
SUBDIRS += libffms
endif
SUBDIRS += \ SUBDIRS += \
universalchardet \ universalchardet \
libaegisub \ libaegisub \

View file

@ -4,18 +4,15 @@
HAVE_ALSA = @with_alsa@ HAVE_ALSA = @with_alsa@
HAVE_AUTO4_LUA = @with_auto4_lua@ HAVE_AUTO4_LUA = @with_auto4_lua@
HAVE_AUTOMATION = @with_automation@ HAVE_AUTOMATION = @with_automation@
HAVE_FFMPEG = @agi_cv_with_ffmpeg@
HAVE_HUNSPELL = @with_hunspell@ HAVE_HUNSPELL = @with_hunspell@
HAVE_OPENAL = @with_openal@ HAVE_OPENAL = @with_openal@
HAVE_OPENMP = @with_openmp@ HAVE_OPENMP = @with_openmp@
HAVE_OSS = @with_oss@ HAVE_OSS = @with_oss@
HAVE_PORTAUDIO = @with_portaudio@ HAVE_PORTAUDIO = @with_portaudio@
HAVE_PROVIDER_FFMPEGSOURCE = @with_provider_ffmpegsource@ HAVE_FFMS = @with_ffms@
HAVE_PULSEAUDIO = @with_pulseaudio@ HAVE_PULSEAUDIO = @with_pulseaudio@
WITH_EXTERNAL_LIBASS = @with_external_libass@ HAVE_LIBASS = @with_libass@
WITH_LIBASS = @with_libass@
FOUND_AUDIO_PLAYER = @found_audio_player@ 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_FONTCONFIG = @FONTCONFIG_CFLAGS@
CFLAGS_FREETYPE = @FREETYPE_CFLAGS@ CFLAGS_FREETYPE = @FREETYPE_CFLAGS@
CFLAGS_GL = @GL_CFLAGS@ CFLAGS_GL = @GL_CFLAGS@
CFLAGS_FFMPEGSOURCE = -I../libffms/include CFLAGS_FFMS = @FFMS_CFLAGS@
CFLAGS_HUNSPELL = @HUNSPELL_CFLAGS@ CFLAGS_HUNSPELL = @HUNSPELL_CFLAGS@
CFLAGS_ICONV = @ICONV_CFLAGS@ CFLAGS_ICONV = @ICONV_CFLAGS@
CFLAGS_LIBASS = -I../libass CFLAGS_LIBASS = @LIBASS_CFLAGS@
#CFLAGS_LIBASS = @LIBASS_CFLAGS@
CFLAGS_LIBAVCODEC = @LIBAVCODEC_CFLAGS@
CFLAGS_LIBAVFORMAT = @LIBAVFORMAT_CFLAGS@
CFLAGS_LIBAVUTIL = @LIBAVUTIL_CFLAGS@
CFLAGS_LIBCURL = @LIBCURL_CFLAGS@ CFLAGS_LIBCURL = @LIBCURL_CFLAGS@
CFLAGS_LIBPOSTPROC = @LIBPOSTPROC_CFLAGS@
CFLAGS_LIBPULSE = @LIBPULSE_CFLAGS@ CFLAGS_LIBPULSE = @LIBPULSE_CFLAGS@
CFLAGS_LIBSWSCALE = @LIBSWSCALE_CFLAGS@
CFLAGS_LUA = @LUA_CFLAGS@ CFLAGS_LUA = @LUA_CFLAGS@
CFLAGS_OPENAL = @OPENAL_CFLAGS@ CFLAGS_OPENAL = @OPENAL_CFLAGS@
CFLAGS_PORTAUDIO = @PORTAUDIO_CFLAGS@ CFLAGS_PORTAUDIO = @PORTAUDIO_CFLAGS@
@ -116,18 +107,12 @@ LDFLAGS_ALSA = @ALSA_LDFLAGS@
LDFLAGS_FONTCONFIG = @FONTCONFIG_LIBS@ LDFLAGS_FONTCONFIG = @FONTCONFIG_LIBS@
LDFLAGS_FREETYPE = @FREETYPE_LIBS@ LDFLAGS_FREETYPE = @FREETYPE_LIBS@
LDFLAGS_GL = @GL_LIBS@ LDFLAGS_GL = @GL_LIBS@
LDFLAGS_FFMPEGSOURCE = ../libffms/libffmpegsource_aegisub.a LDFLAGS_FFMS = @FFMS_LIBS@
LDFLAGS_HUNSPELL = @HUNSPELL_LIBS@ LDFLAGS_HUNSPELL = @HUNSPELL_LIBS@
LDFLAGS_ICONV = @ICONV_LDFLAGS@ LDFLAGS_ICONV = @ICONV_LDFLAGS@
LDFLAGS_LIBASS = ../libass/libass_aegisub.a LDFLAGS_LIBASS = @LIBASS_LIBS@
#LDFLAGS_LIBASS = @LIBASS_LIBS@
LDFLAGS_LIBAVCODEC = @LIBAVCODEC_LIBS@
LDFLAGS_LIBAVFORMAT = @LIBAVFORMAT_LIBS@
LDFLAGS_LIBAVUTIL = @LIBAVUTIL_LIBS@
LDFLAGS_LIBCURL = @LIBCURL_LIBS@ LDFLAGS_LIBCURL = @LIBCURL_LIBS@
LDFLAGS_LIBPOSTPROC = @LIBPOSTPROC_LIBS@
LDFLAGS_LIBPULSE = @LIBPULSE_LIBS@ LDFLAGS_LIBPULSE = @LIBPULSE_LIBS@
LDFLAGS_LIBSWSCALE = @LIBSWSCALE_LIBS@
LDFLAGS_LUA = @LUA_LDFLAGS@ LDFLAGS_LUA = @LUA_LDFLAGS@
LDFLAGS_OPENAL = @OPENAL_LIBS@ LDFLAGS_OPENAL = @OPENAL_LIBS@
LDFLAGS_PTHREAD = @PTHREAD_LIBS@ LDFLAGS_PTHREAD = @PTHREAD_LIBS@

View file

@ -121,7 +121,7 @@ $(SUBDIRS):
$(MAKE) -C $@ $(MAKECMDGOALS) $(MAKE) -C $@ $(MAKECMDGOALS)
# Set relations to ensure dependencies are built before their targets during parallel builds. # 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 tests: libaegisub
reporter: src reporter: src
command: libresrc command: libresrc

View file

@ -16,12 +16,6 @@ m4_define([aegisub_version_data], [aegisub_version_major.aegisub_version_minor])
################### ###################
# Required packages # 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([lua_auto4_required_version], [5.1])
m4_define([portaudio_required_version], [19]) m4_define([portaudio_required_version], [19])
m4_define([pulseaudio_required_version], [0.5]) 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([pkgconfig_required_version], [0.20])
m4_define([wx_required_version], [2.9.0]) m4_define([wx_required_version], [2.9.0])
m4_define([libass_required_version], [0.9.7]) 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 # 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. if test "$with_ffms" != "no"; then
Disables FFMPEG and FFmpegSource A/V providers. PKG_CHECK_MODULES(FFMS, ffms2 >= ffms_required_version, [with_ffms="yes"], [with_ffms="no"])
(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"])
fi fi
if test "$with_ffmpeg" != "no" && test "$enable_old_ffmpeg" != "yes"; then AC_SUBST(with_ffms)
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 <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
}
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 <libpostproc/postprocess.h>
}
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)
################### ###################
# Subtitle Provider # 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)") if test "$with_libass" != "no"; then
AC_ARG_WITH(external-libass, [ --with-external-libass link to external libass (default=use internal)]) PKG_CHECK_MODULES(LIBASS, libass >= libass_required_version, [with_libass="yes"], [with_libass="no"])
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"
fi fi
AC_SUBST(with_libass) 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],[ AC_AGI_COMPILE([Hunspell], [hunspell], [$HUNSPELL_CFLAGS], [$HUNSPELL_LIBS],[
#include <hunspell.hxx> #include <hunspell.hxx>
int main(void) { int main(void) {
Hunspell *hunspell; return !(new Hunspell(".", "."));
hunspell = new Hunspell(".", ".");
if (!hunspell) return 1;
return 0;
} ]) } ])
fi fi
@ -1163,44 +1025,10 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
# it above. # 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) AC_ARG_WITH(player-audio, [ --with-player-audio=(alsa|openal|portaudio|pulseaudio)
Default Audio Player (default: Linux/ALSA, Default Audio Player (default: Linux/ALSA,
Darwin/OpenAL, 1:*/OSS, 2:*/PortAudio.]) 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. # Default audio player.
if ! test -z "$with_player_audio"; then if ! test -z "$with_player_audio"; then
default_player_audio="$with_player_audio" 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. # 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 if test -z "$default_player_audio"; then
default_player_audio="NONE" default_player_audio="NONE"
fi fi
############### ###############
# Misc settings # Misc settings
############### ###############
AC_SUBST(found_audio_player) AC_SUBST(found_audio_player)
AC_SUBST(found_audio_provider)
AC_SUBST(found_video_provider)
# Files that need substitution. # Files that need substitution.
AC_CONFIG_FILES([ AC_CONFIG_FILES([
@ -1273,7 +1085,7 @@ if test -z "$found_audio_player"; then
]) ])
fi fi
if test -z "$found_video_provider"; then if test "$with_ffms" != "yes"; then
AC_MSG_NOTICE([ AC_MSG_NOTICE([
*********************************************************************** ***********************************************************************
@ -1284,8 +1096,8 @@ if test -z "$found_video_provider"; then
* virtual video clip with subtitles overlaid. * virtual video clip with subtitles overlaid.
* Currently we only support one video/audio provider on non-Windows * Currently we only support one video/audio provider on non-Windows
* systems: * systems:
* - FFmpeg (libavcodec + libavformat) * - FFMS2
* * http://ffmpeg.mplayerhq.hu/ * * http://code.google.com/p/ffmpegsource/
*********************************************************************** ***********************************************************************
]) ])
fi fi
@ -1300,9 +1112,6 @@ Configure settings
LDFLAGS $LDFLAGS LDFLAGS $LDFLAGS
Default Settings Default Settings
Video Provider: $default_provider_video
Audio Provider: $default_provider_audio
Subtitle Provider: $default_provider_subtitle
Audio Player: $default_player_audio Audio Player: $default_player_audio
Scripting Engines Scripting Engines
@ -1315,16 +1124,11 @@ Audio Players
PortAudio: $with_portaudio $portaudio_disabled PortAudio: $with_portaudio $portaudio_disabled
PulseAudio: $with_pulseaudio $pulseaudio_disabled PulseAudio: $with_pulseaudio $pulseaudio_disabled
A/V Support
FFMPEG: $with_ffmpeg $ffmpeg_disabled
(required for video providers)
A/V Providers A/V Providers
FFmpegSource: $with_provider_ffmpegsource $ffmpegsource_provider_disabled FFMS2: $with_ffms $ffms_disabled
Subtitle Providers: Subtitle Providers:
libASS $with_libass $libass_disabled $libass_default libass $with_libass $libass_disabled
(both require iconv and fontconfig)
Misc Packages Misc Packages
Hunspell: $with_hunspell $with_hunspell_version $hunspell_disabled Hunspell: $with_hunspell $with_hunspell_version $hunspell_disabled

View file

@ -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.

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -1,383 +0,0 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* 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 <stdio.h>
#include <stdarg.h>
#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 */

View file

@ -1,531 +0,0 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* 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 <stdlib.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include <ft2build.h>
#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;
}

View file

@ -1,56 +0,0 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* 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 <ft2build.h>
#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 */

View file

@ -1,385 +0,0 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* 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 <inttypes.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include <assert.h>
#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);
}

View file

@ -1,117 +0,0 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* 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 */

View file

@ -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

View file

@ -1,492 +0,0 @@
/*
* Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org>
*
* 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 <ft2build.h>
#include FT_GLYPH_H
#include FT_OUTLINE_H
#include FT_BBOX_H
#include <math.h>
#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;
}

View file

@ -1,80 +0,0 @@
/*
* Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org>
*
* 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 <ft2build.h>
#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 */

View file

@ -1,701 +0,0 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* 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 <inttypes.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_SYNTHESIS_H
#include FT_GLYPH_H
#include FT_TRUETYPE_TABLES_H
#include FT_OUTLINE_H
#include <strings.h>
#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);
}

View file

@ -1,66 +0,0 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* 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 <stdint.h>
#include <ft2build.h>
#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 */

View file

@ -1,531 +0,0 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* 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 <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <inttypes.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include "ass_utils.h"
#include "ass.h"
#include "ass_library.h"
#include "ass_fontconfig.h"
#ifdef CONFIG_FONTCONFIG
#include <fontconfig/fontconfig.h>
#include <fontconfig/fcfreetype.h>
#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);
}

View file

@ -1,45 +0,0 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* 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 <stdint.h>
#include "ass_types.h"
#include "ass.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#ifdef CONFIG_FONTCONFIG
#include <fontconfig/fontconfig.h>
#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 */

View file

@ -1,146 +0,0 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* 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 <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#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;
}
}

View file

@ -1,41 +0,0 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* 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 <stdarg.h>
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 */

View file

@ -1,943 +0,0 @@
/*
* Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#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;
}

View file

@ -1,38 +0,0 @@
/*
* Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org>
*
* 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 */

File diff suppressed because it is too large Load diff

View file

@ -1,264 +0,0 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org>
*
* 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 <inttypes.h>
#include <ft2build.h>
#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 */

View file

@ -1,139 +0,0 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* Copyright (C) 2010 Grigori Goronzy <greg@geekmind.org>
*
* 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;
}

View file

@ -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 <stdlib.h>
#include <ctype.h>
#include <errno.h>
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;
}

View file

@ -1,123 +0,0 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* 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 <stdint.h>
#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 */

View file

@ -1,206 +0,0 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* 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 <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <ft2build.h>
#include FT_GLYPH_H
#include <strings.h>
#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

View file

@ -1,145 +0,0 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
* 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 <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifdef CONFIG_ENCA
#include <enca.h>
#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 */

View file

@ -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

View file

@ -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: <memory> (%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

View file

@ -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 <stdlib.h>
/* According to autoconf stdlib may not be enough for size_t */
#include <sys/types.h>
#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

View file

@ -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_ ]

View file

@ -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 <limits.h>
// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
// compiling for ARM we should wrap <wchar.h> 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 <wchar.h>
#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 <wchar.h>
#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_ ]

View file

@ -1,3 +0,0 @@
#define strncasecmp _strnicmp
#define strcasecmp _stricmp

View file

@ -1,26 +0,0 @@
#! /usr/bin/env python
# encoding: utf-8
#
# Copyright (c) 2009, Kevin Ollivier <kollivier@aegisub.org>
#
# 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('.')

View file

@ -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

View file

@ -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 <stdint.h>
#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

View file

@ -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

View file

@ -1,304 +0,0 @@
// Copyright (c) 2010 Thomas Goyne <tgoyne@gmail.com>
//
// 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 <algorithm>
#include <cassert>
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<int>(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<int64_t>((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<uint8_t*>(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<size_t>(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<size_t>(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<size_t>(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<size_t>(Start - CurrentSample);
size_t Samples = static_cast<size_t>(Decoded - FirstSample);
size_t Bytes = FFMIN(Samples, static_cast<size_t>(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;
}

View file

@ -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 <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}
#include <vector>
#include <list>
#include <memory>
#include "indexing.h"
#include "utils.h"
#include "ffms.h"
#ifdef HAALISOURCE
# define WIN32_LEAN_AND_MEAN
# define _WIN32_DCOM
# include <windows.h>
# include <tchar.h>
# include <atlbase.h>
# include <dshow.h>
# include <initguid.h>
# include "CoParser.h"
# include "guids.h"
#endif
class FFMS_AudioSource {
struct AudioBlock {
int64_t Age;
int64_t Start;
int64_t Samples;
std::vector<uint8_t> 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<AudioBlock>::iterator CacheIterator;
// delay in samples to apply to the audio
int64_t Delay;
// cache of decoded audio blocks
std::list<AudioBlock> 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<uint8_t> 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<TrackCompressionContext> 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<IMMContainer> pMMC;
CComPtr<IMMFrame> 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

View file

@ -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 <sstream>
#include <iomanip>
#include "ffms.h"
#include "videosource.h"
#include "audiosource.h"
#include "indexing.h"
extern "C" {
#include <libavutil/pixdesc.h>
}
#ifdef FFMS_WIN_DEBUG
# include <windows.h>
#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<IMMContainer> 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<int>(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<int>(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<FFMS_FrameInfo *>(&(*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<char *>(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<int>(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;
}

View file

@ -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 <algorithm>
#include <fstream>
#include <iostream>
#include <limits>
extern "C" {
#include <libavutil/sha1.h>
#include <zlib.h>
}
#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<unsigned int>(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<int>(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<int>(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<uint8_t> FileBuffer(1024*1024, 0);
std::vector<uint8_t> 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<size_t> 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<FFMS_TrackType>(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<char> 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<int, FFMS_AudioProperties>::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());
}
}

View file

@ -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 <map>
#include <memory>
#include "utils.h"
#include "wave64writer.h"
#ifdef HAALISOURCE
# define WIN32_LEAN_AND_MEAN
# define _WIN32_DCOM
# include <windows.h>
# include <tchar.h>
# include <atlbase.h>
# include <dshow.h>
# include <initguid.h>
# 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<TFrameInfo> {
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<FFMS_Track> {
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<int, FFMS_AudioProperties> LastAudioProperties;
protected:
int IndexMask;
int DumpMask;
int ErrorHandling;
TIndexCallback IC;
void *ICPrivate;
TAudioNameCallback ANC;
void *ANCPrivate;
const char *SourceFile;
AlignedBuffer<uint8_t> 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<IMMContainer> pMMC;
int NumTracks;
FFMS_TrackType TrackType[32];
CComQIPtr<IPropertyBag> 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

View file

@ -1,86 +0,0 @@
// Copyright (c) 2011 Thomas Goyne <tgoyne@gmail.com>
//
// 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 <cassert>
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);
}

View file

@ -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<SharedAudioContext> AudioContexts(FormatContext->nb_streams, SharedAudioContext(false));
std::vector<SharedVideoContext> VideoContexts(FormatContext->nb_streams, SharedVideoContext(false));
std::auto_ptr<FFMS_Index> 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<FFMS_TrackType>(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<int64_t> 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<FFMS_TrackType>(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;
}

View file

@ -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<FFMS_VideoSource>(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);
}

View file

@ -1,75 +0,0 @@
// Copyright (c) 2011 Thomas Goyne <tgoyne@gmail.com>
//
// 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 <cassert>
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;
}

View file

@ -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<SharedAudioContext> AudioContexts(mkv_GetNumTracks(MF), SharedAudioContext(true));
std::vector<SharedVideoContext> VideoContexts(mkv_GetNumTracks(MF), SharedVideoContext(true));
std::auto_ptr<FFMS_Index> 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;
}

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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<FFMS_VideoSource>(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);
}

View file

@ -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 <errno.h>
/* 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;
}

View file

@ -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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#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 */

View file

@ -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 <string.h>
#include <errno.h>
#include "utils.h"
#include "indexing.h"
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <io.h>
# include <fcntl.h>
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 <libavutil/opt.h>
#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<class T> static void safe_realloc(T *&ptr, size_t size) {
void *newalloc = realloc(ptr, size);
if (newalloc) {
ptr = static_cast<T*>(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<FFMS_SampleFormat>(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<FFMS_BITMAPINFOHEADER *>(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<uint8_t *>(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<IPropertyBag> 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<uint8_t*>(av_malloc(bih.biSize));
memcpy(CodecContext->extradata, &bih, sizeof bih);
vtCopy(pV, CodecContext->extradata + sizeof bih);
}
else {
CodecContext->extradata = static_cast<uint8_t*>(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<uint8_t*>(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<size_t>(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<IMMContainer> HaaliOpenFile(const char *SourceFile, enum FFMS_Sources SourceMode) {
CComPtr<IMMContainer> 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<IMemAlloc> pMA;
if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc)))
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED,
"Can't create memory allocator");
CComPtr<IMMStream> 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<IMMStreamOpen> 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;
}
}

View file

@ -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 <vector>
#include <sstream>
#include <fstream>
#include <cstdio>
#include "ffms.h"
#include "matroskaparser.h"
extern "C" {
#include "stdiostream.h"
#include <libavutil/mem.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#ifdef FFMS_USE_POSTPROC
#include <libpostproc/postprocess.h>
#endif // FFMS_USE_POSTPROC
}
// must be included after ffmpeg headers
#include "ffmscompat.h"
#ifdef HAALISOURCE
# define WIN32_LEAN_AND_MEAN
# define _WIN32_DCOM
# include <windows.h>
# include <tchar.h>
# include <atlbase.h>
# include <dshow.h>
# include <initguid.h>
# 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<int64_t>(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 T>
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 <typename T>
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<IPropertyBag> 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<IMMContainer> 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

View file

@ -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<const uint8_t **>(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<FFMS_SWS_CONST_PARAM uint8_t **>(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<FFMS_SWS_CONST_PARAM uint8_t **>(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<FFMS_SWS_CONST_PARAM uint8_t **>(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<int>(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<int64_t>((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);
}

View file

@ -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 <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#ifdef FFMS_USE_POSTPROC
#include <libpostproc/postprocess.h>
#endif // FFMS_USE_POSTPROC
}
// must be included after ffmpeg headers
#include "ffmscompat.h"
#include <vector>
#include <sstream>
#include "indexing.h"
#include "utils.h"
#include "ffms.h"
#ifdef HAALISOURCE
# define WIN32_LEAN_AND_MEAN
# define _WIN32_DCOM
# include <windows.h>
# include <tchar.h>
# include <atlbase.h>
# include <dshow.h>
# include <initguid.h>
# include "CoParser.h"
# include "guids.h"
#endif
class FFMS_VideoSource {
friend class FFSourceResources<FFMS_VideoSource>;
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<FFMS_VideoSource> 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<FFMS_VideoSource> 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<IMMContainer> pMMC;
AVBitStreamFilterContext *BitStreamFilter;
FFSourceResources<FFMS_VideoSource> 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

View file

@ -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 <string.h>
#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<const char *>(Header), sizeof(Header));
if (!Initial)
WavFile.seekp(CPos, std::ios::beg);
}
void Wave64Writer::WriteData(void *Data, std::streamsize Length) {
WavFile.write(reinterpret_cast<char *>(Data), Length);
BytesWritten += Length;
}

View file

@ -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 <stdint.h>
#include <iostream>
#include <fstream>
#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

View file

@ -1,26 +0,0 @@
#! /usr/bin/env python
# encoding: utf-8
#
# Copyright (c) 2009, Kevin Ollivier <kollivier@aegisub.org>
#
# 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')

View file

@ -55,12 +55,12 @@ endif
####################### #######################
# AUDIO / VIDEO SUPPORT # 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 SRC_OPT += audio_provider_ffmpegsource.cpp video_provider_ffmpegsource.cpp ffmpegsource_common.cpp
audio_provider_ffmpegsource.o video_provider_ffmpegsource.o ffmpegsource_common.o: \ 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 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) LDFLAGS_POST += $(LDFLAGS_FFMPEGSOURCE)
endif endif
@ -70,8 +70,8 @@ endif
########### ###########
ifeq (yes, $(WITH_LIBASS)) ifeq (yes, $(WITH_LIBASS))
SRC_OPT += subtitles_provider_libass.cpp SRC_OPT += subtitles_provider_libass.cpp
subtitles_provider_libass.o: CXXFLAGS += $(CFLAGS_LIBASS) $(CFLAGS_ICONV) subtitles_provider_libass.o: CXXFLAGS += $(CFLAGS_LIBASS)
LDFLAGS += $(LDFLAGS_LIBASS) $(LDFLAGS_ICONV) $(LDFLAGS_FONTCONFIG) LDFLAGS += $(LDFLAGS_LIBASS)
LDFLAGS_POST += $(LDFLAGS_LIBASS) LDFLAGS_POST += $(LDFLAGS_LIBASS)
endif endif
@ -106,7 +106,7 @@ charset_detect.o: CXXFLAGS += -D_X86_
font_file_lister_fontconfig.o: CXXFLAGS += $(CFLAGS_FONTCONFIG) $(CFLAGS_FREETYPE) font_file_lister_fontconfig.o: CXXFLAGS += $(CFLAGS_FONTCONFIG) $(CFLAGS_FREETYPE)
font_file_lister.o: CXXFLAGS += $(CFLAGS_FREETYPE) font_file_lister.o: CXXFLAGS += $(CFLAGS_FREETYPE)
text_file_reader.o: CXXFLAGS += -D_X86_ 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. # Ensure any optional source files above are added for compilation.