Tuesday, July 26, 2011

How to Create a Web-Based Video Converter

My wife teaches cooking classes near Boston and blogs about food. Recently, she's become interested in video. She's already made a few videos using Windows Movie Maker (WMM), like this one on how to dice an onion. But, she's finding that free movie editing software just doesn't cut it. The kicker was when she published a video, learned that the volume was too low and couldn't find any way to raise the volume in WMM---it only allows limited volume adjustment.

She had already started researching video software options and settled on Sony Vegas. Yes, people complain that it crashes and/or runs slowly, but those same people are trying to process relatively large movies. My wife is looking to publish 5 minute non-HD videos on YouTube. So far, Sony Vegas has worked well for her. The one problem she's encountered is that Sony Vegas can't import Flip Video AVI files. She found a free Windows-based converter. But, it leaves a blatant watermark and screwed-up a half-second of the audio track.

I figured this would be an easy problem to solve with Linux software. Sure enough, a bit of searching and I discovered ffmpeg, which is available in Debian. After a few minutes of man-page reading, I had a command-line to perform the conversion:

ffmpeg -i myvideo.avi -target ntsc-vcd myvideo.mpg
Note that this generates NTSC video. If you're in Europe, you might want PAL, which you'd get by changing the target to pal-vcd.

But, this wouldn't cut it. My wife didn't want to have to copy to Linux, convert, then copy back or wait for me to get home just so she could start editing a video. So, I set to work on creating a web-based converter. Creating a script to upload the video is straightforward and easy to find. Here's an example of file upload HTML and PHP. But, what this page doesn't tell you about are the hard limits PHP has on file and memory sizes. Here is a PHP "bug" which describes the max file size ("exceeds the limit of 8388608 bytes") problem I quickly encountered. Sniper provides the config settings that need to be edited:

post_max_size = 256M
upload_max_filesize = 256M
memory_limit = 256M
I modified these settings in /etc/php5/apache2/php.ini, restarted my web server, and then was able to upload video files larger than 8 megs. The final question was how to push the converted video back to my wife's web browser. For some reason, all the pages I found on how to upload a file didn't mention anything about the possibility of pushing binary data back to the user. Finally, I stumbled upon the PHP readfile function. Occasionally, PHP is nice in that it provides tools and examples for what you probably want to do, like push an entire file to a user's web browser. The readfile manual page provides a full example for how to do this, including the necessary HTTP headers and proper output buffer management.

Here's what I ended-up with. Note that this script is unsafe due to the fact that it executes a shell command. Also, it relies on /tmp being the usual "temp" directory and can fail if a file already exists with the name $outfile. But, it serves it's purpose for me.

<?php
if ($_FILES["video"]["name"]) {
  $pattern = '/(.+)\.avi$/i';
  $replacement = '${1}.mpg';
  $outfile = preg_replace($pattern, $replacement, basename($_FILES["video"]["name"]));
  $cmd = "ffmpeg -i " . $_FILES["video"]["tmp_name"] . " -target ntsc-vcd " . $outfile;
  chdir("/tmp");
  shell_exec($cmd);
  header('Content-Type: application/octet-stream');
  header('Content-Disposition: attachment; filename=' . $outfile);
  header('Content-Transfer-Encoding: binary');
  header('Expires: 0');
  header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  header('Pragma: public');
  header('Content-Length: ' . filesize($outfile));
  ob_clean();
  flush();
  readfile($outfile);
  unlink($outfile);
  exit;
} else {
?>
<html>
<body>
<form enctype="multipart/form-data" action="video.php" method="POST">
Choose a file to upload: <input name="video" type="file" /><br />
<input type="submit" value="Upload Video" />
</form>
</body>
<?php
}
?>

1 comment: