Added some comments to describe what each extension was being used for
[videoscripts/.git] / mkv_extract_chapter
1 #!/usr/bin/perl
2 # Author: Alan J. Pippin
3 # Description: Extract the given chapter(s) from an mkv file into separate video files
4 # Requires: Newer version of ffmpeg to be installed that supports MKV chapters
5
6 # MOV:
7 # major_brand : qt
8 # Video: h264 (avc1 / 0x31637661) 
9 # Audio: pcm_s16le
10
11 # MTS:
12 # Video: h264 (High) (HDMV / 0x564D4448)
13 # Audio: ac3 (AC-3 / 0x332D4341)
14
15 # MP4:
16 # Video: h264 (Baseline) (avc1 / 0x31637661)
17 # Audio: aac (mp4a / 0x6134706D)
18
19 # 3GP:
20 # Video: h264 (Baseline) (avc1 / 0x31637661)
21 # Audio: aac (mp4a / 0x6134706D)
22
23 # FFMPEG EXAMPLE CMD:
24 #ffmpeg -ss 00:00:00.000 -t 00:00:38.299 -i 2010-12-04.000.mkv -map 0 -vcodec copy -acodec copy test.mov
25
26 ####################################################################################################
27 # Includes
28 use File::Copy;
29 use File::Basename;
30 use Getopt::Std;
31
32 ####################################################################################################
33 # Configuration parameters - CHANGE THESE TO SUITE YOUR NEEDS
34 my $ffmpeg=`which ffmpeg`; chomp($ffmpeg);
35 my $tmpfile = `tempfile`; chomp($tmpfile);
36 ####################################################################################################
37
38 ####################################################################################################
39 # Command Line Options
40 getopts("c:i:o:se:");
41
42 if(! -x $ffmpeg) { die "-E- Unable to find required program: ffmpeg\n"; }
43 if(! defined $opt_i) { &usage(); die "-E- Missing required argument input video name: -i <input.mkv>\n"; }
44 if(! defined $opt_c) { &usage(); die "-E- Missing required chapter: -c <all|1,2,...>"; }
45 if(! -r $opt_i) { &usage(); die "-E- Unable to open/find the input mkv file: $opt_i\n"; }
46 if(! defined $opt_o) {
47     if($opt_i =~ /^(\d+-\d+-\d+)/) { $opt_o = $1; }
48     elsif($opt_i =~ /^(.*?)\./) { $opt_o = $1; }
49 }
50
51 sub usage {
52     print "usage: $0 -i <input.mkv> -o <output_base_name> -c <chapter,chapter,...>\n";
53     print "  -c <chapter,chapter,...>  Specify which chapter from the mkv file to extract (all or 1,2,3, ...)\n";
54     print "  -i <input.mkv>            Specify the name of the input mkv file\n";
55     print "  -o <output_base_name>     Sets the base name to use when naming the output videos extracted from the chapters\n";
56     print "                            If not specified, the base name will be taken from the name of your input video\n";
57     print "  -e <ext>                  Specify the extension that should be used on the output files.\n";
58     print "                            This is automatically determined. You only need to specify it if autodetection fails.\n";
59     print "  -s                        Simulate mode. Don't actually make the video, but tell us what you will do\n";
60     print "\n";
61     return 1;
62 }
63
64 ####################################################################################################
65 # SUBROUTINES
66
67 sub detect_ext {
68     my ($ffmpeg_info) = @_;
69
70     my $h264 = 0;
71     my $h264_high = 0;
72     my $ac3 = 0;
73     my $aac = 0;
74     my $pcm_s16le = 0;
75     
76     foreach $line (@{$ffmpeg_info}) {
77         if($line =~ /Stream/) {
78             if($line =~ /h264/i) { $h264 = 1; }
79             if($line =~ /h264.*High/i) { $h264_high = 1; }
80             if($line =~ /ac3/i) { $ac3 = 1; }
81             if($line =~ /aac/i) { $aac = 1; }
82             if($line =~ /pcm_s16le/i) { $pcm_s16le = 1; }
83         }
84     }
85
86     # Quicktime/MOV
87     if($h264 && $pcm_s16le) { return "mov"; }
88     # MTS
89     if($h264_high && $ac3) { return "mp4"; }
90     # 3GP/MP4
91     if($h264 && $aac) { return "mp4"; }
92
93     return "UNKNOWN";
94 }
95
96 sub export_chapter {
97     my ($chapters, $chapter) = @_;
98     foreach $ch (@{$chapters}) {
99         if($ch == $chapter) { return 1; }
100     }
101     return 0;
102 }
103
104 ####################################################################################################
105 # MAIN
106
107 # Turn our list of chapters into an ordered array
108 my @chapters = sort(split(/,/, $opt_c));
109 my $all_chapters = 0;
110 if($opt_c =~ /all/) { $all_chapters = 1; }
111 print "-> Extracting the following chapters from $opt_i: @chapters\n";
112
113 # Use ffmpeg to extract the list of chapters available to rip
114 # For each chapter specified on the command line, use ffmpeg to extract a video clip from that chapter
115 my @ffmpeg_info = `$ffmpeg -i $opt_i 2>&1`;
116 foreach $line (@ffmpeg_info) {
117     if($line =~ /Chapter #\d+\.(\d+): start (\S+), end (\S+)/) {
118         $chapter = $1;
119         $start = $2;
120         if($start > 0) { $start += 1; } # Add some margin to prevent taking a piece of the previous clip
121         $end = $3;
122         $duration = $end - $start;
123         if($duration < 0) { die "-E- Unexpected negative duration detected for chapter $chapter\n"; }
124         if($all_chapters || &export_chapter(\@chapters,$chapter)) {
125             $ext = "UNKNOWN"; 
126             if(defined $opt_e) { $ext = $opt_e; }
127             else { $ext = &detect_ext(\@ffmpeg_info); } 
128             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"; }
129             $dstfile = $opt_o . ".c" . sprintf("%03d",$chapter) . "." . $ext;
130             print "-> Exporting $chapter to $dstfile: ";
131             $cmd = "ffmpeg -ss $start -t $duration -i $opt_i -map 0 -vcodec copy -acodec copy $dstfile 2>&1";
132             if(defined $opt_s) {
133                 print "$cmd\n";
134             } else {
135                 print "\n";
136                 print "$cmd\n";
137                 $errno = system("$cmd");
138                 $errno = $errno >> 8;
139                 if($errno > 0) { die "-E- ffmpeg encountered some errors with exit code $errno\n"; }
140             }
141         }
142     }
143 }
144
145 ####################################################################################################