From cdd49b02b46dc2399354494626d1493b6208dd0c Mon Sep 17 00:00:00 2001 From: Amar Takhar Date: Thu, 6 Mar 2008 16:59:48 +0000 Subject: [PATCH] Bring CSRI into the repository fully, I'm not an advocate of this! but it's the best 'solution' for now. Originally committed to SVN as r1915. --- csri/Doxyfile | 22 + csri/LICENSE | 31 + csri/Makefile.am | 12 + csri/ac/Makefile.am | 3 + csri/ac/csri.sln | 20 + csri/ac/subhelp.vcproj | 179 ++++++ csri/acinclude.m4 | 67 +++ csri/backends/Makefile.am | 4 + csri/backends/libass/Makefile.am | 6 + csri/backends/libass/libass_csri.c | 258 +++++++++ csri/bootstrap | 4 + csri/configure.ac | 135 +++++ csri/csri.pc.in | 10 + csri/csri.vcproj | 328 +++++++++++ csri/csri_2008.vcproj | 327 +++++++++++ csri/frontends/Makefile.am | 4 + csri/frontends/avisynth25/Makefile.am | 15 + csri/frontends/avisynth25/avisynth.cpp | 155 +++++ csri/frontends/avisynth25/avisynth.h | 754 +++++++++++++++++++++++++ csri/frontends/cmdline/Makefile.am | 6 + csri/frontends/cmdline/cmdmain.c | 473 ++++++++++++++++ csri/frontends/cmdline/render.c | 240 ++++++++ csri/frontends/cmdline/render.h | 34 ++ csri/include/Makefile.am | 2 + csri/include/csri/csri.h | 348 ++++++++++++ csri/include/csri/fmtids.h | 65 +++ csri/include/csri/logging.h | 79 +++ csri/include/csri/openerr.h | 124 ++++ csri/include/csri/stream.h | 143 +++++ csri/include/subhelp.h | 114 ++++ csri/include/visibility.h | 43 ++ csri/lib/Makefile.am | 14 + csri/lib/csrilib.h | 85 +++ csri/lib/list.c | 148 +++++ csri/lib/posix/csrilib_os.h | 27 + csri/lib/posix/enumerate.c | 199 +++++++ csri/lib/win32/csrilib_os.h | 25 + csri/lib/win32/enumerate.c | 175 ++++++ csri/lib/wrap.c | 118 ++++ csri/subhelp/Makefile.am | 10 + csri/subhelp/logging.c | 102 ++++ csri/subhelp/posix/openfile.c | 82 +++ csri/subhelp/win32/openfile.c | 84 +++ 43 files changed, 5074 insertions(+) create mode 100644 csri/Doxyfile create mode 100644 csri/LICENSE create mode 100644 csri/Makefile.am create mode 100644 csri/ac/Makefile.am create mode 100644 csri/ac/csri.sln create mode 100644 csri/ac/subhelp.vcproj create mode 100644 csri/acinclude.m4 create mode 100644 csri/backends/Makefile.am create mode 100644 csri/backends/libass/Makefile.am create mode 100644 csri/backends/libass/libass_csri.c create mode 100755 csri/bootstrap create mode 100644 csri/configure.ac create mode 100644 csri/csri.pc.in create mode 100644 csri/csri.vcproj create mode 100644 csri/csri_2008.vcproj create mode 100644 csri/frontends/Makefile.am create mode 100644 csri/frontends/avisynth25/Makefile.am create mode 100644 csri/frontends/avisynth25/avisynth.cpp create mode 100644 csri/frontends/avisynth25/avisynth.h create mode 100644 csri/frontends/cmdline/Makefile.am create mode 100644 csri/frontends/cmdline/cmdmain.c create mode 100644 csri/frontends/cmdline/render.c create mode 100644 csri/frontends/cmdline/render.h create mode 100644 csri/include/Makefile.am create mode 100644 csri/include/csri/csri.h create mode 100644 csri/include/csri/fmtids.h create mode 100644 csri/include/csri/logging.h create mode 100644 csri/include/csri/openerr.h create mode 100644 csri/include/csri/stream.h create mode 100644 csri/include/subhelp.h create mode 100644 csri/include/visibility.h create mode 100644 csri/lib/Makefile.am create mode 100644 csri/lib/csrilib.h create mode 100644 csri/lib/list.c create mode 100644 csri/lib/posix/csrilib_os.h create mode 100644 csri/lib/posix/enumerate.c create mode 100644 csri/lib/win32/csrilib_os.h create mode 100644 csri/lib/win32/enumerate.c create mode 100644 csri/lib/wrap.c create mode 100644 csri/subhelp/Makefile.am create mode 100644 csri/subhelp/logging.c create mode 100644 csri/subhelp/posix/openfile.c create mode 100644 csri/subhelp/win32/openfile.c diff --git a/csri/Doxyfile b/csri/Doxyfile new file mode 100644 index 000000000..1fe4e6ad9 --- /dev/null +++ b/csri/Doxyfile @@ -0,0 +1,22 @@ +PROJECT_NAME = CSRI +PROJECT_NUMBER = 0.1 +OUTPUT_DIRECTORY = doc + +JAVADOC_AUTOBRIEF = YES +OPTIMIZE_OUTPUT_FOR_C = YES +PREDEFINED = DOXYGEN +ENUM_VALUES_PER_LINE = 1 +EXPAND_AS_DEFINED = _CSRI_H _CSRI_HELPER_H + +INPUT = include/csri include/subhelp.h +FILE_PATTERNS = *.h +RECURSIVE = YES + +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html + +GENERATE_MAN = YES +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO + diff --git a/csri/LICENSE b/csri/LICENSE new file mode 100644 index 000000000..32bccc30c --- /dev/null +++ b/csri/LICENSE @@ -0,0 +1,31 @@ +Copyright (c) 2007, David Lamparter +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * The name of the author may not 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 + diff --git a/csri/Makefile.am b/csri/Makefile.am new file mode 100644 index 000000000..1c0752cc5 --- /dev/null +++ b/csri/Makefile.am @@ -0,0 +1,12 @@ +AUTOMAKE_OPTIONS = foreign +EXTRA_DIST = csri.pc.in acinclude.m4 + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = csri.pc + +SUBDIRS = ac \ + include \ + subhelp \ + lib \ + backends \ + frontends diff --git a/csri/ac/Makefile.am b/csri/ac/Makefile.am new file mode 100644 index 000000000..c8c87da1a --- /dev/null +++ b/csri/ac/Makefile.am @@ -0,0 +1,3 @@ +EXTRA_DIST = \ + csri.sln \ + subhelp.vcproj diff --git a/csri/ac/csri.sln b/csri/ac/csri.sln new file mode 100644 index 000000000..98d1eafb1 --- /dev/null +++ b/csri/ac/csri.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "subhelp", "subhelp.vcproj", "{870552FE-7B73-41AB-A7D9-475440F4731C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {870552FE-7B73-41AB-A7D9-475440F4731C}.Debug|Win32.ActiveCfg = Debug|Win32 + {870552FE-7B73-41AB-A7D9-475440F4731C}.Debug|Win32.Build.0 = Debug|Win32 + {870552FE-7B73-41AB-A7D9-475440F4731C}.Release|Win32.ActiveCfg = Release|Win32 + {870552FE-7B73-41AB-A7D9-475440F4731C}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/csri/ac/subhelp.vcproj b/csri/ac/subhelp.vcproj new file mode 100644 index 000000000..004d4b065 --- /dev/null +++ b/csri/ac/subhelp.vcproj @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/csri/acinclude.m4 b/csri/acinclude.m4 new file mode 100644 index 000000000..dc8bd2bab --- /dev/null +++ b/csri/acinclude.m4 @@ -0,0 +1,67 @@ +dnl AC_CPP_PRAGMA_ONCE +dnl - check for #pragma once +AC_DEFUN([AC_CPP_PRAGMA_ONCE], [{ + AC_MSG_CHECKING([[whether $CPP supports #pragma once]]) + AC_PREPROC_IFELSE( + [AC_LANG_PROGRAM([[#pragma once]])], + [ + AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_PRAGMA_ONCE], [1], [Preprocessor support for #pragma once]) + ], + [AC_MSG_RESULT([no])]) + }]) + +dnl AC_C_FLAG([-flag]) +dnl - check for CFLAG support in CC +AC_DEFUN([AC_C_FLAG], [{ + AC_LANG_PUSH(C) + ac_c_flag_save="$CFLAGS" + CFLAGS="$CFLAGS $1" + AC_MSG_CHECKING([[whether $CC supports $1]]) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[]])], + [ + AC_MSG_RESULT([yes]) + m4_if([$3], [], [], [ + CFLAGS="$ac_c_flag_save" + $3 + ]) + ], [ + CFLAGS="$ac_c_flag_save" + AC_MSG_RESULT([no]) + $2 + ]) + AC_LANG_POP(C) + }]) + +dnl AC_C_FLAG([-flag]) +dnl - check for CFLAG support in CC +AC_DEFUN([AC_GCC_VISIBILITY], [{ + AC_MSG_CHECKING([[whether $CC supports GCC visibility]]) + # always use Werror since visibility is sensitive + # doesn't work on anything other than gcc either way, so Werror is fine... + vis_type="$1" + save_cflags="$CFLAGS" + CFLAGS="$CFLAGS -fvisibility=$vis_type -Werror" + AC_LINK_IFELSE( + [AC_LANG_SOURCE([[ + int a() __attribute__((visibility("default"))); + int b() __attribute__((visibility("hidden"))); + int c() __attribute__((visibility("internal"))); + + int a() { return 1; } + int b() { return 2; } + int c() { return 3; } + + int main() { return a()+b()+c(); } + ]])], + [ + AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_GCC_VISIBILITY], [1], [Compiler support for GCC visibility attributes]) + save_cflags="$save_cflags -fvisibility=$vis_type" + ], + [AC_MSG_RESULT([no])] + ) + CFLAGS="$save_cflags" + }]) + diff --git a/csri/backends/Makefile.am b/csri/backends/Makefile.am new file mode 100644 index 000000000..514d9a69d --- /dev/null +++ b/csri/backends/Makefile.am @@ -0,0 +1,4 @@ +if BUILD_LIBASS +LIBASS_DIR = libass +endif +SUBDIRS = $(LIBASS_DIR) diff --git a/csri/backends/libass/Makefile.am b/csri/backends/libass/Makefile.am new file mode 100644 index 000000000..9ec4801fb --- /dev/null +++ b/csri/backends/libass/Makefile.am @@ -0,0 +1,6 @@ +csrilib_LTLIBRARIES = libass_csri.la +libass_csri_la_SOURCES = libass_csri.c +libass_csri_la_LDFLAGS = -avoid-version -no-undefined +libass_csri_la_LIBADD = ../../subhelp/libsubhelp_la-openfile.lo $(LIBASS_LIBS) +libass_csri_la_CFLAGS = -I$(top_srcdir)/include $(LIBASS_CFLAGS) + diff --git a/csri/backends/libass/libass_csri.c b/csri/backends/libass/libass_csri.c new file mode 100644 index 000000000..295b182d6 --- /dev/null +++ b/csri/backends/libass/libass_csri.c @@ -0,0 +1,258 @@ +/***************************************************************************** + * csri: common subtitle renderer interface + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + +/** libass csri wrapper. + * Indirectly based on code from aegisub, + * (c) 2006-2007, Rodrigo Braz Monteiro, Evgeniy Stepanov + */ + +#ifdef HAVE_CONFIG_H +#include "acconf.h" +#endif + +#include +#include +#include + +#include + +#ifdef _WIN32 +# define CSRIAPI __declspec(dllexport) +#else +# ifdef HAVE_GCC_VISIBILITY +# define CSRIAPI __attribute__((visibility ("default"))) +# else +# define CSRIAPI +# endif +#endif + +#define CSRI_OWN_HANDLES +typedef struct csri_libass_rend { + ass_library_t* ass_library; +} csri_rend; + +typedef struct csri_asa_inst { + ass_renderer_t* ass_renderer; + ass_track_t* ass_track; +} csri_inst; + +#include +#include +#include + +static struct csri_libass_rend csri_libass = { NULL }; + +csri_inst *csri_open_file(csri_rend *renderer, + const char *filename, struct csri_openflag *flags) +{ + return subhelp_open_file(renderer, csri_open_mem, filename, flags); +} + +csri_inst *csri_open_mem(csri_rend *renderer, + const void *data, size_t length, struct csri_openflag *flags) +{ + csri_inst *rv; + if (renderer != &csri_libass) + return NULL; + + rv = (csri_inst *)malloc(sizeof(csri_inst)); + if (!rv) + return NULL; + + rv->ass_renderer = ass_renderer_init(renderer->ass_library); + if (!rv->ass_renderer) { + free(rv); + return NULL; + } + ass_set_font_scale(rv->ass_renderer, 1.); + ass_set_fonts(rv->ass_renderer, NULL, "Sans"); + rv->ass_track = ass_read_memory(csri_libass.ass_library, + (void *)data, length, "UTF-8"); + if (!rv->ass_track) { + ass_renderer_done(rv->ass_renderer); + free(rv); + return NULL; + } + return rv; +} + +void csri_close(csri_inst *inst) +{ + ass_free_track(inst->ass_track); + ass_renderer_done(inst->ass_renderer); + free(inst); +} + +int csri_request_fmt(csri_inst *inst, const struct csri_fmt *fmt) +{ + if (!csri_is_rgb(fmt->pixfmt) || csri_has_alpha(fmt->pixfmt)) + return -1; + ass_set_frame_size(inst->ass_renderer, fmt->width, fmt->height); + return 0; +} + +void csri_render(csri_inst *inst, struct csri_frame *frame, double time) +{ + ass_image_t *img = ass_render_frame(inst->ass_renderer, + inst->ass_track, (int)(time * 1000), NULL); + + while (img) { + unsigned bpp, alpha = 256 - (img->color && 0xFF); + int src_d, dst_d; + unsigned char *src, *dst, *endy, *endx; + unsigned char c[3] = { + (img->color >> 8) & 0xFF, /* B */ + (img->color >> 16) & 0xFF, /* G */ + img->color >> 24 /* R */ + }; + if ((frame->pixfmt | 1) == CSRI_F__RGB + || frame->pixfmt == CSRI_F_RGB) { + unsigned char tmp = c[2]; + c[2] = c[0]; + c[0] = tmp; + } + bpp = frame->pixfmt >= 0x200 ? 3 : 4; + + dst = frame->planes[0] + + img->dst_y * frame->strides[0] + + img->dst_x * bpp; + if (frame->pixfmt & 1) + dst++; + src = img->bitmap; + + src_d = img->stride - img->w; + dst_d = frame->strides[0] - img->w * bpp; + endy = src + img->h * img->stride; + + while (src != endy) { + endx = src + img->w; + while (src != endx) { + /* src[x]: 0..255, alpha: 1..256 (see above) + * -> src[x]*alpha: 0<<8..255<<8 + * -> need 1..256 for mult => +1 + */ + unsigned s = ((*src++ * alpha) >> 8) + 1; + unsigned d = 257 - s; + /* c[0]: 0.255, s/d: 1..256 */ + dst[0] = (s*c[0] + d*dst[0]) >> 8; + dst[1] = (s*c[1] + d*dst[1]) >> 8; + dst[2] = (s*c[2] + d*dst[2]) >> 8; + dst += bpp; + } + dst += dst_d; + src += src_d; + } + img = img->next; + } +} + +static csri_inst *libass_init_stream(csri_rend *renderer, + const void *header, size_t headerlen, + struct csri_openflag *flags) +{ + csri_inst *rv; + + if (renderer != &csri_libass) + return NULL; + + rv = (csri_inst *)malloc(sizeof(csri_inst)); + if (!rv) + return NULL; + + rv->ass_renderer = ass_renderer_init(renderer->ass_library); + if (!rv->ass_renderer) { + free(rv); + return NULL; + } + ass_set_font_scale(rv->ass_renderer, 1.); + ass_set_fonts(rv->ass_renderer, NULL, "Sans"); + rv->ass_track = ass_new_track(csri_libass.ass_library); + if (!rv->ass_track) { + ass_renderer_done(rv->ass_renderer); + free(rv); + return NULL; + } + ass_process_codec_private(rv->ass_track, (void *)header, headerlen); + return rv; +} + +static void libass_push_packet(csri_inst *inst, + const void *packet, size_t packetlen, + double pts_start, double pts_end) +{ + ass_process_chunk(inst->ass_track, (void *)packet, packetlen, + (int)(pts_start * 1000), (int)((pts_end - pts_start) * 1000)); +} + +static struct csri_stream_ext streamext = { + libass_init_stream, + libass_push_packet, + NULL +}; + +void *csri_query_ext(csri_rend *rend, csri_ext_id extname) +{ + if (!rend) + return NULL; + if (!strcmp(extname, CSRI_EXT_STREAM_ASS)) + return &streamext; + return NULL; +} + +static struct csri_info csri_libass_info = { + "libass", + "0.9.x", + "libass (the MPlayer SSA/ASS renderer, 0.9.x API)", + "Evgeniy Stepanov", + "Copyright (c) 2006, 2007 by Evgeniy Stepanov" +}; + +struct csri_info *csri_renderer_info(csri_rend *rend) +{ + return &csri_libass_info; +} + +csri_rend *csri_renderer_byname(const char *name, + const char *specific) +{ + if (strcmp(name, csri_libass_info.name)) + return NULL; + if (specific && strcmp(specific, csri_libass_info.specific)) + return NULL; + return &csri_libass; +} + +csri_rend *csri_renderer_default() +{ + csri_libass.ass_library = ass_library_init(); + if (!csri_libass.ass_library) + return NULL; + + ass_set_fonts_dir(csri_libass.ass_library, ""); + ass_set_extract_fonts(csri_libass.ass_library, 0); + ass_set_style_overrides(csri_libass.ass_library, NULL); + return &csri_libass; +} + +csri_rend *csri_renderer_next(csri_rend *prev) +{ + return NULL; +} + diff --git a/csri/bootstrap b/csri/bootstrap new file mode 100755 index 000000000..f3a56638d --- /dev/null +++ b/csri/bootstrap @@ -0,0 +1,4 @@ +#!/bin/bash -x + +autoreconf -f -i -s + diff --git a/csri/configure.ac b/csri/configure.ac new file mode 100644 index 000000000..d009c61b7 --- /dev/null +++ b/csri/configure.ac @@ -0,0 +1,135 @@ +AC_PREREQ(2.57) +AC_INIT(csri, 0.1.0) +AC_CONFIG_AUX_DIR(ac) +AM_INIT_AUTOMAKE +AC_CONFIG_HEADER([include/acconf.h]) + +# Checks for programs. +AC_PROG_CC + +AC_PROG_LIBTOOL +AC_C_CONST +AC_C_INLINE +AC_C_VOLATILE + +AC_PROG_INSTALL + +AC_CHECK_HEADERS([png.h]) +AC_CHECK_LIB([z], [inflate]) +AC_CHECK_LIB([m], [pow]) +AC_CHECK_LIB([png], [png_init_io]) + +AC_CPP_PRAGMA_ONCE +AC_C_FLAG([-Wall]) +AC_C_FLAG([-Wextra],[AC_C_FLAG([-W])]) +AC_C_FLAG([-Wno-unused-parameter]) +AC_C_FLAG([-Winvalid-pch]) +AC_C_FLAG([-pedantic]) +AC_C_FLAG([-std=c99],[AC_C_FLAG([-c99])]) +AC_ARG_ENABLE([werror], + AC_HELP_STRING([--enable-werror], [compile with -Werror (for developers)]), + [ if test "$enableval" == "yes" + then AC_C_FLAG([-Werror]) + fi]) +AC_GCC_VISIBILITY([internal]) + +AC_PATH_XTRA +platform="" +case $host_os in +mingw*) # LoadLibraryEx + GetProcAddress + platform="win32" + ;; +*) AC_SEARCH_LIBS([dlopen], [dl], [], [ + AC_MSG_FAILURE([dlopen not found - unsupported dynamic loader architecture / operating system]) + ]) +esac + +AM_CONDITIONAL([BUILD_MINGW], [test "$platform" == "win32" ]) + + +# Checks for header files. +AC_HEADER_STDC +AC_HEADER_STDBOOL +AC_CHECK_HEADERS([stdlib.h string.h sys/ioctl.h sys/time.h sys/wait.h sys/mman.h unistd.h libgen.h getopt.h]) +AC_CHECK_FUNCS([getopt_long]) +# +# Checks for typedefs, structures, and compiler characteristics. +# Checks for library functions. +#AC_FUNC_FORK +#AC_PROG_GCC_TRADITIONAL +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_TYPE_SIGNAL +AC_FUNC_MEMCMP +AC_CHECK_FUNCS([memset strdup strerror strtoul]) + +AC_ARG_WITH([libass],[ + AS_HELP_STRING([--with-libass],[build libass backend @<:@default=auto@:>@]) +],[],[ with_libass=auto +]) + +libass=no +AS_IF([test "$with_libass" != no],[ + ac_cflags_save="$CFLAGS" + ac_ldflags_save="$LDFLAGS" + + if test "$with_libass" != auto -a "$with_libass" != yes + then LIBASS_CFLAGS="-I$with_libass/include" + LIBASS_LIBS="-L$with_libass/lib -lass" + else PKG_CHECK_MODULES([LIBASS], libass >= 0.9.1,[],[ + AC_MSG_WARN([libass not found via pkg-config]) + LIBASS_LIBS="-lass" + ]) + fi + CFLAGS="$CFLAGS $LIBASS_CFLAGS" + LDFLAGS="$LDFLAGS $LIBASS_LIBS" + + AC_CHECK_LIB([ass], [ass_library_init], [libass_lib="yes"]) + AC_CHECK_HEADER([ass/ass.h], [libass_hdr="yes"]) + + if test "$libass_lib" == yes -a "$libass_hdr" == yes + then libass=yes + else if "$with_libass" != auto + then AC_MSG_FAILURE([libass requested but not found]) + fi + fi + CFLAGS="$ac_cflags_save" + LDFLAGS="$ac_ldflags_save" +]) +AM_CONDITIONAL([BUILD_LIBASS], [test "$libass" == "yes" ]) +AC_SUBST([LIBASS_CFLAGS]) +AC_SUBST([LIBASS_LIBS]) + +csrilibdir="${libdir}/csri" +AC_SUBST([csrilibdir]) + +csri_path="${csrilibdir}:/usr/lib/csri:/usr/local/lib/csri:~/.csri/lib" +AC_ARG_WITH(csri-libpath, + AC_HELP_STRING([--with-csri-libpath=PATH1:PATH2], + [Look for CSRI renderers in the specified directories. Separate directory names with colons. Tildes are expanded at run-time. [[$csri_path]]]), +[ + if test "$withval" != "yes" -a "$withval" != "" -a "$withval" != "no" + then csri_path="$withval" + fi +]) +AC_SUBST([csri_path], ["$csri_path"]) + +AC_CONFIG_FILES([csri.pc]) + +echo "" +echo " using search path: ${csri_path}" +echo "building libass wrapper: ${libass}" +echo "" +AC_OUTPUT([ + ac/Makefile + include/Makefile + subhelp/Makefile + lib/Makefile + frontends/Makefile + frontends/avisynth25/Makefile + frontends/cmdline/Makefile + backends/Makefile + backends/libass/Makefile + Makefile +]) + diff --git a/csri/csri.pc.in b/csri/csri.pc.in new file mode 100644 index 000000000..ad79302b9 --- /dev/null +++ b/csri/csri.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: csri +Description: csri - common subtitle renderer interface +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lcsri +Cflags: -I${includedir} diff --git a/csri/csri.vcproj b/csri/csri.vcproj new file mode 100644 index 000000000..3bf6c0338 --- /dev/null +++ b/csri/csri.vcproj @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/csri/csri_2008.vcproj b/csri/csri_2008.vcproj new file mode 100644 index 000000000..b266e3192 --- /dev/null +++ b/csri/csri_2008.vcproj @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/csri/frontends/Makefile.am b/csri/frontends/Makefile.am new file mode 100644 index 000000000..9676881c3 --- /dev/null +++ b/csri/frontends/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = \ + avisynth25 \ + cmdline + diff --git a/csri/frontends/avisynth25/Makefile.am b/csri/frontends/avisynth25/Makefile.am new file mode 100644 index 000000000..b5977f0a7 --- /dev/null +++ b/csri/frontends/avisynth25/Makefile.am @@ -0,0 +1,15 @@ +if BUILD_MINGW +AM_CPPFLAGS = \ + -I$(top_srcdir)/include +lib_LTLIBRARIES = csri_avs.la +csri_avs_la_SOURCES = avisynth.cpp +csri_avs_la_LIBADD = \ + ../../lib/libcsri_la-enumerate.lo \ + ../../lib/libcsri_la-list.lo \ + ../../lib/libcsri_la-wrap.lo \ + ../../subhelp/libsubhelp_la-logging.lo +csri_avs_la_LDFLAGS = -avoid-version -no-undefined -module +endif + +noinst_HEADERS = avisynth.h +EXTRA_DIST = avisynth.cpp diff --git a/csri/frontends/avisynth25/avisynth.cpp b/csri/frontends/avisynth25/avisynth.cpp new file mode 100644 index 000000000..bde755c49 --- /dev/null +++ b/csri/frontends/avisynth25/avisynth.cpp @@ -0,0 +1,155 @@ +/***************************************************************************** + * asa: portable digital subtitle renderer + ***************************************************************************** + * Copyright (C) 2006 David Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + +#include +#include "avisynth.h" +#include + +#include + +class CSRIAviSynth : public GenericVideoFilter { + csri_inst *inst; + double spf; + +public: + CSRIAviSynth(PClip _child, IScriptEnvironment *env, const char *file, + const char *rendname, const char *rendver); + ~CSRIAviSynth(); + + enum csri_pixfmt GetPixfmt(); + + PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment *env); + + static AVSValue __cdecl Create(AVSValue args, void* user_data, + IScriptEnvironment* env); +}; + +CSRIAviSynth::CSRIAviSynth(PClip _child, IScriptEnvironment *env, + const char *file, const char *rendname, const char *rendver) + : GenericVideoFilter(_child) +{ + csri_rend *r = csri_renderer_byname(rendname, rendver); + if (!r) { + if (rendver) + env->ThrowError("Failed to load renderer \"%s\"" + " version \"%s\"", rendname, rendver); + else if (rendname) + env->ThrowError("Failed to load renderer \"%s\"", + rendname); + else + env->ThrowError("Failed to load default renderer"); + } + + struct csri_fmt fmt; + fmt.pixfmt = GetPixfmt(); + if (fmt.pixfmt == -1) + env->ThrowError("Pixel format not supported by " + "AviSynth interface"); + + inst = csri_open_file(r, file, NULL); + if (!inst) + env->ThrowError("Failed to load \"%s\"", file); + + fmt.width = vi.width; + fmt.height = vi.height; + if (csri_request_fmt(inst, &fmt)) { + csri_close(inst); + env->ThrowError("Selected pixel format or size not supported " + "by selected subtitle renderer", file); + } + spf = (double)vi.fps_denominator / (double)vi.fps_numerator; +} + +CSRIAviSynth::~CSRIAviSynth() +{ + csri_close(inst); +} + +enum csri_pixfmt CSRIAviSynth::GetPixfmt() +{ + switch (vi.pixel_type) { + case VideoInfo::CS_BGR24: return CSRI_F_BGR; + case VideoInfo::CS_BGR32: return CSRI_F_BGR_; + case VideoInfo::CS_YUY2: return CSRI_F_YUY2; + case VideoInfo::CS_YV12: return CSRI_F_YV12; + } + return (enum csri_pixfmt)-1; +} + +PVideoFrame __stdcall CSRIAviSynth::GetFrame(int n, IScriptEnvironment *env) +{ + PVideoFrame avsframe = child->GetFrame(n, env); + struct csri_frame frame; + + env->MakeWritable(&avsframe); + + frame.pixfmt = GetPixfmt(); + frame.planes[0] = avsframe->GetWritePtr(); + frame.strides[0] = avsframe->GetPitch(); + if (csri_is_yuv_planar(frame.pixfmt)) { + frame.planes[1] = avsframe->GetWritePtr(PLANAR_U); + frame.strides[1] = avsframe->GetPitch(PLANAR_U); + frame.planes[2] = avsframe->GetWritePtr(PLANAR_V); + frame.strides[2] = avsframe->GetPitch(PLANAR_V); + } + if (csri_is_rgb(frame.pixfmt)) { + frame.planes[0] += (vi.height - 1) * frame.strides[0]; + frame.strides[0] = -frame.strides[0]; + } + + csri_render(inst, &frame, n * spf); + return avsframe; +} + +AVSValue __cdecl CSRIAviSynth::Create(AVSValue args, void* user_data, + IScriptEnvironment* env) +{ + const char *rname = args.ArraySize() >= 2 ? args[2].AsString() : NULL, + *rver = args.ArraySize() >= 3 ? args[3].AsString() : NULL; + return new CSRIAviSynth(args[0].AsClip(), env, args[1].AsString(), + rname, rver); +} + +extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2( + IScriptEnvironment* env) +{ + static char avs_name_f[2048]; + const char *avs_cmdname = (const char *)csri_query_ext(NULL, + "csri.avisynth25.command_name"); + const char *avs_name = (const char *)csri_query_ext(NULL, + "csri.avisynth25.name"); + + if (!csri_renderer_default()) + return NULL; + + if (!avs_cmdname) + avs_cmdname = "CSRI"; + if (!avs_name) + avs_name = "Common Subtitle Renderer Interface"; + + env->AddFunction(avs_cmdname, "cs", CSRIAviSynth::Create, 0); + env->AddFunction(avs_cmdname, "css", CSRIAviSynth::Create, 0); + env->AddFunction(avs_cmdname, "csss", CSRIAviSynth::Create, 0); + + snprintf(avs_name_f, sizeof(avs_name_f), + "%s [AviSynth 2.5 front-end]", avs_name); + return avs_name_f; +} + diff --git a/csri/frontends/avisynth25/avisynth.h b/csri/frontends/avisynth25/avisynth.h new file mode 100644 index 000000000..d0a543aa3 --- /dev/null +++ b/csri/frontends/avisynth25/avisynth.h @@ -0,0 +1,754 @@ +// Avisynth v2.5. Copyright 2002 Ben Rudiak-Gould et al. +// http://www.avisynth.org + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit +// http://www.gnu.org/copyleft/gpl.html . +// +// Linking Avisynth statically or dynamically with other modules is making a +// combined work based on Avisynth. Thus, the terms and conditions of the GNU +// General Public License cover the whole combination. +// +// As a special exception, the copyright holders of Avisynth give you +// permission to link Avisynth with independent modules that communicate with +// Avisynth solely through the interfaces defined in avisynth.h, regardless of the license +// terms of these independent modules, and to copy and distribute the +// resulting combined work under terms of your choice, provided that +// every copy of the combined work is accompanied by a complete copy of +// the source code of Avisynth (the version of Avisynth used to produce the +// combined work), being distributed under the terms of the GNU General +// Public License plus this exception. An independent module is a module +// which is not derived from or based on Avisynth, such as 3rd-party filters, +// import and export plugins, or graphical user interfaces. + + + + + +#ifndef __AVISYNTH_H__ +#define __AVISYNTH_H__ + +enum { AVISYNTH_INTERFACE_VERSION = 3 }; + + +/* Define all types necessary for interfacing with avisynth.dll + Moved from internal.h */ + +// Win32 API macros, notably the types BYTE, DWORD, ULONG, etc. +#include + +// COM interface macros +#include + + +// Raster types used by VirtualDub & Avisynth +#define in64 (__int64)(unsigned short) +typedef unsigned long Pixel; // this will break on 64-bit machines! +typedef unsigned long Pixel32; +typedef unsigned char Pixel8; +typedef long PixCoord; +typedef long PixDim; +typedef long PixOffset; + + +/* Compiler-specific crap */ + +// Tell MSVC to stop precompiling here +#ifdef _MSC_VER + #pragma hdrstop +#endif + +// Set up debugging macros for MS compilers; for others, step down to the +// standard interface +#ifdef _MSC_VER + #include +#else + #define _RPT0(a,b) ((void)0) + #define _RPT1(a,b,c) ((void)0) + #define _RPT2(a,b,c,d) ((void)0) + #define _RPT3(a,b,c,d,e) ((void)0) + #define _RPT4(a,b,c,d,e,f) ((void)0) + + #define _ASSERTE(x) assert(x) + #define _ASSERT(x) assert(x) + #include +#endif + + + +// I had problems with Premiere wanting 1-byte alignment for its structures, +// so I now set the Avisynth struct alignment explicitly here. +#pragma pack(push,8) + +#define FRAME_ALIGN 16 +// Default frame alignment is 16 bytes, to help P4, when using SSE2 + +// The VideoInfo struct holds global information about a clip (i.e. +// information that does not depend on the frame number). The GetVideoInfo +// method in IClip returns this struct. + +// Audio Sample information +typedef float SFLOAT; + +enum {SAMPLE_INT8 = 1<<0, + SAMPLE_INT16 = 1<<1, + SAMPLE_INT24 = 1<<2, // Int24 is a very stupid thing to code, but it's supported by some hardware. + SAMPLE_INT32 = 1<<3, + SAMPLE_FLOAT = 1<<4}; + +enum { + PLANAR_Y=1<<0, + PLANAR_U=1<<1, + PLANAR_V=1<<2, + PLANAR_ALIGNED=1<<3, + PLANAR_Y_ALIGNED=PLANAR_Y|PLANAR_ALIGNED, + PLANAR_U_ALIGNED=PLANAR_U|PLANAR_ALIGNED, + PLANAR_V_ALIGNED=PLANAR_V|PLANAR_ALIGNED, + }; + +struct VideoInfo { + int width, height; // width=0 means no video + unsigned fps_numerator, fps_denominator; + int num_frames; + // This is more extensible than previous versions. More properties can be added seeminglesly. + + // Colorspace properties. + enum { + CS_BGR = 1<<28, + CS_YUV = 1<<29, + CS_INTERLEAVED = 1<<30, + CS_PLANAR = 1<<31 + }; + + // Specific colorformats + enum { CS_UNKNOWN = 0, + CS_BGR24 = 1<<0 | CS_BGR | CS_INTERLEAVED, + CS_BGR32 = 1<<1 | CS_BGR | CS_INTERLEAVED, + CS_YUY2 = 1<<2 | CS_YUV | CS_INTERLEAVED, + CS_YV12 = 1<<3 | CS_YUV | CS_PLANAR, // y-v-u, planar + CS_I420 = 1<<4 | CS_YUV | CS_PLANAR, // y-u-v, planar + CS_IYUV = 1<<4 | CS_YUV | CS_PLANAR // same as above + }; + int pixel_type; // changed to int as of 2.5 + + + int audio_samples_per_second; // 0 means no audio + int sample_type; // as of 2.5 + __int64 num_audio_samples; // changed as of 2.5 + int nchannels; // as of 2.5 + + // Imagetype properties + + int image_type; + + enum { + IT_BFF = 1<<0, + IT_TFF = 1<<1, + IT_FIELDBASED = 1<<2 + }; + + // useful functions of the above + bool HasVideo() const { return (width!=0); } + bool HasAudio() const { return (audio_samples_per_second!=0); } + bool IsRGB() const { return !!(pixel_type&CS_BGR); } + bool IsRGB24() const { return (pixel_type&CS_BGR24)==CS_BGR24; } // Clear out additional properties + bool IsRGB32() const { return (pixel_type & CS_BGR32) == CS_BGR32 ; } + bool IsYUV() const { return !!(pixel_type&CS_YUV ); } + bool IsYUY2() const { return (pixel_type & CS_YUY2) == CS_YUY2; } + bool IsYV12() const { return ((pixel_type & CS_YV12) == CS_YV12)||((pixel_type & CS_I420) == CS_I420); } + bool IsColorSpace(int c_space) const { return ((pixel_type & c_space) == c_space); } + bool Is(int property) const { return ((pixel_type & property)==property ); } + bool IsPlanar() const { return !!(pixel_type & CS_PLANAR); } + bool IsFieldBased() const { return !!(image_type & IT_FIELDBASED); } + bool IsParityKnown() const { return ((image_type & IT_FIELDBASED)&&(image_type & (IT_BFF|IT_TFF))); } + bool IsBFF() const { return !!(image_type & IT_BFF); } + bool IsTFF() const { return !!(image_type & IT_TFF); } + + bool IsVPlaneFirst() const {return ((pixel_type & CS_YV12) == CS_YV12); } // Don't use this + int BytesFromPixels(int pixels) const { return pixels * (BitsPerPixel()>>3); } // Will not work on planar images, but will return only luma planes + int RowSize() const { return BytesFromPixels(width); } // Also only returns first plane on planar images + int BMPSize() const { if (IsPlanar()) {int p = height * ((RowSize()+3) & ~3); p+=p>>1; return p; } return height * ((RowSize()+3) & ~3); } + __int64 AudioSamplesFromFrames(__int64 frames) const { return (fps_numerator && HasVideo()) ? ((__int64)(frames) * audio_samples_per_second * fps_denominator / fps_numerator) : 0; } + int FramesFromAudioSamples(__int64 samples) const { return (fps_denominator && HasAudio()) ? (int)((samples * (__int64)fps_numerator)/((__int64)fps_denominator * (__int64)audio_samples_per_second)) : 0; } + __int64 AudioSamplesFromBytes(__int64 bytes) const { return HasAudio() ? bytes / BytesPerAudioSample() : 0; } + __int64 BytesFromAudioSamples(__int64 samples) const { return samples * BytesPerAudioSample(); } + int AudioChannels() const { return nchannels; } + int SampleType() const{ return sample_type;} + bool IsSampleType(int testtype) const{ return !!(sample_type&testtype);} + int SamplesPerSecond() const { return audio_samples_per_second; } + int BytesPerAudioSample() const { return nchannels*BytesPerChannelSample();} + void SetFieldBased(bool isfieldbased) { if (isfieldbased) image_type|=IT_FIELDBASED; else image_type&=~IT_FIELDBASED; } + void Set(int property) { image_type|=property; } + void Clear(int property) { image_type&=~property; } + + int BitsPerPixel() const { + switch (pixel_type) { + case CS_BGR24: + return 24; + case CS_BGR32: + return 32; + case CS_YUY2: + return 16; + case CS_YV12: + case CS_I420: + return 12; + default: + return 0; + } + } + int BytesPerChannelSample() const { + switch (sample_type) { + case SAMPLE_INT8: + return sizeof(signed char); + case SAMPLE_INT16: + return sizeof(signed short); + case SAMPLE_INT24: + return 3; + case SAMPLE_INT32: + return sizeof(signed int); + case SAMPLE_FLOAT: + return sizeof(SFLOAT); + default: + _ASSERTE("Sample type not recognized!"); + return 0; + } + } + + // useful mutator + void SetFPS(unsigned numerator, unsigned denominator) { + if ((numerator == 0) || (denominator == 0)) { + fps_numerator = 0; + fps_denominator = 1; + } + else { + unsigned x=numerator, y=denominator; + while (y) { // find gcd + unsigned t = x%y; x = y; y = t; + } + fps_numerator = numerator/x; + fps_denominator = denominator/x; + } + } + + // Range protected multiply-divide of FPS + void MulDivFPS(unsigned multiplier, unsigned divisor) { + unsigned __int64 numerator = UInt32x32To64(fps_numerator, multiplier); + unsigned __int64 denominator = UInt32x32To64(fps_denominator, divisor); + + unsigned __int64 x=numerator, y=denominator; + while (y) { // find gcd + unsigned __int64 t = x%y; x = y; y = t; + } + numerator /= x; // normalize + denominator /= x; + + unsigned __int64 temp = numerator | denominator; // Just looking top bit + unsigned u = 0; +#ifdef __GNUC__ + while (temp & 0xffffffff80000000LL) { // or perhaps > 16777216*2 +#else + while (temp & 0xffffffff80000000) { // or perhaps > 16777216*2 +#endif + temp = Int64ShrlMod32(temp, 1); + u++; + } + if (u) { // Scale to fit + const unsigned round = 1 << (u-1); + SetFPS( (unsigned)Int64ShrlMod32(numerator + round, u), + (unsigned)Int64ShrlMod32(denominator + round, u) ); + } + else { + fps_numerator = (unsigned)numerator; + fps_denominator = (unsigned)denominator; + } + } + + // Test for same colorspace + bool IsSameColorspace(const VideoInfo& vi) const { + if (vi.pixel_type == pixel_type) return TRUE; + if (IsYV12() && vi.IsYV12()) return TRUE; + return FALSE; + } + +}; + + + + +// VideoFrameBuffer holds information about a memory block which is used +// for video data. For efficiency, instances of this class are not deleted +// when the refcount reaches zero; instead they're stored in a linked list +// to be reused. The instances are deleted when the corresponding AVS +// file is closed. + +class VideoFrameBuffer { + BYTE* const data; + const int data_size; + // sequence_number is incremented every time the buffer is changed, so + // that stale views can tell they're no longer valid. + long sequence_number; + + friend class VideoFrame; + friend class Cache; + friend class ScriptEnvironment; + long refcount; + +public: + VideoFrameBuffer(int size); + VideoFrameBuffer(); + ~VideoFrameBuffer(); + + const BYTE* GetReadPtr() const { return data; } + BYTE* GetWritePtr() { ++sequence_number; return data; } + int GetDataSize() { return data_size; } + int GetSequenceNumber() { return sequence_number; } + int GetRefcount() { return refcount; } +}; + + +class IClip; +class PClip; +class PVideoFrame; +class IScriptEnvironment; +class AVSValue; + + +// VideoFrame holds a "window" into a VideoFrameBuffer. Operator new +// is overloaded to recycle class instances. + +class VideoFrame { + int refcount; + VideoFrameBuffer* const vfb; + const int offset, pitch, row_size, height, offsetU, offsetV, pitchUV; // U&V offsets are from top of picture. + + friend class PVideoFrame; + void AddRef() { InterlockedIncrement((long *)&refcount); } + void Release() { if (refcount==1) InterlockedDecrement(&vfb->refcount); InterlockedDecrement((long *)&refcount); } + + friend class ScriptEnvironment; + friend class Cache; + + VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height); + VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height, int _offsetU, int _offsetV, int _pitchUV); + + void* operator new(size_t size); +// TESTME: OFFSET U/V may be switched to what could be expected from AVI standard! +public: + int GetPitch() const { return pitch; } + int GetPitch(int plane) const { switch (plane) {case PLANAR_U: case PLANAR_V: return pitchUV;} return pitch; } + int GetRowSize() const { return row_size; } + int GetRowSize(int plane) const { + switch (plane) { + case PLANAR_U: case PLANAR_V: if (pitchUV) return row_size>>1; else return 0; + case PLANAR_U_ALIGNED: case PLANAR_V_ALIGNED: + if (pitchUV) { + int r = ((row_size+FRAME_ALIGN-1)&(~(FRAME_ALIGN-1)) )>>1; // Aligned rowsize + if (r<=pitchUV) + return r; + return row_size>>1; + } else return 0; + case PLANAR_Y_ALIGNED: + int r = (row_size+FRAME_ALIGN-1)&(~(FRAME_ALIGN-1)); // Aligned rowsize + if (r<=pitch) + return r; + return row_size; + } + return row_size; } + int GetHeight() const { return height; } + int GetHeight(int plane) const { switch (plane) {case PLANAR_U: case PLANAR_V: if (pitchUV) return height>>1; return 0;} return height; } + + // generally you shouldn't use these three + VideoFrameBuffer* GetFrameBuffer() const { return vfb; } + int GetOffset() const { return offset; } + int GetOffset(int plane) const { switch (plane) {case PLANAR_U: return offsetU;case PLANAR_V: return offsetV;default: return offset;}; } + + // in plugins use env->SubFrame() + VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height) const; + VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int pitchUV) const; + + + const BYTE* GetReadPtr() const { return vfb->GetReadPtr() + offset; } + const BYTE* GetReadPtr(int plane) const { return vfb->GetReadPtr() + GetOffset(plane); } + + bool IsWritable() const { return (refcount == 1 && vfb->refcount == 1); } + + BYTE* GetWritePtr() const { + if (vfb->GetRefcount()>1) { + _ASSERT(FALSE); + //throw AvisynthError("Internal Error - refcount was more than one!"); + } + return IsWritable() ? (vfb->GetWritePtr() + offset) : 0; + } + + BYTE* GetWritePtr(int plane) const { + if (plane==PLANAR_Y) { + if (vfb->GetRefcount()>1) { + _ASSERT(FALSE); +// throw AvisynthError("Internal Error - refcount was more than one!"); + } + return IsWritable() ? vfb->GetWritePtr() + GetOffset(plane) : 0; + } + return vfb->data + GetOffset(plane); + } + + ~VideoFrame() { InterlockedDecrement(&vfb->refcount); } +}; + +enum { + CACHE_NOTHING=0, + CACHE_RANGE=1, + CACHE_ALL=2, + CACHE_AUDIO=3, + CACHE_AUDIO_NONE=4 + }; + +// Base class for all filters. +class IClip { + friend class PClip; + friend class AVSValue; + int refcnt; + void AddRef() { InterlockedIncrement((long *)&refcnt); } + void Release() { InterlockedDecrement((long *)&refcnt); if (!refcnt) delete this; } +public: + IClip() : refcnt(0) {} + + virtual int __stdcall GetVersion() { return AVISYNTH_INTERFACE_VERSION; } + + virtual PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) = 0; + virtual bool __stdcall GetParity(int n) = 0; // return field parity if field_based, else parity of first field in frame + virtual void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) = 0; // start and count are in samples + virtual void __stdcall SetCacheHints(int cachehints,int frame_range) = 0 ; // We do not pass cache requests upwards, only to the next filter. + virtual const VideoInfo& __stdcall GetVideoInfo() = 0; + virtual __stdcall ~IClip() {} +}; + + +// smart pointer to IClip +class PClip { + + IClip* p; + + IClip* GetPointerWithAddRef() const { if (p) p->AddRef(); return p; } + friend class AVSValue; + friend class VideoFrame; + + void Init(IClip* x) { + if (x) x->AddRef(); + p=x; + } + void Set(IClip* x) { + if (x) x->AddRef(); + if (p) p->Release(); + p=x; + } + +public: + PClip() { p = 0; } + PClip(const PClip& x) { Init(x.p); } + PClip(IClip* x) { Init(x); } + void operator=(IClip* x) { Set(x); } + void operator=(const PClip& x) { Set(x.p); } + + IClip* operator->() const { return p; } + + // useful in conditional expressions + operator void*() const { return p; } + bool operator!() const { return !p; } + + ~PClip() { if (p) p->Release(); } +}; + + +// smart pointer to VideoFrame +class PVideoFrame { + + VideoFrame* p; + + void Init(VideoFrame* x) { + if (x) x->AddRef(); + p=x; + } + void Set(VideoFrame* x) { + if (x) x->AddRef(); + if (p) p->Release(); + p=x; + } + +public: + PVideoFrame() { p = 0; } + PVideoFrame(const PVideoFrame& x) { Init(x.p); } + PVideoFrame(VideoFrame* x) { Init(x); } + void operator=(VideoFrame* x) { Set(x); } + void operator=(const PVideoFrame& x) { Set(x.p); } + + VideoFrame* operator->() const { return p; } + + // for conditional expressions + operator void*() const { return p; } + bool operator!() const { return !p; } + + ~PVideoFrame() { if (p) p->Release();} +}; + + +class AVSValue { +public: + + AVSValue() { type = 'v'; } + AVSValue(IClip* c) { type = 'c'; clip = c; if (c) c->AddRef(); } + AVSValue(const PClip& c) { type = 'c'; clip = c.GetPointerWithAddRef(); } + AVSValue(bool b) { type = 'b'; boolean = b; } + AVSValue(int i) { type = 'i'; integer = i; } +// AVSValue(__int64 l) { type = 'l'; longlong = l; } + AVSValue(float f) { type = 'f'; floating_pt = f; } + AVSValue(double f) { type = 'f'; floating_pt = float(f); } + AVSValue(const char* s) { type = 's'; string = s; } + AVSValue(const AVSValue* a, int size) { type = 'a'; array = a; array_size = size; } + AVSValue(const AVSValue& v) { Assign(&v, true); } + + ~AVSValue() { if (IsClip() && clip) clip->Release(); } + AVSValue& operator=(const AVSValue& v) { Assign(&v, false); return *this; } + + // Note that we transparently allow 'int' to be treated as 'float'. + // There are no int<->bool conversions, though. + + bool Defined() const { return type != 'v'; } + bool IsClip() const { return type == 'c'; } + bool IsBool() const { return type == 'b'; } + bool IsInt() const { return type == 'i'; } +// bool IsLong() const { return (type == 'l'|| type == 'i'); } + bool IsFloat() const { return type == 'f' || type == 'i'; } + bool IsString() const { return type == 's'; } + bool IsArray() const { return type == 'a'; } + + PClip AsClip() const { _ASSERTE(IsClip()); return IsClip()?clip:0; } + bool AsBool() const { _ASSERTE(IsBool()); return boolean; } + int AsInt() const { _ASSERTE(IsInt()); return integer; } +// int AsLong() const { _ASSERTE(IsLong()); return longlong; } + const char* AsString() const { _ASSERTE(IsString()); return IsString()?string:0; } + double AsFloat() const { _ASSERTE(IsFloat()); return IsInt()?integer:floating_pt; } + + bool AsBool(bool def) const { _ASSERTE(IsBool()||!Defined()); return IsBool() ? boolean : def; } + int AsInt(int def) const { _ASSERTE(IsInt()||!Defined()); return IsInt() ? integer : def; } + double AsFloat(double def) const { _ASSERTE(IsFloat()||!Defined()); return IsInt() ? integer : type=='f' ? floating_pt : def; } + const char* AsString(const char* def) const { _ASSERTE(IsString()||!Defined()); return IsString() ? string : def; } + + int ArraySize() const { _ASSERTE(IsArray()); return IsArray()?array_size:1; } + + const AVSValue& operator[](int index) const { + _ASSERTE(IsArray() && index>=0 && index=0 && indexIsClip() && src->clip) + src->clip->AddRef(); + if (!init && IsClip() && clip) + clip->Release(); + // make sure this copies the whole struct! + ((__int32*)this)[0] = ((__int32*)src)[0]; + ((__int32*)this)[1] = ((__int32*)src)[1]; + } +}; + + +// instantiable null filter +class GenericVideoFilter : public IClip { +protected: + PClip child; + VideoInfo vi; +public: + GenericVideoFilter(PClip _child) : child(_child) { vi = child->GetVideoInfo(); } + PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) { return child->GetFrame(n, env); } + void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) { child->GetAudio(buf, start, count, env); } + const VideoInfo& __stdcall GetVideoInfo() { return vi; } + bool __stdcall GetParity(int n) { return child->GetParity(n); } + void __stdcall SetCacheHints(int cachehints,int frame_range) { } ; // We do not pass cache requests upwards, only to the next filter. +}; + + +class AvisynthError /* exception */ { +public: + const char* const msg; + AvisynthError(const char* _msg) : msg(_msg) {} +}; + + + + +/* Helper classes useful to plugin authors */ + +class AlignPlanar : public GenericVideoFilter +{ +public: + AlignPlanar(PClip _clip); + static PClip Create(PClip clip); + PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env); +}; + + + +class FillBorder : public GenericVideoFilter +{ +public: + FillBorder(PClip _clip); + static PClip Create(PClip clip); + PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env); +}; + + + +class ConvertAudio : public GenericVideoFilter +/** + * Helper class to convert audio to any format + **/ +{ +public: + ConvertAudio(PClip _clip, int prefered_format); + void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env); + void __stdcall SetCacheHints(int cachehints,int frame_range); // We do pass cache requests upwards, to the cache! + + static PClip Create(PClip clip, int sample_type, int prefered_type); + static AVSValue __cdecl Create_float(AVSValue args, void*, IScriptEnvironment*); + static AVSValue __cdecl Create_32bit(AVSValue args, void*, IScriptEnvironment*); + static AVSValue __cdecl Create_24bit(AVSValue args, void*, IScriptEnvironment*); + static AVSValue __cdecl Create_16bit(AVSValue args, void*, IScriptEnvironment*); + static AVSValue __cdecl Create_8bit(AVSValue args, void*, IScriptEnvironment*); + virtual ~ConvertAudio(); + +private: + void convertToFloat(char* inbuf, float* outbuf, char sample_type, int count); + void convertToFloat_3DN(char* inbuf, float* outbuf, char sample_type, int count); + void convertToFloat_SSE(char* inbuf, float* outbuf, char sample_type, int count); + void convertToFloat_SSE2(char* inbuf, float* outbuf, char sample_type, int count); + void convertFromFloat(float* inbuf, void* outbuf, char sample_type, int count); + void convertFromFloat_3DN(float* inbuf, void* outbuf, char sample_type, int count); + void convertFromFloat_SSE(float* inbuf, void* outbuf, char sample_type, int count); + void convertFromFloat_SSE2(float* inbuf, void* outbuf, char sample_type, int count); + + __inline int Saturate_int8(float n); + __inline short Saturate_int16(float n); + __inline int Saturate_int24(float n); + __inline int Saturate_int32(float n); + + char src_format; + char dst_format; + int src_bps; + char *tempbuffer; + SFLOAT *floatbuffer; + int tempbuffer_size; +}; + + +// For GetCPUFlags. These are backwards-compatible with those in VirtualDub. +enum { + /* slowest CPU to support extension */ + CPUF_FORCE = 0x01, // N/A + CPUF_FPU = 0x02, // 386/486DX + CPUF_MMX = 0x04, // P55C, K6, PII + CPUF_INTEGER_SSE = 0x08, // PIII, Athlon + CPUF_SSE = 0x10, // PIII, Athlon XP/MP + CPUF_SSE2 = 0x20, // PIV, Hammer + CPUF_3DNOW = 0x40, // K6-2 + CPUF_3DNOW_EXT = 0x80, // Athlon + CPUF_X86_64 = 0xA0, // Hammer (note: equiv. to 3DNow + SSE2, which only Hammer + // will have anyway) + CPUF_SSE3 = 0x100, // Some P4 & Athlon 64. +}; +#define MAX_INT 0x7fffffff +#define MIN_INT -0x7fffffff + + + +class IScriptEnvironment { +public: + virtual __stdcall ~IScriptEnvironment() {} + + virtual /*static*/ long __stdcall GetCPUFlags() = 0; + + virtual char* __stdcall SaveString(const char* s, int length = -1) = 0; + virtual char* __stdcall Sprintf(const char* fmt, ...) = 0; + // note: val is really a va_list; I hope everyone typedefs va_list to a pointer + virtual char* __stdcall VSprintf(const char* fmt, void* val) = 0; + + __declspec(noreturn) virtual void __stdcall ThrowError(const char* fmt, ...) = 0; + + class NotFound /*exception*/ {}; // thrown by Invoke and GetVar + + typedef AVSValue (__cdecl *ApplyFunc)(AVSValue args, void* user_data, IScriptEnvironment* env); + + virtual void __stdcall AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data) = 0; + virtual bool __stdcall FunctionExists(const char* name) = 0; + virtual AVSValue __stdcall Invoke(const char* name, const AVSValue args, const char** arg_names=0) = 0; + + virtual AVSValue __stdcall GetVar(const char* name) = 0; + virtual bool __stdcall SetVar(const char* name, const AVSValue& val) = 0; + virtual bool __stdcall SetGlobalVar(const char* name, const AVSValue& val) = 0; + + virtual void __stdcall PushContext(int level=0) = 0; + virtual void __stdcall PopContext() = 0; + + // align should be 4 or 8 + virtual PVideoFrame __stdcall NewVideoFrame(const VideoInfo& vi, int align=FRAME_ALIGN) = 0; + + virtual bool __stdcall MakeWritable(PVideoFrame* pvf) = 0; + + virtual /*static*/ void __stdcall BitBlt(BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height) = 0; + + typedef void (__cdecl *ShutdownFunc)(void* user_data, IScriptEnvironment* env); + virtual void __stdcall AtExit(ShutdownFunc function, void* user_data) = 0; + + virtual void __stdcall CheckVersion(int version = AVISYNTH_INTERFACE_VERSION) = 0; + + virtual PVideoFrame __stdcall Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height) = 0; + + virtual int __stdcall SetMemoryMax(int mem) = 0; + + virtual int __stdcall SetWorkingDir(const char * newdir) = 0; + + virtual void* __stdcall ManageCache(int key, void* data) = 0; + + enum PlanarChromaAlignmentMode { + PlanarChromaAlignmentOff, + PlanarChromaAlignmentOn, + PlanarChromaAlignmentTest }; + + virtual bool __stdcall PlanarChromaAlignment(PlanarChromaAlignmentMode key) = 0; + + virtual PVideoFrame __stdcall SubframePlanar(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV) = 0; +}; + + +// avisynth.dll exports this; it's a way to use it as a library, without +// writing an AVS script or without going through AVIFile. +IScriptEnvironment* __stdcall CreateScriptEnvironment(int version = AVISYNTH_INTERFACE_VERSION); + + +#pragma pack(pop) + +#endif //__AVISYNTH_H__ diff --git a/csri/frontends/cmdline/Makefile.am b/csri/frontends/cmdline/Makefile.am new file mode 100644 index 000000000..10a384563 --- /dev/null +++ b/csri/frontends/cmdline/Makefile.am @@ -0,0 +1,6 @@ +bin_PROGRAMS = csri +AM_CPPFLAGS = -I$(top_srcdir)/include +csri_SOURCES = cmdmain.c render.c +csri_LDADD = ../../lib/libcsri.la + +noinst_HEADERS = render.h diff --git a/csri/frontends/cmdline/cmdmain.c b/csri/frontends/cmdline/cmdmain.c new file mode 100644 index 000000000..5b1d47196 --- /dev/null +++ b/csri/frontends/cmdline/cmdmain.c @@ -0,0 +1,473 @@ +/***************************************************************************** + * asa: portable digital subtitle renderer + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "acconf.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "render.h" + +csri_rend *r; + +static int do_usage(FILE *fd) +{ + fprintf(fd, "usage: csri [COMMON-OPTIONS] COMMAND [COMMAND-OPTIONS]\n" + "\n" + "common options: [-r renderer [-s specific]]\n" + "\t-r\tselect a renderer, by name\n" + "\t-s\tselect a renderer version, by specific name\n" + "\n" + "commands:\n" + "\tlist\tshow installed renderers\n" + "\tinfo\tshow detailed renderer information\n" + "\trender\trender subtitle output\n" +#ifdef HAVE_LIBPNG + "\t\t-i FILE\t\tread background from PNG file\n" + "\t\t-o PREFIX\twrite output to PREFIX_nnnn.png\n" +#endif + "\t\t-A\t\tkeep alpha\n" + "\t\t-t [start][:[end][:[step]]]\tspecify timestamps to be rendered\n" + "\t\tSUBFILE\t\tsubtitle file to load\n" + "\n"); + return 2; +} + +static int do_list(int argc, char **argv) +{ + unsigned num = 0; + + if (argc) + return do_usage(stderr); + + while (r) { + struct csri_info *info = csri_renderer_info(r); + if (!info) + continue; + printf("%s:%s %s, %s, %s\n", info->name, info->specific, + info->longname, info->author, info->copyright); + r = csri_renderer_next(r); + num++; + } + fprintf(stderr, "%u renderers found\n", num); + return num > 0 ? 0 : 1; +} + +static csri_ext_id known_exts[] = { + CSRI_EXT_OPENERR, + CSRI_EXT_LOGGING, + CSRI_EXT_STREAM, + CSRI_EXT_STREAM_ASS, + CSRI_EXT_STREAM_TEXT, + CSRI_EXT_STREAM_DISCARD, + NULL +}; + +static const char *dummy_script = "[Script Info]\r\n" + "ScriptType: v4.00\r\n" + "[V4 Styles]\r\n" + "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, " + "TertiaryColour, BackColour, Bold, Italic, BorderStyle, " + "Outline, Shadow, Alignment, MarginL, MarginR, MarginV, " + "AlphaLevel, Encoding\r\n" + "Style: Default,Arial,20,&HFFFFFF&,&H00FFFF&,&H000000&,&H000000&," + "0,0,1,2,2,2,10,10,10,0,0\r\n" + "[Events]\r\n" + "Format: Marked, Start, End, Style, Name, " + "MarginL, MarginR, MarginV, Effect, Text\r\n" + "Dialogue: Marked=0,0:00:01.00,0:00:02.00,Default,,0000,0000,0000,," + "test\r\n"; + +static const char *dummy_stream = "1,0,Default,,0000,0000,0000,,stream\r\n"; + +#define e(x) { #x, CSRI_F_ ## x } +#define ree(x) e(RGB ## x), e(x ## RGB), e(BGR ## x), e(x ## BGR) +static struct csri_fmtlistent { + const char *label; + enum csri_pixfmt fmt; +} csri_fmts[] = { + ree(A), ree(_), + e(RGB), e(BGR), + e(AYUV), e(YUVA), e(YVUA), e(YUY2), e(YV12A), e(YV12), + { NULL, 0 } +}; + +static void listfmts() +{ + csri_inst *i; + struct csri_fmtlistent *fmt; + struct csri_fmt f; + + printf("\ntrying to get list of supported colorspaces:\n"); + fflush(stdout); + i = csri_open_mem(r, dummy_script, strlen(dummy_script), NULL); + + f.width = f.height = 256; + for (fmt = csri_fmts; fmt->label; fmt++) { + f.pixfmt = fmt->fmt; + if (!csri_request_fmt(i, &f)) + printf("\t[%04x] %s\n", fmt->fmt, fmt->label); + } + csri_close(i); +} + +static int do_info(int argc, char **argv) +{ + struct csri_info *info; + csri_ext_id *id; + if (argc) + return do_usage(stderr); + + info = csri_renderer_info(r); + if (!info) + return 1; + printf("%s:%s\n\t%s\n\t%s\n\t%s\n", info->name, info->specific, + info->longname, info->author, info->copyright); + printf("supported CSRI extensions:\n"); + for (id = known_exts; *id; id++) { + void *rext = csri_query_ext(r, *id); + void *lext = csri_query_ext(NULL, *id); + if (lext || rext) { + printf("\t%s ", *id); + if (!lext) + printf("\n"); + else if (!rext) + printf("[library only]\n"); + else if (rext == lext) + printf("[emulated by library]\n"); + else + printf("\n"); + } + } + listfmts(); + return 0; +} + +static void logfunc(void *appdata, enum csri_logging_severity sev, + const char *message) +{ + char severity[32]; + switch (sev) { + case CSRI_LOG_DEBUG: strcpy(severity, "[debug]"); break; + case CSRI_LOG_INFO: strcpy(severity, "[info]"); break; + case CSRI_LOG_NOTICE: strcpy(severity, "[notice]"); break; + case CSRI_LOG_WARNING: strcpy(severity, "[warning]"); break; + case CSRI_LOG_ERROR: strcpy(severity, "[error]"); break; + default: snprintf(severity, 32, "[%d?]", (int)sev); + } + fprintf(stderr, "%-10s %s\n", severity, message); +} + +static int real_render(double *times, const char *infile, const char *outfile, + const char *script, enum csri_pixfmt pfmt) +{ + struct csri_frame *bg, *a; + csri_inst *inst; + struct csri_fmt fmt; + double now; + int idx; + uint32_t width = 640, height = 480; + + bg = infile ? png_load(infile, &width, &height, pfmt) + : frame_alloc(width, height, pfmt); + a = frame_alloc(width, height, pfmt); + if (!bg || !a) { + fprintf(stderr, "failed to allocate frame\n"); + if (!bg) + fprintf(stderr, "\t- problem with background.\n"); + return 2; + } + inst = csri_open_file(r, script, NULL); + if (!inst) { + fprintf(stderr, "failed to open script \"%s\"\n", script); + return 2; + } + fmt.pixfmt = pfmt; + fmt.width = width; + fmt.height = height; + if (csri_request_fmt(inst, &fmt)) { + fprintf(stderr, "format not supported by renderer\n"); + return 2; + } + + idx = 0; + for (now = times[0]; now <= times[1]; now += times[2]) { + frame_copy(a, bg, width, height); + csri_render(inst, a, now); + if (outfile) { + char buffer[256]; + snprintf(buffer, sizeof(buffer), + "%s_%04d.png", outfile, idx); + printf("%s\n", buffer); + png_store(a, buffer, width, height); + } + idx++; + } + csri_close(inst); + inst = NULL; + frame_free(bg); + frame_free(a); + return 0; +} + +static int do_render(int argc, char **argv) +{ + double times[3] = {0.0, 0.0, 1.0}; + const char *outfile = NULL, *infile = NULL; + struct csri_fmtlistent *fmte; + int keepalpha = 0; + enum csri_pixfmt fmt = ~0U; + argv--, argc++; + while (1) { + int c, i; + const char *short_options = "t:o:i:F:A"; + char *arg, *end, *err; + struct option long_options[] = { + {"time", 1, 0, 't'}, + {"output", 1, 0, 'o'}, + {"input", 1, 0, 'i'}, + {"format", 0, 0, 'F'}, + {"alpha", 0, 0, 'A'}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, short_options, long_options, NULL); + if (c == -1) + break; + switch (c) { + case 't': + arg = optarg; + for (i = 0; i < 3; i++) { + end = strchr(arg, ':'); + if (end) + *end = '\0'; + if (*arg) { + times[i] = strtod(arg, &err); + if (*err) { + fprintf(stderr, + "invalid time: %s\n", + arg); + return do_usage(stderr); + } + } + if (!end) + break; + arg = end + 1; + } + break; + case 'i': + infile = optarg; + break; + case 'o': + outfile = optarg; + break; + case 'A': + if (fmt != ~0U) + return do_usage(stderr); + keepalpha = 1; + break; + case 'F': + if (keepalpha || fmt != ~0U) + return do_usage(stderr); + for (fmte = csri_fmts; fmte->label; fmte++) + if (!strcmp(fmte->label, optarg)) + break; + if (!fmte->label) + return do_usage(stderr); + fmt = fmte->fmt; + break; + default: + return do_usage(stderr); + }; + } + if (fmt == ~0U) + fmt = keepalpha ? CSRI_F_RGBA : CSRI_F_RGB_; + if (!isfinite(times[0])) { + fprintf(stderr, "invalid start time\n"); + return do_usage(stderr); + } + if (!isfinite(times[1]) || times[1] < times[0]) { + fprintf(stderr, "invalid end time\n"); + return do_usage(stderr); + } + if (!isnormal(times[2]) || times[2] < 0.0) { + fprintf(stderr, "invalid end time\n"); + return do_usage(stderr); + } + if (argc - optind != 1) { + fprintf(stderr, "script name missing\n"); + return do_usage(stderr); + } + return real_render(times, infile, outfile, argv[optind], fmt); +} + +static int do_streamtest(int argc, char **argv) +{ + const char *outfile = NULL; + struct csri_fmtlistent *fmte; + enum csri_pixfmt pfmt = ~0U; + struct csri_frame *bg, *a; + csri_inst *inst; + struct csri_fmt fmt; + uint32_t width = 640, height = 480; + struct csri_stream_ext *sext; + + argv--, argc++; + while (1) { + int c; + const char *short_options = "o:F:"; + struct option long_options[] = { + {"output", 1, 0, 'o'}, + {"format", 0, 0, 'F'}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, short_options, long_options, NULL); + if (c == -1) + break; + switch (c) { + case 'o': + outfile = optarg; + break; + case 'F': + if (pfmt != ~0U) + return do_usage(stderr); + for (fmte = csri_fmts; fmte->label; fmte++) + if (!strcmp(fmte->label, optarg)) + break; + if (!fmte->label) + return do_usage(stderr); + pfmt = fmte->fmt; + break; + default: + return do_usage(stderr); + }; + } + if (pfmt == ~0U) + pfmt = CSRI_F_RGB_; + + sext = (struct csri_stream_ext *)csri_query_ext(r, + CSRI_EXT_STREAM_ASS); + if (!sext) { + fprintf(stderr, "renderer does not support ASS streaming\n"); + return 2; + } + + bg = frame_alloc(width, height, pfmt); + a = frame_alloc(width, height, pfmt); + if (!bg || !a) { + fprintf(stderr, "failed to allocate frame\n"); + return 2; + } + inst = sext->init_stream(r, dummy_script, strlen(dummy_script), NULL); + if (!inst) { + fprintf(stderr, "failed to initialize stream\n"); + return 2; + } + fmt.pixfmt = pfmt; + fmt.width = width; + fmt.height = height; + if (csri_request_fmt(inst, &fmt)) { + fprintf(stderr, "format not supported by renderer\n"); + return 2; + } + + frame_copy(a, bg, width, height); + csri_render(inst, a, 1.75); + if (outfile) { + char buffer[256]; + snprintf(buffer, sizeof(buffer), + "%s_nstream.png", outfile); + printf("%s\n", buffer); + png_store(a, buffer, width, height); + } + + frame_copy(a, bg, width, height); + sext->push_packet(inst, dummy_stream, strlen(dummy_stream), + 1.5, 2.0); + csri_render(inst, a, 1.75); + if (outfile) { + char buffer[256]; + snprintf(buffer, sizeof(buffer), + "%s_stream.png", outfile); + printf("%s\n", buffer); + png_store(a, buffer, width, height); + } + + csri_close(inst); + inst = NULL; + frame_free(bg); + frame_free(a); + return 0; +} + +int main(int argc, char **argv) +{ + struct csri_logging_ext *logext; + + if (argc < 2) + return do_usage(stderr); + + logext = (struct csri_logging_ext *)csri_query_ext(NULL, + CSRI_EXT_LOGGING); + if (logext && logext->set_logcallback) + logext->set_logcallback(logfunc, NULL); + else + fprintf(stderr, "warning: unable to set log callback\n"); + + r = csri_renderer_default(); + argc--, argv++; + if (!strcmp(argv[0], "list")) + return do_list(argc - 1, argv + 1); + if (!strcmp(argv[0], "-r")) { + const char *name = NULL, *spec = NULL; + if (argc < 2) + return do_usage(stderr); + name = argv[1]; + argc -= 2, argv += 2; + if (!strcmp(argv[0], "-s")) { + if (argc < 2) + return do_usage(stderr); + spec = argv[1]; + } + r = csri_renderer_byname(name, spec); + if (!r) { + fprintf(stderr, "renderer %s:%s not found.\n", + name, spec ? spec : "*"); + return 2; + } + } + if (!strcmp(argv[0], "info")) + return do_info(argc - 1, argv + 1); + if (!strcmp(argv[0], "render")) + return do_render(argc - 1, argv + 1); + if (!strcmp(argv[0], "streamtest")) + return do_streamtest(argc - 1, argv + 1); + return do_usage(stderr); +} diff --git a/csri/frontends/cmdline/render.c b/csri/frontends/cmdline/render.c new file mode 100644 index 000000000..87b205ace --- /dev/null +++ b/csri/frontends/cmdline/render.c @@ -0,0 +1,240 @@ +/***************************************************************************** + * asa: portable digital subtitle renderer + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "acconf.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "render.h" + +extern csri_rend *r; + +#ifdef HAVE_LIBPNG +#include + +struct csri_frame *png_load(const char *filename, + uint32_t *width, uint32_t *height, enum csri_pixfmt fmt) +{ + struct csri_frame *frame; + int bit_depth, color_type; + png_structp png_ptr; + png_infop info_ptr; + png_bytep *rows; + unsigned char *imgdata; + FILE *fp; + + if (!csri_is_rgb(fmt)) { + fprintf(stderr, "PNG loader: can't load non-RGB.\n"); + return NULL; + } + + frame = (struct csri_frame *)malloc(sizeof(struct csri_frame)); + if (!frame) + return NULL; + memset(frame, 0, sizeof(*frame)); + frame->pixfmt = fmt; + + fp = fopen(filename, "rb"); + if (!fp) { + fprintf(stderr, "Error opening \"%s\": %s (%d)\n", + filename, strerror(errno), errno); + return NULL; + } + + assert(png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL)); + assert(info_ptr = png_create_info_struct(png_ptr)); + //keepalpha ? CSRI_F_RGBA : CSRI_F_RGB_; + + png_init_io(png_ptr, fp); + png_read_info(png_ptr, info_ptr); + png_get_IHDR(png_ptr, info_ptr, + (png_uint_32 *)width, (png_uint_32 *)height, + &bit_depth, &color_type, NULL, NULL, NULL); + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png_ptr); + if (bit_depth < 8) + png_set_packing(png_ptr); + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png_ptr); + if (bit_depth == 16) + png_set_strip_16(png_ptr); + + if ((fmt < 0x200 && (fmt & 2)) || fmt == CSRI_F_BGR) + png_set_bgr(png_ptr); + if (csri_has_alpha(fmt)) { + int before = fmt == CSRI_F_ARGB || fmt == CSRI_F_ABGR; + if (color_type == PNG_COLOR_TYPE_RGB) + png_set_filler(png_ptr, 0xff, before + ? PNG_FILLER_BEFORE : PNG_FILLER_AFTER); + else { + png_set_invert_alpha(png_ptr); + if (before) + png_set_swap_alpha(png_ptr); + } + } else { + if (color_type & PNG_COLOR_MASK_ALPHA) + png_set_strip_alpha(png_ptr); + if (fmt != CSRI_F_RGB && fmt != CSRI_F_BGR) { + int before = fmt == CSRI_F__RGB || fmt == CSRI_F__BGR; + png_set_filler(png_ptr, 0xff, before + ? PNG_FILLER_BEFORE : PNG_FILLER_AFTER); + } + } + + rows = (png_bytep *)malloc(sizeof(png_bytep) * *height); + assert(rows); + imgdata = (unsigned char *)malloc(4 * *height * *width); + assert(imgdata); + for (uint32_t y = 0; y < *height; y++) + rows[y] = imgdata + 4 * *width * y; + png_read_image(png_ptr, rows); + png_read_end(png_ptr, NULL); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + free(rows); + + frame->planes[0] = imgdata; + frame->strides[0] = *width * 4; + printf("\033[32;1mloaded %ux%u\033[m\n", *width, *height); + return frame; +} + +void png_store(struct csri_frame *frame, const char *filename, + uint32_t width, uint32_t height) +{ + enum csri_pixfmt fmt = frame->pixfmt; + int xforms = 0, before = 0, after = 0; + png_structp png_ptr; + png_infop info_ptr; + png_bytep *rows; + FILE *fp; + + fp = fopen(filename, "wb"); + if (!fp) { + fprintf(stderr, "Error opening \"%s\": %s (%d)\n", + filename, strerror(errno), errno); + return; + } + + rows = (png_bytep *)malloc(sizeof(png_bytep) * height); + assert(rows); + + after = fmt == CSRI_F_RGB_ || fmt == CSRI_F_BGR_; + before = fmt == CSRI_F__RGB || fmt == CSRI_F__BGR; + for (uint32_t y = 0; y < height; y++) { + rows[y] = frame->planes[0] + frame->strides[0] * y; + if (before || after) { + unsigned char *d = rows[y], *s = rows[y], + *e = d + frame->strides[0]; + if (before) + s++; + while (d < e) { + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + s++; + } + } + } + + assert(png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL)); + assert(info_ptr = png_create_info_struct(png_ptr)); + png_init_io(png_ptr, fp); + if (csri_has_alpha(fmt) + && (fmt == CSRI_F_ARGB || fmt == CSRI_F_ABGR)) + xforms |= PNG_TRANSFORM_SWAP_ALPHA; + if ((fmt < 0x200 && (fmt & 2)) || fmt == CSRI_F_BGR) + xforms |= PNG_TRANSFORM_BGR; + png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); + png_set_IHDR(png_ptr, info_ptr, width, height, 8, csri_has_alpha(fmt) + ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + png_set_rows(png_ptr, info_ptr, rows); + png_write_png(png_ptr, info_ptr, xforms, NULL); + fflush(fp); + png_destroy_write_struct(&png_ptr, &info_ptr); +} + +#else +struct csri_frame *png_load(const char *filename, + uint32_t *width, uint32_t *height, enum csri_pixfmt fmt) +{ + fprintf(stderr, "PNG support not compiled in.\n"); + return NULL; +} + +void png_store(struct csri_frame *frame, const char *filename, + uint32_t width, uint32_t height) +{ + fprintf(stderr, "PNG support not compiled in.\n"); + return; +} +#endif + +struct csri_frame *frame_alloc(uint32_t width, uint32_t height, + enum csri_pixfmt fmt) +{ + int bpp; + unsigned char *d; + struct csri_frame *frame; + + if (!csri_is_rgb(fmt)) + return NULL; + + bpp = fmt < CSRI_F_RGB ? 4 : 3; + d = (unsigned char *)malloc(width * height * bpp); + frame = (struct csri_frame *)malloc(sizeof(struct csri_frame)); + if (!frame || !d) + return NULL; + memset(frame, 0, sizeof(*frame)); + frame->pixfmt = fmt; + frame->strides[0] = width * bpp; + memset(d, csri_has_alpha(fmt) ? 0x00 : 0x80, width * height * bpp); + frame->planes[0] = d; + return frame; +} + +void frame_free(struct csri_frame *frame) +{ + int c; + for (c = 0; c < 4; c++) + if (frame->planes[c]) + free(frame->planes[c]); + + free(frame); +} + +void frame_copy(struct csri_frame *dst, struct csri_frame *src, + uint32_t width, uint32_t height) +{ + memcpy(dst->planes[0], src->planes[0], height * src->strides[0]); +} diff --git a/csri/frontends/cmdline/render.h b/csri/frontends/cmdline/render.h new file mode 100644 index 000000000..77d91a315 --- /dev/null +++ b/csri/frontends/cmdline/render.h @@ -0,0 +1,34 @@ +/***************************************************************************** + * asa: portable digital subtitle renderer + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + +#ifndef _RENDER_H +#define _RENDER_H + +extern struct csri_frame *png_load(const char *filename, + uint32_t *width, uint32_t *height, enum csri_pixfmt fmt); +extern void png_store(struct csri_frame *frame, const char *filename, + uint32_t width, uint32_t height); +extern struct csri_frame *frame_alloc(uint32_t width, uint32_t height, + enum csri_pixfmt fmt); +extern void frame_free(struct csri_frame *frame); +extern void frame_copy(struct csri_frame *dst, struct csri_frame *src, + uint32_t width, uint32_t height); + +#endif diff --git a/csri/include/Makefile.am b/csri/include/Makefile.am new file mode 100644 index 000000000..7ed1e0f91 --- /dev/null +++ b/csri/include/Makefile.am @@ -0,0 +1,2 @@ +noinst_HEADERS = subhelp.h visibility.h +pkginclude_HEADERS = csri/csri.h csri/openerr.h csri/logging.h csri/stream.h diff --git a/csri/include/csri/csri.h b/csri/include/csri/csri.h new file mode 100644 index 000000000..172428b1c --- /dev/null +++ b/csri/include/csri/csri.h @@ -0,0 +1,348 @@ +/***************************************************************************** + * csri: common subtitle renderer interface + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - The name of the author may not 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 + ****************************************************************************/ + +/** \file csri.h - main CSRI (common subtitle renderer interface) include. + * $Id: csri.h 45 2007-06-20 01:00:40Z equinox $ */ + +#ifndef _CSRI_H +/** \cond */ +#define _CSRI_H 20070119 +/** \endcond */ + +#include /* ptrdiff_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef CSRIAPI +/** CSRI API attributes. + * defaults to \c extern. + */ +#define CSRIAPI extern +#endif + +/** \defgroup base CSRI base API. */ +/*@{*/ + +/** pixel format specification for frames */ +enum csri_pixfmt { + CSRI_F_RGBA = 0, + CSRI_F_ARGB, + CSRI_F_BGRA, + CSRI_F_ABGR, + + CSRI_F_RGB_ = 0x100, + CSRI_F__RGB, + CSRI_F_BGR_, /**< Windows "RGB32" */ + CSRI_F__BGR, + + CSRI_F_RGB = 0x200, + CSRI_F_BGR, /**< Windows "RGB24" */ + + CSRI_F_AYUV = 0x1000, + CSRI_F_YUVA, + CSRI_F_YVUA, + + CSRI_F_YUY2 = 0x1100, + + CSRI_F_YV12A = 0x2011, /**< planar YUV 2x2 + alpha plane */ + CSRI_F_YV12 = 0x2111 /**< planar YUV 2x2 */ +}; + +#define csri_is_rgb(x) ((x) < 0x1000) +#define csri_is_yuv(x) ((x) >= 0x1000) +#define csri_is_yuv_planar(x) ((x) >= 0x2000) +#define csri_get_yuv_planar_xred(x) (0xf & (x)) +#define csri_get_yuv_planar_yred(x) (0xf & ((x) >> 4)) +#define csri_is_yuv_packed(x) ((x) >= 0x1000 && (x) < 0x2000) +#define csri_has_alpha(x) (((x) & 0xfff) < 0x100) + +/** frame/image format specification pre-fed to the renderer */ +struct csri_fmt { + /** format to be used */ + enum csri_pixfmt pixfmt; + /** image width, full frame. + * + * This should specify the full size of the frame. + * Specifying the video sub-size (in case of added black + * borders) is left to an extension. + */ + unsigned width; + /** image height */ + unsigned height; +}; + +/** single frame to be fed to the renderer. */ +struct csri_frame { + /** frame format. + * It is an application bug if this differs from the one + * passed in struct #csri_fmt to csri_query_fmt() + */ + enum csri_pixfmt pixfmt; + /** the frame's data. + * Packed formats only use planes[0]; planar formats + * have the data ordered as Y, U, V[, A]. + * + * Also note that the topmost line always comes first. + * The Windows biHeight strange-ity is \a NOT duplicated. + */ + unsigned char *planes[4]; + /** strides for the individual planes. + * Stride means full byte offset, i.e. do \a not add + * frame width + */ + ptrdiff_t strides[4]; +}; + +#ifndef CSRI_OWN_HANDLES +/** opaque renderer data */ +typedef void csri_rend; +/** opaque instance data */ +typedef void csri_inst; +#endif + +#ifdef DOXYGEN +/** disable the emission of the csri_rend and csri_inst typedefs. + * define this if you are in a renderer and are typedef'ing + * csri_rend and csri_inst to your own structs. + */ +#define CSRI_OWN_HANDLES +#endif + +/** renderer description. + * \ingroup loader + */ +struct csri_info { + /** an identifier for the renderer. + * - MUST match the regular expression + * \code ^[a-zA-Z]([a-zA-Z0-9_]*[a-zA-Z0-9])? \endcode + * i.e. consists only of letters, numbers and underscores; + * must start with a letter and doesnt have an underscore + * as the last character. + */ + const char *name; + /** an identifier to the exact version of the renderer. + * most likely a version number or revision identifier. + * + * The helper library does a strcmp over this field in order + * to order multiple instances of the same renderer. Use + * higher-byte-value strings for newer renderers. + */ + const char *specific; + + /** a nice name to be presented to the user */ + const char *longname; + /** the renderer's author */ + const char *author; + /** a copyright string. Copyright (c) 2042 by Mr. Nice Guy */ + const char *copyright; +}; + +/** data of extension-dependent type. + * The field to be used MUST be specified in the extension where it is used. + */ +union csri_vardata { + long lval; + double dval; + const char *utf8val; + void *otherval; +}; + +/** extension identifier. + * This follows reverse DNS syntax, i.e.: + * \code root.branch.leaf \endcode + * you can either reverse a registered domain name, e.g. + * \code com.microsoft.csri.usegdiplus \endcode + * or ask the CSRI maintainers to assign a namespace to you. + * + * currently registered namespaces are: + * + * \code + * csri.* - official extensions + * asa.* - custom extensions of the asa renderer + * \endcode + */ +typedef const char *csri_ext_id; + +/** script loading parameters. + * each flag MUST have an associated extension, which can be queried + * with csri_query_ext(). If the open flag constitutes an extension on its + * sole own, csri_query_ext() can return a meaningless non-NULL value for + * it. + * + * The data field used must be specified. + * + * An extension can have multiple flags. In that case, the flags should have + * the extension name as common prefix, separated with a dot. + * + * A renderer MUST ignore unknown open flags. It MUST NOT return an error + * just because it does not support a particular flag. + */ +struct csri_openflag { + /** flag name */ + csri_ext_id name; + /** flag data argument */ + union csri_vardata data; + /** link to next flag */ + struct csri_openflag *next; +}; + +/** load a script from a file. + * \param renderer the handle to the renderer + * \param filename the path to the file to be loaded. \n + * The filename should be encoded as UTF-8. Windows renderers are + * expected to convert it to UTF-16 and use the Unicode Windows API + * functions. + * \param flags a linked list of open flags. \n + * The caller manages memory allocation, i.e. static allocation is OK. + * \return the renderer instance handle, or NULL on error. + */ +CSRIAPI csri_inst *csri_open_file(csri_rend *renderer, + const char *filename, struct csri_openflag *flags); + +/** load a script from memory. + * \param renderer the handle to the renderer + * \param data pointer to the first data byte. \n + * The caller manages memory allocation and should free the data after + * calling csri_open_mem(). If the renderer needs to keep the data, it + * must copy it. \n + * The renderer is not allowed to write to the data. + * \param length length, in bytes, of the data + * \param flags see csri_open_file() + * \return the render instance handle, or NULL on error. + */ + +CSRIAPI csri_inst *csri_open_mem(csri_rend *renderer, + const void *data, size_t length, struct csri_openflag *flags); + +/** close a renderer instance. + * \param inst the instance handle. + */ +CSRIAPI void csri_close(csri_inst *inst); + + +/** query / set the image format and size. + * \param inst the renderer instance handle + * \param fmt the format and image size to be used + * \return 0 if the format was successfully set, + * any other value in case of error. + */ +CSRIAPI int csri_request_fmt(csri_inst *inst, const struct csri_fmt *fmt); + +/** render a single frame + * \param inst the renderer instance handle + * \param frame frame data to render to + * \param time associated timestamp of the frame + */ +CSRIAPI void csri_render(csri_inst *inst, struct csri_frame *frame, + double time); + + +/** query for an extension. + * \param rend the renderer handle + * \param extname the extension's identifier + * \return NULL if the extension is not supported, + * a pointer to extension-specific data otherwise + * + * The data pointed to by the return value does not neccessarily need to + * have any meaning; An extension that does not need to return data + * can return a pointer to whatever it wants, as long as that pointer is + * not NULL. + * + * In the usual case, the pointer is supposed to point to a struct with + * function pointers and other information as needed. + */ +CSRIAPI void *csri_query_ext(csri_rend *rend, csri_ext_id extname); + +/*@}*/ + +/** \defgroup loader CSRI loader API. + * + * These functions locate renderers based on given parameters. + * + * Renderers must implement these functions as well. + * + * They are used by the library to grab renderer information + * from a shared object; and also this way a single renderer + * can be linked directly into an appliaction. + */ +/*@{*/ + +/** get renderer information + * \param rend the renderer handle + * \return information about the renderer, or NULL in case the renderer + * encountered an internal error. + */ +CSRIAPI struct csri_info *csri_renderer_info(csri_rend *rend); + +/** try to load a given renderer + * \param name the name of the renderer, as in csri_info.name + * \param specific the specific version of the renderer, + * as in csri_info.specific;\n + * alternatively NULL if any version of the renderer is ok. + * \return a handle to the renderer if it was successfully loaded, + * NULL otherwise. + */ +CSRIAPI csri_rend *csri_renderer_byname(const char *name, + const char *specific); + +/** try to find an implementation of the given extensions. + * \param next number of extensions pointed to by ext + * \param ext array of extensions to search for + * \return a handle to a renderer supporting ALL of the + * extensions, NULL if none was found. + */ +CSRIAPI csri_rend *csri_renderer_byext(unsigned n_ext, csri_ext_id *ext); + +/** get the default (highest priority) renderer + * \return a handle to the default renderer, or NULL if + * no renderer is installed. + * + * Together with csri_renderer_next(), this can be used + * to enumerate all installed renderers. + */ +CSRIAPI csri_rend *csri_renderer_default(); + +/** get the next lower priority renderer + * \param prev the current renderer + * \return the renderer with the next lower priority than + * the one named in prev, or NULL if prev is the last + * renderer installed. + */ +CSRIAPI csri_rend *csri_renderer_next(csri_rend *prev); + +/*@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _CSRI_H */ diff --git a/csri/include/csri/fmtids.h b/csri/include/csri/fmtids.h new file mode 100644 index 000000000..620d818d7 --- /dev/null +++ b/csri/include/csri/fmtids.h @@ -0,0 +1,65 @@ +/***************************************************************************** + * csri: common subtitle renderer interface + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - The name of the author may not 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 + ****************************************************************************/ + +/** \file fmtids.h - csri.format - extension identifiers for subtitle formats. + * $Id$ */ + +#ifndef _CSRI_FMTIDS_H +/** \cond */ +#define _CSRI_FMTIDS_H 20070119 +/** \endcond */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup fmtids csri.format identifiers. + * only includes the most important ones for now, more to be added. */ +/*@{*/ + +/** SSA / ASS. Sub Station Alpha - versions 2, 3, 4, 4+ and 4++ */ +#define CSRI_EXT_FMT_SSA (csri_ext_id)"csri.format.ssa" +/** SRT. SubRip Text, SubRip Titles or something similar. */ +#define CSRI_EXT_FMT_SRT (csri_ext_id)"csri.format.srt" +/** MicroDVD */ +#define CSRI_EXT_FMT_MICRODVD (csri_ext_id)"csri.format.microdvd" +/** SAMI. Microsoft Synchronized Accessible Media Interchange */ +#define CSRI_EXT_FMT_SAMI (csri_ext_id)"csri.format.sami" +/** SMIL. W3C Synchronized Multimedia Integration Language. + * NB: this format uses separate files for text streams */ +#define CSRI_EXT_FMT_SMIL (csri_ext_id)"csri.format.smil" + +/*@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _CSRI_OPENERR_H */ diff --git a/csri/include/csri/logging.h b/csri/include/csri/logging.h new file mode 100644 index 000000000..b530f4d16 --- /dev/null +++ b/csri/include/csri/logging.h @@ -0,0 +1,79 @@ +/***************************************************************************** + * csri: common subtitle renderer interface + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - The name of the author may not 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 + ****************************************************************************/ + +/** \file logging.h - csri.logging - logging for renderers via callback. + * $Id$ + * + * THE SPECIFICATION OF THIS EXTENSION IS TENTATIVE + * AND NOT FINALIZED YET + */ + +#ifndef _CSRI_LOGGING_H +/** \cond */ +#define _CSRI_LOGGING_H 20070119 +/** \endcond */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup logging csri.logging extension. + * THE SPECIFICATION OF THIS EXTENSION IS TENTATIVE + * AND NOT FINALIZED YET + */ +/*@{*/ + +/** extension identifier */ +#define CSRI_EXT_LOGGING (csri_ext_id)"csri.logging" + +/** -. TODO: add scope? */ +enum csri_logging_severity { + CSRI_LOG_DEBUG = 0, + CSRI_LOG_INFO, + CSRI_LOG_NOTICE, + CSRI_LOG_WARNING, + CSRI_LOG_ERROR +}; + +typedef void csri_logging_func(void *appdata, + enum csri_logging_severity sev, + const char *message); + +struct csri_logging_ext { + void (*set_logcallback)(csri_logging_func *func, void *appdata); +}; + +/*@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _CSRI_LOGGING_H */ diff --git a/csri/include/csri/openerr.h b/csri/include/csri/openerr.h new file mode 100644 index 000000000..a5b1fd7ff --- /dev/null +++ b/csri/include/csri/openerr.h @@ -0,0 +1,124 @@ +/***************************************************************************** + * csri: common subtitle renderer interface + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - The name of the author may not 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 + ****************************************************************************/ + +/** \file openerr.h - csri.openerr - error return extension. + * $Id$ */ + +#ifndef _CSRI_OPENERR_H +/** \cond */ +#define _CSRI_OPENERR_H 20070119 +/** \endcond */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup openerr csri.openerr extension. */ +/*@{*/ + +/** extension identifier */ +#define CSRI_EXT_OPENERR (csri_ext_id)"csri.openerr" + +/** flag field describing which fields of #csri_openerr_flag are valid */ +enum csri_openerr_flags { + /** support indicator. + * set if the structure was filled with any meaningful data + */ + CSRI_OPENERR_FILLED = (1 << 0), + /** csri_openerr_flag.posixerrno valid */ + CSRI_OPENERR_ERRNO = (1 << 1), + /** csri_openerr_flag.winerr valid */ + CSRI_OPENERR_WINERR = (1 << 2), + /** csri_openerr_flag.custerr valid */ + CSRI_OPENERR_CUSTERR = (1 << 3), + /** csri_openerr_flag.warncount valid */ + CSRI_OPENERR_WARNCOUNT = (1 << 4) +}; + +/** returned error information. + * to be passed via csri_vardata.otherval as an openflag with + * extension ID #CSRI_EXT_OPENERR. + * + * Memory management by caller. + * + * The three error fields should only be filled when csri_open_file() + * / csri_open_mem() returned NULL. The warning counter can indicate + * information on successfully loaded scripts. + */ +struct csri_openerr_flag { + /** bitfield of valid information */ + enum csri_openerr_flags flags; + /** posix errno value indicating the error occured */ + int posixerrno; + /** Windows GetLastError value */ + unsigned winerr; + /** renderer-specific custom error value. + * should be string-lookupable via csri_openerr_ext.strerror + * (csri_query_ext()) + */ + union csri_vardata custerr; + /** warning count. + * The number of (renderer-specific) warnings that occured + * during loading the script. The content of these warnings + * should be retrievable via a renderer-specific extension. + */ + unsigned warncount; +}; + +/** openerr extension information structure */ +struct csri_openerr_ext { + /** csri_openerr_flag.custerr to string lookup. + * \param renderer the renderer handle. + * \param custerr renderer-specific error + * \param buffer buffer to fill with an UTF-8 error message. + * may be NULL; in that case only size is filled in + * \param size buffer size.\n + * in: maximum bytes to write to buffer, in bytes, + * including terminating \\0.\nthe renderer MUST always + * zero-terminate this, even when the space is not sufficient + * \n\n + * out: number of bytes (including terminating \\0) needed + * to return the full error message + * + * This function pointer may be NULL if the renderer does not + * return custom error codes. #CSRI_OPENERR_CUSTERR must not be + * used in that case. + */ + void (*strerror)(csri_rend *renderer, union csri_vardata custerr, + char *buffer, size_t *size); +}; + +/*@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _CSRI_OPENERR_H */ diff --git a/csri/include/csri/stream.h b/csri/include/csri/stream.h new file mode 100644 index 000000000..cc24d97d6 --- /dev/null +++ b/csri/include/csri/stream.h @@ -0,0 +1,143 @@ +/***************************************************************************** + * csri: common subtitle renderer interface + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - The name of the author may not 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 + ****************************************************************************/ + +/** \file stream.h - subtitle streaming (MKV & co). + * $Id$ + * + * THE SPECIFICATION OF THIS EXTENSION IS TENTATIVE + * AND NOT FINALIZED YET + */ + +#ifndef _CSRI_STREAM_H +/** \cond */ +#define _CSRI_STREAM_H 20070119 +/** \endcond */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup stream csri.stream.* extensions. */ +/*@{*/ + +/** stream extension group. + * note: you cannot query for csri.stream, you need + * to query for one of the streaming formats instead, + * which will return a csri_stream_ext pointer. + */ +#define CSRI_EXT_STREAM (csri_ext_id)"csri.stream" + +/** Matroska-style ASS streaming. + * header contains standard SSA stuff, packet contains + * ReadOrder,Layer,Style,Speaker,MarginL,R,V,Effect,Text + */ +#define CSRI_EXT_STREAM_ASS CSRI_EXT_STREAM ".ass" +/** Simple text + timestamp streams */ +#define CSRI_EXT_STREAM_TEXT CSRI_EXT_STREAM ".text" +/* missing: USF, MPEG-4 TT */ + +/** stream extension information structure */ +struct csri_stream_ext { + /** create streaming renderer instance. + * \param renderer the renderer handle. + * \param header codec-private stream header. + * \param headerlen byte length of header data. + * \param flags openflags. + * + * not NULL if this extension is supported. + * may take various flags like csri_openerr_flag. + * + * the returned instance can be used with + * csri_request_fmt, csri_render and csri_close. + */ + csri_inst *(*init_stream)(csri_rend *renderer, + const void *header, size_t headerlen, + struct csri_openflag *flags); + + /** process a streamed packet. + * \param inst instance created with init_stream. + * \param packet stream packet data. + * \param packetlen byte length of packet. + * \param pts_start start timestamp from container. + * \param pts_end end timestamp from container. + * + * add a single packet to the renderer instance. + */ + void (*push_packet)(csri_inst *inst, + const void *packet, size_t packetlen, + double pts_start, double pts_end); + + /** discard processed packets. + * \param inst instance created with init_stream. + * \param all discard possibly-active packets too.\n + * a possibly-active packet is a packet which + * has not seen a csri_render call with a pts + * beyond its end timestamp yet. + * + * frees up memory, or can force a clean renderer. + * may be NULL if unsupported, check before calling! + */ + void (*discard)(csri_inst *inst, int all); +}; + +/** streaming openflag ext for controlling subtitle lifetime */ +#define CSRI_EXT_STREAM_DISCARD CSRI_EXT_STREAM ".discard" + +/** subtitle packet lifetime */ +enum csri_stream_discard { + /** lifetime: timestamp expiry. + * delete packets from csri_render if the current + * timestamp is beyond the packet's end timestamp. + * this should be the default + */ + CSRI_STREAM_DISCARD_TSEXPIRE = 0, + /** lifetime: discard immediately. + * discard all packets on returning from csri_render. + */ + CSRI_STREAM_DISCARD_IMMEDIATELY, + /** lifetime: discard explicitly. + * never discard packets, use csri_stream_ext.discard + */ + CSRI_STREAM_DISCARD_EXPLICIT +}; + +/** openflag for csri_stream_ext.init_stream */ +struct csri_stream_discard_flag { + /** the lifetime to be used for subtitle packets */ + enum csri_stream_discard lifetime; +}; + +/*@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _CSRI_STREAM_H */ diff --git a/csri/include/subhelp.h b/csri/include/subhelp.h new file mode 100644 index 000000000..ffde5d992 --- /dev/null +++ b/csri/include/subhelp.h @@ -0,0 +1,114 @@ +/***************************************************************************** + * csri: common subtitle renderer interface + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - The name of the author may not 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 + ****************************************************************************/ + +/** \file subhelp.h - subtitle helper API. + * $Id$ */ + +#ifndef _SUBHELP_H +#define _SUBHELP_H + +#include +#include +#include + +/** \defgroup subhelp subtitle filter helper API. */ +/*@{*/ + +/** file opening wrapper. + * can be used to implement csri_open_file() by using csri_open_mem(). + * \param renderer the renderer handle + * \param memopenfunc function pointer to a csri_open_mem() implementation + * \param filename name of file to open + * \param flags pointer #csri_openflag.\n + * subhelp_open_file will fill in csri.openerr if present + * \return return value from memopenfunc or NULL on fs error + */ +extern csri_inst *subhelp_open_file(csri_rend *renderer, + csri_inst *(*memopenfunc)(csri_rend *renderer, const void *data, + size_t length, struct csri_openflag *flags), + const char *filename, struct csri_openflag *flags); + + +/** logging extension query function. + * call from csri_query_ext BEFORE checking whether the renderer. + * \code + * void *csri_query_ext(csri_rend *rend, csri_ext_id extname) { + * void *rv; + * if ((rv = subhelp_query_ext_logging(extname))) + * return rv; + * if (!rend) + * return NULL; + * ... + * \endcode + * \param extname the extension name. compared to "csri.logging" by + * this function. + * \return logging extension pointer, if the extension name matched. + * NULL otherwise. + */ +extern void *subhelp_query_ext_logging(csri_ext_id extname); + +/** configure other renderer with our settings. + * \param logext csri.logging from configuree. + */ +extern void subhelp_logging_pass(struct csri_logging_ext *logext); + +/** logging function. + * \param severity severity of this message, as defined by csri.logging + * \param msg log message, one line, without \\n at the end. + */ +extern void subhelp_log(enum csri_logging_severity severity, + const char *msg, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 2, 3))) +#endif + ; + +/** logging function, varargs version. + * \param severity severity of this message, as defined by csri.logging + * \param msg log message, one line, without \\n at the end. + * \param args argument list + */ +extern void subhelp_vlog(enum csri_logging_severity severity, + const char *msg, va_list args) +#ifdef __GNUC__ + __attribute__((format(printf, 2, 0))) +#endif + ; + +/** logging function, fixed string version. + * \param severity severity of this message, as defined by csri.logging + * \param msg log message, one line, without \\n at the end. + */ +extern void subhelp_slog(enum csri_logging_severity severity, const char *msg); + +/*@}*/ + +#endif /* _SUBHELP_H */ + diff --git a/csri/include/visibility.h b/csri/include/visibility.h new file mode 100644 index 000000000..632f07ef2 --- /dev/null +++ b/csri/include/visibility.h @@ -0,0 +1,43 @@ +/***************************************************************************** + * asa: portable digital subtitle renderer + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + + +#ifndef _VISIBILITY_H +#define _VISIBILITY_H + +#ifdef HAVE_CONFIG_H +#include "acconf.h" +#endif + +#ifdef _WIN32 +#define export __declspec(dllexport) +#define internal +#define hidden +#elif HAVE_GCC_VISIBILITY +#define export __attribute__((visibility ("default"))) +#define internal __attribute__((visibility ("internal"))) +#define hidden __attribute__((visibility ("hidden"))) +#else +#define export +#define internal +#define hidden +#endif + +#endif /* _VISIBILITY_H */ diff --git a/csri/lib/Makefile.am b/csri/lib/Makefile.am new file mode 100644 index 000000000..7bea258d3 --- /dev/null +++ b/csri/lib/Makefile.am @@ -0,0 +1,14 @@ +if BUILD_MINGW +PLATF = win32 +else +PLATF = posix +AM_CPPFLAGS = -DCSRI_PATH="\"$(csri_path)\"" +endif +lib_LTLIBRARIES = libcsri.la +noinst_HEADERS = csrilib.h posix/csrilib_os.h win32/csrilib_os.h +libcsri_la_SOURCES = list.c wrap.c $(PLATF)/enumerate.c +libcsri_la_CFLAGS = -I$(top_srcdir)/lib/posix -I$(top_srcdir)/include +libcsri_la_LIBADD = ../subhelp/libsubhelp_la-logging.lo +libcsri_la_LDFLAGS = -static + +EXTRA_DIST = posix/enumerate.c win32/enumerate.c diff --git a/csri/lib/csrilib.h b/csri/lib/csrilib.h new file mode 100644 index 000000000..66a6a27e8 --- /dev/null +++ b/csri/lib/csrilib.h @@ -0,0 +1,85 @@ +/***************************************************************************** + * asa: portable digital subtitle renderer + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + +#ifndef _CSRILIB_H +#define _CSRILIB_H + +#ifdef HAVE_CONFIG_H +#include "acconf.h" +#endif + +#include "visibility.h" + +#define CSRIAPI export + +#include +#include +#include "csrilib_os.h" + +struct csri_wrap_rend { + struct csri_wrap_rend *next; + + csri_rend *rend; + csri_inst *(*open_file)(csri_rend *renderer, + const char *filename, struct csri_openflag *flags); + csri_inst *(*open_mem)(csri_rend *renderer, + const void *data, size_t length, + struct csri_openflag *flags); + void (*close)(csri_inst *inst); + int (*request_fmt)(csri_inst *inst, const struct csri_fmt *fmt); + void (*render)(csri_inst *inst, struct csri_frame *frame, + double time); + void *(*query_ext)(csri_rend *rend, csri_ext_id extname); + struct csri_stream_ext stream_ass, stream_text; + csri_inst *(*init_stream_ass)(csri_rend *renderer, + const void *header, size_t headerlen, + struct csri_openflag *flags); + csri_inst *(*init_stream_text)(csri_rend *renderer, + const void *header, size_t headerlen, + struct csri_openflag *flags); + + struct csri_info *info; + struct csrilib_os os; +}; + +struct csri_wrap_inst { + struct csri_wrap_inst *next; + + csri_inst *inst; + struct csri_wrap_rend *wrend; + void (*close)(csri_inst *inst); + int (*request_fmt)(csri_inst *inst, const struct csri_fmt *fmt); + void (*render)(csri_inst *inst, struct csri_frame *frame, + double time); +}; + +extern struct csri_wrap_rend *wraprends; + +extern struct csri_wrap_rend *csrilib_rend_lookup(csri_rend *rend); +extern void csrilib_rend_initadd(struct csri_wrap_rend *wrend); + +extern struct csri_wrap_inst *csrilib_inst_lookup(csri_inst *inst); +extern csri_inst *csrilib_inst_initadd(struct csri_wrap_rend *wrend, + csri_inst *inst); +extern void csrilib_inst_remove(struct csri_wrap_inst *winst); + +extern void csrilib_os_init(); + +#endif /*_CSRILIB_H */ diff --git a/csri/lib/list.c b/csri/lib/list.c new file mode 100644 index 000000000..3c5826698 --- /dev/null +++ b/csri/lib/list.c @@ -0,0 +1,148 @@ +/***************************************************************************** + * asa: portable digital subtitle renderer + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + +#include +#include +#include +#include + +#include "csrilib.h" + +struct csri_wrap_rend *wraprends = NULL; + +struct csri_wrap_rend *csrilib_rend_lookup(csri_rend *rend) +{ + struct csri_wrap_rend *wrap = wraprends; + for (; wrap; wrap = wrap->next) + if (wrap->rend == rend) + return wrap; + return NULL; +} + +#define INSTHASHSZ 256 +#define HASH(x) (((intptr_t)(x) & 0xff00) >> 8) +static struct csri_wrap_inst *wrapinsts[INSTHASHSZ]; + +struct csri_wrap_inst *csrilib_inst_lookup(csri_inst *inst) +{ + struct csri_wrap_inst *ent = wrapinsts[HASH(inst)]; + while (ent && ent->inst != inst) + ent = ent->next; + return ent; +} + +csri_inst *csrilib_inst_initadd(struct csri_wrap_rend *wrend, + csri_inst *inst) +{ + struct csri_wrap_inst *winst = (struct csri_wrap_inst *) + malloc(sizeof(struct csri_wrap_inst)), + **pnext; + if (!winst) { + wrend->close(inst); + return NULL; + } + winst->wrend = wrend; + winst->inst = inst; + winst->close = wrend->close; + winst->request_fmt = wrend->request_fmt; + winst->render = wrend->render; + winst->next = NULL; + pnext = &wrapinsts[HASH(inst)]; + while (*pnext) + pnext = &(*pnext)->next; + *pnext = winst; + return inst; +} + +void csrilib_inst_remove(struct csri_wrap_inst *winst) +{ + struct csri_wrap_inst **pnext = &wrapinsts[HASH(winst->inst)]; + while (*pnext && *pnext != winst) + pnext = &(*pnext)->next; + if (!*pnext) + return; + *pnext = (*pnext)->next; +} + +void csrilib_rend_initadd(struct csri_wrap_rend *wrend) +{ + wrend->next = wraprends; + wraprends = wrend; +} + +static int initialized = 0; + +csri_rend *csri_renderer_default() +{ + if (!initialized) { + csrilib_os_init(); + initialized = 1; + } + if (!wraprends) + return NULL; + return wraprends->rend; +} + +csri_rend *csri_renderer_next(csri_rend *prev) +{ + struct csri_wrap_rend *wrend = csrilib_rend_lookup(prev); + if (!wrend || !wrend->next) + return NULL; + return wrend->next->rend; +} + +csri_rend *csri_renderer_byname(const char *name, const char *specific) +{ + struct csri_wrap_rend *wrend; + if (!initialized) { + csrilib_os_init(); + initialized = 1; + } + if (!name) + return NULL; + for (wrend = wraprends; wrend; wrend = wrend->next) { + if (strcmp(wrend->info->name, name)) + continue; + if (specific && strcmp(wrend->info->specific, specific)) + continue; + return wrend->rend; + } + return NULL; +} + +csri_rend *csri_renderer_byext(unsigned n_ext, csri_ext_id *ext) +{ + struct csri_wrap_rend *wrend; + unsigned i; + if (!initialized) { + csrilib_os_init(); + initialized = 1; + } + for (wrend = wraprends; wrend; wrend = wrend->next) { + for (i = 0; i < n_ext; i++) { + if (!wrend->query_ext(wrend->rend, ext[i])) + break; + } + if (i == n_ext) + return wrend->rend; + } + return NULL; +} + diff --git a/csri/lib/posix/csrilib_os.h b/csri/lib/posix/csrilib_os.h new file mode 100644 index 000000000..d8b780233 --- /dev/null +++ b/csri/lib/posix/csrilib_os.h @@ -0,0 +1,27 @@ +/***************************************************************************** + * asa: portable digital subtitle renderer + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + +#include + +struct csrilib_os { + void *dlhandle; + dev_t device; + ino_t inode; +}; diff --git a/csri/lib/posix/enumerate.c b/csri/lib/posix/enumerate.c new file mode 100644 index 000000000..ab4eb06bc --- /dev/null +++ b/csri/lib/posix/enumerate.c @@ -0,0 +1,199 @@ +/***************************************************************************** + * asa: portable digital subtitle renderer + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + +#define _POSIX_C_SOURCE 200112L /* for PATH_MAX */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../csrilib.h" +#include "subhelp.h" + +static const char csri_path[] = CSRI_PATH; + +static void csrilib_enum_dir(const char *dir); + +static void csrilib_add(csri_rend *rend, + const struct csri_wrap_rend *tmp, struct csri_info *info) +{ + struct csri_wrap_rend *wrend = (struct csri_wrap_rend *) + malloc(sizeof(struct csri_wrap_rend)); + if (!wrend) + return; + memcpy(wrend, tmp, sizeof(struct csri_wrap_rend)); + wrend->rend = rend; + wrend->info = info; + csrilib_rend_initadd(wrend); +} + +static void csrilib_do_load(const char *filename, dev_t device, ino_t inode) +{ + void *dlhandle = dlopen(filename, RTLD_NOW); + struct csri_wrap_rend tmp; + csri_rend *rend; + struct csri_info *(*renderer_info)(csri_rend *rend); + csri_rend *(*renderer_default)(); + csri_rend *(*renderer_next)(csri_rend *prev); + const char *sym; + + + if (!dlhandle) { + subhelp_log(CSRI_LOG_WARNING, "dlopen(\"%s\") says: %s", + filename, dlerror()); + return; + } + if (dlsym(dlhandle, "csri_library")) { + subhelp_log(CSRI_LOG_WARNING, "ignoring library %s", + filename); + return; + } + subhelp_log(CSRI_LOG_INFO, "loading %s", filename); + + tmp.os.dlhandle = dlhandle; + tmp.os.device = device; + tmp.os.inode = inode; + +/* okay, this is uber-ugly. either I end up casting from void * + * to a fptr (which yields a cast warning), or I do a *(void **)&tmp.x + * (which yields a strict-aliasing warning). + * casting via char* works because char* can alias anything. + */ +#define _dl_map_function(x, dst) do { \ + char *t1 = (char *)&dst; \ + union x { void *ptr; } *ptr = (union x *)t1; \ + sym = "csri_" # x; \ + ptr->ptr = dlsym(dlhandle, sym);\ + if (!ptr->ptr) goto out_dlfail; } while (0) +#define dl_map_function(x) _dl_map_function(x, tmp.x) + dl_map_function(query_ext); + subhelp_logging_pass((struct csri_logging_ext *) + tmp.query_ext(NULL, CSRI_EXT_LOGGING)); + dl_map_function(open_file); + dl_map_function(open_mem); + dl_map_function(close); + dl_map_function(request_fmt); + dl_map_function(render); +#define dl_map_local(x) _dl_map_function(x, x) + dl_map_local(renderer_info); + dl_map_local(renderer_default); + dl_map_local(renderer_next); + + rend = renderer_default(); + while (rend) { + csrilib_add(rend, &tmp, renderer_info(rend)); + rend = renderer_next(rend); + } + return; + +out_dlfail: + subhelp_log(CSRI_LOG_WARNING, "%s: symbol %s not found (%s)", + filename, sym, dlerror()); + dlclose(dlhandle); +} + +static void csrilib_load(const char *filename) +{ + struct csri_wrap_rend *rend; + struct stat st; + if (stat(filename, &st)) + return; + + if (S_ISDIR(st.st_mode)) { + csrilib_enum_dir(filename); + return; + } + if (!S_ISREG(st.st_mode)) + return; + if (access(filename, X_OK)) + return; + + for (rend = wraprends; rend; rend = rend->next) + if (rend->os.device == st.st_dev + && rend->os.inode == st.st_ino) + return; + + csrilib_do_load(filename, st.st_dev, st.st_ino); +} + +static void csrilib_enum_dir(const char *dir) +{ + DIR *dirh; + struct dirent *e; + char buf[PATH_MAX]; + + dirh = opendir(dir); + if (!dirh) { + subhelp_log(CSRI_LOG_WARNING, "ignoring directory \"%s\":" + " %s (%d)", dir, strerror(errno), errno); + return; + } + + subhelp_log(CSRI_LOG_INFO, "scanning directory \"%s\"", dir); + while ((e = readdir(dirh))) { + if (e->d_name[0] == '.') + continue; + snprintf(buf, sizeof(buf), "%s/%s", dir, e->d_name); + csrilib_load(buf); + } + + closedir(dirh); +} + +static void csrilib_expand_enum_dir(const char *dir) +{ + if (dir[0] == '~' && (dir[1] == '\0' || dir[1] == '/')) { + char buf[PATH_MAX], *home = getenv("HOME"); + if (!home) + home = ""; + snprintf(buf, sizeof(buf), "%s%s", home, dir + 1); + csrilib_enum_dir(buf); + } else + csrilib_enum_dir(dir); +} + +void csrilib_os_init() +{ + char buf[4096]; + char *envpath = getenv("CSRI_PATH"); + char *pos, *next = buf; + + if (envpath) + snprintf(buf, sizeof(buf), "%s:%s", csri_path, envpath); + else { + strncpy(buf, csri_path, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + } + do { + pos = next; + next = strchr(pos, ':'); + if (next) + *next++ = '\0'; + csrilib_expand_enum_dir(pos); + } while (next); +} + diff --git a/csri/lib/win32/csrilib_os.h b/csri/lib/win32/csrilib_os.h new file mode 100644 index 000000000..c40014d99 --- /dev/null +++ b/csri/lib/win32/csrilib_os.h @@ -0,0 +1,25 @@ +/***************************************************************************** + * asa: portable digital subtitle renderer + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + +#include + +struct csrilib_os { + HMODULE dlhandle; +}; diff --git a/csri/lib/win32/enumerate.c b/csri/lib/win32/enumerate.c new file mode 100644 index 000000000..74c5b925a --- /dev/null +++ b/csri/lib/win32/enumerate.c @@ -0,0 +1,175 @@ +/***************************************************************************** + * asa: portable digital subtitle renderer + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + +#include +#include +#include +#include + +#include "../csrilib.h" +#include "subhelp.h" + +static void csrilib_enum_dir(const wchar_t *dir); + +static const char *get_errstr() +{ + static char msg[2048]; + DWORD err = GetLastError(); + + if (!FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, 0, msg, sizeof(msg), NULL)) + strcpy(msg, "Unknown Error"); + else { + int msglen = strlen(msg) - 1; + if (msg[msglen] == '\n') + msg[msglen] = '\0'; + } + return msg; +} + +static void csrilib_add(csri_rend *rend, + const struct csri_wrap_rend *tmp, struct csri_info *info) +{ + struct csri_wrap_rend *wrend = (struct csri_wrap_rend *) + malloc(sizeof(struct csri_wrap_rend)); + if (!wrend) + return; + memcpy(wrend, tmp, sizeof(struct csri_wrap_rend)); + wrend->rend = rend; + wrend->info = info; + csrilib_rend_initadd(wrend); +} + +static void csrilib_do_load(const wchar_t *filename) +{ + HMODULE dlhandle = LoadLibraryExW(filename, NULL, + LOAD_WITH_ALTERED_SEARCH_PATH); + struct csri_wrap_rend tmp; + csri_rend *rend; + struct csri_info *(*renderer_info)(csri_rend *rend); + csri_rend *(*renderer_default)(); + csri_rend *(*renderer_next)(csri_rend *prev); + const char *sym; + + if (!dlhandle) { + subhelp_log(CSRI_LOG_WARNING, "LoadLibraryEx(\"%ls\") failed: " + "%s", filename, get_errstr()); + return; + } + if (GetProcAddress(dlhandle, "csri_library")) { + subhelp_log(CSRI_LOG_WARNING, "ignoring library %ls", + filename); + goto out_freelib; + } + subhelp_log(CSRI_LOG_INFO, "loading %ls", filename); + + tmp.os.dlhandle = dlhandle; + +/* okay, this is uber-ugly. either I end up casting from void * + * to a fptr (which yields a cast warning), or I do a *(void **)&tmp.x + * (which yields a strict-aliasing warning). + * casting via char* works because char* can alias anything. + */ +#define _dl_map_function(x, dst) do { \ + char *t1 = (char *)&dst; \ + union x { FARPROC ptr; } *ptr = (union x *)t1; \ + sym = "csri_" # x; \ + ptr->ptr = GetProcAddress(dlhandle, sym);\ + if (!ptr->ptr) goto out_dlfail; } while (0) +#define dl_map_function(x) _dl_map_function(x, tmp.x) + dl_map_function(query_ext); + subhelp_logging_pass((struct csri_logging_ext *) + tmp.query_ext(NULL, CSRI_EXT_LOGGING)); + dl_map_function(open_file); + dl_map_function(open_mem); + dl_map_function(close); + dl_map_function(request_fmt); + dl_map_function(render); +#define dl_map_local(x) _dl_map_function(x, x) + dl_map_local(renderer_info); + dl_map_local(renderer_default); + dl_map_local(renderer_next); + + rend = renderer_default(); + while (rend) { + csrilib_add(rend, &tmp, renderer_info(rend)); + rend = renderer_next(rend); + } + return; + +out_dlfail: + subhelp_log(CSRI_LOG_WARNING, "%ls: symbol %s not found (%s)", + filename, sym, get_errstr()); +out_freelib: + FreeLibrary(dlhandle); +} + +static void csrilib_load(const wchar_t *filename) +{ + DWORD attr = GetFileAttributesW(filename); + if (attr == INVALID_FILE_ATTRIBUTES) + return; + + if (attr & FILE_ATTRIBUTE_DIRECTORY) { + csrilib_enum_dir(filename); + return; + } + csrilib_do_load(filename); +} + +static void csrilib_enum_dir(const wchar_t *dir) +{ + WIN32_FIND_DATAW data; + HANDLE res; + wchar_t buf[MAX_PATH]; + + _snwprintf(buf, sizeof(buf) / sizeof(buf[0]), L"%ls\\*", dir); + res = FindFirstFileW(buf, &data); + if (res == INVALID_HANDLE_VALUE) { + subhelp_log(CSRI_LOG_WARNING, "ignoring directory \"%ls\": %s", + dir, get_errstr()); + return; + } + + subhelp_log(CSRI_LOG_INFO, "scanning directory \"%ls\"", dir); + do { + if (data.cFileName[0] == '.') + continue; + _snwprintf(buf, sizeof(buf) / sizeof(buf[0]), + L"%ls\\%ls", dir, data.cFileName); + csrilib_load(buf); + } while (FindNextFileW(res, &data)); + FindClose(res); +} + +void csrilib_os_init() +{ + wchar_t filename[MAX_PATH], *slash; + DWORD rv = GetModuleFileNameW(NULL, filename, MAX_PATH); + if (!rv) + *filename = L'\0'; + slash = wcsrchr(filename, L'\\'); + slash = slash ? slash + 1 : filename; + *slash = L'\0'; + wcsncpy(slash, L"csri", filename + MAX_PATH - slash); + csrilib_enum_dir(filename); +} + diff --git a/csri/lib/wrap.c b/csri/lib/wrap.c new file mode 100644 index 000000000..51e37da33 --- /dev/null +++ b/csri/lib/wrap.c @@ -0,0 +1,118 @@ +/***************************************************************************** + * asa: portable digital subtitle renderer + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + +#include "csrilib.h" +#include "subhelp.h" + +#include + +csri_inst *csri_open_file(csri_rend *rend, + const char *filename, struct csri_openflag *flags) +{ + struct csri_wrap_rend *wrend = csrilib_rend_lookup(rend); + if (!wrend) + return NULL; + return csrilib_inst_initadd(wrend, + wrend->open_file(rend, filename, flags)); +} + +#define instance_wrapper(wrapname, funcname) \ +csri_inst *wrapname(csri_rend *rend, \ + const void *data, size_t length, struct csri_openflag *flags) \ +{ \ + struct csri_wrap_rend *wrend = csrilib_rend_lookup(rend); \ + if (!wrend) \ + return NULL; \ + return csrilib_inst_initadd(wrend, \ + wrend->funcname(rend, data, length, flags)); \ +} + +instance_wrapper(csri_open_mem, open_mem) +static instance_wrapper(wrap_init_stream_ass, init_stream_ass) +static instance_wrapper(wrap_init_stream_text, init_stream_text) + +void *csri_query_ext(csri_rend *rend, csri_ext_id extname) +{ + struct csri_wrap_rend *wrend; + void *rv; + + if (!rend && (rv = subhelp_query_ext_logging(extname))) + return rv; + + wrend = csrilib_rend_lookup(rend); + if (!wrend) + return NULL; + rv = wrend->query_ext(rend, extname); + if (rv && !strcmp(extname, CSRI_EXT_STREAM_ASS)) { + struct csri_stream_ext *e = (struct csri_stream_ext *)rv; + memcpy(&wrend->stream_ass, e, sizeof(*e)); + wrend->init_stream_ass = e->init_stream; + wrend->stream_ass.init_stream = wrap_init_stream_ass; + return &wrend->stream_ass; + } + if (rv && !strcmp(extname, CSRI_EXT_STREAM_TEXT)) { + struct csri_stream_ext *e = (struct csri_stream_ext *)rv; + memcpy(&wrend->stream_text, e, sizeof(*e)); + wrend->init_stream_text = e->init_stream; + wrend->stream_text.init_stream = wrap_init_stream_text; + return &wrend->stream_text; + } + return rv; +} + +struct csri_info *csri_renderer_info(csri_rend *rend) +{ + struct csri_wrap_rend *wrend = csrilib_rend_lookup(rend); + if (!wrend) + return NULL; + return wrend->info; +} + +void csri_close(csri_inst *inst) +{ + struct csri_wrap_inst *winst = csrilib_inst_lookup(inst); + if (!winst) + return; + winst->close(inst); + csrilib_inst_remove(winst); +} + +int csri_request_fmt(csri_inst *inst, const struct csri_fmt *fmt) +{ + struct csri_wrap_inst *winst = csrilib_inst_lookup(inst); + if (!winst) + return 0; + return winst->request_fmt(inst, fmt); +} + +void csri_render(csri_inst *inst, struct csri_frame *frame, + double time) +{ + struct csri_wrap_inst *winst = csrilib_inst_lookup(inst); + if (!winst) + return; + winst->render(inst, frame, time); +} + +const char *csri_library() +{ + return "DEV"; +} + diff --git a/csri/subhelp/Makefile.am b/csri/subhelp/Makefile.am new file mode 100644 index 000000000..449853cd6 --- /dev/null +++ b/csri/subhelp/Makefile.am @@ -0,0 +1,10 @@ +if BUILD_MINGW +PLATF = win32 +else +PLATF = posix +endif +noinst_LTLIBRARIES = libsubhelp.la +libsubhelp_la_SOURCES = $(PLATF)/openfile.c logging.c +libsubhelp_la_CFLAGS = -I$(top_srcdir)/include + +EXTRA_DIST = win32/openfile.c posix/openfile.c diff --git a/csri/subhelp/logging.c b/csri/subhelp/logging.c new file mode 100644 index 000000000..e34978d05 --- /dev/null +++ b/csri/subhelp/logging.c @@ -0,0 +1,102 @@ +/***************************************************************************** + * asa: portable digital subtitle renderer + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "acconf.h" +#endif + +#include +#include +#include +#include + +#include "visibility.h" +#include "subhelp.h" +#include + +static void *appdata = NULL; +static csri_logging_func *logfunc = NULL; + +void setlogcallback(csri_logging_func *_logfunc, void *_appdata) hidden; + +void setlogcallback(csri_logging_func *_logfunc, void *_appdata) +{ + logfunc = _logfunc; + appdata = _appdata; +} + +static struct csri_logging_ext logext = { + setlogcallback +}; + +void subhelp_logging_pass(struct csri_logging_ext *nlogext) +{ + if (!nlogext || !nlogext->set_logcallback) + return; + nlogext->set_logcallback(logfunc, appdata); +} + +void *subhelp_query_ext_logging(csri_ext_id extname) +{ + if (strcmp(extname, CSRI_EXT_LOGGING)) + return NULL; + return &logext; +} + +void subhelp_log(enum csri_logging_severity severity, const char *msg, ...) +{ + va_list args; + va_start(args, msg); + subhelp_vlog(severity, msg, args); + va_end(args); +} + +void subhelp_vlog(enum csri_logging_severity severity, + const char *msg, va_list args) +{ + char *buffer; + const char *final; + size_t size = 256; + int n; + + buffer = (char *)malloc(256); + while (buffer) { + n = vsnprintf(buffer, size, msg, args); + if (n >= 0 && (unsigned)n < size) + break; + size = n > 0 ? (unsigned)n + 1 : size * 2; + buffer = (char *)realloc(buffer, size); + } + final = buffer ? buffer : ""; + subhelp_slog(severity, final); + if (buffer) + free(buffer); +} + +void subhelp_slog(enum csri_logging_severity severity, const char *msg) +{ + if (logfunc) + logfunc(appdata, severity, msg); + else { + fprintf(stderr, msg); + fprintf(stderr, "\n"); + } +} + diff --git a/csri/subhelp/posix/openfile.c b/csri/subhelp/posix/openfile.c new file mode 100644 index 000000000..1b2aad9aa --- /dev/null +++ b/csri/subhelp/posix/openfile.c @@ -0,0 +1,82 @@ +/***************************************************************************** + * asa: portable digital subtitle renderer + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "acconf.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#include +#ifdef HAVE_SYS_MMAN_H +#include +#endif +#include +#include +#include + +#include "subhelp.h" +#include + +csri_inst *subhelp_open_file(csri_rend *renderer, + csri_inst *(*memopenfunc)(csri_rend *renderer, const void *data, + size_t length, struct csri_openflag *flags), + const char *filename, struct csri_openflag *flags) +{ + csri_inst *rv = NULL; + void *data; + int fd; + struct stat st; + struct csri_openflag *err; + + fd = open(filename, O_RDONLY); + if (fd == -1) + goto out_err; + if (fstat(fd, &st) + || !(data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, + fd, 0))) { + close(fd); + goto out_err; + } + + rv = memopenfunc(renderer, data, st.st_size, flags); + + munmap(data, st.st_size); + close(fd); + return rv; + +out_err: + for (err = flags; err; err = err->next) + if (!strcmp(err->name, CSRI_EXT_OPENERR)) + break; + if (err) { + struct csri_openerr_flag *errd = (struct csri_openerr_flag *) + err->data.otherval; + errd->flags = CSRI_OPENERR_FILLED | CSRI_OPENERR_ERRNO; + errd->posixerrno = errno; + } + return NULL; +} + diff --git a/csri/subhelp/win32/openfile.c b/csri/subhelp/win32/openfile.c new file mode 100644 index 000000000..5e177d91c --- /dev/null +++ b/csri/subhelp/win32/openfile.c @@ -0,0 +1,84 @@ +/***************************************************************************** + * asa: portable digital subtitle renderer + ***************************************************************************** + * Copyright (C) 2007 David Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + +#include +#include + +#include "subhelp.h" +#include + +csri_inst *subhelp_open_file(csri_rend *renderer, + csri_inst *(*memopenfunc)(csri_rend *renderer, const void *data, + size_t length, struct csri_openflag *flags), + const char *filename, struct csri_openflag *flags) +{ + csri_inst *rv = NULL; + void *data; + struct csri_openflag *err; + HANDLE file, mapping; + DWORD size; + int namesize; + wchar_t *namebuf; + + namesize = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); + if (!namesize) + goto out_err; + namesize++; + namebuf = malloc(sizeof(wchar_t) * namesize); + MultiByteToWideChar(CP_UTF8, 0, filename, -1, namebuf, namesize); + + file = CreateFileW(namebuf, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, 0, NULL); + free(namebuf); + if (file == INVALID_HANDLE_VALUE) + goto out_err; + size = GetFileSize(file, NULL); + if (size == INVALID_FILE_SIZE || !size) + goto out_closefile; + mapping = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL); + if (!mapping) + goto out_closefile; + data = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, size); + if (!data) + goto out_closemap; + + rv = memopenfunc(renderer, data, size, flags); + + UnmapViewOfFile(data); + CloseHandle(mapping); + CloseHandle(file); + return rv; + +out_closemap: + CloseHandle(mapping); +out_closefile: + CloseHandle(file); +out_err: + for (err = flags; err; err = err->next) + if (!strcmp(err->name, CSRI_EXT_OPENERR)) + break; + if (err) { + struct csri_openerr_flag *errd = err->data.otherval; + errd->flags = CSRI_OPENERR_FILLED | CSRI_OPENERR_WINERR; + errd->winerr = GetLastError(); + } + return NULL; +} +