107 lines
3.0 KiB
PHP
107 lines
3.0 KiB
PHP
<?php
|
|
|
|
namespace App\Media\QuickTime;
|
|
|
|
use Illuminate\Support\Collection;
|
|
|
|
trait FindQuicktimeAtoms
|
|
{
|
|
protected function get_atoms(string $class_prefix,string $unknown,int $offset,int $size,string $atom=NULL,string $passthru=NULL,\Closure $callback=NULL): Collection
|
|
{
|
|
// List of atoms
|
|
// File Type atom should proceed move, movie data, preview. free space atoms, if it exists.
|
|
// Can assume it doesnt exist if those other atoms are discovered first.
|
|
// Atoms can be present in any order
|
|
// Must contain a movie atom.
|
|
// Movie data atoms can exceed 2^32.
|
|
|
|
$rp = 0;
|
|
if (! $atom) {
|
|
$fh = fopen($this->filename,'r');
|
|
fseek($fh,$offset);
|
|
}
|
|
|
|
$result = collect();
|
|
|
|
$prelude = 8;
|
|
while ($rp < $size) {
|
|
$read = $atom ? substr($atom,$rp,$prelude) : fread($fh,$prelude);
|
|
$header = unpack('Nsize/a4atom',$read);
|
|
$rp += strlen($read);
|
|
|
|
// For mdat atoms, if size = 1, the true size is in the 64 bit extended header, in the next 8 bytes
|
|
if (($header['atom'] === 'mdat') && ($header['size'] === 1)) {
|
|
$eheader = unpack('Jsize',fread($fh,8));
|
|
$rp += 8;
|
|
$prelude = 16;
|
|
|
|
$header['size'] = $eheader['size'];
|
|
}
|
|
|
|
// Load our class for this supplier
|
|
$class = $class_prefix.$header['atom'];
|
|
|
|
$data = $atom
|
|
? substr($atom,$rp,$header['size']-$prelude)
|
|
: ($header['size']-$prelude && ($header['size']-$prelude <= self::record_size) ? fread($fh,$header['size']-$prelude) : NULL);
|
|
|
|
if ($header['size'] >= $prelude) {
|
|
$o = class_exists($class)
|
|
? new $class($offset+$rp,$header['size']-$prelude,$this->filename,$data,$passthru)
|
|
: new $unknown($offset+$rp,$header['size']-$prelude,$this->filename,$header['atom'],$data);
|
|
|
|
$result->push($o);
|
|
|
|
$rp += $header['size']-$prelude;
|
|
|
|
// Only need to seek if we didnt read all the data
|
|
if ((! $atom) && ($header['size']-8 > static::record_size))
|
|
fseek($fh,$offset+$rp);
|
|
|
|
} else {
|
|
dd([get_class($this) => $data]);
|
|
}
|
|
|
|
// Work out if data from the last atom next to be passed onto the next one
|
|
if ($callback)
|
|
$passthru = $callback($o);
|
|
}
|
|
|
|
if (! $atom) {
|
|
fclose($fh);
|
|
unset($fh);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Recursively look through our object hierarchy of atoms for a specific atom class
|
|
*
|
|
* @param string $subatom
|
|
* @param int|NULL $expect
|
|
* @param int $depth
|
|
* @return Collection|Atom|NULL
|
|
* @throws \Exception
|
|
*/
|
|
public function find_atoms(string $subatom,?int $expect=NULL,int $depth=100): Collection|Atom|NULL
|
|
{
|
|
if (! isset($this->atoms) || ($depth < 0))
|
|
return NULL;
|
|
|
|
$subatomo = $this->atoms->filter(fn($item)=>get_class($item)===$subatom);
|
|
|
|
$subatomo = $subatomo
|
|
->merge($this->atoms->map(fn($item)=>$item->find_atoms($subatom,NULL,$depth-1))
|
|
->filter(fn($item)=>$item ? $item->count() : NULL)
|
|
->flatten());
|
|
|
|
if (! $subatomo->count())
|
|
return $subatomo;
|
|
|
|
if ($expect && ($subatomo->count() !== $expect))
|
|
throw new \Exception(sprintf('! Expected %d subatoms of %s, but have %d',$expect,$subatom,$subatomo->count()));
|
|
|
|
return ($expect === 1) ? $subatomo->pop() : $subatomo;
|
|
}
|
|
} |