PHP Classes

File: src/PHPVideoToolkit/ProgressHandlerOutput.php

Recommend this page to a friend!
  Classes of Oliver Lillie   PHP Video Toolkit   src/PHPVideoToolkit/ProgressHandlerOutput.php   Download  
File: src/PHPVideoToolkit/ProgressHandlerOutput.php
Role: Class source
Content type: text/plain
Description: Class source
Class: PHP Video Toolkit
Manipulate and convert videos with ffmpeg program
Author: By
Last change: added bug fix workaround for progress handlers where the output file is prematurely deleted. switched end status to ProgressHandlerDefaultData::ENCODING_STATUS_FINISHED to determine when the process has finished rather than completed because completed applies to the encode and not the entire process.
fixed potential bugs in progress handler completed value where if ffmpeg has finished encoding but the process has not completed errors could occur from trying to get the output of the process. Added progress handler status constants for checking statuses
Merge branch 'refs/heads/multiple-output'

Conflicts:
README.md
src/PHPVideoToolkit/ProgressHandlerOutput.php
src/PHPVideoToolkit/ProgressHandlerPortable.php
fixed small issue of whitespace
updated version in source
fixed two regex bugs when using progress handlers Output and Portable to track image frame output
bug fix for only encoding and parsing multiple audio output. ref #21
Date: 1 year ago
Size: 8,394 bytes
 

Contents

Class file image Download
<?php
   
   
/**
     * This file is part of the PHP Video Toolkit v2 package.
     *
     * @author Oliver Lillie (aka buggedcom) <publicmail@buggedcom.co.uk>
     * @license Dual licensed under MIT and GPLv2
     * @copyright Copyright (c) 2008-2014 Oliver Lillie <http://www.buggedcom.co.uk>
     * @package PHPVideoToolkit V2
     * @version 2.1.7-beta
     * @uses ffmpeg http://ffmpeg.sourceforge.net/
     */
    
   
namespace PHPVideoToolkit;

   
/**
     * @access public
     * @author Oliver Lillie
     * @package default
     */
   
class ProgressHandlerOutput extends ProgressHandlerAbstract
   
