"Same as source" FPS Improvements

Archive of historical development discussions
Discussions / Development has moved to GitHub
Forum rules
*******************************
Please be aware we are now using GitHub for issue tracking and feature requests.
- This section of the forum is now closed to new topics.

*******************************
van
Veteran User
Posts: 417
Joined: Wed Aug 29, 2007 6:35 am

Post by van »

Jon,

I'm new to all this code & feel free to ignore this but there are two things I don't understand about the changes to muxmp4.c. The first is that my understanding of the line

Code: Select all

duration    = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
is that it corrects for an asymptotic bias due to timebase conversion. Since the mp4 (audio) timebase isn't an integer mutiple of the video timebase, the duration isn't an integer in the new timebase and the conversion loses, on average, half a bit. This happens each frame so over say 100,000 frames the video will lag the audio by 50,000 samples. This will be noticable. The line above corrects for this bias by adding the fractional bits lost to the integer division truncation back into "duration" whenever they add up to a full bit. I don't understand how this line can get removed unless the container timebase is changed to 90KHz in which case there would have to be an equivalent line added for the audio muxing to remove the bias in its timebase conversion (plus it moves the variability from the video to the audio & I remember that it used to be really nasty to deal with the resulting quasi-periodic gaps in a way that didn't produce audible artifacts - maybe the mp4 container uses the audio timebase because variably timed audio was (is?) *way* harder than variably timed video).

The other thing is that I don't see how the buf flags work in the presence of frame reordering in the x264 encoder. A frame gets to encx264Work, it's timestamp & video data are passed to x264_encoder_encode but not the buffer flags or chapter mark. x264_encoder_encode returns some frame that was passed to it 0 to 32 frame times ago (depending on the bframes setting & how much reordering it had to do to accomodate whatever random pattern of Ps & Bs it decided to use). If you just copy flags from buf_in to buf_out they'll almost always end up way early & on the wrong frame. Seems like there at least needs to be pv_flags arrary (similar to the existing pv_start & pv_stop) in encx264Work to move the flags from the input buffer to an output buffer. (They won't be on the right buffer most of the time but the average delay will be right and since audio is muxed by dts, not pts, that should be all that matters).
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Post by jbrjake »

van wrote:my understanding of the line

Code: Select all

duration    = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
is that it corrects for an asymptotic bias due to timebase conversion.
Your assessment is correct -- like I bring up in the comments, it's a bad, bad way to do it and certainly won't get into the svn like that. Right now I'm basically just doing this as proof-of-concept and for that, it's cleaner to just plug in the hard coded durations.

Since I posted that diff, I've been working more locally, and I've moved that code out of the mp4 muxer. Now, I'm just modifying the buffer's stop time in render.c. Currently, even with the crude way I'm doing that (just adding 750 ticks to the 23.976 frames), it gives me the properly-varying durations.

I still want to pursue moving HB's mp4 muxer to a 90khz clock. I'd played around with changing the timebase and editing the audio muxing line, and I did get some nasty audio choppiness from it. However, do all other mp4 muxers really work the same way? When I look at the stts atom in mp4s not made by HandBrake, I never see similar variances in duration. My gut keeps telling me there has to be a way to do this.
The other thing is that I don't see how the buf flags work in the presence of frame reordering in the x264 encoder.
Err...the diff I pasted above isn't for use with x264 and certainly not with b-frames. I'm not there yet. I'm trying to find out if it's even possible to do VFR ivtc like this in libhb. Working with out of order encodes will come later. Anyway, with it now working off the buf->stop time, I'm not sure how much of a problem this will be.

I definitely think, though, that HB needs a way to pass information about frames from the decoder all the way to the muxer, matching frames correctly in the encoder. Just for diagnostics, if nothing else.
sdm
Bright Spark User
Posts: 194
Joined: Mon Feb 19, 2007 4:53 pm

Post by sdm »

I just wanted to report

I've applied the patch above to an svn of HandBrake and tested on 30 Rock season 1 - (its hard-telecined to 29.97 fps - at least the episodes I've looked at)
I encoded using ffmpeg encoder into mp4 container (mp4+aac) and chose 29.97fps and de-telecine as recommended.

The resulting file had a framerate of 26.27 fps instead of 23.976
The video looked pretty smooth - even in pans - but not perfect.
and, as expected, very out-of sync audio.

Hope this is helpful jbrjake and others.

--sdm.
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

status report

Post by jbrjake »

Struggling with IVTC

So, I've been trying all sorts of different methods.

1) Keep track of whether any the last 4 frames were dropped. If so, extend the duration of the current frame by 751 ticks.
2) Keep track of the frame durations detelecine assigns. If one of the last two frames was a 3 or a 1, extend the duration of the current frame by 751 ticks.
3) Like method 1, except only extend the frame directly after the dropped one, by a full 3003 ticks. It'd be the same effect as not duping frames (a slight stutter) but without the bitrate costs of duped frames.
4) Compare the buf->data and when one is the same as the last one's, discard it.

1) doesn't work because sometimes more than 1 frame out of 4 is dropped so then you're left with an excess of time to make up.
2) doesn't work because sometimes the durations don't go steady 323232 and you start losing time when it goes back to interlaced sections
3) might work if i stared at it some more but it seems stupid
4) doesn't work because apparently there are slight differences between the "duped" frames that the eye doesn't see, but make them not equal, numerically. Or I'm reading them wrong. Or something.

Then I've tried massaging all those methods in various ways, like adjusting their sensitivities (like to only 'notice' 29.97 material after it had been going on for a dozen frames).

Here's a messy diff with a bunch of different attempts, most commented out:
http://pastebin.ca/742806

But finally I figured out how to do it, I think. It's so simple I can't believe I've been working on this so long:

Keep a running count of dropped frames. Keep a running count of extended frames. When dropped * 4 > extended, extend the current frame. This solves the problems with method 1. Whenever the numbers are out of sync, they're made up at the next available opportunity (like a static shot of a wall, when ivtc didn't see anything to discard), instead of accumulating into a growing av desync. Sure, we're still ~1500 ticks off on when telecined sections start and end, and making up that lost time will extend the end even more, but, imo, it'll square the desync within a reasonable window. It will get even more reasonable when someone smarter than I am figures out how to buffer a couple of frames in the render or muxing pipeline, so the extended durations can begin 2 frames before the dropped frame like they should.

Now, this works great with 29.97 material. But I'm getting a weird result with soft telecined 23.976 stuff. To wit, it won't drop frames. I get duped frames on output. It drops the frames fine from hard telecined material, but ignores soft telecined. Weirder still, detelecine.c *does* recognize that the material has varying frame lengths, and by virtue of the duped frames being present, it *is* correctly reading and applying the repeat flags, rendering that extra material.

So I don't know what's up with that.

What I was doing earlier in this thread -- disabling pullup's ability to see soft telecine markers -- is a Bad Idea I've found. It doesn't seem to get the durations quite right, so things get a little jerky and out of sync. But I hope to figure out something. In the mean time, my long-term goal of replacing "Same as source" with always-on IVTC and variable frame rates just ain't gonna happen.

On the other hand, what I have now is pretty cool: variable frame-rate for purely NTSC video sources. Any part of the video that runs at video speed does so. Any part that ivtc does its magic on runs at film speed. It all stays reasonably within sync, most of the time. Are the video parts interlaced? Then throw a deinterlacing filter on as well. It'll work fine. You can feed in film stuff too -- it just doesn't get frames dropped like it should, so those parts runs at 29.97.

Obviously, this code still has a long ways to go. Anyway, here's what I've got...

http://pastebin.ca/742803

Code: Select all

Index: libhb/detelecine.c
===================================================================
--- libhb/detelecine.c	(revision 1022)
+++ libhb/detelecine.c	(working copy)
@@ -975,7 +975,7 @@
         }
         else
         {
-            goto output_frame;
+            goto discard_frame;
         }
     }
     
@@ -987,7 +987,7 @@
         
 		if (!frame) 
         {
-            goto output_frame;
+            goto discard_frame;
         }
 		if( frame->length < 2 ) 
         {
@@ -995,19 +995,19 @@
 
 			if( !(buf_in->flags & PIC_FLAG_REPEAT_FIRST_FIELD) )
             {
-                goto output_frame;
+                goto discard_frame;
             }
 			
             frame = pullup_get_frame( ctx );
 			
             if( !frame ) 
             {
-                goto output_frame;
+                goto discard_frame;
             }
 			if( frame->length < 2 ) 
             {
 				pullup_release_frame( frame );
-                goto output_frame;
+                goto discard_frame;
 			}
 		}
     }
@@ -1034,6 +1034,15 @@
 output_frame:    
     *buf_out = pv->buf_out;    
     return FILTER_OK;
+
+/* This and all discard_frame calls shown above are
+   the result of me restoring the functionality in
+   pullup that huevos_rancheros disabled because
+   HB couldn't handle it.                           */
+discard_frame:    
+    *buf_out = pv->buf_out;
+    return FILTER_DROP;
+
 }
 
 
Index: libhb/muxmp4.c
===================================================================
--- libhb/muxmp4.c	(revision 1022)
+++ libhb/muxmp4.c	(working copy)
@@ -380,7 +380,7 @@
         /* Because we use the audio samplerate as the timescale,
            we have to use potentially variable durations so the video
            doesn't go out of sync */
-        duration    = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
+        duration    = ( ( buf->stop * job->arate / 90000 ) - ( buf->start * job->arate / 90000 ) );
         m->sum_dur += duration;
     }
     else
Index: libhb/render.c
===================================================================
--- libhb/render.c	(revision 1022)
+++ libhb/render.c	(working copy)
@@ -9,6 +9,10 @@
 #include "ffmpeg/avcodec.h"
 #include "ffmpeg/swscale.h"
 
