Added a workaround for a bug in mkv tool's ability to handle 1080i (interlaced HD...
authorAlan J. Pippin <alan@pippins.net>
Sun, 11 Dec 2011 17:45:25 +0000 (10:45 -0700)
committerAlan J. Pippin <ajp@pippins.net>
Sun, 11 Dec 2011 17:45:25 +0000 (10:45 -0700)
make_mkv
mkv2mp4
mkv_extract_chapter

index 042e38eaca0eed66ede829b9c8f4a05f8ddaf0eb..04556e523fca95ddf1c500130cb2fdd4c31f7a49 100755 (executable)
--- a/make_mkv
+++ b/make_mkv
@@ -106,8 +106,44 @@ print "$chapter_file_contents\n";
 print "-> Creating the MKV video file '$opt_o'\n";
 
 my $cmd = "$mkvmerge --title \"$opt_t\" $output_file_options -o \"$opt_o\"";
+
+# Make our input file command line options for all the videos
 my $video_count = 0;
+my @video_tmp_files;
 foreach my $video (sort{$videos{$a} <=> $videos{$b}} keys %videos) {
+    # Detect if the input file is interlaced or not.
+    # There is a bug in mkvmerge 5.0.1 and earlier where if the video content is 1080i, it doesn't mux it properly, and it stutters.
+    # The quick solution to this issue is to run the interlaced video file through ffmpeg and tell it to copy the video/audio streams to a mkv container.
+    # We will then merge this temporarily created mkv container into the final mkv container instead of the original interlaced video.
+    # http://ubuntuforums.org/showthread.php?t=1627194
+    # http://forum.doom9.org/showthread.php?t=155732&page=31
+    my $interlaced = system('ffmpeg -i "$video" 2>&1 | grep -q "frame rate differs"');
+    if($interlaced != 0) {
+       my $video_mkv = $video;
+       my $video_ext = $video;
+       $video_ext =~ s/.*\.(\S+)$/$1/;
+       print "   Detected interlaced video content: $video\n";
+       if($video_ext !~ /mkv/i) {
+           $video_mkv =~ s/\.[^.]*$//; $video_mkv .= ".mkv";
+           print "   Re-muxing the interlaced video content as an mkv file: $video_mkv\n";
+           my $make_mkv_cmd = "ffmpeg -i \"$video\" -scodec copy -acodec copy -vcodec copy -f matroska \"$video_mkv\" > /dev/null 2>&1";
+           if(! defined $opt_s) { 
+               my $errno = system("$make_mkv_cmd");
+               $errno = $errno >> 8;
+               if($errno > 1) {
+                   unlink "$video_mkv";
+                   die "-E- ffmpeg encountered some errors with exit code $errno\n";
+               }
+           }
+           # Push the name of our intermediate video file onto a list of files to delete on exit
+           push(@video_tmp_files, "$video_mkv");
+           # Update the name of our video file to equal the name of our new intermediate video file name
+           $video = $video_mkv;
+       }
+    } else { 
+       print "   Detected progressive video content: $video\n";
+    }
+    # Create our intput file command line options for this video
     if($video_count == 0) {
        $cmd .= " $input_file_options \"$video\"";
     } else {
@@ -115,7 +151,9 @@ foreach my $video (sort{$videos{$a} <=> $videos{$b}} keys %videos) {
     }
     $video_count++;
 }
-print "$cmd\n";
+
+# Execute our command line
+print "\n$cmd\n";
 if(! defined $opt_s) { 
     my $errno = system("$cmd");
     $errno = $errno >> 8;
@@ -128,5 +166,10 @@ if(! defined $opt_s) {
 # Remove the temporary file used for the chapter generation
 if(-e "$tmpfile") { unlink "$tmpfile"; }
 
+# Remove any temporary video files created during the merge process
+foreach my $video (@video_tmp_files) {
+    if(-e "$video") { unlink "$video"; }
+}
+
 ####################################################################################################
 
diff --git a/mkv2mp4 b/mkv2mp4
index 463c3d3852884b68bbcb1fa80e613bad5993e8a7..afbd0a8adb25b95a4f9c410395667421900e5f8d 100755 (executable)
--- a/mkv2mp4
+++ b/mkv2mp4
@@ -1,12 +1,27 @@
 #!/bin/bash
 
-# step 1: convert the mkv to mpg ; many mkv files do not directly get converted to mp4
-mencoder "$1" -ovc lavc -lavcopts vcodec=mpeg1video -aid 0 -oac pcm -o delete_me.mpg
+INPUT=$1
+FILENAME=${INPUT%%.*}
+OUTPUT="$FILENAME.mp4"
 
-# step 2: convert the mpg file to mp4
-mencoder -of lavf -lavfopts format=mp4 -oac lavc -ovc lavc -lavcopts \
-aglobal=1:vglobal=1:acodec=libfaac:vcodec=mpeg4:abitrate=128:vbitrate=640:keyint=250:mbd=1:vqmax=10:lmax=10:turbo  -af lavcresample=44100 \
--vf harddup,scale=640:-3 "delete_me.mpg" -o "$1.mp4"
+echo "-> Converting $INPUT to $OUTPUT file"
 
-# step 3: delete the temporary huge sized mpg file 
-rm "delete_me.mpg"
+if [[ -e "$OUTPUT" ]]; then
+  echo "-E- Output file $OUTPUT already exists. Aborting..."
+  exit 1
+fi
+
+ffmpeg -i $INPUT \
+  -b:v 3000k \
+  -vcodec libx264 \
+  -acodec copy \
+  -coder 1 \
+  -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 \
+  -subq 5 \
+  -me_range 16 \
+  -g 250 \
+  -keyint_min 25 \
+  -sc_threshold 40 \
+  -i_qfactor 0.71 \
+  -threads 2 \
+  $OUTPUT
index 4653278b1e1b675e5152a664e2aa268e79031114..b70a7501db76507225bf6e638ff1ebd970be1be1 100755 (executable)
@@ -44,7 +44,7 @@ my $tmpfile = `tempfile`; chomp($tmpfile);
 
 ####################################################################################################
 # Command Line Options
-getopts("c:i:o:se:");
+getopts("c:i:o:kse:");
 
 if(! -x $ffmpeg) { die "-E- Unable to find required program: ffmpeg\n"; }
 if(! defined $opt_i) { &usage(); die "-E- Missing required argument input video name: -i <input.mkv>\n"; }
@@ -64,6 +64,7 @@ sub usage {
     print "  -e <ext>                  Specify the extension that should be used on the output files.\n";
     print "                            This is automatically determined. You only need to specify it if autodetection fails.\n";
     print "  -s                        Simulate mode. Don't actually make the video, but tell us what you will do\n";
+    print "  -k                        Keep intermediate mkv file created during the extraction/conversion of mp4 clips\n";
     print "\n";
     return 1;
 }
@@ -72,7 +73,7 @@ sub usage {
 # SUBROUTINES
 
 sub detect_ext {
-    my ($ffmpeg_info) = @_;
+    my ($ffmpeg_info, $interlaced) = @_;
 
     my $h264 = 0;
     my $h264_high = 0;
@@ -93,9 +94,9 @@ sub detect_ext {
     # Quicktime/MOV
     if($h264 && $pcm_s16le) { return "mov"; }
     # MTS
-    if($h264_high && $ac3) { return "mp4"; }
+    if($h264_high && $ac3) { if($interlaced) { return "mkv"; } else { return "mp4"; } }
     # 3GP/MP4
-    if($h264 && $aac) { return "mp4"; }
+    if($h264 && $aac) { if($interlaced) { return "mkv"; } else { return "mp4"; } }
 
     return "UNKNOWN";
 }
@@ -117,6 +118,13 @@ my $all_chapters = 0;
 if($opt_c =~ /all/) { $all_chapters = 1; }
 print "-> Extracting the following chapters from $opt_i: @chapters\n";
 
+my $interlaced = system('ffmpeg -i "$opt_i" 2>&1 | grep -q "frame rate differs"');
+if($interlaced != 0) {
+    print "   Detected interlaced video content\n";
+} else {
+    print "   Detected progressive video content\n";
+}
+
 # Use ffmpeg to extract the list of chapters available to rip
 # For each chapter specified on the command line, use ffmpeg to extract a video clip from that chapter
 my @ffmpeg_info = `$ffmpeg -i $opt_i 2>&1`;
@@ -131,10 +139,10 @@ foreach $line (@ffmpeg_info) {
        if($all_chapters || &export_chapter(\@chapters,$chapter)) {
            $ext = "UNKNOWN"; 
            if(defined $opt_e) { $ext = $opt_e; }
-           else { $ext = &detect_ext(\@ffmpeg_info); } 
+           else { $ext = &detect_ext(\@ffmpeg_info, $interlaced); } 
            if($ext =~ /UNKNOWN/) { die "-E- Unable to determine the file type/extension to use for the output videos. Specify it with the -e <ext> option.\n"; }
            $dstfile = $opt_o . ".c" . sprintf("%03d",$chapter) . "." . $ext;
-           print "-> Exporting $chapter to $dstfile: ";
+           print "-> Exporting $chapter to $dstfile:\n";
            $cmd = "ffmpeg -ss $start -t $duration -i $opt_i -map 0 -vcodec copy -acodec copy $dstfile 2>&1";
            if(defined $opt_s) {
                print "$cmd\n";
@@ -145,6 +153,22 @@ foreach $line (@ffmpeg_info) {
                $errno = $errno >> 8;
                if($errno > 0) { die "-E- ffmpeg encountered some errors with exit code $errno\n"; }
            }
+           if($interlaced && $ext =~ /mkv/i) {
+               $ext = "mp4";
+               $srcfile = $dstfile;
+               $dstfile = $dstfile = $opt_o . ".c" . sprintf("%03d",$chapter) . "." . $ext;
+               $cmd = "mencoder -oac copy -ovc copy -of lavf -lavfopts format=mp4 -o $dstfile $srcfile";
+               if(defined $opt_s) {
+                   print "$cmd\n";
+               } else {
+                   print "\n";
+                   print "$cmd\n";
+                   $errno = system("$cmd");
+                   $errno = $errno >> 8;
+                   if(! defined $opt_k) { unlink "$srcfile"; }
+                   if($errno > 0) { die "-E- mencoder encountered some errors with exit code $errno\n"; }
+               }
+           }
        }
     }
 }