ffmpeg and ffmpeg-php

 

Video Upload and conversion with ffmpeg

This is an example of how to implement a video upload, conversion and display with ffmpeg and ffmpeg-php. This is meant as a beginners guide to getting started with ffmpeg and ffmpeg-php and not a full implementation of how you would include it in a live site, as there is room for much improvement in error cathching and security. This demonstration assumes the installation of both ffmpeg and ffmpeg-php on the server that will be running these scripts.

We will be creating 3 files for this demonstration.

utils.php - For holding configuration values and our functions.
videoUpload.php - for processing the upload and calling the functions
viewVideo.php - for playing the video.

You will need a Flash video player for this to work. We will use the free flash player that is available from flowplayer.org. The link to the download of the free flowplayer is: 

http://flowplayer.org/releases/flowplayer/flowplayer-3.1.1.zip

This will consist of 3 files that we will be using:


flowplayer-3.1.1.swf
flowplayer.controls-3.1.1.swf
flowplayer-3.1.1.min.js

I have the two .swf (flowplayer-3.1.1.swf & flowplayer.controls-3.1.1.swf) files in a folder named 'swf' and the .js file (flowplayer-3.1.1.min.js) in a folder name '.js' in my web root. You will need 3 other folders in your web root that are readable and writable by the web server:

uploaded_videos/
flash_videos/
video_thumbnails/

These will be where the uploaded and converted videos and thumbnails will be stored.

First we will create the utils.php script which will contain constants used by our other scripts and the functions that will display the web page and process the video conversion. This file should be in your web root.

utils.php

<?php
/***** DEFINE CONSTANTS *****/
// DIRECTORY PATHS
define('BASE_PATH', realpath(dirname(__FILE__)) . '/');
define('UPLOAD_PATH', BASE_PATH . 'uploaded_videos/');
define('FLASH_PATH', BASE_PATH . 'flash_videos/');
define('THUMBNAIL_PATH', BASE_PATH . 'video_thumbnails/');
define('SHOW_MEDIA_INFO', true); // whether to print information about the video
define('FRAME_RATIO', 10); // sets how far into video to grab the thumbnail, ie. 1/x

// VIDEO SETTINGS
define('BIT_RATE', 32000);
define('SAMPLE_RATE', 22050);
define('MAX_WIDTH', 320); // place to set the max video width
define('MIN_DURATION', 1); // sets the shortest vid length. Leave at least at one to eliminate image uploads

// other code

First we define the paths to the directories we will need. We also have a trigger that we can use to display additional information about the video as well as a constant to define where we want to take the thumbnail from.


Now we will define functions to display the webpage and upload form and to execute the transfer.

