Implement our own quicktime parser

This commit is contained in:
2024-09-16 22:10:19 +10:00
parent a3013078e0
commit f9cdc8f9d2
42 changed files with 1367 additions and 62 deletions

View File

@@ -0,0 +1,10 @@
<?php
namespace App\Media\QuickTime\Atoms\moov\trak\mdia\minf;
use App\Media\QuickTime\Atoms\moov\trak\mdia\hdlr as MdiaHdlr;
class hdlr extends MdiaHdlr
{
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Media\QuickTime\Atoms\moov\trak\mdia\minf;
use App\Media\QuickTime\Atoms\SubAtom;
use App\Media\QuickTime\Atoms\Unknown;
class stbl extends SubAtom
{
private const subatom_classes = 'App\\Media\\QuickTime\\Atoms\\moov\\trak\\mdia\\minf\\stbl\\';
public function __construct(int $offset,int $size,string $filename,?string $data,?string $arg=NULL)
{
parent::__construct($offset,$size,$filename);
$this->atoms = $this->get_atoms(self::subatom_classes,Unknown::class,$offset,$size,$data,$arg);
// For debugging
if (FALSE)
$this->debug = hex_dump($data ?: $this->data());
}
}

View File

@@ -0,0 +1,104 @@
<?php
namespace App\Media\QuickTime\Atoms\moov\trak\mdia\minf\stbl;
use Illuminate\Support\Arr;
use App\Media\QuickTime\Atoms\SubAtom;
class stsd extends SubAtom
{
protected const unpack = [
'size'=>['N',4],
'format'=>['a4',4],
'reserved'=>['a6',6],
'index'=>['n',2],
'encoder_version'=>['n',2],
'encoder_revision'=>['n',2],
'encoder_vendor'=>['a4',4],
];
protected const audio_record = [
'audio_channels'=>['n',2],
'audio_bit_depth'=>['n',2],
'audio_compression_id'=>['n',2],
'audio_packet_size'=>['n',2],
'audio_sample_rate'=>['a4',4],
];
protected const video_record = [
'temporal_quality'=>['N',4],
'spatial_quality'=>['N',4],
'width'=>['n',2],
'height'=>['n',2],
'resolution_x'=>['a4',4],
'resolution_y'=>['a4',4],
'data_size'=>['N',4],
'frame_count'=>['n',2],
//'codec_name'=>['a4',4], // pascal string
];
public function __construct(int $offset,int $size,string $filename,?string $data,string $arg=NULL)
{
parent::__construct($offset,$size,$filename);
$this->type = $arg;
$read = unpack($this->unpack(self::atom_record),substr($data,0,$ptr=$this->unpack_size(self::atom_record)));
for ($i=0; $i<$read['count']; $i++) {
$this->cache = collect(unpack($this->unpack(),substr($data,$ptr,$x=$this->unpack_size())));
$ptr += $x;
switch ($this->type) {
case 'soun':
// Audio Track
$this->audio = unpack($this->unpack(self::audio_record),substr($data,$ptr,$x=$this->unpack_size(self::audio_record)));
$ptr += $x;
break;
case 'vide':
// Video Track
$this->video = unpack($this->unpack(self::video_record),substr($data,$ptr,$x=$this->unpack_size(self::video_record)));
$ptr += $x;
// codec - pascal string
$this->video['codec'] = pascal_string(substr($data,$ptr));
$ptr += strlen($this->video['codec'])+1;
}
$this->extra = substr($data,$ptr);
}
}
public function __get(string $key):mixed
{
switch ($key) {
case 'audio_channels':
return Arr::get($this->audio,$key);
case 'audio_codec':
switch ($this->cache->get('format')) {
case 'mp4a': return 'ISO/IEC 14496-3 AAC';
default:
return $this->cache->get('format');
}
case 'audio_samplerate':
return fixed_point_int_to_float(Arr::get($this->audio,'audio_sample_rate',0));
case 'video_codec':
return Arr::get($this->video,'codec');
case 'video_framerate':
dd($this);
// return Arr::get($this->video,'codec');
default:
return parent::__get($key);
}
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Media\QuickTime\Atoms\moov\trak\mdia\minf\stbl;
use App\Media\QuickTime\Atoms\SubAtom;
class stts extends SubAtom
{
public function __construct(int $offset,int $size,string $filename,?string $data)
{
parent::__construct($offset,$size,$filename);
$read = unpack($this->unpack(self::atom_record),substr($data,0,$ptr=$this->unpack_size(self::atom_record)));
for ($i=0; $i<$read['count']; $i++) {
$this->cache->push(unpack('Ncount/Nduration',substr($data,$ptr,8)));
$ptr += 8;
}
// For debugging
if (FALSE)
$this->debug = hex_dump($data ?: $this->data());
}
public function frame_rate(float $time_scale): float
{
return $this->cache
->pluck('duration')
->map(fn($item)=>$time_scale/$item)
->max();
}
}