How to make an online video transcoder in few minutes

Last year, I decided to put online a website able to transcode (almost) every kind of video format or codec into another (from avi to mkv, from xvid to h.264, and so on); it was called onlinevideotranscoder.com (yes, I admit that my fantasy isn’t very rich), and it had been around the web for some times before I decided to pull off the plug. I still think that an Internet-based video transcoder can be a very useful tool, so here are some lines of code to create it.

1. Requirements

The key engine behind this idea of video transcoder is FFmpeg, a “complete, cross-platform solution to record, convert and stream audio and video“. So, a little familiarity with this piece of (open and free) software is needed in order to achieve the goal. A PHP server is required too.

Hence the complete setup consists of a machine (or a web hosting) that runs Apache and FFmpeg. You can can put together a LAMP (or WAMP, or what else supporting PHP) + FFmpeg configuration at home or you can buy an hosting that offers this kind of solution by googling “ffmpeg web hosting” (advertising: I found Hosting Marketers very cheap and suited to my needs).

2. The structure

This transcoder is very simple: it needs an upload form with some parameters (the desired output formats) and some PHP to upload the video file onto the server, call ffmpeg to convert it, and to print the url of the transcoded video file in order to let the user to download it. Accordingly, we need 3 files: upload.php, output.php, and output_functions.php.

3. upload.php

Here is the upload form with the choice of the desired output format.

<div id="upload_form">
    <form action="output.php" name="form" method="post">
       <h1>Please choose the file to upload</h1>
	<input name="myfile" type="file" class="browse"/>
	<h1>Select the output format</h1>
	<select name="format" class="format_selection">
	   <option value="mp4">.MP4 (x264 - AAC)</option>
	   <option value="avi1">.AVI (x264 - AAC) (reccommended)</option>
	   <option value="avi2">.AVI (WMV2 - MP3)</option>
	   <option value="avi3">.AVI (WMV2 - WMA)</option>
	   <option value="avi4">.AVI (x264 - MP3)</option>
	   <option value="flv">.FLV (x264 - AAC)</option>
	   <option value="mov">.MOV (x264 - AAC)</option>
	   <option value="wmv">.WMV (WMV2 - WMA)</option>
	   <option value="f4v">.F4V (x264 - AAC)</option>
	   <option value="mkv1">.MKV (x264 - AAC) (reccommended)</option>
	   <option value="mkv2">.MKV (WMV2 - MP3)</option>
	   <option value="mkv3">.MKV (WMV2 - WMA)</option>
	   <option value="mkv4">.MKV (x264 - MP3)</option>
	</select>
<br /><input type="submit" value="Convert!" />
   </form>
</div>

It’s pretty self-explanatory, isn’t it? Please note that the key feature is the “option value” that will be the input parameter of the transcoding function.

4. output.php

This file contains the outputs of the upload and transcoding functions; nothing special.

<?php
  require_once('output_functions.php');
?>

5. output_functions.php

Finally here is the core of this web utility; it’s made by three parts: in the first part it checks that the input file has the right size and format, in the second part it uploads the input file to the server, and in the last one it makes the conversion by choosing the case that matches the “option value” set by the user.

