Log in

View Full Version : Painless (mostly) conversion from 120fps files to vfr matroska files


HeadlessCow
11th January 2004, 07:06
Being that I'm into anime I tend to run into lots of 120 fps encodes because someone thought that was the best way to keep mixed film and ntsc video in the same file at a single usable framerate. Well, that was valid with avi files...but why do that when we've now got matroska?!

So...I decided to take a stab at making the conversion relatively easy...and most importantly, not manual!! Nothing sucks more than counting dropped frames while scrolling through a file in vdub so you can figure out a correct decimation scheme.

Well, thanks to Donald Graft's very useful MultiDecimate (http://neuron2.net/multidecimate/multidecimate.html) tool, gizmotech's helpful thread (http://forum.doom9.org/showthread.php?s=&threadid=64314) about vfr encoding, and little (maybe too much) free time I think I've come up with a good way.

I'll tell you how I came up with this approach, then I'll walk you through all the necessary steps.

First, I was playing with using MultiDecimate to get the whole clip down to a single usable framerate. So I looked in its output file and noticed that, with a 120 fps encode that was created by adding dropped frame, most of the frames had 0 difference from the previous frame. Hmm, I thought, those must be all the dropped frames. So I continued on and generated a dfile.txt with the GUI. Looking in there I could see that it merely contained a list of output-input frame matchings. Sadly, some of my favorite frames were missing from the 29.97 fps segments since I had told it to decimate 4 of every 5 frames to give me 23.976 fps. Hmm, I thought again, what if I wanted all the frames, but with none of the dropped frames. Well...that was not to be because I just couldn't squish ~30 frames into the time of ~24. Or could I?

At about this same time I was bopping around on the doom9 board looking at Matroska stuff because I had free time while my encode was running. I again came across some variable frame rate stuff, and it seemed like a perfect match, because I could get ~30 frames into the time of ~24 if I changed the framerate! But, gizmotech's guide, as helpful as it was left me sad because for a video with as many interwoven sections of framerates (there are 2190 framerate ranges in my final timecodes.txt) as this one had would take me years to get correct. There had to be a better way!

So I started off by looking at mfile.txt for a way to pull out all the good frames, and somehow figure out the correct fps at the same time. I start of by scanning once through the file and counting each good frame and the number of identical (or dropped) frames that follow it. I store all of these in a big list for my second round of computation.

During the next pass I cycle through my list and write out a dfile.txt that matches all the good input frames to output frames, regardless of framerate. At the same time I keep track of all the consecutive output frames that had the same number of dupes following them. Everytime I find a change I write out the previous sections frame range and compute the fps for that segment based on the user input original framerate.

Now I'm left with a dfile.txt that contains all the good frames and a timecodes.txt that tells matroska what ranges are what framerate. Now I open trusty old vdub and encode the video. This file has half the fps of the input file because of the header I wrote to dfile.txt, but that doesn't matter because I just want the video. Demux and encoding the audio is done seperately because otherwise it ends up messed up by the different length of the video due to the fps change.

With video, audio and timecodes.txt I've got everything I need to mux a nice matroska file.

Now, for the step-by-step.

0) Get a 120 fps video that includes mixed film and ntsc. Here's a sample file.

1) Grab the MultiDecimate filter and create an avs for the file you wish to encode. Make sure to trim off any junk frames you don't want before you call MultiDecimate in your script. You should end up with a file that looks something like this (no processing filters are necessary now):
AviSource("mixed-fps.avi").trim(8,0)
ConvertToYUY2()
MultiDecimate(pass=1)


2) Open your avs in vdub and play through it exactly once, then close it. You should now have an mfile.txt file in the same directory as your avs script. If you haven't look in one it will look like this:
0 0.000000
1 0.000000
2 0.000000
3 0.000000
4 0.049069
...



Each line contains a framenumber and the difference to the previous frame.

3) You're probably thinking, I know what's coming, run the MultiDecimate GUI...but no! This is where my code comes in :)
Go grab a copy of the source and executable here. (http://www.matroska.org/downloads/windows.html) It's on the Windows page, but since it's Java you can run it on just about any platform you want.

Make sure you have a Java VM installed first. Run my program and pass it the name of the mfile.txt file (MultiDec doesn't like it renamed, but I don't care :)) and the original fps of the file, probably 119.88.
java -jar vfrTools.jar mfile.txt 119.88


This will generate two files in your directory. The first is the dlist.txt file that MultiDec pass 2 will expect.
3 2 1 1557 359
0 0
1 4
2 8
3 12
4 17
...


