Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change

Convert videos for my iPhone

by kyle (Abbot)
on Feb 28, 2009 at 14:56 UTC ( #747163=CUFP: print w/replies, xml ) Need Help??

Since I got an iPhone back when they were new, I've been using a commercial application to convert movies and TV shows from the web for it. It runs on my old laptop, and converting any given video takes about five times longer than the duration of the video. I lived with it since it worked well and I didn't want to try to figure out how to use a command line tool on my faster Linux box.

Recently I got every episode of Buffy the Vampire Slayer in some format that my commercial application couldn't understand, and I was forced to go to the trusty command line. Those command lines being what they are, I naturally wrapped it all up in Perl.

One big problem I had was to get rid of the (Danish) subtitles, so that's part of this code's behavior. Otherwise, it simply takes what you give it and scales it down to the iPhone's resolution and reencodes it to an .m4v file. The gory details are in POD, readily available via Pod::Usage.


Update: There are a couple of details I left out.

It uses mencoder to do the actual work.

It can fork multiple concurrent jobs to take advantage of multiple cores using code derived from tilly's Run commands in parallel.

#!/usr/bin/perl use strict; use warnings; use 5.010; use File::Find; use English '-no_match_vars'; use Getopt::Long; use Pod::Usage; my %OPT = ( unlink => 0, loud => 0, quiet => 0, abits => 128, vbits => 800, jobs => 1, ); Getopt::Long::Configure('bundling'); GetOptions( 'quiet|q' => \$OPT{quiet}, 'loud' => \$OPT{loud}, 'unlink|delete' => \$OPT{unlink}, 'audio-bitrate=i' => \$OPT{abits}, 'video-bitrate=i' => \$OPT{vbits}, 'jobs|j=i' => \$OPT{jobs}, 'help|h|?' => sub { pod2usage(1) }, 'man' => sub { pod2usage( -exitstatus => 0, -verbose => 2 ); }, ) or pod2usage(2); my @dirlist = grep -d, @ARGV; my @filelist = grep -f, @ARGV; if ( ! @ARGV ) { @dirlist = ( '.' ); } if ( @dirlist ) { find( \&wanted, @dirlist ); } if ( $OPT{jobs} > 1 ) { convert_many( $OPT{jobs}, sort @filelist ); } else { foreach my $filename ( sort @filelist ) { convert( $filename ); } } exit; # sub convert_many { my ( $jobs_wanted, @filelist ) = @_; my %running; while ( @filelist || %running ) { if ( @filelist && $jobs_wanted > scalar keys %running ) { my $file = shift @filelist; my $pid = fork // die "Can't fork: $!"; if ( $pid ) { # parent $running{ $pid } = $file; } else { # child convert( $file ); exit; } } else { my $pid = wait; if ( ! defined delete $running{ $pid } ) { die "reaped unknown child PID '$pid'"; } if ( $CHILD_ERROR ) { die "child exited with status ($CHILD_ERROR)"; } } } return; } # From the mplayer man page: # It plays most MPEG/VOB, AVI, ASF/WMA/WMV, RM, QT/MOV/MP4, Ogg/OGM, # MKV, VIVO, FLI, NuppelVideo, yuv4mpeg, FILM and RoQ files, # supported by many native and binary codecs. You can watch VCD, # SVCD, DVD, 3ivx, DivX 3/4/5, WMV and even H.264 movies, too. sub wanted { return if ! -f; return if ! m{ \. (?: avi | mkv | mpe?g | wmv | asf | rm | qt | mov | mp4 | ogm | fli ) \z }xmsi; push @filelist, $File::Find::name; return; } sub convert { my ( $src_file ) = @_; my $src_file_quoted = shell_quote( $src_file ); ( my $new_file = $src_file ) =~ s{ \. [^.]+ \z }{.m4v}xms; if ( $new_file eq $src_file ) { die "renamed '$src_file' to the same filename"; } my $new_file_quoted = shell_quote( $new_file ); # # Many of these options came from a web page I've since lost. # To remove subtitles, I added -noass and -noautosub, but that # didn't work. What worked was to add 'expand' to the end of # the -vf filter chain and add -noautoexpand to keep mencoder # from putting its own expand at the end (which added subtitles). # my $cmd = join q{ }, qq{mencoder $src_file_quoted -o $new_file_quoted}, qq{-vf dsize=480:320:0,scale=0:0,expand -noautoexpand}, qq{-oac faac -faacopts mpeg=4:object=2:raw:br=$OPT{abits}}, qq{-ovc x264 -x264encopts nocabac:level_idc=30:bframes=0:globa +l_header:bitrate=$OPT{vbits}:frameref=6:partitions=all}, q{-of lavf -lavfopts format=mp4}, q{-noass -noautosub}, (q{-really-quiet > /dev/null 2> /dev/null}) x! $OPT{loud} ; if ( ! $OPT{quiet} ) { say scalar localtime(), " $new_file_quoted"; } system( "$cmd" ) == 0 or die "MENCODER GAVE ERROR EXIT STATUS ($CHILD_ERROR) for $cm +d\n"; if ( $OPT{unlink} ) { unlink $src_file or die "Can't unlink '$src_file': $!"; } return; } sub shell_quote { my ($s) = @_; $s //= q{}; $s =~ s{ ([\\"`\$]) }{\\$1}xmsg; return qq{"$s"}; } __END__ =pod =head1 NAME - Convert videos for the iPhone =head1 SYNOPSIS [options] [files/directories] Options: -h, --help brief help message --man full documentation --unlink delete originals after converting -j, --jobs number of conversions to run at once --audio-bitrate set output audio bitrate --video-bitrate set output video bitrate -q, --quiet output only errors --loud output progress and more =head1 DESCRIPTION This uses mencoder to convert videos it can read into videos that can play on an Apple iPhone. =head1 USAGE Just run this with a list of files and/or directories as arguments. It will traverse directories recursively to find video files. If no arguments are given, it will default to looking in the current directory. All files it finds are converted to .m4v files suitable to play on an Apple iPhone. By default it will output one line per file with the date and output filename when processing of that file starts. Any file explicitly listed on the command line will be processed regardless of its name. Files discovered through directory traversal must match a list of file extensions. Output file names are the same as the input file names with the extension changed .m4v. =head1 OPTIONS =over 8 =item B<--help> =item B<-h> Print a brief help message and exit. =item B<--man> Print the full documentation and exit. =item B<--delete> =item B<--unlink> Delete the original file after a successful conversion. =item B<--jobs> =item B<-j> Specifies the number of conversions to run simultaneously. =item B<--audio-bitrate> Default: 128 Set the bitrate for audio output. =item B<--video-bitrate> Default: 800 Set the bitrate for video output. =item B<--quiet> =item B<-q> Output only errors during processing. =item B<--loud> This allows the normal output from mencoder during encoding. =back =head1 DEPENDENCIES This uses mencoder to do the real work, so that has to be in your path. The mencoder invocation uses AAC for audio encoding and libavformat for the container format, so those have to be available. =head1 BUGS AND LIMITATIONS There really is not nearly the control available that mencoder provides if used directly. In particular, the code is written explicitly to try to strip subtitles where possible. The list of file extensions that it will recognize as videos during searches is hard coded and might not be comprehensive. =head1 DIAGNOSTICS If mencoder exits with a non-zero exit status, processing will halt. Without the B<--loud> option, you might not know why (mencoder's output is normally supressed). =over =item MENCODER GAVE ERROR EXIT STATUS ($CHILD_ERROR) for $cmd This is the general error any time mencoder fails for any reason. The mencoder command line is given at the end in case you want to try it manually or look for obvious problems there. =item renamed '$src_file' to the same filename This probably means that the source file name ends in .m4v. It's refusing to overwrite it. =item Can't fork Try running without the B<--jobs> option. =item reaped unknown child PID '$pid' A call to wait() got back a child process ID for a child it does not remember spawning. This should never happen. =item child exited with status ($CHILD_ERROR) If running with multiple B<--jobs>, this is the error that appears if a job fails for some reason. Hopefully there's a more descriptive message above this one. =item Can't unlink '$src_file' Try running without the B<--unlink> option. For some reason, it can't delete the original files when it's done with them. =back =head1 COPYRIGHT AND LICENSE Copyright (c) 2009 Kyle Hasselbacher. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 AUTHOR Kyle Hasselbacher <> =cut

Replies are listed 'Best First'.
Re: Convert videos for my iPhone
by telemachus (Friar) on Mar 02, 2009 at 16:34 UTC
    You have to love a program that has a -really-quiet flag.

    As for the subtitles, maybe that's your computer's way of saying you should stop downloading torrents from Danish pirate sites?

Re: Convert videos for my iPhone
by generator (Pilgrim) on Apr 27, 2009 at 04:55 UTC
    I'm curious. What was your conversion time to runtime ratio on the perl solution?



      All the actual conversion is done by mencoder, so performance will really depend on that. For an individual file, conversion takes about the same as run time—on my system, anyway. The real victory comes from using more than one core to convert multiple files at once.

Re: Convert videos for my iPhone
by kyle (Abbot) on Dec 16, 2009 at 16:18 UTC

    I was asked privately whether this could be used to convert RealVideo to mp4 without losing resolution. Rather than try to fit my answer into a private /msg, I'm posting here.

    Whether this converts RealVideo at all depends on the mencoder tool that it uses to do the conversion. I see .rm is a supported extension, so I'm guessing it does, but YMMV.

    If it will do the conversion, it will try to reduce the resolution to that of an iPhone. If you want to change that, look in sub convert, which composes a big command line and executes it with system. In the snippet below, I've bolded the (hard coded) resolution constraints.

        my $cmd = join q{ },
            qq{mencoder $src_file_quoted -o $new_file_quoted},
            qq{-vf dsize=480:320:0,scale=0:0,expand -noautoexpand},
            qq{-oac faac -faacopts mpeg=4:object=2:raw:br=$OPT{abits}},

    I've since forgotten exactly what that all does or exactly how to change it to keep the original resolution, but I found the mencoder documentation good enough to figure it out. What I remember is that the code, as written, fits the input video into the constraints given while retaining the aspect ratio (so a tall image will fit into the screen with black boxes at the sides instead of being stretched into a too-wide image).

    The whole program could be seen as a really overblown wrapper around that one big command line. Changing what it does with videos you hand it depends on changing the command that it builds.

    I hope this helps.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: CUFP [id://747163]
Approved by Corion
Front-paged by Corion
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (5)
As of 2018-05-24 20:34 GMT
Find Nodes?
    Voting Booth?