+/* Used for keeping track of when frames are dropped by detelecine. */
+uint32_t dropped_frames = 0;
+uint32_t extended_durations = 0;
+
 struct hb_work_private_s
 {
     hb_job_t * job;
@@ -225,6 +229,9 @@
             }            
             else if( result == FILTER_DROP )
             {
+                /* A drop means detelecine's latched its teeth onto
+                   something and has duped frames to discard. */
+                dropped_frames++;
                 hb_fifo_get( pv->subtitle_queue );
                 buf_tmp_in = NULL;
                 break;
@@ -271,6 +278,24 @@
     /* Set output to render buffer */
     (*buf_out) = buf_render;
 
+    if ( buf_tmp_in )
+    {
+        /* Pass VFR NTSC IVTC frame durations on through the workflow.
+           I totally wish I could slip some cryptic abbreviations into
+           the last sentence.                                          
+           
+           (90090000 / 120000) is the rational equivalent of 750.75.
+           That's the number of ticks by which a 23.976 frame is 
+           longer than a 29.97 frame. God I hate fractions.
+        */
+
+         if ( (dropped_frames) &&( dropped_frames * 4 ) > extended_durations )
+         {
+             buf_render->stop = buf_render->stop + (90090000 / 120000);
+             extended_durations++;
+         }
+    }
+
     if( buf_tmp_in == NULL )
     {
         /* Teardown and cleanup buffers if we are emitting NULL */
@@ -297,6 +322,10 @@
 
 void renderClose( hb_work_object_t * w )
 {
+    hb_log("RENDER: dropped frames: %i (%i ticks)", dropped_frames, (dropped_frames * 3003) );
+    hb_log("RENDER: extended frames: %i (%i ticks)", extended_durations, (extended_durations * (90090000 / 120000) ) );
+    hb_log("RENDER: Lost time: %i ticks", (dropped_frames * 3003) - (extended_durations *  (90090000 / 120000) ) );
+    
     hb_work_private_t * pv = w->private_data;   
         
     /* Cleanup subtitle queue */
I'm planning on cleaning this stuff up soon so it can all be optional and only run when a job->vfr boolean's true. Then, I'd like to commit it. Not because it's ready for regular usage yet, but because it needs more testing than I can do alone.

If you have audio sync problems, see if this code from eddyg works as a temporary fix to at least hide some of the messages:
http://pastebin.ca/742810
eddyg
Veteran User
Posts: 798
Joined: Mon Apr 23, 2007 3:34 am

Post by eddyg »

Well I read it :)

Interesting ideas about accumulating the time deficit and then catching up later. I'll have to see it to see whether it was a workable solution. So I'm fine with a commit in order to allow us to at least play with it.

One way of having a workspace in render may be to have a 4 buffer fifo "working space" in render. You use this instead of the output fifo, when it fills you pop one off the top and put it on the output fifo. That will give you 4 local buffers to play with.

Modify fifo.c to give you direct access to the buffers, and the ability to remove a buffer from the middle of the fifo list.

e.g.

buf = hb_fifo_see_inside(fifo, location);
buf = hb_fifo_remove_inside(fifo, location);

When you get an incoming empty buffer, you sit in a loop and flush the "working space" to the output fifo.

Cheers, Ed.
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Something worth a commitment?

Post by jbrjake »

Per my last post, I've gone and set up that prior diff as a CLI option. Before, it was running as default behavior -- dropped frames were always dropped, etc. And you had to force fps to 29.97 and enable detelecine by hand.

Now, all you need to do is add --vfr or -V to the command line string and it'll take care of the rest in the core lib's work.c. When --detelecine is specified, it knows not to add it twice.

All dynaflash would need to do for the MacGUI is have a checkbox to set the value of job->vfr to 1.

I've tested this with several sources. Fooly Cooly seems to stay in sync (hard to tell with dubbed anime of course), as does another hard telecined source, It's Always Sunny in Philadelphia. Progressive stuff ends up being 29.97 with duped frames, but it's in sync...

Now, remember, pullup is a stateless ivtc filter. This means it does not strictly obey a 3:2 pattern. It does not always drop 1 frame out of 5. If a shot is static, it will be 30fps, because pullup doesn't see combing it can correct. It can be pretty sensitive. So sometimes it'll leave duped frames when it notices slight differences in them. Sometimes this is a little annoying (when the differences are just blocks from the DVD's compression) but in the end it's worthwhile. Why? Because this makes some pretty cool stuff possible:

The video shown behind the end credits of Kids in the Hall is telecined. The credits themselves are interlaced. In the moments between credits appearing on the screen, or when the credits aren't scrolling, pullup goes to 24fps. Then, during the parts where the credits are scrolling, it goes back to 30fps, so you can deinterlace the text.

As I hinted the other week, I'd like to consider checking this into the svn, to make testing easier, even though it's definitely not ready for prime time.

pastebin: http://pastebin.ca/751598

Code: Select all

Index: test/test.c
===================================================================
--- test/test.c	(revision 1034)
+++ test/test.c	(working copy)
@@ -67,6 +67,7 @@
 static int    turbo_opts_enabled = 0;
 static char * turbo_opts = "ref=1:subme=1:me=dia:analyse=none:trellis=0:no-fast-pskip=0:8x8dct=0";
 static int    largeFileSize = 0;
+static int    vfr           = 0;
 
 /* Exit cleanly on Ctrl-C */
 static volatile int die = 0;
@@ -633,6 +634,9 @@
                 job->maxWidth = maxWidth;
             if (maxHeight)
                 job->maxHeight = maxHeight;
+            
+            if (vfr)
+                job->vfr = 1;
 	
             if( subtitle_force )
             {
@@ -928,14 +932,15 @@
 	"\n"
 	
 	
-    "### Advanced H264 Options----------------------------------------------------\n\n"
+    "### Advanced Options---------------------------------------------------------\n\n"
     "    -x, --x264opts <string> Specify advanced x264 options in the\n"
     "                            same style as mencoder:\n"
     "                            option1=value1:option2=value2\n"
     "    -T, --turbo             When using 2-pass use the turbo options\n"
     "                            on the first pass to improve speed\n"
     "                            (only works with x264, affects PSNR by about 0.05dB,\n"
-    "                            and increases first pass speed two to four times)\n");
+    "                            and increases first pass speed two to four times)\n"
+    "    -V, --vfr               Perform variable framerate detelecine\n");
 }
 
 /****************************************************************************
@@ -991,7 +996,7 @@
             { "crf",         no_argument,       NULL,    'Q' },
             { "x264opts",    required_argument, NULL,    'x' },
             { "turbo",       no_argument,       NULL,    'T' },
-            
+            { "vfr",         no_argument,       NULL,    'V' },
             { "maxHeight",   required_argument, NULL,    'Y' },
             { "maxWidth",    required_argument, NULL,    'X' },
 			
@@ -1002,7 +1007,7 @@
         int c;
 
         c = getopt_long( argc, argv,
-                         "hvuC:f:4i:o:t:Lc:ma:6:s:UFN:e:E:2d789gpP::w:l:n:b:q:S:B:r:R:Qx:TY:X:",
+                         "hvuC:f:4i:o:t:Lc:ma:6:s:UFN:e:E:2d789gpP::w:l:n:b:q:S:B:r:R:Qx:TY:X:V",
                          long_options, &option_index );
         if( c < 0 )
         {
@@ -1315,6 +1320,9 @@
             case 'X':
                 maxWidth = atoi (optarg );
                 break;
+            case 'V':
+                vfr = 1;
+                break;
 				
             default:
                 fprintf( stderr, "unknown option (%s)\n", argv[optind] );
Index: libhb/detelecine.c
===================================================================
--- libhb/detelecine.c	(revision 1034)
+++ libhb/detelecine.c	(working copy)
@@ -975,7 +975,7 @@
         }
         else
         {
-            goto output_frame;
+            goto discard_frame;
         }
     }
     
@@ -987,7 +987,7 @@
         
 		if (!frame) 
         {
-            goto output_frame;
+            goto discard_frame;
         }
 		if( frame->length < 2 ) 
         {
@@ -995,19 +995,19 @@
 
 			if( !(buf_in->flags & PIC_FLAG_REPEAT_FIRST_FIELD) )
             {
-                goto output_frame;
+                goto discard_frame;
             }
 			
             frame = pullup_get_frame( ctx );
 			
             if( !frame ) 
             {
-                goto output_frame;
+                goto discard_frame;
             }
 			if( frame->length < 2 ) 
             {
 				pullup_release_frame( frame );
-                goto output_frame;
+                goto discard_frame;
 			}
 		}
     }
@@ -1034,6 +1034,15 @@
 output_frame:    
     *buf_out = pv->buf_out;    
     return FILTER_OK;
+
+/* This and all discard_frame calls shown above are
+   the result of me restoring the functionality in
+   pullup that huevos_rancheros disabled because
+   HB couldn't handle it.                           */
+discard_frame:    
+    *buf_out = pv->buf_out;
+    return FILTER_DROP;
+
 }
 
 
Index: libhb/muxmp4.c
===================================================================
--- libhb/muxmp4.c	(revision 1034)
+++ libhb/muxmp4.c	(working copy)
@@ -393,7 +393,14 @@
         /* Because we use the audio samplerate as the timescale,
            we have to use potentially variable durations so the video
            doesn't go out of sync */
-        duration    = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
+        if ( job->vfr )
+        {
+            duration    = ( ( buf->stop * job->arate / 90000 ) - ( buf->start * job->arate / 90000 ) );
+        }
+        else
+        {
+            duration    = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
+        }
         m->sum_dur += duration;
     }
     else
Index: libhb/render.c
===================================================================
--- libhb/render.c	(revision 1034)
+++ libhb/render.c	(working copy)
@@ -9,6 +9,10 @@
 #include "ffmpeg/avcodec.h"
 #include "ffmpeg/swscale.h"
 
+/* Used for keeping track of when frames are dropped by detelecine. */
+uint32_t dropped_frames = 0;
+uint32_t extended_durations = 0;
+
 struct hb_work_private_s
 {
     hb_job_t * job;
@@ -225,8 +229,20 @@
             }            
             else if( result == FILTER_DROP )
             {
-                hb_fifo_get( pv->subtitle_queue );
-                buf_tmp_in = NULL;
+                /* A drop means detelecine's latched its teeth onto
+                   something and has duped frames to discard. */
+                
+                if ( job->vfr )
+                {
+                    dropped_frames++;
+                    hb_fifo_get( pv->subtitle_queue );
+                    buf_tmp_in = NULL;                    
+                }
+                else
+                {
+                    buf_tmp_in = buf_tmp_out;
+                }
+                
                 break;
             }
         }
@@ -271,6 +287,24 @@
     /* Set output to render buffer */
     (*buf_out) = buf_render;
 
