...

View Full Version : FFMPEG Mini Library



idalatob
09-12-2012, 07:07 PM
Hi guys,

Hacked together a quick FFMPEG php library, as that compiling one is a pain. Its in a very rudimentary form at the moment and any suggestions or feedback or indeed contributions would be appreciated!

file: class.ffmpeg.php


<?php
/**
* This wont work on windows. Sorry
*/

require_once("class.ffmpeg.config.php");

class ffmpegPHP extends ffmpegPHPConfig {

protected $_inputVideos = array();
protected $_outputVideos = array();

private $_pathToFFMPEG = "";
private $_pathToFFMPEG2THEORA = "";
private $_pathToFFPROBE = "";

private $_infoCache = array();

public function __construct() {
//check if all the requirements are met

if (is_null(ffmpegPHPConfig::pathToFFMPEG)) {
$pathToFFMPEG = trim(exec('which ffmpeg'));
if ($pathToFFMPEG == "") {
throw new Exception("FFMPEG not located");
}
$this->_pathToFFMPEG = $pathToFFMPEG;
} else {
$this->_pathToFFMPEG = ffmpegPHPConfig::pathToFFMPEG;
}

if (is_null(ffmpegPHPConfig::pathToFFMPEG2THEORA)) {
$pathToFFMPEG2THEORA = trim(exec('which ffmpeg2theora'));
if ($pathToFFMPEG2THEORA == "") {
throw new Exception("FFMPEG2Theora not located");
}
$this->_pathToFFMPEG2THEORA = $pathToFFMPEG2THEORA;
} else {
$this->_pathToFFMPEG2THEORA = ffmpegPHPConfig::pathToFFMPEG2THEORA;
}
if (is_null(ffmpegPHPConfig::pathToFFPROBE)) {
$pathToFFPROBE = trim(exec('which ffprobe'));
if ($pathToFFPROBE == "") {
throw new Exception("ffprobe not located");
}
$this->_pathToFFPROBE = $pathToFFPROBE;
} else {
$this->_pathToFFPROBE = ffmpegPHPConfig::pathToFFPROBE;
}


}

/**
* Execute a shell command
*
*@param string $cmd 'The shell command to execute'
*@param string $dataPipe 'The pipe to return content from. 0 = stdin, 1 = stdout, 2 = stderr'
*@return string 'Returned data from process'
*/
private function _openProcess($cmd, $dataPipe = 2) {
//create descriptor
$descriptorspec = array(
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
2 => array("pipe", "w"), // stderr
);
//open the process
$process = proc_open($cmd, $descriptorspec, $pipes, dirname(__FILE__), null);
$rawInfo = stream_get_contents($pipes[$dataPipe]); //get content from the pipe
proc_close($process); //close the process
return $rawInfo;
}

/**
* Add an input video to the current processing chain
*
*@param string $path 'Path to the video input file'
* @return boolean 'Returns false if the video could not be located, or true if added'
*/

public function addInputFile($path) {
if ((file_exists($path)) &&
(!is_dir($path))) {
//get the basename of the file and use that as the key
$this->_inputVideos[basename($path)]['path'] = $path;
$this->_getFileInfo($path);
return true;
}
return false;
}

/**
* Add an output video to the current processing chain
*
*@param string $path 'Path to the video output file'
* @return boolean 'Returns true'
*/

public function addOutputFile($path) {
$this->_outputVideos[basename($path)]['path'] = $path;
return true;
}

private function _getFileInfo($path) {
if (!isset($this->_infoCache[$path]["rawinfo"])) {
$this->_infoCache[$path]["rawinfo"] = $this->_openProcess($this->_pathToFFPROBE . " " . $path);
}

/**
* Add the video info from this video to the data array
*/
$methods = get_class_methods($this);
foreach ($methods as $methodName) {
if (stripos($methodName,'parseGet') !== false) {
//run this function
$this->_infoCache[$path]['data'][str_replace("parseGet","",$methodName)] = $this->$methodName($this->_infoCache[$path]["rawinfo"]);
}
}
}

/**
* Add a flag to an input video!
* EG addFlagToInput("input.mpg", "-ss 00:00:01.000");
*
* @param string $inputKey 'The key of the input video'
* @param string $flag 'The flag to add'
*
* @return boolean 'True on success, false on earthquake (or failure)'
*/
public function addFlagToInput($inputKey, $flag = null){
if ($flag) {
//check if the key exists
if (isset($this->_inputVideos[$inputKey])) {
$this->_inputVideos[$inputKey]["flags"][] = trim($flag);
return true;
}
}
return false;
}

/**
* Add a flag to an output video!
* EG addFlagToOutput("output.mp4", "-t 15");
*
* @param string $inputKey 'The key of the output video'
* @param string $flag 'The flag to add'
*
* @return boolean 'True on success, false on earthquake (or failure)'
*/
public function addFlagToOutput($inputKey, $flag = null){
if ($flag) {
//check if the key exists
if (isset($this->_outputVideos[$inputKey])) {
$this->_outputVideos[$inputKey]["flags"][] = trim($flag);
return true;
}
}
return false;
}

/**
* Build and execute a command
*/

public function execute() {
$command = $this->_buildCommand();
echo("\n\n\n");
die($command);
}

/**
* Build the command
*/

private function _buildCommand() {
if (empty($this->_inputVideos)) {
throw new Exception("No input videos specified");
}
if (empty($this->_outputVideos)) {
throw new Exception("No output videos specified");
}

$baseCmd = $this->_pathToFFMPEG;

foreach ($this->_inputVideos as $inputVideo) {
if (isset($inputVideo["flags"])) {
if (!empty($inputVideo["flags"])) {
foreach ($inputVideo["flags"] as $flag) {
$baseCmd .= " {$flag}";
}
}
}
$baseCmd .= " -i {$inputVideo['path']}";
}

foreach ($this->_outputVideos as $outputVideo) {
if (isset($outputVideo["flags"])) {
if (!empty($outputVideo["flags"])) {
foreach ($outputVideo["flags"] as $flag) {
$baseCmd .= " {$flag}";
}
}
}
$baseCmd .= " {$outputVideo['path']}";
}

return $baseCmd;

}
/**
* DATA PARSING FUNCTIONS
*/

/**
* Returns the duration of a video file based on a ffmpeg info dump
*
* @param string $data 'FFPROBE dump'
* @return array
*/

public function parseGetDuration($data) {
preg_match(ffmpegPHPConfig::REGEX_DURATION,$data, $matches);
return array("ms"=>(int)$matches[5],"sec"=>(int)$matches[3],"min"=>(int)$matches[2], "hours"=>(int)$matches[1]);
}

public function parseGetError($data) {
preg_match(ffmpegPHPConfig::REGEX_ERRORS, $data, $matches);
if (empty($array)) {
return false;
}
return true;
}

public function parseGetVideoCodec($data) {
preg_match(ffmpegPHPConfig::REGEX_VIDEO_CODEC, $data, $matches);
return $matches;
}

public function parseGetAudioCodec($data) {
preg_match(ffmpegPHPConfig::REGEX_AUDIO_CODEC, $data, $matches);
return $matches;
}

public function parseGetFrameRate($data) {
preg_match(ffmpegPHPConfig::REGEX_FRAME_RATE, $data, $matches);
return $matches;
}
public function parseGetPixelFormat($data) {
preg_match(ffmpegPHPConfig::REGEX_PIXEL_FORMAT, $data, $matches);
return $matches;
}

public function parseGetDimensions($data) {
preg_match(ffmpegPHPConfig::REGEX_FRAME_WH, $data, $matches);
return array("w"=>$matches[1], "h"=>$matches[2]);
}

public function parseGetBitrate($data) {
preg_match(ffmpegPHPConfig::REGEX_BITRATE, $data, $matches);
return $matches;
}

public function parseGetAudioBitrate($data) {
preg_match(ffmpegPHPConfig::REGEX_AUDIO_BITRATE, $data, $matches);
return $matches;
}

public function parseGetHasAudio($data) {
preg_match(ffmpegPHPConfig::REGEX_HAS_AUDIO, $data, $matches);
return $matches;
}

public function parseGetHasVideo($data) {
preg_match(ffmpegPHPConfig::REGEX_HAS_VIDEO, $data, $matches);
return $matches;
}

}
?>


