Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion. Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules. |
9th June 2006, 13:34 | #1 | Link |
Registered User
Join Date: May 2006
Location: Yokohama, Japan
Posts: 6
|
A script for timecode-based VFR mp4
I've made a script and executable to make a VFR mp4 from mkv timecode file.
It can download from rapidshare. http://sidheog.org/Documents/tc2mp4_20070124.rar password: doom9.org Last edited by zmi; 12th March 2007 at 17:44. Reason: update script. |
9th June 2006, 13:35 | #2 | Link |
Registered User
Join Date: May 2006
Location: Yokohama, Japan
Posts: 6
|
Description
Code:
Merge timecode into mp4 requirement: avisynth with TIVTC plug-in x264 mp4box usage: tc2mp4 -i [source CFR-mp4] -t [timecode file] -o [destination VFR-mp4] [-k] [-n TrackNumber] example: 1. Prepare CFR-mp4 and timecode format v1/v2 file. - You can use AVISynth script like below. :example.avs: DGDecode_mpeg2source("example.d2v") tfm(d2v="example.d2v").tdecimate(mode=3,hybrid=2,vfrDec=1,mkvOut="example_timecode.txt") - Encode. x264 --output example.mp4 example.avs 2. Merge timecode into mp4 tc2mp4 -i example.mp4 -t example_timecode.txt -o example_vfr.mp4 3. Merge audio into mp4 mp4box -add example.aac example_vfr.mp4 2006/06/09 First release 2007/01/24 Support directory which name contains space. Add -n flag to specify track number to extract. Add -k flag to keep modified nhml file. Last edited by zmi; 12th March 2007 at 17:45. Reason: update script. |
9th June 2006, 13:38 | #3 | Link |
Registered User
Join Date: May 2006
Location: Yokohama, Japan
Posts: 6
|
Perl script
Code:
#!/usr/bin/perl # author: zmi # version: 01/24/2007 # info: http://d.hatena.ne.jp/zmi/ (japanese) # info: http://forum.doom9.org/showthread.php?t=112199 (english) use Getopt::Std; use Math::BigInt; use File::Basename; $usage = <<USAGE tc2mp4 (01/24/2007) usage: tc2mp4 -i InputFile -o OutputFile -t TimecodeFile [-k] [-n TrackNumber] USAGE ; getopts('i:t:o:n:kh'); die $usage if $opt_h; $srcMP4 = $opt_i or die $usage; $dstMP4 = $opt_o or die $usage; $srcTC = $opt_t or die $usage; $number = ($opt_n or 1); $keep = $opt_k; @extlist = ('\.mp4','\.m4v'); ($basename, $dir) = fileparse($opt_i, @extlist); $srcNHML = "${dir}${basename}_track$number.nhml"; $tcNHML = "${dir}${basename}_track$number_tc.nhml"; doCmd("mp4box -nhml $number \"$srcMP4\""); $tcfv = detectTCFVersion($srcTC); print "[Timecode info]\n"; print " Timecode format(v$tcfv)\n"; if($tcfv == 1){ @timescales = parseTimecodeV1($srcTC); $ntsc = detectNTSC($srcTC); for($frame = 0; $frame < @timescales; $frame++){ $factors{$timescales[$frame]}++; } $lcmTimescale = Math::BigInt::blcm(keys %factors); # generate CTS from timecode @cts = toCTS(\@timescales, $lcmTimescale, $ntsc); print " Total frames(".@cts."), LCM Timescale($lcmTimescale)\n"; print " " . ($ntsc ? 'NTSC, ' : '') ."Factors(" . join('/',sort keys %factors) . ")\n"; }elsif($tcfv == 2){ $lcmTimescale = 1000000; @cts = parseTimecodeV2($srcTC); print " Total frames(".@cts."), Timescale($lcmTimescale)\n"; } # read Timebase from source nhml $timebase = readTimebase($srcNHML); print "[NHML info]\n"; print " Timebase($timebase)\n"; applyTimecode($srcNHML, $tcNHML, $timebase, \@cts); doCmd("mp4box -add \"$tcNHML\" -new \"$dstMP4\""); unlink("$srcNHML"); unlink("$tcNHML") unless $keep; unlink("${basename}_track$number.info","${dir}${basename}_track$number.media"); doCmd("mp4box -info \"$dstMP4\""); print "Completed.\n"; exit 0; sub doCmd{ my $cmd = shift @_; print "$cmd\n"; system($cmd) and die $!; } sub applyTimecode{ my $srcNHML = shift @_; my $tcNHML = shift @_; my $timebase = shift @_; my @cts = @{shift @_}; my $dtsFrame = 0,$dts; my $ctsFrame,$ctsOffset,$cts; my $delayFrame,$delayTC; open SRC_NHML, "<$srcNHML" or die $!; open TC_NHML, ">$tcNHML" or die $!; while(<SRC_NHML>){ s/timeScale="\d+"/timeScale="$lcmTimescale"/ if(/<NHNTStream.*?>/); ((print TC_NHML), next) unless /<NHNTSample.*\/>/; next unless $dtsFrame < @cts; /CTSOffset="(\d+)"/; $ctsOffset = ($1 or 0); $ctsFrame = $dtsFrame + int($ctsOffset / $timebase + 0.5); if($dtsFrame == 0){ $delayFrame = $ctsFrame; $delayTC = $cts[$delayFrame]; print "[Delay]\n"; print " Frames($delayFrame), Timecode($delayTC) Milliseconds(" . ($delayTC * 1000 / $lcmTimescale) . ")\n"; } if($dtsFrame < $delayFrame){ $dts = $cts[$dtsFrame]; $cts = $cts[$ctsFrame]; }else{ $dts = $cts[$dtsFrame - $delayFrame] + $delayTC; $cts = $cts[$ctsFrame - $delayFrame] + $delayTC; } $ctsOffset = $cts - $dts; s/DTS="\d+"/DTS="$dts"/; s/CTSOffset="\d+"/CTSOffset="$ctsOffset"/; s/(dataLength="\d+")/$1 CTSOffset="$ctsOffset"/ if $ctsOffset == 0; # hack for mp4box's bug print TC_NHML; $dtsFrame++; } close SRC_NHML; close TC_NHML; } sub toCTS{ my @timescales = @{shift @_}; my $lcmTimescale = shift @_; my $ntsc = shift @_; my @cts, $frame, $duration; my $step = $ntsc ? 1001 : 1000; $cts[0] = 0; for($frame = 1; $frame < @timescales; $frame++){ $duration = ($lcmTimescale / $timescales[$frame -1]) * $step; $cts[$frame] = $cts[$frame -1] + $duration; } return @cts; } sub readTimebase{ my $nhmlFile = shift @_; my @dts; my $timebase; my $count = 0; open NHML, "<$nhmlFile" or die $!; while(<NHML>){ next unless /<NHNTSample.*DTS="(\d+)".*\/>/; $dts[$count] = $1; last if (++$count >= 10); } close NHML; $timebase = $dts[1] - $dts[0]; $avgTimebase = ($dts[$count - 1] - $dts[0]) / ($count - 1); die "Input file is not CFR." if ($timebase != $avgTimebase); return $timebase; } sub toTimescale{ my $fps = shift @_; my $timescale = int($fps * 1.001 + 0.5) * 1000; return $timescale; } sub detectTCFVersion{ my $timecodeFile = shift @_; my $tcfv; open TC, "<$timecodeFile" or die $!; while(<TC>){ next unless /^\#\s+timecode format v([1-2])/; $tcfv = $1; } close TC; defined $tcfv or die "Only timecode format v1/v2 is supported."; return $tcfv; } sub detectNTSC{ my $timecodeFile = shift @_; my $assume; open TC, "<$timecodeFile" or die $!; while(<TC>){ next if /^\#/; next unless /^Assume ([0-9.]+)/; $assume = $1; } close TC; if(toTimescale($assume) == $assume * 1000){ return 0; }else{ return 1; } } sub parseTimecodeV1{ my $timecodeFile = shift @_; my $ntsc = shift @_; my @timescale; my $assume; my $begin,$end,$last,$fps; my $frame = 0; open TC, "<$timecodeFile" or die $!; while(<TC>){ ($last = $1, next) if /^\# TDecimate Mode.*Last Frame = (\d+)/; next if /^\#/; ($assume = $1, next) if /^Assume ([0-9.]+)/; next unless /(\d+),(\d+),([\d.]+)/; ($begin, $end, $fps) = ($1, $2, $3); for($frame; $frame < $begin; $frame++){ $timescale[$frame] = toTimescale($assume); } for($frame = $begin; $frame <= $end; $frame++){ $timescale[$frame] = toTimescale($fps); } } for(;$frame <= $last; $frame++){ $timescale[$frame] = toTimescale($assume); } close TC; $ntsc = 1111; return @timescale; } sub parseTimecodeV2{ my $timecodeFile = shift @_; my @cts; my $frame = 0; open TC, "<$timecodeFile" or die $!; while(<TC>){ next if /^\#/; next unless /([\d.]+)/; $cts[$frame++] = int($1 * 1000 + 0.5); } close TC; return @cts; } Last edited by zmi; 12th March 2007 at 17:47. Reason: update script. |
10th June 2006, 14:28 | #4 | Link |
Registered User
Join Date: Nov 2001
Posts: 9,770
|
whats the password for the archive?
edit: and how does tc2mp4 work? does it overwrite the stts info in the mp4 file? is it reliable to use on all sorts of mp4 files (eg with 64bit times?)
__________________
Between the weak and the strong one it is the freedom which oppresses and the law that liberates (Jean Jacques Rousseau) I know, that I know nothing (Socrates) MPEG-4 ASP FAQ | AVC/H.264 FAQ | AAC FAQ | MP4 FAQ | MP4Menu stores DVD Menus in MP4 (guide) Ogg Theora | Ogg Vorbis use WM9 today and get Micro$oft controlling the A/V market tomorrow for free Last edited by bond; 10th June 2006 at 14:38. |
10th June 2006, 15:49 | #6 | Link | ||
Registered User
Join Date: Nov 2001
Posts: 9,770
|
Quote:
Quote:
__________________
Between the weak and the strong one it is the freedom which oppresses and the law that liberates (Jean Jacques Rousseau) I know, that I know nothing (Socrates) MPEG-4 ASP FAQ | AVC/H.264 FAQ | AAC FAQ | MP4 FAQ | MP4Menu stores DVD Menus in MP4 (guide) Ogg Theora | Ogg Vorbis use WM9 today and get Micro$oft controlling the A/V market tomorrow for free Last edited by bond; 10th June 2006 at 17:49. |
||
10th June 2006, 17:50 | #8 | Link | |
Registered User
Join Date: Nov 2001
Posts: 9,770
|
Quote:
damn, didnt realise that mp4box already supports vfr file creation that way, but it again prooves that mp4box is damn powerful
__________________
Between the weak and the strong one it is the freedom which oppresses and the law that liberates (Jean Jacques Rousseau) I know, that I know nothing (Socrates) MPEG-4 ASP FAQ | AVC/H.264 FAQ | AAC FAQ | MP4 FAQ | MP4Menu stores DVD Menus in MP4 (guide) Ogg Theora | Ogg Vorbis use WM9 today and get Micro$oft controlling the A/V market tomorrow for free |
|
10th June 2006, 18:39 | #9 | Link |
Registered User
Join Date: Nov 2001
Posts: 9,770
|
zmi, shouldnt the vfr file have exactly the same length timewise as the source cfr file? the vfr file i get is shorter (20s vs. 16.616s)
also how do you drop the not needed, duplicate frames? i wonder why x264 outputs a 500 frames output file when using the avs script you described above (tdecimate). shouldnt avisynth drop the frames? also my timecode file looks like this Code:
# timecode format v1 Assume 25.000000 # TDecimate v0.9.12.1 by tritical # Mode 3 - Auto-generated mkv timecodes file 0,3,20.000000 369,404,20.000000
__________________
Between the weak and the strong one it is the freedom which oppresses and the law that liberates (Jean Jacques Rousseau) I know, that I know nothing (Socrates) MPEG-4 ASP FAQ | AVC/H.264 FAQ | AAC FAQ | MP4 FAQ | MP4Menu stores DVD Menus in MP4 (guide) Ogg Theora | Ogg Vorbis use WM9 today and get Micro$oft controlling the A/V market tomorrow for free |
10th June 2006, 22:20 | #10 | Link |
Angel of Night
Join Date: Nov 2004
Location: Tangled in the silks
Posts: 9,559
|
They'll only have the same length (timewise) if the cfr output was set to the average framerate, as described on the vfr page. Next version of TDec may do that automatically, as it keeps bitrate calculators happier as well.
Thanks for providing such a useful tool! Automated vfr in mp4 is nice. Since this is modifying the atoms, does that mean it could do v2 timecodes as well, once a parser is written? (At first I thought it was just splitting and joining the mp4 on each timecode entry!) Last edited by foxyshadis; 10th June 2006 at 22:24. |
11th June 2006, 04:07 | #11 | Link | |
結城有紀
Join Date: Dec 2003
Location: NJ; OR; Shanghai
Posts: 894
|
Quote:
|
|
11th June 2006, 11:50 | #12 | Link | ||
Registered User
Join Date: Nov 2001
Posts: 9,770
|
Quote:
Quote:
afaik it does the following: 1) use mp4box to create a .nhml file containing the timestamps of the source mp4 file (dts) 2) edit the timestamps in the .nhml file according to the info in the timecode file 3) feed this new .nhml file to mp4box, which then creates a new mp4 with the new timestamps so the script is basically using an option of mp4box already being here for long but noone realised it i think the .nhml files can be seen as an equivalent to the mkv timecode files so what we need (and have here) is a "mkv style timecode file" -> "mp4box style timecode file" converter or tdecimate/dedup to offer the option to output .nhml files directly
__________________
Between the weak and the strong one it is the freedom which oppresses and the law that liberates (Jean Jacques Rousseau) I know, that I know nothing (Socrates) MPEG-4 ASP FAQ | AVC/H.264 FAQ | AAC FAQ | MP4 FAQ | MP4Menu stores DVD Menus in MP4 (guide) Ogg Theora | Ogg Vorbis use WM9 today and get Micro$oft controlling the A/V market tomorrow for free |
||
11th June 2006, 14:36 | #13 | Link |
Two bit encoder
Join Date: May 2005
Location: England, UK
Posts: 157
|
An MKV timecodes v2 > v1 converter/script would be very useful (or v2 input > nhml would be even better). In the file I was going to experiment with I can only extract it as a v2 file, and AFAIK there aren't any such tools to convert it (also I believe mkvextract will only extract to v2 anyway). Well maybe there is, but I couldn't find any.
Would/have you thought about submitting this for inclusion in mp4box? |
11th June 2006, 14:41 | #14 | Link | |
Registered User
Join Date: Nov 2001
Posts: 9,770
|
the script seems to be slightly inaccurate, because of the way it chooses the timescale
on a ~25fps stream it used a timescale of 2600000, leading to funky dts values for the frames and introducing rounding errors i think, giving the normal frames a length of 40.04 instead of 40ms (i also noticed that an inaccuracy is also introduced by the structure of the v1 timecode files when dealing with things like 1/3 aso) i also noticed that it seems to have problems sometimes with the dts value for the last frame (giving it a negative value, which is not allowed and surely a bug) edit: Quote:
__________________
Between the weak and the strong one it is the freedom which oppresses and the law that liberates (Jean Jacques Rousseau) I know, that I know nothing (Socrates) MPEG-4 ASP FAQ | AVC/H.264 FAQ | AAC FAQ | MP4 FAQ | MP4Menu stores DVD Menus in MP4 (guide) Ogg Theora | Ogg Vorbis use WM9 today and get Micro$oft controlling the A/V market tomorrow for free |
|
11th June 2006, 14:56 | #16 | Link | |
Registered User
Join Date: Nov 2001
Posts: 9,770
|
Quote:
__________________
Between the weak and the strong one it is the freedom which oppresses and the law that liberates (Jean Jacques Rousseau) I know, that I know nothing (Socrates) MPEG-4 ASP FAQ | AVC/H.264 FAQ | AAC FAQ | MP4 FAQ | MP4Menu stores DVD Menus in MP4 (guide) Ogg Theora | Ogg Vorbis use WM9 today and get Micro$oft controlling the A/V market tomorrow for free |
|
11th June 2006, 15:22 | #17 | Link |
Two bit encoder
Join Date: May 2005
Location: England, UK
Posts: 157
|
Seems not. I don't have the CFR source to hand, but the VFR MKV is 27:01, and this turns out to be 28:31. You can go to around the 26:50 mark and hear the ending of the audio, it finishes as it should at 27:01 (or around there) but the video continues.
I wonder if it's caused by converting V2 > V1. Can anyone try this with an original (not converted) V1 timecode? There isn't anything too odd about this video, but the decimation was pretty agressive and it was a very static CG anime (a combination of wanting to see how small I could get a file, it was viewable at 33MB if anyone is interested ), much more agressive than I would use normally, so this might exaggerate the problem, rather than it being off by a few seconds with some light decimation. Here is the output from tc2mp4 anyway Code:
C:\Documents and Settings\Michael\Desktop\tc2mp4>tc2mp4.exe -i Track2.mp4 -t tim ev1.txt -o Track2vfr.mp4 mp4box -nhml 1 Track2.mp4 [Timecode info] Total frames(16178), LCM Timescale(120000), Factors(1000/12000/2000/24000/3000 /4000/5000/6000/8000) [NHML info] Timebase(1001) [Delay] Frames(2), Timecode(10010) mp4box -add Track2_track1_tc.nhml -new Track2vfr.mp4 NHML import - Stream Type Visual - ObjectTypeIndication 0x21 Saving Track2vfr.mp4: 0.500 secs Interleaving mp4box -info Track2vfr.mp4 * Movie Info * Timescale 600 - Duration 00:28:31.968 Fragmented File no - 1 track(s) File Brand isom - version 1 Created: GMT Sun Jun 11 13:49:58 2006 File has no MPEG4 IOD/OD Track # 1 Info - TrackID 1 - TimeScale 120000 - Duration 00:28:31.968 Media Info: Language "Undetermined" - Type "vide" - Sub Type "avc1" - 16178 samp les MPEG-4 Config: Visual Stream - ObjectTypeIndication 0x21 AVC/H264 Video - Visual Size 480 x 352 - Profile High @ Level 5.1 Pixel Aspect Ratio 4:3 - Indicated track size 640 x 352 Self-synchronized Completed. I should drop in that the maximum possible framerate at any one time would have been 23.976 (the source was constant 23.976 and I decimated redundant frames to save a little space) Last edited by Zero1; 11th June 2006 at 15:24. |
12th June 2006, 19:12 | #18 | Link |
Registered User
Join Date: May 2006
Location: Yokohama, Japan
Posts: 6
|
Updated tc2mp4.
http://rapidshare.de/files/22887498/...60613.rar.html - Added timecode format v2 support. - Improved accuracy for non-NTSC movie. Last edited by zmi; 12th June 2006 at 19:16. |
12th June 2006, 22:48 | #19 | Link |
Two bit encoder
Join Date: May 2005
Location: England, UK
Posts: 157
|
Great work zmi.
I just tested this and can report some interesting things. I used the v2 timecode extracted from my original MKV and tc2mp4 created the vfr mp4 from it, and it was exactly the correct duration. So what about the test that I did in the previous post? Well that was a v1 timecode created from a v2 timecode (using the tool bond linked too). I muxed using this v1 timecode and I got the wrong duration again. So I conclude that tc2mp4 was right, but the program that converted the v2 > v1 was doing something weird. Here is the output using the updated tc2mp4 for the v1 timecode file created by converting the v2 file using the program bond linked to: Code:
C:\Documents and Settings\Michael\Desktop\tc2mp4>tc2mp4.exe -i Track2.mp4 -t Tim ev1.txt -o 2.mp4 mp4box -nhml 1 Track2.mp4 [Timecode info] Timecode format(v1) Total frames(16178), LCM Timescale(120000) Factors(1000/12000/2000/24000/3000/4000/5000/6000/8000) [NHML info] Timebase(1001) [Delay] Frames(2), Timecode(10000) mp4box -add Track2_track1_tc.nhml -new 2.mp4 NHML import - Stream Type Visual - ObjectTypeIndication 0x21 Saving 2.mp4: 0.500 secs Interleaving mp4box -info 2.mp4 * Movie Info * Timescale 600 - Duration 00:28:30.258 Fragmented File no - 1 track(s) File Brand isom - version 1 Created: GMT Mon Jun 12 21:40:55 2006 File has no MPEG4 IOD/OD Track # 1 Info - TrackID 1 - TimeScale 120000 - Duration 00:28:30.258 Media Info: Language "Undetermined" - Type "vide" - Sub Type "avc1" - 16178 samp les MPEG-4 Config: Visual Stream - ObjectTypeIndication 0x21 AVC/H264 Video - Visual Size 480 x 352 - Profile High @ Level 5.1 Pixel Aspect Ratio 4:3 - Indicated track size 640 x 352 Self-synchronized Completed. Code:
C:\Documents and Settings\Michael\Desktop\tc2mp4>tc2mp4.exe -i Track2.mp4 -t Tim e.txt -o 1.mp4 mp4box -nhml 1 Track2.mp4 [Timecode info] Timecode format(v2) Total frames(16178), Timescale(1000000) [NHML info] Timebase(1001) [Delay] Frames(2), Timecode(83000) mp4box -add Track2_track1_tc.nhml -new 1.mp4 NHML import - Stream Type Visual - ObjectTypeIndication 0x21 Saving 1.mp4: 0.500 secs Interleaving mp4box -info 1.mp4 * Movie Info * Timescale 600 - Duration 00:27:01.831 Fragmented File no - 1 track(s) File Brand isom - version 1 Created: GMT Mon Jun 12 21:39:43 2006 File has no MPEG4 IOD/OD Track # 1 Info - TrackID 1 - TimeScale 1000000 - Duration 00:27:01.832 Media Info: Language "Undetermined" - Type "vide" - Sub Type "avc1" - 16178 samp les MPEG-4 Config: Visual Stream - ObjectTypeIndication 0x21 AVC/H264 Video - Visual Size 480 x 352 - Profile High @ Level 5.1 Pixel Aspect Ratio 4:3 - Indicated track size 640 x 352 Self-synchronized Completed. |
14th June 2006, 18:50 | #20 | Link |
Registered User
Join Date: Nov 2001
Posts: 9,770
|
in my first test the v2 -> v1 converter did the right thing and tc2mp4 was wrong slightly. i assume this is now fixed with zmis latest version
there is another reason why things can be wrong and this is because of flaws in the mkv timecode files themselves eg they store 1/3fps not as 1/3fps but as 0.333333fps which is obviously not 100% the same and will therefore lead to slightly different results such problems can be avioded with the nhml format
__________________
Between the weak and the strong one it is the freedom which oppresses and the law that liberates (Jean Jacques Rousseau) I know, that I know nothing (Socrates) MPEG-4 ASP FAQ | AVC/H.264 FAQ | AAC FAQ | MP4 FAQ | MP4Menu stores DVD Menus in MP4 (guide) Ogg Theora | Ogg Vorbis use WM9 today and get Micro$oft controlling the A/V market tomorrow for free |
|
|