+    if ( buf_tmp_in )
+    {
+        /* Pass VFR NTSC IVTC frame durations on through the workflow.
+           I totally wish I could slip some cryptic abbreviations into
+           the last sentence.                                          
+           
+           (90090000 / 120000) is the rational equivalent of 750.75.
+           That's the number of ticks by which a 23.976 frame is 
+           longer than a 29.97 frame. God I hate fractions.
+        */
+
+         if ( (dropped_frames) &&( dropped_frames * 4 ) > extended_durations )
+         {
+             buf_render->stop = buf_render->stop + (90090000 / 120000);
+             extended_durations++;
+         }
+    }
+
     if( buf_tmp_in == NULL )
     {
         /* Teardown and cleanup buffers if we are emitting NULL */
@@ -297,6 +331,10 @@
 
 void renderClose( hb_work_object_t * w )
 {
+    hb_log("render: dropped frames: %i (%i ticks)", dropped_frames, (dropped_frames * 3003) );
+    hb_log("render: extended frames: %i (%i ticks)", extended_durations, (extended_durations * (90090000 / 120000) ) );
+    hb_log("render: Lost time: %i ticks", (dropped_frames * 3003) - (extended_durations *  (90090000 / 120000) ) );
+    
     hb_work_private_t * pv = w->private_data;   
         
     /* Cleanup subtitle queue */
Index: libhb/work.c
===================================================================
--- libhb/work.c	(revision 1034)
+++ libhb/work.c	(working copy)
@@ -160,6 +160,27 @@
             job->crop[0], job->crop[1], job->crop[2], job->crop[3] );
     hb_log( " + grayscale %s", job->grayscale ? "on" : "off" );
     
+    if ( job->vfr )
+    {
+        job->vrate_base = 900900;
+
+        int detelecine_present = 0;        
+        if ( job->filters )
+        {
+            for( i = 0; i < hb_list_count( job->filters ); i++ )
+            {
+                hb_filter_object_t * filter = hb_list_item( job->filters, i );
+                if (filter->id == FILTER_DETELECINE)
+                    detelecine_present = 1;
+            }            
+        }
+        
+        if (!detelecine_present)
+            hb_list_add( job->filters, &hb_filter_detelecine );
+        
+        hb_log("work: VFR mode -- Switching FPS to 29.97 and detelecining.");
+    }
+    
     if( job->filters )
     {
         hb_log(" + filters");
Index: libhb/common.h
===================================================================
--- libhb/common.h	(revision 1034)
+++ libhb/common.h	(working copy)
@@ -172,7 +172,8 @@
     int             crf;
     char            *x264opts;
     int             areBframes;
-    
+    int             vfr;
+
     /* Audio tracks:
          audios:          Indexes in hb_title_t's audios list, starting from 0.
                           -1 indicates the end of the list
dynaflash
Veteran User
Posts: 3820
Joined: Thu Nov 02, 2006 8:19 pm

Post by dynaflash »

For what its worth, as soon as you check this into the svn, I have the macgui ready to roll. When vfr is checked in picture preview window heres what happens:
-Detelecine checkbox gets checked and disabled
-Framerate dropdown in the main window gets set to 29.976 fps and it gets disabled.
-job->vfr gets set to "1".
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Are we there yet?

Post by jbrjake »

Current state:

* Hard telecined content:
VFR works great. TV on DVD, 1080i transport streams, whatever. If it's all video, VFR IVTC works fine.

* Soft telecined content:
VFR works some of the time. For some progressive DVDs it leaves behind repeated frames, for others it discards them. Example: Fear and Loathing comes out at 29.97, Run Ronnie Run seems to enjoy 26fps. And both seem to display the same picture flags, so I really don't know what's up. Until this is fixed, I can't advocate for VFR to be on by default, the whole point of this thread. However, it does handle soft telecined content with equanimity. You're not going to get weird audio problems.

* Mixed content:
It's a crapshoot. Sometimes it will work perfectly, sometimes the sync and durations will be horribly off. The worst cases seem to happen with the worst content. For example, there's this crappy old anime series called Blue Seed, and that comes out terribly -- audio is way ahead of video. On the other hand, the iPod killer vob comes out pretty well. JFK's soundbite is off sync because of pts gaps at the beginning, but by the time Tom Hanks is speaking, audio and video are running the same, and this stays true during the scene at the White House with Al Franken. In fact, the only real issue with the iPod killer is right when Tom Hanks begins his monologue. Before the camera shows him, he's giving a voiceover while the camera pans over blended, translucent shots of an old marble frieze. This bit makes the video stutter slightly.

With some help from eddyg, I got past one of the niggling details that had been bugging me this whole time: not buffering frames. Until now I've been extending the durations of 4 frames following a dropped frame. Buuuuut, telecined sequences begin two frames before a dropped frame. Now, by adding a 2-frame fifo delaying the render workflow, it's possible to extend the right frames' durations on the fly. When render inputs a dropped frame, it's outputting 2 frames before, so it's able to start extending the correct sequence to 23.986 fps.

Also, one enduring problem has been float->int conversions. A 29.976 frame is 3003 ticks. When one is dropped, those 3003 ticks have to be spread amongst the two frames preceding the drop and the two frames succeeding it. 3003/4 = 750.75, and I was losing those .75 ticks. That led to around a half second delay over the course of an hour of video. Now, instead of trying to add 750.75 to each, I'm using a slight variation on my original technique: adding 751 to 3 and 750 to 1.

Obviously, further improvements can be made. But I do think it's ready for checking in, and this time that's a serious threat ;>

Right now, the most useful purpose for VFR will be TV captures. Almost anything worth recording in 1080i is actually hard telecined 1080p content, and currently I see people deinterlacing that stuff. Not a best practice. I'm also finding it works very well with hard telecined TV shows on DVD, like News Radio and It's Always Sunny in Philadelphia.

http://pastebin.ca/767324

Code: Select all

Index: test/test.c
===================================================================
--- test/test.c	(revision 1034)
+++ test/test.c	(working copy)
@@ -67,6 +67,7 @@
 static int    turbo_opts_enabled = 0;
 static char * turbo_opts = "ref=1:subme=1:me=dia:analyse=none:trellis=0:no-fast-pskip=0:8x8dct=0";
 static int    largeFileSize = 0;
+static int    vfr           = 0;
 
 /* Exit cleanly on Ctrl-C */
 static volatile int die = 0;
@@ -633,6 +634,9 @@
                 job->maxWidth = maxWidth;
             if (maxHeight)
                 job->maxHeight = maxHeight;
+            
+            if (vfr)
+                job->vfr = 1;
 	
             if( subtitle_force )
             {
@@ -928,14 +932,15 @@
 	"\n"
 	
 	
-    "### Advanced H264 Options----------------------------------------------------\n\n"
+    "### Advanced Options---------------------------------------------------------\n\n"
     "    -x, --x264opts <string> Specify advanced x264 options in the\n"
     "                            same style as mencoder:\n"
     "                            option1=value1:option2=value2\n"
     "    -T, --turbo             When using 2-pass use the turbo options\n"
     "                            on the first pass to improve speed\n"
     "                            (only works with x264, affects PSNR by about 0.05dB,\n"
-    "                            and increases first pass speed two to four times)\n");
+    "                            and increases first pass speed two to four times)\n"
+    "    -V, --vfr               Perform variable framerate detelecine\n");
 }
 
 /****************************************************************************
@@ -991,7 +996,7 @@
             { "crf",         no_argument,       NULL,    'Q' },
             { "x264opts",    required_argument, NULL,    'x' },
             { "turbo",       no_argument,       NULL,    'T' },
-            
+            { "vfr",         no_argument,       NULL,    'V' },
             { "maxHeight",   required_argument, NULL,    'Y' },
             { "maxWidth",    required_argument, NULL,    'X' },
 			
@@ -1002,7 +1007,7 @@
         int c;
 
         c = getopt_long( argc, argv,
-                         "hvuC:f:4i:o:t:Lc:ma:6:s:UFN:e:E:2d789gpP::w:l:n:b:q:S:B:r:R:Qx:TY:X:",
+                         "hvuC:f:4i:o:t:Lc:ma:6:s:UFN:e:E:2d789gpP::w:l:n:b:q:S:B:r:R:Qx:TY:X:V",
                          long_options, &option_index );
         if( c < 0 )
         {
@@ -1315,6 +1320,9 @@
             case 'X':
                 maxWidth = atoi (optarg );
                 break;
+            case 'V':
+                vfr = 1;
+                break;
 				
             default:
                 fprintf( stderr, "unknown option (%s)\n", argv[optind] );
Index: libhb/detelecine.c
===================================================================
--- libhb/detelecine.c	(revision 1034)
+++ libhb/detelecine.c	(working copy)
@@ -975,7 +975,7 @@
         }
         else
         {
-            goto output_frame;
+            goto discard_frame;
         }
     }
     
@@ -987,7 +987,7 @@
         
 		if (!frame) 
         {
-            goto output_frame;
+            goto discard_frame;
         }
 		if( frame->length < 2 ) 
         {
@@ -995,19 +995,19 @@
 
 			if( !(buf_in->flags & PIC_FLAG_REPEAT_FIRST_FIELD) )
             {
-                goto output_frame;
+                goto discard_frame;
             }
 			
             frame = pullup_get_frame( ctx );
 			
             if( !frame ) 
             {
-                goto output_frame;
+                goto discard_frame;
             }
 			if( frame->length < 2 ) 
             {
 				pullup_release_frame( frame );
-                goto output_frame;
+                goto discard_frame;
 			}
 		}
     }
@@ -1034,6 +1034,15 @@
 output_frame:    
     *buf_out = pv->buf_out;    
     return FILTER_OK;
+
+/* This and all discard_frame calls shown above are
+   the result of me restoring the functionality in
+   pullup that huevos_rancheros disabled because
+   HB couldn't handle it.                           */
+discard_frame:    
+    *buf_out = pv->buf_out;
+    return FILTER_DROP;
+
 }
 
 