utils.php(cont')

// other code

/***** FUNCTIONS  *****/
// function to output the html
function displayForm(){
echo <<<EOT
    <html>
    <head>
        <title>upload test</title>
    </head>
    <body>
    <h2>Upload A Video</h2>
    <form action="uploadVideo.php" method="post" enctype="multipart/form-data">
        <p>
            <label for="video_upload">Upload Video</label>
            <input type="file" name="video" />
        </p>

        <p><input type="submit" name="submit" value="Upload"></p>
    </form>
    </body>
    </html>
EOT;
}

// function that executes ffmpeg command to convert uploaded video to flash
function ffmpegConvert($params = array()){
    // build the command
    $command = "ffmpeg -i " . $params['input'] . " -s " . $params['width'] . "x" . $params['height'] . " -sameq -ab 32000 -ar 22050 -f flv " . $params['output'] . " | flvtool2 -U " . $params['output'] . ' &';
   
    // output information if set
    if(SHOW_MEDIA_INFO){
        echo 'Command executed to convert this video...<br/><b>';
        echo $command . '</b><br />';
    }
   
    // estimate the time it will take ffmpeg to do its work so script wont time out
    // I am using a conversion speed of 40 fps, this should allow more than enough time
    $conversionTime = ceil($params['frames'] / 40);
    $iniExecutionTime  = ini_get('max_execution_time');
   
    // set ini param if estimated time longer than value in php.ini
    if ($conversionTime > $iniExecutionTime) {
        ini_set('max_execution_time', $conversionTime);
        if (SHOW_MEDIA_INFO)
            echo 'ini_set for conversion time of ' . $conversionTime . ' seconds<br/>';
    }
   
    // execute the command
    exec($command);
}
// end of file
?>


The displayForm() function outputs the entire web page including the upload form. We have the method set to uploadVideo.php which we will create next.

 The ffmpegConvert() function is used to build and execute the command. Values passed in by the $param array will set the input/output directories, the width/height, and the total number of frames to estimate the amount of time ffmpeg will need to convert the video.

 I have found that if the php.ini value for max_execution_time is set to low, the script will terminate the shell execution before larger files have finished converting. This setting should allow more than enough time for ffmpeg to do its work.

 The uploadVideo.php file will check whether the form has been submitted, showing the html if it hasn't, and processing it if it has and then showing the form again for another upload after its done.


uploadVideo.php

<?php
require_once('utils.php');

// check whether form has been submited
if (isset($_POST['submit'])) {
    // get the post info
    $file = $_FILES['video'];
   
    // get the file name
    $name = $file['name'];
   
    // replace any spaces in the name with underscores
    $name = str_replace(' ', '_', $name);
   
    //strip the file extension and make flash extension for destination file
    $baseName = substr($name, 0, strrpos($name, '.'));
    $flashName = $baseName . '.flv';
    $imagePath  = './video_thumbnails/' . $baseName . '.jpg';
    $tmpImage   = './video_thumbnails/' . $baseName . '_tmp.jpg';
    $input = UPLOAD_PATH . $name;
    $output = FLASH_PATH . $flashName;
   
    // move the uploaded file to its place
    if (move_uploaded_file($file['tmp_name'], $input))
        echo 'Upload moved successfully...<br/>';
    else
        echo '<span style="color:#f00;font-weight:bold;">Error uploading file</span>';

// other code


So we have taken the file, cleaned up its name, made a copy of it with the '.flv' extension and moved the file from the temporary directory to our uploaded video directory. There are many chances for improvement with error checking in this script, but for demonstration purposes this will work fine.
Next we will create a movie object using the ffmpeg-php extension. This allows us to access alot of valuable information about the uploaded video. With this information we can determine if the file that was uploaded was actually a video or not and act accordingly.

uploadVideo.php(cont')

// other code
   
    // get and display information about the video with ffmpeg-php
    if(@$mov = new ffmpeg_movie($input)){
        echo "Upload is a valid media...<br />";
        $frame = $mov->getFrame(ceil($mov->getFrameCount() / FRAME_RATIO));
        if (SHOW_MEDIA_INFO){
            echo "<pre>";
                printf("file name = %s\n", basename($mov->getFileName()));
                printf("duration = %s seconds\n", $mov->getDuration());
                printf("frame count = %s\n", $mov->getFrameCount());
                printf("frame rate = %0.3f fps\n", $mov->getFrameRate());
                printf("comment = %s\n", $mov->getComment());
                printf("title = %s\n", $mov->getTitle());
                printf("author = %s\n", $mov->getAuthor());
                printf("copyright = %s\n", $mov->getCopyright());
                printf("get bit rate = %d\n", $mov->getBitRate());
                printf("has audio = %s\n", $mov->hasAudio() == 0 ? 'No' : 'Yes');
       
            // test if the upload has video
            if ($mov->hasVideo()){
                // print out the video specific information
                printf("frame height = %d pixels\n", $mov->getFrameHeight());
                printf("frame width = %d pixels\n", $mov->getFrameWidth());
                printf("get video stream id= %s\n", $mov->getVideoStreamId());
                printf("get video codec = %s\n", $mov->getVideoCodec());
                printf("get video bit rate = %d\n", $mov->getVideoBitRate());
                printf("get pixel format = %s\n", $mov->getPixelFormat());
                printf("get pixel aspect ratio = %s\n", $mov->getPixelAspectRatio());
            }
   
            // display information about frame grabbed earlier
            if ($frame){
                // print some info about the frame
                printf("get frame = %s\n", is_object($frame) ? 'true' : 'false');
                printf("get frame number = %d\n", $mov->getFrameNumber());
                printf("get frame width = %d\n", $frame->getWidth());
                printf("get frame height = %d\n", $frame->getHeight());
            }
            echo '</pre>';
       
        }
       
// other code


If the SHOW_MEDIA_INFO constant in utils.php is set to true, it will out put all the information above to the screen. We also grab a frame from the movie using the value from the FRAME_RATIO constant in utils.php. In this instance it will grab a frame 1/10th of the way in the video. 

 Next we will test the video length to make sure that it is long enough. Ffmpeg-php will recognize images as a valid video format allowing them by our first verification check. If you want to eliminate images we set the MIN_DURATION constant to at least one second.

Then we will grab some size dimensions to see if we need to reduce the input video to our desired output flash size. Flash video expands nicely and will look great at full screen even when created with a width of only 320 pixels. Saving them in this size also helps keep file sizes and conversion times down.

uploadVideo.php(cont')


// other code

        // make sure video is at least MIN_DURATION long
        if ($mov->getDuration() < MIN_DURATION) {
            echo '<span style="color:#f00;font-weight:bold;">Video not long enough, deleting file....</span>';
            exec('rm -f '. $input);
            displayForm();
            exit;
           
        } else { // CHECKS OUT OK
           
            // Set up some parameters for video and thumbnails
            $width = $mov->getFrameWidth();
            $height = $mov->getFrameHeight();
           
            // tstore origional values for use with thumbnail resize
            $originalWidth = $width;
            $originalHeight = $height;
           
            // if video is very large, we reduce it to our acceptable size
            if ($width > MAX_WIDTH) {
                $ratio = $height / $width;
                $width = MAX_WIDTH;
                $height = MAX_WIDTH * $ratio;
            }
           
            // we have to make sure the width/height is in even numbers for ffmpeg
            if ($width % 2 != 0)
                $width = $width + 1;
            if ($height %2 != 0)
                $height = $height + 1;

// other code

ffmpeg only likes to work with even number widths and heights, so we make adjustments for that just in case.

 Next we will work on saving a thumbnail to use as a link to the video. Since we took the frame from the original video, if we had to resize the video we will have to resize this image as well, which we will do by saving the image temporarily and then saving a resized copy and deleting the temp image. When this is done we will output the image as a link to the viewVideo.php page.

uploadVideo.php(cont')


// other code
    
            // force the frame to a gd image and set up thumb path info
            $gdImage = $frame->toGDImage();
           
            // verify, convert and save the image as a jpeg
            if ($gdImage){
           
                // check if image needs to be resized since it was taken before video resized
                if ($originalWidth > MAX_WIDTH) {
                   
                    // save the temporary larger copy
                    $image = imagejpeg($gdImage, $tmpImage);
                   
                    //get the aspect ratio and set new dimensions
                    $ratio = $originalHeight / $originalWidth;
                    $newwidth = MAX_WIDTH;
                    $newheight = MAX_WIDTH * $ratio;
                   
                    // create the GD destination image holder
                    $destImage = imagecreatetruecolor($newwidth, $newheight);
                    // get the temporary image
                    $source = imagecreatefromjpeg($tmpImage);
                    // resize the temporary image
                    imagecopyresized($destImage, $source, 0, 0, 0, 0, $newwidth, $newheight, $width, $height);
                    // and save it
                    $resizedImage = imagejpeg($destImage, $imagePath);
                    // delete the temporary image
                    unlink($tmpImage);
                   
                } else {
                    // save image as is
                    $image = imagejpeg($gdImage, $imagePath);
                }

                // display the image as a link to preview the video
                echo '<h3>Uploaded Video ' . $name . '</h3><a href="./viewVideo.php/?video=' . $baseName . '" target="_blank"><img src="'.$imagePath. '" /></a><br/><br />';
            } else {
                echo '<span style="color:#f00;font-weight:bold;">error with frame to gd image conversion...</span>';
            }

// other code


Now all we have to do is set up our $params array and call the ffmpegConvert() function to convert the video. As a convienience we will redisplay the form sou the user can upload another video.

uploadVideo.php(cont')


// other code
           
            $frames = $mov->getFrameCount();
           
            // set parameters for conversion
            $params = array('input'  => $input,
                            'output' => $output,
                            'width'  => $width,
                            'height' => $height,
                            'frames' => $frames);
           
            // convert the video
            ffmpegConvert($params);
            echo '<b>Your video is being converted. this may take a few minutes....</b><br/>';
           
        }
        // display form to allow for another upload and exit
        displayForm();
        exit;
       
    } else {
        // if uploaded file was not a valid format.. delete it
        echo '<span style="color:#f00;font-weight:bold;">Not a valid file format, deleting file....</span>';
        exec('rm -f '.  $input);
        displayForm();
    }
       
} else { // form not submitted, so display the web page and form
    displayform();
}
?>

 Now we will create the viewVideo.php file that will display the video. This file makes use of the flowplayer flash video player. Make sure your paths to the flowplayer files are set correctly within this file.

 First we will check to make sure the video variable is set, and if not redirect to the upload form. Then we will echo the page with the video variable in the javascript to call the video we just uploaded, which will display the flash video as desired.

viewVideo.php


<?php
if(isset($_GET['video']) && strlen(trim($_GET['video'])) > 1){
    $video = $_GET['video'] . '.flv';
} else {
    header('Location: /uploadVideo.php');
}
echo <<<EOT
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>

<head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <title>Test Flash Player</title>
    <script type="text/javascript" src="/js/flowplayer-3.1.1.min.js" charset="utf-8"></script>
   
</head>

<body>
   
    <div id="player" style="display:block;width:520px;height:330px" >
       
    </div>
   
    <script>
        flowplayer("player", "/swf/flowplayer-3.1.1.swf", "http://localhost/flash_videos/$video");
    </script>
   
</body>


</html>
EOT;
?>

I hope this help demonstrate how to accept video uploads and process them into a flash video files for display on your web site. Again, there is a lot of room for improvement for a live implementation of this, but it should give you an idea of how to get started.

 
 

Leave a Comment Below

 
From ericopter on Mar 4, 2010
I think what you want to know is if you could use this example to convert the video after you use another program to handle the file upload. That is definitly possible. You would just need to use the applicable part of the code where it makes sense, ie. after the upload is complete, to call the conversion on the video. This particular script is more for demonstration purposes. I will be posting an entire article on how to apply this in a more functional manner, including storing info about the video in a database, soon.
 
 
From deivys on Mar 4, 2010
well first of all let me congratulate you on your great tuto esta muy bueno my question is this if for example I use your script with SWFUpload eg FancyUpload or they work from the upload.php file as you would to modify the code to run it yours with that???? because your code is all together and I'm not a programming expert if I'm through I hope your response could help as soon as possible note: sorry for the English as it is translated by google xD
 
 
 

Leave A Comment

CAPTCHA image