Wyciąganie klatek z filmów za pomocą PHP

Tytuł brzmi nieco niedo­rzecz­nie, co? Język stwo­rzony do tworze­nia stron WWW posłuży nam do mani­pu­lo­wa­nia plikami multi­me­dial­nymi. Za sprawą Vladi­mira Goreja i jego skryptu FFmpeg PHP opubli­ko­wa­nego na phpclasses.org jest to możliwe.

Skrypt korzy­sta z biblio­tek ffmpeg (wersji na Windowsa chyba nie ma). Mając duży plik na serwe­rze możemy wycią­gać z niego takie rzeczy jak czas trwa­nia, liczba klatek na sek., szero­kość i wyso­kość, bitrate i inne. Najistot­niej­szą dla nas funk­cją jest możli­wość wycią­gnię­cia danej klatki z pliku i zapi­sa­nie go na dysku w postaci pliku jpg.

Zanim zacznie­cie cokol­wiek z tym robić proszę o popra­wie­nie linii 635 pliku FFmpegMovie.php na

exec('ffmpeg -ss '.$frameTime.' -i '.escapeshellarg($this->movieFile).' -vframes 1 -sameq '.$frameFilePath.' 2>&1');

Zgła­sza­łem to auto­rowi kilka dni temu, ale na razie bez odzewu. Okazuje się, że wywo­łu­jąc ffmpeg z konsoli, kolej­ność poda­wa­nych atry­bu­tów ma kolo­salne znacze­nie w szyb­ko­ści dzia­ła­nia. Jeżeli wycią­gamy klatkę z początku filmu to pal sześć, gorzej jak gdzieś z końca!

Sama obsługa skryptu jest bajecz­nie prosta: tworzymy sobie obiekt FFmpegMovie i używamy funk­cji getFrame().

require_once('./libs/FFmpegMovie.php');
require_once('./libs/FFmpegFrame.php');
 
$ffMovie = new FFmpegMovie('./jakis-plik.avi');
$frameNum = floor($ffMovie->getFrameCount() / 2); // wyciągamy ze środka pliku
$ffMovie->getFrame($frameNum);

Mamy tutaj niestety kilka mankamentów:

  • klatka nie jest takiej samej jako­ści jak oryginał,
  • klatkę trzeba najpierw wyrzu­cić jako GD reso­urce za pomocą metody toGDImage() i potem zapi­sać funk­cją imagejpeg(),
  • nie wiemy czy pobrana klatka jest cała czarna (czyli bezużyteczna),
  • nie mamy ładnego wyświe­tla­nia z którego momentu filmu pobrano klatkę

Na pierw­szy zarzut nic nie pomo­żemy — jest to wbudo­wane ogra­ni­cze­nie w ffmpeg, ale za to na resztę można coś zara­dzić — stwo­rzyć klasę SpiechuSmartFrame dzie­dzi­czącą po FFmpegFrame.

class SpiechuSmartFrame extends FFmpegFrame {
 
    protected $isBlack = null;
 
    /**
     * @return bool
     */
    public function isBlack() {
        if ($this->isBlack == null) {
            $frame = new BlackFrameFinder($this->toGDImage());
            $this->isBlack = $frame->isBlack();
        }
        return $this->isBlack;
    }
 
    /**
     * @return string Returns frame time in hh:mm:ss format
     */
    public function getFrameTimeString() {
        return gmdate("H:i:s",$this->getPts());
    }
 
    /**
     * @param string $filename
     * @param int $quality quality of jpg file (100 best, 0 worst)
     * @return bool true if saving to file was success
     */
    public function saveToJpg($filename, $quality = 85) {
        return imagejpeg($this->toGDImage(), $filename, $quality);
    }
}

Funk­cja isBlack() może być dla Was zapewne niezro­zu­miała. Kod klasy BlackFrameFinder jest na tyle długi, że wkle­iłem go do Paste­bin. Klasa wykrywa czy obra­zek jest czarny w ten sposób:

  • skaluje obra­zek źródłowy na kwadrat 24×24 px,
  • zlicza kolory wszyst­kich pikseli i prze­li­cza je na kolor w skali szarości,
  • zlicza kolory od czar­nego do „prawie czar­nego” (ja przy­ją­łem indeksy kolo­rów od 0–20),
  • porów­nuje czy prze­kro­czono limit czar­nych punk­tów (przy­ją­łem więcej niż 70% cało­ści obrazka)

Klasa oczy­wi­ście nie jest „przy­spa­wana” do wykry­wa­nia wyłącz­nie czar­nych klatek z filmów. Działa na dowol­nych obra­zach. Jak zwykle może­cie robić sobie z nią co chce­cie pod warun­kiem zosta­wie­nia komen­ta­rza pochwal­nego i klik­nię­cia na „Lubię to” :-)

Nie zapo­mnij­cie zmody­fi­ko­wać orygi­nal­nego pliku FFmpegMovie.php w linii 651 na

$frame = new SpiechuSmartFrame($gdImage, $frameTime);

Podobne wpisy:

  1. 2 sposoby pozy­ski­wa­nia i konwer­sji filmów z YouTube i innych
  2. Rozsze­rza­nie możli­wo­ści Smarty za pomocą pluginów
  3. PDO poprzez Depen­dency Injec­tion Conta­iner [cz. 2/2]
  4. Bezstratna opty­ma­li­za­cja plików JPG

One comment

  • 6 września 2010 - 22:23 | Permalink
  • Dodaj komentarz

    Twój adres e-mail nie zostanie opublikowany.

    Możesz użyć następujących tagów oraz atrybutów HTML-a: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <p> <pre lang="" line="" escaped=""> <q cite=""> <strike> <strong>