The first line is bogus except for the frame counts, but the first 3 values are set to stuff that seemed "safe". (no crashes or other weirdness)

The second is the timecodes.txt which is in the format the mkvmerge expects.
# timecode format v1
Assume 119.881
0,2,29.97025
3,6,23.9762
7,126,29.97025
127,146,23.9762
...



Assume fps is set to the input framerate, which seems safe enough. From then on every frame in the file is part of some range and assigned an fps.

4) Switch to using the second pass of MultiDec and encode your video. Your avs should look like this, but feel free to add any filtering you want to the end.
AviSource("mixed-fps.avi").trim(8,0)
ConvertToYUY2()
MultiDecimate(pass=2)

Yes, this output is gonna be at an odd fps. But it doesn't matter, we'll fix it in the muxing process.

5) Merge your video and any audio you want using mkvmerge. Just make sure you use the timecodes.txt file with your video. Make sure you use a version new enough to support this. I used 0.8.1.
Command split so the post isn't super-wide.
"mkvmerge" -o "variable-fps.mkv" -d 0 -A -S --track-order 0
--timecodes "0:timecodes.txt" "single-fps.avi"


Voila! Now you've nicely converted that EVIL 120 fps encode that probably wouldn't even play in real time on your computer to a HAPPY variable fps matroska file that will :)

Ok, so maybe it's a little more work than I lead you to believe...but the important part is that you had to do almost nothing other than start processes and type a few scary commands. Hopefully I can expand my project a bit and make it even easier. So if you have any feedback or bug reports, please post here!

edit: fixed download links to reflect my updates. Examples too.
edit: removed download links to original files and added a link to matroska.org instead since robUx4 nicely offered to host it there. I don't have my original sample files anymore, so no nice step-by-step output examples :'(

robUx4
11th January 2004, 09:50
Really nice.
I think someone else already tried the same kind of thing a while back.
What is the size difference (source vs result) in your case ?

HeadlessCow
11th January 2004, 18:49
Size difference is impossible to judge, at least for me, because by going through avisynth to do decimation on the stream I lose the original video data. Making a size comparison after reencoding merely measures the video codec used.

My guess is that as long as you match the bitrate of the original video data you'll get a smaller encode because you won't be storing ~4-5 times as many frame headers anymore.

Edit: Oh yeah, before anyone gets too excited this is NOT a general tool for generating timecode files. It will only work on files created in the fashion used to generate 120 fps files.

In the soon future I'll be trying to add the ability to work on telecined input. I think I've got a good idea on how to make that work.

ChristianHJW
11th January 2004, 20:57
Originally posted by HeadlessCow In the soon future I'll be trying to add the ability to work on telecined input. I think I've got a good idea on how to make that work. .... great work ! You should come to the matroska IRC channel on irc.corecodec.com by time and talk with Myrsloik and A-L|Gizmo about this, as they are working on something similar IIRC. They were also proposing some changes to the timestamp files for mkvmerge, your input would be precious here.

About putting telecined material into MKV, all the necessary information is more or less present in the original MPEG2 stream also AFAIK. I hardly understand why anybody is using special filters to recreate this information from the raw frames in AVISynth, if it could be read from the MPEG2 file before ? spyder has his first MPEG2-in-MKV files working TODAY, they play on DirectShow, and he was recently telling me that his MILK ( no, not markFD's MILK, spyder's plugin API ) based reader plugin could gather all this information from the MPEG2, and pass it to another app then ....

HeadlessCow
11th January 2004, 21:17
Well...I popped into the channel. But I'm not sure how much help I'd be. I've never actually used Matroska for anything other than this little project :cool:

haibane
12th January 2004, 02:41
Thanks HeadlessCow....
This is really great......
I am trying it right now......

I have a question, does this checks for the totally black scene(or white), two black scene may have 0 diffrernce, how does this tool cope with that?

HeadlessCow
12th January 2004, 03:01
It will eat the second one and output a single frame at half the framerate.

If this is a problem for people I can try to fix it by allowing people to set allowable framerates.

haibane
12th January 2004, 03:10
I have found something funny with the tool....
this is the time code it generates....
there are lots of one frame with weird fps(i am using the win32 ver ).....
i don't know if it's going to matter, but it looks scary....
the final results seems to play fine.....


# timecode format v1
Assume 119.88
0,0,23.976
1,2690,29.97
2691,2691,5.994
2692,2692,2.664
2693,2693,7.9919996
2694,2795,23.976
2796,2796,11.988
2797,2801,23.976
2802,2802,11.988
2803,2805,23.976
2806,2806,11.988
2807,2807,23.976
2808,2808,7.9919996
2809,2814,23.976
2815,2815,11.988
2816,2816,23.976
2817,2817,7.9919996
2818,2819,23.976
2820,2820,11.988
2821,2822,23.976
2823,2823,11.988
2824,2824,23.976
2825,2825,7.9919996
2826,2830,23.976
2831,2831,7.9919996
2832,2832,23.976
2833,2833,7.9919996

HeadlessCow
12th January 2004, 05:18
The weird framerates come from totally static scenes.

A framerate of 7.9919996 means that there were 15 duplicate or dropped frames consecutively. The only time you'd want to worry is when the input framerate/output framerate != integer.

Edit:
If you don't trust me (and maybe you shouldn't) you can look at mfile.txt and dfile.txt and compare the frames around that area to makes sure there really are big stretches of identical frames.

