144 lines
2.8 KiB
PHP
144 lines
2.8 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace App\Media\QuickTime;
|
||
|
|
||
|
use Illuminate\Support\Collection;
|
||
|
|
||
|
use App\Media\QuickTime\Atoms\moov\{mvhd,trak};
|
||
|
use App\Traits\FindQuicktimeAtoms;
|
||
|
|
||
|
abstract class Atom
|
||
|
{
|
||
|
use FindQuicktimeAtoms;
|
||
|
|
||
|
protected const record_size = 16384;
|
||
|
|
||
|
protected const BLOCK_SIZE = 4096;
|
||
|
|
||
|
protected int $offset;
|
||
|
protected int $size;
|
||
|
protected string $filename;
|
||
|
|
||
|
private mixed $fh;
|
||
|
private int $fp;
|
||
|
protected Collection $cache;
|
||
|
protected Collection $atoms;
|
||
|
|
||
|
public function __construct(int $offset,int $size,string $filename)
|
||
|
{
|
||
|
$this->offset = $offset;
|
||
|
|
||
|
// Quick validation
|
||
|
if ($size < 0)
|
||
|
throw new \Exception(sprintf('Atom cannot be negative. (%d)',$size));
|
||
|
|
||
|
$this->size = $size;
|
||
|
$this->filename = $filename;
|
||
|
$this->cache = collect();
|
||
|
}
|
||
|
|
||
|
public function __get(string $key): mixed
|
||
|
{
|
||
|
switch ($key) {
|
||
|
// Create time is in the MOOV/MVHD atom
|
||
|
case 'creation_date':
|
||
|
case 'duration':
|
||
|
case 'preferred_rate':
|
||
|
case 'preferred_volume':
|
||
|
$subatom = $this->find_atoms(mvhd::class,1);
|
||
|
|
||
|
return $subatom->{$key};
|
||
|
|
||
|
// Height is in the moov/trak/tkhd attom
|
||
|
case 'height':
|
||
|
// Width is in the moov/trak/tkhd attom
|
||
|
case 'width':
|
||
|
$atom = $this->find_atoms(trak::class);
|
||
|
|
||
|
return $atom->map(fn($item)=>$item->{$key})->filter()->max();
|
||
|
|
||
|
// Signatures are calculated by the sha of the MDAT atom.
|
||
|
case 'signature':
|
||
|
return $this->signature();
|
||
|
|
||
|
default:
|
||
|
throw new \Exception('Unknown key: '.$key);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected function data(): string
|
||
|
{
|
||
|
// Quick validation
|
||
|
if ($this->size > self::record_size)
|
||
|
throw new \Exception(sprintf('Refusing to read more than %d of data',self::record_size));
|
||
|
|
||
|
$data = '';
|
||
|
if ($this->fopen()) {
|
||
|
while (! is_null($read=$this->fread()))
|
||
|
$data .= $read;
|
||
|
|
||
|
$this->fclose();
|
||
|
}
|
||
|
|
||
|
return $data;
|
||
|
}
|
||
|
|
||
|
protected function fclose(): bool
|
||
|
{
|
||
|
fclose($this->fh);
|
||
|
unset($this->fh);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Open the file and seek to the atom
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
protected function fopen(): bool
|
||
|
{
|
||
|
$this->fh = fopen($this->filename,'r');
|
||
|
fseek($this->fh,$this->offset);
|
||
|
$this->fp = 0;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Read the atom from the file
|
||
|
*
|
||
|
* @param int $size
|
||
|
* @return string|NULL
|
||
|
*/
|
||
|
protected function fread(int $size=4096): ?string
|
||
|
{
|
||
|
if ($this->fp === $this->size)
|
||
|
return NULL;
|
||
|
|
||
|
if ($this->fp+$size > $this->size)
|
||
|
$size = $this->size-$this->fp;
|
||
|
|
||
|
$read = fread($this->fh,$size);
|
||
|
$this->fp += $size;
|
||
|
|
||
|
return $read;
|
||
|
}
|
||
|
|
||
|
protected function fseek(int $offset): int
|
||
|
{
|
||
|
$this->fp = $offset;
|
||
|
|
||
|
return fseek($this->fh,$this->offset+$this->fp);
|
||
|
}
|
||
|
|
||
|
protected function unpack(array $unpack=[]): string
|
||
|
{
|
||
|
return collect($unpack ?: static::unpack)->map(fn($v,$k)=>$v[0].$k)->join('/');
|
||
|
}
|
||
|
|
||
|
protected function unpack_size(array $unpack=[]): int
|
||
|
{
|
||
|
return collect($unpack ?: static::unpack)->map(fn($v,$k)=>$v[1])->sum();
|
||
|
}
|
||
|
}
|