<?php
	// Setting Maximum Allowed File Dimension in bytes (ATTENZIONE: trovare una funzione che peschi dalla max dim del file e del server)
	$max_dim = 104857600; // 100 Mb
	// Setting Target Upload Directory
	$target_dir = "upload/";
	// Setting Conversion Output Directory
	$output_dir = "output/";
	// Home
	$home = "HOMEPAGE URL";
	// Allowed File Formats
	$file_formats = array('.mp4','.avi','.wmv','.mkv','.3gp','.f4v','.flv','.mpg','.ogg','.mov','.mpeg','.yuv');
	$err = 0;
	// Checking File
	$file_field = 'myfile';
	if(!isset($_FILES[$file_field]) || $_FILES[$file_field]['size']==0) {
 		echo "No file has been selected!"; }
	elseif($_FILES[$field_field]['size']>$max_dim)	{
  		$max_dim=$max_dim/(1024^2);
		echo "The selected file is greater than the size limit of $max_dim Mbytes!"; }
	else {
		$filename=$_FILES[$file_field]['name'];
  		$format = strtolower(substr($filename, strrpos($filename, "."), strlen($filename)-strrpos($filename, ".")));
		if(!in_array($format,$file_formats)) {
			echo "File format not supported; <br/>Supported formats: ".implode(", ",$arr_ext)."<br/>";
			$err = 1; }
		}
	// File Uploading
	if($err==0) {
   		$t = time();
		// Inserting Safe Characters in the Filename
		$target_file = preg_replace("/[[:space:]]/" , "_", $_FILES[$file_field]['name']);
		$target_file = preg_replace("/[^[:alnum:]._-]/" , "", $target_file);
		// Removing the Extension in the Filename
   		$filename = substr($target_file,0,strrpos($target_file,'.'));
   		if(move_uploaded_file($_FILES[$file_field]['tmp_name'], $target_dir.$t.'_'.$target_file)) {
			chmod($target_dir.$t.'_'.$target_file,0777);
			echo "Upload Successful. Transcoding now...<br/>";
	// File Converting
			$format = $_POST['format'];
			$input = $target_dir.$t.'_'.$target_file;
			switch($format) {
				case 'mp4':
					$input = $target_dir.$t.'_'.$target_file;
					$vcodec = '-vcodec libx264 -vpre medium -vpre main -crf 20';
					$acodec = '-acodec libfaac -ab 192k';
					$output = $output_dir.$t.'_'.$filename.'.'.$format;
					$string = 'ffmpeg -i '.$input.' '.$vcodec.' '.$acodec.' '.$output;
					exec($string, $output_string, $retval);
				break;
				case 'avi1':
					$input = $target_dir.$t.'_'.$target_file;
					$vcodec = '-vcodec libx264 -vpre medium -vpre main -crf 20';
					$acodec = '-acodec libfaac -ab 192k';
					$format = 'avi';
					$output = $output_dir.$t.'_'.$filename.'.'.$format;
					$string = 'ffmpeg -i '.$input.' '.$vcodec.' '.$acodec.' '.$output;
					exec($string, $output_string, $retval);
				break;
				case 'avi2':
					$input = $target_dir.$t.'_'.$target_file;
					$vcodec = '-vcodec wmv2 -sameq';
					$acodec = '-acodec libmp3lame -ab 192k';
					$format = 'avi';
					$output = $output_dir.$t.'_'.$filename.'.'.$format;
					$string = 'ffmpeg -i '.$input.' '.$vcodec.' '.$acodec.' '.$output;
					exec($string, $output_string, $retval);
				break;
				case 'avi3':
					$input = $target_dir.$t.'_'.$target_file;
					$vcodec = '-vcodec wmv2 -sameq';
					$acodec = '-acodec wmav2 -ab 192k';
					$format = 'avi';
					$output = $output_dir.$t.'_'.$filename.'.'.$format;
					$string = 'ffmpeg -i '.$input.' '.$vcodec.' '.$acodec.' '.$output;
					exec($string, $output_string, $retval);
				break;
				case 'avi4':
					$input = $target_dir.$t.'_'.$target_file;
					$vcodec = '-vcodec libx264 -vpre medium -vpre main -crf 20';
					$acodec = '-acodec libmp3lame -ab 192k';
					$format = 'avi';
					$output = $output_dir.$t.'_'.$filename.'.'.$format;
					$string = 'ffmpeg -i '.$input.' '.$vcodec.' '.$acodec.' '.$output;
					exec($string, $output_string, $retval);
				break;
				case 'flv':
					$input = $target_dir.$t.'_'.$target_file;
					$vcodec = '-vcodec libx264 -vpre medium -vpre main -crf 20';
					$acodec = '-acodec libfaac -ab 192k';
					$output = $output_dir.$t.'_'.$filename.'.'.$format;
					$string = 'ffmpeg -i '.$input.' '.$vcodec.' '.$acodec.' '.$output;
					exec($string, $output_string, $retval);
				break;
				case 'f4v':
					$input = $target_dir.$t.'_'.$target_file;
					$vcodec = '-vcodec libx264 -vpre medium -vpre main -crf 20';
					$acodec = '-acodec libfaac -ab 192k';
					$output = $output_dir.$t.'_'.$filename.'.'.$format;
					$string = 'ffmpeg -i '.$input.' '.$vcodec.' '.$acodec.' '.$output;
					exec($string, $output_string, $retval);
				break;
				case 'mov':
					$input = $target_dir.$t.'_'.$target_file;
					$vcodec = '-vcodec libx264 -vpre medium -vpre main -crf 20';
					$acodec = '-acodec libfaac -ab 192k';
					$output = $output_dir.$t.'_'.$filename.'.'.$format;
					$string = 'ffmpeg -i '.$input.' '.$vcodec.' '.$acodec.' '.$output;
					exec($string, $output_string, $retval);
				break;
				case 'wmv':
					$input = $target_dir.$t.'_'.$target_file;
					$vcodec = '-vcodec wmv2 -sameq';
					$acodec = '-acodec wmav2 -ab 192k';
					$output = $output_dir.$t.'_'.$filename.'.'.$format;
					$string = 'ffmpeg -i '.$input.' '.$vcodec.' '.$acodec.' '.$output;
					exec($string, $output_string, $retval);
				break;
				case 'mkv1':
					$input = $target_dir.$t.'_'.$target_file;
					$vcodec = '-vcodec libx264 -vpre medium -vpre main -crf 20';
					$acodec = '-acodec libfaac -ab 192k';
					$format = 'mkv';
					$output = $output_dir.$t.'_'.$filename.'.'.$format;
					$string = 'ffmpeg -i '.$input.' '.$vcodec.' '.$acodec.' '.$output;
					exec($string, $output_string, $retval);
				break;
				case 'mkv2':
					$input = $target_dir.$t.'_'.$target_file;
					$vcodec = '-vcodec wmv2 -sameq';
					$acodec = '-acodec libmp3lame -ab 192k';
					$format = 'mkv';
					$output = $output_dir.$t.'_'.$filename.'.'.$format;
					$string = 'ffmpeg -i '.$input.' '.$vcodec.' '.$acodec.' '.$output;
					exec($string, $output_string, $retval);
				break;
				case 'mkv3':
					$input = $target_dir.$t.'_'.$target_file;
					$vcodec = '-vcodec wmv2 -sameq';
					$acodec = '-acodec wmav2 -ab 192k';
					$format = 'mkv';
					$output = $output_dir.$t.'_'.$filename.'.'.$format;
					$string = 'ffmpeg -i '.$input.' '.$vcodec.' '.$acodec.' '.$output;
					exec($string, $output_string, $retval);
				break;
				case 'mkv4':
					$input = $target_dir.$t.'_'.$target_file;
					$vcodec = '-vcodec libx264 -vpre medium -vpre main -crf 20';
					$acodec = '-acodec libmp3lame -ab 192k';
					$format = 'mkv';
					$output = $output_dir.$t.'_'.$filename.'.'.$format;
					$string = 'ffmpeg -i '.$input.' '.$vcodec.' '.$acodec.' '.$output;
					exec($string, $output_string, $retval);
				break;
			}
			if ($retval==0) {
				echo "<br/> Clip successfully converted.";
				echo "<br/> Click <a href=\"".$home.$output."\">here</a> to download your clip!";
				echo "<br/> (Right Click and Save As...)";
			}
			else {
				echo "<br/> Sorry, there was an error converting the clip. Retry or contact the webmaster."; 		}
		}
   		else {
			echo "File upload unsuccessful; please retry!"; }
  		}
  	else {
		echo "File upload unsuccessful; please retry!"; }
?>

6. Final notes

The solution I just showed in this post is obviously very limited; it lacks an higher degree of parameters’ customization and has no ajax feature because my aim was to remain as simple as possible, but I hope this code will be useful for more sophisticated Internet-based transcoding tools.

Below is attached a zip containing the whole onlinevideotranscoder.com website (just a bit more refined than what is written above).

5 comments

  1. Does not seem to correctly allow the file to upload. Each time I try it, I get :
    “File upload unsuccessful; please retry! ”

    What is the javascript for ?

      1. 777 permissions on the upload and download directories (should they be full paths, or root relative, in the configuration of output_functions.php?).
        It just does not seem to pass in the file…

        1. I used root relative directories, but it has to work anyways… Are you below upload size limit (it isn’t 100 megabytes as written in output_functions, it depends on php.ini of your server)? Are you trying to upload accepted file formats? 
          Can you send to me your whole project by mail (emanuele.colucci@gmail.com)? I could take a look, perhaps it’s a simple syntax error (it could be made by me too when I zipped the sources :D).

Leave a comment

Your email address will not be published. Required fields are marked *