haibane
12th January 2004, 14:29
Originally posted by HeadlessCow
The weird framerates come from totally static scenes.

A framerate of 7.9919996 means that there were 15 duplicate or dropped frames consecutively. The only time you'd want to worry is when the input framerate/output framerate != integer.

Edit:
If you don't trust me (and maybe you shouldn't) you can look at mfile.txt and dfile.txt and compare the frames around that area to makes sure there really are big stretches of identical frames.


I see what's happening right now....
thanks for the explanation......
so it actually allows codecs other than RV9 to do real dup-drop filtering VFR......
if combine this with the dup-drop filter in avs, it could save lots useless static frames..........
really cool.......

spyder
12th January 2004, 19:43
I tried this last night on NTSC sources containing real video, not anime. It works well as an intelligent deinterlacing. You can telecide the clip with post processing off and get back the progressive frames and leave the combed ones. Then I doubled the framerate and number fo frames of the first clip by interleaving it with itself and setting the framerate. Then I used DgBob to smartbob the clip for the interlaced parts using ConditionalFilter and the IsCombed() function. The result, 23.976fps for the progressive part and 29.97fps-59.94fps for the interlaced. The motion is very smooth in the interlaced parts with this.

Kurosu
14th January 2004, 13:52
Originally posted by robUx4
Really nice.
I think someone else already tried the same kind of thing a while back.
What is the size difference (source vs result) in your case ?
There was a grand total of 2 midly interested people in the tool (which is basically the implementation of the workflow I describe here (http://forum.doom9.org/showthread.php?s=&threadid=64314#post394186)), so I dropped the matter. Plus various reasons, one being I couldn't fix something weird happening with the timestamps, producing a slight drift.

The avantage over the present method is that it could use an avisynth script to filter (mid-level spatial+temporal filtering) your clip before doing an estimation of the duplicate runs.

HeadlessCow
14th January 2004, 18:21
Well, there's nothing to stop you from using filtering before the call to Multidecimate, but since my tool only looks for frames that are exactly the same, there's no real reason to do that unless you insert a call to Graft's Dup filter as well which would allow you t replace near Dups with perfect Dups.

As far as I can tell, all weird combinations of still frames should be correctly culled, with the exception of IVTCed video at 30 fps. That will have 30fps and 15 fps sections instead of 30 and 24 fps section which is correct.

Kurosu
14th January 2004, 20:58
Indeed you are right, my apologies.

Anyway, it's not the smartest thing to do yet. One could just parse the frames, read their size: all those that are 0 are dropped frames. Without a particular knowledge of the fileformat, this is possible. For someone familiar with it, like Alexnoe, that might just be a matter of minutes to write such a parser (if he hasn't already written one).

Kurosu
14th January 2004, 23:52
I've found 2 errors in your output. Let's say 1 is for a dropped frame, 0 for an encoded one - to keep, then:

1) Start of clip
The first 25 frames are: 011101110111011101110111011110
Your program writes:
0,0,23.976
1,2,29.97
3,6,23.97
But it should be:
0,4,29.97
5,8,23.976

2) End of clip
Last frames are: 011101111
Your program writes:
353,357,29.97
It should at least be:
353,357,29.97
357,358,23.976
With the above correction, it seems it should be:
355,359,29.97
#360,361 or 360,361
360,361,23.976

Removed side information that was just interfering with the proper development of this thread

HeadlessCow
25th February 2004, 08:09
Two updates:

1) I fixed my code to correctly handle the start and the end of the clip correctly. Easy fixes for both of them.
Updated source and executable jar is here (http://www.rit.edu/~ncg2558/Files/doom9/vfrTools.jar).

2) My pal roxfan created a Delphi implementation of this that actually reads the frame headers and writes out the necessary files. His tool also allows you to output the timecodes file in timecode format v2. This format is just timestamps for each frame instead of ranges with assigned framerates.
His source and executable is available here (http://www.rit.edu/~ncg2558/Files/doom9/vfrtool_10.rar).

If you find any problems in either, post here and I promise that I'll probably respond at least slightly quicker than this last time :)

Bonus update!
mkvmerge, and perhaps VirtualDubMod(in direct stream copy mode), will both write create variable framerate files if you give a file with dropped frames as input, so if you don't want to do any filtering(including using my tool as a drop-dupe filter), you can avoid this entire process and just use one of them to mux your file. ;)

dbzgundam
15th April 2004, 03:58
I got a really stuttery result out of this... And during the encode process the video was doubled up?! Oo (The video was basically repeated again after it ended, and encoded along with the existing video)

Any reason why? I never got any errors during the process either. The clip I used was the one headlesscow provided in the first post.

HeadlessCow
15th April 2004, 04:58
Can you please give me links to the mfile, dfile and timecodes text files that you get as output? Just pop them up on whatever free hosting you can find.

dbzgundam
15th April 2004, 21:17
Here they are:

www.esenet.com/baka/mfile.txt

www.esenet.com/baka/dfile.txt

www.esenet.com/baka/timecodes.txt

Also, is there a way to make hybrid, but 29.97 video into VFR? Or do you first need to convert it to 120fps?

HeadlessCow
16th April 2004, 06:59
Looking at the files it looks like you didn't go straight through the video in one pass when you had pass=1 set for multidecimate. You can't skip around at all or the resulting files are gonna be messed up. Just open, advance through every frame in order, then close the video.

The problem with this approach and 29.97 fps mixed video is that I don't know how to reliably detect the differences between 29.97 and 23.976 source video when being played back at 29.97. Since I'd need to determine that something is FILM and then use a different way of distributing framerates than the simple method I'm using now. Since 120 fps clips are generally created by either capping at higher than the source framerate, or by using info from a d2v file to change the framerates, the stream can always be decimated just by counting duplicates and adjusting the framerate per output frame like I do. Adding the other would mean I'd have to detect 4 and 5 frame cycles correctly, and deal with cycles that are only partially present, or partial cycles that have been put together from cutting commercials and such.

To make a long explanation short, it's possible, but after trying for a bit...it's just too much for me to figure out :)

dbzgundam
16th April 2004, 21:34
Hmm I did what you said, and recreated my dfile and textcodes... I didn't get doubled up video, but I sure got jerky video.

I hate this...... Any solutions?

HeadlessCow
16th April 2004, 23:35
Do you get stuttering when you play back my encode of the video, or just when you make an encode yourself?

dbzgundam
17th April 2004, 04:06
Originally posted by HeadlessCow
Do you get stuttering when you play back my encode of the video, or just when you make an encode yourself?

Only my encode.... So obviously I'm doing something wrong.

HeadlessCow
17th April 2004, 08:29
Well...check to make sure you're following the instructions that I listed exactly... The mkvmerge command should be all one line when you run it, if you didn't do that the results will be kinda odd :)

dbzgundam
17th April 2004, 17:56
Well originally I used the GUI for muxing and after reading your usage of the command line version, I decided to do it that way....

No success.

HeadlessCow
18th April 2004, 18:05
Do your intermediate text files match mine? If you're using xvid or divx5 instead ffvfw to decode the video get rid of the Trim(8,0) in my scripts.

slavik
20th January 2005, 04:55
This method could also be used to encrease the quality of encodes by getting rid of unessessery frames :)

slavik
4th February 2005, 02:52
Wanted to bump this up and post a program I wrote ...

http://home.ripway.com/2005-2/255257/vfrFrameCount.exe

Put it in the same dir as the dfile.txt
The program will read the file and calculate how many frames there are in the source, how many frames are similar (0.0 difference to previous) and how many unique frames there are (not similar to previous)

This is to aid in deciding whether to use the method or not and in helping of calculations for "size per frame" :)

inurenegade
11th August 2005, 05:53
um sorry to bug you but how do you start that jar file
i dont know exactly what to use to execute it so if you could plz tell me what to do im knda a n00b on this kinda stuff

Mosu
12th August 2005, 11:04
Please read http://forum.doom9.org/showthread.php?t=98573 Thanks