{
        protected function
_parseOutputData(&$return_data, $raw_data)
        {
           
$return_data['started'] = true;

            if(empty(
$raw_data) === true)
            {
                if(
$this->_last_probe_data === null)
                {
                   
$return_data['status'] = self::ENCODING_STATUS_PENDING;
                }
                else
                {
                   
$return_data = $this->_last_probe_data;
                   
$return_data['finished'] = true;
                   
$return_data['status'] = self::ENCODING_STATUS_FINISHED;
                }
                return;
            }

            if(
preg_match_all('/Input\s#[0-9]+,\s+[^\s]+,\s+from\s+(.*):/', $raw_data, $input_matches) > 0)
            {
               
array_walk($input_matches[1], function(&$value)
                {
                   
$value = trim($value, '\'"');
                });
               
$return_data['input_count'] = count($input_matches[1]);
               
$return_data['input_file'] = $return_data['input_count'] === 1 ? $input_matches[1][0] : $input_matches[1];
            }
            if(
preg_match_all('/Output\s#[0-9]+,\s+[^\s]+,\s+to\s+(.*):/', $raw_data, $output_matches) > 0)
            {
               
array_walk($output_matches[1], function(&$value)
                {
                   
$value = trim($value, '\'"');
                });
               
$return_data['output_count'] = count($output_matches[1]);
               
$return_data['output_file'] = $return_data['output_count'] === 1 ? $output_matches[1][0] : $output_matches[1];
            }
           
$return_data['process_file'] = $this->_ffmpeg_process->getBufferOutput();

// determine how many video outs there are as that dictates the number of q= regexes to add as well as some others such as frame.
           
$video_stream_count = preg_match_all('/\s*Stream\s*\#([0-9]+):1s*\(und\)\s*:/i', substr($raw_data, strpos($raw_data, 'Output #0')));
           
$q_regex = '';
           
$size_regex = '';
           
$frame_regex = '';
           
$fps_regex = '';
            if(
$video_stream_count > 0)
            {
               
$frame_regex = 'frame=\s*(?<frame>[0-9]+)\s';
               
$fps_regex = 'fps=\s*(?<fps>[0-9\.]+)\s';
            }

// parse out the details of the data.
// fucking non standardness in ffmpeg. I'm sure there is a reason for it but for fucks sake there has to be a better way
           
if($video_stream_count > 1)
            {
               
$q_regex_array = array();
               
$q_regex = '(?<lastq>L)?q=(?<q>[0-9\.]+)\s';
                for(
$i=0; $i<$video_stream_count; $i++)
                {
                   
array_push($q_regex_array, str_replace('<q>', '<q'.$i.'>', str_replace('<lastq>', '<lastq'.$i.'>', $q_regex)));
                }
               
$q_regex = implode('', $q_regex_array);
               
$size_regex = 'size=\s*(?<size>[0-9\.bkBmg]+|N\/A)\s';
            }
            else if(
$video_stream_count === 1)
            {
               
$q_regex = 'q=(?<q0>[0-9\.]+)\s';
               
$size_regex = '(?<lastsize>L)?size=\s*(?<size>[0-9\.bkBmg]+|N\/A)\s';
            }
            else
            {
               
$size_regex = 'size=\s*(?<size>[0-9\.bkBmg]+|N\/A)\s';
            }

// compile the regex dependant on the numebr of video streams
           
$regex =
               
'/'.
               
$frame_regex.
               
$fps_regex.
               
$q_regex.
               
$size_regex.
               
'time=\s*(?<time>[0-9]{2,}:[0-9]{2}:[0-9]{2}.[0-9]+)\s'.
               
'bitrate=\s*(?<bitrate>[0-9\.]+\s?[bkitsBmg\/s]+|N\/A)'.
               
'(\sdup=\s*(?<dup>[0-9]+))?'.
               
'(\sdrop=\s*(?<drop>[0-9]+))?'.
               
'/';

            if(
preg_match_all($regex, $raw_data, $matches) > 0)
            {
               
$return_data['status'] = self::ENCODING_STATUS_ENCODING;

               
$last_key = count($matches[0])-1;
               
$return_data['frame'] = isset($matches['frame']) === true ? $matches['frame'][$last_key] : null;
               
$return_data['fps'] = isset($matches['fps']) === true ? $matches['fps'][$last_key] : null;
               
$return_data['size'] = $matches['size'][$last_key];
               
$return_data['duration'] = new Timecode($matches['time'][$last_key], Timecode::INPUT_FORMAT_TIMECODE);
               
$return_data['percentage'] = ($return_data['duration']->total_seconds/$this->_total_duration->total_seconds)*100;
               
$return_data['dup'] = $matches['dup'][$last_key];
               
$return_data['drop'] = $matches['drop'][$last_key];
               
               
$is_last = false;
                if(
$video_stream_count > 1)
                {
                    for(
$i=0; $i<$return_data['output_count']; $i++)
                    {
                        if(isset(
$matches['lastq'.$i]) === true && $matches['lastq'.$i][$last_key] === 'L')
                        {
                           
$is_last = true;
                            break;
                        }
                    }
                }
                else if(isset(
$matches['lastsize']) === true && $matches['lastsize'][$last_key] === 'L')
                {
                   
$is_last = true;
                }

// if we have the last frame then signal that the process has finished.
               
if($is_last === true)
                {
                    if(
$return_data['percentage'] < 99.5)
                    {
                       
$return_data['interrupted'] = true;
                       
$return_data['status'] = self::ENCODING_STATUS_INTERRUPTED;
                    }
                    else
                    {
                       
$return_data['percentage'] = 100;
                    }
                }

// work out the fps average for performance reasons
               
if(count($matches[2]) === 1)
                {
                   
$return_data['fps_avg'] = $return_data['frame']/$return_data['run_time'];
                }
                else
                {
                   
$total_fps = 0;
                    foreach (
$matches[2] as $fps)
                    {
                       
$total_fps += $fps;
                    }
                   
$return_data['fps_avg'] = $total_fps/($last_key+1);
                }
            }
            else if(
strpos($raw_data, 'Stream mapping:') !== false && strpos($raw_data, 'Press [q] to stop, [?] for help') !== false)
            {
               
$return_data['status'] = self::ENCODING_STATUS_DECODING;
            }
            else
            {
               
$return_data['status'] = self::ENCODING_STATUS_ERROR;
            }

            if(
$this->_ffmpeg_process->isCompleted() === true)
            {
               
$return_data['finished'] = true;
                if(
$return_data['status'] !== self::ENCODING_STATUS_INTERRUPTED)
                {
                   
$return_data['completed'] = true;
                   
$return_data['status'] = self::ENCODING_STATUS_FINISHED;
                }
            }
            else if(
$return_data['percentage'] === 100)
            {
               
$return_data['completed'] = true;
               
$return_data['status'] = self::ENCODING_STATUS_COMPLETED;
            }
            else if(
$return_data['percentage'] >= 99.5)
            {
               
$return_data['percentage'] = 100;
               
$return_data['status'] = self::ENCODING_STATUS_FINALISING;
            }

           
$this->_last_probe_data = $return_data;
        }
        
        protected function
_getRawData()
        {
            return
$this->_ffmpeg_process->getBuffer();
        }
     }