From 75451c9e69007a30fbbff4573a787a8b3fa09f62 Mon Sep 17 00:00:00 2001 From: "Alan J. Pippin" Date: Fri, 18 Nov 2011 18:37:24 -0700 Subject: [PATCH] First full flush of functionality to enable chapters to be extracted from mkv files --- mkv_extract_chapter | 131 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 3 deletions(-) diff --git a/mkv_extract_chapter b/mkv_extract_chapter index 2aa4c54..53d3cb7 100755 --- a/mkv_extract_chapter +++ b/mkv_extract_chapter @@ -1,14 +1,139 @@ +#!/usr/bin/perl +# Author: Alan J. Pippin +# Description: Extract the given chapter(s) from an mkv file into separate video files # MOV: +# major_brand : qt # Video: h264 (avc1 / 0x31637661) # Audio: pcm_s16le -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 # MTS: -# Video: h264 (High) -# Audio: ac3 +# Video: h264 (High) (HDMV / 0x564D4448) +# Audio: ac3 (AC-3 / 0x332D4341) # MP4: # Video: h264 (Baseline) (avc1 / 0x31637661) # Audio: aac (mp4a / 0x6134706D) +# 3GP: +# Video: h264 (Baseline) (avc1 / 0x31637661) +# Audio: aac (mp4a / 0x6134706D) + +# FFMPEG EXAMPLE CMD: +#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 + +#################################################################################################### +# Includes +use File::Copy; +use File::Basename; +use Getopt::Std; + +#################################################################################################### +# Configuration parameters - CHANGE THESE TO SUITE YOUR NEEDS +my $ffmpeg=`which ffmpeg`; chomp($ffmpeg); +my $tmpfile = `tempfile`; chomp($tmpfile); +#################################################################################################### + +#################################################################################################### +# Command Line Options +getopts("c:i:o:se:"); + +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 \n"; } +if(! defined $opt_c) { &usage(); die "-E- Missing required chapter: -c "; } +if(! -r $opt_i) { &usage(); die "-E- Unable to open/find the input mkv file: $opt_i\n"; } +if(! defined $opt_o) { + if($opt_i =~ /^(\d+-\d+-\d+)/) { $opt_o = $1; } + elsif($opt_i =~ /^(.*?)\./) { $opt_o = $1; } +} + +sub usage { + print "usage: $0 -i -o -c \n"; + print " -c Specify which chapter from the mkv file to extract (all or 1,2,3, ...)\n"; + print " -i Specify the name of the input mkv file\n"; + print " -o Sets the base name to use when naming the output videos extracted from the chapters\n"; + print " If not specified, the base name will be taken from the name of your input video\n"; + print " -e 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 "\n"; + return 1; +} + +#################################################################################################### +# SUBROUTINES + +sub detect_ext { + my ($ffmpeg_info) = @_; + + my $h264 = 0; + my $h264_high = 0; + my $ac3 = 0; + my $aac = 0; + my $pcm_s16le = 0; + + foreach $line (@{$ffmpeg_info}) { + if($line =~ /Stream/) { + if($line =~ /h264/i) { $h264 = 1; } + if($line =~ /h264.*High/i) { $h264_high = 1; } + if($line =~ /ac3/i) { $ac3 = 1; } + if($line =~ /aac/i) { $aac = 1; } + if($line =~ /pcm_s16le/i) { $pcm_s16le = 1; } + } + } + + if($h264 && $pcm_s16le) { return "mov"; } + if($h264_high && $ac3) { return "mts"; } + if($h264 && $aac) { return "mp4"; } + + return "UNKNOWN"; +} + +sub export_chapter { + my ($chapters, $chapter) = @_; + foreach $ch (@{$chapters}) { + if($ch == $chapter) { return 1; } + } + return 0; +} + +#################################################################################################### +# MAIN + +# Turn our list of chapters into an ordered array +my @chapters = sort(split(/,/, $opt_c)); +my $all_chapters = 0; +if($opt_c =~ /all/) { $all_chapters = 1; } +print "-> Extracting the following chapters from $opt_i: @chapters\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`; +foreach $line (@ffmpeg_info) { + if($line =~ /Chapter #\d+\.(\d+): start (\S+), end (\S+)/) { + $chapter = $1; + $start = $2; + $end = $3; + $duration = $end - $start; + if($duration < 0) { die "-E- Unexpected negative duration detected for chapter $chapter\n"; } + if($all_chapters || &export_chapter(\@chapters,$chapter)) { + $ext = "UNKNOWN"; + if(defined $opt_e) { $ext = $opt_e; } + else { $ext = &detect_ext(\@ffmpeg_info); } + if($ext =~ /UNKNOWN/) { die "-E- Unable to determine the file type/extension to use for the output videos. Specify it with the -e option.\n"; } + $dstfile = $opt_o . ".c" . sprintf("%03d",$chapter) . "." . $ext; + print "-> Exporting $chapter to $dstfile: "; + $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"; + } else { + print "\n"; + $errno = system("$cmd"); + $errno = $errno >> 8; + if($errno > 0) { die "-E- ffmpeg encountered some errors with exit code $errno\n"; } + } + } + } +} + +#################################################################################################### -- 2.34.1