With the patch this time...

Allan N. Snider wrote:
> This patch adds the nextsub filter, (a replacement for extsub).  It
> allows better control over which subtitle indexes can be used, better
> positioning control, allows opacity, zooming, rgb colours, etc.  It's
> well documented (something extsub was in need of).  This is an old
> patch but I see it still didn't make it in, so I'm resubmitting it.
>
>     YES yes, I know.  I need to determine these subtitle indices from
> the VOB header and make them available to dvdrip.  Been on my todo
> list for two years now.  That, and the "forced" flag.  What can I say.
>                                                                      
>                Allan
>
>
diff -ruN old/filter/extsub/Makefile.am new/filter/extsub/Makefile.am
--- old/filter/extsub/Makefile.am       2007-03-11 22:25:51.000000000 -0400
+++ new/filter/extsub/Makefile.am       2007-03-11 22:24:26.000000000 -0400
@@ -7,12 +7,12 @@
 
 pkgdir = $(MOD_PATH)
 
-pkg_LTLIBRARIES = filter_extsub.la filter_extsub2.la
+pkg_LTLIBRARIES = filter_extsub.la filter_nextsub.la
 
 filter_extsub_la_SOURCES = filter_extsub.c subproc.c subtitle_buffer.c
 filter_extsub_la_LDFLAGS = -module -avoid-version
 
-filter_extsub2_la_SOURCES = filter_extsub.c subproc.c subtitle_buffer.c
-filter_extsub2_la_LDFLAGS = -module -avoid-version
+filter_nextsub_la_SOURCES = filter_nextsub.c subproc.c subtitle_buffer.c
+filter_nextsub_la_LDFLAGS = -module -avoid-version
 
