2024-09-16 12:10:19 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace App\Media;
|
|
|
|
|
|
|
|
use Illuminate\Support\Collection;
|
|
|
|
|
|
|
|
use App\Media\QuickTime\Atoms\{mdat,moov,Unknown};
|
|
|
|
use App\Traits\FindQuicktimeAtoms;
|
|
|
|
|
|
|
|
// https://developer.apple.com/documentation/quicktime-file-format/quicktime_movie_files
|
|
|
|
|
|
|
|
class QuickTime extends Base {
|
|
|
|
use FindQuicktimeAtoms;
|
|
|
|
|
|
|
|
private const LOGKEY = 'MFQ';
|
|
|
|
|
|
|
|
private Collection $atoms;
|
|
|
|
|
|
|
|
private const atom_classes = 'App\\Media\\QuickTime\\Atoms\\';
|
|
|
|
|
|
|
|
public function __construct(string $filename,string $type)
|
|
|
|
{
|
|
|
|
parent::__construct($filename,$type);
|
|
|
|
|
|
|
|
// Parse the atoms
|
|
|
|
$this->atoms = $this->get_atoms(self::atom_classes,Unknown::class,0,$this->filesize);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function __get(string $key): mixed
|
|
|
|
{
|
|
|
|
switch ($key) {
|
|
|
|
case 'audio_channels':
|
|
|
|
case 'audio_codec':
|
|
|
|
case 'audio_samplerate':
|
|
|
|
return $this->getAudioAtoms()
|
|
|
|
->map(fn($item)=>$item->find_atoms(moov\trak\mdia\minf\stbl\stsd::class,1))
|
|
|
|
->flatten()
|
|
|
|
->map(fn($item)=>$item->{$key})
|
|
|
|
->join(',');
|
|
|
|
|
|
|
|
// Signatures are calculated by the sha of the MDAT atom.
|
|
|
|
case 'signature':
|
|
|
|
$atom = $this->find_atoms(mdat::class,1);
|
|
|
|
|
|
|
|
return $atom->signature;
|
|
|
|
|
|
|
|
// Creation Time is in the MOOV/MVHD atom
|
|
|
|
case 'creation_date':
|
|
|
|
case 'duration':
|
|
|
|
case 'preferred_rate':
|
|
|
|
case 'preferred_volume':
|
2024-09-18 08:16:31 +00:00
|
|
|
// Height/Width is in the moov/trak/tkhd atom
|
2024-09-16 12:10:19 +00:00
|
|
|
case 'height':
|
|
|
|
case 'width':
|
|
|
|
$atom = $this->find_atoms(moov::class,1);
|
|
|
|
|
|
|
|
return $atom->{$key};
|
|
|
|
|
|
|
|
case 'gps_altitude':
|
|
|
|
case 'gps_lat':
|
|
|
|
case 'gps_lon':
|
|
|
|
$atom = $this->find_atoms(moov::class,1)
|
|
|
|
->find_atoms(moov\meta::class,1);
|
|
|
|
|
|
|
|
return $atom->{$key};
|
|
|
|
|
2024-09-16 13:17:51 +00:00
|
|
|
case 'make':
|
|
|
|
case 'model':
|
|
|
|
case 'software':
|
|
|
|
return $this->find_atoms(moov\meta::class,1)->{$key};
|
|
|
|
|
2024-09-16 12:10:19 +00:00
|
|
|
case 'time_scale':
|
|
|
|
$atom = $this->find_atoms(moov\mvhd::class,1);
|
|
|
|
|
|
|
|
return $atom->{$key};
|
|
|
|
|
|
|
|
case 'type':
|
|
|
|
return parent::__get($key);
|
|
|
|
|
|
|
|
case 'video_codec':
|
|
|
|
return $this->getVideoAtoms()
|
|
|
|
->map(fn($item)=>$item->find_atoms(moov\trak\mdia\minf\stbl\stsd::class,1))
|
|
|
|
->flatten()
|
|
|
|
->map(fn($item)=>$item->{$key})
|
|
|
|
->join(',');
|
|
|
|
|
|
|
|
case 'video_framerate':
|
|
|
|
$atom = $this->getVideoAtoms()
|
|
|
|
->map(fn($item)=>$item->find_atoms(moov\trak\mdia\minf\stbl\stts::class,1))
|
|
|
|
->pop();
|
|
|
|
|
|
|
|
return $atom->frame_rate($this->time_scale);
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw new \Exception('Unknown key: '.$key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find all the audio track atoms
|
|
|
|
*
|
|
|
|
* Audio atoms are in moov/trak/mdia/minf
|
|
|
|
*
|
|
|
|
* @return Collection
|
|
|
|
* @throws \Exception
|
|
|
|
*/
|
|
|
|
public function getAudioAtoms(): Collection
|
|
|
|
{
|
|
|
|
return $this->find_atoms(moov\trak\mdia\minf::class)
|
|
|
|
->filter(fn($item)=>$item->type==='soun');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find all the video track atoms
|
|
|
|
*
|
|
|
|
* Audio atoms are in moov/trak/mdia/minf
|
|
|
|
*
|
|
|
|
* @return Collection
|
|
|
|
* @throws \Exception
|
|
|
|
*/
|
|
|
|
public function getVideoAtoms(): Collection
|
|
|
|
{
|
|
|
|
return $this->find_atoms(moov\trak\mdia\minf::class)
|
|
|
|
->filter(fn($item)=>$item->type==='vide');
|
|
|
|
}
|
|
|
|
}
|