├── 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 |
--------------------------------------------------------------------------------