-EXTRA_DIST = subproc.h subtitle_buffer.h
+EXTRA_DIST = subproc.h subtitle_buffer.h nextsub.h
diff -ruN old/filter/extsub/filter_nextsub.c new/filter/extsub/filter_nextsub.c
--- old/filter/extsub/filter_nextsub.c  1969-12-31 19:00:00.000000000 -0500
+++ new/filter/extsub/filter_nextsub.c  2007-03-11 22:43:35.000000000 -0400
@@ -0,0 +1,1272 @@
+/*
+ *  filter_nextsub.c
+ *
+ *  Copyright (C) Thomas Oestreich - January 2002
+ *  Copyright (C) Allan Snider     - March 2007
+ *
+ *  This file is part of transcode, a video stream processing tool
+ *
+ *  transcode 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, or (at your option)
+ *  any later version.
+ *
+ *  transcode 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 GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ *     v0.3.5 -> v0.4.0:       (Allan Snider)
+ *             - code style cleanup
+ *             - add opacity and rendering color for each index
+ *             - add zoom flag
+ *             - crop subtitle, (for consistent placement)
+ *             - modified positioning behaviour
+ *             - better (?) index guessing
+ */
+
+#define MOD_NAME       "filter_nextsub.so"
+#define MOD_VERSION    "0.4.0 (2007-03-01)"
+#define MOD_CAP                "DVD subtitle overlay plugin"
+#define MOD_AUTHOR     "Thomas Oestreich / Allan Snider"
+
+#include "nextsub.h"
+
+static MyFilterData *mfd;
+
+
+/*
+ *     help_opstr:
+ */
+
+static void
+help_optstr( void )
+{
+    tc_log_info( MOD_NAME, "(%s) help:\n\n"
+        "* Overview:\n"
+        "\n"
+        "      DVD subtitle overlay plugin.\n"
+        "          Original code by Thomas Destreich,\n"
+        "          Enhancements added by Allan Snider.\n"
+        "\n"
+        "      A subtitle bitmap contains 4 index values, background, text 
body,\n"
+        "      inner outline, and outer outline.  The ordering of the indices 
vary\n"
+        "      from DVD to DVD.  The background will always be transparent.  
One can\n"
+        "      specify opacity values (0-1) and rendering colors (hex rrggbb) 
for each\n"
+        "      index (1-3).  The bitmap can be anti-aliased, and zoomed (XxY). 
 The\n"
+        "      resulting bitmap is then cropped giving control of where the 
subtitle\n"
+        "      is located, regardless of where the original bitmap had placed 
it.\n"
+        "\n"
+        "      Occasionally, a subtitle component (eg. text body), will be 
composed\n"
+        "      of more than one index.  One could assign them the same opacity 
and\n"
+        "      color, however, each index is anti-aliased and zoomed 
individually\n"
+        "      which would produce poor results.  Therefore, the 'map' option 
was\n"
+        "      added, which allows one index value to be remapped to another.  
This\n"
+        "      provides a mechanism to join two index values into one 
component.\n"
+        "\n"
+        "      If no opacity arguments are given, the highest frequency 
(non-background)\n"
+        "      subtitle index is assumed to be the body of the text, and the 
next\n"
+        "      highest, the inner text outline.  Both indices are assigned an 
opacity\n"
+        "      of 1.0.  The text body is assigned the default color and the 
outline is\n"
+        "      assigned as black.  The index frequencies are calculated from 
the first\n"
+        "      subtitle rendered.\n"
+        "\n"
+        "      For positioning, the default (pos=0,topalign=0) places the 
bottom line of\n"
+        "      the subtitle at the bottom line of the video frame.  Positive 
values move\n"
+        "      the subtitle up by the given number of rows.  If topalign=1, 
then the\n"
+        "      top line of the subtitle is placed at the position given (and 
should be\n"
+        "      at least the height of the subtitle, else it will be 
clipped).\n"
+        "\n"
+        "      For boolean arguments, a value of TRUE is assumed if the option 
is\n"
+        "      present but no value is given.  For opacity arguments, a 
value\n"
+        "      of 1.0 is assumed if the option is given with no value.\n"
+        "\n"
+        "* Options:\n"
+        "\n"
+        "        'track'   Subtitle track (0-255) [0]\n"
+        "        'forced'  Render only forced subtitles (0=off, 1=on) [0]\n"
+        "        'pos'     Vertical position of subtitle (0-height) [0]\n"
+        "        'tshift'  Start time correction (msec) [0]\n"
+        "        'pre'     Run as a pre filter (0=no, 1=yes) [0]\n"
+        "        'aalias'  Anti-aliasing (0=off, 1=on) [0]\n"
+        "        'zoom'    Zoom subtitle image (X[xY]) [1x1]\n"
+        "        'o1'      Index 1 opacity (0-1) [0]\n"
+        "        'o2'      Index 2 opacity (0-1) [0]\n"
+        "        'o3'      Index 3 opacity (0-1) [0]\n"
+        "        'c1'      Index 1 rendering color (rr[ggbb]) [clr]\n"
+        "        'c2'      Index 2 rendering color (rr[ggbb]) [clr]\n"
+        "        'c3'      Index 3 rendering color (rr[ggbb]) [clr]\n"
+        "        'clr'     Default rendering color (rr[ggbb]) [ffffff]\n"
+        "        'map'     Remap an index value (ItoJ) [0to0]\n"
+        "\n"
+        "* Examples:\n"
+        "\n"
+        "      -J nextsub\n"
+        "\n"
+        "          Let the filter guess the indices, rendering text with the 
default\n"
+        "          color (white) and the outline as black.  Both are assigned 
an\n"
+        "          opacity of 1.0.  You should get something visible, either 
solid or\n"
+        "          outlined text, rendered white, and at the very bottom of 
the screen.\n"
+        "\n"
+        "      -J nextsub=clr=d0d000:zoom=0.8\n"
+        "\n"
+        "          Similar to the above, but use yellow as the default color 
and shrink\n"
+        "          the subtitle by 0.8 x 0.8.\n"
+        "\n"
+        "      -J 
nextsub=o1:o2=0.9:c1=d0d000:c2=00:pos=75:top:aalias:zoom=0.9x0.75\n"
+        "\n"
+        "          Specify explicit subtitle indices to use.  Index 1 assumes 
an\n"
+        "          opacity of 1.0, and index 2 is specified as 0.9.  Render 
index 1\n"
+        "          with yellow, and index 2 with black.  Top align the 
subtitle at\n"
+        "          row 75.  Anti-alias it, and zoom to 0.9 x 0.75.\n"
+        "\n"
+        "          The above zoom factors can be a good choice for some 16:9 
films\n"
+        "          when zooming a 720x480 frame.  The subtitle is overlayed in 
the post\n"
+        "          filter phase and sometimes we need a similar aspect ratio 
correction\n"
+        "          applied to the subtitle bitmap as well.  Alternatively, run 
it as\n"
+        "          a pre-filter.\n"
+        "\n"
+        "      -J nextsub=o2:map=3to2:forced\n"
+        "\n"
+        "          Join indices 2 and 3 together to render the text body as 
index 2.\n"
+        "          In addition, only render subtitles which have the forced 
flag set.\n"
+        "\n"
+        "      -J nextsub=o1:o2:o3:c1=ff0000:c2=00ff00:c3=0000ff\n"
+        "\n"
+        "          Render all indices with a different color.  Encoding just a 
small\n"
+        "          number of frames where a subtitle is present, this can be 
useful\n"
+        "          for determining what components of the subtitle each index 
has\n"
+        "          been assigned for a particular DVD.\n"
+        "\n"
+        , MOD_CAP );
+}
+
+
+/*
+ *     tc_filter:
+ *             Single function interface.
+ */
+
+int
+tc_filter( frame_list_t *ptr_, char *opt )
+{
+    vframe_list_t *ptr = (vframe_list_t*) ptr_;
+
+
+    //----------------------------------
+    //
+    // filter init
+    //
+    //----------------------------------
+
+    if ( ptr->tag & TC_FILTER_INIT ) {
+        int n, i;
+
+        mfd = tc_zalloc( sizeof(MyFilterData) );
+        if ( !mfd ) {
+            tc_log_perror( MOD_NAME, "out of memory" );
+            return( TC_EXPORT_ERROR );
+        }
+
+        mfd->vob = tc_get_vob();
+        if ( !mfd->vob )
+            return( -1 );
+
+        mfd->tcvhandle = tcv_init();
+        if ( !mfd->tcvhandle ) {
+            tc_log_error( MOD_NAME, "tcv initialization failed" );
+            return( TC_EXPORT_ERROR );
+        }
+
+        mfd->sub = tc_malloc( SIZE_RGB_FRAME );
+        mfd->tmp = tc_malloc( SIZE_RGB_FRAME );
+        if ( !mfd->sub || !mfd->tmp ) {
+            tc_log_perror( MOD_NAME, "out of memory" );
+            return( TC_EXPORT_ERROR );
+        }
+
+        /* split sub and tmp into individual planes */
+        n = TC_MAX_V_FRAME_WIDTH * TC_MAX_V_FRAME_HEIGHT;
+        for ( i=0; i<3; i++ ) {
+            mfd->sp[i] = mfd->sub + i*n;
+            mfd->dp[i] = mfd->tmp + i*n;
+        }
+
+        mfd->clr_def.r = 255;
+        mfd->clr_def.g = 255;
+        mfd->clr_def.b = 255;
+        for ( i=0; i<3; i++ ) {
+            mfd->clr[i].r = -1;
+            mfd->clr[i].g = -1;
+            mfd->clr[i].b = -1;
+        }
+
+        mfd->pts1 = -1;
+        mfd->pts2 = -1;
+
+        if ( opt != NULL ) {
+            char buf[100];
+            int n;
+
+            if ( verbose )
+                tc_log_info( MOD_NAME, "options=%s", opt );
+
+            /* booleans, default to TRUE if no value is given */
+            n = optstr_get( opt, "forced", "%d", &mfd->forced );
+            if ( !n )
+                mfd->forced = TC_TRUE;
+            n = optstr_get( opt, "top", "%d", &mfd->topalign );
+            if ( !n )
+                mfd->topalign = TC_TRUE;
+            n = optstr_get( opt, "pre", "%d", &mfd->prefilter );
+            if ( !n )
+                mfd->prefilter = TC_TRUE;
+            n = optstr_get( opt, "aalias", "%d", &mfd->antialias );
+            if ( !n )
+                mfd->antialias = TC_TRUE;
+
+            optstr_get( opt, "track", "%d", &mfd->vob->s_track );
+            optstr_get( opt, "pos", "%d", &mfd->pos );
+            optstr_get( opt, "tshift", "%d", &mfd->tshift );
+
+            if ( optstr_get(opt,"zoom","%[^:]",buf) >= 0 ) {
+                if ( get_zoom(buf,&mfd->z_x,&mfd->z_y) )
+                    mfd->do_zoom = 1;
+            }
+
+            /* default these to 1.0 if no value is given */
+            n = optstr_get( opt, "o1", "%lf", &mfd->fopc[0] );
+            if ( !n )
+                mfd->fopc[0] = 1.0;
+            n = optstr_get( opt, "o2", "%lf", &mfd->fopc[1] );
+            if ( !n )
+                mfd->fopc[1] = 1.0;
+            n = optstr_get( opt, "o3", "%lf", &mfd->fopc[2] );
+            if ( !n )
+                mfd->fopc[2] = 1.0;
+
+            if ( optstr_lookup(opt,"o1") ||
+                    optstr_lookup(opt,"o2") ||
+                    optstr_lookup(opt,"o3") )
+                mfd->have_indices = 1;
+
+            if ( optstr_get(opt,"c1","%[^:]",buf) > 0 )
+                mfd->clr[0] = get_rgb( buf );
+            if ( optstr_get(opt,"c2","%[^:]",buf) > 0 )
+                mfd->clr[1] = get_rgb( buf );
+            if ( optstr_get(opt,"c3","%[^:]",buf) > 0 )
+                mfd->clr[2] = get_rgb( buf );
+            if ( optstr_get(opt,"clr","%[^:]",buf) > 0 )
+                mfd->clr_def = get_rgb( buf );
+
+            optstr_get( opt, "map", "%dto%d", &mfd->remap[0], &mfd->remap[1] );
+
+            if ( optstr_lookup(opt,"help") )
+                help_optstr();
+        }
+
+        /* assign default for unset colors */
+        for ( i=0; i<3; i++ )
+            if ( mfd->clr[i].r == -1 )
+                mfd->clr[i] = mfd->clr_def;
+
+        /* convert opacities from 0-1 to 0-255 */
+        for ( i=0; i<3; i++ ) {
+            int o;
+
+            o = mfd->fopc[i] * 255;
+            if ( o < 0 )
+                o = 0;
+            if ( o > 255 )
+                o = 255;
+
+            mfd->opc[i] = o;
+        }
+
+        if ( verbose & TC_DEBUG ) {
+            tc_log_info( MOD_NAME, "Option values:" );
+            tc_log_info( MOD_NAME, "     forced: %d", mfd->forced );
+            tc_log_info( MOD_NAME, "     pos: %d", mfd->pos );
+            tc_log_info( MOD_NAME, "     top: %d", mfd->topalign );
+            tc_log_info( MOD_NAME, "     tshift: %d", mfd->tshift );
+            tc_log_info( MOD_NAME, "     pre: %d", mfd->prefilter );
+            tc_log_info( MOD_NAME, "     aalias: %d", mfd->antialias );
+            tc_log_info( MOD_NAME, "     zoom: %s", pr_zoom() );
+            for ( i=0; i<3; i++ )
+                tc_log_info( MOD_NAME, "     o%d: %g", i+1, mfd->fopc[i] );
+            for ( i=0; i<3; i++ )
+                tc_log_info( MOD_NAME, "     c%d: %s", i+1, 
pr_clr(mfd->clr[i]) );
+            tc_log_info( MOD_NAME, "     clr: %s", pr_clr(mfd->clr_def) );
+            tc_log_info( MOD_NAME, "     map: %d to %d", mfd->remap[0], 
mfd->remap[1] );
+        }
+
+        // start subtitle stream
+        mfd->import.flag = TC_SUBEX;
+        if ( tcv_import(TC_IMPORT_OPEN,&mfd->import,mfd->vob) < 0 )
+            tc_log_error( MOD_NAME, "popen subtitle stream" );
+
+        subproc_init( NULL, "title", 0, (unsigned short) mfd->vob->s_track );
+        sframe_alloc( SUBTITLE_BUFFER, mfd->import.fd );
+
+        // start thread
+        if ( pthread_create(&mfd->thread,NULL,(void*)subtitle_reader,NULL) != 
0 )
+            tc_log_error( MOD_NAME, "failed to start subtitle import thread" );
+
+        mfd->f_time = 1.0 / (mfd->prefilter ? mfd->vob->fps : 
mfd->vob->ex_fps);
+
+        if ( verbose )
+            tc_log_info( MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP );
+
+        return( 0 );
+    }
+
+    if ( ptr->tag & TC_FILTER_GET_CONFIG ) {
+        optstr_filter_desc( opt, MOD_NAME, MOD_CAP, MOD_VERSION, MOD_AUTHOR, 
"VRYOE", "1" );
+        optstr_param( opt, "track", "Subtitle track", "%d", "0", "0", "255" );
+        optstr_param( opt, "forced", "Render only forced subtitles", "%d", 
"0", "0", "1" );
+        optstr_param( opt, "pos", "Vertical position of subtitle", "%d", "0", 
"0", "height" );
+        optstr_param( opt, "tshift", "Start time correction (msec)", "%d", 
"0", "0", "-1" );
+        optstr_param( opt, "pre", "Run as a pre filter", "%d", "0", "0", "1" );
+        optstr_param( opt, "aalias", "Anti-aliasing", "%d", "1", "0", "1" );
+        optstr_param( opt, "zoom", "Zoom subtitle image (XxY)", "%s", "1x1" );
+        optstr_param( opt, "o1", "Index 1 opacity", "%f", "1", "0", "1" );
+        optstr_param( opt, "o2", "Index 2 opacity", "%f", "1", "0", "1" );
+        optstr_param( opt, "o3", "Index 3 opacity", "%f", "1", "0", "1" );
+        optstr_param( opt, "c1", "Index 1 rendering color", "%s", "ffffff" );
+        optstr_param( opt, "c2", "Index 2 rendering color", "%s", "ffffff" );
+        optstr_param( opt, "c3", "Index 3 rendering color", "%s", "ffffff" );
+        optstr_param( opt, "clr", "Default rendering color (hex rrggbb)", 
"%s", "ffffff" );
+        optstr_param( opt, "map", "Remap an index value (ItoJ)", "%dto%d", 
"0to0" );
+
+        return( 0 );
+    }
+
+
+    //----------------------------------
+    //
+    // filter close
+    //
+    //----------------------------------
+
+    if ( ptr->tag & TC_FILTER_CLOSE ) {
+        static int close_done;
+        void *status;
+
+        if ( !close_done ) {
+            pthread_cancel( mfd->thread );
+
+#ifdef BROKEN_PTHREADS
+            pthread_cond_signal( &sframe_list_full_cv );
+#endif
+
+            pthread_join( mfd->thread, &status );
+
+            mfd->import.flag = TC_SUBEX;
+            if ( mfd->import.fd != NULL )
+                pclose( mfd->import.fd );
+
+            tcv_free( mfd->tcvhandle );
+            free( mfd->sub );
+            free( mfd->tmp );
+
+            free( mfd );
+
+            close_done = TC_TRUE;
+        }
+
+        return( 0 );
+    }
+
+    //----------------------------------
+    //
+    // filter frame routine
+    //
+    //----------------------------------
+
+    if ( ptr->tag & TC_VIDEO ) {
+        double f_pts;
+
+        if ( mfd->prefilter ) {
+            if ( !(ptr->tag & TC_PRE_S_PROCESS) )
+                return( 0 );
+        } else {
+            if ( !(ptr->tag & TC_POST_S_PROCESS) )
+                return( 0 );
+        }
+
+        if ( verbose & TC_STATS )
+            tc_log_info( MOD_NAME, "%s/%s %s %s",
+                         mfd->vob->mod_path, MOD_NAME, MOD_VERSION, MOD_CAP );
+
+
+        
//-------------------------------------------------------------------------
+        //
+        // below is a very fuzzy concept of rendering the subtitle on the movie
+        //
+        // (1) check if we have a valid subtitle and render it
+        // (2) if (1) fails try to get a new one
+        // (3) buffer and display the new one if it's showtime, if not return
+        //
+        // repeat steps throughout the movie
+
+        // calculate current frame video PTS in [s]
+        // adjust for dropped frames so far
+        // adjust for user shift (in milliseconds)
+
+        f_pts = mfd->f_time * 
(ptr->id-tc_get_frames_dropped()+mfd->vob->psu_offset)
+                + mfd->tshift / 1000.0;
+
+        if ( verbose & TC_DEBUG )
+            tc_log_info( MOD_NAME, "frame=%06d pts=%.3f sub1=%.3f sub2=%.3f",
+                         ptr->id, f_pts, mfd->pts1, mfd->pts2 );
+
+        /* TODO: weird logic here, but going to leave it for now */
+
+        // overlay now?
+        if ( mfd->pts1<=f_pts && f_pts<=mfd->pts2 ) {
+            if ( !mfd->forced || mfd->fflg )
+                subtitle_overlay( ptr->video_buf, ptr->v_width, ptr->v_height 
);
+            return( 0 );
+        }
+
+        // get a new subtitle, if the last one has expired:
+        if ( f_pts > mfd->pts2 ) {
+            mfd->made_subtitle = 0;
+            if ( subtitle_retrieve() < 0 ) {
+                if ( verbose & TC_DEBUG )
+                    tc_log_info( MOD_NAME, "no subtitle available at this 
time" );
+                return( 0 );
+            }
+        }
+
+        // overlay now?
+        if ( mfd->pts1<f_pts && f_pts<mfd->pts2 )
+            if ( !mfd->forced || mfd->fflg )
+                subtitle_overlay( ptr->video_buf, ptr->v_width, ptr->v_height 
);
+    }
+
+    return( 0 );
+}
+
+
+/*
+ *     get_zoom:
+ *             Given a string of the form XxY, where X and Y are floating point
+ *     numbers, return (via parameter) the two scaling values supplied as 
doubles.
+ *     If only a single value is given, Y assumes the value of X.
+ */
+
+static int
+get_zoom( char *buf, double *px, double *py )
+{
+    double x, y;
+    char *p;
+
+    x = atof( buf );
+    if ( x <= 0 )
+        return( TC_FALSE );
+
+    p = strchr( buf, 'x' );
+    if ( p ) {
+        y = atof( p + 1 );
+        if ( y <= 0 )
+            return( TC_FALSE );
+    } else
+        y = x;
+
+    *px = x;
+    *py = y;
+
+    return( TC_TRUE );
+}
+
+
+/*
+ *     get_rgb:
+ *             Given a string of the form (hex) "rrggbb", return an Rgb 
structure
+ *     containing the (integer) tuplet [r,g,b].  If only an "rr" component is
+ *     given, gg, and bb, assume the value of rr.  For example, "ff" would be
+ *     interpreted as white (ffffff).  Return the structure by function value.
+ */
+
+static Rgb
+get_rgb( char *buf )
+{
+    Rgb c;
+    int v;
+
+    if ( strlen(buf) < 6 ) {
+        if ( strlen(buf) < 2 )
+            v = 255;
+        else
+            v = htoi( buf );
+        c.r = v;
+        c.g = v;
+        c.b = v;
+
+        return( c );
+    }
+
+    c.r = htoi( buf+0 );
+    c.g = htoi( buf+2 );
+    c.b = htoi( buf+4 );
+
+    return( c );
+}
+
+
+/*
+ *     htoi:
+ *             Convert a 2 digit hex number to integer, and return via function
+ *     value.
+ */
+
+static int
+htoi( char *buf )
+{
+    return( (htob(buf[0]) << 4) + htob(buf[1]) );
+}
+
+
+/*
+ *     htob:
+ *             Convert a hex digit to integer, and return via function value.
+ */
+
+static int
+htob( char c )
+{
+    c = tolower( c );
+    if ( c>='a' && c<='f' )
+        return( c-'a'+10 );
+    return( c-'0' );
+}
+
+
+/*
+ *     pr_zoom:
+ *             Return the zoom x, and y scaling values as a (static) string of
+ *     the form "XxY", via function value.
+ */
+
+static char *
+pr_zoom( void )
+{
+    static char buf[100];
+
+    if ( mfd->do_zoom )
+        tc_snprintf( buf, 100, "%gx%g", mfd->z_x, mfd->z_y );
+    else
+        strcpy( buf, "none" );
+
+    return( buf );
+}
+
+
+/*
+ *     pr_clr:
+ *             Return the given Rgb structure as a string of the form (hex) 
"rrggbb".
+ */
+
+static char *
+pr_clr( Rgb c )
+{
+    static char buf[100];
+
+    tc_snprintf( buf, 100, "%02x%02x%02x", c.r, c.g, c.b );
+    return( buf );
+}
+
+
+/*
+ *     subtitle_retrieve:
+ *             Retrieve the next subtitle from the subtitle reader thread.
+ *     Store the results in mfd:
+ *
+ *             sub     - subtitle bitmap
+ *             id      - frame number
+ *             pts1    - start time
+ *             pts2    - end time
+ *             fflg    - forced flag
+ *             xp      - suggested x position
+ *             xp      - suggested y position
+ *             sw      - subtitle width
+ *             sh      - subtitle height
+ *
+ *     The function returns 0 if successful, -1 otherwise.
+ */
+
+static int
+subtitle_retrieve( void )
+{
+    sframe_list_t *sptr = NULL;
+    sub_info_t sub;
+
+    // get a subtitle from buffer
+    pthread_mutex_lock( &sframe_list_lock );
+    if ( sframe_fill_level(TC_BUFFER_EMPTY) ) {
+        pthread_mutex_unlock( &sframe_list_lock );
+        return( -1 );
+    }
+
+    // not being empty does not mean ready to go!!!!!
+    if ( sframe_fill_level(TC_BUFFER_READY) ) {
+        pthread_mutex_unlock( &sframe_list_lock );
+        if ( (sptr=sframe_retrieve()) == NULL ) {
+            // this shouldn't happen
+            tc_log_error( MOD_NAME, "internal error (S)" );
+            return( -1 );
+        }
+    } else {
+        pthread_mutex_unlock( &sframe_list_lock );
+        return( -1 );
+    }
+
+    // conversion
+    sub.frame = mfd->sub;
+    if ( 
subproc_feedme(sptr->video_buf,sptr->video_size,sptr->id,sptr->pts,&sub) < 0 ) {
+        // problems, drop this subtitle
+        if ( verbose & TC_DEBUG )
+            tc_log_warn( MOD_NAME, "subtitle dropped" );
+        sframe_remove( sptr );
+        pthread_cond_signal( &sframe_list_full_cv );
+        return( -1 );
+    }
+
+    // save data
+    mfd->id = sptr->id;
+    mfd->pts1 = sptr->pts * mfd->f_time;
+    mfd->pts2 = mfd->pts1 + sub.time / 100.0;
+
+    mfd->fflg = sub.forced;
+    mfd->orig.x = sub.x;
+    mfd->orig.y = sub.y;
+    mfd->sz.x = sub.w;
+    mfd->sz.y = sub.h;
+
+    // release packet buffer
+    sframe_remove( sptr );
+    pthread_cond_signal( &sframe_list_full_cv );
+
+    if ( verbose & TC_DEBUG )
+        tc_log_info( MOD_NAME, "got SUBTITLE %d with forced=%d, pts=%.3f 
dtime=%.3f",
+                     mfd->id, mfd->fflg, mfd->pts1, mfd->pts2-mfd->pts1 );
+
+    return( 0 );
+}
+
+
+/*
+ *     subtitle_overlay:
+ *             The current subtitle is within the time window of the current
+ *     video frame.  Overlay it into the image.  If this is the first time
+ *     the subtitle is being rendered, anti-alias it, zoom it, and crop it.
+ *     If this is the very first subtitle rendered, and no opacity arguments
+ *     were given, then guess appropriate indices to use.  Finally, determine
+ *     the subtitle position and overlay the resulting bitmap by blending pixel
+ *     values according to opacity.
+ */
+
+static void
+subtitle_overlay( uint8_t *vid, int w, int h )
+{
+    if ( verbose & TC_DEBUG )
+        tc_log_info( MOD_NAME, "SUBTITLE id=%d, x=%d, y=%d, w=%d, h=%d, t=%f",
+                     mfd->id, mfd->orig.x, mfd->orig.y, mfd->sz.x, mfd->sz.y,
+                     mfd->pts2 - mfd->pts1 );
+
+    if ( !mfd->have_indices ) {
+        /* this is only ever done once, for the first subtitle rendered */
+        mfd->have_indices = TC_TRUE;
+        guess_indices();
+    }
+
+    /* anti-alias, zoom, and crop the subtitle */
+    if ( !mfd->made_subtitle ) {
+        /* this is done once for each subtitle rendered */
+        mfd->made_subtitle = TC_TRUE;
+        make_subtitle( w, h );
+    }
+
+    if ( mfd->vob->im_v_codec == CODEC_RGB )
+        subtitle_overlay_rgb( vid, w, h );
+
+    if ( mfd->vob->im_v_codec == CODEC_YUV )
+        subtitle_overlay_yuv( vid, w, h );
+}
+
+
+/*
+ *     guess_indices:
+ *             This is called if no opacity arguments were given.  The highest
+ *     frequency (non-background) subtitle index is assumed to be the body of
+ *     the text, and the next highest the inner text outline.  Both indices
+ *     are assigned an opacity of 1.0.  The text body is assigned the default
+ *     color, and the outline is assigned as black.
+ */
+
+static void
+guess_indices( void )
+{
+    int subi[4];
+    int ib, io;
+    int n, i;
+
+    for ( i=0; i<4; i++ )
+        subi[i] = 0;
+
+    n = mfd->sz.x * mfd->sz.y;
+    for ( i=0; i<n; i++ )
+        subi[ get_sub_index(i) ]++;
+
+    ib = 1;
+    io = 2;
+    if ( subi[1]>subi[2] && subi[1]>subi[3] ) {
+        ib = 1;
+        io = (subi[2]>subi[3]) ? 2 : 3;
+    }
+    if ( subi[2]>subi[1] && subi[2]>subi[3] ) {
+        ib = 2;
+        io = (subi[1]>subi[3]) ? 1 : 3;
+    }
+    if ( subi[3]>subi[1] && subi[3]>subi[2] ) {
+        ib = 3;
+        io = (subi[1]>subi[2]) ? 1 : 2;
+    }
+
+    --ib;
+    --io;
+
+    mfd->opc[ib] = 255;
+    mfd->opc[io] = 255;
+
+    mfd->clr[io].r = 0;
+    mfd->clr[io].g = 0;
+    mfd->clr[io].b = 0;
+
+    if ( verbose & TC_INFO )
+        tc_log_info( MOD_NAME, "index counts: 0=%d, 1=%d, 2=%d, 3=%d, body=%d, 
outline=%d",
+                     subi[0], subi[1], subi[2], subi[3],
+                     ib+1, io+1 );
+}
+
+
+/*
+ *     get_sub_index:
+ *             Return, via function value,  the subtitle index value at 
element i.
+ *     Check for a remapped index, and clamp the result to [0,3].
+ */
+
+static int
+get_sub_index( int i )
+{
+    int c;
+
+    c = mfd->sub[i];
+    if ( c == mfd->remap[0] )
+        c = mfd->remap[1];
+
+    if ( c < 0 )
+        c = 0;
+    if ( c > 3 )
+        c = 3;
+
+    return( c );
+}
+
+
+/*
+ *     make_subtitle:
+ *             We are given a single NxM plane of indices whose values can be 
0 to 3.
+ *     A value of 0 is always considered background and transparent.  The 
values 1, 2,
+ *     and 3 correspond to the text body, inner outline, and outer outline, 
(non resp).
+ *     First, the indices are replaced by their corresponding opacity values, 
with each
+ *     index written to a separate plane.  These planes are then anti-aliased, 
and
+ *     zoomed, Then, a minimum bounding box is computed over all planes (UxV), 
and the
+ *     subtitle frame is replaced with the UxV frame (three planes).  We are 
now capable
+ *     of rendering the subtitle using different opacity values and rendering 
colors for
+ *     each index.  Since the frame has been cropped, we also control where 
the subtitle
+ *     is located, regardless of where the original bitmap had placed it.
+ */
+
+static void
+make_subtitle( int w, int h )
+{
+    uint8_t **sp, **dp;
+    Dim sz, p;
+    int n, c, i;
+
+    sp = mfd->sp;
+    dp = mfd->dp;
+
+    /* plane 0 is current subtitle */
+    /* zero planes 1 and 2 */
+    n = mfd->sz.x * mfd->sz.y;
+    memset( sp[1], 0, n );
+    memset( sp[2], 0, n );
+
+    /* replace index values with opacity values */
+    /* store opacities for each index in separate planes */
+    for ( i=0; i<n; i++ ) {
+        c = get_sub_index( i );
+        if ( c ) {
+            --c;
+            sp[c][i] = mfd->opc[c];
+        }
+    }
+
+    /* if anti-aliasing or zooming, crop to reduce work */
+    if ( mfd->antialias || mfd->do_zoom ) {
+        /* crop, with margin 6 */
+        crop_subtitle( sp, dp, mfd->sz, &sz, 6 );
+        cpy3( sp, dp, sz );
+        mfd->sz = sz;
+    }
+
+    if ( mfd->antialias ) {
+        /* anti-alias each plane (individually) */
+        for ( i=0; i<3; i++ )
+            tcv_antialias( mfd->tcvhandle, sp[i], dp[i], mfd->sz.x, mfd->sz.y, 
1,
+                           mfd->vob->aa_weight, mfd->vob->aa_bias );
+        cpy3( sp, dp, sz );
+    }
+
+    if ( mfd->do_zoom ) {
+        /* compute zoomed dimensions */
+        sz.x = mfd->sz.x * mfd->z_x + 0.5;
+        sz.y = mfd->sz.y * mfd->z_y + 0.5;
+
+        /* odd dimensions is probably not a good thing */
+        if ( sz.x % 2 )
+            sz.x++;
+        if ( sz.y % 2 )
+            sz.y++;
+
+        /* do not allow the zoom to exceed the dimensions of the video frame */
+        if ( sz.x > w )
+            sz.x = w;
+        if ( sz.y > h )
+            sz.y = h;
+
+        /* zoom each plane (individually) */
+        for ( i=0; i<3; i++ )
+            tcv_zoom( mfd->tcvhandle, sp[i], dp[i], mfd->sz.x, mfd->sz.y,
+                      1, sz.x, sz.y, mfd->vob->zoom_filter );
+
+        cpy3( sp, dp, sz );
+        mfd->sz = sz;
+    }
+
+    /* crop, with margin 0 */
+    crop_subtitle( sp, dp, mfd->sz, &sz, 0 );
+    cpy3( sp, dp, sz );
+    mfd->sz = sz;
+
+    /* find position of overlay region within frame */
+    /*     - zero corresponds to the bottom line of the frame */
+    /*     - if topalign is true, position the top line at pos */
+    /*       (pos should be at least the height of the subtitle) */
+    /*     - if topalign is false, position the bottom line at pos */
+    p.x = (w - mfd->sz.x) / 2;
+    p.y = h - mfd->pos;
+    if ( !mfd->topalign )
+        p.y -= mfd->sz.y;
+
+    /* make the region even aligned */
+    p.x -= p.x % 2;
+    p.y -= p.y % 2;
+
+    /* top clip */
+    mfd->ys = 0;
+    mfd->yn = mfd->sz.y;
+    if ( p.y < 0 ) {
+        mfd->ys = -p.y;
+        mfd->yn += p.y;
+        p.y = 0;
+    }
+
+    /* bottom clip */
+    n = p.y + mfd->sz.y - h;
+    if ( n > 0 )
+        mfd->yn -= n;
+
+    if ( mfd->yn <= 0 )
+        tc_log_warn( MOD_NAME, "Subtitle is complety clipped.  Check the 'pos' 
option." );
+
+    mfd->orig.x = p.x;
+    mfd->orig.y = p.y;
+}
+
+
+/*
+ *     crop_subtitle:
+ *             Find the minimum bounding box over the three opacity planes
+ *     and copy that region into the dst buffer, (effectively cropping
+ *     the image).  Force both width and height to be a even dimension.
+ *     Return the dimensions of the cropped image via parameter.
+ */
+
+static void
+crop_subtitle( uint8_t **sp, uint8_t **dp, Dim sd, Dim *dd, int margin )
+{
+    Dim ll, ur;
+    int x, y, n, m;
+    int flg, v, i;
+
+    ll.x = 0;
+    ll.y = 0;
+    ur.x = 0;
+    ur.y = 0;
+
+    flg = TC_FALSE;
+    n = 0;
+    for ( y=0; y<sd.y; y++ )
+        for ( x=0; x<sd.x; x++ ) {
+            v = sum3( sp, n );
+            n++;
+
+            if ( !v )
+                continue;
+
+            if ( !flg ) {
+                ll.x = x;
+                ur.x = x;
+                ll.y = y;
+                ur.y = y;
+
+                flg = TC_TRUE;
+                continue;
+            }
+
+            if ( x < ll.x )
+                ll.x = x;
+            if ( x > ur.x )
+                ur.x = x;
+            if ( y < ll.y )
+                ll.y = y;
+            if ( y > ur.y )
+                ur.y = y;
+        }
+
+    /* add margin */
+    ll.x -= margin;
+    ll.y -= margin;
+    ur.x += margin;
+    ur.y += margin;
+
+    /* force even dimensions */
+    dd->x = ur.x - ll.x + 1;
+    dd->y = ur.y - ll.y + 1;
+    if ( dd->x % 2 ) {
+        ur.x++;
+        dd->x++;
+    }
+
+    if ( dd->y % 2 ) {
+        ur.y++;
+        dd->y++;
+    }
+
+    /* extract bounded region to tmp, cropping, possibly padding */
+    m = 0;
+    for ( y=ll.y; y<=ur.y; y++ )
+        for ( x=ll.x; x<=ur.x; x++, m++ ) {
+            if ( x<0 || x>=sd.x || y<0 || y>=sd.y )
+                n = -1;
+            else
+                n = y*sd.x + x;
+
+            for ( i=0; i<3; i++ )
+                dp[i][m] = (n<0) ? 0 : sp[i][n];
+        }
+}
+
+
+/*
+ *     sum3:
+ *             Return the sum of the three opacity planes for element n.
+ */
+
+static int
+sum3( uint8_t **sp, int n )
+{
+    int s, i;
+
+    s = 0;
+    for ( i=0; i<3; i++ )
+        s += sp[i][n];
+
+    return( s );
+}
+
+
+/*
+ *     max3:
+ *             Return the maximum opacity value for element n, over the three
+ *     opacity planes.
+ */
+
+static int
+max3( uint8_t **sp, int n )
+{
+    int v, i;
+
+    v = 0;
+    for ( i=0; i<3; i++ )
+        if ( sp[i][n] > v )
+            v = sp[i][n];
+
+    return( v );
+}
+
+
+/*
+ *     avg3:
+ *             Return the weighted average for the RGB rendering colors at 
element n,
+ *     as determined by the contributing opacity values on each plane.
+ */
+
+static Rgb
+avg3( uint8_t **sp, int n )
+{
+    Rgb clr;
+    double s[3];
+    int w, i;
+
+    w = sum3( sp, n );
+    for ( i=0; i<3; i++ )
+        s[i] = sp[i][n] / (double) w;
+
+    /* I'm thinking Rgb would have been better as an array */
+    clr.r = 0;
+    clr.g = 0;
+    clr.b = 0;
+    for ( i=0; i<3; i++ ) {
+        clr.r += mfd->clr[i].r * s[i] + 0.5;
+        clr.g += mfd->clr[i].g * s[i] + 0.5;
+        clr.b += mfd->clr[i].b * s[i] + 0.5;
+    }
+
+    if ( clr.r > 255 )
+        clr.r = 255;
+    if ( clr.g > 255 )
+        clr.g = 255;
+    if ( clr.b > 255 )
+        clr.b = 255;
+
+    return( clr );
+}
+
+
+/*
+ *     cpy3:
+ *             Copy src to dst, for each of the three planes.
+ */
+
+static void
+cpy3( uint8_t **sp, uint8_t **dp, Dim sz )
+{
+    int n, i;
+
+    n = sz.x * sz.y;
+    for ( i=0; i<3; i++ )
+        ac_memcpy( sp[i], dp[i], n );
+}
+
+
+/*
+ *     subtitle_overlay_rgb:
+ *             Overlay the subtitle bitmap into the given video frame.  For 
each pixel,
+ *     the opacity used is the maximum value over all index planes.  The color 
used is
+ *     a weighted average as determined by the contributing opacity values on 
each
+ *     plane.  The resulting color is then blended with the pixel, ie:
+ *
+ *             (1-opacity) * pixel_color + opacity * subtitle_color
+ */
+
+static void
+subtitle_overlay_rgb( uint8_t *vid, int w, int h )
+{
+    uint8_t *v;
+    Rgb clr;
+    double s;
+    int x, y, n, m;
+    int opc, yo;
+
+    for ( y=0; y<mfd->yn; y++ ) {
+        yo = mfd->orig.y + y;
+        m = (yo*w + mfd->orig.x) * 3;
+
+        n = (mfd->ys + y) * mfd->sz.x;
+        for ( x=0; x<mfd->sz.x; x++ ) {
+            opc = max3( mfd->sp, n );
+            if ( opc ) {
+                clr = avg3( mfd->sp, n );
+                s = opc / 255.0;
+                if ( s > 1.0 )
+                    s = 1.0;
+
+                v = vid + m;
+                blend_pixel( v+0, s, clr.r );
+                blend_pixel( v+1, s, clr.g );
+                blend_pixel( v+2, s, clr.b );
+            }
+
+            m += 3;
+            n++;
+        }
+    }
+}
+
+
+/*
+ *     subtitle_overlay_yuv:
+ *             Trying to blend in pixels in a non-linear color space just 
doesn't
+ *     produce aesthetically pleasing results.  Many variations were attempted.
+ *     At edges of the text, even a slightly opaque pixel would cause the UV 
component
+ *     for the entire 4x4 pixel cell to be modified, which generated very 
visable
+ *     artifacts.  Therefore, although quite expensive, the overlay region of 
the
+ *     video frame is extracted to a temporary buffer, converted to rgb, 
overlayed
+ *     with the subtitle, converted back to yuv, and re-inserted back into the
+ *     video frame.  The results are better, but still not as good as just 
running
+ *     with -V rgb24.  When the sub region is converted back to yuv, you still 
get
+ *     some color blurring around edges of sharp contrast.  You really have to 
be
+ *     looking for it to notice though.
+ */
+
+static void
+subtitle_overlay_yuv( uint8_t *vid, int w, int h )
+{
+    uint8_t *sp, *dp, *v;
+    Rgb clr;
+    double s;
+    int sw, sh, sn, sy, su, sv;
+    int dw, dh, dn, dy, du, dv;
+    int x, y, r;
+    int opc, n, m;
+
+    if ( mfd->yn <= 0 )
+        /* completely clipped */
+        return;
+
+    /* src, video frame */
+    sp = vid;
+    sw = w;
+    sh = h;
+    sn = sw * sh;
+
+    sy = mfd->orig.y * sw + mfd->orig.x;
+    su = sn + mfd->orig.y/2 * sw/2 + mfd->orig.x/2;
+    sv = su + sn/4;
+
+    /* dst, extracted region */
+    dp = mfd->tmp;
+    dw = mfd->sz.x;
+    dh = mfd->yn;
+    dn = dw * dh;
+
+    dy = 0;
+    du = dn;
+    dv = du + dn/4;
+
+    /* extract subtitle region */
+    for ( y=0; y<dh; y++, sy+=sw, dy+=dw ) {
+        ac_memcpy( dp+dy, sp+sy, dw );
+
+        if ( !(y % 2) ) {
+            ac_memcpy( dp+du, sp+su, dw/2 );
+            ac_memcpy( dp+dv, sp+sv, dw/2 );
+
+            su += sw/2;
+            sv += sw/2;
+
+            du += dw/2;
+            dv += dw/2;
+        }
+    }
+
+    /* convert to rgb */
+    r = tcv_convert( mfd->tcvhandle, dp, dp, dw, dh,
+                     IMG_YUV_DEFAULT, IMG_RGB_DEFAULT );
+    if ( !r ) {
+        tc_log_error( MOD_NAME, "overlay, yuv to rgb conversion failed" );
+        return;
+    }
+
+    /* overlay subtitle */
+    m = 0;
+    n = mfd->ys * dw;
+    for ( y=0; y<dh; y++ )
+        for ( x=0; x<dw; x++, m+=3, n++ ) {
+            opc = max3( mfd->sp, n );
+            if ( opc ) {
+                clr = avg3( mfd->sp, n );
+                s = opc / 255.0;
+                if ( s > 1.0 )
+                    s = 1.0;
+
+                v = dp + m;
+                blend_pixel( v+0, s, clr.r );
+                blend_pixel( v+1, s, clr.g );
+                blend_pixel( v+2, s, clr.b );
+            }
+        }
+
+    /* convert back to yuv */
+    r = tcv_convert( mfd->tcvhandle, dp, dp, dw, dh,
+                     IMG_RGB_DEFAULT, IMG_YUV_DEFAULT );
+    if ( !r ) {
+        tc_log_error( MOD_NAME, "overlay, rgb to yuv conversion failed" );
+        return;
+    }
+
+    /* re-insert extracted region */
+    sy -= dh * sw;
+    su -= dh * sw / 4;
+    sv -= dh * sw / 4;
+
+    dy -= dh * dw;
+    du -= dh * dw / 4;
+    dv -= dh * dw / 4;
+
+    for ( y=0; y<dh; y++, sy+=sw, dy+=dw ) {
+        ac_memcpy( sp+sy, dp+dy, dw );
+
+        if ( !(y % 2) ) {
+            ac_memcpy( sp+su, dp+du, dw/2 );
+            ac_memcpy( sp+sv, dp+dv, dw/2 );
+
+            su += sw/2;
+            sv += sw/2;
+
+            du += dw/2;
+            dv += dw/2;
+        }
+    }
+}
+
+
+/*
+ *     blend_pixel:
+ *             Blend in a subtitle color component against the current pixel 
color
+ *     component, according to the subtitle opacity value.
+ */
+
+static void
+blend_pixel( uint8_t *v, double s, int color )
+{
+    int t;
+
+    t = (1-s)*(*v) + s*color + 0.5;
+    if ( t > 255 )
+        t = 255;
+
+    *v = t;
+}
diff -ruN old/filter/extsub/nextsub.h new/filter/extsub/nextsub.h
--- old/filter/extsub/nextsub.h 1969-12-31 19:00:00.000000000 -0500
+++ new/filter/extsub/nextsub.h 2007-03-11 22:24:26.000000000 -0400
@@ -0,0 +1,105 @@
+#include "transcode.h"
+#include "encoder.h"
+#include "filter.h"
+#include "libtc/optstr.h"
+#include "libtcvideo/tcvideo.h"
+
+#include <stdint.h>
+#include <ctype.h>
+
+#include "dl_loader.h"
+#include "import/magic.h"
+
+#include "subtitle_buffer.h"
+#include "subproc.h"
+
+#define BUFFER_SIZE SIZE_RGB_FRAME
+#define SUBTITLE_BUFFER 100
+
+
+typedef struct rgb_t Rgb;
+struct rgb_t
+    {
+    int        r;
+    int        g;
+    int        b;
+    };
+
+typedef struct dim_t Dim;
+struct dim_t
+       {
+       int x;
+       int y;
+       };
+
+typedef struct MyFilterData_t MyFilterData;
+struct MyFilterData_t
+    {
+    /* filter options */
+    int        forced;                 /* render only forced subtitles */
+    int        pos;                    /* position, vertical offset */
+    int        topalign;               /* align via top (not bottom) row of 
the subtitle */
+    int        tshift;                 /* time shift */
+    int        prefilter;              /* run as a pre-filter */
+    int        antialias;              /* anti-alias */
+    int        do_zoom;                /* zoom the subtitle */
+    double z_x, z_y;           /* zoom factors */
+    double fopc[3];            /* index opacities [0-1] */
+    Rgb        clr[3];                 /* index rendering colors */
+    Rgb        clr_def;                /* default rendering color */
+    int remap[2];              /* remap an index value to a different index */
+
+    /* private data */
+    transfer_t import;
+    pthread_t thread;
+    double f_time;
+    double pts1;
+    double pts2;
+
+    uint8_t *sub;              /* subtitle frame */
+    uint8_t *tmp;              /* temp buffer */
+
+    uint8_t *sp[3];            /* sub, decomposed to three planes */
+    uint8_t *dp[3];            /* tmp, decomposed to three planes */
+
+    Dim orig;                  /* origin of clipped overlay region in video */
+    Dim sz;                    /* dimension of subtitle */
+    int ys;                    /* starting on-screen row of subtitle */
+    int yn;                    /* number of on-screen subtitle rows */
+
+    int id;                    /* frame number */
+    int fflg;                  /* subtitle has forced flag set */
+    int opc[3];                        /* opacities [0-255] */
+
+    int have_indices;          /* indices to render are known */
+    int made_subtitle;         /* subtitle has been prepared (zoomed, etc) */
+
+    vob_t *vob;
+    TCVHandle tcvhandle;
+    };
+
+
+/*
+ *     prototypes:
+ */
+
+static int get_zoom( char*, double*, double* );
+static Rgb get_rgb( char* );
+static int htoi( char* );
+static int htob( char );
+static char *pr_zoom( void );
+static char *pr_clr( Rgb );
+static int subtitle_retrieve( void );
+static void subtitle_overlay( uint8_t*, int, int );
+static void guess_indices( void );
+static int get_sub_index( int );
+static void make_subtitle( int, int );
+static void crop_subtitle( uint8_t**, uint8_t**, Dim, Dim*, int );
+static int sum3( uint8_t**, int );
+static int max3( uint8_t**, int );
+static Rgb avg3( uint8_t**, int );
+static void cpy3( uint8_t**, uint8_t**, Dim );
+static void subtitle_overlay_rgb( uint8_t*, int, int );
+static void subtitle_overlay_yuv( uint8_t*, int, int );
+static void blend_pixel( uint8_t*, double, int );
+
diff -ruN old/filter/extsub/subproc.c new/filter/extsub/subproc.c
--- old/filter/extsub/subproc.c 2007-03-11 22:25:51.000000000 -0400
+++ new/filter/extsub/subproc.c 2007-03-11 22:24:26.000000000 -0400
@@ -178,7 +178,7 @@
   //char filename[16];
 
   //buffer for plugin
-  unsigned char *picture;
+  uint8_t *picture;
 
   picture = config.sub.frame;
 
diff -ruN old/filter/extsub/subproc.h new/filter/extsub/subproc.h
--- old/filter/extsub/subproc.h 2007-03-11 22:25:51.000000000 -0400
+++ new/filter/extsub/subproc.h 2007-03-11 22:24:26.000000000 -0400
@@ -39,7 +39,7 @@
   int x, y;
   int w, h;
 
-  char *frame;
+  uint8_t *frame;
 
   int colour[4];
   int alpha[4];

Reply via email to