<?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(); } }