Index: libhb/muxmp4.c
===================================================================
--- libhb/muxmp4.c	(revision 1034)
+++ libhb/muxmp4.c	(working copy)
@@ -393,7 +393,14 @@
         /* Because we use the audio samplerate as the timescale,
            we have to use potentially variable durations so the video
            doesn't go out of sync */
-        duration    = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
+        if ( job->vfr )
+        {
+            duration    = ( ( buf->stop * job->arate / 90000 ) - ( buf->start * job->arate / 90000 ) );
+        }
+        else
+        {
+            duration    = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
+        }
         m->sum_dur += duration;
     }
     else
Index: libhb/render.c
===================================================================
--- libhb/render.c	(revision 1047)
+++ libhb/render.c	(working copy)
@@ -19,6 +19,10 @@
     AVPicture            pic_tmp_out;        
     hb_buffer_t        * buf_scale;
     hb_fifo_t          * subtitle_queue;
+    hb_fifo_t          * delay_queue;
+    int                  frames_to_extend;
+    int                  dropped_frames;
+    int                  extended_frames;
 };
 
 int  renderInit( hb_work_object_t *, hb_job_t * );
@@ -153,10 +157,14 @@
     hb_job_t   * job   = pv->job;
     hb_title_t * title = job->title;
     hb_buffer_t * in = *buf_in, * buf_tmp_in = *buf_in;
+    hb_buffer_t * ivtc_buffer = NULL;
     
     if(!in->data)
     {
-        /* If the input buffer is end of stream, send out an empty one to the next stage as well. */
+        /* If the input buffer is end of stream, send out an empty one
+         * to the next stage as well. Note that this will result in us
+         * losing the current contents of the delay queue.
+         */
        *buf_out = hb_buffer_init(0);
        return HB_WORK_OK;
     }
@@ -227,6 +235,11 @@
             {
                 hb_fifo_get( pv->subtitle_queue );
                 buf_tmp_in = NULL;
+                if( job->vfr )
+                {
+                    pv->frames_to_extend += 4;
+                    pv->dropped_frames++;
+                }
                 break;
             }
         }
@@ -291,7 +304,50 @@
         memcpy( buf_render->data, buf_tmp_in->data, buf_render->size );
         hb_buffer_copy_settings( buf_render, buf_tmp_in );
     }
+    
+    if (*buf_out)
+    {
+        hb_fifo_push( pv->delay_queue, *buf_out );
+        *buf_out = NULL;        
+    }
 
+    /*
+     * Keep the last three frames in our queue, this ensures that we have the last
+     * two always in there should we need to rewrite the durations on them.
+     */
+    if( hb_fifo_size( pv->delay_queue ) >= 3 )
+    {
+        *buf_out = hb_fifo_get( pv->delay_queue );
+    } 
+
+    if( *buf_out )
+    {
+        if( pv->frames_to_extend )
+        {
+            /*
+             * A frame's been dropped by VFR detelecine.
+             * Gotta make up the lost time. This will also
+             * slow down the video to 23.976fps.
+             * The dropped frame ran for 3003 ticks, so
+             * divvy it up amongst the 4 frames left behind.
+             * This is what the delay_queue is for;
+             * telecined sequences start 2 frames before
+             * the dropped frame, so to slow down the right
+             * ones you need a 2 frame delay between
+             * reading input and writing output.
+             */
+            ivtc_buffer = *buf_out;
+            
+            if (pv->frames_to_extend % 4)
+                ivtc_buffer->stop += 751;
+            else
+                ivtc_buffer->stop += 750;
+                
+            pv->frames_to_extend--;
+            pv->extended_frames++;
+        }
+    }
+
     return HB_WORK_OK;
 }
 
@@ -299,12 +355,21 @@
 {
     hb_work_private_t * pv = w->private_data;   
         
+    hb_log("render: dropped frames: %i (%i ticks)", pv->dropped_frames, (pv->dropped_frames * 3003) );
+    hb_log("render: extended frames: %i (%i ticks)", pv->extended_frames, ( ( pv->extended_frames / 4 ) * 3003 ) );
+    hb_log("render: Lost time: %i frames (%i ticks)", (pv->dropped_frames * 4) - (pv->extended_frames), (pv->dropped_frames * 3003) - ( ( pv->extended_frames / 4 ) * 3003 ) );
+
     /* Cleanup subtitle queue */
     if( pv->subtitle_queue )
     {
         hb_fifo_close( &pv->subtitle_queue );
     }
     
+    if( pv->delay_queue )
+    {
+        hb_fifo_close( &pv->delay_queue );
+    }
+   
     /* Cleanup filters */
     /* TODO: Move to work.c? */
     if( pv->job->filters )
@@ -352,6 +417,10 @@
     
     /* Setup FIFO queue for subtitle cache */
     pv->subtitle_queue = hb_fifo_init( 8 );    
+    pv->delay_queue = hb_fifo_init( 8 );
+    pv->frames_to_extend = 0;
+    pv->dropped_frames = 0;
+    pv->extended_frames = 0;
     
     /* Setup filters */
     /* TODO: Move to work.c? */
Index: libhb/work.c
===================================================================
--- libhb/work.c	(revision 1034)
+++ libhb/work.c	(working copy)
@@ -160,6 +160,27 @@
             job->crop[0], job->crop[1], job->crop[2], job->crop[3] );
     hb_log( " + grayscale %s", job->grayscale ? "on" : "off" );
     