file: class.ffmpeg.config.php


<?php

/**
* This class contains a whole bunch of settings you can change to suit your needs
*/

class ffmpegPHPConfig {

const workingDirectory = "/tmp"; //path to directory to use for any temporary videos

const pathToFFMPEG = null; //set the path to ffmpeg, this means we wont autodetect. useful for windows.
const pathToFFMPEG2THEORA = null; //set the path to ffmpeg2theora, this means we wont autodetect. useful for windows.
const pathToFFPROBE = null; //set the path to ffprobe, this means we wont autodetect. useful for windows.

/**
* Some regular expressions to parse data provided by FFMPEG
*/

const REGEX_DURATION = '/Duration: ([0-9]{2}):([0-9]{2}):([0-9]{2})(\.([0-9]+))?/';
const REGEX_FRAME_RATE = '/([0-9\.]+\sfps,\s)?([0-9\.]+)\stbr/';
const REGEX_COMMENT = '/comment\s*(:|=)\s*(.+)/i';
const REGEX_TITLE = '/title\s*(:|=)\s*(.+)/i';
const REGEX_ARTIST = '/(artist|author)\s*(:|=)\s*(.+)/i';
const REGEX_COPYRIGHT = '/copyright\s*(:|=)\s*(.+)/i';
const REGEX_GENRE = '/genre\s*(:|=)\s*(.+)/i';
const REGEX_TRACK_NUMBER = '/track\s*(:|=)\s*(.+)/i';
const REGEX_YEAR = '/year\s*(:|=)\s*(.+)/i';
const REGEX_FRAME_WH = '/Video:.+?([1-9][0-9]*)x([1-9][0-9]*)/';
const REGEX_PIXEL_FORMAT = '/Video: [^,]+, ([^,]+)/';
const REGEX_BITRATE = '/bitrate: ([0-9]+) kb\/s/';
const REGEX_VIDEO_BITRATE = '/Video:.+?([0-9]+) kb\/s/';
const REGEX_AUDIO_BITRATE = '/Audio:.+?([0-9]+) kb\/s/';
const REGEX_AUDIO_SAMPLE_RATE = '/Audio:.+?([0-9]+) Hz/';
const REGEX_VIDEO_CODEC = '/Video:\s([^,]+),/';
const REGEX_AUDIO_CODEC = '/Audio:\s([^,]+),/';
const REGEX_AUDIO_CHANNELS = '/Audio:\s[^,]+,[^,]+,([^,]+)/';
const REGEX_HAS_AUDIO = '/Stream.+Audio/';
const REGEX_HAS_VIDEO = '/Stream.+Video/';
const REGEX_ERRORS = '/.*(Error|Permission denied|could not seek to position|Invalid pixel format|Unknown encoder|could not find codec|does not contain any stream).*/i';

}


file: index.php (small usage example)




<?php
require("class.ffmpeg.php");
$test = new ffmpegPHP;
$test->addInputFile(dirname(__FILE__) . "/sample.mp4");
$test->addFlagToInput("sample.mp4", "-ss 00:00:01.000");

$test->addOutputFile(dirname(__FILE__) . "/sample_out.mp4");
$test->addFlagToOutput("sample_out.mp4", "-s 100x100");
$test->execute();
?>


Have fun chaps!



EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum