Hi all,
I'm back on work on transcode, slowly as usual because the spare time
always too low. Stuck on my ~FIFO[1] TODO, I've recently merged yait
code.
I've also performed a little deeper code review on filter_yait.c, and I
like ot submit it here (especially to Allan's attention) before to merge
it on CVS. Please check out attached patch[0], and point out my
mistakes.
There is a few comments and some tiny code logic changes (nothing
serious of course, just initialization reorder, some code factorization
and constantization and stuff like that). Please let me know if I broke
something. I can't test it new version now or soon due to lack of NTSC
material and, most important, time.
A review and STYLE-ification of tcyait will be follow into near future.
A word about of STYLE. I know that can be annoying (everyone has it's
own style and everyone wants to keep it), but I strongly believe that
having a consistent codebase helps significantly in keeping codebase
safe and fun to hack in. So submitted code -like yait- will be
STYLE-ificated[2].
I've already seen too much horrors in the past, even (especially?) into
our own codebase :)
Thoughts?
+++
[0] yeah, it's big. That's mainly due to tab removal.
[1] Please bug me if a task starts to starve (i.e., recent configure
patches to Os X, that was my bad)
[2] Resistance is futile, you will be STYLE-ificated :)
Bests,
--
Francesco Romani
Index: filter/filter_yait.c
===================================================================
RCS file: /cvstc/transcode/filter/filter_yait.c,v
retrieving revision 1.1
diff -u -r1.1 filter_yait.c
--- filter/filter_yait.c 26 May 2007 15:52:10 -0000 1.1
+++ filter/filter_yait.c 27 May 2007 09:28:47 -0000
@@ -20,10 +20,10 @@
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#define MOD_NAME "filter_yait.so"
-#define MOD_VERSION "v0.1 (2007-02-14)"
-#define MOD_CAP "Yet Another Inverse Telecine filter"
-#define MOD_AUTHOR "Allan Snider"
+#define MOD_NAME "filter_yait.so"
+#define MOD_VERSION "v0.2 (2007-05-26)"
+#define MOD_CAP "Yet Another Inverse Telecine filter"
+#define MOD_AUTHOR "Allan Snider"
#include "transcode.h"
#include "filter.h"
@@ -36,729 +36,686 @@
/*
- * yait:
- * Yet Another Inverse Telecine filter.
+ * yait:
+ * Yet Another Inverse Telecine filter.
*
- * Usage:
- * -J yait=log[=file] (-y null)
- * -J yait=ops[=file]
- *
- * Description:
- *
- * This filter is designed specifically to handle mixed progressive and NTSC
- * telecined data (2:3 pulldown), converting from NTSC_VIDEO (29.97 fps) to NTSC_FILM
- * (23.976 fps). It uses row save and copy operations to reconstruct progressive
- * frames. It is provided as an alternative to the -J ivtc,32detect,decimate method.
- *
- * For those who don't care how much cpu is used but are interested only
- * in trying to achieve the best quality rendering, then read on. If not, then stop
- * reading right now, as this filter requires a complete separate pass on the video
- * stream and an external analysis tool to be run.
- *
- * The main advantage of using a separate pass is that it provides a much
- * larger window of frames to examine when deciding what frames need to be dropped
- * or de-interlaced. The video stream is read at 30 fps (--hard_fps). Duplicate
- * frames are inserted by the demuxer to keep the frame rate at 30 when progressive
- * data is encountered. These frames can appear quite early or late, far beyond the
- * five frame local window used by ivtc, etc. This approach allows drop frames to
- * be credited, and debited, (up to a point), making better drop frame choices. The
- * result is a (noticeably) smoother video.
- *
- * Another advantage of using a large frame window is to provide context
- * for determining interlace patterns. Local interlace patterns (eg. a 5 frame
- * window) can sometimes be impossible to determine. When able to look ahead or
- * behind for existing patterns, usually the correct pattern can be inherited.
- *
- * The filter guarantees one drop frame per 5 frames. No more, no less.
- *
- * Using the filter:
- *
- * Pass 1:
- *
- * -J yait=log -y null
- *
- * The first pass is used only to generate row (even/odd) delta information.
- * This is written as a text log file (called yait.log by default). (The
- * file name can be specified using yait=log=file). This is the only data
- * required by this pass, so no video (or audio) frames need to be encoded.
- * You do need to specify the demuxer_sync method however. (It must be 2).
- *
- * Alternatively (for debug purposes), you may want to generate a frame
- * labeled video file, then compare the yait analysis to the original video.
- * In this case use something like:
- * -H 10 -x vob -i ...
- * --export_fps 0,4 --demuxer_sync 2 -y xvid4,null -o label.ogm
- * -J yait=log -J text=frame:posdef=8
- *
- * Running the tcyait tool:
- *
- * Pass 1 created a yait.log file. The analysis tool 'tcyait' is then run
- * which reads the log file and determines which areas are telecined and
- * progressive and detects the telecine patterns. A yait frame operations
- * file is then written (yait.ops by default). It is a text file containing
- * instructions for each frame, such as nop, save even or odd rows, copy rows,
- * drop frames, or blend a frame. The usage of the tool is as follows:
- *
- * tcyait [-d] [-l log] [-o ops] [-m mode]
- * -d Print debug information to stdout.
- * -l log Specify input yait log file name [yait.log].
- * -o ops Specify output yait frame ops file name [yait.ops].
- * -m mode Specify transcode de-interlace method [3].
- *
- * I typically run it as:
- * tcyait -d > yait.info
- *
- * One could query why pass 1 doesn't just create the .ops file directly. That
- * is, run the analysis at TC_FILTER_CLOSE time and save the user from having
- * to run the tool directly. The main reason is that the tcyait code is alpha
- * and still undergoing a lot of fine tuning. I would not want to have to
- * regenerate the row delta information every time I tweaked the analysis
- * portion. So, it exists as a separate tool for now.
- *
- * Pass 2 (and 3):
- *
- * -J yait=ops -y ...
- *
- * The second pass (or third for -R 1 and -R 2), reads the frame operations file
- * generated by tcyait, (called yait.ops by default). (The file name can be
- * specified using yait=ops=file). This file instructs the filter to save or
- * copy rows, skip frames, or de-interlace frames, and causes the pre filtering
- * to reduce the frame rate to 24 fps. Hence, you must specify the export fps
- * as 24, or will get truncated audio, (ie. --export_fps 24,1). The frame
- * sequence seen by the filter must match exactly what pass 1 saw. That is, you
- * cannot specify a frame range or audio track in pass 1, but not pass 2, and
- * visa versa. Here is an example invocation:
- *
- * transcode -H 10 -a 0 -x vob -i ... -w 1800,50 -b 192,0,0 -Z ...
- * -R 1 -y xvid4,null --demuxer_sync 2 --export_fps 24,1
- * -J yait=ops --progress_rate 25
- *
- * transcode -H 10 -a 0 -x vob -i ... -w 1800,50 -b 192,0,0 -Z ...
- * -R 2 -y xvid4 --demuxer_sync 2 --export_fps 24,1 -o ...
- * -J yait=ops --progress_rate 25
- *
- * The import frame rate and --hard_fps flags are forced by the filter and
- * need not be specified.
- *
- * DISCLAIMER:
- *
- * This is a work in progress. For non-NTSC telecine patterns, PAL, or purely
- * interlaced material, you are going to get nonsense results. Best stick to 'ivtc' or
- * 'smartyuv' for those.
- *
- * For some video, remarkably good results were obtained. (I was quite pleased and
- * hence felt obliged to distribute this). In a few cases I had video constantly switching
- * frame rates, with single or small grouped telecine, both even and odd patterns, and was
- * able to reconstruct the original 24 fps progressive film for the entire file, without
- * blending a single frame. For others, not so good. The analysis tool can sometimes
- * generate a lot of false positives for interlace detection and specify needless frame
- * blending. Generally, wherever a frame blend is specified, something went wrong. I
- * usually step frame by frame in the original (frame labelled) .ogm and edit/correct
- * the .ops file manually.
- *
- * There is much work to be done still (especially documentation), but here it
- * is, such as it is.
- * Allan
+ * Usage:
+ * -J yait=log[=file] (-y null)
+ * -J yait=ops[=file]
+ *
+ * Description:
+ *
+ * This filter is designed specifically to handle mixed progressive and NTSC
+ * telecined data (2:3 pulldown), converting from NTSC_VIDEO (29.97 fps) to NTSC_FILM
+ * (23.976 fps). It uses row save and copy operations to reconstruct progressive
+ * frames. It is provided as an alternative to the -J ivtc,32detect,decimate method.
+ *
+ * For those who don't care how much cpu is used but are interested only
+ * in trying to achieve the best quality rendering, then read on. If not, then stop
+ * reading right now, as this filter requires a complete separate pass on the video
+ * stream and an external analysis tool to be run.
+ *
+ * The main advantage of using a separate pass is that it provides a much
+ * larger window of frames to examine when deciding what frames need to be dropped
+ * or de-interlaced. The video stream is read at 30 fps (--hard_fps). Duplicate
+ * frames are inserted by the demuxer to keep the frame rate at 30 when progressive
+ * data is encountered. These frames can appear quite early or late, far beyond the
+ * five frame local window used by ivtc, etc. This approach allows drop frames to
+ * be credited, and debited, (up to a point), making better drop frame choices. The
+ * result is a (noticeably) smoother video.
+ *
+ * Another advantage of using a large frame window is to provide context
+ * for determining interlace patterns. Local interlace patterns (eg. a 5 frame
+ * window) can sometimes be impossible to determine. When able to look ahead or
+ * behind for existing patterns, usually the correct pattern can be inherited.
+ *
+ * The filter guarantees one drop frame per 5 frames. No more, no less.
+ *
+ * Using the filter:
+ *
+ * Pass 1:
+ *
+ * -J yait=log -y null
+ *
+ * The first pass is used only to generate row (even/odd) delta information.
+ * This is written as a text log file (called yait.log by default). (The
+ * file name can be specified using yait=log=file). This is the only data
+ * required by this pass, so no video (or audio) frames need to be encoded.
+ * You do need to specify the demuxer_sync method however. (It must be 2).
+ *
+ * Alternatively (for debug purposes), you may want to generate a frame
+ * labeled video file, then compare the yait analysis to the original video.
+ * In this case use something like:
+ * -H 10 -x vob -i ...
+ * --export_fps 0,4 --demuxer_sync 2 -y xvid4,null -o label.ogm
+ * -J yait=log -J text=frame:posdef=8
+ *
+ * Running the tcyait tool:
+ *
+ * Pass 1 created a yait.log file. The analysis tool 'tcyait' is then run
+ * which reads the log file and determines which areas are telecined and
+ * progressive and detects the telecine patterns. A yait frame operations
+ * file is then written (yait.ops by default). It is a text file containing
+ * instructions for each frame, such as nop, save even or odd rows, copy rows,
+ * drop frames, or blend a frame. The usage of the tool is as follows:
+ *
+ * tcyait [-d] [-l log] [-o ops] [-m mode]
+ * -d Print debug information to stdout.
+ * -l log Specify input yait log file name [yait.log].
+ * -o ops Specify output yait frame ops file name [yait.ops].
+ * -m mode Specify transcode de-interlace method [3].
+ *
+ * I typically run it as:
+ * tcyait -d > yait.info
+ *
+ * One could query why pass 1 doesn't just create the .ops file directly. That
+ * is, run the analysis at TC_FILTER_CLOSE time and save the user from having
+ * to run the tool directly. The main reason is that the tcyait code is alpha
+ * and still undergoing a lot of fine tuning. I would not want to have to
+ * regenerate the row delta information every time I tweaked the analysis
+ * portion. So, it exists as a separate tool for now.
+ *
+ * Pass 2 (and 3):
+ *
+ * -J yait=ops -y ...
+ *
+ * The second pass (or third for -R 1 and -R 2), reads the frame operations file
+ * generated by tcyait, (called yait.ops by default). (The file name can be
+ * specified using yait=ops=file). This file instructs the filter to save or
+ * copy rows, skip frames, or de-interlace frames, and causes the pre filtering
+ * to reduce the frame rate to 24 fps. Hence, you must specify the export fps
+ * as 24, or will get truncated audio, (ie. --export_fps 24,1). The frame
+ * sequence seen by the filter must match exactly what pass 1 saw. That is, you
+ * cannot specify a frame range or audio track in pass 1, but not pass 2, and
+ * visa versa. Here is an example invocation:
+ *
+ * transcode -H 10 -a 0 -x vob -i ... -w 1800,50 -b 192,0,0 -Z ...
+ * -R 1 -y xvid4,null --demuxer_sync 2 --export_fps 24,1
+ * -J yait=ops --progress_rate 25
+ *
+ * transcode -H 10 -a 0 -x vob -i ... -w 1800,50 -b 192,0,0 -Z ...
+ * -R 2 -y xvid4 --demuxer_sync 2 --export_fps 24,1 -o ...
+ * -J yait=ops --progress_rate 25
+ *
+ * The import frame rate and --hard_fps flags are forced by the filter and
+ * need not be specified.
+ *
+ * DISCLAIMER:
+ *
+ * This is a work in progress. For non-NTSC telecine patterns, PAL, or purely
+ * interlaced material, you are going to get nonsense results. Best stick to 'ivtc' or
+ * 'smartyuv' for those.
+ *
+ * For some video, remarkably good results were obtained. (I was quite pleased and
+ * hence felt obliged to distribute this). In a few cases I had video constantly switching
+ * frame rates, with single or small grouped telecine, both even and odd patterns, and was
+ * able to reconstruct the original 24 fps progressive film for the entire file, without
+ * blending a single frame. For others, not so good. The analysis tool can sometimes
+ * generate a lot of false positives for interlace detection and specify needless frame
+ * blending. Generally, wherever a frame blend is specified, something went wrong. I
+ * usually step frame by frame in the original (frame labelled) .ogm and edit/correct
+ * the .ops file manually.
+ *
+ * There is much work to be done still (especially documentation), but here it
+ * is, such as it is.
+ * Allan
*/
/*
- * Defines:
+ * Defines:
*/
-#define FALSE 0
-#define TRUE 1
-
-#define Y_LOG_FN "yait.log" /* log file read */
-#define Y_OPS_FN "yait.ops" /* frame operation file written */
+#define Y_LOG_FN "yait.log" /* log file read */
+#define Y_OPS_FN "yait.ops" /* frame operation file written */
/* frame ops */
-#define Y_OP_ODD 0x10
-#define Y_OP_EVEN 0x20
-#define Y_OP_PAT 0x30
-
-#define Y_OP_NOP 0x0
-#define Y_OP_SAVE 0x1
-#define Y_OP_COPY 0x2
-#define Y_OP_DROP 0x4
-#define Y_OP_DEINT 0x8
-
-
-/*
- * Globals:
- */
-
-FILE *Log_fp; /* output log file */
-FILE *Ops_fp; /* input frame ops file */
-
-uint8_t *Fbuf; /* video frame buffer */
-int Codec; /* internal codec */
-int Fn; /* frame number */
-
-
-/*
- * Prototypes:
- */
-
-static int yait_get_config( char* );
-static int yait_init( char* );
-static int yait_fini( void );
-static int yait_process( vframe_list_t* );
-
-static void yait_compare( vframe_list_t*, uint8_t*, int );
-static void yait_cmp_rgb( uint8_t*, uint8_t*, int, int, int*, int* );
-static void yait_cmp_yuv( uint8_t*, uint8_t*, int, int, int*, int* );
-static int yait_ops( vframe_list_t* );
-static int yait_ops_chk( void );
-static int yait_ops_get( char*, int, int* );
-static int yait_ops_decode( char*, int* );
-static void yait_put_rows( uint8_t*, uint8_t*, int, int, int );
-
-
-/*
- * tc_filter:
- * YAIT filter main entry point. Single instance.
- */
-
-int
-tc_filter( frame_list_t *ptr_, char *opt )
- {
- vframe_list_t *ptr = (vframe_list_t*) ptr_;
-
- if( ptr->tag & TC_AUDIO )
- return( 0 );
-
- if( ptr->tag & TC_FILTER_GET_CONFIG )
- return( yait_get_config(opt) );
-
- if( ptr->tag & TC_FILTER_INIT )
- return( yait_init(opt) );
-
- if( ptr->tag & TC_FILTER_CLOSE )
- return( yait_fini() );
-
- if( ptr->tag & TC_PRE_S_PROCESS )
- return( yait_process(ptr) );
-
- return( 0 );
- }
-
-
-/*
- * yait_get_config:
- */
-
-static int
-yait_get_config( char *opt )
- {
- optstr_filter_desc( opt, MOD_NAME, MOD_CAP, MOD_VERSION, MOD_AUTHOR, "VRYE", "1" );
- optstr_param( opt, "log", "Compute and write yait delta log file", "%s", "" );
- optstr_param( opt, "ops", "Read and apply yait frame operation file", "%s", "" );
-
- return( 0 );
- }
-
-
-/*
- * yait_init:
- */
-
-static int
-yait_init( char *opt )
- {
- static vob_t *vob;
- char buf[256], *fn;
- const char *p;
- int n;
-
- if( verbose )
- {
- tc_log_info( MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP );
- tc_log_info( MOD_NAME, "options=%s", opt );
- }
-
- vob = tc_get_vob();
- if( !vob )
- {
- tc_log_error( MOD_NAME, "cannot get VOB info." );
- return( -1 );
- }
-
- Codec = vob->im_v_codec;
- fn = NULL;
-
- /* log file */
- p = optstr_lookup( opt, "log" );
- if( p )
- {
- fn = Y_LOG_FN;
- n = optstr_get( opt, "log", "%[^:]", buf );
- if( n > 0 )
- fn = buf;
-
- Log_fp = fopen( fn, "w" );
- if( !Log_fp )
- {
- perror( "fopen" );
- tc_log_error( MOD_NAME, "cannot create log file, '%s'", buf );
- return( -1 );
- }
- }
-
- /* ops file */
- p = optstr_lookup( opt, "ops" );
- if( p )
- {
- fn = Y_OPS_FN;
- n = optstr_get( opt, "ops", "%[^:]", buf );
- if( n > 0 )
- fn = buf;
-
- Ops_fp = fopen( fn, "r" );
- if( !Ops_fp )
- {
- perror( "fopen" );
- tc_log_error( MOD_NAME, "cannot open yait ops file, '%s'", buf );
- return( -1 );
- }
-
- if( !yait_ops_chk() )
- {
- tc_log_error( MOD_NAME, "invalid yait ops file" );
- return( -1 );
- }
- }
-
- if( !Log_fp && !Ops_fp )
- {
- tc_log_error( MOD_NAME, "at least one operation (log|ops) must be specified" );
- return( -1 );
- }
-
- if( Log_fp && Ops_fp )
- {
- tc_log_error( MOD_NAME, "only one operation (log|ops) may be specified" );
- return( -1 );
- }
-
- if( Log_fp )
- {
- tc_log_info( MOD_NAME, "Generating YAIT delta log file '%s'", fn );
- tc_log_info( MOD_NAME, "Forcing --hard_fps, -f 30,4, --export_fps 30,4" );
-
- /* try to lock everything in at 30 fps */
- vob->hard_fps_flag = TC_TRUE;
- vob->im_frc = 4;
- vob->ex_frc = 4;
- vob->fps = NTSC_VIDEO;
- vob->ex_fps = NTSC_VIDEO;
- }
-
- if( Ops_fp )
- {
- tc_log_info( MOD_NAME, "Applying YAIT frame operations file '%s'", fn );
- tc_log_info( MOD_NAME, "Forcing --hard_fps, -f 30,4, --export_fps 24,1" );
-
- /* try to lock import at 30 fps, export at 24 fps */
- vob->hard_fps_flag = TC_TRUE;
- vob->im_frc = 4;
- vob->ex_frc = 1;
- vob->fps = NTSC_VIDEO;
- vob->ex_fps = NTSC_FILM;
- }
-
- Fbuf = tc_malloc( SIZE_RGB_FRAME );
- if( !Fbuf )
- {
- perror( "tc_malloc" );
- tc_log_error( MOD_NAME, "cannot allocate frame buffer" );
- return( -1 );
- }
-
- memset( Fbuf, 0, SIZE_RGB_FRAME );
-
- Fn = -1;
-
- return( 0 );
- }
-
-
-/*
- * yait_fini:
- */
-
-static int
-yait_fini( void )
- {
- if( Log_fp )
- fclose( Log_fp );
- if( Ops_fp )
- fclose( Ops_fp );
- if( Fbuf )
- free( Fbuf );
-
- Log_fp = NULL;
- Ops_fp = NULL;
- Fbuf = NULL;
-
- return( 0 );
- }
-
-
-/*
- * yait_process:
- */
-
-static int
-yait_process( vframe_list_t *ptr )
- {
- if( Fn == -1 )
- {
- Fn = ptr->id;
- ac_memcpy( Fbuf, ptr->video_buf, ptr->video_size );
- }
-
- if( ptr->id != Fn )
- {
- tc_log_error( MOD_NAME, "inconsistent frame numbers" );
- yait_fini();
- return( -1 );
- }
-
- if( Log_fp )
- {
- yait_compare( ptr, Fbuf, Fn );
- ac_memcpy( Fbuf, ptr->video_buf, ptr->video_size );
- }
-
- if( Ops_fp )
- if( !yait_ops(ptr) )
- {
- yait_fini();
- return( -1 );
- }
-
- Fn++;
- return( 0 );
- }
-
-
-/*
- * yait_compare:
- */
-
-static void
-yait_compare( vframe_list_t *ptr, uint8_t *lv, int fn )
- {
- uint8_t *cv;
- int ed, od;
- int w, h;
-
- cv = ptr->video_buf;
- w = ptr->v_width;
- h = ptr->v_height;
-
- if( Codec == CODEC_RGB )
- yait_cmp_rgb( lv, cv, w, h, &ed, &od );
- else
- yait_cmp_yuv( lv, cv, w, h, &ed, &od );
-
- fprintf( Log_fp, "%d: e: %d, o: %d\n", fn, ed, od );
-
- /* BUG: until the blocked tcdecode pipe problem is fixed... */
- if( !(fn%5) )
- fflush( Log_fp );
- }
-
-
-/*
- * yait_cmp_rgb:
- */
-
-static void
-yait_cmp_rgb( uint8_t *lv, uint8_t *cv, int w, int h, int *ed, int *od )
- {
- uint8_t *lp, *cp;
- int x, y, p;
- int e, o;
-
- /* even row delta */
- e = 0;
- for( y=0; y<h; y+=2 )
- {
- p = y * w * 3;
- lp = lv + p;
- cp = cv + p;
- for( x=0; x<w; x++ )
- {
- e += abs( *lp++ - *cp++ );
- e += abs( *lp++ - *cp++ );
- e += abs( *lp++ - *cp++ );
- }
- }
-
- /* odd row delta */
- o = 0;
- for( y=1; y<h; y+=2 )
- {
- p = y * w * 3;
- lp = lv + p;
- cp = cv + p;
- for( x=0; x<w; x++ )
- {
- o += abs( *lp++ - *cp++ );
- o += abs( *lp++ - *cp++ );
- o += abs( *lp++ - *cp++ );
- }
- }
-
- *ed = e;
- *od = o;
- }
-
-
-/*
- * yait_cmp_yuv:
- */
-
-static void
-yait_cmp_yuv( uint8_t *lv, uint8_t *cv, int w, int h, int *ed, int *od )
- {
- uint8_t *lp, *cp;
- int x, y, p;
- int e, o;
-
- /* even row delta */
- e = 0;
- for( y=0; y<h; y+=2 )
- {
- /* y */
- p = y * w;
- lp = lv + p;
- cp = cv + p;
- for( x=0; x<w; x++ )
- e += abs( *lp++ - *cp++ );
-
- /* uv */
- p = w*h + y * w/2;
- lp = lv + p;
- cp = cv + p;
- for( x=0; x<w/2; x++ )
- e += abs( *lp++ - *cp++ );
- }
-
- /* odd row delta */
- o = 0;
- for( y=1; y<h; y+=2 )
- {
- /* y */
- p = y * w;
- lp = lv + p;
- cp = cv + p;
- for( x=0; x<w; x++ )
- o += abs( *lp++ - *cp++ );
-
- /* uv */
- p = w*h + y * w/2;
- lp = lv + p;
- cp = cv + p;
- for( x=0; x<w/2; x++ )
- o += abs( *lp++ - *cp++ );
- }
-
- *ed = e;
- *od = o;
- }
-
-
-/*
- * yait_ops:
- */
-
-static int
-yait_ops( vframe_list_t *ptr )
- {
- char buf[256];
- uint8_t *v;
- int mode, op;
- int w, h;
-
- v = ptr->video_buf;
- w = ptr->v_width;
- h = ptr->v_height;
-
- fgets( buf, 256, Ops_fp );
- op = yait_ops_get( buf, Fn, &mode );
-
- if( op < 0 )
- return( FALSE );
-
- if( op & Y_OP_SAVE )
- yait_put_rows( Fbuf, v, w, h, op&Y_OP_PAT );
-
- if( op & Y_OP_COPY )
- yait_put_rows( v, Fbuf, w, h, op&Y_OP_PAT );
-
- if( op & Y_OP_DROP )
- ptr->attributes |= TC_FRAME_IS_SKIPPED;
-
- if( op & Y_OP_DEINT )
- {
- ptr->attributes |= TC_FRAME_IS_INTERLACED;
- ptr->deinter_flag = mode;
- }
-
- return( TRUE );
- }
-
-
-/*
- * yait_ops_chk:
- */
-
-static int
-yait_ops_chk( void )
- {
- char buf[256], *p;
- int fn, op;
-
- fscanf( Ops_fp, "%d:", &fn );
- rewind( Ops_fp );
- for( ;; )
- {
- p = fgets( buf, 256, Ops_fp );
- if( !p )
- break;
-
- op = yait_ops_get( buf, fn, NULL );
- if( op < 0 )
- return( FALSE );
- fn++;
- }
-
- rewind( Ops_fp );
- return( TRUE );
- }
-
-
-/*
- * yait_ops_get:
- */
-
-static int
-yait_ops_get( char *buf, int fn, int *mode )
- {
- char str[256];
- int op, f, n;
-
- f = -1;
- str[0] = 0;
-
- n = sscanf( buf, "%d: %s\n", &f, str );
- if( n < 1 )
- {
- if( feof(Ops_fp) )
- tc_log_error( MOD_NAME, "truncated yait ops file, frame: %d", fn );
- else
- tc_log_error( MOD_NAME, "invalid yait ops format, frame: %d", fn );
- return( -1 );
- }
-
- if( f != fn )
- {
- tc_log_error( MOD_NAME, "invalid yait ops frame number, frame: %d", fn );
- return( -1 );
- }
-
- op = yait_ops_decode( str, mode );
- if( op < 0 )
- {
- tc_log_error( MOD_NAME, "invalid yait ops code, frame: %d", fn );
- return( -1 );
- }
-
- return( op );
- }
-
-
-/*
- * yait_ops_decode:
- */
-
-static int
-yait_ops_decode( char *str, int *mode )
- {
- int op, c;
-
- op = 0;
- while( *str )
- {
- c = *str++;
- if( c>='1' && c<='5' )
- {
- op |= Y_OP_DEINT;
- if( mode )
- *mode = c - '0';
- continue;
- }
-
- switch( c )
- {
- case 'o':
- op |= Y_OP_ODD;
- break;
- case 'e':
- op |= Y_OP_EVEN;
- break;
- case 's':
- op |= Y_OP_SAVE;
- break;
- case 'c':
- op |= Y_OP_COPY;
- break;
- case 'd':
- op |= Y_OP_DROP;
- break;
- default:
- return( -1 );
- break;
- }
- }
-
- return( op );
- }
-
-
-/*
- * yait_put_rows:
- */
-
-static void
-yait_put_rows( uint8_t *dst, uint8_t *src, int w, int h, int flg )
- {
- int y, o;
-
- y = (flg==Y_OP_EVEN) ? 0 : 1;
-
- if( Codec == CODEC_RGB )
- {
- for( ; y<h; y+=2 )
- {
- o = y * w * 3;
- ac_memcpy( dst+o, src+o, w*3 );
- }
- }
- else
- {
- for( ; y<h; y+=2 )
- {
- /* y (luminance) */
- o = y * w;
- ac_memcpy( dst+o, src+o, w );
-
- /* 2 * h/2 blocks (u and v) = h */
- o = w*h + y * w/2;
- ac_memcpy( dst+o, src+o, w/2 );
- }
- }
- }
+enum {
+ Y_OP_ODD = 0x10,
+ Y_OP_EVEN = 0x20,
+ Y_OP_PAT = 0x30,
+};
+
+enum {
+ Y_OP_NOP = 0x0,
+ Y_OP_SAVE = 0x1,
+ Y_OP_COPY = 0x2,
+ Y_OP_DROP = 0x4,
+ Y_OP_DEINT = 0x8,
+};
+
+
+typedef void (*yait_cmp_hook_fn)(const uint8_t *lv, const uint8_t *cv,
+ int w, int h, int *ed , int *od);
+
+
+/*
+ * Globals:
+ */
+
+FILE *Log_fp = NULL; /* output log file */
+FILE *Ops_fp = NULL; /* input frame ops file */
+
+uint8_t *Fbuf = NULL; /* video frame buffer */
+int Codec; /* internal codec */
+int Frame_num; /* frame number */
+yait_cmp_hook_fn cmp_fn = NULL; /* real compare function (saves one if()) */
+
+
+/*
+ * Prototypes:
+ */
+
+static int yait_get_config(char* opt);
+static int yait_init(const char* opt);
+static int yait_fini(void);
+static int yait_process(vframe_list_t* ptr);
+
+static void yait_compare(vframe_list_t* ptr, const uint8_t* lv, int fn);
+static void yait_cmp_rgb(const uint8_t *lv, const uint8_t *cv,
+ int w, int h, int *ed, int *od);
+static void yait_cmp_yuv(const uint8_t *lv, const uint8_t *cv,
+ int w, int h, int *ed, int *od);
+static int yait_ops(vframe_list_t* ptr);
+static int yait_ops_chk(void);
+static int yait_ops_get(const char* buf, int fn, int* mode);
+static int yait_ops_decode(const char *str, int *mode);
+static void yait_put_rows(uint8_t *dst, const uint8_t *src,
+ int w, int h, int flag);
+
+
+/*
+ * tc_filter:
+ * YAIT filter main entry point. Single instance.
+ */
+int tc_filter(frame_list_t *ptr_, char *opt)
+{
+ vframe_list_t *ptr = (vframe_list_t*) ptr_;
+
+ if (ptr->tag & TC_AUDIO)
+ return TC_ERROR;
+
+ if (ptr->tag & TC_FILTER_GET_CONFIG)
+ return yait_get_config(opt);
+
+ if (ptr->tag & TC_FILTER_INIT)
+ return yait_init(opt);
+
+ if (ptr->tag & TC_FILTER_CLOSE)
+ return yait_fini();
+
+ if (ptr->tag & TC_PRE_S_PROCESS)
+ return yait_process(ptr);
+
+ return TC_ERROR;
+}
+
+
+/*
+ * yait_get_config:
+ */
+static int yait_get_config(char *opt)
+{
+ optstr_filter_desc(opt, MOD_NAME, MOD_CAP, MOD_VERSION,
+ MOD_AUTHOR, "VRYE", "1");
+ optstr_param(opt, "log", "Compute and write yait delta log file",
+ "%s", "");
+ optstr_param(opt, "ops", "Read and apply yait frame operation file",
+ "%s", "");
+
+ return TC_OK;
+}
+
+
+/*
+ * yait_init:
+ */
+static int yait_init(const char *opt)
+{
+ vob_t *vob = tc_get_vob();
+ char buf[256], *fn = NULL;
+ const char *p = NULL;
+ int n;
+
+ if (verbose) {
+ tc_log_info( MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP );
+ tc_log_info( MOD_NAME, "options=%s", opt );
+ }
+
+ /* FIXME: options=help handling still missing */
+
+ if (!vob) {
+ /* can't happen */
+ tc_log_error(MOD_NAME, "cannot get VOB info.");
+ return TC_ERROR;
+ }
+
+ Frame_num = -1;
+ Codec = vob->im_v_codec;
+ if (Codec == CODEC_RGB)
+ cmp_fn = yait_cmp_rgb;
+ else
+ cmp_fn = yait_cmp_yuv;
+
+ /* log file */
+ p = optstr_lookup(opt, "log");
+ if (p != NULL) {
+ fn = Y_LOG_FN;
+ n = optstr_get(p, "log", "%[^:]", buf); // XXX
+ if (n > 0)
+ fn = buf;
+
+ Log_fp = fopen(fn, "w");
+ if (!Log_fp) {
+ tc_log_error(MOD_NAME, "cannot create log file, '%s'", buf);
+ return TC_ERROR;
+ }
+ }
+
+ /* ops file */
+ p = optstr_lookup(opt, "ops");
+ if (p != NULL) {
+ fn = Y_OPS_FN;
+ n = optstr_get(p, "ops", "%[^:]", buf); // XXX
+ if (n > 0)
+ fn = buf;
+
+ Ops_fp = fopen(fn, "r");
+ if(!Ops_fp) {
+ tc_log_error(MOD_NAME,
+ "cannot open yait ops file, '%s'", buf);
+ return TC_ERROR;
+ }
+
+ if (!yait_ops_chk()) {
+ tc_log_error(MOD_NAME, "invalid yait ops file");
+ return TC_ERROR;
+ }
+ }
+
+ if (!Log_fp && !Ops_fp) {
+ tc_log_error(MOD_NAME,
+ "at least one operation (log|ops) must be specified");
+ return TC_ERROR;
+ }
+
+ if (Log_fp && Ops_fp) {
+ tc_log_error(MOD_NAME,
+ "only one operation (log|ops) may be specified");
+ return TC_ERROR;
+ }
+
+ /* try to lock import at 30 fps (needed by both modes ) */
+ vob->hard_fps_flag = TC_TRUE;
+ vob->im_frc = 4;
+ vob->fps = NTSC_VIDEO;
+
+ if (Log_fp) {
+ tc_log_info(MOD_NAME, "Generating YAIT delta log file '%s'", fn);
+ tc_log_info(MOD_NAME,
+ "Forcing --hard_fps, -f 30,4, --export_fps 30,4");
+
+ /* try to lock export too at 30 fps */
+ vob->ex_fps = NTSC_VIDEO;
+ vob->ex_frc = 4;
+ }
+
+ if (Ops_fp) {
+ tc_log_info(MOD_NAME,
+ "Applying YAIT frame operations file '%s'", fn);
+ tc_log_info(MOD_NAME,
+ "Forcing --hard_fps, -f 30,4, --export_fps 24,1");
+
+ /* try to lock export at 24 fps */
+ vob->ex_fps = NTSC_FILM;
+ vob->ex_frc = 1;
+ }
+
+ Fbuf = tc_zalloc(SIZE_RGB_FRAME);
+ if (!Fbuf) {
+ tc_log_error(MOD_NAME, "cannot allocate frame buffer");
+ return TC_ERROR;
+ }
+ return TC_OK;
+}
+
+
+/*
+ * yait_fini:
+ */
+static int yait_fini(void)
+{
+ if (Log_fp != NULL) {
+ fclose(Log_fp);
+ Log_fp = NULL;
+ }
+ if (Ops_fp != NULL) {
+ fclose(Ops_fp);
+ Ops_fp = NULL;
+ }
+ if (Fbuf != NULL) {
+ tc_free(Fbuf);
+ Fbuf = NULL;
+ }
+
+ return TC_OK;
+}
+
+
+/*
+ * yait_process:
+ */
+static int yait_process(vframe_list_t *ptr)
+{
+ if (Frame_num == -1) {
+ /* first run */
+ Frame_num = ptr->id;
+ ac_memcpy(Fbuf, ptr->video_buf, ptr->video_size);
+ }
+
+ if (ptr->id != Frame_num) {
+ tc_log_error( MOD_NAME, "inconsistent frame numbers" );
+ yait_fini();
+ return TC_ERROR;
+ }
+
+ if (Log_fp != NULL) {
+ yait_compare(ptr, Fbuf, Frame_num);
+ ac_memcpy(Fbuf, ptr->video_buf, ptr->video_size);
+ }
+
+ if (Ops_fp != NULL) {
+ if (!yait_ops(ptr)) {
+ yait_fini();
+ return TC_ERROR;
+ }
+ }
+
+ Frame_num++;
+ return TC_OK;
+}
+
+
+/*
+ * yait_compare:
+ */
+static void yait_compare(vframe_list_t *ptr, const uint8_t *lv, int fn)
+{
+ const uint8_t *cv = ptr->video_buf; // XXX: checking?
+ int w = ptr->v_width, h = ptr->v_height; // XXX: checking?
+ int ed, od;
+
+ cmp_fn(lv, cv, w, h, &ed, &od);
+ fprintf(Log_fp, "%d: e: %d, o: %d\n", fn, ed, od); // XXX: write check?
+
+ /* FIXME BUG: until the blocked tcdecode pipe problem is fixed... */
+ if (!(fn % 5))
+ fflush(Log_fp); // XXX: write check?
+}
+
+
+/*
+ * yait_cmp_rgb:
+ */
+static void yait_cmp_rgb(const uint8_t *lv, const uint8_t *cv,
+ int w, int h, int *ed, int *od)
+{
+ const uint8_t *lp = NULL, *cp = NULL;
+ int x, y, p;
+ int e, o; /* we really need those two? */
+
+ /* even row delta */
+ e = 0;
+ for (y = 0; y < h; y += 2) {
+ p = y * w * 3;
+ lp = lv + p;
+ cp = cv + p;
+ for (x = 0; x < w; x++) {
+ e += abs(*lp++ - *cp++);
+ e += abs(*lp++ - *cp++);
+ e += abs(*lp++ - *cp++);
+ }
+ }
+
+ /* odd row delta */
+ o = 0;
+ for (y = 1; y < h; y += 2) {
+ p = y * w * 3;
+ lp = lv + p;
+ cp = cv + p;
+ for (x = 0; x < w; x++) {
+ o += abs(*lp++ - *cp++);
+ o += abs(*lp++ - *cp++);
+ o += abs(*lp++ - *cp++);
+ }
+ }
+
+ *ed = e;
+ *od = o;
+}
+
+
+/*
+ * yait_mp_yuv:
+ */
+static void yait_cmp_yuv(const uint8_t *lv, const uint8_t *cv,
+ int w, int h, int *ed, int *od)
+{
+ const uint8_t *lp = NULL, *cp = NULL;
+ int x, y, p;
+ int e, o; /* we really need those two? */
+
+ /* even row delta */
+ e = 0;
+ for (y = 0; y < h; y += 2) {
+ /* y */
+ p = y * w;
+ lp = lv + p;
+ cp = cv + p;
+ for (x = 0; x < w; x++)
+ e += abs(*lp++ - *cp++);
+
+ /* uv */
+ p = w * h + y * w/2;
+ lp = lv + p;
+ cp = cv + p;
+ for (x = 0; x < w/2; x++)
+ e += abs(*lp++ - *cp++);
+ }
+
+ /* odd row delta */
+ o = 0;
+ for (y = 1; y < h; y += 2) {
+ /* y */
+ p = y * w;
+ lp = lv + p;
+ cp = cv + p;
+ for (x = 0; x < w; x++)
+ o += abs(*lp++ - *cp++);
+
+ /* uv */
+ p = w * h + y * w/2;
+ lp = lv + p;
+ cp = cv + p;
+ for(x = 0; x < w/2; x++)
+ o += abs(*lp++ - *cp++);
+ }
+
+ *ed = e;
+ *od = o;
+}
+
+
+/*
+ * yait_ops:
+ */
+static int yait_ops(vframe_list_t *ptr)
+{
+ char buf[TC_BUF_LINE];
+ int mode, op;
+ uint8_t *v = ptr->video_buf;
+ int w = ptr->v_width, h = ptr->v_height;
+
+ fgets(buf, TC_BUF_LINE, Ops_fp);
+ op = yait_ops_get(buf, Frame_num, &mode);
+
+ if (op < 0)
+ return TC_FALSE;
+
+ if (op & Y_OP_SAVE)
+ yait_put_rows(Fbuf, v, w, h, op & Y_OP_PAT);
+
+ if (op & Y_OP_COPY)
+ yait_put_rows(v, Fbuf, w, h, op & Y_OP_PAT);
+
+ if (op & Y_OP_DROP)
+ ptr->attributes |= TC_FRAME_IS_SKIPPED;
+
+ if (op & Y_OP_DEINT) {
+ ptr->attributes |= TC_FRAME_IS_INTERLACED;
+ ptr->deinter_flag = mode;
+ }
+
+ return TC_TRUE;
+}
+
+
+/*
+ * yait_ops_chk:
+ */
+static int yait_ops_chk(void)
+{
+ char buf[TC_BUF_LINE], *p = NULL;
+ int fn, op;
+
+ fscanf(Ops_fp, "%d:", &fn);
+ rewind(Ops_fp);
+ for (;;) {
+ p = fgets(buf, TC_BUF_LINE, Ops_fp);
+ if (!p)
+ break;
+
+ op = yait_ops_get(buf, fn, NULL);
+ if (op < 0)
+ return TC_FALSE;
+ fn++;
+ }
+
+ rewind(Ops_fp);
+ return TC_TRUE;
+}
+
+
+/*
+ * yait_ops_get:
+ */
+static int yait_ops_get(const char *buf, int fn, int *mode)
+{
+ char str[TC_BUF_LINE];
+ int op, f = -1, n;
+
+ str[0] = 0; // XXX
+
+ n = sscanf(buf, "%d: %s\n", &f, str);
+ if (n < 1) {
+ if (feof(Ops_fp))
+ tc_log_error(MOD_NAME, "truncated yait ops file, frame: %d", fn);
+ else
+ tc_log_error(MOD_NAME, "invalid yait ops format, frame: %d", fn);
+ return -1;
+ }
+
+ if (f != fn) {
+ tc_log_error(MOD_NAME,
+ "invalid yait ops frame number, frame: %d", fn);
+ return -1;
+ }
+
+ op = yait_ops_decode(str, mode);
+ if (op < 0) {
+ tc_log_error(MOD_NAME, "invalid yait ops code, frame: %d", fn);
+ return -1;
+ }
+
+ return op;
+}
+
+
+/*
+ * yait_ops_decode:
+ */
+static int yait_ops_decode(const char *str, int *mode)
+{
+ int op = 0, c;
+
+ while (*str) {
+ c = *str++;
+ if (c >= '1' && c <= '5') {
+ op |= Y_OP_DEINT;
+ if (mode)
+ *mode = c - '0';
+ continue;
+ }
+
+ switch (c) {
+ case 'o':
+ op |= Y_OP_ODD;
+ break;
+ case 'e':
+ op |= Y_OP_EVEN;
+ break;
+ case 's':
+ op |= Y_OP_SAVE;
+ break;
+ case 'c':
+ op |= Y_OP_COPY;
+ break;
+ case 'd':
+ op |= Y_OP_DROP;
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ return op;
+}
+
+
+/*
+ * yait_put_rows:
+ */
+static void yait_put_rows(uint8_t *dst, const uint8_t *src, int w, int h, int flag)
+{
+ int y = ((flag == Y_OP_EVEN) ?0 :1), o;
+
+ if (Codec == CODEC_RGB) {
+ for(; y < h; y += 2) {
+ o = y * w * 3;
+ ac_memcpy(dst + o, src + o, w * 3);
+ }
+ } else {
+ for(; y < h; y += 2) {
+ /* y (luminance) */
+ o = y * w;
+ ac_memcpy(dst + o, src + o, w);
+
+ /* 2 * h/2 blocks (u and v) = h */
+ o = w * h + y * w/2;
+ ac_memcpy(dst + o, src + o, w/2);
+ }
+ }
+}
+
+
+/*************************************************************************/
+
+/*
+ * Local variables:
+ * c-file-style: "stroustrup"
+ * c-file-offsets: ((case-label . *) (statement-case-intro . *))
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vim: expandtab shiftwidth=4:
+ */