FFmpegPHP get thumbnail from external URL
Asked Answered
P

2

6

I'm trying to create thumbnails from external videos, mostly MP4s and FLVs. I'm using FFmpegPHP. I already have the thumbnail generation working fine, however, I need to load the video entirely on my server first. Would it be possible to stream only a small part of the video then extract the thumbnail from that?

Here's the code I have so far:

require_once PRIV . 'Vendor/FFmpegPHP/FFmpegAutoloader.php';

// Download the whole video.
$video = file_get_contents($_PUT['video']);
$file = 'path_to_cache';
file_put_contents($file, $video);

$movie = new FFmpegMovie($file);

// Generate the thumbnail.
$thumb = $movie->getFrame($movie->getFrameCount() / 2);
$thumb->resize(320, 240);
imagejpeg($thumb->toGDImage(), 'path_to_thumb');

Anyone has a suggestion?

EDIT

As Brad suggested, here is the updated code:

$file = CACHE . 'moodboard_video_' . rand();
$fh = fopen($file, 'w');
$size = 0;

curl_setopt($ch, CURLOPT_URL, $_PUT['video']);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($ch, $data) use($fh, &$size){
    $length = fwrite($fh, $data);

    if($length === FALSE) {
        return 0;
    } else {
        $size += $length;
    }

    // Downloads 1MB.
    return $size < 1024 * 1024 * XXXXXX ? $length : 0;
});

curl_exec($ch);

fclose($fh);
curl_close($ch);

// Create the thumbnail.
$thumb = $movie->getFrame(XXXXXX);
$thumb->resize(static::DEFAULT_THUMBNAIL_WIDTH, $thumb->getHeight() / $thumb->getWidth() * static::DEFAULT_THUMBNAIL_WIDTH);
$image = $thumb->toGDImage();
imagejpeg($image, PRIV . static::THUMBNAILS_PATH . $item->getLastInsertIdentifier() . '_' . static::DEFAULT_THUMBNAIL_WIDTH);
Plenum answered 27/9, 2012 at 11:45 Comment(0)
V
3

FFMPEG is very good about working with broken streams. Because of this, I think you should try downloading the first few megs of that remote media, and try to get a thumbnail from the incomplete file.

First, drop file_get_contents() and use cURL. You can set the CURLOPT_WRITEFUNCTION option to a custom function of yours that writes to a temporary file on disk, chunk by chunk. When you've received enough data, return 0 from your function, and cURL will stop downloading. You will have to experiment to see what the optimum size is. If you get too little data, you will only have the earliest frames to work with, or no frames at all. Too late, and you are wasting bandwidth.

For some container types, file information is at the end of the file. For those, I do not have a suggestion for you. Short of writing your own decoder and splicing on the info at the end with something from the beginning, I do not know of a way to do it without downloading the whole file.

Villalpando answered 1/10, 2012 at 13:45 Comment(4)
So far, I can extract a thumbnail without having to download the whole thing. However, I was hoping to get the middle(ish) frame if possible. Can I just advance my stream somehow and then write 0 until I hit the middle of the stream, and just get information around that. Also, is there any way to calculate how long I need to download a file to get information from it?Plenum
@jValdron, For MPEG streams, you can often just jump right in the middle. Many other codecs and containers allow the same. You can use a range request to specify what bytes you want. See also: https://mcmap.net/q/217522/-sample-http-range-request-session Most servers support it. To get the length, you can do a HEAD request (where supported), or GET and then cancel after you receive the length header.Villalpando
Thanks for your time with this Brad. Now, I just need to figure out how to get the middle frame :)Plenum
@jValdron, Use a range request and get a chunk of data from the middle of the file. Hopefully, you'll get a container/format that FFMPEG can parse with a random needle drop like that.Villalpando
I
0

running code with PHP 5,ffmpeg and CURL with class based structure is as below:

require_once  'PHP_CURFN/ffmpeg-php-master/FFmpegFrame.php';
  require_once  'PHP_CURFN/ffmpeg-php-master/FFmpegAutoloader.php';

class MyVideoTest{

private $fh="";
private $size=0;
private $ch="";

public function __construct(){

                $video = 'http://[hosturl]/video/41a669911fd635167475d7530bffcc9f.mp4';
                $file = 'PHP_CURFN/cache/tmp_video_' . rand();


                $this->ch = curl_init();
                $this->fh = fopen ($file, 'w+');
                $this->ch = curl_init($video);

                $this->size = 0;
                curl_setopt($this->ch, CURLOPT_URL, $video);
                curl_setopt($this->ch, CURLOPT_HEADER, 0);


                curl_setopt($this->ch, CURLOPT_WRITEFUNCTION, array($this, "myfunction"));

                curl_exec($this->ch);

                fclose($this->fh);
                curl_close($this->ch);
                $movie = new FFmpegMovie($file);
                // Create the thumbnail.

                $thumb = $movie->getFrame(2);
                $thumb->resize(320, 240);
                $image = $thumb->toGDImage();
                imagejpeg($image, 'PHP_CURFN/cache' . $item->getLastInsertIdentifier() . '_' . 320);

}
function myfunction($ch, $data)     {
        $length = fwrite($this->fh, $data);
        $size=&$this->size;

        if($length === FALSE) {
            return 0;
        } else {
            $size += $length;
        }

        // Downloads 1MB.

        return $size < 1024 * 1024 *1 ? $length : 0;
} }


$MyVideoTest= new MyVideoTest();
pr($MyVideoTest);
exit;
Inject answered 22/1, 2013 at 6:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.