PageRenderTime 60ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 1ms

/app/vendors/phpvideotoolkit.php5.php

https://github.com/mariuz/firetube
PHP | 3311 lines | 1748 code | 166 blank | 1397 comment | 259 complexity | a33e3bdf4e68c9038f80644b65dd875a MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. /* SVN FILE: $Id$ */
  3. /**
  4. * @author Oliver Lillie (aka buggedcom) <publicmail@buggedcom.co.uk>
  5. *
  6. * @license BSD
  7. * @copyright Copyright (c) 2008 Oliver Lillie <http://www.buggedcom.co.uk>
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
  9. * files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
  10. * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
  11. * is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be
  12. * included in all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  15. * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  16. * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  17. * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  18. *
  19. * @package PHPVideoToolkit (was called ffmpeg)
  20. * @version 0.1.5
  21. * @changelog SEE CHANGELOG
  22. * @abstract This class can be used in conjunction with several server binary libraries to manipulate video and audio
  23. * through PHP. It is not intended to solve any particular problems, however you may find it useful. This php class
  24. * is in no way associated with the actual FFmpeg releases. Any mistakes contained in this php class are mine and mine
  25. * alone.
  26. *
  27. * Please Note: There are several prerequisites that are required before this class can be used as an aid to manipulate
  28. * video and audio. You must at the very least have FFMPEG compiled on your server. If you wish to use this class for FLV
  29. * manipulation you must compile FFMPEG with LAME and Ruby's FLVTOOL2. I cannot answer questions regarding the install of
  30. * the server binaries needed by this class. I had too learn the hard way and it isn't easy, however it is a good learning
  31. * experience. For those of you who do need help read the install.txt file supplied along side this class. It wasn't written
  32. * by me however I found it useful when installing ffmpeg for the first time. The original source for the install.txt file
  33. * is located http://www.luar.com.hk/blog/?p=669 and the author is Lunar.
  34. *
  35. * @see install.txt
  36. *
  37. * @uses ffmpeg http://ffmpeg.sourceforge.net/
  38. * @uses lame http://lame.sourceforge.net/
  39. * @uses flvtool2 http://www.inlet-media.de/flvtool2 (and ruby http://www.ruby-lang.org/en/)
  40. *
  41. * @config examples/example-config.php Please edit this files in order for the examples to work.
  42. * @example examples/example01.php Converts video to Flash Video (ie FLV).
  43. * @example examples/example02.php Screen grabs video frames.
  44. * @example examples/example03.php Compile a movie from multiple jpegs
  45. * @example examples/example04.php Watermark a video.
  46. * @example examples/example05.php Access media metadata without using the ffmpeg-php library.
  47. * @example examples/example06.php Extract audio from video.
  48. * @example examples/example07.php Join multiple videos together.
  49. * @example examples/example08.php Easy video conversion to common formats using the adapters.
  50. * @example examples/example09.php Shows you how to access the information about your ffmpeg installation.
  51. * @example examples/example10.php Shows you how to extract a specific frame from a movie.
  52. * @example examples/example11.php Shows you how to use the ffmpeg-php adapters to provide a pure php emulation of ffmpeg-php.
  53. * @example examples/example12.php Shows you how to manipulate/format timecode strings.
  54. * @example examples/example13.php This demonstrates how to simply create a FLV stream script.
  55. */
  56. /**
  57. * Set the ffmpeg binary path
  58. */
  59. if(!defined('PHPVIDEOTOOLKIT_TEMP_DIRECTORY'))
  60. {
  61. define('PHPVIDEOTOOLKIT_TEMP_DIRECTORY', '/tmp/');
  62. }
  63. /**
  64. * Set the ffmpeg binary path
  65. */
  66. if(!defined('PHPVIDEOTOOLKIT_FFMPEG_BINARY'))
  67. {
  68. define('PHPVIDEOTOOLKIT_FFMPEG_BINARY', '/usr/local/bin/ffmpeg');
  69. }
  70. /**
  71. * Set the flvtool2 binary path
  72. */
  73. if(!defined('PHPVIDEOTOOLKIT_FLVTOOLS_BINARY'))
  74. {
  75. define('PHPVIDEOTOOLKIT_FLVTOOLS_BINARY', '/usr/bin/flvtool2');
  76. }
  77. /**
  78. * Set the watermark vhook path
  79. */
  80. if(!defined('PHPVIDEOTOOLKIT_FFMPEG_WATERMARK_VHOOK'))
  81. {
  82. define('PHPVIDEOTOOLKIT_FFMPEG_WATERMARK_VHOOK', '/usr/local/lib/vhook/watermark.so');
  83. }
  84. /**
  85. * Set the memcoder path
  86. */
  87. if(!defined('PHPVIDEOTOOLKIT_MENCODER_BINARY'))
  88. {
  89. define('PHPVIDEOTOOLKIT_MENCODER_BINARY', '/usr/local/bin/mencoder');
  90. }
  91. class PHPVideoToolkit
  92. {
  93. public $version = '0.1.5';
  94. /**
  95. * Error strings
  96. */
  97. private $_messages = array(
  98. 'generic_temp_404' => 'The temporary directory does not exist.',
  99. 'generic_temp_writable' => 'The temporary directory is not write-able by the web server.',
  100. 'getFileInfo_no_input' => 'Input file does not exist so no information can be retrieved.',
  101. 'streamFLV_no_input' => 'Input file has not been set so the FLV cannot be streamed.',
  102. 'streamFLV_passed_eof' => 'You have tried to stream to a point in the file that does not exit.',
  103. 'setInputFile_file_existence' => 'Input file "#file" does not exist',
  104. 'extractAudio_valid_format' => 'Value "#format" set from $toolkit->extractAudio, is not a valid audio format. Valid values ffmpeg self::FORMAT_AAC, PHPVideoToolkit::FORMAT_AIFF, PHPVideoToolkit::FORMAT_MP2, PHPVideoToolkit::FORMAT_MP3, PHPVideoToolkit::FORMAT_MP4, PHPVideoToolkit::FORMAT_MPEG4, PHPVideoToolkit::FORMAT_M4A or PHPVideoToolkit::FORMAT_WAV. If you wish to specifically try to set another format you should use the advanced function $toolkit->addCommand. Set $command to "-f" and $argument to your required value.',
  105. 'extractFrame_video_frame_rate_404' => 'You have attempted to extract a thumbnail from a video while automagically guessing the framerate of the video, but the framerate could not be accessed. You can remove this error by manually setting the frame rate of the video.',
  106. 'extractFrame_video_info_404' => 'You have attempted to extract a thumbnail from a video and check to see if the thumbnail exists, however it was not possible to access the video information. Please check your temporary directory permissions for read/write access by the webserver.',
  107. 'extractFrame_video_frame_count' => 'You have attempted to extract a thumbnail from a video but the thumbnail you are trying to extract does not exist in the video.',
  108. 'extractFrames_video_begin_frame_count' => 'You have attempted to extract thumbnails from a video but the thumbnail you are trying to start the extraction from does not exist in the video.',
  109. 'extractFrames_video_end_frame_count' => 'You have attempted to extract thumbnails from a video but the thumbnail you are trying to end the extraction at does not exist in the video.',
  110. 'setFormat_valid_format' => 'Value "#format" set from $toolkit->setFormat, is not a valid format. Valid values are PHPVideoToolkit::FORMAT_3GP2, PHPVideoToolkit::FORMAT_3GP, PHPVideoToolkit::FORMAT_AAC, PHPVideoToolkit::FORMAT_AIFF, PHPVideoToolkit::FORMAT_AMR, PHPVideoToolkit::FORMAT_ASF, PHPVideoToolkit::FORMAT_AVI, PHPVideoToolkit::FORMAT_FLV, PHPVideoToolkit::FORMAT_GIF, PHPVideoToolkit::FORMAT_MJ2, PHPVideoToolkit::FORMAT_MP2, PHPVideoToolkit::FORMAT_MP3, PHPVideoToolkit::FORMAT_MP4, PHPVideoToolkit::FORMAT_MPEG4, PHPVideoToolkit::FORMAT_M4A, PHPVideoToolkit::FORMAT_MPEG, PHPVideoToolkit::FORMAT_MPEG1, PHPVideoToolkit::FORMAT_MPEG2, PHPVideoToolkit::FORMAT_MPEGVIDEO, PHPVideoToolkit::FORMAT_PSP, PHPVideoToolkit::FORMAT_RM, PHPVideoToolkit::FORMAT_SWF, PHPVideoToolkit::FORMAT_VOB, PHPVideoToolkit::FORMAT_WAV, PHPVideoToolkit::FORMAT_JPG. If you wish to specifically try to set another format you should use the advanced function $toolkit->addCommand. Set $command to "-f" and $argument to your required value.',
  111. 'setAudioSampleFrequency_valid_frequency' => 'Value "#frequency" set from $toolkit->setAudioSampleFrequency, is not a valid integer. Valid values are 11025, 22050, 44100. If you wish to specifically try to set another frequency you should use the advanced function $toolkit->addCommand. Set $command to "-ar" and $argument to your required value.',
  112. 'setAudioFormat_valid_format' => 'Value "#format" set from $toolkit->setAudioFormat, is not a valid format. Valid values are PHPVideoToolkit::FORMAT_AAC, PHPVideoToolkit::FORMAT_AIFF, PHPVideoToolkit::FORMAT_AMR, PHPVideoToolkit::FORMAT_ASF, PHPVideoToolkit::FORMAT_MP2, PHPVideoToolkit::FORMAT_MP3, PHPVideoToolkit::FORMAT_MP4, PHPVideoToolkit::FORMAT_MPEG2, PHPVideoToolkit::FORMAT_RM, PHPVideoToolkit::FORMAT_WAV. If you wish to specifically try to set another format you should use the advanced function $toolkit->addCommand. Set $command to "-acodec" and $argument to your required value.',
  113. 'setVideoFormat_valid_format' => 'Value "#format" set from $toolkit->setAudioFormat, is not a valid format. Valid values are PHPVideoToolkit::FORMAT_3GP2, PHPVideoToolkit::FORMAT_3GP, PHPVideoToolkit::FORMAT_AVI, PHPVideoToolkit::FORMAT_FLV, PHPVideoToolkit::FORMAT_GIF, PHPVideoToolkit::FORMAT_MJ2, PHPVideoToolkit::FORMAT_MP4, PHPVideoToolkit::FORMAT_MPEG4, PHPVideoToolkit::FORMAT_M4A, PHPVideoToolkit::FORMAT_MPEG, PHPVideoToolkit::FORMAT_MPEG1, PHPVideoToolkit::FORMAT_MPEG2, PHPVideoToolkit::FORMAT_MPEGVIDEO. If you wish to specifically try to set another format you should use the advanced function $toolkit->addCommand. Set $command to "-vcodec" and $argument to your required value.',
  114. 'setAudioBitRate_valid_bitrate' => 'Value "#bitrate" set from $toolkit->setAudioBitRate, is not a valid integer. Valid values are 16, 32, 64, 128. If you wish to specifically try to set another bitrate you should use the advanced function $toolkit->addCommand. Set $command to "-ab" and $argument to your required value.',
  115. 'prepareImagesForConversionToVideo_one_img' => 'When compiling a movie from a series of images, you must include at least one image.',
  116. 'prepareImagesForConversionToVideo_img_404' => '"#img" does not exist.',
  117. 'prepareImagesForConversionToVideo_img_copy' => '"#img" can not be copied to "#tmpfile"',
  118. 'prepareImagesForConversionToVideo_img_type' => 'The images can not be prepared for conversion to video. Please make sure all images are of the same type, ie gif, png, jpeg and then try again.',
  119. 'setVideoOutputDimensions_valid_format' => 'Value "#format" set from $toolkit->setVideoOutputDimensions, is not a valid preset dimension. Valid values are PHPVideoToolkit::SIZE_SQCIF, PHPVideoToolkit::SIZE_SAS, PHPVideoToolkit::SIZE_QCIF, PHPVideoToolkit::SIZE_CIF, PHPVideoToolkit::SIZE_4CIF, PHPVideoToolkit::SIZE_QQVGA, PHPVideoToolkit::SIZE_QVGA, PHPVideoToolkit::SIZE_VGA, PHPVideoToolkit::SIZE_SVGA, PHPVideoToolkit::SIZE_XGA, PHPVideoToolkit::SIZE_UXGA, PHPVideoToolkit::SIZE_QXGA, PHPVideoToolkit::SIZE_SXGA, PHPVideoToolkit::SIZE_QSXGA, PHPVideoToolkit::SIZE_HSXGA, PHPVideoToolkit::SIZE_WVGA, PHPVideoToolkit::SIZE_WXGA, PHPVideoToolkit::SIZE_WSXGA, PHPVideoToolkit::SIZE_WUXGA, PHPVideoToolkit::SIZE_WOXGA, PHPVideoToolkit::SIZE_WQSXGA, PHPVideoToolkit::SIZE_WQUXGA, PHPVideoToolkit::SIZE_WHSXGA, PHPVideoToolkit::SIZE_WHUXGA, PHPVideoToolkit::SIZE_CGA, PHPVideoToolkit::SIZE_EGA, PHPVideoToolkit::SIZE_HD480, PHPVideoToolkit::SIZE_HD720, PHPVideoToolkit::SIZE_HD1080. You can also manually set the width and height.',
  120. 'setVideoOutputDimensions_sas_dim' => 'It was not possible to determine the input video dimensions so it was not possible to continue. If you wish to override this error please change the call to setVideoOutputDimensions and add a true argument to the arguments list... setVideoOutputDimensions(PHPVideoToolkit::SIZE_SAS, true);',
  121. 'setVideoOutputDimensions_valid_integer' => 'You tried to set the video output dimensions to an odd number. FFmpeg requires that the video output dimensions are of event value and divisible by 2. ie 2, 4, 6,... etc',
  122. 'setVideoAspectRatio_valid_ratio' => 'Value "#ratio" set from $toolkit->setVideoOutputDimensions, is not a valid preset dimension. Valid values are PHPVideoToolkit::RATIO_STANDARD, PHPVideoToolkit::RATIO_WIDE, PHPVideoToolkit::RATIO_CINEMATIC. If you wish to specifically try to set another video aspect ratio you should use the advanced function $toolkit->addCommand. Set $command to "-aspect" and $argument to your required value.',
  123. 'addWatermark_img_404' => 'Watermark file "#watermark" does not exist.',
  124. 'addWatermark_vhook_disabled' => 'Vhooking is not enabled in your FFmpeg binary. In order to allow video watermarking you must have FFmpeg compiled with --enable-vhook set. You can however watermark any extracted images using GD. To enable frame watermarking, call $toolkit->addGDWatermark($file) before you execute the extraction.',
  125. 'addVideo_file_404' => 'File "#file" does not exist.',
  126. 'setOutput_output_dir_404' => 'Output directory "#dir" does not exist!',
  127. 'setOutput_output_dir_writable' => 'Output directory "#dir" is not writable!',
  128. 'setOutput_%_missing' => 'The output of this command will be images yet you have not included the "%index" or "%timecode" in the $output_name.',
  129. 'setOutput_%d_depreciated' => 'The use of %d in the output file name is now depreciated. Please use %index. Number padding is still supported. You may also use %timecode instead to add a timecode to the filename.',
  130. 'execute_input_404' => 'Execute error. Input file missing.',
  131. 'execute_output_not_set' => 'Execute error. Output not set.',
  132. 'execute_overwrite_process' => 'Execute error. A file exists in the temp directory and is of the same name as this process file. It will conflict with this conversion. Conversion stopped.',
  133. 'execute_overwrite_fail' => 'Execute error. Output file exists. Process halted. If you wish to automatically overwrite files set the third argument in "PHPVideoToolkit::setOutput();" to "PHPVideoToolkit::OVERWRITE_EXISTING".',
  134. 'execute_partial_error' => 'Execute error. Output for file "#input" encountered a partial error. Files were generated, however one or more of them were empty.',
  135. 'execute_image_error' => 'Execute error. Output for file "#input" was not found. No images were generated.',
  136. 'execute_output_404' => 'Execute error. Output for file "#input" was not found. Please check server write permissions and/or available codecs compiled with FFmpeg. You can check the encode decode availability by inspecting the output array from PHPVideoToolkit::getFFmpegInfo().',
  137. 'execute_output_empty' => 'Execute error. Output for file "#input" was found, but the file contained no data. Please check the available codecs compiled with FFmpeg can support this type of conversion. You can check the encode decode availability by inspecting the output array from PHPVideoToolkit::getFFmpegInfo().',
  138. 'execute_image_file_exists' => 'Execute error. There is a file name conflict. The file "#file" already exists in the filesystem. If you wish to automatically overwrite files set the third argument in "PHPVideoToolkit::setOutput();" to "PHPVideoToolkit::OVERWRITE_EXISTING".',
  139. 'execute_result_ok_but_unwritable' => 'Process Partially Completed. The process successfully completed however it was not possible to output to "#output". The output was left in the temp directory "#process" for a manual file movement.',
  140. 'execute_result_ok' => 'Process Completed. The process successfully completed. Output was generated to "#output".',
  141. 'ffmpeg_log_ffmpeg_output' => 'OUTPUT',
  142. 'ffmpeg_log_ffmpeg_result' => 'RESULT',
  143. 'ffmpeg_log_ffmpeg_command' => 'COMMAND',
  144. 'ffmpeg_log_ffmpeg_join_gunk' => 'FFMPEG JOIN OUTPUT',
  145. 'ffmpeg_log_ffmpeg_gunk' => 'FFMPEG OUTPUT',
  146. 'ffmpeg_log_separator' => '-------------------------------'
  147. );
  148. /**
  149. * Process Results from PHPVideoToolkit::execute
  150. */
  151. // any return value with this means everything is ok
  152. const RESULT_OK = true;
  153. // any return value with this means the file has been processed/converted ok however it was
  154. // not able to be written to the output address. If this occurs you will need to move the
  155. // processed file manually from the temp location
  156. const RESULT_OK_BUT_UNWRITABLE = -1;
  157. /**
  158. * Codec support constants
  159. */
  160. const ENCODE = 'encode';
  161. const DECODE = 'decode';
  162. /**
  163. * Overwrite constants used in setOutput
  164. */
  165. const OVERWRITE_FAIL = 'fail';
  166. const OVERWRITE_PRESERVE = 'preserve';
  167. const OVERWRITE_EXISTING = 'existing';
  168. const OVERWRITE_UNIQUE = 'unique';
  169. /**
  170. * Formats supported
  171. * 3g2 3gp2 format
  172. * 3gp 3gp format
  173. * aac ADTS AAC
  174. * aiff Audio IFF
  175. * amr 3gpp amr file format
  176. * asf asf format
  177. * avi avi format
  178. * flv flv format
  179. * gif GIF Animation
  180. * mov mov format
  181. * mov,mp4,m4a,3gp,3g2,mj2 QuickTime/MPEG4/Motion JPEG 2000 format
  182. * mp2 MPEG audio layer 2
  183. * mp3 MPEG audio layer 3
  184. * mp4 mp4 format
  185. * mpeg MPEG1 System format
  186. * mpeg1video MPEG video
  187. * mpeg2video MPEG2 video
  188. * mpegvideo MPEG video
  189. * psp psp mp4 format
  190. * rm rm format
  191. * swf Flash format
  192. * vob MPEG2 PS format (VOB)
  193. * wav wav format
  194. * jpeg mjpeg format
  195. * yuv4mpegpipe yuv4mpegpipe format
  196. */
  197. const FORMAT_3GP2 = '3g2';
  198. const FORMAT_3GP = '3gp';
  199. const FORMAT_AAC = 'aac';
  200. const FORMAT_AIFF = 'aiff';
  201. const FORMAT_AMR = 'amr';
  202. const FORMAT_ASF = 'asf';
  203. const FORMAT_AVI = 'avi';
  204. const FORMAT_FLV = 'flv';
  205. const FORMAT_GIF = 'gif';
  206. const FORMAT_MJ2 = 'mj2';
  207. const FORMAT_MP2 = 'mp2';
  208. const FORMAT_MP3 = 'mp3';
  209. const FORMAT_MP4 = 'mp4';
  210. const FORMAT_MPEG4 = 'mpeg4';
  211. const FORMAT_M4A = 'm4a';
  212. const FORMAT_MPEG = 'mpeg';
  213. const FORMAT_MPEG1 = 'mpeg1video';
  214. const FORMAT_MPEG2 = 'mpeg2video';
  215. const FORMAT_MPEGVIDEO = 'mpegvideo';
  216. const FORMAT_PSP = 'psp';
  217. const FORMAT_RM = 'rm';
  218. const FORMAT_SWF = 'swf';
  219. const FORMAT_VOB = 'vob';
  220. const FORMAT_WAV = 'wav';
  221. const FORMAT_JPG = 'mjpeg';
  222. const FORMAT_Y4MP = 'yuv4mpegpipe';
  223. /**
  224. * Size Presets
  225. */
  226. const SIZE_SAS = 'SameAsSource';
  227. const SIZE_SQCIF = '128x96';
  228. const SIZE_QCIF = '176x144';
  229. const SIZE_CIF = '352x288';
  230. const SIZE_4CIF = '704x576';
  231. const SIZE_QQVGA = '160x120';
  232. const SIZE_QVGA = '320x240';
  233. const SIZE_VGA = '640x480';
  234. const SIZE_SVGA = '800x600';
  235. const SIZE_XGA = '1024x768';
  236. const SIZE_UXGA = '1600x1200';
  237. const SIZE_QXGA = '2048x1536';
  238. const SIZE_SXGA = '1280x1024';
  239. const SIZE_QSXGA = '2560x2048';
  240. const SIZE_HSXGA = '5120x4096';
  241. const SIZE_WVGA = '852x480';
  242. const SIZE_WXGA = '1366x768';
  243. const SIZE_WSXGA = '1600x1024';
  244. const SIZE_WUXGA = '1920x1200';
  245. const SIZE_WOXGA = '2560x1600';
  246. const SIZE_WQSXGA = '3200x2048';
  247. const SIZE_WQUXGA = '3840x2400';
  248. const SIZE_WHSXGA = '6400x4096';
  249. const SIZE_WHUXGA = '7680x4800';
  250. const SIZE_CGA = '320x200';
  251. const SIZE_EGA = '640x350';
  252. const SIZE_HD480 = '852x480';
  253. const SIZE_HD720 = '1280x720';
  254. const SIZE_HD1080 = '1920x1080';
  255. /**
  256. * Ratio Presets
  257. */
  258. const RATIO_STANDARD = '4:3';
  259. const RATIO_WIDE = '16:9';
  260. const RATIO_CINEMATIC = '1.85';
  261. /**
  262. * A public var that is to the information available about
  263. * the current ffmpeg compiled binary.
  264. * @var mixed
  265. * @access public
  266. */
  267. public static $ffmpeg_info = false;
  268. /**
  269. * A private var that contains the info of any file that is accessed by PHPVideoToolkit::getFileInfo();
  270. * @var array
  271. * @access private
  272. */
  273. private static $_file_info = array();
  274. /**
  275. * Determines what happens when an error occurs
  276. * @var boolean If true then the script will die, if not false is return by the error
  277. * @access public
  278. */
  279. public $on_error_die = false;
  280. /**
  281. * Holds the log file name
  282. * @var string
  283. * @access private
  284. */
  285. private $_log_file = null;
  286. /**
  287. * Determines if when outputting image frames if the outputted files should have the %d number
  288. * replaced with the frames timecode.
  289. * @var boolean If true then the files will be renamed.
  290. * @access public
  291. */
  292. public $image_output_timecode = true;
  293. /**
  294. * Holds the timecode separator for when using $image_output_timecode = true
  295. * Not all systems allow ':' in filenames.
  296. * @var string
  297. * @access public
  298. */
  299. public $timecode_seperator_output = '-';
  300. /**
  301. * Holds the starting time code when outputting image frames.
  302. * @var string The timecode hh(n):mm:ss:ff
  303. * @access private
  304. */
  305. private $_image_output_timecode_start = '00:00:00.00';
  306. /**
  307. * The format in which the image %timecode placeholder string is outputted.
  308. * - %hh (hours) representative of hours
  309. * - %mm (minutes) representative of minutes
  310. * - %ss (seconds) representative of seconds
  311. * - %fn (frame number) representative of frames (of the current second, not total frames)
  312. * - %ms (milliseconds) representative of milliseconds (of the current second, not total milliseconds) (rounded to 3 decimal places)
  313. * - %ft (frames total) representative of total frames (ie frame number)
  314. * - %st (seconds total) representative of total seconds (rounded).
  315. * - %sf (seconds floored) representative of total seconds (floored).
  316. * - %mt (milliseconds total) representative of total milliseconds. (rounded to 3 decimal places)
  317. * NOTE; there are special characters that will be replace by PHPVideoToolkit::$timecode_seperator_output, these characters are
  318. * - :
  319. * - .
  320. * @var string
  321. * @access public
  322. */
  323. private $image_output_timecode_format = '%hh-%mm-%ss-%fn';
  324. /**
  325. * Holds the fps of image extracts
  326. * @var integer
  327. * @access private
  328. */
  329. private $_image_output_timecode_fps = 1;
  330. /**
  331. * Holds the current execute commands that will need to be combined
  332. * @var array
  333. * @access private
  334. */
  335. private $_commands = array();
  336. /**
  337. * Holds the commands executed
  338. * @var array
  339. * @access private
  340. */
  341. private $_processed = array();
  342. /**
  343. * Holds the file references to those that have been processed
  344. * @var array
  345. * @access private
  346. */
  347. private $_files = array();
  348. /**
  349. * Holds the errors encountered
  350. * @access private
  351. * @var array
  352. */
  353. private $_errors = array();
  354. /**
  355. * Holds the input file / input file sequence
  356. * @access private
  357. * @var string
  358. */
  359. private $_input_file = null;
  360. /**
  361. * Holds the output file / output file sequence
  362. * @access private
  363. * @var string
  364. */
  365. private $_output_address = null;
  366. /**
  367. * Holds the process file / process file sequence
  368. * @access private
  369. * @var string
  370. */
  371. private $_process_address = null;
  372. /**
  373. * Temporary filename prefix
  374. * @access private
  375. * @var string
  376. */
  377. private $_tmp_file_prefix = 'tmp_';
  378. /**
  379. * Holds the temporary directory name
  380. * @access private
  381. * @var string
  382. */
  383. private $_tmp_directory = null;
  384. /**
  385. * Holds the directory paths that need to be removed by the ___destruct function
  386. * @access private
  387. * @var array
  388. */
  389. private $_unlink_dirs = array();
  390. /**
  391. * Holds the file paths that need to be deleted by the ___destruct function
  392. * @access private
  393. * @var array
  394. */
  395. private $_unlink_files = array();
  396. /**
  397. * Holds the timer start micro-float.
  398. * @access private
  399. * @var integer
  400. */
  401. private $_timer_start = 0;
  402. /**
  403. * Holds the times taken to process each file.
  404. * @access private
  405. * @var array
  406. */
  407. private $_timers = array();
  408. /**
  409. * Holds the times taken to process each file.
  410. * @access private
  411. * @var constant
  412. */
  413. private $_overwrite_mode = null;
  414. /**
  415. * Holds a integer value that flags if the image extraction is just a single frame.
  416. * @access private
  417. * @var integer
  418. */
  419. private $_single_frame_extraction = null;
  420. /**
  421. * Holds the watermark file that is used to watermark any outputted images via GD.
  422. * @access private
  423. * @var string
  424. */
  425. private $_watermark_url = null;
  426. /**
  427. * Holds the watermark options used to watermark any outputted images via GD.
  428. * @access private
  429. * @var array
  430. */
  431. private $_watermark_options = null;
  432. /**
  433. * Holds the number of files processed per run.
  434. * @access private
  435. * @var integer
  436. */
  437. private $_process_file_count = 0;
  438. /**
  439. * Holds the times taken to process each file.
  440. * @access private
  441. * @var array
  442. */
  443. private $_post_processes = array();
  444. /**
  445. * Holds the times taken to process each file.
  446. * @access private
  447. * @var array
  448. */
  449. private static $_static = true;
  450. /**
  451. * Holds commands should be sent added to the exec before the input file, this is by no means a definitive list
  452. * of all the ffmpeg commands, as it only utilizes the ones in use by this class. Also only commands that have
  453. * specific required places are entered in the arrays below. Anything not in these arrays will be treated as an
  454. * after-input item.
  455. * @access private
  456. * @var array
  457. */
  458. // private $_cmds_before_input = array();
  459. private $_cmds_before_input = array('-inputr');
  460. // private $_cmds_before_input = array('-r', '-f');
  461. /**
  462. * Constructs the class and sets the temporary directory.
  463. *
  464. * @access private
  465. * @param string $tmp_directory A full absolute path to you temporary directory
  466. */
  467. function __construct($tmp_directory='/tmp/')
  468. {
  469. $this->_tmp_directory = $tmp_directory;
  470. PHPVideoToolkit::$_static = false;
  471. }
  472. public static function microtimeFloat()
  473. {
  474. list($usec, $sec) = explode(" ", microtime());
  475. return ((float) $usec + (float) $sec);
  476. }
  477. /**
  478. * Resets the class
  479. *
  480. * @access public
  481. * @param boolean $keep_input_file Determines whether or not to reset the input file currently set.
  482. */
  483. public function reset($keep_input_file=false, $keep_processes=false)
  484. {
  485. if($keep_input_file === false)
  486. {
  487. $this->_input_file = null;
  488. }
  489. if($keep_processes === false)
  490. {
  491. $this->_post_processes = array();
  492. }
  493. $this->_single_frame_extraction = null;
  494. $this->_output_address = null;
  495. $this->_process_address = null;
  496. $this->_log_file = null;
  497. $this->_commands = array();
  498. $this->_timer_start = 0;
  499. $this->_process_file_count = 0;
  500. $this->__destruct();
  501. }
  502. /**
  503. * Returns information about the specified file without having to use ffmpeg-php
  504. * as it consults the ffmpeg binary directly.
  505. *
  506. * @access public
  507. * @param string $tmp_directory A full absolute path to you temporary directory. Only needed in a static call.
  508. * @return mixed false on error encountered, true otherwise
  509. **/
  510. public function getFFmpegInfo($tmp_directory='/tmp/')
  511. {
  512. // check to see if the info has already been cached
  513. if(PHPVideoToolkit::$ffmpeg_info !== false)
  514. {
  515. return PHPVideoToolkit::$ffmpeg_info;
  516. }
  517. // check to see if this is a static call
  518. if(PHPVideoToolkit::$_static === true)
  519. {
  520. $toolkit = new PHPVideoToolkit($tmp_directory);
  521. return $toolkit->getFFmpegInfo();
  522. }
  523. $format = '';
  524. // $info_file = $this->_tmp_directory.$this->unique('ffinfo').'.info';
  525. // execute the ffmpeg lookup
  526. // exec(PHPVIDEOTOOLKIT_FFMPEG_BINARY.' -formats &> '.$info_file);
  527. exec(PHPVIDEOTOOLKIT_FFMPEG_BINARY.' -formats 2>&1', $buffer);
  528. $buffer = implode("\r\n", $buffer);
  529. // die($buffer);
  530. // $data = false;
  531. // try to open the file
  532. // $handle = fopen($info_file, 'r');
  533. // if($handle)
  534. // {
  535. $data = array();
  536. // $buffer = '';
  537. // loop through the lines of data and collect the buffer
  538. // while (!feof($handle))
  539. // {
  540. // $buffer .= fgets($handle, 4096);
  541. // }
  542. // echo($buffer);
  543. $data['compiler'] = array();
  544. $look_ups = array('configuration'=>'configuration: ', 'formats'=>'File formats:', 'codecs'=>'Codecs:', 'filters'=>'Bitstream filters:', 'protocols'=>'Supported file protocols:', 'abbreviations'=>'Frame size, frame rate abbreviations:', 'Note:');
  545. $total_lookups = count($look_ups);
  546. $pregs = array();
  547. $indexs = array();
  548. foreach($look_ups as $key=>$reg)
  549. {
  550. if(strpos($buffer, $reg) !== false)
  551. {
  552. $index = array_push($pregs, $reg);
  553. $indexs[$key] = $index;
  554. }
  555. }
  556. preg_match('/'.implode('(.*)', $pregs).'/s', $buffer, $matches);
  557. $configuration = trim($matches[$indexs['configuration']]);
  558. // grab the ffmpeg configuration flags
  559. preg_match_all('/--[a-zA-Z0-9\-]+/', $configuration, $config_flags);
  560. $data['compiler']['configuration'] = $config_flags[0];
  561. $data['compiler']['vhook-support'] = in_array('--enable-vhook', $config_flags[0]) && !in_array('--disable-vhook', $config_flags[0]);
  562. // grab the versions
  563. $data['compiler']['versions'] = array();
  564. preg_match_all('/([a-zA-Z0-9\-]+) version: ([0-9\.]+)/', $configuration, $versions);
  565. for($i=0, $a=count($versions[0]); $i<$a; $i++)
  566. {
  567. $data['compiler']['versions'][strtolower(trim($versions[1][$i]))] = $versions[2][$i];
  568. }
  569. // grab the ffmpeg compile info
  570. preg_match('/built on (.*), gcc: (.*)/', $configuration, $conf);
  571. if(count($conf) > 0)
  572. {
  573. $data['compiler']['gcc'] = $conf[2];
  574. $data['compiler']['build_date'] = $conf[1];
  575. $data['compiler']['build_date_timestamp'] = strtotime($conf[1]);
  576. }
  577. // grab the file formats available to ffmpeg
  578. preg_match_all('/ (DE|D|E) (.*) {1,} (.*)/', trim($matches[$indexs['formats']]), $formats);
  579. $data['formats'] = array();
  580. // loop and clean
  581. for($i=0, $a=count($formats[0]); $i<$a; $i++)
  582. {
  583. $data['formats'][strtolower(trim($formats[2][$i]))] = array(
  584. 'encode' => $formats[1][$i] == 'DE' || $formats[1][$i] == 'E',
  585. 'decode' => $formats[1][$i] == 'DE' || $formats[1][$i] == 'D',
  586. 'fullname' => $formats[3][$i]
  587. );
  588. }
  589. // grab the bitstream filters available to ffmpeg
  590. $data['filters'] = array();
  591. if(isset($indexs['filters']) && isset($matches[$indexs['filters']]))
  592. {
  593. $filters = trim($matches[$indexs['filters']]);
  594. if(empty($filters))
  595. {
  596. $data['filters'] = explode(' ', $filters);
  597. }
  598. }
  599. // grab the file prototcols available to ffmpeg
  600. $data['filters'] = array();
  601. if(isset($indexs['protocols']) && isset($matches[$indexs['protocols']]))
  602. {
  603. $protocols = trim($matches[$indexs['protocols']]);
  604. if(empty($protocols))
  605. {
  606. $data['protocols'] = explode(' ', str_replace(':', '', $protocols));
  607. }
  608. }
  609. // grab the abbreviations available to ffmpeg
  610. $data['abbreviations'] = array();
  611. if(isset($indexs['abbreviations']) && isset($matches[$indexs['abbreviations']]))
  612. {
  613. $abbreviations = trim($matches[$indexs['abbreviations']]);
  614. if(empty($abbreviations))
  615. {
  616. $data['abbreviations'] = explode(' ', $abbreviations);
  617. }
  618. }
  619. PHPVideoToolkit::$ffmpeg_info = $data;
  620. // }
  621. // fclose($handle);
  622. // if(is_file($info_file))
  623. // {
  624. // if the info file exists remove it
  625. // unlink($info_file);
  626. // }
  627. $data['ffmpeg-php-support'] = $this->hasFFmpegPHPSupport();
  628. return $data;
  629. }
  630. /**
  631. * Determines if your ffmpeg has particular codec support for encode or decode.
  632. *
  633. * @access public
  634. * @param string $codec The name of the codec you are checking for.
  635. * @param const $support PHPVideoToolkit::ENCODE or PHPVideoToolkit::DECODE, depending on which functionality is desired.
  636. * @return mixed. Boolean false if there is no support, true if there is support.
  637. */
  638. public function hasCodecSupport($codec, $support=PHPVideoToolkit::ENCODE)
  639. {
  640. $codec = strtolower($codec);
  641. $data = $this->getFFmpegInfo();
  642. return isset($data['formats'][$codec]) ? $data['formats'][$codec][$support] : false;
  643. }
  644. /**
  645. * Determines the type of support that exists for the FFmpeg-PHP module.
  646. *
  647. * @access public
  648. * @return mixed. Boolean false if there is no support, String 'module' if the actuall
  649. * FFmpeg-PHP module is loaded, or String 'emulated' if the FFmpeg-PHP classes
  650. * can be emulated through the adapter classes.
  651. */
  652. public function hasFFmpegPHPSupport()
  653. {
  654. return extension_loaded('ffmpeg') ? 'module' : (is_file(dirname(__FILE__).DIRECTORY_SEPARATOR.'adapters'.DIRECTORY_SEPARATOR.'ffmpeg-php'.DIRECTORY_SEPARATOR.'ffmpeg_movie.php') && is_file(dirname(__FILE__).DIRECTORY_SEPARATOR.'adapters'.DIRECTORY_SEPARATOR.'ffmpeg-php'.DIRECTORY_SEPARATOR.'ffmpeg_frame.php') && is_file(dirname(__FILE__).DIRECTORY_SEPARATOR.'adapters'.DIRECTORY_SEPARATOR.'ffmpeg-php'.DIRECTORY_SEPARATOR.'ffmpeg_animated_gif.php') ? 'emulated' : false);
  655. }
  656. /**
  657. * Determines if the ffmpeg binary has been compiled with vhook support.
  658. *
  659. * @access public
  660. * @return mixed. Boolean false if there is no support, true there is support.
  661. */
  662. public function hasVHookSupport()
  663. {
  664. $info = $this->getFFmpegInfo();
  665. return $info['compiler']['vhook-support'];
  666. }
  667. /**
  668. * Returns information about the specified file without having to use ffmpeg-php
  669. * as it consults the ffmpeg binary directly. This idea for this function has been borrowed from
  670. * a French ffmpeg class located: http://www.phpcs.com/codesource.aspx?ID=45279
  671. *
  672. * @access public
  673. * @todo Change the search from string explode to a regex based search
  674. * @param string $file The absolute path of the file that is required to be manipulated.
  675. * @return mixed false on error encountered, true otherwise
  676. **/
  677. public function getFileInfo($file=false, $tmp_directory='/tmp/')
  678. {
  679. // check to see if this is a static call
  680. if($file !== false && PHPVideoToolkit::$_static === true)
  681. {
  682. $toolkit = new PHPVideoToolkit($tmp_directory);
  683. return $toolkit->getFileInfo($file);
  684. }
  685. // if the file has not been specified check to see if an input file has been specified
  686. if($file === false)
  687. {
  688. if(!$this->_input_file)
  689. {
  690. // input file not valid
  691. return $this->_raiseError('getFileInfo_no_input');
  692. //<- exits
  693. }
  694. $file = $this->_input_file;
  695. }
  696. $file = escapeshellarg($file);
  697. // die($file);
  698. // create a hash of the filename
  699. $hash = md5($file);
  700. // check to see if the info has already been generated
  701. if(isset(self::$_file_info[$hash]))
  702. {
  703. return self::$_file_info[$hash];
  704. }
  705. // generate a random filename
  706. $info_file = $this->_tmp_directory.$this->unique($hash).'.info';
  707. // execute the ffmpeg lookup
  708. // exec(PHPVIDEOTOOLKIT_FFMPEG_BINARY.' -i '.$file.' &> '.$info_file);
  709. exec(PHPVIDEOTOOLKIT_FFMPEG_BINARY.' -i '.$file.' 2>&1', $buffer);
  710. $buffer = implode("\r\n", $buffer);
  711. // $data = false;
  712. // try to open the file
  713. // $handle = fopen($info_file, 'r');
  714. // if($handle)
  715. // {
  716. $data = array();
  717. // $buffer = '';
  718. // loop through the lines of data and collect the buffer
  719. // while (!feof($handle))
  720. // {
  721. // $buffer .= fgets($handle, 4096);
  722. // }
  723. // die($buffer);
  724. // grab the duration and bitrate data
  725. preg_match_all('/Duration: (.*)/', $buffer, $matches);
  726. if(count($matches) > 0)
  727. {
  728. $parts = explode(', ', trim($matches[1][0]));
  729. $data['duration'] = array();
  730. $timecode = $parts[0];
  731. $data['duration']['seconds'] = $this->timecodeToSeconds($timecode);
  732. $data['bitrate'] = intval(ltrim($parts[2], 'bitrate: '));
  733. $data['duration']['start'] = ltrim($parts[1], 'start: ');
  734. $data['duration']['timecode'] = array();
  735. $data['duration']['timecode']['rounded'] = substr($timecode, 0, 8);
  736. $data['duration']['timecode']['seconds'] = array();
  737. $data['duration']['timecode']['seconds']['exact'] = $timecode;
  738. $data['duration']['timecode']['seconds']['excess'] = intval(substr($timecode, 9));
  739. }
  740. // match the video stream info
  741. preg_match('/Stream(.*): Video: (.*)/', $buffer, $matches);
  742. if(count($matches) > 0)
  743. {
  744. $data['video'] = array();
  745. // get the dimension parts
  746. // print_r($matches);
  747. preg_match('/([0-9]{1,5})x([0-9]{1,5})/', $matches[2], $dimensions_matches);
  748. // print_r($dimensions_matches);
  749. $dimensions_value = $dimensions_matches[0];
  750. $data['video']['dimensions'] = array(
  751. 'width' => floatval($dimensions_matches[1]),
  752. 'height' => floatval($dimensions_matches[2])
  753. );
  754. // get the framerate
  755. preg_match('/([0-9\.]+) (fps|tb)\(r\)/', $matches[0], $fps_matches);
  756. $data['video']['frame_rate'] = floatval($fps_matches[1]);
  757. $fps_value = $fps_matches[0];
  758. // get the ratios
  759. preg_match('/\[PAR ([0-9\:\.]+) DAR ([0-9\:\.]+)\]/', $matches[0], $ratio_matches);
  760. if(count($ratio_matches))
  761. {
  762. $data['video']['pixel_aspect_ratio'] = $ratio_matches[1];
  763. $data['video']['display_aspect_ratio'] = $ratio_matches[2];
  764. }
  765. // work out the number of frames
  766. if(isset($data['duration']) && isset($data['video']))
  767. {
  768. // set the total frame count for the video
  769. $data['video']['frame_count'] = ceil($data['duration']['seconds'] * $data['video']['frame_rate']);
  770. // set the framecode
  771. $frames = ceil($data['video']['frame_rate']*($data['duration']['timecode']['seconds']['excess']/10));
  772. $data['duration']['timecode']['frames'] = array();
  773. $data['duration']['timecode']['frames']['exact'] = $data['duration']['timecode']['rounded'].'.'.$frames;
  774. $data['duration']['timecode']['frames']['excess'] = $frames;
  775. $data['duration']['timecode']['frames']['total'] = $data['video']['frame_count'];
  776. }
  777. // formats should be anything left over, let me know if anything else exists
  778. $parts = explode(',', $matches[2]);
  779. $other_parts = array($dimensions_value, $fps_value);
  780. $formats = array();
  781. foreach($parts as $key=>$part)
  782. {
  783. $part = trim($part);
  784. if(!in_array($part, $other_parts))
  785. {
  786. array_push($formats, $part);
  787. }
  788. }
  789. $data['video']['pixel_format'] = $formats[1];
  790. $data['video']['codec'] = $formats[0];
  791. }
  792. // match the audio stream info
  793. preg_match('/Stream(.*): Audio: (.*)/', $buffer, $matches);
  794. if(count($matches) > 0)
  795. {
  796. // setup audio values
  797. $data['audio'] = array(
  798. 'stereo' => -1,
  799. 'sample_rate' => -1,
  800. 'sample_rate' => -1
  801. );
  802. $other_parts = array();
  803. // get the stereo value
  804. preg_match('/(stereo|mono)/i', $matches[0], $stereo_matches);
  805. if(count($stereo_matches))
  806. {
  807. $data['audio']['stereo'] = $stereo_matches[0];
  808. array_push($other_parts, $stereo_matches[0]);
  809. }
  810. // get the sample_rate
  811. preg_match('/([0-9]{3,6}) Hz/', $matches[0], $sample_matches);
  812. if(count($sample_matches))
  813. {
  814. $data['audio']['sample_rate'] = count($sample_matches) ? floatval($sample_matches[1]) : -1;
  815. array_push($other_parts, $sample_matches[0]);
  816. }
  817. // get the bit rate
  818. preg_match('/([0-9]{1,3}) kb\/s/', $matches[0], $bitrate_matches);
  819. if(count($bitrate_matches))
  820. {
  821. $data['audio']['bitrate'] = count($bitrate_matches) ? floatval($bitrate_matches[1]) : -1;
  822. array_push($other_parts, $bitrate_matches[0]);
  823. }
  824. // formats should be anything left over, let me know if anything else exists
  825. $parts = explode(',', $matches[2]);
  826. $formats = array();
  827. foreach($parts as $key=>$part)
  828. {
  829. $part = trim($part);
  830. if(!in_array($part, $other_parts))
  831. {
  832. array_push($formats, $part);
  833. }
  834. }
  835. $data['audio']['codec'] = $formats[0];
  836. }
  837. // check that some data has been obtained
  838. if(!count($data))
  839. {
  840. $data = false;
  841. }
  842. else
  843. {
  844. $data['_raw_info'] = $buffer;
  845. }
  846. // fclose($handle);
  847. // }
  848. // if(is_file($info_file))
  849. // {
  850. // if the info file exists remove it
  851. // unlink($info_file);
  852. // }
  853. // cache info and return
  854. return self::$_file_info[$hash] = $data;
  855. }
  856. /**
  857. * Sets the input file that is going to be manipulated.
  858. *
  859. * @access public
  860. * @param string $file The absolute path of the file that is required to be manipulated.
  861. * @param mixed $input_frame_rate If 0 (default) then no input frame rate is set, if false it is automatically retreived, otherwise
  862. * any other integer will be set as the incoming frame rate.
  863. * @return boolean false on error encountered, true otherwise
  864. */
  865. public function setInputFile($file, $input_frame_rate=0)
  866. {
  867. $files_length = count($file);
  868. // if the total number of files entered is 1 then only one file is being processed
  869. if($files_length == 1)
  870. {
  871. // check the input file, if there is a %d in there or a similar %03d then the file inputted is a sequence, if neither of those is found
  872. // then qheck to see if the file exists
  873. if(!preg_match('/\%([0-9]+)d/', $file) && strpos($file, '%d') === false && !is_file($file))
  874. {
  875. // input file not valid
  876. return $this->_raiseError('setInputFile_file_existence', array('file'=>$file));
  877. //<- exits
  878. }
  879. $escaped_name = $file;
  880. // $escaped_name = escapeshellarg($files[0]);
  881. $this->_input_file = $escaped_name;
  882. $this->_input_file_id = md5($escaped_name);
  883. // the -inputr is a hack for -r to come before the input
  884. if($input_frame_rate !== 0)
  885. {
  886. $info = $this->getFileInfo();
  887. if(isset($info['video']))
  888. {
  889. if($input_frame_rate === false)
  890. {
  891. $input_frame_rate = $info['video']['frame_rate'];
  892. }
  893. // input frame rate is a command hack
  894. $this->addCommand('-inputr', $input_frame_rate);
  895. }
  896. }
  897. }
  898. else
  899. {
  900. // more than one video is being added as input so we must join them all
  901. call_user_func_array(array(&$this, 'addVideo'), array($file, $input_frame_rate));
  902. }
  903. return true;
  904. }
  905. /**
  906. * A shortcut for converting video to FLV.
  907. *
  908. * @access public
  909. * @param integer $audio_sample_frequency
  910. * @param integer $audio_bitrate
  911. */
  912. public function setFormatToFLV($audio_sample_frequency=44100, $audio_bitrate=64)
  913. {
  914. $this->addCommand('-sameq');
  915. $this->setAudioFormat(self::FORMAT_MP3);
  916. // adjust the audio rates
  917. $this->setAudioBitRate($audio_bitrate);
  918. $this->setAudioSampleFrequency($audio_sample_frequency);
  919. // set the video format
  920. $this->setFormat(self::FORMAT_FLV);
  921. // flag that the flv has to have meta data added after the excecution of this command
  922. // register the post tidy process
  923. $this->registerPostProcess('_addMetaToFLV', $this);
  924. }
  925. /**
  926. * When converting video to FLV the meta data has to be added by a ruby program called FLVTools2.
  927. * This is a second exec call only after the video has been converted to FLV
  928. * http://inlet-media.de/flvtool2
  929. *
  930. * @access private
  931. */
  932. private function _addMetaToFLV($files)
  933. {
  934. $file = array_pop($files);
  935. // prepare the command suitable for exec
  936. $exec_string = $this->_prepareCommand(PHPVIDEOTOOLKIT_FLVTOOLS_BINARY, '-U '.$file);
  937. // execute the command
  938. exec($exec_string);
  939. if(is_array($this->_processed[0]))
  940. {
  941. array_push($this->_processed[0], $exec_string);
  942. }
  943. else
  944. {
  945. $this->_processed[0] = array($this->_processed[0], $exec_string);
  946. }
  947. return true;
  948. }
  949. /**
  950. * Streams a FLV file from a given point. You can control bandwidth, cache and session options.
  951. * Inspired by xmoov-php
  952. * @see xmoov-php,
  953. * - @link http://xmoov.com/
  954. * - @author Eric Lorenzo Benjamin jr
  955. * @access public
  956. * @param integer $seek_pos The position in the file to seek to.
  957. * @param array|boolean $bandwidth_options If a boolean value, false then no bandwidth limiting will take place.
  958. * If true then bandwidth limiting will take place with packet_size = 90 and packet_interval = 0.3.
  959. * If an array the following values are default packet_size = 90 and packet_interval = 0.3, you will also
  960. * have to set active = true, ie array('active'=>true, 'packet_size'=>90, 'packet_interval'=>0.3)
  961. * @param boolean $allow_cache If true the file will be allowed to cache in the browser, if false then it won't
  962. * @return boolean
  963. */
  964. public function flvStreamSeek($seek_pos=0, $bandwidth_options=array(), $allow_cache=true)
  965. {
  966. // check for input file
  967. if(!$this->_input_file)
  968. {
  969. // input file not valid
  970. return $this->_raiseError('streamFLV_no_input');
  971. //<- exits
  972. }
  973. // make the pos safe
  974. $seek_pos = intval($seek_pos);
  975. // absorb the bandwidth options
  976. $bandwidth_options = is_array($bandwidth_options) ? array_merge(array('active'=>false, 'packet_size'=>90, 'packet_interval'=>0.3), $bandwidth_options) : array('active'=>$bandwidth_options, 'packet_size'=>90, 'packet_interval'=>0.3);
  977. // if this file is not allowed to be cached send cache headers for all browsers.
  978. if(!$allow_cache)
  979. {
  980. session_cache_limiter('nocache');
  981. header('Expires: Thu, 19 Nov 1981 08:52:00 GMT');
  982. header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
  983. header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
  984. header('Pragma: no-cache');
  985. }
  986. // open file
  987. $handle = fopen($this->_input_file, 'rb');
  988. $file_size = filesize($this->_input_file) - (($seek_pos > 0) ? $seek_pos + 1 : 0);
  989. // send the flv headers
  990. header('Content-Type: video/x-flv');
  991. header('Content-Disposition: attachment; filename="'.basename($this->_input_file).'"');
  992. header('Content-Length: '.$file_size);
  993. // flv format header
  994. if($seek_pos != 0)
  995. {
  996. print('FLV');
  997. print(pack('C', 1));
  998. print(pack('C', 1));
  999. print(pack('N', 9));
  1000. print(pack('N', 9));
  1001. }
  1002. // seek to the required point
  1003. if(fseek($handle, $seek_pos) === -1)
  1004. {
  1005. // input file not valid
  1006. return $this->_raiseError('streamFLV_passed_eof');
  1007. //<- exits
  1008. }
  1009. // if bandwidth control is active then workout the options
  1010. if($bandwidth_options['active'])
  1011. {
  1012. $packet_interval = intval($bandwidth_options['packet_interval']);
  1013. $packet_size = intval($bandwidth_options['packet_size']) * 1042;
  1014. }
  1015. // output the file
  1016. while(!feof($handle))
  1017. {
  1018. // limit the bandwidth
  1019. if($bandwidth_options['active'] && $packet_interval > 0)
  1020. {
  1021. // output the required packet
  1022. $time_start = self::microtimeFloat();
  1023. echo fread($handle, $packet_size);
  1024. $time_stop = self::microtimeFloat();
  1025. // delay the output
  1026. $time_difference = $time_stop - $time_start;
  1027. if($time_difference < $packet_interval)
  1028. {
  1029. usleep(($packet_interval * 1000000) - ($time_difference * 1000000));
  1030. }
  1031. }
  1032. // no bandwidth limiting
  1033. else
  1034. {
  1035. echo fread($handle, $file_size);
  1036. }
  1037. }
  1038. // close the file
  1039. fclose($handle);
  1040. return true;
  1041. }
  1042. /**
  1043. * This is an alias for setFormat, but restricts it to audio only formats.
  1044. *
  1045. * @access public
  1046. * @param integer $format A supported audio format.
  1047. * @param integer $audio_sample_frequency
  1048. * @param integer $audio_bitrate
  1049. **/
  1050. public function extractAudio($format=PHPVideoToolkit::FORMAT_MP3, $audio_sample_frequency=44100, $audio_bitrate=64)
  1051. {
  1052. // check the format is one of the audio formats
  1053. if(!in_array($format, array(self::FORMAT_AAC, self::FORMAT_AIFF, self::FORMAT_MP2, self::FORMAT_MP3, self::FORMAT_MP4, self::FORMAT_MPEG4, self::FORMAT_M4A, self::FORMAT_WAV)))
  1054. {
  1055. return $this->_raiseError('extractAudio_valid_format', array('format'=>$format));
  1056. //<- exits
  1057. }
  1058. $this->setFormat($format);
  1059. // adjust the audio rates
  1060. $this->setAudioBitRate($audio_bitrate);
  1061. $this->setAudioSampleFrequency($audio_sample_frequency);
  1062. }
  1063. /**
  1064. * Sets the new video format.
  1065. *
  1066. * @access public
  1067. * @param defined $format The format should use one of the defined variables stated below.
  1068. * PHPVideoToolkit::FORMAT_3GP2 - 3g2
  1069. * PHPVideoToolkit::FORMAT_3GP - 3gp
  1070. * PHPVideoToolkit::FORMAT_AAC - aac
  1071. * PHPVideoToolkit::FORMAT_AIFF - aiff
  1072. * PHPVideoToolkit::FORMAT_AMR - amr
  1073. * PHPVideoToolkit::FORMAT_ASF - asf
  1074. * PHPVideoToolkit::FORMAT_AVI - avi
  1075. * PHPVideoToolkit::FORMAT_FLV - flv
  1076. * PHPVideoToolkit::FORMAT_GIF - gif
  1077. * PHPVideoToolkit::FORMAT_MJ2 - mj2
  1078. * PHPVideoToolkit::FORMAT_MP2 - mp2
  1079. * PHPVideoToolkit::FORMAT_MP3 - mp3
  1080. * PHPVideoToolkit::FORMAT_MP4 - mp4
  1081. * PHPVideoToolkit::FORMAT_MPEG4 - mpeg4
  1082. * PHPVideoToolkit::FORMAT_M4A - m4a
  1083. * PHPVideoToolkit::FORMAT_MPEG - mpeg
  1084. * PHPVideoToolkit::FORMAT_MPEG1 - mpeg1video
  1085. * PHPVideoToolkit::FORMAT_MPEG2 - mpeg2video
  1086. * PHPVideoToolkit::FORMAT_MPEGVIDEO - mpegvideo
  1087. * PHPVideoToolkit::FORMAT_PSP - psp
  1088. * PHPVideoToolkit::FORMAT_RM - rm
  1089. * PHPVideoToolkit::FORMAT_SWF - swf
  1090. * PHPVideoToolkit::FORMAT_VOB - vob
  1091. * PHPVideoToolkit::FORMAT_WAV - wav
  1092. * PHPVideoToolkit::FORMAT_JPG - jpg
  1093. * @return boolean false on error encountered, true otherwise
  1094. */
  1095. public function setFormat($format)
  1096. {
  1097. // validate input
  1098. if(!in_array($format, array(self::FORMAT_3GP2, self::FORMAT_3GP, self::FORMAT_AAC, self::FORMAT_AIFF, self::FORMAT_AMR, self::FORMAT_ASF, self::FORMAT_AVI, self::FORMAT_FLV, self::FORMAT_GIF, self::FORMAT_MJ2, self::FORMAT_MP2, self::FORMAT_MP3, self::FORMAT_MP4, self::FORMAT_MPEG4, self::FORMAT_M4A, self::FORMAT_MPEG, self::FORMAT_MPEG1, self::FORMAT_MPEG2, self::FORMAT_MPEGVIDEO, self::FORMAT_PSP, self::FORMAT_RM, self::FORMAT_SWF, self::FORMAT_VOB, self::FORMAT_WAV, self::FORMAT_JPG)))
  1099. {
  1100. return $this->_raiseError('setFormat_valid_format', array('format'=>$format));
  1101. //<- exits
  1102. }
  1103. return $this->addCommand('-f', $format);
  1104. }
  1105. /**
  1106. * Sets the audio sample frequency for audio outputs
  1107. *
  1108. * @access public
  1109. * @param integer $audio_sample_frequency Valid values are 11025, 22050, 44100
  1110. * @return boolean false on error encountered, true otherwise
  1111. */
  1112. public function setAudioSampleFrequency($audio_sample_frequency)
  1113. {
  1114. // validate input
  1115. if(!in_array(intval($audio_sample_frequency), array(11025, 22050, 44100)))
  1116. {
  1117. return $this->_raiseError('setAudioSampleFrequency_valid_frequency', array('frequency'=>$audio_sample_frequency));
  1118. //<- exits
  1119. }
  1120. return $this->addCommand('-ar', $audio_sample_frequency);
  1121. }
  1122. /**
  1123. * @access public
  1124. * @depreciated
  1125. * @see PHPVideoToolkit::setAudioCodec()
  1126. */
  1127. public function setAudioFormat($video_format)
  1128. {
  1129. return $this->setAudioCodec($video_format);
  1130. }
  1131. /**
  1132. * Sets the audio format for audio outputs
  1133. *
  1134. * @access public
  1135. * @param integer $audio_format Valid values are PHPVideoToolkit::FORMAT_AAC, PHPVideoToolkit::FORMAT_AIFF, PHPVideoToolkit::FORMAT_AMR, PHPVideoToolkit::FORMAT_ASF, PHPVideoToolkit::FORMAT_MP2, PHPVideoToolkit::FORMAT_MP3, PHPVideoToolkit::FORMAT_MP4, PHPVideoToolkit::FORMAT_MPEG2, PHPVideoToolkit::FORMAT_RM, PHPVideoToolkit::FORMAT_WAV
  1136. * @return boolean false on error encountered, true otherwise
  1137. */
  1138. public function setAudioCodec($audio_format)
  1139. {
  1140. // validate input
  1141. if(!in_array($audio_format, array(self::FORMAT_AAC, self::FORMAT_AIFF, self::FORMAT_AMR, self::FORMAT_ASF, self::FORMAT_MP2, self::FORMAT_MP3, self::FORMAT_MP4, self::FORMAT_MPEG2, self::FORMAT_RM, self::FORMAT_WAV)))
  1142. {
  1143. return $this->_raiseError('setAudioFormat_valid_format', array('format'=>$audio_format));
  1144. //<- exits
  1145. }
  1146. // run a libmp3lame check as it require different mp3 codec
  1147. // updated thanks to Varon for providing the research
  1148. if($audio_format == self::FORMAT_MP3)
  1149. {
  1150. $info = $this->getFFmpegInfo();
  1151. // if(isset($info['formats']['libmp3lame']))
  1152. // {
  1153. $audio_format = 'libmp3lame';
  1154. // }
  1155. }
  1156. return $this->addCommand('-acodec', $audio_format);
  1157. }
  1158. /**
  1159. * @access public
  1160. * @depreciated
  1161. * @see PHPVideoToolkit::setVideoCodec()
  1162. */
  1163. public function setVideoFormat($video_format)
  1164. {
  1165. return $this->setVideoCodec($video_format);
  1166. }
  1167. /**
  1168. * Sets the video format for video outputs. This should not be confused with setFormat. setVideoFormat does not generally need to
  1169. * be called unless setting a specific video format for a type of media format. It gets a little confusing...
  1170. *
  1171. * @access public
  1172. * @param integer $video_format Valid values are 11025, 22050, 44100
  1173. * @return boolean false on error encountered, true otherwise
  1174. */
  1175. public function setVideoCodec($video_format)
  1176. {
  1177. // validate input
  1178. if(!in_array($video_format, array(self::FORMAT_3GP2, self::FORMAT_3GP, self::FORMAT_AVI, self::FORMAT_FLV, self::FORMAT_GIF, self::FORMAT_MJ2, self::FORMAT_MP4, self::FORMAT_MPEG4, self::FORMAT_M4A, self::FORMAT_MPEG, self::FORMAT_MPEG1, self::FORMAT_MPEG2, self::FORMAT_MPEGVIDEO)))
  1179. {
  1180. return $this->_raiseError('setVideoFormat_valid_format', array('format'=>$video_format));
  1181. //<- exits
  1182. }
  1183. return $this->addCommand('-vcodec', $video_format);
  1184. }
  1185. /**
  1186. * Disables audio encoding
  1187. *
  1188. * @access public
  1189. * @return boolean false on error encountered, true otherwise
  1190. */
  1191. public function disableAudio()
  1192. {
  1193. return $this->addCommand('-an');
  1194. }
  1195. /**
  1196. * Sets the audio bitrate
  1197. *
  1198. * @access public
  1199. * @param integer $audio_bitrate Valid values are 16, 32, 64
  1200. * @return boolean false on error encountered, true otherwise
  1201. */
  1202. public function setAudioBitRate($bitrate)
  1203. {
  1204. // validate input
  1205. if(!in_array(intval($bitrate), array(16, 32, 64, 128)))
  1206. {
  1207. return $this->_raiseError('setAudioBitRate_valid_bitrate', array('bitrate'=>$bitrate));
  1208. //<- exits
  1209. }
  1210. return $this->addCommand('-ab', $bitrate.'kb');
  1211. }
  1212. /**
  1213. * Compiles an array of images into a video. This sets the input file (setInputFile) so you do not need to set it.
  1214. * The images should be a full absolute path to the actual image file.
  1215. * NOTE 1; This copies and renames all the supplied images into a temporary folder so the images don't have to be specifically named. However, when
  1216. * creating the ffmpeg instance you will need to set the absolute path to the temporary folder. The default path is '/tmp/'.
  1217. * NOTE 2; Please make sure all of the images are all of the same type.
  1218. *
  1219. * @access public
  1220. * @param array $images An array of images that are to be joined and converted into a video
  1221. * @param integer $input_frame_rate An integer that will specify the input frame rate for the images.
  1222. * @return boolean Returns false on encountering an error
  1223. */
  1224. public function prepareImagesForConversionToVideo($images, $input_frame_rate)
  1225. {
  1226. // http://ffmpeg.mplayerhq.hu/faq.html#TOC3
  1227. // ffmpeg -f image2 -i img%d.jpg /tmp/a.mpg
  1228. if(empty($images))
  1229. {
  1230. return $this->_raiseError('prepareImagesForConversionToVideo_one_img');
  1231. //<- exits
  1232. }
  1233. // loop through and validate existence first before making a temporary copy
  1234. foreach ($images as $key=>$img)
  1235. {
  1236. if(!is_file($img))
  1237. {
  1238. return $this->_raiseError('prepareImagesForConversionToVideo_img_404', array('img'=>$img));
  1239. //<- exits
  1240. }
  1241. }
  1242. if(!is_dir($this->_tmp_directory))
  1243. {
  1244. return $this->_raiseError('generic_temp_404');
  1245. //<- exits
  1246. }
  1247. if(!is_writeable($this->_tmp_directory))
  1248. {
  1249. return $this->_raiseError('generic_temp_writable');
  1250. //<- exits
  1251. }
  1252. // get the number of preceding places for the files based on how many files there are to copy
  1253. $total = count($images);
  1254. // create a temp dir in the temp dir
  1255. $uniqid = $this->unique();
  1256. mkdir($this->_tmp_directory.$uniqid, 0777);
  1257. // loop through, copy and rename specified images to the temp dir
  1258. $ext = false;
  1259. foreach ($images as $key=>$img)
  1260. {
  1261. $file_ext = array_pop(explode('.', $img));
  1262. if($ext !== false && $ext !== $file_ext)
  1263. {
  1264. return $this->_raiseError('prepareImagesForConversionToVideo_img_type');
  1265. //<- exits
  1266. }
  1267. $ext = $file_ext;
  1268. $tmp_file = $this->_tmp_directory.$uniqid.DIRECTORY_SEPARATOR.$this->_tmp_file_prefix.$key.'.'.$ext;
  1269. if(!@copy($img, $tmp_file))
  1270. {
  1271. return $this->_raiseError('prepareImagesForConversionToVideo_img_copy', array('img'=>$img, 'tmpfile'=>$tmp_file));
  1272. //<- exits
  1273. }
  1274. // push the tmp file name into the unlinks so they can be deleted on class destruction
  1275. array_push($this->_unlink_files, $tmp_file);
  1276. }
  1277. // the inputr is a hack for -r to come before the input
  1278. $this->addCommand('-inputr', $input_frame_rate);
  1279. // exit;
  1280. // add the directory to the unlinks
  1281. array_push($this->_unlink_dirs, $this->_tmp_directory.$uniqid);
  1282. // get the input file format
  1283. $file_iteration = $this->_tmp_file_prefix.'%d.'.$ext;
  1284. // set the input filename
  1285. return $this->setInputFile($this->_tmp_directory.$uniqid.DIRECTORY_SEPARATOR.$file_iteration);
  1286. }
  1287. /**
  1288. * Sets the video bitrate
  1289. *
  1290. * @access public
  1291. * @param integer $bitrate
  1292. * @return boolean
  1293. */
  1294. public function setVideoBitRate($bitrate)
  1295. {
  1296. $bitrate = intval($bitrate);
  1297. return $this->addCommand('-b', $bitrate.'kb');
  1298. }
  1299. /**
  1300. * Sets the amount of time an animated gif output will loop
  1301. *
  1302. * @access public
  1303. * @param integer $loop_count If false the gif will not loop, if 0 it will loop endlessly, any other number it will loop that amount.
  1304. */
  1305. public function setGifLoops($loop_count)
  1306. {
  1307. if($loop_count !== false)
  1308. {
  1309. $this->addCommand('-loop_output', $loop_count);
  1310. }
  1311. }
  1312. /**
  1313. * @access public
  1314. * @depreciated
  1315. * @see PHPVideoToolkit::setVideoDimensions()
  1316. */
  1317. public function setVideoOutputDimensions($width, $height=null)
  1318. {
  1319. return $this->setVideoDimensions($width, $height);
  1320. }
  1321. /**
  1322. * Sets the video output dimensions (in pixels)
  1323. *
  1324. * @access public
  1325. * @param mixed $width If an integer height also has to be specified, otherwise you can use one of the class constants
  1326. * PHPVideoToolkit::SIZE_SAS = Same as input source
  1327. * PHPVideoToolkit::SIZE_SQCIF = 128 x 96
  1328. * PHPVideoToolkit::SIZE_QCIF = 176 x 144
  1329. * PHPVideoToolkit::SIZE_CIF = 352 x 288
  1330. * PHPVideoToolkit::SIZE_4CIF = 704 x 576
  1331. * PHPVideoToolkit::SIZE_QQVGA = 160 x 120
  1332. * PHPVideoToolkit::SIZE_QVGA = 320 x 240
  1333. * PHPVideoToolkit::SIZE_VGA = 640 x 480
  1334. * PHPVideoToolkit::SIZE_SVGA = 800 x 600
  1335. * PHPVideoToolkit::SIZE_XGA = 1024 x 768
  1336. * PHPVideoToolkit::SIZE_UXGA = 1600 x 1200
  1337. * PHPVideoToolkit::SIZE_QXGA = 2048 x 1536
  1338. * PHPVideoToolkit::SIZE_SXGA = 1280 x 1024
  1339. * PHPVideoToolkit::SIZE_QSXGA = 2560 x 2048
  1340. * PHPVideoToolkit::SIZE_HSXGA = 5120 x 4096
  1341. * PHPVideoToolkit::SIZE_WVGA = 852 x 480
  1342. * PHPVideoToolkit::SIZE_WXGA = 1366 x 768
  1343. * PHPVideoToolkit::SIZE_WSXGA = 1600 x 1024
  1344. * PHPVideoToolkit::SIZE_WUXGA = 1920 x 1200
  1345. * PHPVideoToolkit::SIZE_WOXGA = 2560 x 1600
  1346. * PHPVideoToolkit::SIZE_WQSXGA = 3200 x 2048
  1347. * PHPVideoToolkit::SIZE_WQUXGA = 3840 x 2400
  1348. * PHPVideoToolkit::SIZE_WHSXGA = 6400 x 4096
  1349. * PHPVideoToolkit::SIZE_WHUXGA = 7680 x 4800
  1350. * PHPVideoToolkit::SIZE_CGA = 320 x 200
  1351. * PHPVideoToolkit::SIZE_EGA = 640 x 350
  1352. * PHPVideoToolkit::SIZE_HD480 = 852 x 480
  1353. * PHPVideoToolkit::SIZE_HD720 = 1280 x 720
  1354. * PHPVideoToolkit::SIZE_HD1080 = 1920 x 1080
  1355. * @param integer $height
  1356. * @return boolean
  1357. */
  1358. public function setVideoDimensions($width, $height=null)
  1359. {
  1360. if($height === null || $height === true)
  1361. {
  1362. // validate input
  1363. if(!in_array($width, array(self::SIZE_SAS, self::SIZE_SQCIF, self::SIZE_QCIF, self::SIZE_CIF, self::SIZE_4CIF, self::SIZE_QQVGA, self::SIZE_QVGA, self::SIZE_VGA, self::SIZE_SVGA, self::SIZE_XGA, self::SIZE_UXGA, self::SIZE_QXGA, self::SIZE_SXGA, self::SIZE_QSXGA, self::SIZE_HSXGA, self::SIZE_WVGA, self::SIZE_WXGA, self::SIZE_WSXGA, self::SIZE_WUXGA, self::SIZE_WOXGA, self::SIZE_WQSXGA, self::SIZE_WQUXGA, self::SIZE_WHSXGA, self::SIZE_WHUXGA, self::SIZE_CGA, self::SIZE_EGA, self::SIZE_HD480, self::SIZE_HD720, self::SIZE_HD1080)))
  1364. {
  1365. return $this->_raiseError('setVideoOutputDimensions_valid_format', array('format'=>$format));
  1366. //<- exits
  1367. }
  1368. if($width === self::SIZE_SAS)
  1369. {
  1370. // and override is made so no command is added in the hope that ffmpeg will just output the source
  1371. if($height === true)
  1372. {
  1373. return true;
  1374. }
  1375. // get the file info
  1376. $info = $this->getFileInfo();
  1377. if(!isset($info['video']) || !isset($info['video']['dimensions']))
  1378. {
  1379. return $this->_raiseError('setVideoOutputDimensions_sas_dim');
  1380. }
  1381. else
  1382. {
  1383. $width = $info['video']['dimensions']['width'].'x'.$info['video']['dimensions']['height'];
  1384. }
  1385. }
  1386. }
  1387. else
  1388. {
  1389. // check that the width and height are even
  1390. if($width % 2 !== 0 || $height % 2 !== 0)
  1391. {
  1392. return $this->_raiseError('setVideoOutputDimensions_valid_integer');
  1393. //<- exits
  1394. }
  1395. $width = $width.'x'.$height;
  1396. }
  1397. $this->addCommand('-s', $width);
  1398. return true;
  1399. }
  1400. /**
  1401. * Sets the video aspect ratio
  1402. *
  1403. * @access public
  1404. * @param string|integer $ratio Valid values are PHPVideoToolkit::RATIO_STANDARD, PHPVideoToolkit::RATIO_WIDE, PHPVideoToolkit::RATIO_CINEMATIC, or '4:3', '16:9', '1.85'
  1405. * @return boolean
  1406. */
  1407. public function setVideoAspectRatio($ratio)
  1408. {
  1409. if(!in_array($ratio, array(self::RATIO_STANDARD, self::RATIO_WIDE, self::RATIO_CINEMATIC)))
  1410. {
  1411. return $this->_raiseError('setVideoAspectRatio_valid_ratio', array('ratio'=>$ratio));
  1412. }
  1413. $this->addCommand('-aspect', $ratio);
  1414. return true;
  1415. }
  1416. /**
  1417. * Sets the frame rate of the video
  1418. *
  1419. * @access public
  1420. * @param string|integer $fps 1 being 1 frame per second, 1:2 being 0.5 frames per second
  1421. * @return boolean
  1422. */
  1423. public function setVideoFrameRate($fps)
  1424. {
  1425. return $this->addCommand('-r', $fps);
  1426. }
  1427. /**
  1428. * Extracts frames from a video.
  1429. * (Note; If set to 1 and the duration set by $extract_begin_timecode and $extract_end_timecode is equal to 1 you get more than one frame.
  1430. * For example if you set $extract_begin_timecode='00:00:00' and $extract_end_timecode='00:00:01' you might expect because the time span is
  1431. * 1 second only to get one frame if you set $frames_per_second=1. However this is not correct. The timecode you set in $extract_begin_timecode
  1432. * acts as the beginning frame. Thus in this example the first frame exported will be from the very beginning of the video, the video will
  1433. * then move onto the next frame and export a frame there. Therefore if you wish to export just one frame from one position in the video,
  1434. * say 1 second in you should set $extract_begin_timecode='00:00:01' and set $extract_end_timecode='00:00:01'.)
  1435. *
  1436. * @access public
  1437. * @param string $extract_begin_timecode A timecode (hh:mm:ss.fn - you can change the timecode format by changing the $timecode_format param
  1438. * it obeys the formatting of PHPVideoToolkit::formatTimecode(), see below for more info)
  1439. * @param string|integer|boolean $extract_end_timecode A timecode (hh:mm:ss.fn - you can change the timecode format by changing the $timecode_format param
  1440. * it obeys the formatting of PHPVideoToolkit::formatTimecode(), see below for more info), or false
  1441. * if all frames from the begin timecode are to be exported. (Boolean added by Matthias. Thanks. 12th March 2007)
  1442. * @param boolean|integer $frames_per_second The number of frames per second to extract. If left as default false, then the number of frames per second
  1443. * will be automagically gained from PHPVideoToolkit::fileGetInfo();
  1444. * @param boolean|integer $frame_limit Frame limiter. If set to false then all the frames will be exported from the given time codes, however
  1445. * if you wish to set a export limit to the number of frames that are exported you can set an integer. For example; if you set
  1446. * $extract_begin_timecode='00:00:11.01', $extract_end_timecode='00:01:10.01', $frames_per_second=1, you will get one frame for every second
  1447. * in the video between 00:00:11 and 00:01:10 (ie 60 frames), however if you ant to artificially limit this to exporting only ten frames
  1448. * then you set $frame_limit=10. You could of course alter the timecode to reflect you desired frame number, however there are situations
  1449. * when a shortcut such as this is useful and necessary.
  1450. * @param integer $timecode_format The format of the $extract_begin_timecode and $extract_end_timecode timecodes are being given in.
  1451. * default '%hh:%mm:%ss'
  1452. * - %hh (hours) representative of hours
  1453. * - %mm (minutes) representative of minutes
  1454. * - %ss (seconds) representative of seconds
  1455. * - %fn (frame number) representative of frames (of the current second, not total frames)
  1456. * - %ms (milliseconds) representative of milliseconds (of the current second, not total milliseconds) (rounded to 3 decimal places)
  1457. * - %ft (frames total) representative of total frames (ie frame number)
  1458. * - %st (seconds total) representative of total seconds (rounded).
  1459. * - %sf (seconds floored) representative of total seconds (floored).
  1460. * - %mt (milliseconds total) representative of total milliseconds. (rounded to 3 decimal places)
  1461. * Thus you could use an alternative, '%hh:%mm:%ss:%ms', or '%hh:%mm:%ss' dependent on your usage.
  1462. */
  1463. public function extractFrames($extract_begin_timecode, $extract_end_timecode, $frames_per_second=false, $frame_limit=false, $timecode_format='%hh:%mm:%ss.%fn', $check_frames_exist=true)
  1464. {
  1465. // are we autoguessing the frame rate?
  1466. if($frames_per_second === false || $check_frames_exist)
  1467. {
  1468. // get the file info, will exit if no input has been set
  1469. $info = $this->getFileInfo();
  1470. if(!isset($info['video']))
  1471. {
  1472. // the input has not returned any video data so the frame rate can not be guessed
  1473. return $this->_raiseError('extractFrame_video_frame_rate_404');
  1474. }
  1475. }
  1476. // check to see if we have to get the fps of the input movie
  1477. if($frames_per_second === false)
  1478. {
  1479. $frames_per_second = $info['video']['frame_rate'];
  1480. }
  1481. // check if frame exists
  1482. if($check_frames_exist)
  1483. {
  1484. if($info['video']['frame_count'] < $this->formatTimecode($extract_end_timecode, $timecode_format, '%ft', $frames_per_second))
  1485. {
  1486. // the input has not returned any video data so the frame rate can not be guessed
  1487. return $this->_raiseError('extractFrames_video_end_frame_count');
  1488. }
  1489. else if($info['video']['frame_count'] < $this->formatTimecode($extract_begin_timecode, $timecode_format, '%ft', $frames_per_second))
  1490. {
  1491. // the input has not returned any video data so the frame rate can not be guessed
  1492. return $this->_raiseError('extractFrames_video_begin_frame_count');
  1493. }
  1494. }
  1495. // disable audio output
  1496. $this->disableAudio();
  1497. // format the begin timecode if the timecode format is not already ok.
  1498. if($timecode_format !== '%hh:%mm:%ss.%ms')
  1499. {
  1500. $extract_begin_timecode = $this->formatTimecode($extract_begin_timecode, $timecode_format, '%hh:%mm:%ss.%ms', $frames_per_second);
  1501. }
  1502. $this->addCommand('-ss', $extract_begin_timecode);
  1503. // added by Matthias on 12th March 2007
  1504. // allows for exporting the entire timeline
  1505. if($extract_end_timecode !== false)
  1506. {
  1507. // format the end timecode if the timecode format is not already ok.
  1508. if($timecode_format !== '%hh:%mm:%ss.%ms')
  1509. {
  1510. $extract_end_timecode = $this->formatTimecode($extract_end_timecode, $timecode_format, '%hh:%mm:%ss.%ms', $frames_per_second);
  1511. }
  1512. $this->addCommand('-t', $extract_end_timecode);
  1513. }
  1514. // set the output frame rate
  1515. $this->setVideoFrameRate($frames_per_second);
  1516. // do we need to limit the number of frames outputted?
  1517. if($frame_limit !== false)
  1518. {
  1519. $this->addCommand('-vframes', $frame_limit);
  1520. }
  1521. $this->_image_output_timecode_start = $extract_begin_timecode;
  1522. $this->_image_output_timecode_fps = $frames_per_second;
  1523. }
  1524. /**
  1525. * Extracts exactly one frame
  1526. *
  1527. * @access public
  1528. * @uses $toolkit->extractFrames
  1529. * @param string $frame_timecode A timecode (hh:mm:ss.fn) where fn is the frame number of that second
  1530. * @param integer|boolean $frames_per_second The frame rate of the movie. If left as the default, false. We will use PHPVideoToolkit::getFileInfo() to get
  1531. * the actual frame rate. It is recommended that it is left as false because an incorrect frame rate may produce unexpected results.
  1532. * @param integer $timecode_format The format of the $extract_begin_timecode and $extract_end_timecode timecodes are being given in.
  1533. * default '%hh:%mm:%ss'
  1534. * - %hh (hours) representative of hours
  1535. * - %mm (minutes) representative of minutes
  1536. * - %ss (seconds) representative of seconds
  1537. * - %fn (frame number) representative of frames (of the current second, not total frames)
  1538. * - %ms (milliseconds) representative of milliseconds (of the current second, not total milliseconds) (rounded to 3 decimal places)
  1539. * - %ft (frames total) representative of total frames (ie frame number)
  1540. * - %st (seconds total) representative of total seconds (rounded).
  1541. * - %sf (seconds floored) representative of total seconds (floored).
  1542. * - %mt (milliseconds total) representative of total milliseconds. (rounded to 3 decimal places)
  1543. * Thus you could use an alternative, '%hh:%mm:%ss:%ms', or '%hh:%mm:%ss' dependent on your usage.
  1544. * @param boolean $check_frame_exists Makes an explicit check to see if the frame exists, default = true.
  1545. * Thanks to Istvan Szakacs for suggesting this check. Note, to improve performance disable this check.
  1546. */
  1547. public function extractFrame($frame_timecode, $frames_per_second=false, $frame_timecode_format='%hh:%mm:%ss.%fn', $check_frame_exists=true)
  1548. {
  1549. // get the file info, will exit if no input has been set
  1550. if($check_frame_exists || $frames_per_second === false)
  1551. {
  1552. $info = $this->getFileInfo();
  1553. if($info === false || !isset($info['video']))
  1554. {
  1555. // the input has not returned any video data so the frame rate can not be guessed
  1556. return $this->_raiseError('extractFrame_video_info_404');
  1557. }
  1558. }
  1559. // are we autoguessing the frame rate?
  1560. if($frames_per_second === false)
  1561. {
  1562. if(!isset($info['video']['frame_rate']))
  1563. {
  1564. // the input has not returned any video data so the frame rate can not be guessed
  1565. return $this->_raiseError('extractFrame_video_frame_rate_404');
  1566. }
  1567. $frames_per_second = $info['video']['frame_rate'];
  1568. }
  1569. // check if frame exists
  1570. if($check_frame_exists)
  1571. {
  1572. if($info['video']['frame_count'] < $this->formatTimecode($frame_timecode, $frame_timecode_format, '%ft', $frames_per_second))
  1573. {
  1574. // the input has not returned any video data so the frame rate can not be guessed
  1575. return $this->_raiseError('extractFrame_video_frame_count');
  1576. }
  1577. }
  1578. // format the frame details if the timecode format is not already ok.
  1579. if($frame_timecode_format !== '%hh:%mm:%ss.%ms')
  1580. {
  1581. $frame_timecode = $this->formatTimecode($frame_timecode, $frame_timecode_format, '%hh:%mm:%ss.%ms', $frames_per_second);
  1582. }
  1583. $this->_single_frame_extraction = 1;
  1584. // we will limit the number of frames produced so the desired frame is the last image
  1585. // this way we limit the cpu usage of ffmpeg
  1586. // Thanks to Istvan Szakacs for pointing out that ffmpeg can export frames using the -ss hh:mm:ss[.xxx]
  1587. // it has saved a lot of cpu intensive processes.
  1588. $this->extractFrames($frame_timecode, $frame_timecode, $frames_per_second, 1, '%hh:%mm:%ss.%ms', false);
  1589. // register the post tidy process
  1590. // $this->registerPostProcess('_extractFrameTidy', $this);
  1591. }
  1592. // /**
  1593. // * Tidies up after ffmpeg exports all frames from one second of video.
  1594. // *
  1595. // * @access public
  1596. // * @uses $toolkit->extractFrames
  1597. // * @param string $frame_timecode A timecode (hh:mm:ss.fn) where fn is the frame number of that second
  1598. // * @param integer|boolean $frames_per_second The frame rate of the movie. If left as the default, false. We will use PHPVideoToolkit::getFileInfo() to get
  1599. // * the actual frame rate. It is recommended that it is left as false because an incorrect frame rate may produce unexpected results.
  1600. // */
  1601. // private function _extractFrameTidy(&$files)
  1602. // {
  1603. // $frame_number = 1;
  1604. // $frame_file = array();
  1605. // // print_r($files);
  1606. // foreach($files as $file=>$filename)
  1607. // {
  1608. // // print_R(array($this->_single_frame_extraction, $frame_number));
  1609. // if($this->_single_frame_extraction == $frame_number)
  1610. // {
  1611. // // leave this file alone as it is the required frame
  1612. // $frame_file[$file] = $filename;
  1613. // }
  1614. // else
  1615. // {
  1616. // // add the frame to the unlink files list
  1617. // array_push($this->_unlink_files, $file);
  1618. // }
  1619. // $frame_number += 1;
  1620. // }
  1621. // // print_r($frame_file);
  1622. // // update the files list
  1623. // $files = $frame_file;
  1624. // return true;
  1625. // }
  1626. /**
  1627. * Adds a watermark to the outputted files. This effects both video and image output.
  1628. *
  1629. * @access public
  1630. * @param string $watermark_url The absolute path to the watermark image.
  1631. * @param string $vhook The absolute path to the ffmpeg vhook watermark library.
  1632. */
  1633. public function addWatermark($watermark_url, $vhook=PHPVIDEOTOOLKIT_FFMPEG_WATERMARK_VHOOK)
  1634. {
  1635. // check to see if the ffmpeg binary has support for vhooking
  1636. if(!$this->hasVHookSupport())
  1637. {
  1638. return $this->_raiseError('addWatermark_vhook_disabled');
  1639. }
  1640. // does the file exist?
  1641. if(!is_file($watermark_url))
  1642. {
  1643. return $this->_raiseError('addWatermark_img_404', array('watermark'=>$watermark_url));
  1644. }
  1645. $this->addCommand('-vhook', $vhook.' -f '.$watermark_url);
  1646. }
  1647. /**
  1648. * Adds a watermark to the outputted image files using the PHP GD module.
  1649. * This effects only image output.
  1650. *
  1651. * @access public
  1652. * @param string $watermark_url The absolute path to the watermark image.
  1653. */
  1654. public function addGDWatermark($watermark_url, $options=array('x-offset'=>0, 'y-offset'=>0, 'position'=>'bottom-right'))
  1655. {
  1656. // does the file exist?
  1657. if(!is_file($watermark_url))
  1658. {
  1659. return $this->_raiseError('addWatermark_img_404', array('watermark'=>$watermark_url));
  1660. }
  1661. // save the watermark_url
  1662. $this->_watermark_url = $watermark_url;
  1663. $this->_watermark_options = array_merge(array('x-offset'=>0, 'y-offset'=>0, 'position'=>'bottom-right'), $options);
  1664. // register the post process
  1665. $this->registerPostProcess('_addGDWatermark', $this);
  1666. }
  1667. /**
  1668. * Adds watermark to any outputted images via GD instead of using vhooking.
  1669. *
  1670. * @access private
  1671. * @param array $files An array of image files.
  1672. * @return array
  1673. */
  1674. private function _addGDWatermark($files)
  1675. {
  1676. // create the watermark resource and give it alpha blending
  1677. $info = pathinfo($this->_watermark_url);
  1678. switch(strtolower($info['extension']))
  1679. {
  1680. case 'jpeg' :
  1681. case 'jpg' :
  1682. $watermark = imagecreatefromjpeg($this->_watermark_url);
  1683. break;
  1684. case 'gif' :
  1685. $watermark = imagecreatefromgif($this->_watermark_url);
  1686. break;
  1687. case 'png' :
  1688. $watermark = imagecreatefrompng($this->_watermark_url);
  1689. break;
  1690. default :
  1691. return false;
  1692. }
  1693. imagealphablending($watermark, true);
  1694. imagesavealpha($watermark, true);
  1695. // get the watermark dimensions
  1696. $watermark_width = imagesx($watermark);
  1697. $watermark_height = imagesy($watermark);
  1698. // $image = imagecreatetruecolor($watermark_width, $watermark_height);
  1699. // loop and watermark each file
  1700. $blended_files = array();
  1701. foreach($files as $file=>$filename)
  1702. {
  1703. // detect the file extension and create the resource from them appropriate function
  1704. $info = pathinfo($file);
  1705. $quality = $output_function = null;
  1706. switch(strtolower($info['extension']))
  1707. {
  1708. case 'jpeg' :
  1709. case 'jpg' :
  1710. $quality = 80;
  1711. $output_function = 'imagejpeg';
  1712. $image = imagecreatefromjpeg($file);
  1713. break;
  1714. case 'gif' :
  1715. $output_function = 'imagegif';
  1716. $image = imagecreatefromgif($file);
  1717. break;
  1718. case 'png' :
  1719. $quality = 9;
  1720. $output_function = 'imagepng';
  1721. $image = imagecreatefrompng($file);
  1722. break;
  1723. default :
  1724. continue 1;
  1725. }
  1726. // the dimensions will/should be the same for each image however still best to check
  1727. $image_width = imagesx($image);
  1728. $image_height = imagesy($image);
  1729. // calculate where to position the watermark
  1730. $dest_x = 0;
  1731. $dest_y = 0;
  1732. switch($this->_watermark_options['position'])
  1733. {
  1734. case 'top-left' :
  1735. $dest_x = 0;
  1736. $dest_y = 0;
  1737. break;
  1738. case 'top-middle' :
  1739. $dest_x = ($image_width-$watermark_width)/2;
  1740. $dest_y = 0;
  1741. break;
  1742. case 'top-right' :
  1743. $dest_x = $image_width-$watermark_width;
  1744. $dest_y = 0;
  1745. break;
  1746. case 'center-left' :
  1747. $dest_x = 0;
  1748. $dest_y = ($image_height-$watermark_height)/2;
  1749. break;
  1750. case 'center-middle' :
  1751. $dest_x = ($image_width-$watermark_width)/2;
  1752. $dest_y = ($image_height-$watermark_height)/2;
  1753. break;
  1754. case 'center-right' :
  1755. $dest_x = $image_width-$watermark_width;
  1756. $dest_y = ($image_height-$watermark_height)/2;
  1757. break;
  1758. case 'bottom-left' :
  1759. $dest_x = 0;
  1760. $dest_y = $image_height - $watermark_height;
  1761. break;
  1762. case 'bottom-middle' :
  1763. $dest_x = ($image_width-$watermark_width)/2;
  1764. $dest_y = $image_height - $watermark_height;
  1765. break;
  1766. case 'bottom-right' :
  1767. default :
  1768. $dest_x = $image_width-$watermark_width;
  1769. $dest_y = $image_height - $watermark_height;
  1770. break;
  1771. }
  1772. $dest_x += $this->_watermark_options['x-offset'];
  1773. $dest_y += $this->_watermark_options['y-offset'];
  1774. // copy the watermark to the new image
  1775. // print_r(array($this->_watermark_url, $image, $watermark, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height));
  1776. imagecopy($image, $watermark, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height);
  1777. // delete the old image
  1778. unlink($file);
  1779. // save the new image in place of the old
  1780. $output_function($image, $file, $quality);
  1781. // remove the image resouce
  1782. imagedestroy($image);
  1783. array_push($blended_files, $file);
  1784. }
  1785. // remove the watermark resource
  1786. imagedestroy($watermark);
  1787. return $blended_files;
  1788. }
  1789. // /**
  1790. // * This will overlay an audio file over the top of a video file
  1791. // **/
  1792. // public function overlayAudio($audio_file)
  1793. // {
  1794. // $this->addCommand('-newaudio', '');
  1795. // }
  1796. /**
  1797. * This will adjust the audio volume.
  1798. *
  1799. * @access public
  1800. * @param integer $vol 256 = normal
  1801. **/
  1802. public function adjustVolume($vol=256)
  1803. {
  1804. $this->addCommand('-vol', '');
  1805. }
  1806. /**
  1807. * This process will combine the original input video with the video specified by this function.
  1808. * This function accepts more than one video as arguments. They will be added in order of the arguments.
  1809. * ie. input_video -> video1 -> video2 etc
  1810. * The process of doing this can take a long time as each incoming video has to be first converted
  1811. * into a format that accepts joining. The default joining codec is "mpg". However for almost lossless
  1812. * quality you can use the "yuv4mpegpipe" format. This is of course dependent upon your ffmpeg binary.
  1813. * You can check to see if you server supports yuv4mpegpipe by typing "ffmpeg -formats" into the
  1814. * command line. If you want to use the yuv4mpegpipe format you can add the flag, FFMPEG_USE_HQ_JOIN to the
  1815. * end of the video inputs. WARNING: High Quality joins will take longer to process. (well duh!)
  1816. *
  1817. * @access public
  1818. * @param $video1, $video2, $video3... $video(n) Paths of videos to attach to the input video.
  1819. * @param $flag integer FFMPEG_USE_HQ_JOIN If you wish to use the yuv4mpegpipe format for join add this to the end of the video list.
  1820. */
  1821. public function addVideo()
  1822. {
  1823. $videos = func_get_args();
  1824. $videos_length = count($videos);
  1825. // is last arg the hq join flag
  1826. // check to see if a starter file has been added, if not set the input as an array
  1827. if($this->_input_file === null)
  1828. {
  1829. $this->_input_file = array();
  1830. }
  1831. // if the input file is already set as a string that means as start file has been added so absorb into the input array
  1832. else if(is_string($this->_input_file))
  1833. {
  1834. $this->_input_file = array($this->_input_file);
  1835. }
  1836. foreach($videos as $key=>$file)
  1837. {
  1838. if(!preg_match('/\%([0-9]+)d/', $file) && strpos($file, '%d') === false && !is_file($file))
  1839. {
  1840. // input file not valid
  1841. return $this->_raiseError('addVideo_file_404', array('file'=>$file));
  1842. //<- exits
  1843. }
  1844. array_push($this->_input_file, $file);
  1845. // array_push($this->_input_file, escapeshellarg($file));
  1846. }
  1847. }
  1848. /**
  1849. * @access public
  1850. * @uses addVideo()
  1851. */
  1852. public function addVideos()
  1853. {
  1854. $videos = func_get_args();
  1855. call_user_func_array(array(&$this, 'addVideo'), $videos);
  1856. }
  1857. /**
  1858. * Sets the output.
  1859. *
  1860. * @access public
  1861. * @param string $output_directory The directory to output the command output to
  1862. * @param string $output_name The filename to output to.
  1863. * (Note; if you are outputting frames from a video then you will need to add an extra item to the output_name. The output name you set is required
  1864. * to contain '%d'. '%d' is replaced by the image number. Thus entering setting output_name $output_name='img%d.jpg' will output
  1865. * 'img1.jpg', 'img2.jpg', etc... However 'img%03d.jpg' generates `img001.jpg', `img002.jpg', etc...)
  1866. * @param boolean $overwrite_mode Accepts one of the following class constants
  1867. * - PHPVideoToolkit::OVERWRITE_FAIL - This produces an error if there is a file conflict and the processing is halted.
  1868. * - PHPVideoToolkit::OVERWRITE_PRESERVE - This continues with the processing but no file overwrite takes place. The processed file is left in the temp directory
  1869. * for you to manually move.
  1870. * - PHPVideoToolkit::OVERWRITE_EXISTING - This will replace any existing files with the freshly processed ones.
  1871. * - PHPVideoToolkit::OVERWRITE_UNIQUE - This will appended every output with a unique hash so that the filesystem is preserved.
  1872. * @return boolean false on error encountered, true otherwise
  1873. */
  1874. public function setOutput($output_directory, $output_name, $overwrite_mode=PHPVideoToolkit::OVERWRITE_FAIL)
  1875. {
  1876. // check if directoy exists
  1877. if(!is_dir($output_directory))
  1878. {
  1879. return $this->_raiseError('setOutput_output_dir_404', array('dir'=>$output_directory));
  1880. //<- exits
  1881. }
  1882. // check if directory is writeable
  1883. if(!is_writable($output_directory))
  1884. {
  1885. return $this->_raiseError('setOutput_output_dir_writable', array('dir'=>$output_directory));
  1886. //<- exits
  1887. }
  1888. $process_name = '';
  1889. // check to see if a output delimiter is set
  1890. $has_d = preg_match('/\%([0-9]+)d/', $output_name) || strpos($output_name, '%d') !== false;
  1891. if($has_d)
  1892. {
  1893. return $this->_raiseError('setOutput_%d_depreciated');
  1894. //<- exits
  1895. }
  1896. else
  1897. {
  1898. // determine if the extension is an image. If it is then we will be extracting frames so check for %d
  1899. $output_name_info = pathinfo($output_name);
  1900. $is_image = in_array(strtolower($output_name_info['extension']), array('jpg', 'jpeg', 'png'));
  1901. $is_gif = strtolower($output_name_info['extension']) === 'gif';
  1902. // NOTE: for now we'll just stick to the common image formats, SUBNOTE: gif is ignore because ffmpeg can create animated gifs
  1903. if($this->_single_frame_extraction !== null && strpos($output_name, '%timecode') !== false && !(preg_match('/\%index/', $output_name) || strpos($output_name, '%index') !== false) && $is_image)
  1904. {
  1905. return $this->_raiseError('setOutput_%_missing');
  1906. //<- exits
  1907. }
  1908. $process_name = '.'.$output_name_info['extension'];
  1909. // print_r(array($is_image, ($this->_single_frame_extraction !== null && $is_gif)));
  1910. if($is_image || ($this->_single_frame_extraction !== null && $is_gif))
  1911. {
  1912. $process_name = '-%12d'.$process_name;
  1913. }
  1914. }
  1915. // set the output address
  1916. $this->_output_address = $output_directory.$output_name;
  1917. // set the processing address in the temp folder so it does not conflict with any other conversions
  1918. $this->_process_address = $this->_tmp_directory.$this->unique().$process_name;
  1919. $this->_overwrite_mode = $overwrite_mode;
  1920. return true;
  1921. }
  1922. /**
  1923. * Sets a constant quality value to the encoding. (but a variable bitrate)
  1924. *
  1925. * @param integer $quality The quality to adhere to. 100 is highest quality, 1 is the lowest quality
  1926. */
  1927. public function setConstantQuality($quality)
  1928. {
  1929. // interpret quality into ffmpeg value
  1930. $quality = 31 - round(($quality/100) * 31);
  1931. if($quality > 31)
  1932. {
  1933. $quality = 31;
  1934. }
  1935. else if($quality < 1)
  1936. {
  1937. $quality = 1;
  1938. }
  1939. $this->addCommand('-qscale', $quality);
  1940. }
  1941. /**
  1942. * Translates a number of seconds to a timecode.
  1943. * NOTE: this is now a depreciated, use formatSeconds() instead.
  1944. *
  1945. * @depreciated Use formatSeconds() instead.
  1946. * @access public
  1947. * @uses PHPVideoToolkit::formatSeconds()
  1948. * @param integer $input_seconds The number of seconds you want to calculate the timecode for.
  1949. */
  1950. public function secondsToTimecode($input_seconds=0)
  1951. {
  1952. return $this->formatSeconds($input_seconds, '%hh:%mm:%ss');
  1953. }
  1954. /**
  1955. * Translates a timecode to the number of seconds.
  1956. * NOTE: this is now a depreciated, use formatTimecode() instead.
  1957. *
  1958. * @depreciated Use formatTimecode() instead.
  1959. * @access public
  1960. * @uses PHPVideoToolkit::formatTimecode()
  1961. * @param integer $input_seconds The number of seconds you want to calculate the timecode for.
  1962. */
  1963. public function timecodeToSeconds($input_timecode='00:00:00')
  1964. {
  1965. return $this->formatTimecode($input_timecode, '%hh:%mm:%ss', '%st');
  1966. }
  1967. /**
  1968. * Translates a number of seconds to a timecode.
  1969. *
  1970. * @access public
  1971. * @param integer $input_seconds The number of seconds you want to calculate the timecode for.
  1972. * @param integer $return_format The format of the timecode to return. The default is
  1973. * default '%hh:%mm:%ss'
  1974. * - %hh (hours) representative of hours
  1975. * - %mm (minutes) representative of minutes
  1976. * - %ss (seconds) representative of seconds
  1977. * - %fn (frame number) representative of frames (of the current second, not total frames)
  1978. * - %ms (milliseconds) representative of milliseconds (of the current second, not total milliseconds) (rounded to 3 decimal places)
  1979. * - %ft (frames total) representative of total frames (ie frame number)
  1980. * - %st (seconds total) representative of total seconds (rounded).
  1981. * - %sf (seconds floored) representative of total seconds (floored).
  1982. * - %sc (seconds ceiled) representative of total seconds (ceiled).
  1983. * - %mt (milliseconds total) representative of total milliseconds. (rounded to 3 decimal places)
  1984. * Thus you could use an alternative, '%hh:%mm:%ss:%ms', or '%hh:%mm:%ss' dependent on your usage.
  1985. * @param mixed|boolean|integer $frames_per_second The number of frames per second to translate for. If left false
  1986. * the class automagically gets the fps from PHPVideoToolkit::getFileInfo(), but the input has to be set
  1987. * first for this to work properly.
  1988. * @return string|integer Returns the timecode, but if $frames_per_second is not set and a frame rate lookup is required
  1989. * but can't be reached then -1 will be returned.
  1990. */
  1991. public function formatSeconds($input_seconds, $return_format='%hh:%mm:%ss', $frames_per_second=false)
  1992. {
  1993. $timestamp = mktime(0, 0, $input_seconds, 0, 0);
  1994. $floored = floor($input_seconds);
  1995. $hours = date('H', $timestamp);
  1996. $mins = date('i', $timestamp);
  1997. $searches = array();
  1998. $replacements = array();
  1999. // these ones are the simple replacements
  2000. // replace the hours
  2001. $using_hours = strpos($return_format, '%hh') !== false;
  2002. if($using_hours)
  2003. {
  2004. array_push($searches, '%hh');
  2005. array_push($replacements, $hours);
  2006. }
  2007. // replace the minutes
  2008. $using_mins = strpos($return_format, '%mm') !== false;
  2009. if($using_mins)
  2010. {
  2011. array_push($searches, '%mm');
  2012. // check if hours are being used, if not and hours are required enable smart minutes
  2013. if(!$using_hours && $hours > 0)
  2014. {
  2015. $value = ($hours * 60) + $mins;
  2016. }
  2017. else
  2018. {
  2019. $value = $mins;
  2020. }
  2021. array_push($replacements, $value);
  2022. }
  2023. // replace the seconds
  2024. if(strpos($return_format, '%ss') !== false)
  2025. {
  2026. // check if hours are being used, if not and hours are required enable smart minutes
  2027. if(!$using_mins && !$using_hours && $hours > 0)
  2028. {
  2029. $mins = ($hours * 60) + $mins;
  2030. }
  2031. // check if mins are being used, if not and hours are required enable smart minutes
  2032. if(!$using_mins && $mins > 0)
  2033. {
  2034. $value = ($mins * 60) + date('s', $timestamp);
  2035. }
  2036. else
  2037. {
  2038. $value = date('s', $timestamp);
  2039. }
  2040. array_push($searches, '%ss');
  2041. array_push($replacements, $value);
  2042. }
  2043. // replace the milliseconds
  2044. if(strpos($return_format, '%ms') !== false)
  2045. {
  2046. $milli = round($input_seconds - $floored, 3);
  2047. $milli = substr($milli, 2);
  2048. $milli = empty($milli) ? '0' : $milli;
  2049. array_push($searches, '%ms');
  2050. array_push($replacements, $milli);
  2051. }
  2052. // replace the total seconds (rounded)
  2053. if(strpos($return_format, '%st') !== false)
  2054. {
  2055. array_push($searches, '%st');
  2056. array_push($replacements, round($input_seconds));
  2057. }
  2058. // replace the total seconds (floored)
  2059. if(strpos($return_format, '%sf') !== false)
  2060. {
  2061. array_push($searches, '%sf');
  2062. array_push($replacements, floor($input_seconds));
  2063. }
  2064. // replace the total seconds (ceiled)
  2065. if(strpos($return_format, '%sc') !== false)
  2066. {
  2067. array_push($searches, '%sc');
  2068. array_push($replacements, ceil($input_seconds));
  2069. }
  2070. // replace the total seconds
  2071. if(strpos($return_format, '%mt') !== false)
  2072. {
  2073. array_push($searches, '%mt');
  2074. array_push($replacements, round($input_seconds, 3));
  2075. }
  2076. // these are the more complicated as they depend on $frames_per_second / frames per second of the current input
  2077. $has_frames = strpos($return_format, '%fn') !== false;
  2078. $has_total_frames = strpos($return_format, '%ft') !== false;
  2079. if($has_frames || $has_total_frames)
  2080. {
  2081. // if the fps is false then we must automagically detect it from the input file
  2082. if($frames_per_second === false)
  2083. {
  2084. $info = $this->getFileInfo();
  2085. // check the information has been received
  2086. if($info === false || (!isset($info['video']) || !isset($info['video']['frame_rate'])))
  2087. {
  2088. // fps cannot be reached so return -1
  2089. return -1;
  2090. }
  2091. $frames_per_second = $info['video']['frame_rate'];
  2092. }
  2093. // replace the frames
  2094. $excess_frames = false;
  2095. if($has_frames)
  2096. {
  2097. $excess_frames = ceil(($input_seconds - $floored) * $frames_per_second);
  2098. // print_r(array($input_seconds, $excess_frames));
  2099. array_push($searches, '%fn');
  2100. array_push($replacements, $excess_frames);
  2101. }
  2102. // replace the total frames (ie frame number)
  2103. if($has_total_frames)
  2104. {
  2105. $round_frames = $floored * $frames_per_second;
  2106. if(!$excess_frames)
  2107. {
  2108. $excess_frames = ceil(($input_seconds - $floored) * $frames_per_second);
  2109. }
  2110. array_push($searches, '%ft');
  2111. array_push($replacements, $round_frames + $excess_frames);
  2112. }
  2113. }
  2114. // print_r(array($searches, $replacements, $return_format));
  2115. // print_r(array($input_seconds, $timestamp, $return_format, str_replace($searches, $replacements, $return_format)));
  2116. return str_replace($searches, $replacements, $return_format);
  2117. }
  2118. /**
  2119. * Translates a timecode to the number of seconds
  2120. *
  2121. * @access public
  2122. * @param integer $input_seconds The number of seconds you want to calculate the timecode for.
  2123. * @param integer $input_format The format of the timecode is being given in.
  2124. * default '%hh:%mm:%ss'
  2125. * - %hh (hours) representative of hours
  2126. * - %mm (minutes) representative of minutes
  2127. * - %ss (seconds) representative of seconds
  2128. * - %fn (frame number) representative of frames (of the current second, not total frames)
  2129. * - %ms (milliseconds) representative of milliseconds (of the current second, not total milliseconds) (rounded to 3 decimal places)
  2130. * - %ft (frames total) representative of total frames (ie frame number)
  2131. * - %st (seconds total) representative of total seconds (rounded).
  2132. * - %sf (seconds floored) representative of total seconds (floored).
  2133. * - %sc (seconds ceiled) representative of total seconds (ceiled).
  2134. * - %mt (milliseconds total) representative of total milliseconds. (rounded to 3 decimal places)
  2135. * Thus you could use an alternative, '%hh:%mm:%ss:%ms', or '%hh:%mm:%ss' dependent on your usage.
  2136. * @param integer $return_format The format of the timecode to return. The default is
  2137. * default '%ts'
  2138. * - %hh (hours) representative of hours
  2139. * - %mm (minutes) representative of minutes
  2140. * - %ss (seconds) representative of seconds
  2141. * - %fn (frame number) representative of frames (of the current second, not total frames)
  2142. * - %ms (milliseconds) representative of milliseconds (of the current second, not total milliseconds) (rounded to 3 decimal places)
  2143. * - %ft (frames total) representative of total frames (ie frame number)
  2144. * - %st (seconds total) representative of total seconds (rounded).
  2145. * - %sf (seconds floored) representative of total seconds (floored).
  2146. * - %sc (seconds ceiled) representative of total seconds (ceiled).
  2147. * - %mt (milliseconds total) representative of total milliseconds. (rounded to 3 decimal places)
  2148. * Thus you could use an alternative, '%hh:%mm:%ss:%ms', or '%hh:%mm:%ss' dependent on your usage.
  2149. * @param mixed|boolean|integer $frames_per_second The number of frames per second to translate for. If left false
  2150. * the class automagically gets the fps from PHPVideoToolkit::getFileInfo(), but the input has to be set
  2151. * first for this to work properly.
  2152. * @return float Returns the value of the timecode in seconds.
  2153. */
  2154. public function formatTimecode($input_timecode, $input_format='%hh:%mm:%ss', $return_format='%ts', $frames_per_second=false)
  2155. {
  2156. // first we must get the timecode into the current seconds
  2157. $input_quoted = preg_quote($input_format);
  2158. $placeholders = array('%hh', '%mm', '%ss', '%fn', '%ms', '%ft', '%st', '%sf', '%sc', '%mt');
  2159. $seconds = 0;
  2160. $input_regex = str_replace($placeholders, '([0-9]+)', preg_quote($input_format));
  2161. preg_match('/'.$input_regex.'/', $input_timecode, $matches);
  2162. // work out the sort order for the placeholders
  2163. $sort_table = array();
  2164. foreach($placeholders as $key=>$placeholder)
  2165. {
  2166. if(($pos = strpos($input_format, $placeholder)) !== false)
  2167. {
  2168. $sort_table[$pos] = $placeholder;
  2169. }
  2170. }
  2171. ksort($sort_table);
  2172. // check to see if frame related values are in the input
  2173. $has_frames = strpos($input_format, '%fn') !== false;
  2174. $has_total_frames = strpos($input_format, '%ft') !== false;
  2175. if($has_frames || $has_total_frames)
  2176. {
  2177. // if the fps is false then we must automagically detect it from the input file
  2178. if($frames_per_second === false)
  2179. {
  2180. $info = $this->getFileInfo();
  2181. // check the information has been received
  2182. if($info === false || (!isset($info['video']) || !isset($info['video']['frame_rate'])))
  2183. {
  2184. // fps cannot be reached so return -1
  2185. return -1;
  2186. }
  2187. $frames_per_second = $info['video']['frame_rate'];
  2188. }
  2189. }
  2190. // increment the seconds with each placeholder value
  2191. $key = 1;
  2192. foreach($sort_table as $placeholder)
  2193. {
  2194. if(!isset($matches[$key]))
  2195. {
  2196. break;
  2197. }
  2198. $value = $matches[$key];
  2199. switch($placeholder)
  2200. {
  2201. // time related ones
  2202. case '%hh' :
  2203. $seconds += $value * 3600;
  2204. break;
  2205. case '%mm' :
  2206. $seconds += $value * 60;
  2207. break;
  2208. case '%ss' :
  2209. case '%sf' :
  2210. case '%sc' :
  2211. $seconds += $value;
  2212. break;
  2213. case '%ms' :
  2214. $seconds += floatval('0.'.$value);
  2215. break;
  2216. case '%st' :
  2217. case '%mt' :
  2218. $seconds = $value;
  2219. break 1;
  2220. break;
  2221. // frame related ones
  2222. case '%fn' :
  2223. $seconds += $value/$frames_per_second;
  2224. break;
  2225. case '%ft' :
  2226. $seconds = $value/$frames_per_second;
  2227. break 1;
  2228. break;
  2229. }
  2230. $key += 1;
  2231. }
  2232. // then we just format the seconds
  2233. return $this->formatSeconds($seconds, $return_format, $frames_per_second);
  2234. }
  2235. /**
  2236. * This is a private function that joins multiple input sources into one source before
  2237. * the final processing takes place. All videos are temporarily converted into mpg for
  2238. * joining.
  2239. *
  2240. * PLEASE NOTE. This process is experimental an might not work on all systems.
  2241. *
  2242. * @access private
  2243. * @param boolean $log
  2244. */
  2245. private function _joinInput($log)
  2246. {
  2247. die('INPUT CANNOT YET BE JOINED.');
  2248. // ---- ffmpeg works
  2249. /*
  2250. mkfifo /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/intermediate1.mpg
  2251. mkfifo /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/intermediate2.mpg
  2252. ffmpeg -i /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/MOV02820.MPG -sameq -y /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/intermediate1.mpg < /dev/null &
  2253. ffmpeg -i /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/MOV02832.MPG -sameq -y /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/intermediate2.mpg < /dev/null &
  2254. cat /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/intermediate1.mpg /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/intermediate2.mpg |\
  2255. ffmpeg -f mpeg -i - -sameq -vcodec flv -acodec mp3 -ar 22050 /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/output.flv
  2256. */
  2257. // ---- mencoder works
  2258. /*
  2259. PHPVIDEOTOOLKIT_MENCODER_BINARY.' -oac copy -ovc copy -idx -o '.$temp_file.' '.implode(' ', $this->_input_file);
  2260. */
  2261. // run a libmp3lame check as it require different mp3 codec
  2262. $audio_codec = 'mp3';
  2263. $info = $this->getFFmpegInfo();
  2264. if(isset($info['compiler']['configuration']) && in_array('--enable-libmp3lame', $info['compiler']['configuration']))
  2265. {
  2266. // $audio_codec = 'liblamemp3';
  2267. $audio_codec = 'libmp3lame';
  2268. }
  2269. // build commands
  2270. $temp_files = array();
  2271. $mkinfo_commands = array();
  2272. $ffmpeg_commands = array();
  2273. $cat_files = array();
  2274. $unique = $this->unique();
  2275. foreach($this->_input_file as $key=>$file)
  2276. {
  2277. $unique_name = $this->_tmp_directory.$unique.'-'.$key.'-temp.mpg';
  2278. $unique_name_escaped = escapeshellarg($unique_name);
  2279. $logfile1 = $this->_tmp_directory.$unique.'-'.$key.'-log1.log';
  2280. $logfile2 = $this->_tmp_directory.$unique.'-'.$key.'-log2.log';
  2281. array_push($mkinfo_commands, array('cmd'=> 'mkfifo '.$unique_name_escaped.($log ? ' &> '.$logfile1 : ''), 'logfile'=>$logfile1));
  2282. array_push($ffmpeg_commands, array('cmd'=> PHPVIDEOTOOLKIT_FFMPEG_BINARY.' -i '.escapeshellarg($file).' -acodec '.$audio_codec.' -sameq '.$unique_name_escaped.' < /dev/null '.($log ? '&> '.$logfile2 : '&'), 'logfile'=>$logfile2));
  2283. array_push($cat_files, $unique_name_escaped);
  2284. // array_push($this->_unlink_files, $unique_name);
  2285. if($log)
  2286. {
  2287. // array_push($this->_unlink_files, $logfile1);
  2288. // array_push($this->_unlink_files, $logfile2);
  2289. }
  2290. }
  2291. // start log
  2292. if($log)
  2293. {
  2294. $log_lines = array();
  2295. array_unshift($log_lines, $this->_getMessage('ffmpeg_log_separator'), $this->_getMessage('ffmpeg_log_ffmpeg_join_gunk'), $this->_getMessage('ffmpeg_log_separator'));
  2296. }
  2297. // mkinfo for temp files
  2298. foreach($mkinfo_commands as $cmd)
  2299. {
  2300. // exec($cmd['cmd']);
  2301. echo($cmd['cmd']."\r\n");
  2302. if($log)
  2303. {
  2304. array_push($log_lines, '---------', trim(file_get_contents($cmd['logfile'])));
  2305. }
  2306. }
  2307. // extract data
  2308. foreach($ffmpeg_commands as $cmd)
  2309. {
  2310. // exec($cmd['cmd']);
  2311. echo($cmd['cmd']."\r\n");
  2312. if($log)
  2313. {
  2314. array_push($log_lines, trim(file_get_contents($cmd['logfile'])), '---------');
  2315. }
  2316. }
  2317. // join command
  2318. $unique = $this->unique();
  2319. $temp_join_file = $this->_tmp_directory.$unique.'-combined-joined.mpg';
  2320. $temp_join_file_escaped = escapeshellarg($temp_join_file);
  2321. $temp_process_file = $this->_tmp_directory.$unique.'-combined-temp.mpg';
  2322. $temp_process_file_escaped = escapeshellarg($temp_process_file);
  2323. $logfile = $this->_tmp_directory.$unique.'.log';
  2324. // command for use with cat mkinfo files
  2325. // exec('cat '.implode(' ', $cat_files).' |\
  2326. // '.PHPVIDEOTOOLKIT_FFMPEG_BINARY.' -f mpeg -i - -sameq -vcodec mpeg4 -acodec '.$audio_codec.' '.escapeshellarg($temp_process_file).($log ? ' &> '.$logfile : ''));
  2327. echo('cat '.implode(' ', $cat_files).' |\
  2328. '.PHPVIDEOTOOLKIT_FFMPEG_BINARY.' -f mpeg -i - -sameq -vcodec mpeg4 -acodec '.$audio_codec.' '.escapeshellarg($temp_process_file).($log ? ' &> '.$logfile : '')."\r\n");
  2329. // echo('cat '.implode(' ', $cat_files).' > '.$temp_join_file_escaped.'
  2330. // '.PHPVIDEOTOOLKIT_FFMPEG_BINARY.' -i '.$temp_join_file_escaped.' -sameq -vcodec mpeg4 -acodec '.$audio_codec.' '.$temp_process_file_escaped.($log ? ' &> '.$logfile : ''));
  2331. // exec('cat '.implode(' ', $cat_files).' > '.$temp_join_file_escaped.'
  2332. // '.PHPVIDEOTOOLKIT_FFMPEG_BINARY.' -i '.$temp_join_file_escaped.' -sameq -vcodec mpeg4 -acodec '.$audio_codec.' '.$temp_process_file_escaped.($log ? ' &> '.$logfile : ''));
  2333. if($log)
  2334. {
  2335. array_push($log_lines, trim(file_get_contents($logfile)));
  2336. array_push($this->_unlink_files, $logfile);
  2337. $this->_addToLog($log_lines, 'r+');
  2338. print_r($log_lines);
  2339. }
  2340. // create a temp dir in the temp dir
  2341. // $temp_file = $this->_tmp_directory.$this->unique().'.'.array_pop(explode('.', $this->_process_address));
  2342. // print_r($temp_file);
  2343. $this->addCommand('-i', $temp_process_file);
  2344. // array_push($this->_unlink_files, $temp_process_file);
  2345. exit;
  2346. }
  2347. public function canCodecBeEncoded()
  2348. {
  2349. }
  2350. public function canCodecBeDecoded()
  2351. {
  2352. }
  2353. public function validateCodec($codec, $method)
  2354. {
  2355. }
  2356. /**
  2357. * Commits all the commands and executes the ffmpeg procedure. This will also attempt to validate any outputted files in order to provide
  2358. * some level of stop and check system.
  2359. *
  2360. * @access public
  2361. * @param $multi_pass_encode boolean Determines if multi (2) pass encoding should be used.
  2362. * @param $log boolean Determines if a log file of the results should be generated.
  2363. * @return mixed
  2364. * - false On error encountered.
  2365. * - PHPVideoToolkit::RESULT_OK (bool true) If the file has successfully been processed and moved ok to the output address
  2366. * - PHPVideoToolkit::RESULT_OK_BUT_UNWRITABLE (int -1) If the file has successfully been processed but was not able to be moved correctly to the output address
  2367. * If this is the case you will manually need to move the processed file from the temp directory. You can
  2368. * get around this by settings the third argument from PHPVideoToolkit::setOutput(), $overwrite to true.
  2369. * - n (int) A positive integer is only returned when outputting a series of frame grabs from a movie. It dictates
  2370. * the total number of frames grabbed from the input video. You should also not however, that if a conflict exists
  2371. * with one of the filenames then this return value will not be returned, but PHPVideoToolkit::RESULT_OK_BUT_UNWRITABLE
  2372. * will be returned instead.
  2373. * Because of the mixed return value you should always go a strict evaluation of the returned value. ie
  2374. *
  2375. * $result = $toolkit->excecute();
  2376. * if($result === false)
  2377. * {
  2378. * // error
  2379. * }
  2380. * else if($result === PHPVideoToolkit::RESULT_OK_BUT_UNWRITABLE)
  2381. * {
  2382. * // ok but a manual move is required. The file to move can be it can be retrieved by $toolkit->getLastOutput();
  2383. * }
  2384. * else if($result === PHPVideoToolkit::RESULT_OK)
  2385. * {
  2386. * // everything is ok.
  2387. * }
  2388. */
  2389. public function execute($multi_pass_encode=false, $log=false)
  2390. {
  2391. // check for inut and output params
  2392. $has_placeholder = preg_match('/\%([0-9]+)index/', $this->_process_address) || (strpos($this->_process_address, '%index') === false && strpos($this->_process_address, '%timecode') === false);
  2393. if($this->_input_file === null && !$has_placeholder)
  2394. {
  2395. return $this->_raiseError('execute_input_404');
  2396. //<- exits
  2397. }
  2398. // check to see if the output address has been set
  2399. if($this->_process_address === null)
  2400. {
  2401. return $this->_raiseError('execute_output_not_set');
  2402. //<- exits
  2403. }
  2404. if(($this->_overwrite_mode == self::OVERWRITE_PRESERVE || $this->_overwrite_mode == self::OVERWRITE_FAIL) && is_file($this->_process_address))
  2405. {
  2406. return $this->_raiseError('execute_overwrite_process');
  2407. //<- exits
  2408. }
  2409. // carry out some overwrite checks if required
  2410. $overwrite = '';
  2411. switch($this->_overwrite_mode)
  2412. {
  2413. case self::OVERWRITE_UNIQUE :
  2414. // insert a unique id into the output address (the process address already has one)
  2415. $unique = $this->unique();
  2416. $last_index = strrpos($this->_output_address, DIRECTORY_SEPARATOR);
  2417. $this->_output_address = substr($this->_output_address, 0, $last_index+1).$unique.'-'.substr($this->_output_address, $last_index+1);
  2418. break;
  2419. case self::OVERWRITE_EXISTING :
  2420. // add an overwrite command to ffmpeg execution call
  2421. $overwrite = '-y ';
  2422. break;
  2423. case self::OVERWRITE_PRESERVE :
  2424. // do nothing as the preservation comes later
  2425. break;
  2426. case self::OVERWRITE_FAIL :
  2427. default :
  2428. // if the file should fail
  2429. if(!$has_placeholder && is_file($this->_output_address))
  2430. {
  2431. return $this->_raiseError('execute_overwrite_fail');
  2432. //<- exits
  2433. }
  2434. break;
  2435. }
  2436. $this->_timer_start = self::microtimeFloat();
  2437. // we have multiple inputs that require joining so convert them to a joinable format and join
  2438. if(is_array($this->_input_file))
  2439. {
  2440. $this->_joinInput($log);
  2441. }
  2442. // add the input file command to the mix
  2443. $this->addCommand('-i', $this->_input_file);
  2444. // if multi pass encoding is enabled add the commands and logfile
  2445. if($multi_pass_encode)
  2446. {
  2447. $multi_pass_file = $this->_tmp_directory.$this->unique().'-multipass';
  2448. $this->addCommand('-pass', 1);
  2449. $this->addCommand('-passlogfile', $multi_pass_file);
  2450. }
  2451. // combine all the output commands
  2452. $command_string = $this->_combineCommands();
  2453. // prepare the command suitable for exec
  2454. // the input and overwrite commands have specific places to be set so they have to be added outside of the combineCommands function
  2455. $exec_string = $this->_prepareCommand(PHPVIDEOTOOLKIT_FFMPEG_BINARY, $command_string, $overwrite.escapeshellcmd($this->_process_address));
  2456. // $exec_string = $this->_prepareCommand(PHPVIDEOTOOLKIT_FFMPEG_BINARY, '-i '.$this->_commands['-i'].' '.$command_string, $overwrite.escapeshellcmd($this->_process_address));
  2457. if($log)
  2458. {
  2459. $this->_log_file = $this->_tmp_directory.$this->unique().'.info';
  2460. array_push($this->_unlink_files, $this->_log_file);
  2461. $exec_string = $exec_string.' &> '.$this->_log_file;
  2462. }
  2463. // execute the command
  2464. exec($exec_string);
  2465. // track the processed command by adding it to the class
  2466. array_unshift($this->_processed, $exec_string);
  2467. // create the multiple pass encode
  2468. if($multi_pass_encode)
  2469. {
  2470. $pass2_exc_string = str_replace('-pass '.escapeshellarg(1), '-pass '.escapeshellarg(2), $exec_string);
  2471. exec($pass2_exc_string);
  2472. $this->_processed[0] = array($this->_processed[0], $pass2_exc_string);
  2473. // remove the multipass log file
  2474. unlink($multi_pass_file.'-0.log');
  2475. }
  2476. // keep track of the time taken
  2477. $execution_time = self::microtimeFloat() - $this->_timer_start;
  2478. array_unshift($this->_timers, $execution_time);
  2479. // add the exec string to the log file
  2480. if($log)
  2481. {
  2482. $lines = $this->_processed[0];
  2483. if(!is_array($lines))
  2484. {
  2485. $lines = array($lines);
  2486. }
  2487. array_unshift($lines, $this->_getMessage('ffmpeg_log_separator'), $this->_getMessage('ffmpeg_log_ffmpeg_command'), $this->_getMessage('ffmpeg_log_separator'));
  2488. array_unshift($lines, $this->_getMessage('ffmpeg_log_separator'), $this->_getMessage('ffmpeg_log_ffmpeg_gunk'), $this->_getMessage('ffmpeg_log_separator'));
  2489. $this->_addToLog($lines, 'r+');
  2490. }
  2491. // must validate a series of outputed items
  2492. // detect if the output address is a sequence output
  2493. if(preg_match('/\%([0-9]+)d/', $this->_process_address, $d_matches) || strpos($this->_process_address, '%d') !== false)
  2494. {
  2495. // get the path details
  2496. $process_info = pathinfo($this->_process_address);
  2497. $output_info = pathinfo($this->_output_address);
  2498. $pad_amount = intval($d_matches[1]);
  2499. // print_r(array($process_info, $output_info));
  2500. // get the %index padd amounts
  2501. $has_preg_index = preg_match('/\%([0-9]+)index/', $output_info['basename'], $index_matches);
  2502. $output_index_pad_amount = isset($index_matches[1]) ? intval($index_matches[1], 1) : 0;
  2503. // var_dump($index_matches);
  2504. // init the iteration values
  2505. $num = 1;
  2506. $files = array();
  2507. $produced = array();
  2508. $error = false;
  2509. $name_conflict = false;
  2510. $file_exists = false;
  2511. // get the first files name
  2512. $filename = $process_info['dirname'].DIRECTORY_SEPARATOR.str_replace($d_matches[0], str_pad($num, $pad_amount, '0', STR_PAD_LEFT), $process_info['basename']);
  2513. $use_timecode = strpos($output_info['basename'], '%timecode') !== false;
  2514. $use_index = $has_preg_index || strpos($output_info['basename'], '%index') !== false;
  2515. // if(!$use_timecode && $use_index)
  2516. // {
  2517. // if($log)
  2518. // {
  2519. // $this->_logResult('execute_overwrite_fail');
  2520. // }
  2521. // return $this->_raiseError('execute_overwrite_fail');
  2522. // }
  2523. // start the timecode pattern replacement values
  2524. if($use_timecode)
  2525. {
  2526. $secs_start = $this->formatTimecode($this->_image_output_timecode_start, '%hh:%mm:%ss.%ms', '%mt', $this->_image_output_timecode_fps);
  2527. $fps_inc = 1/$this->_image_output_timecode_fps;
  2528. $fps_current_sec = 0;
  2529. $fps_current_frame = 0;
  2530. }
  2531. // loop checking for file existence
  2532. while(@is_file($filename))
  2533. {
  2534. // check for empty file
  2535. $size = filesize($filename);
  2536. if($size == 0)
  2537. {
  2538. $error = true;
  2539. }
  2540. array_push($produced, $filename);
  2541. // create the substitution arrays
  2542. $searches = array();
  2543. $replacements = array();
  2544. if($use_index)
  2545. {
  2546. array_push($searches, isset($index_matches[0]) ? $index_matches[0] : '%index');
  2547. array_push($replacements, str_pad($num, $output_index_pad_amount, '0', STR_PAD_LEFT));
  2548. }
  2549. // check if timecode is in the output name, no need to use it if not
  2550. if($use_timecode)
  2551. {
  2552. $fps_current_sec += $fps_inc;
  2553. $fps_current_frame += 1;
  2554. if($fps_current_sec >= 1)
  2555. {
  2556. $fps_current_sec = $fps_inc;
  2557. $secs_start += 1;
  2558. $fps_current_frame = 1;
  2559. }
  2560. $timecode = $this->formatSeconds($secs_start, $this->image_output_timecode_format, $this->_image_output_timecode_fps);
  2561. $timecode = str_replace(array(':', '.'), $this->timecode_seperator_output, $timecode);
  2562. // add to the substitution array
  2563. array_push($searches, '%timecode');
  2564. array_push($replacements, $timecode);
  2565. }
  2566. // check if the file exists already and if it does check that it can be overriden
  2567. $old_filename = $filename;
  2568. // print_r(array($searches, $replacements, $output_info['basename']));
  2569. $new_file = str_replace($searches, $replacements, $output_info['basename']);
  2570. $new_filename = $output_info['dirname'].DIRECTORY_SEPARATOR.$new_file;
  2571. // var_dump($filename, $new_filename);
  2572. if(!is_file($new_filename) || $this->_overwrite_mode == self::OVERWRITE_EXISTING)
  2573. {
  2574. rename($filename, $new_filename);
  2575. $filename = $new_filename;
  2576. }
  2577. // the file exists and is not allowed to be overriden so just rename in the temp directory using the timecode
  2578. else if($this->_overwrite_mode == self::OVERWRITE_PRESERVE)
  2579. {
  2580. $new_filename = $process_info['dirname'].DIRECTORY_SEPARATOR.'tbm-'.$this->unique().'-'.$new_file;
  2581. rename($filename, $new_filename);
  2582. $filename = $new_filename;
  2583. // add the error to the log file
  2584. if($log)
  2585. {
  2586. $this->_logResult('execute_image_file_exists', array('file'=>$new_filename));
  2587. }
  2588. // flag the conflict
  2589. $file_exists = true;
  2590. }
  2591. // the file exists so the process must fail
  2592. else
  2593. {
  2594. // add the error to the log file
  2595. if($log)
  2596. {
  2597. $this->_logResult('execute_overwrite_fail');
  2598. }
  2599. // tidy up the produced files
  2600. array_merge($this->_unlink_files, $produced);
  2601. return $this->_raiseError('execute_overwrite_fail');
  2602. }
  2603. // process the name change if the %d is to be replaced with the timecode
  2604. $num += 1;
  2605. $files[$filename] = $size > 0 ? basename($filename) : false;
  2606. // print_r("\r\n\r\n".is_file($old_filename)." - ".$old_filename.' => '.$new_filename);
  2607. // print_r($files);
  2608. // get the next incremented filename to check for existance
  2609. $filename = $process_info['dirname'].DIRECTORY_SEPARATOR.str_replace($d_matches[0], str_pad($num, $pad_amount, '0', STR_PAD_LEFT), $process_info['basename']);
  2610. }
  2611. // de-increment the last num as it wasn't found
  2612. $num -= 1;
  2613. // if the file was detected but were empty then display a different error
  2614. if($error === true)
  2615. {
  2616. // add the error to the log file
  2617. if($log)
  2618. {
  2619. $this->_logResult('execute_partial_error', array('input'=>$this->_input_file));
  2620. }
  2621. return $this->_raiseError('execute_partial_error', array('input'=>$this->_input_file));
  2622. //<- exits
  2623. }
  2624. // post process any files
  2625. // print_r($files);
  2626. $post_process_result = $this->_postProcess($log, $files);
  2627. // print_r($files);
  2628. if(is_array($post_process_result))
  2629. {
  2630. // post process has occurred and everything is fine
  2631. $num = count($files);
  2632. }
  2633. else if($post_process_result !== false)
  2634. {
  2635. // the file has encountered an error in the post processing of the files
  2636. return $post_process_result;
  2637. }
  2638. // var_dump("\r\n\r\n", $files, __LINE__, __FILE__, "\r\n\r\n");
  2639. // exit;
  2640. // if the result is false then no post process has taken place
  2641. $this->_process_file_count = $num;
  2642. // no files were generated in this sequence
  2643. if($num == 0)
  2644. {
  2645. // add the error to the log file
  2646. if($log)
  2647. {
  2648. $this->_logResult('execute_image_error', array('input'=>$this->_input_file));
  2649. }
  2650. return $this->_raiseError('execute_image_error', array('input'=>$this->_input_file));
  2651. //<- exits
  2652. }
  2653. // add the files the the class a record of what has been generated
  2654. array_unshift($this->_files, $files);
  2655. array_push($lines, $this->_getMessage('ffmpeg_log_separator'), $this->_getMessage('ffmpeg_log_ffmpeg_output'), $this->_getMessage('ffmpeg_log_separator'), implode("\n", $files));
  2656. $this->_addToLog($lines, 'r+');
  2657. return $file_exists ? self::RESULT_OK_BUT_UNWRITABLE : self::RESULT_OK;
  2658. }
  2659. // must validate one file
  2660. else
  2661. {
  2662. // check that it is a file
  2663. if(!is_file($this->_process_address))
  2664. {
  2665. // add the error to the log file
  2666. if($log)
  2667. {
  2668. $this->_logResult('execute_output_404', array('input'=>$this->_input_file));
  2669. }
  2670. return $this->_raiseError('execute_output_404', array('input'=>$this->_input_file));
  2671. //<- exits
  2672. }
  2673. // the file does exist but is it empty?
  2674. if(filesize($this->_process_address) == 0)
  2675. {
  2676. // add the error to the log file
  2677. if($log)
  2678. {
  2679. $this->_logResult('execute_output_empty', array('input'=>$this->_input_file));
  2680. }
  2681. return $this->_raiseError('execute_output_empty', array('input'=>$this->_input_file));
  2682. //<- exits
  2683. }
  2684. // the file is ok so move to output address
  2685. if(!is_file($this->_output_address) || $this->_overwrite_mode == self::OVERWRITE_EXISTING)
  2686. {
  2687. // post process any files
  2688. $post_process_result = $this->_postProcess($log, array($this->_process_address));
  2689. if(is_array($post_process_result) || $post_process_result === true)
  2690. {
  2691. // post process has occurred and everything is fine
  2692. }
  2693. else if($post_process_result !== false)
  2694. {
  2695. return $post_process_result;
  2696. }
  2697. // if the result is false then no post process has taken place
  2698. // rename the file to the final destination and check it went ok
  2699. if(rename($this->_process_address, $this->_output_address))
  2700. {
  2701. array_push($lines, $this->_getMessage('ffmpeg_log_separator'), $this->_getMessage('ffmpeg_log_ffmpeg_output'), $this->_getMessage('ffmpeg_log_separator'), $this->_output_address);
  2702. $this->_addToLog($lines, 'r+');
  2703. // the file has been renamed ok
  2704. // add the error to the log file
  2705. if($log)
  2706. {
  2707. $this->_logResult('execute_result_ok', array('output'=>$this->_output_address));
  2708. }
  2709. $this->_process_file_count = 1;
  2710. // add the file the the class a record of what has been generated
  2711. array_unshift($this->_files, array($this->_output_address));
  2712. return self::RESULT_OK;
  2713. }
  2714. // renaming failed so return ok but erro
  2715. else
  2716. {
  2717. // add the error to the log file
  2718. if($log)
  2719. {
  2720. $this->_logResult('execute_result_ok_but_unwritable', array('process'=>$this->_process_address, 'output'=>$this->_output_address));
  2721. }
  2722. // add the file the the class a record of what has been generated
  2723. array_unshift($this->_files, array($this->_process_address));
  2724. array_push($lines, $this->_getMessage('ffmpeg_log_separator'), $this->_getMessage('ffmpeg_log_ffmpeg_output'), $this->_getMessage('ffmpeg_log_separator'), $this->_process_address);
  2725. $this->_addToLog($lines, 'r+');
  2726. return self::RESULT_OK_BUT_UNWRITABLE;
  2727. }
  2728. }
  2729. // if it is not we signal that it has been created but has not been moved.
  2730. else if($this->_overwrite_mode == self::OVERWRITE_PRESERVE)
  2731. {
  2732. // add the error to the log file
  2733. if($log)
  2734. {
  2735. $this->_logResult('execute_result_ok_but_unwritable', array('process'=>$this->_process_address, 'output'=>$this->_output_address));
  2736. }
  2737. // add the file the the class a record of what has been generated
  2738. array_unshift($this->_files, array($this->_process_address));
  2739. return self::RESULT_OK_BUT_UNWRITABLE;
  2740. }
  2741. // the file exists so the process must fail
  2742. else
  2743. {
  2744. // add the error to the log file
  2745. if($log)
  2746. {
  2747. $this->_logResult('execute_overwrite_fail');
  2748. }
  2749. // tidy up the produced files
  2750. array_push($this->_unlink_files, $this->_process_address);
  2751. return $this->_raiseError('execute_overwrite_fail');
  2752. }
  2753. }
  2754. return null;
  2755. }
  2756. /**
  2757. * This function registers a post process after the internal handling of the ffmpeg output has been cleaned and checked.
  2758. * Each function that is set will be called in the order it is set unless an index is specified. All callbacks will be
  2759. * supplied with one argument with is an array of the outputted files.
  2760. *
  2761. * NOTE1: If a post process function is being applied to an outputted video or audio then the process will be applied
  2762. * before it has been moved to it's final destination, however if the output is an image sequence the post process
  2763. * function will be called after the images have been moved to their final destinations.
  2764. *
  2765. * NOTE2: Also it is important to return a boolean 'true' if the post process has been carried out ok. If the process is not
  2766. * a true value then the value will be treated/returned as an error and if applicable logged.
  2767. *
  2768. * @access public
  2769. * @param string $function The name of a function
  2770. * @param object|boolean $class The name of the callback class. If left as false the callback will be treated as a standalone function.
  2771. * @param integer|boolean $index The index of the callback array to put the callback into. If left as false it will be pushed to the end of the array.
  2772. */
  2773. public function registerPostProcess($function, $class=false, $index=false)
  2774. {
  2775. // create the callback
  2776. $callback = $class === false ? $function : array(&$class, $function);
  2777. // add it to the post process array
  2778. if($index === false)
  2779. {
  2780. array_push($this->_post_processes, $callback);
  2781. }
  2782. else
  2783. {
  2784. $this->_post_processes[$index] = $callback;
  2785. }
  2786. }
  2787. /**
  2788. * Carries out the post processing of the files.
  2789. *
  2790. * @access private
  2791. * @param boolean $log Determines if logging of errors should be carried out.
  2792. * @param array $files The array of files that have just been processed.
  2793. * @return mixed
  2794. */
  2795. private function _postProcess($log, $files)
  2796. {
  2797. if(count($this->_post_processes))
  2798. {
  2799. // loop through the post processes
  2800. foreach($this->_post_processes as $key=>$process)
  2801. {
  2802. // call the process
  2803. $return_value = call_user_func_array($process, array($files));
  2804. // if the return value is not strictly equal to true the result will be treated as an error and exit the process loop
  2805. if(!is_array($return_value) && $return_value !== true)
  2806. {
  2807. if($log)
  2808. {
  2809. $this->_logResult($return_value);
  2810. }
  2811. return $this->_raiseError($return_value);
  2812. }
  2813. }
  2814. return $return_value;
  2815. }
  2816. return false;
  2817. }
  2818. /**
  2819. * Returns the number of files outputted in this run. It will be reset when you call PHPVideoToolkit::reset();
  2820. *
  2821. * @access public
  2822. * @return integer
  2823. */
  2824. public function getFileOutputCount()
  2825. {
  2826. return $this->_process_file_count;
  2827. }
  2828. /**
  2829. * Adds lines to the current log file.
  2830. *
  2831. * @access private
  2832. * @param $message
  2833. * @param $replacements
  2834. */
  2835. private function _logResult($message, $replacements=false)
  2836. {
  2837. $this->_addToLog(array($this->_getMessage('ffmpeg_log_separator'), $this->_getMessage('ffmpeg_log_ffmpeg_result'), $this->_getMessage('ffmpeg_log_separator'), $this->_getMessage($message, $replacements)));
  2838. }
  2839. /**
  2840. * Adds lines to the current log file.
  2841. *
  2842. * @access private
  2843. * @param $lines array An array of lines to add to the log file.
  2844. */
  2845. private function _addToLog($lines, $where='a')
  2846. {
  2847. $handle = fopen($this->_log_file, $where);
  2848. if(is_array($lines))
  2849. {
  2850. $data = implode("\n", $lines)."\n";
  2851. }
  2852. else
  2853. {
  2854. $data = $lines."\n";
  2855. }
  2856. fwrite($handle, $data);
  2857. fclose($handle);
  2858. }
  2859. /**
  2860. * Moves the current log file to another file.
  2861. *
  2862. * @access public
  2863. * @param $destination string The absolute path of the new filename for the log.
  2864. * @return boolean Returns the result of the log file rename.
  2865. */
  2866. public function moveLog($destination)
  2867. {
  2868. $result = false;
  2869. if($this->_log_file !== null && is_file($this->_log_file))
  2870. {
  2871. $result = rename($this->_log_file, $destination);
  2872. $this->_log_file = $destination;
  2873. }
  2874. return $result;
  2875. }
  2876. /**
  2877. * Reads the current log file
  2878. *
  2879. * @access public
  2880. * @return string|boolean Returns the current log file content. Returns false on failure.
  2881. */
  2882. public function readLog()
  2883. {
  2884. if($this->_log_file !== null && is_file($this->_log_file))
  2885. {
  2886. $handle = fopen($this->_log_file, 'r');
  2887. $contents = fread($handle, filesize($this->_log_file));
  2888. fclose($handle);
  2889. return $contents;
  2890. }
  2891. return false;
  2892. }
  2893. /**
  2894. * Returns the last outputted file that was processed by ffmpeg from this class.
  2895. *
  2896. * @access public
  2897. * @return mixed array|string Will return an array if the output was a sequence, or string if it was a single file output
  2898. */
  2899. public function getLastOutput()
  2900. {
  2901. return $this->_files[0];
  2902. }
  2903. /**
  2904. * Returns all the outputted files that were processed by ffmpeg from this class.
  2905. *
  2906. * @access public
  2907. * @return array
  2908. */
  2909. public function getOutput()
  2910. {
  2911. return $this->_files;
  2912. }
  2913. /**
  2914. * Returns the amount of time taken of the last file to be processed by ffmpeg.
  2915. *
  2916. * @access public
  2917. * @return mixed integer Will return the time taken in seconds.
  2918. */
  2919. public function getLastProcessTime()
  2920. {
  2921. return $this->_timers[0];
  2922. }
  2923. /**
  2924. * Returns the amount of time taken of all the files to be processed by ffmpeg.
  2925. *
  2926. * @access public
  2927. * @return array
  2928. */
  2929. public function getProcessTime()
  2930. {
  2931. return $this->_timers;
  2932. }
  2933. /**
  2934. * Returns the last encountered error message.
  2935. *
  2936. * @access public
  2937. * @return string
  2938. */
  2939. public function getLastError()
  2940. {
  2941. return $this->_errors[0];
  2942. }
  2943. /**
  2944. * Returns all the encountered errors as an array of strings
  2945. *
  2946. * @access public
  2947. * @return array
  2948. */
  2949. public function getErrors()
  2950. {
  2951. return $this->_errors;
  2952. }
  2953. /**
  2954. * Returns the last command that ffmpeg was given.
  2955. * (Note; if setFormatToFLV was used in the last command then an array is returned as a command was also sent to FLVTool2)
  2956. *
  2957. * @access public
  2958. * @return mixed array|string
  2959. */
  2960. public function getLastCommand()
  2961. {
  2962. return $this->_processed[0];
  2963. }
  2964. /**
  2965. * Returns all the commands sent to ffmpeg from this class
  2966. *
  2967. * @access public
  2968. * @return unknown
  2969. */
  2970. public function getCommands()
  2971. {
  2972. return $this->_processed;
  2973. }
  2974. /**
  2975. * Raises an error
  2976. *
  2977. * @access private
  2978. * @param string $message
  2979. * @param array $replacements a list of replacements in search=>replacement format
  2980. * @return boolean Only returns false if $toolkit->on_error_die is set to false
  2981. */
  2982. private function _raiseError($message, $replacements=false)
  2983. {
  2984. $msg = 'FFMPEG ERROR: '.$this->_getMessage($message, $replacements);
  2985. // check what the error is supposed to do
  2986. if($this->on_error_die === true)
  2987. {
  2988. die($msg);
  2989. //<- exits
  2990. }
  2991. // add the error message to the collection
  2992. array_unshift($this->_errors, $msg);
  2993. return false;
  2994. }
  2995. /**
  2996. * Gets a message.
  2997. *
  2998. * @access private
  2999. * @param string $message
  3000. * @param array $replacements a list of replacements in search=>replacement format
  3001. * @return boolean Only returns false if $toolkit->on_error_die is set to false
  3002. */
  3003. private function _getMessage($message, $replacements=false)
  3004. {
  3005. $message = isset($this->_messages[$message]) ? $this->_messages[$message] : 'Unknown!!!';
  3006. if($replacements)
  3007. {
  3008. $searches = $replaces = array();
  3009. foreach($replacements as $search=>$replace)
  3010. {
  3011. array_push($searches, '#'.$search);
  3012. array_push($replaces, $replace);
  3013. }
  3014. $message = str_replace($searches, $replaces, $message);
  3015. }
  3016. return $message;
  3017. }
  3018. /**
  3019. * Adds a command to be bundled into the ffmpeg command call.
  3020. * (SPECIAL NOTE; None of the arguments are checked or sanitized by this function. BE CAREFUL if manually using this. The commands and arguments are escaped
  3021. * however it is still best to check and sanitize any params given to this function)
  3022. *
  3023. * @access public
  3024. * @param string $command
  3025. * @param mixed $argument
  3026. * @return boolean
  3027. */
  3028. public function addCommand($command, $argument='')
  3029. {
  3030. $this->_commands[$command] = escapeshellarg($argument);
  3031. return true;
  3032. }
  3033. /**
  3034. * Determines if the the command exits.
  3035. *
  3036. * @access public
  3037. * @param string $command
  3038. * @return mixed boolean if failure or value if exists.
  3039. */
  3040. public function hasCommand($command)
  3041. {
  3042. return isset($this->_commands[$command]) ? $this->_commands[$command] : false;
  3043. }
  3044. /**
  3045. * Combines the commands stored into a string
  3046. *
  3047. * @access private
  3048. * @return string
  3049. */
  3050. private function _combineCommands()
  3051. {
  3052. $before_input = array();
  3053. $after_input = array();
  3054. $input = null;
  3055. foreach ($this->_commands as $command=>$argument)
  3056. {
  3057. $command_string = trim($command.($argument ? ' '.$argument : ''));
  3058. // check for specific none combinable commands as they have specific places they have to go in the string
  3059. switch($command)
  3060. {
  3061. case '-i' :
  3062. $input = $command_string;
  3063. break;
  3064. case '-inputr' :
  3065. $command_string = trim('-r'.($argument ? ' '.$argument : ''));;
  3066. default :
  3067. if(in_array($command, $this->_cmds_before_input))
  3068. {
  3069. array_push($before_input, $command_string);
  3070. }
  3071. else
  3072. {
  3073. array_push($after_input, $command_string);
  3074. }
  3075. }
  3076. }
  3077. $before_input = count($before_input) ? implode(' ', $before_input).' ' : '';
  3078. $after_input_string = ' ';
  3079. if(count($after_input))
  3080. {
  3081. $input .= ' ';
  3082. $after_input_string = implode(' ', $after_input).' ';
  3083. }
  3084. return $before_input.$input.$after_input_string;
  3085. }
  3086. /**
  3087. * Prepares the command for execution
  3088. *
  3089. * @access private
  3090. * @param string $path Path to the binary
  3091. * @param string $command Command string to execute
  3092. * @param string $args Any additional arguments
  3093. * @return string
  3094. */
  3095. private function _prepareCommand($path, $command, $args='')
  3096. {
  3097. if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' || !preg_match('/\s/', $path))
  3098. {
  3099. return $path.' '.$command.' '.$args;
  3100. }
  3101. return 'start /D "'.$path.'" /B '.$command.' '.$args;
  3102. }
  3103. /**
  3104. * Generates a unique id. Primarily used in jpeg to movie production
  3105. *
  3106. * @access public
  3107. * @param string $prefix
  3108. * @return string
  3109. */
  3110. public function unique($prefix='')
  3111. {
  3112. return uniqid($prefix.time().'-');
  3113. }
  3114. /**
  3115. * Destructs ffmpeg and removes any temp files/dirs
  3116. * @access private
  3117. */
  3118. function __destruct()
  3119. {
  3120. // loop through the temp files to remove first as they have to be removed before the dir can be removed
  3121. if(!empty($this->_unlink_files))
  3122. {
  3123. foreach ($this->_unlink_files as $key=>$file)
  3124. {
  3125. if(is_file($file))
  3126. {
  3127. @unlink($file);
  3128. }
  3129. }
  3130. $this->_unlink_files = array();
  3131. }
  3132. // loop through the dirs to remove
  3133. if(!empty($this->_unlink_dirs))
  3134. {
  3135. foreach ($this->_unlink_dirs as $key=>$dir)
  3136. {
  3137. if(is_dir($dir))
  3138. {
  3139. @rmdir($dir);
  3140. }
  3141. }
  3142. $this->_unlink_dirs = array();
  3143. }
  3144. }
  3145. }