+    if ( job->vfr )
+    {
+        job->vrate_base = 900900;
+
+        int detelecine_present = 0;        
+        if ( job->filters )
+        {
+            for( i = 0; i < hb_list_count( job->filters ); i++ )
+            {
+                hb_filter_object_t * filter = hb_list_item( job->filters, i );
+                if (filter->id == FILTER_DETELECINE)
+                    detelecine_present = 1;
+            }            
+        }
+        
+        if (!detelecine_present)
+            hb_list_add( job->filters, &hb_filter_detelecine );
+        
+        hb_log("work: VFR mode -- Switching FPS to 29.97 and detelecining.");
+    }
+    
     if( job->filters )
     {
         hb_log(" + filters");
Index: libhb/common.h
===================================================================
--- libhb/common.h	(revision 1034)
+++ libhb/common.h	(working copy)
@@ -172,7 +172,8 @@
     int             crf;
     char            *x264opts;
     int             areBframes;
-    
+    int             vfr;
+
     /* Audio tracks:
          audios:          Indexes in hb_title_t's audios list, starting from 0.
                           -1 indicates the end of the list
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Post by jbrjake »

dynaflash
Veteran User
Posts: 3820
Joined: Thu Nov 02, 2006 8:19 pm

Post by dynaflash »

MacGui updated to handle jbrjakes vfr svn rev 1052. Way to go jbrjake!
cvk_b
Veteran User
Posts: 527
Joined: Sun Mar 18, 2007 2:11 am

Post by cvk_b »

Seriously cool commit.
Cavalicious
Moderator
Posts: 1804
Joined: Mon Mar 26, 2007 12:07 am

Post by Cavalicious »

Loving it so far...doubled my average fps (12) with HD content. Much cleaner vebose also:

Code: Select all

Macintosh:HandBrake.r1055 Cavalicious$ ./HandBrakeCLI -i /Volumes/Encode/Test_MPS_Stream.mpg  -o /Volumes/New\ Downloads/HD_Video2.m4v  -w 960 -l 540 -F -q 0.65 -Q -e x264 -V -E facc  -B 384 -R 44.1 -6 stereo -v -x keyint=300:keyint-min=30:bframes=6:ref=3:mixed-refs=1:subq=5:me=umh:no-fast-pskip=1:trellis=0:no-dct-decimate=1:vbv-maxrate=4500:vbv-bufsize=3000
[22:03:32] hb_init: checking cpu count
[22:03:32] hb_init: starting libhb thread
[22:03:32] thread b0081000 started ("libhb")
HandBrake 0.9.1 (2007111200) - http://handbrake.m0k.org/
2 CPUs detected
Opening /Volumes/Encode/Test_MPS_Stream.mpg...
[22:03:32] hb_scan: path=/Volumes/Encode/Test_MPS_Stream.mpg, title_index=1
[22:03:32] thread b0103000 started ("scan")
[22:03:32] scan: trying to open with libdvdread
ERROR: dvd: DVDOpen failed (/Volumes/Encode/Test_MPS_Stream.mpg)[22:03:33] scan: trying to open as MPEG-2 Stream
[22:03:33] scan: decoding previews for title 0
[22:03:33] scan: preview 1
[22:03:33] scan: AC3, rate=48000Hz, bitrate=384000
Scanning title 0...
[22:03:33] scan: preview 2
Scanning title 0...
[22:03:33] scan: preview 3
Scanning title 0...
[22:03:33] scan: preview 4
[22:03:34] scan: preview 5
Scanning title 0...
[22:03:34] scan: preview 6
Scanning title 0...
[22:03:34] scan: preview 7
[22:03:34] scan: preview 8
Scanning title 0...
[22:03:34] scan: preview 9
Scanning title 0...
[22:03:35] scan: preview 10
[22:03:35] scan: 1920x1088, 29.970 fps, autocrop = 0/8/0/0
[22:03:35] hb_stream_update_audio: id=80bd, lang=Unknown (AC3) (2.0 ch), 3cc=und, rate = 48000, bitrate = 384000, flags = 0x2 (2)
[22:03:35] scan: title (0) job->width:1920, job->height:1072
[22:03:35] thread b0103000 exited ("scan")
[22:03:35] thread b0103000 joined ("scan")
[22:03:35] libhb: scan thread found 1 valid title(s)
+ title 0:
  + vts 0, ttn 0, cells 0->0 (0 blocks)
  + duration: 00:04:58
  + size: 1920x1088, aspect: 1.78, 29.970 fps
  + autocrop: 0/8/0/0
  + chapters:
    + 1: cells 0->0, 0 blocks, duration 00:04:58
  + audio tracks:
    + 1, Unknown (AC3) (2.0 ch), 48000Hz, 384000bps
  + subtitle tracks:
Applying the following x264 options: keyint=300:keyint-min=30:bframes=6:ref=3:mixed-refs=1:subq=5:me=umh:no-fast-pskip=1:trellis=0:no-dct-decimate=1:vbv-maxrate=4500:vbv-bufsize=3000
[22:03:35] thread b0103000 started ("work")
[22:03:35] 1 job(s) to process
[22:03:35] starting job
[22:03:35]  + device /Volumes/Encode/Test_MPS_Stream.mpg
[22:03:35]  + title 0, chapter(s) 1 to 1
[22:03:35]  + 1920x1088 -> 960x540, crop 0/8/0/0
[22:03:35]  + grayscale off
[22:03:35] work: VFR mode -- Switching FPS to 29.97 and detelecining.
[22:03:35]  + filters
[22:03:35]    + Detelecine (pullup) (default settings)
[22:03:35]  + 29.970 fps, video quality 0.65
[22:03:35]  + PixelRatio: 0, width:960, height: 540
[22:03:35]  + encoder x264
[22:03:35]  + audio 384 kbps, 44100 Hz
[22:03:35]  + encoder faac
[22:03:35]    + 80bd, Unknown (AC3) (2.0 ch)
[22:03:35]      + Requested mixdown: Stereo (HB_AMIXDOWN_STEREO)
[22:03:35]      + Actual mixdown: Stereo (HB_AMIXDOWN_STEREO)
[22:03:35] thread b0185000 started ("reader")
[22:03:35]  + output: /Volumes/New Downloads/HD_Video2.m4v
[22:03:35] thread b0207000 started ("muxer")
[22:03:35] thread b0289000 started ("MPEG-2 decoder (libmpeg2)")
ERROR: dvd: DVDOpen failed (/Volumes/Encode/Test_MPS_Stream.mpg)[22:03:35] thread b030b000 started ("Renderer")
[22:03:35] encx264: keyint-min: 30, keyint-max: 300
[22:03:35] encx264: Encoding at constant RF 17.850002
[22:03:35] encx264: opening libx264 (pass 0)
x264 [warning]: width or height not divisible by 16 (960x540), compression will suffer.
x264 [info]: using cpu capabilities: MMX MMXEXT SSE SSE2 
[22:03:35] thread b038d000 started ("H.264/AVC encoder (libx264)")
No accelerated IMDCT transform found
[22:03:35] thread b040f000 started ("AC3 decoder")
[22:03:35] thread b0491000 started ("AAC encoder (libfaac)")
[22:03:35] sync: expecting 8984 video frames
[22:03:35] sync: first pts is 13206
Encoding: task 1 of 1, 98.96 % (14.25 fps, avg 12.02 fps, ETA 00h00m07s)[22:15:55] reader: done
[22:15:55] thread b0185000 exited ("reader")
Encoding: task 1 of 1, 99.76 % (13.69 fps, avg 12.03 fps, ETA 00h00m01s)[22:16:00] sync: got 8965 frames, 8984 expected
Encoding: task 1 of 1, 99.79 % (13.69 fps, avg 12.03 fps, ETA 00h00m01s)[22:16:02] thread b040f000 exited ("AC3 decoder")
[22:16:02] thread b0491000 exited ("AAC encoder (libfaac)")
[22:16:02] thread b0289000 exited ("MPEG-2 decoder (libmpeg2)")
[22:16:02] thread b030b000 exited ("Renderer")
[22:16:02] thread b0289000 joined ("MPEG-2 decoder (libmpeg2)")
[22:16:02] thread b030b000 joined ("Renderer")
[22:16:02] render: dropped frames: 1778 (5339334 ticks)
[22:16:02] thread b038d000 exited ("H.264/AVC encoder (libx264)")
[22:16:02] render: extended frames: 7111 (5336331 ticks)
[22:16:02] render: Lost time: 1 frames (3003 ticks)
[22:16:02] fifo_close: trashing 0 buffer(s)
[22:16:02] fifo_close: trashing 2 buffer(s)
[22:16:02] thread b038d000 joined ("H.264/AVC encoder (libx264)")
x264 [info]: slice I:82    Avg QP:17.46  size: 55313  PSNR Mean Y:48.01 U:49.58 V:50.12 Avg:48.52 Global:48.36
x264 [info]: slice P:2324  Avg QP:19.23  size: 23108  PSNR Mean Y:45.71 U:46.66 V:47.83 Avg:46.14 Global:45.92
x264 [info]: slice B:4773  Avg QP:21.13  size:  5316  PSNR Mean Y:44.99 U:46.15 V:47.45 Avg:45.49 Global:45.32
x264 [info]: mb I  I16..4: 23.9%  0.0% 76.1%
x264 [info]: mb P  I16..4:  9.0%  0.0%  7.0%  P16..4: 45.2% 24.1% 10.9%  0.0%  0.0%    skip: 3.8%
x264 [info]: mb B  I16..4:  0.3%  0.0%  0.4%  B16..8: 24.6%  2.2%  4.9%  direct:11.6%  skip:56.0%
x264 [info]: ref P  78.1% 13.1%  8.8%
x264 [info]: ref B  88.7%  7.1%  4.2%
x264 [info]: SSIM Mean Y:0.9866722
x264 [info]: PSNR Mean Y:45.253 U:46.351 V:47.606 Avg:45.737 Global:45.535 kb/s:2792.44
[22:16:02] thread b040f000 joined ("AC3 decoder")
[22:16:02] thread b0491000 joined ("AAC encoder (libfaac)")
[22:16:02] thread b0185000 joined ("reader")
Muxing: 0.00 %[22:16:02] mux: file size, 89439107 bytes
[22:16:02] mux: track 0, 83589089 bytes, 2238.19 kbps
[22:16:02] mux: track 1, 5674824 bytes, 151.95 kbps
[22:16:02] mux: overhead, 8.74 bytes per frame
[22:16:02] thread b0207000 exited ("muxer")
[22:16:02] thread b0207000 joined ("muxer")
[22:16:02] fifo_close: trashing 0 buffer(s)
[22:16:02] fifo_close: trashing 0 buffer(s)
[22:16:02] fifo_close: trashing 0 buffer(s)
[22:16:02] fifo_close: trashing 1 buffer(s)
[22:16:02] fifo_close: trashing 1 buffer(s)
[22:16:02] fifo_close: trashing 0 buffer(s)
[22:16:02] fifo_close: trashing 0 buffer(s)
[22:16:02] fifo_close: trashing 4 buffer(s)
[22:16:02] fifo_close: trashing 17 buffer(s)
[22:16:02] Freed 1 buffers of size 512
[22:16:02] Freed 2 buffers of size 1024
[22:16:02] Freed 2048 buffers of size 2048
[22:16:02] Freed 0 buffers of size 4096
[22:16:02] Freed 0 buffers of size 8192
[22:16:02] Freed 35 buffers of size 16384
[22:16:02] Freed 0 buffers of size 32768
[22:16:02] Freed 55 buffers of size 3133440
[22:16:02] Allocated 177109504 bytes of buffers on this pass and Freed 177109504 bytes, 0 bytes leaked
[22:16:02] thread b0103000 exited ("work")
[22:16:02] thread b0103000 joined ("work")
[22:16:02] libhb: work result = 0

Rip done!
[22:16:03] thread b0081000 exited ("libhb")
[22:16:03] thread b0081000 joined ("libhb")
HandBrake has exited.
Thanks
evilpenguin
Posts: 21
Joined: Fri Oct 12, 2007 2:44 am

720p?

Post by evilpenguin »

I appologize if my search just missed it but has there been any discussion of how this relates to HDTV 720p60->720p24?
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Re: 720p?

Post by jbrjake »

evilpenguin wrote:I appologize if my search just missed it but has there been any discussion of how this relates to HDTV 720p60->720p24?
I haven't tested on any 720p60 but I'd imagine that the pullup filter would have trouble detecting which frames were duplicated.
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

VFR & B-frames

Post by jbrjake »

Cav pointed out to me a week or two ago that when you use VFR with B-frames in x264, you end up with weirdly stuttering frames, as if it was running at half speed.

After several false starts, I realized the problem: discontinuous time stamps. When dropping frames in render.c, I wasn't renumbering the remaining frames.

So if input was:

Code: Select all

1: 0 -> 3003
2: 3003 -> 6006
3: 6006 -> 9009
4: 9009 -> 12012
...and if the 2nd frame got dropped it'd just stay:

Code: Select all

1: 0 -> 3003
2: 6006 -> 9009
3: 9009 -> 12012
...but with extended durations:

Code: Select all

1: 0-> 3754
2: 6006 -> 9759
3: 9009 -> 12762
See, I was thinking that when the frames hit sync.c, they'd get renumbered automagically. This is not, in fact, the case. It didn't matter for VFR without b-frames, because the muxers don't bother recording the presentation time stamp or stop time, just the sample number and duration.

But in encx264.c, the render offsets for b-frames are calculated by subtracting the decode time from the presentation time. If the presentation times aren't continuous, it gets the render offset wrong, so there's a conflict between when one frame ends and the next starts. This results in some frames lasting for like 10ms, so the video plays jerkily.

The solution, of course, is renumbering the frames in render.c. If the frame to be presented at 3003 is dropped, then the next frame has to take its place, instead of leaving a timecode gap. My way of going about that is very crude; can anyone suggest something a little more elegant or efficient?

I have two 4-slot arrays. One for start time, one for stop time. On every pass through renderWork(), before there's a chance to drop a frame, I rotate the entries up one and load the newest at index 0. Then as the frames leave render.c, I renumber them with the oldest stored start and stop times. If the frame needs to be extended for VFR, I do it, and then edit the next-oldest stored start time to be the new extended stop time, and recalculate the next-oldest stored stop time to be its new start time plus 3003.

This means the times will go:

Code: Select all

1: 0 -> 3754
2: 3754 -> 7507
3: 7507 -> 11260
...in turn allowing the renderOffsets to be set right...

...in turn allowing players that read the CTTS offsets, like QuickTime and VLC, to play the video back without jerkiness. Or at least, so it seems in my testing. Cavalicious, can you try it out?

http://pastebin.ca/819381

Code: Select all

Index: libhb/render.c
===================================================================
--- libhb/render.c	(revision 1128)
+++ libhb/render.c	(working copy)
@@ -23,6 +23,8 @@
     int                  frames_to_extend;
     int                  dropped_frames;
     int                  extended_frames;
+    uint64_t             last_start[4];
+    uint64_t             last_stop[4];
 };
 
 int  renderInit( hb_work_object_t *, hb_job_t * );
@@ -191,7 +193,18 @@
     
     /* Setup render buffer */
     hb_buffer_t * buf_render = hb_buffer_init( 3 * job->width * job->height / 2 );  
-
+    
+    /* Cache frame start and stop times, so we can renumber
+       time stamps if dropping frames for VFR.              */ 
+    int i;
+    for( i = 3; i >= 1; i-- )
+    {
+        pv->last_start[i] = pv->last_start[i-1];
+        pv->last_stop[i] = pv->last_stop[i-1];
+    }
+    pv->last_start[0] = in->start;
+    pv->last_stop[0] = in->stop;
+    
     /* Apply filters */
     if( job->filters )
     {
@@ -322,6 +335,8 @@
 
     if( *buf_out )
     {
+        ivtc_buffer = *buf_out;
+
         if( pv->frames_to_extend )
         {
             /*
@@ -336,16 +351,28 @@
              * ones you need a 2 frame delay between
              * reading input and writing output.
              */
-            ivtc_buffer = *buf_out;
             
+            /* The 4th cached frame will be the to use. */
+            ivtc_buffer->start = pv->last_start[3];
+            ivtc_buffer->stop = pv->last_stop[3];
+
             if (pv->frames_to_extend % 4)
                 ivtc_buffer->stop += 751;
             else
                 ivtc_buffer->stop += 750;
-                
+            
+            /* Set the 3rd cached frame to start when this one stops,
+               and to stop 3003 ticks later -- a normal 29.97fps
+               length frame. If it needs to be extended as well to
+               make up lost time, it'll be handled on the next
+               loop through the renderer.                            */
+            pv->last_start[2] = ivtc_buffer->stop;
+            pv->last_stop[2] = ivtc_buffer->stop + 3003;
+            
             pv->frames_to_extend--;
             pv->extended_frames++;
         }
+
     }
 
     return HB_WORK_OK;
@@ -421,6 +448,8 @@
     pv->frames_to_extend = 0;
     pv->dropped_frames = 0;
     pv->extended_frames = 0;
+    pv->last_start[0] = 0;
+    pv->last_stop[0] = 0;
     
     /* Setup filters */
     /* TODO: Move to work.c? */
Cavalicious
Moderator
Posts: 1804
Joined: Mon Mar 26, 2007 12:07 am

Post by Cavalicious »

Will do...will let you know.
Cavalicious
Moderator
Posts: 1804
Joined: Mon Mar 26, 2007 12:07 am

Post by Cavalicious »

http://pastebin.ca/821656

SVN: 1026 with:
- van's mpeg patch
- van's eyetv patch
- jbrjake's latest VFR patch

Source: EyeTV 1080i HD (exported MPEG PS, not .eyetv file)

Playback: Quicktime 7.1.3 & VLC 0.8.6d

Issues:

- Stutter is till present, though not as pronounced as before. About 1/4 sec stutter per 3 sec.
- Audio was way out of sync (~1sec). Checked that this wasn't present with MPEG PS source.

Will try again later with just your patch on top of 1128 (for apples -to- apples).

Note: Pic did look beautiful.
Cavalicious
Moderator
Posts: 1804
Joined: Mon Mar 26, 2007 12:07 am

Post by Cavalicious »

http://pastebin.ca/822024

SVN: 1028 with:
VFR Patch

Source: EyeTV 1080i HD (exported MPEG PS, not .eyetv file)

Playback: Quicktime 7.1.3 & VLC 0.8.6d

Issues:

- Stutter is till present, though not as pronounced as before. About 1/4 sec stutter per 3 sec.
- [06:13:42] a52_syncinfo failed.
- Audio sync was fine this time.
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Post by jbrjake »

Cav, please test with something hard telecined (where there aren't all those Interlaced->Progressive messages and vice versa). Your source is highly mixed; VFR doesn't handle that stuff right regardless or whether or not b-frames are present. Stuttering can always be expected with it.
Cavalicious
Moderator
Posts: 1804
Joined: Mon Mar 26, 2007 12:07 am

Post by Cavalicious »

Hmm...I have a clip of a show (that I don't normally record) that should fit the bill. But for the most part, *everything* I get from EyeTV is highly mixed (meaning I get those messages).

Van or dynaflash, do you have any suggestions on hard telecined formated shows that I can get from EyeTV?
dynaflash
Veteran User
Posts: 3820
Joined: Thu Nov 02, 2006 8:19 pm

Post by dynaflash »

I *think* all of the frontline series on pbs is hard telecined afaik. Though dont quote me on that ;)
Cavalicious
Moderator
Posts: 1804
Joined: Mon Mar 26, 2007 12:07 am

Post by Cavalicious »

I knew you were going to say that...and PBS is the only channel I can't get OTA in the D/FW area.
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Post by jbrjake »

Cavalicious wrote:Hmm...I have a clip of a show (that I don't normally record) that should fit the bill. But for the most part, *everything* I get from EyeTV is highly mixed (meaning I get those messages).
Well don't I feel stupid. For some reason I was thinking you were the one who'd reported this particular bug, but really it was BradleyS. No wonder you don't have suitable testing material ;>
Cavalicious
Moderator
Posts: 1804
Joined: Mon Mar 26, 2007 12:07 am

Post by Cavalicious »

Actually, I also reported it to you a while ago (during 1st run at this), but I was using my highly mixed sources.
User avatar
BradleyS
Moderator
Posts: 1860
Joined: Thu Aug 09, 2007 12:16 pm

Post by BradleyS »

Works great on the test material I used previously.

x264: ref=5:mixed-refs=1:bframes=5:weightb=1:bime=1:direct=auto:subq=6:me=umh:trellis=2:merange=32:brdo=1:cabac=0
Post Reply