├── FFmpegAnimatedGif.php ├── FFmpegAutoloader.class.php ├── FFmpegFrame.php ├── FFmpegMovie.php ├── adapter ├── ffmpeg_animated_gif.php ├── ffmpeg_frame.php └── ffmpeg_movie.php ├── composer.json ├── index.php ├── provider ├── AbstractOutputProvider.php ├── FFmpegOutputProvider.php ├── FFprobeOutputProvider.php ├── OutputProvider.php └── StringOutputProvider.php ├── readme.md └── vendor ├── autoload.php └── composer ├── ClassLoader.php ├── LICENSE ├── autoload_classmap.php ├── autoload_namespaces.php ├── autoload_psr4.php ├── autoload_real.php ├── autoload_static.php └── installed.json /FFmpegAnimatedGif.php: -------------------------------------------------------------------------------- 1 | outFilePath = $outFilePath; 83 | $this->width = $width; 84 | $this->height = $height; 85 | $this->frameRate = $frameRate; 86 | $this->loopCount = ($loopCount < -1) ? 0 : $loopCount; 87 | $this->frames = array(); 88 | $this->counter = -1; 89 | } 90 | 91 | /** 92 | * Add a frame to the end of the animated gif. 93 | * 94 | * @param FFmpegFrame $frame frame to add 95 | * @return void 96 | */ 97 | public function addFrame(FFmpegFrame $frame) { 98 | $tmpFrame = clone $frame; 99 | $tmpFrame->resize($this->width, $this->height); 100 | ob_start(); 101 | imagegif($tmpFrame->toGDImage()); 102 | $this->frames[] = ob_get_clean(); 103 | $tmpFrame = null; 104 | } 105 | 106 | /** 107 | * Adding header to the animation 108 | * 109 | * @return void 110 | */ 111 | protected function addGifHeader() { 112 | $cmap = 0; 113 | 114 | if (ord($this->frames[0]{10}) & 0x80) { 115 | $cmap = 3 * (2 << (ord($this->frames[0]{10}) & 0x07)); 116 | 117 | $this->gifData = 'GIF89a'; 118 | $this->gifData .= substr($this->frames[0], 6, 7); 119 | $this->gifData .= substr($this->frames[0], 13, $cmap); 120 | $this->gifData .= "!\377\13NETSCAPE2.0\3\1".$this->getGifWord($this->loopCount)."\0"; 121 | } 122 | } 123 | 124 | /** 125 | * Adding frame binary data to the animation 126 | * 127 | * @param int $i index of frame from FFmpegAnimatedGif::frame array 128 | * @param int $d delay (5 seconds = 500 delay units) 129 | * @return void 130 | */ 131 | protected function addFrameData($i, $d) { 132 | $DIS = 2; 133 | $COL = 0; 134 | 135 | $Locals_str = 13 + 3 * (2 << (ord($this->frames[$i]{10}) & 0x07)); 136 | $Locals_end = strlen($this->frames[$i]) - $Locals_str - 1; 137 | $Locals_tmp = substr($this->frames[$i], $Locals_str, $Locals_end ); 138 | 139 | $Global_len = 2 << (ord($this->frames[0]{10}) & 0x07); 140 | $Locals_len = 2 << (ord($this->frames[$i]{10}) & 0x07); 141 | 142 | $Global_rgb = substr($this->frames[0], 13, 3 * (2 << (ord($this->frames[0]{10}) & 0x07))); 143 | $Locals_rgb = substr($this->frames[$i], 13, 3 * (2 << (ord($this->frames[$i]{10}) & 0x07))); 144 | 145 | $Locals_ext = "!\xF9\x04".chr(($DIS << 2 ) + 0). chr(($d >> 0) & 0xFF).chr(($d >> 8) & 0xFF)."\x0\x0"; 146 | 147 | if ($COL > -1 && ord($this->frames[$i]{10}) & 0x80) { 148 | for ($j = 0; $j < (2 << (ord($this->frames[$i]{10}) & 0x07)); $j++) { 149 | if (ord($Locals_rgb{3 * $j + 0}) == (($COL >> 16 ) & 0xFF) 150 | && ord($Locals_rgb{3 * $j + 1}) == (($COL >> 8 ) & 0xFF) 151 | && ord($Locals_rgb{3 * $j + 2}) == (($COL >> 0 ) & 0xFF) 152 | ) { 153 | $Locals_ext = "!\xF9\x04".chr(($DIS << 2) + 1).chr(($d >> 0) & 0xFF).chr(($d >> 8) & 0xFF).chr($j)."\x0"; 154 | break; 155 | } 156 | } 157 | } 158 | switch ($Locals_tmp{0}) { 159 | case "!": 160 | $Locals_img = substr($Locals_tmp, 8, 10); 161 | $Locals_tmp = substr($Locals_tmp, 18, strlen($Locals_tmp) - 18); 162 | break; 163 | case ",": 164 | $Locals_img = substr($Locals_tmp, 0, 10); 165 | $Locals_tmp = substr($Locals_tmp, 10, strlen($Locals_tmp) - 10); 166 | break; 167 | } 168 | if (ord($this->frames[$i]{10}) & 0x80 && $this->counter > -1) { 169 | if ($Global_len == $Locals_len) { 170 | if ($this->gifBlockCompare($Global_rgb, $Locals_rgb, $Global_len)) { 171 | $this->gifData .= ($Locals_ext.$Locals_img.$Locals_tmp); 172 | } 173 | else { 174 | $byte = ord($Locals_img{9}); 175 | $byte |= 0x80; 176 | $byte &= 0xF8; 177 | $byte |= (ord($this->frames[0]{10}) & 0x07); 178 | $Locals_img{9} = chr ($byte); 179 | $this->gifData .= ($Locals_ext.$Locals_img.$Locals_rgb.$Locals_tmp); 180 | } 181 | } 182 | else { 183 | $byte = ord($Locals_img{9}); 184 | $byte |= 0x80; 185 | $byte &= 0xF8; 186 | $byte |= (ord($this->frames[$i]{10}) & 0x07); 187 | $Locals_img{9} = chr($byte); 188 | $this->gifData .= ($Locals_ext.$Locals_img.$Locals_rgb.$Locals_tmp); 189 | } 190 | } 191 | else { 192 | $this->gifData .= ($Locals_ext.$Locals_img.$Locals_tmp); 193 | } 194 | $this->counter = 1; 195 | } 196 | 197 | /** 198 | * Adding footer to the animation 199 | * 200 | * @return void 201 | */ 202 | protected function addGifFooter() { 203 | $this->gifData .= ';'; 204 | } 205 | 206 | /** 207 | * Gif integer wrapper 208 | * 209 | * @param int $int 210 | * @return string 211 | */ 212 | protected function getGifWord($int) { 213 | 214 | return (chr($int & 0xFF).chr(($int >> 8 ) & 0xFF)); 215 | } 216 | 217 | /** 218 | * Gif compare block 219 | * 220 | * @param string $GlobalBlock 221 | * @param string $LocalBlock 222 | * @param int $Len 223 | * @return int 224 | */ 225 | protected function gifBlockCompare($GlobalBlock, $LocalBlock, $Len) { 226 | for ($i = 0; $i < $Len; $i++) { 227 | if ( 228 | $GlobalBlock{3 * $i + 0} != $LocalBlock {3 * $i + 0} || 229 | $GlobalBlock{3 * $i + 1} != $LocalBlock {3 * $i + 1} || 230 | $GlobalBlock{3 * $i + 2} != $LocalBlock {3 * $i + 2} 231 | ) { 232 | return (0); 233 | } 234 | } 235 | 236 | return (1); 237 | } 238 | 239 | /** 240 | * Saving animated gif to remote file 241 | * 242 | * @return boolean 243 | */ 244 | public function save() { 245 | // No images to proces 246 | if (count($this->frames) == 0) return false; 247 | 248 | return (boolean) file_put_contents($this->outFilePath, $this->getAnimation(), LOCK_EX); 249 | } 250 | 251 | /** 252 | * Getting animation binary data 253 | * 254 | * @return string|boolean 255 | */ 256 | public function getAnimation() { 257 | // No images to proces 258 | if (count($this->frames) == 0) return false; 259 | 260 | // Process images as animation 261 | $this->addGifHeader(); 262 | for ($i = 0; $i < count($this->frames); $i++) { 263 | $this->addFrameData($i, (1 / $this->frameRate * 100)); 264 | } 265 | $this->addGifFooter(); 266 | 267 | return $this->gifData; 268 | } 269 | 270 | public function serialize() { 271 | return serialize(array( 272 | $this->outFilePath, 273 | $this->width, 274 | $this->height, 275 | $this->frameRate, 276 | $this->loopCount, 277 | $this->gifData, 278 | $this->frames, 279 | $this->counter 280 | )); 281 | } 282 | 283 | public function unserialize($serialized) { 284 | $data = unserialize($serialized); 285 | list( 286 | $this->outFilePath, 287 | $this->width, 288 | $this->height, 289 | $this->frameRate, 290 | $this->loopCount, 291 | $this->gifData, 292 | $this->frames, 293 | $this->counter 294 | ) = $data; 295 | } 296 | } -------------------------------------------------------------------------------- /FFmpegAutoloader.class.php: -------------------------------------------------------------------------------- 1 | directory = $baseDirectory; 25 | // 当前命名空间 26 | $this->prefix = __NAMESPACE__.'\\'; 27 | // 当前命名空间的字符串数 28 | $this->prefixLength = strlen($this->prefix); 29 | } 30 | /** 31 | * Autoloading mechanizm 32 | * 33 | * @param string $className 34 | * @return boolean 35 | */ 36 | public function autoload($className) { 37 | // new \Phpffmpeg\... 38 | // 判断如果以\命名空间访问的格式符合 39 | if (0 === strpos($className, $this->prefix)) { 40 | //分隔出$this->prefixLength个字符串以后的字符返回,再以\为分隔符分隔 41 | $parts = explode('\\', substr($className, $this->prefixLength)); 42 | // 组合新的路径 43 | $filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php'; 44 | if (is_file($filepath)) { 45 | require_once $filepath; 46 | } 47 | } 48 | } 49 | 50 | /** 51 | * Registering autoloading mechanizm 52 | */ 53 | public static function register($prepend=false) { 54 | if (function_exists('__autoload')) { 55 | trigger_error('FFmpegPHP uses spl_autoload_register() which will bypass your __autoload() and may break your autoloading', E_USER_WARNING); 56 | } else { 57 | // spl_autoload_register(array('FFmpegAutoloader', 'autoload')); 58 | //spl_autoload_register(array('class_name','function')),即是那个类的function 59 | spl_autoload_register(array(new self(), 'autoload'), true, $prepend); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /FFmpegFrame.php: -------------------------------------------------------------------------------- 1 | gdImageData = $this->gdImageToBinaryData($gdImage); 59 | $this->width = imagesx($gdImage); 60 | $this->height = imagesy($gdImage); 61 | $this->pts = $pts; 62 | } 63 | 64 | /** 65 | * Return the width of the frame. 66 | * 67 | * @return int 68 | */ 69 | public function getWidth() { 70 | return $this->width; 71 | } 72 | 73 | /** 74 | * Return the height of the frame. 75 | * 76 | * @return int 77 | */ 78 | public function getHeight() { 79 | return $this->height; 80 | } 81 | 82 | /** 83 | * Return the presentation time stamp of the frame; alias $frame->getPresentationTimestamp() 84 | * 85 | * @return float 86 | */ 87 | public function getPTS() { 88 | return $this->pts; 89 | } 90 | 91 | /** 92 | * Return the presentation time stamp of the frame. 93 | * 94 | * @return float 95 | */ 96 | public function getPresentationTimestamp() { 97 | return $this->getPTS(); 98 | } 99 | 100 | /** 101 | * Resize and optionally crop the frame. (Cropping is built into ffmpeg resizing so I'm providing it here for completeness.) 102 | * 103 | * * width - New width of the frame (must be an even number). 104 | * * height - New height of the frame (must be an even number). 105 | * * croptop - Remove [croptop] rows of pixels from the top of the frame. 106 | * * cropbottom - Remove [cropbottom] rows of pixels from the bottom of the frame. 107 | * * cropleft - Remove [cropleft] rows of pixels from the left of the frame. 108 | * * cropright - Remove [cropright] rows of pixels from the right of the frame. 109 | * 110 | * 111 | * NOTE: Cropping is always applied to the frame before it is resized. Crop values must be even numbers. 112 | * 113 | * @param int $width 114 | * @param int $height 115 | * @param int $cropTop OPTIONAL parameter; DEFAULT value - 0 116 | * @param int $cropBottom OPTIONAL parameter; DEFAULT value - 0 117 | * @param int $cropLeft OPTIONAL parameter; DEFAULT value - 0 118 | * @param int $cropRight OPTIONAL parameter; DEFAULT value - 0 119 | * @return void 120 | */ 121 | public function resize($width, $height, $cropTop = 0, $cropBottom = 0, $cropLeft = 0, $cropRight = 0) { 122 | $widthCrop = ($cropLeft + $cropRight); 123 | $heightCrop = ($cropTop + $cropBottom); 124 | $width -= $widthCrop; 125 | $height -= $heightCrop; 126 | $resizedImage = imagecreatetruecolor($width, $height); 127 | $gdImage = $this->toGDImage(); 128 | imagecopyresampled($resizedImage, $gdImage, 0, 0, $cropLeft, $cropTop, $width, $height, $this->getWidth() - $widthCrop, $this->getHeight() - $heightCrop); 129 | imageconvolution($resizedImage, array( 130 | array( -1, -1, -1 ), 131 | array( -1, 24, -1 ), 132 | array( -1, -1, -1 ), 133 | ), 16, 0); 134 | 135 | $this->gdImageData = $this->gdImageToBinaryData($resizedImage); 136 | $this->width = imagesx($resizedImage); 137 | $this->height = imagesy($resizedImage); 138 | imagedestroy($gdImage); 139 | imagedestroy($resizedImage); 140 | } 141 | 142 | /** 143 | * Crop the frame. 144 | * 145 | * * croptop - Remove [croptop] rows of pixels from the top of the frame. 146 | * * cropbottom - Remove [cropbottom] rows of pixels from the bottom of the frame. 147 | * * cropleft - Remove [cropleft] rows of pixels from the left of the frame. 148 | * * cropright - Remove [cropright] rows of pixels from the right of the frame. 149 | * 150 | * NOTE: Crop values must be even numbers. 151 | * 152 | * @param int $cropTop 153 | * @param int $cropBottom OPTIONAL parameter; DEFAULT value - 0 154 | * @param int $cropLeft OPTIONAL parameter; DEFAULT value - 0 155 | * @param int $cropRight OPTIONAL parameter; DEFAULT value - 0 156 | * @return void 157 | */ 158 | public function crop($cropTop, $cropBottom = 0, $cropLeft = 0, $cropRight = 0) { 159 | $this->resize($this->getWidth(), $this->getHeight(), $cropTop, $cropBottom, $cropLeft, $cropRight); 160 | } 161 | 162 | /** 163 | * Returns a truecolor GD image of the frame. 164 | * 165 | * @return resource resource of type gd 166 | */ 167 | public function toGDImage() { 168 | return imagecreatefromstring($this->gdImageData); 169 | } 170 | 171 | protected function gdImageToBinaryData($gdImage) { 172 | ob_start(); 173 | imagegd2($gdImage); 174 | return ob_get_clean(); 175 | } 176 | 177 | public function serialize() { 178 | $data = array( 179 | $this->gdImageData, 180 | $this->pts, 181 | $this->width, 182 | $this->height 183 | ); 184 | 185 | return serialize($data); 186 | } 187 | 188 | public function unserialize($serialized) { 189 | $data = unserialize($serialized); 190 | list($this->gdImageData, 191 | $this->pts, 192 | $this->width, 193 | $this->height 194 | ) = $data; 195 | } 196 | } -------------------------------------------------------------------------------- /FFmpegMovie.php: -------------------------------------------------------------------------------- 1 | movieFile = $moviePath; 211 | $this->frameNumber = 0; 212 | $this->ffmpegBinary = $ffmpegBinary; 213 | if ($outputProvider === null) { 214 | $outputProvider = new FFmpegOutputProvider($ffmpegBinary); 215 | } 216 | $this->setProvider($outputProvider); 217 | } 218 | 219 | /** 220 | * Setting provider implementation 221 | * 222 | * @param OutputProvider $outputProvider 223 | */ 224 | public function setProvider(OutputProvider $outputProvider) { 225 | $this->provider = $outputProvider; 226 | $this->provider->setMovieFile($this->movieFile); 227 | $this->output = $this->provider->getOutput(); 228 | } 229 | 230 | /** 231 | * Getting current provider implementation 232 | * 233 | * @return OutputProvider 234 | */ 235 | public function getProvider() { 236 | return $this->provider; 237 | } 238 | 239 | /** 240 | * Return the duration of a movie or audio file in seconds. 241 | * 242 | * @return float movie duration in seconds 243 | */ 244 | public function getDuration() { 245 | if ($this->duration === null) { 246 | $match = array(); 247 | preg_match(self::$REGEX_DURATION, $this->output, $match); 248 | if (array_key_exists(1, $match) && array_key_exists(2, $match) && array_key_exists(3, $match)) { 249 | $hours = (int) $match[1]; 250 | $minutes = (int) $match[2]; 251 | $seconds = (int) $match[3]; 252 | $fractions = (float) ((array_key_exists(5, $match)) ? "0.$match[5]" : 0.0); 253 | 254 | $this->duration = (($hours * (3600)) + ($minutes * 60) + $seconds + $fractions); 255 | } else { 256 | $this->duration = 0.0; 257 | } 258 | 259 | return $this->duration; 260 | } 261 | 262 | return $this->duration; 263 | } 264 | 265 | /** 266 | * Return the number of frames in a movie or audio file. 267 | * 268 | * @return int 269 | */ 270 | public function getFrameCount() { 271 | if ($this->frameCount === null) { 272 | $this->frameCount = (int) ($this->getDuration() * $this->getFrameRate()); 273 | } 274 | 275 | return $this->frameCount; 276 | } 277 | 278 | /** 279 | * Return the frame rate of a movie in fps. 280 | * 281 | * @return float 282 | */ 283 | public function getFrameRate() { 284 | if ($this->frameRate === null) { 285 | $match = array(); 286 | preg_match(self::$REGEX_FRAME_RATE, $this->output, $match); 287 | $this->frameRate = (float) ((array_key_exists(1, $match)) ? $match[1] : 0.0); 288 | } 289 | 290 | return $this->frameRate; 291 | } 292 | 293 | /** 294 | * Return the path and name of the movie file or audio file. 295 | * 296 | * @return string 297 | */ 298 | public function getFilename() { 299 | return $this->movieFile; 300 | } 301 | 302 | /** 303 | * Return the comment field from the movie or audio file. 304 | * 305 | * @return string 306 | */ 307 | public function getComment() { 308 | if ($this->comment === null) { 309 | $match = array(); 310 | preg_match(self::$REGEX_COMMENT, $this->output, $match); 311 | $this->comment = (array_key_exists(2, $match)) ? trim($match[2]) : ''; 312 | } 313 | 314 | return $this->comment; 315 | } 316 | 317 | /** 318 | * Return the title field from the movie or audio file. 319 | * 320 | * @return string 321 | */ 322 | public function getTitle() { 323 | if ($this->title === null) { 324 | $match = array(); 325 | preg_match(self::$REGEX_TITLE, $this->output, $match); 326 | $this->title = (array_key_exists(2, $match)) ? trim($match[2]) : ''; 327 | } 328 | 329 | return $this->title; 330 | } 331 | 332 | /** 333 | * Return the author field from the movie or the artist ID3 field from an mp3 file; alias $movie->getArtist() 334 | * 335 | * @return string 336 | */ 337 | public function getArtist() { 338 | if ($this->artist === null) { 339 | $match = array(); 340 | preg_match(self::$REGEX_ARTIST, $this->output, $match); 341 | $this->artist = (array_key_exists(3, $match)) ? trim($match[3]) : ''; 342 | } 343 | 344 | return $this->artist; 345 | } 346 | 347 | /** 348 | * Return the author field from the movie or the artist ID3 field from an mp3 file. 349 | * 350 | * @return string 351 | */ 352 | public function getAuthor() { 353 | return $this->getArtist(); 354 | } 355 | 356 | /** 357 | * Return the copyright field from the movie or audio file. 358 | * 359 | * @return string 360 | */ 361 | public function getCopyright() { 362 | if ($this->copyright === null) { 363 | $match = array(); 364 | preg_match(self::$REGEX_COPYRIGHT, $this->output, $match); 365 | $this->copyright = (array_key_exists(2, $match)) ? trim($match[2]) : ''; 366 | } 367 | 368 | return $this->copyright; 369 | } 370 | 371 | /** 372 | * Return the genre ID3 field from an mp3 file. 373 | * 374 | * @return string 375 | */ 376 | public function getGenre() { 377 | if ($this->genre === null) { 378 | $match = array(); 379 | preg_match(self::$REGEX_GENRE, $this->output, $match); 380 | $this->genre = (array_key_exists(2, $match)) ? trim($match[2]) : ''; 381 | } 382 | 383 | return $this->genre; 384 | } 385 | 386 | /** 387 | * Return the track ID3 field from an mp3 file. 388 | * 389 | * @return int 390 | */ 391 | public function getTrackNumber() { 392 | if ($this->trackNumber === null) { 393 | $match = array(); 394 | preg_match(self::$REGEX_TRACK_NUMBER, $this->output, $match); 395 | $this->trackNumber = (int) ((array_key_exists(2, $match)) ? $match[2] : 0); 396 | } 397 | 398 | return $this->trackNumber; 399 | } 400 | 401 | /** 402 | * Return the year ID3 field from an mp3 file. 403 | * 404 | * @return int 405 | */ 406 | public function getYear() { 407 | if ($this->year === null) { 408 | $match = array(); 409 | preg_match(self::$REGEX_YEAR, $this->output, $match); 410 | $this->year = (int) ((array_key_exists(2, $match)) ? $match[2] : 0); 411 | } 412 | 413 | return $this->year; 414 | } 415 | 416 | /** 417 | * Return the height of the movie in pixels. 418 | * 419 | * @return int 420 | */ 421 | public function getFrameHeight() { 422 | if ($this->frameHeight == null) { 423 | $match = array(); 424 | preg_match(self::$REGEX_FRAME_WH, $this->output, $match); 425 | if (array_key_exists(1, $match) && array_key_exists(2, $match)) { 426 | $this->frameWidth = (int) $match[1]; 427 | $this->frameHeight = (int) $match[2]; 428 | } else { 429 | $this->frameWidth = 0; 430 | $this->frameHeight = 0; 431 | } 432 | } 433 | 434 | return $this->frameHeight; 435 | } 436 | 437 | /** 438 | * Return the width of the movie in pixels. 439 | * 440 | * @return int 441 | */ 442 | public function getFrameWidth() { 443 | if ($this->frameWidth === null) { 444 | $this->getFrameHeight(); 445 | } 446 | 447 | return $this->frameWidth; 448 | } 449 | 450 | /** 451 | * Return the pixel format of the movie. 452 | * 453 | * @return string 454 | */ 455 | public function getPixelFormat() { 456 | if ($this->pixelFormat === null) { 457 | $match = array(); 458 | preg_match(self::$REGEX_PIXEL_FORMAT, $this->output, $match); 459 | $this->pixelFormat = (array_key_exists(1, $match)) ? trim($match[1]) : ''; 460 | } 461 | 462 | return $this->pixelFormat; 463 | } 464 | 465 | /** 466 | * Return the bit rate of the movie or audio file in bits per second. 467 | * 468 | * @return int 469 | */ 470 | public function getBitRate() { 471 | if ($this->bitRate === null) { 472 | $match = array(); 473 | preg_match(self::$REGEX_BITRATE, $this->output, $match); 474 | $this->bitRate = (int) ((array_key_exists(1, $match)) ? ($match[1] * 1000) : 0); 475 | } 476 | 477 | return $this->bitRate; 478 | } 479 | 480 | /** 481 | * Return the bit rate of the video in bits per second. 482 | * 483 | * NOTE: This only works for files with constant bit rate. 484 | * 485 | * @return int 486 | */ 487 | public function getVideoBitRate() { 488 | if ($this->videoBitRate === null) { 489 | $match = array(); 490 | preg_match(self::$REGEX_VIDEO_BITRATE, $this->output, $match); 491 | $this->videoBitRate = (int) ((array_key_exists(1, $match)) ? ($match[1] * 1000) : 0); 492 | } 493 | 494 | return $this->videoBitRate; 495 | } 496 | 497 | /** 498 | * Return the audio bit rate of the media file in bits per second. 499 | * 500 | * @return int 501 | */ 502 | public function getAudioBitRate() { 503 | if ($this->audioBitRate === null) { 504 | $match = array(); 505 | preg_match(self::$REGEX_AUDIO_BITRATE, $this->output, $match); 506 | $this->audioBitRate = (int) ((array_key_exists(1, $match)) ? ($match[1] * 1000) : 0); 507 | } 508 | 509 | return $this->audioBitRate; 510 | } 511 | 512 | /** 513 | * Return the audio sample rate of the media file in bits per second. 514 | * 515 | * @return int 516 | */ 517 | public function getAudioSampleRate() { 518 | if ($this->audioSampleRate === null) { 519 | $match = array(); 520 | preg_match(self::$REGEX_AUDIO_SAMPLE_RATE, $this->output, $match); 521 | $this->audioSampleRate = (int) ((array_key_exists(1, $match)) ? $match[1] : 0); 522 | } 523 | 524 | return $this->audioSampleRate; 525 | } 526 | 527 | /** 528 | * Return the current frame index. 529 | * 530 | * @return int 531 | */ 532 | public function getFrameNumber() { 533 | return ($this->frameNumber == 0) ? 1 : $this->frameNumber; 534 | } 535 | 536 | /** 537 | * Return the name of the video codec used to encode this movie as a string. 538 | * 539 | * @return string 540 | */ 541 | public function getVideoCodec() { 542 | if ($this->videoCodec === null) { 543 | $match = array(); 544 | preg_match(self::$REGEX_VIDEO_CODEC, $this->output, $match); 545 | $this->videoCodec = (array_key_exists(1, $match)) ? trim($match[1]) : ''; 546 | } 547 | 548 | return $this->videoCodec; 549 | } 550 | 551 | /** 552 | * Return the name of the audio codec used to encode this movie as a string. 553 | * 554 | * @return string 555 | */ 556 | public function getAudioCodec() { 557 | if ($this->audioCodec === null) { 558 | $match = array(); 559 | preg_match(self::$REGEX_AUDIO_CODEC, $this->output, $match); 560 | $this->audioCodec = (array_key_exists(1, $match)) ? trim($match[1]) : ''; 561 | } 562 | 563 | return $this->audioCodec; 564 | } 565 | 566 | /** 567 | * Return the number of audio channels in this movie as an integer. 568 | * 569 | * @return int 570 | */ 571 | public function getAudioChannels() { 572 | if ($this->audioChannels === null) { 573 | $match = array(); 574 | preg_match(self::$REGEX_AUDIO_CHANNELS, $this->output, $match); 575 | if (array_key_exists(1, $match)) { 576 | switch (trim($match[1])) { 577 | case 'mono': 578 | $this->audioChannels = 1; break; 579 | case 'stereo': 580 | $this->audioChannels = 2; break; 581 | case '5.1': 582 | $this->audioChannels = 6; break; 583 | case '5:1': 584 | $this->audioChannels = 6; break; 585 | default: 586 | $this->audioChannels = (int) $match[1]; 587 | } 588 | } else { 589 | $this->audioChannels = 0; 590 | } 591 | } 592 | 593 | return $this->audioChannels; 594 | } 595 | 596 | /** 597 | * Return boolean value indicating whether the movie has an audio stream. 598 | * 599 | * @return boolean 600 | */ 601 | public function hasAudio() { 602 | return (boolean) preg_match(self::$REGEX_HAS_AUDIO, $this->output); 603 | } 604 | 605 | /** 606 | * Return boolean value indicating whether the movie has a video stream. 607 | * 608 | * @return boolean 609 | */ 610 | public function hasVideo() { 611 | return (boolean) preg_match(self::$REGEX_HAS_VIDEO, $this->output); 612 | } 613 | 614 | /** 615 | * Returns a frame from the movie as an FFmpegFrame object. Returns false if the frame was not found. 616 | * 617 | * * framenumber - Frame from the movie to return. If no framenumber is specified, returns the next frame of the movie. 618 | * 619 | * @param int $framenumber 620 | * @param int $height 621 | * @param int $width 622 | * @param int $quality 623 | * @return FFmpegFrame|boolean 624 | */ 625 | public function getFrame($framenumber = null, $height = null, $width = null, $quality = null) { 626 | $framePos = ($framenumber === null) ? $this->frameNumber : (((int) $framenumber) - 1); 627 | 628 | // Frame position out of range 629 | if (!is_numeric($framePos) || $framePos < 0 || $framePos > $this->getFrameCount()) { 630 | return false; 631 | } 632 | 633 | $frameTime = round((($framePos / $this->getFrameCount()) * $this->getDuration()), 4); 634 | 635 | $frame = $this->getFrameAtTime($frameTime, $height, $width, $quality); 636 | 637 | // Increment internal frame number 638 | if ($framenumber === null) { 639 | ++$this->frameNumber; 640 | } 641 | 642 | return $frame; 643 | } 644 | 645 | /** 646 | * Returns a frame from the movie as an FFmpegFrame object. Returns false if the frame was not found. 647 | * 648 | * @param float $seconds 649 | * @param int $width 650 | * @param int $height 651 | * @param int $quality 652 | * @param string $frameFilePath 653 | * @param array $output 654 | * 655 | * @throws Exception 656 | * 657 | * @return FFmpegFrame|boolean 658 | * 659 | */ 660 | public function getFrameAtTime($seconds = null, $width = null, $height = null, $quality = null, $frameFilePath = null, &$output = null) { 661 | // Set frame position for frame extraction 662 | $frameTime = ($seconds === null) ? 0 : $seconds; 663 | 664 | // time out of range 665 | if (!is_numeric($frameTime) || $frameTime < 0 || $frameTime > $this->getDuration()) { 666 | throw(new Exception('Frame time is not in range '.$frameTime.'/'.$this->getDuration().' '.$this->getFilename())); 667 | } 668 | 669 | if(is_numeric($height) && is_numeric($width)) { 670 | $image_size = ' -s '.$width.'x'.$height; 671 | } else { 672 | $image_size = ''; 673 | } 674 | 675 | if(is_numeric($quality)) { 676 | $quality = ' -qscale '.$quality; 677 | } else { 678 | $quality = ''; 679 | } 680 | 681 | $deleteTmp = false; 682 | if ($frameFilePath === null) { 683 | $frameFilePath = sys_get_temp_dir().DIRECTORY_SEPARATOR.uniqid('frame', true).'.jpg'; 684 | $deleteTmp = true; 685 | } 686 | 687 | $output = array(); 688 | 689 | // Fast and accurate way to seek. First quick-seek before input up to 690 | // a point just before the frame, and then accurately seek after input 691 | // to the exact point. 692 | // See: http://ffmpeg.org/trac/ffmpeg/wiki/Seeking%20with%20FFmpeg 693 | if ($frameTime > 30) { 694 | $seek1 = $frameTime - 30; 695 | $seek2 = 30; 696 | } else { 697 | $seek1 = 0; 698 | $seek2 = $frameTime; 699 | } 700 | 701 | exec(implode(' ', array( 702 | $this->ffmpegBinary, 703 | '-ss '.$seek1, 704 | '-i '.escapeshellarg($this->movieFile), 705 | '-f image2', 706 | '-ss '.$seek2, 707 | '-vframes 1', 708 | $image_size, 709 | $quality, 710 | escapeshellarg($frameFilePath), 711 | '2>&1', 712 | )), $output, $retVar); 713 | $output = join(PHP_EOL, $output); 714 | 715 | // Cannot write frame to the data storage 716 | if (!file_exists($frameFilePath)) { 717 | // Find error in output 718 | preg_match(self::$REGEX_ERRORS, $output, $errors); 719 | if ($errors) { 720 | throw new Exception($errors[0]); 721 | } 722 | // Default file not found error 723 | throw new Exception('TMP image not found/written '. $frameFilePath); 724 | } 725 | 726 | // Create gdimage and delete temporary image 727 | $gdImage = imagecreatefromjpeg($frameFilePath); 728 | if ($deleteTmp && is_writable($frameFilePath)) { 729 | unlink($frameFilePath); 730 | } 731 | 732 | $frame = new FFmpegFrame($gdImage, $frameTime); 733 | imagedestroy($gdImage); 734 | 735 | return $frame; 736 | } 737 | 738 | /** 739 | * Returns the next key frame from the movie as an FFmpegFrame object. Returns false if the frame was not found. 740 | * 741 | * @return FFmpegFrame|boolean 742 | */ 743 | public function getNextKeyFrame() { 744 | return $this->getFrame(); 745 | } 746 | 747 | public function __clone() { 748 | $this->provider = clone $this->provider; 749 | } 750 | 751 | public function serialize() { 752 | $data = serialize(array( 753 | $this->ffmpegBinary, 754 | $this->movieFile, 755 | $this->output, 756 | $this->frameNumber, 757 | $this->provider 758 | )); 759 | 760 | return $data; 761 | } 762 | 763 | public function unserialize($serialized) { 764 | list($this->ffmpegBinary, 765 | $this->movieFile, 766 | $this->output, 767 | $this->frameNumber, 768 | $this->provider 769 | ) = unserialize($serialized); 770 | 771 | } 772 | } 773 | -------------------------------------------------------------------------------- /adapter/ffmpeg_animated_gif.php: -------------------------------------------------------------------------------- 1 | adaptee = new FFmpegAnimatedGif($outFilePath, $width, $height, $frameRate, $loopCount); 24 | } 25 | 26 | public function addFrame(ffmpeg_frame $frame) { 27 | $this->adaptee->addFrame(new FFmpegFrame($frame->toGDImage(), $frame->getPTS())); 28 | return $this->adaptee->save(); 29 | } 30 | 31 | public function __clone() { 32 | $this->adaptee = clone $this->adaptee; 33 | } 34 | 35 | public function __destruct() { 36 | $this->adaptee = null; 37 | } 38 | } -------------------------------------------------------------------------------- /adapter/ffmpeg_frame.php: -------------------------------------------------------------------------------- 1 | adaptee = new FFmpegFrame($gdImage, $pts); 24 | } 25 | 26 | public function getWidth() { 27 | return $this->adaptee->getWidth(); 28 | } 29 | 30 | public function getHeight() { 31 | return $this->adaptee->getHeight(); 32 | } 33 | 34 | public function getPTS() { 35 | return $this->adaptee->getPTS(); 36 | } 37 | 38 | public function getPresentationTimestamp() { 39 | return $this->adaptee->getPresentationTimestamp(); 40 | } 41 | 42 | public function resize($width, $height, $cropTop = 0, $cropBottom = 0, $cropLeft = 0, $cropRight = 0) { 43 | return $this->adaptee->resize($width, $height, $cropTop, $cropBottom, $cropLeft, $cropRight); 44 | } 45 | 46 | public function crop($cropTop, $cropBottom = 0, $cropLeft = 0, $cropRight = 0) { 47 | return $this->adaptee->crop($cropTop, $cropBottom, $cropLeft, $cropRight); 48 | } 49 | 50 | public function toGDImage() { 51 | return $this->adaptee->toGDImage(); 52 | } 53 | 54 | public function __clone() { 55 | $this->adaptee = clone $this->adaptee; 56 | } 57 | 58 | public function __destruct() { 59 | $this->adaptee = null; 60 | } 61 | } -------------------------------------------------------------------------------- /adapter/ffmpeg_movie.php: -------------------------------------------------------------------------------- 1 | adaptee = new FFmpegMovie($moviePath, new FFmpegOutputProvider($this->commend, $persistent)); 26 | $this->moviePath = $moviePath; 27 | } 28 | 29 | public function getDuration() { 30 | return $this->adaptee->getDuration(); 31 | } 32 | 33 | public function getFrameCount() { 34 | return $this->adaptee->getFrameCount(); 35 | } 36 | 37 | public function getFrameRate() { 38 | return $this->adaptee->getFrameRate(); 39 | } 40 | 41 | public function getFilename() { 42 | return $this->adaptee->getFilename(); 43 | } 44 | 45 | public function getComment() { 46 | return $this->adaptee->getComment(); 47 | } 48 | 49 | public function getTitle() { 50 | return $this->adaptee->getTitle(); 51 | } 52 | 53 | public function getArtist() { 54 | return $this->adaptee->getArtist(); 55 | } 56 | 57 | public function getAuthor() { 58 | return $this->adaptee->getAuthor(); 59 | } 60 | 61 | public function getCopyright() { 62 | return $this->adaptee->getCopyright(); 63 | } 64 | 65 | public function getGenre() { 66 | return $this->adaptee->getGenre(); 67 | } 68 | 69 | public function getTrackNumber() { 70 | return $this->adaptee->getTrackNumber(); 71 | } 72 | 73 | public function getYear() { 74 | return $this->adaptee->getYear(); 75 | } 76 | 77 | public function getFrameHeight() { 78 | return $this->adaptee->getFrameHeight(); 79 | } 80 | 81 | public function getFrameWidth() { 82 | return $this->adaptee->getFrameWidth(); 83 | } 84 | 85 | public function getPixelFormat() { 86 | return $this->adaptee->getPixelFormat(); 87 | } 88 | 89 | public function getBitRate() { 90 | return $this->adaptee->getBitRate(); 91 | } 92 | 93 | public function getVideoBitRate() { 94 | return $this->adaptee->getVideoBitRate(); 95 | } 96 | 97 | public function getAudioBitRate() { 98 | return $this->adaptee->getAudioBitRate(); 99 | } 100 | 101 | public function getAudioSampleRate() { 102 | return $this->adaptee->getAudioSampleRate(); 103 | } 104 | 105 | public function getFrameNumber() { 106 | return $this->adaptee->getFrameNumber(); 107 | } 108 | 109 | public function getVideoCodec() { 110 | return $this->adaptee->getVideoCodec(); 111 | } 112 | 113 | public function getAudioCodec() { 114 | return $this->adaptee->getAudioCodec(); 115 | } 116 | 117 | public function getAudioChannels() { 118 | return $this->adaptee->getAudioChannels(); 119 | } 120 | 121 | public function hasAudio() { 122 | return $this->adaptee->hasAudio(); 123 | } 124 | 125 | public function hasVideo() { 126 | return $this->adaptee->hasVideo(); 127 | } 128 | 129 | public function getFrame($framenumber = null) { 130 | $toReturn = null; 131 | $frame = $this->adaptee->getFrame($framenumber); 132 | if ($frame != null) { 133 | $toReturn = new ffmpeg_frame($frame->toGDImage(), $frame->getPTS()); 134 | $frame = null; 135 | } 136 | 137 | return $toReturn; 138 | } 139 | 140 | public function getNextKeyFrame() { 141 | $toReturn = null; 142 | $frame = $this->adaptee->getNextKeyFrame(); 143 | if ($frame != null) { 144 | $toReturn = new ffmpeg_frame($frame->toGDImage(), $frame->getPTS()); 145 | $frame = null; 146 | } 147 | 148 | return $toReturn; 149 | } 150 | 151 | public function getSize() { 152 | return ceil(sprintf("%u",filesize($this->moviePath)) / 1024); 153 | } 154 | 155 | public function getImage($savePath,$second,$width,$height) { 156 | if($savePath && $second && $width && $height){ 157 | $second = (int) $second; 158 | $width = (int) $width; 159 | $height = (int) $height; 160 | if($second > ($this->getDuration()-2)) { 161 | die("startsecond is over video duration!"); 162 | } 163 | exec($this->commend.' -i '.$this->moviePath.' -y -f image2 -ss '.$second.' -t 0.001 -s '.$width.'x'.$height.' '.$savePath,$out,$status); 164 | if($status==0) { 165 | return true; 166 | }else { 167 | return false; 168 | } 169 | 170 | }else { 171 | die("four params is not set!"); 172 | } 173 | 174 | } 175 | 176 | public function getGif($savePath,$startsecond,$lastsecond,$width,$height) { 177 | if($savePath && $startsecond && $lastsecond && $width && $height){ 178 | $startsecond = (int) $startsecond; 179 | $lastsecond = (int) $lastsecond; 180 | $width = (int) $width; 181 | $height = (int) $height; 182 | if($startsecond > ($this->getDuration()-2)) { 183 | die("startsecond is over video duration!"); 184 | }else if(($startsecond+$lastsecond) > ($this->getDuration()-2)) { 185 | die("startsecond+lastsecond is over video duration!"); 186 | } 187 | exec($this->commend.' -i '.$this->moviePath.' -y -f gif -ss '.$startsecond.' -t '.$lastsecond.' -s '.$width.'x'.$height.' -pix_fmt rgb24 '.$savePath,$out,$status); 188 | if($status==0) { 189 | return true; 190 | }else { 191 | return false; 192 | } 193 | 194 | }else { 195 | die("five params is not set!"); 196 | } 197 | 198 | } 199 | 200 | public function __clone() { 201 | $this->adaptee = clone $this->adaptee; 202 | } 203 | 204 | public function __destruct() { 205 | $this->adaptee = null; 206 | } 207 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bingcool/php-ffmpeg", 3 | "description": "php call cli to handle the videos and audio", 4 | "keywords": ["php", "ffmpeg", "videos","audio"], 5 | "time": "2017-08-01", 6 | "license": "MIT", 7 | "type": "project", 8 | "authors": [{ 9 | "name": "bingcool", 10 | "email": "bingcoolhuang@gmail.com", 11 | "role": "Engineer of php" 12 | }], 13 | "require": { 14 | 15 | }, 16 | "autoload":{ 17 | "psr-4":{ 18 | "Phpffmpeg\\":"" 19 | } 20 | }, 21 | "repositories": { 22 | "packagist": { 23 | "type": "composer", 24 | "url": "https://packagist.phpcomposer.com" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | getDuration(); 10 | $framenum=$movie->getFrameCount(); 11 | $name=$movie->getFilename(); 12 | $width=$movie->getFrameWidth(); 13 | $height=$movie->getFrameHeight(); 14 | $comment=$movie->getVideoCodec(); 15 | 16 | $frame =$movie->getFrame(6); 17 | $frame->resize(400,400,20,20,20,50); 18 | $img=$frame->toGDImage(); 19 | imagejpeg($img,'my.jpeg'); 20 | imagedestroy($img); 21 | 22 | $size = $movie->getSize(); 23 | 24 | $image = $movie->getImage('my.jpg',6,1000,1000); 25 | 26 | $gif = $movie->getGif('uu.gif',40,5,300,300); 27 | 28 | var_dump($duration).'
'; 29 | var_dump($framenum).'
'; 30 | var_dump($name)."
"; 31 | var_dump($width.'*'.$height); 32 | var_dump($comment); 33 | ?> -------------------------------------------------------------------------------- /provider/AbstractOutputProvider.php: -------------------------------------------------------------------------------- 1 | binary = $binary; 49 | $this->persistent = $persistent; 50 | } 51 | 52 | /** 53 | * Setting movie file path 54 | * 55 | * @param string $movieFile 56 | */ 57 | public function setMovieFile($movieFile) { 58 | $this->movieFile = $movieFile; 59 | } 60 | 61 | public function serialize() { 62 | return serialize(array( 63 | $this->binary, 64 | $this->movieFile, 65 | $this->persistent 66 | )); 67 | } 68 | 69 | public function unserialize($serialized) { 70 | list( 71 | $this->binary, 72 | $this->movieFile, 73 | $this->persistent 74 | ) = unserialize($serialized); 75 | } 76 | } -------------------------------------------------------------------------------- /provider/FFmpegOutputProvider.php: -------------------------------------------------------------------------------- 1 | persistent == true && array_key_exists(get_class($this).$this->binary.$this->movieFile, self::$persistentBuffer)) { 37 | return self::$persistentBuffer[get_class($this).$this->binary.$this->movieFile]; 38 | } 39 | 40 | // File doesn't exist 41 | if (!file_exists($this->movieFile)) { 42 | throw new Exception('Movie file not found', self::$EX_CODE_FILE_NOT_FOUND); 43 | } 44 | 45 | // Get information about file from ffmpeg 46 | $output = array(); 47 | 48 | exec($this->binary.' -i '.escapeshellarg($this->movieFile).' 2>&1', $output, $retVar); 49 | $output = join(PHP_EOL, $output); 50 | 51 | // ffmpeg installed 52 | if (!preg_match('/FFmpeg version/i', $output)) { 53 | throw new Exception('FFmpeg is not installed on host server', self::$EX_CODE_NO_FFMPEG); 54 | } 55 | 56 | // Storing persistent opening 57 | if ($this->persistent == true) { 58 | self::$persistentBuffer[get_class($this).$this->binary.$this->movieFile] = $output; 59 | } 60 | 61 | return $output; 62 | } 63 | } -------------------------------------------------------------------------------- /provider/FFprobeOutputProvider.php: -------------------------------------------------------------------------------- 1 | persistent == true && array_key_exists(get_class($this).$this->binary.$this->movieFile, self::$persistentBuffer)) { 36 | return self::$persistentBuffer[get_class($this).$this->binary.$this->movieFile]; 37 | } 38 | 39 | // File doesn't exist 40 | if (!file_exists($this->movieFile)) { 41 | throw new Exception('Movie file not found', self::$EX_CODE_FILE_NOT_FOUND); 42 | } 43 | 44 | // Get information about file from ffprobe 45 | $output = array(); 46 | 47 | exec($this->binary.' '.escapeshellarg($this->movieFile).' 2>&1', $output, $retVar); 48 | $output = join(PHP_EOL, $output); 49 | 50 | // ffprobe installed 51 | if (!preg_match('/FFprobe version/i', $output)) { 52 | throw new Exception('FFprobe is not installed on host server', self::$EX_CODE_NO_FFPROBE); 53 | } 54 | 55 | // Storing persistent opening 56 | if ($this->persistent == true) { 57 | self::$persistentBuffer[get_class($this).$this->binary.$this->movieFile] = $output; 58 | } 59 | 60 | return $output; 61 | } 62 | } -------------------------------------------------------------------------------- /provider/OutputProvider.php: -------------------------------------------------------------------------------- 1 | _output = ''; 26 | parent::__construct($ffmpegBinary, $persistent); 27 | } 28 | 29 | /** 30 | * Getting parsable output from ffmpeg binary 31 | * 32 | * @return string 33 | */ 34 | public function getOutput() { 35 | 36 | // Persistent opening 37 | if ($this->persistent == true && array_key_exists(get_class($this).$this->binary.$this->movieFile, self::$persistentBuffer)) { 38 | return self::$persistentBuffer[get_class($this).$this->binary.$this->movieFile]; 39 | } 40 | 41 | return $this->_output; 42 | } 43 | 44 | /** 45 | * Setting parsable output 46 | * 47 | * @param string $output 48 | */ 49 | public function setOutput($output) { 50 | 51 | $this->_output = $output; 52 | 53 | // Storing persistent opening 54 | if ($this->persistent == true) { 55 | self::$persistentBuffer[get_class($this).$this->binary.$this->movieFile] = $output; 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 前言 2 | 3 | Phpffmpeg这个模块是基于php5写的,定义了许多可以处理的视频API,同时既可以根据实际需要不断拓展API。 4 | 5 | 可以参考:http://ffmpeg-php.sourceforge.net/doc/api/ffmpeg_frame.php 6 | 7 | 用法 8 | ####第一种方式: 9 | 模块已经实现了命名空间与require的映射结合,利用php5的命名空间和自动加载可以很方便地使用。 10 | 在项目中要建立一个名为Phpffmpeg(名称不能改,因为要与文件定义的根命名空间对应相同)的空的文件夹,把文件下载后,将里面的文件复制至新建的Phpffmpeg文件夹下。 11 | 12 | 首先要导入 FFmpegAutoloader.class.php ,在执行静态的register()注册方法 13 | require_once "path/FFmpegAutoloader.class.php"; 14 | \Phpffmpeg\FFmpegAutoloader::register(); 15 | 16 | 这个path是新建Phpffmpeg文件的路径。 17 | 18 | 其实将FFmpegAutoloader文件命名成带有class的形式,如FFmpegAutoloader.class.php,是为了兼容在thinkphp3.2以上框架的命名空间加载。 19 | 所以如果实在tp3.2以上的的框架中使用该模块的话,同样,需要在核心框架library中新建Phpffmpeg文件夹, 20 | 将下载的文件全部复制至Phpffmpeg文件夹中。那么在Controller中使用的话,就不需要require_once了,直接执行 21 | 22 | \Phpffmpeg\FFmpegAutoloader::register(); 23 | 24 | 这样已经实现了自动加载和自动注册功能,因为tp自动的自动加载功能会先执行require_once将文件包含进来。 25 | 26 | ####第二种方式: 27 | 最近引入了一个composer的psr4自动加载的,在使用中,直接include "./vendor/autoload.php",这个也是完成命名空间的自动加载的注册,不需要再引入FFmpegAutoloader.class.php 28 | 29 | API详解 30 | 31 | ffmpeg_movie部分 32 | 33 | (1)$movie = new ffmpeg_movie(String path_to_media, boolean persistent); 34 | 35 | 创建视频处理对象,第一个参数是视频url,第二个参数是以长连接打开视频资源,true或者false。 36 | 37 | (2)$movie->getDuration(); 38 | 获取视频时长,单位秒 39 | 40 | (3)$movie->getFrameCount(); 41 | 获取视频帧数 42 | 43 | (4)$movie->getFrameRate(); 44 | 获取帧率 45 | 46 | (5)$movie->getFilename(); 47 | 获取文件路径,包括文件名称 48 | 49 | (6)$movie->getComment(); 50 | 获取视频的注释 51 | 52 | (7)$movie->getTitle(); 53 | 获取主题 54 | 55 | (8)$movie->getFrameHeight(); 56 | 获取视频高度(分辨率) 57 | 58 | (9)$movie->getFrameWidth() 59 | 获取视频宽度(分辨率) 60 | 61 | (10)$movie->getPixelFormat() 62 | 63 | 获取像素格式,例如yuv420p 64 | 65 | (11)$movie->getFrameNumber(); 66 | 获取当前帧索引 67 | 68 | (12)$movie->getFrame([Integer framenumber]); 69 | 70 | 获取对应的帧,返回的对象将作为new ffmpeg_frame(Resource gd_image)的实例化对象。 71 | Returns a frame from the movie as an ffmpeg_frame object. Returns false if the frame was not found. 72 | 73 | (13)$movie->getNextKeyFrame(); 74 | 75 | 获取下一帧 76 | Returns the next key frame from the movie as an ffmpeg_frame object. Returns false if the frame was not found. 77 | 78 | (14)$movie->getSize(); 79 | 获取视频的大小,单位kb 80 | 81 | (15)$movie->getImage($savePath,$second,$width,$height); 82 | 获取视频的某个时间的帧图片(关键帧) 83 | $savePath 图片保存的路径,包含图片的名称。例如path/my.jpg; 84 | $second 视频对应的秒数,int。 85 | $width 图片的宽度,int。 86 | $height 图片的高度,int。 87 | 88 | 执行此函数成功时返回true,失败返回false。 89 | 90 | (16)$movie->getGif($savePath,$startsecond,$lastsecond,$width,$height); 91 | 截取视频的某个时间开始至某个时间的gif动态图。 92 | $savePath 图片保存的路径,包含图片的名称。例如path/my.gif; 93 | $startsecond 视频开始秒数,int。 94 | $lastsecond 持续的秒数,即gif的时长。 95 | $width gif的宽度,int。 96 | $height gif的高度,int。 97 | 98 | 执行此函数成功时返回true,失败返回false。 99 | 100 | 101 | 102 | 103 | ffmpeg_frame部分 104 | 105 | (1)$frame = new ffmpeg_frame(Resource gd_image); 106 | 107 | 参数是一个图片资源对象(需要自己创建),如果执行$movie->getFrame([Integer framenumber]);择返回的就是一个相当于new ffmpeg_frame(Resource gd_image);的实例化对象; 108 | 109 | (2)$frame->getWidth(); 110 | 111 | 返回宽度 112 | 113 | (3)$frame->getHeight(); 114 | 115 | 返回高度 116 | 117 | (3)$frame->getPTS(); 118 | 119 | (4)$frame->getPresentationTimestamp(); 120 | 121 | (5)$frame->resize(Integer width, Integer height [, Integer crop_top [, Integer crop_bottom [, Integer crop_left [, Integer crop_right ]]]]); 122 | 123 | (6)$frame->crop(Integer crop_top [, Integer crop_bottom [, Integer crop_left [, Integer crop_right ]]]); 124 | 125 | (7)$frame->toGDImage(); 126 | 127 | 返回一个dg对象。 128 | 129 | 例如: 130 | 131 | $frame = $movie->getFrame(6);//返回ffmpeg_frame的对象 132 | $frame->resize(400,400,20,20,20,50); 133 | $img = $frame->toGDImage(); 134 | //将gd对象保存为图片 135 | imagejpeg($img,'my.jpeg'); 136 | imagedestroy($img); 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /vendor/autoload.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Composer\Autoload; 14 | 15 | /** 16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. 17 | * 18 | * $loader = new \Composer\Autoload\ClassLoader(); 19 | * 20 | * // register classes with namespaces 21 | * $loader->add('Symfony\Component', __DIR__.'/component'); 22 | * $loader->add('Symfony', __DIR__.'/framework'); 23 | * 24 | * // activate the autoloader 25 | * $loader->register(); 26 | * 27 | * // to enable searching the include path (eg. for PEAR packages) 28 | * $loader->setUseIncludePath(true); 29 | * 30 | * In this example, if you try to use a class in the Symfony\Component 31 | * namespace or one of its children (Symfony\Component\Console for instance), 32 | * the autoloader will first look for the class under the component/ 33 | * directory, and it will then fallback to the framework/ directory if not 34 | * found before giving up. 35 | * 36 | * This class is loosely based on the Symfony UniversalClassLoader. 37 | * 38 | * @author Fabien Potencier 39 | * @author Jordi Boggiano 40 | * @see http://www.php-fig.org/psr/psr-0/ 41 | * @see http://www.php-fig.org/psr/psr-4/ 42 | */ 43 | class ClassLoader 44 | { 45 | // PSR-4 46 | private $prefixLengthsPsr4 = array(); 47 | private $prefixDirsPsr4 = array(); 48 | private $fallbackDirsPsr4 = array(); 49 | 50 | // PSR-0 51 | private $prefixesPsr0 = array(); 52 | private $fallbackDirsPsr0 = array(); 53 | 54 | private $useIncludePath = false; 55 | private $classMap = array(); 56 | private $classMapAuthoritative = false; 57 | private $missingClasses = array(); 58 | private $apcuPrefix; 59 | 60 | public function getPrefixes() 61 | { 62 | if (!empty($this->prefixesPsr0)) { 63 | return call_user_func_array('array_merge', $this->prefixesPsr0); 64 | } 65 | 66 | return array(); 67 | } 68 | 69 | public function getPrefixesPsr4() 70 | { 71 | return $this->prefixDirsPsr4; 72 | } 73 | 74 | public function getFallbackDirs() 75 | { 76 | return $this->fallbackDirsPsr0; 77 | } 78 | 79 | public function getFallbackDirsPsr4() 80 | { 81 | return $this->fallbackDirsPsr4; 82 | } 83 | 84 | public function getClassMap() 85 | { 86 | return $this->classMap; 87 | } 88 | 89 | /** 90 | * @param array $classMap Class to filename map 91 | */ 92 | public function addClassMap(array $classMap) 93 | { 94 | if ($this->classMap) { 95 | $this->classMap = array_merge($this->classMap, $classMap); 96 | } else { 97 | $this->classMap = $classMap; 98 | } 99 | } 100 | 101 | /** 102 | * Registers a set of PSR-0 directories for a given prefix, either 103 | * appending or prepending to the ones previously set for this prefix. 104 | * 105 | * @param string $prefix The prefix 106 | * @param array|string $paths The PSR-0 root directories 107 | * @param bool $prepend Whether to prepend the directories 108 | */ 109 | public function add($prefix, $paths, $prepend = false) 110 | { 111 | if (!$prefix) { 112 | if ($prepend) { 113 | $this->fallbackDirsPsr0 = array_merge( 114 | (array) $paths, 115 | $this->fallbackDirsPsr0 116 | ); 117 | } else { 118 | $this->fallbackDirsPsr0 = array_merge( 119 | $this->fallbackDirsPsr0, 120 | (array) $paths 121 | ); 122 | } 123 | 124 | return; 125 | } 126 | 127 | $first = $prefix[0]; 128 | if (!isset($this->prefixesPsr0[$first][$prefix])) { 129 | $this->prefixesPsr0[$first][$prefix] = (array) $paths; 130 | 131 | return; 132 | } 133 | if ($prepend) { 134 | $this->prefixesPsr0[$first][$prefix] = array_merge( 135 | (array) $paths, 136 | $this->prefixesPsr0[$first][$prefix] 137 | ); 138 | } else { 139 | $this->prefixesPsr0[$first][$prefix] = array_merge( 140 | $this->prefixesPsr0[$first][$prefix], 141 | (array) $paths 142 | ); 143 | } 144 | } 145 | 146 | /** 147 | * Registers a set of PSR-4 directories for a given namespace, either 148 | * appending or prepending to the ones previously set for this namespace. 149 | * 150 | * @param string $prefix The prefix/namespace, with trailing '\\' 151 | * @param array|string $paths The PSR-4 base directories 152 | * @param bool $prepend Whether to prepend the directories 153 | * 154 | * @throws \InvalidArgumentException 155 | */ 156 | public function addPsr4($prefix, $paths, $prepend = false) 157 | { 158 | if (!$prefix) { 159 | // Register directories for the root namespace. 160 | if ($prepend) { 161 | $this->fallbackDirsPsr4 = array_merge( 162 | (array) $paths, 163 | $this->fallbackDirsPsr4 164 | ); 165 | } else { 166 | $this->fallbackDirsPsr4 = array_merge( 167 | $this->fallbackDirsPsr4, 168 | (array) $paths 169 | ); 170 | } 171 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) { 172 | // Register directories for a new namespace. 173 | $length = strlen($prefix); 174 | if ('\\' !== $prefix[$length - 1]) { 175 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 176 | } 177 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 178 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 179 | } elseif ($prepend) { 180 | // Prepend directories for an already registered namespace. 181 | $this->prefixDirsPsr4[$prefix] = array_merge( 182 | (array) $paths, 183 | $this->prefixDirsPsr4[$prefix] 184 | ); 185 | } else { 186 | // Append directories for an already registered namespace. 187 | $this->prefixDirsPsr4[$prefix] = array_merge( 188 | $this->prefixDirsPsr4[$prefix], 189 | (array) $paths 190 | ); 191 | } 192 | } 193 | 194 | /** 195 | * Registers a set of PSR-0 directories for a given prefix, 196 | * replacing any others previously set for this prefix. 197 | * 198 | * @param string $prefix The prefix 199 | * @param array|string $paths The PSR-0 base directories 200 | */ 201 | public function set($prefix, $paths) 202 | { 203 | if (!$prefix) { 204 | $this->fallbackDirsPsr0 = (array) $paths; 205 | } else { 206 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; 207 | } 208 | } 209 | 210 | /** 211 | * Registers a set of PSR-4 directories for a given namespace, 212 | * replacing any others previously set for this namespace. 213 | * 214 | * @param string $prefix The prefix/namespace, with trailing '\\' 215 | * @param array|string $paths The PSR-4 base directories 216 | * 217 | * @throws \InvalidArgumentException 218 | */ 219 | public function setPsr4($prefix, $paths) 220 | { 221 | if (!$prefix) { 222 | $this->fallbackDirsPsr4 = (array) $paths; 223 | } else { 224 | $length = strlen($prefix); 225 | if ('\\' !== $prefix[$length - 1]) { 226 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 227 | } 228 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 229 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 230 | } 231 | } 232 | 233 | /** 234 | * Turns on searching the include path for class files. 235 | * 236 | * @param bool $useIncludePath 237 | */ 238 | public function setUseIncludePath($useIncludePath) 239 | { 240 | $this->useIncludePath = $useIncludePath; 241 | } 242 | 243 | /** 244 | * Can be used to check if the autoloader uses the include path to check 245 | * for classes. 246 | * 247 | * @return bool 248 | */ 249 | public function getUseIncludePath() 250 | { 251 | return $this->useIncludePath; 252 | } 253 | 254 | /** 255 | * Turns off searching the prefix and fallback directories for classes 256 | * that have not been registered with the class map. 257 | * 258 | * @param bool $classMapAuthoritative 259 | */ 260 | public function setClassMapAuthoritative($classMapAuthoritative) 261 | { 262 | $this->classMapAuthoritative = $classMapAuthoritative; 263 | } 264 | 265 | /** 266 | * Should class lookup fail if not found in the current class map? 267 | * 268 | * @return bool 269 | */ 270 | public function isClassMapAuthoritative() 271 | { 272 | return $this->classMapAuthoritative; 273 | } 274 | 275 | /** 276 | * APCu prefix to use to cache found/not-found classes, if the extension is enabled. 277 | * 278 | * @param string|null $apcuPrefix 279 | */ 280 | public function setApcuPrefix($apcuPrefix) 281 | { 282 | $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; 283 | } 284 | 285 | /** 286 | * The APCu prefix in use, or null if APCu caching is not enabled. 287 | * 288 | * @return string|null 289 | */ 290 | public function getApcuPrefix() 291 | { 292 | return $this->apcuPrefix; 293 | } 294 | 295 | /** 296 | * Registers this instance as an autoloader. 297 | * 298 | * @param bool $prepend Whether to prepend the autoloader or not 299 | */ 300 | public function register($prepend = false) 301 | { 302 | spl_autoload_register(array($this, 'loadClass'), true, $prepend); 303 | } 304 | 305 | /** 306 | * Unregisters this instance as an autoloader. 307 | */ 308 | public function unregister() 309 | { 310 | spl_autoload_unregister(array($this, 'loadClass')); 311 | } 312 | 313 | /** 314 | * Loads the given class or interface. 315 | * 316 | * @param string $class The name of the class 317 | * @return bool|null True if loaded, null otherwise 318 | */ 319 | public function loadClass($class) 320 | { 321 | if ($file = $this->findFile($class)) { 322 | includeFile($file); 323 | 324 | return true; 325 | } 326 | } 327 | 328 | /** 329 | * Finds the path to the file where the class is defined. 330 | * 331 | * @param string $class The name of the class 332 | * 333 | * @return string|false The path if found, false otherwise 334 | */ 335 | public function findFile($class) 336 | { 337 | // class map lookup 338 | if (isset($this->classMap[$class])) { 339 | return $this->classMap[$class]; 340 | } 341 | if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 342 | return false; 343 | } 344 | if (null !== $this->apcuPrefix) { 345 | $file = apcu_fetch($this->apcuPrefix.$class, $hit); 346 | if ($hit) { 347 | return $file; 348 | } 349 | } 350 | 351 | $file = $this->findFileWithExtension($class, '.php'); 352 | 353 | // Search for Hack files if we are running on HHVM 354 | if (false === $file && defined('HHVM_VERSION')) { 355 | $file = $this->findFileWithExtension($class, '.hh'); 356 | } 357 | 358 | if (null !== $this->apcuPrefix) { 359 | apcu_add($this->apcuPrefix.$class, $file); 360 | } 361 | 362 | if (false === $file) { 363 | // Remember that this class does not exist. 364 | $this->missingClasses[$class] = true; 365 | } 366 | 367 | return $file; 368 | } 369 | 370 | private function findFileWithExtension($class, $ext) 371 | { 372 | // PSR-4 lookup 373 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 374 | 375 | $first = $class[0]; 376 | if (isset($this->prefixLengthsPsr4[$first])) { 377 | $subPath = $class; 378 | while (false !== $lastPos = strrpos($subPath, '\\')) { 379 | $subPath = substr($subPath, 0, $lastPos); 380 | $search = $subPath.'\\'; 381 | if (isset($this->prefixDirsPsr4[$search])) { 382 | foreach ($this->prefixDirsPsr4[$search] as $dir) { 383 | $length = $this->prefixLengthsPsr4[$first][$search]; 384 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { 385 | return $file; 386 | } 387 | } 388 | } 389 | } 390 | } 391 | 392 | // PSR-4 fallback dirs 393 | foreach ($this->fallbackDirsPsr4 as $dir) { 394 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 395 | return $file; 396 | } 397 | } 398 | 399 | // PSR-0 lookup 400 | if (false !== $pos = strrpos($class, '\\')) { 401 | // namespaced class name 402 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 403 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 404 | } else { 405 | // PEAR-like class name 406 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 407 | } 408 | 409 | if (isset($this->prefixesPsr0[$first])) { 410 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 411 | if (0 === strpos($class, $prefix)) { 412 | foreach ($dirs as $dir) { 413 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 414 | return $file; 415 | } 416 | } 417 | } 418 | } 419 | } 420 | 421 | // PSR-0 fallback dirs 422 | foreach ($this->fallbackDirsPsr0 as $dir) { 423 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 424 | return $file; 425 | } 426 | } 427 | 428 | // PSR-0 include paths. 429 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 430 | return $file; 431 | } 432 | 433 | return false; 434 | } 435 | } 436 | 437 | /** 438 | * Scope isolated include. 439 | * 440 | * Prevents access to $this/self from included files. 441 | */ 442 | function includeFile($file) 443 | { 444 | include $file; 445 | } 446 | -------------------------------------------------------------------------------- /vendor/composer/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) Nils Adermann, Jordi Boggiano 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is furnished 9 | to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /vendor/composer/autoload_classmap.php: -------------------------------------------------------------------------------- 1 | array($baseDir . '/'), 10 | ); 11 | -------------------------------------------------------------------------------- /vendor/composer/autoload_real.php: -------------------------------------------------------------------------------- 1 | = 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); 27 | if ($useStaticLoader) { 28 | require_once __DIR__ . '/autoload_static.php'; 29 | 30 | call_user_func(\Composer\Autoload\ComposerStaticInitc3f9056e0105fcaf057b2acc4e6da117::getInitializer($loader)); 31 | } else { 32 | $map = require __DIR__ . '/autoload_namespaces.php'; 33 | foreach ($map as $namespace => $path) { 34 | $loader->set($namespace, $path); 35 | } 36 | 37 | $map = require __DIR__ . '/autoload_psr4.php'; 38 | foreach ($map as $namespace => $path) { 39 | $loader->setPsr4($namespace, $path); 40 | } 41 | 42 | $classMap = require __DIR__ . '/autoload_classmap.php'; 43 | if ($classMap) { 44 | $loader->addClassMap($classMap); 45 | } 46 | } 47 | 48 | $loader->register(true); 49 | 50 | return $loader; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /vendor/composer/autoload_static.php: -------------------------------------------------------------------------------- 1 | 11 | array ( 12 | 'Phpffmpeg\\' => 10, 13 | ), 14 | ); 15 | 16 | public static $prefixDirsPsr4 = array ( 17 | 'Phpffmpeg\\' => 18 | array ( 19 | 0 => __DIR__ . '/../..' . '/', 20 | ), 21 | ); 22 | 23 | public static function getInitializer(ClassLoader $loader) 24 | { 25 | return \Closure::bind(function () use ($loader) { 26 | $loader->prefixLengthsPsr4 = ComposerStaticInitc3f9056e0105fcaf057b2acc4e6da117::$prefixLengthsPsr4; 27 | $loader->prefixDirsPsr4 = ComposerStaticInitc3f9056e0105fcaf057b2acc4e6da117::$prefixDirsPsr4; 28 | 29 | }, null, ClassLoader::class); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /vendor/composer/installed.json: -------------------------------------------------------------------------------- 1 | [] 2 | --------------------------------------------------------------------------------