Last active
December 7, 2019 21:42
-
-
Save martin-juul/1bf21487beb9302904bbd2366e295db7 to your computer and use it in GitHub Desktop.
hls live transcoding
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Transcode\FFMpeg; | |
use Illuminate\Support\Arr; | |
use Symfony\Component\Process\Process; | |
abstract class AbstractProcess | |
{ | |
abstract protected function getBinaryPath(): string; | |
protected function getProcess(array $args): Process | |
{ | |
$file = $args[0]; | |
unset($args[0]); | |
if (!is_file($file) || !is_readable($file)) { | |
throw new \RuntimeException('Could not read file at ' . $file); | |
} | |
$cmd = array_merge([$this->getBinaryPath()], is_array($args) ? Arr::flatten($args, 1) : $args); | |
$cmd[] = ' ' . $file; | |
$cmd = escapeshellcmd(implode(' ', $cmd)); | |
return Process::fromShellCommandline( | |
$cmd, | |
null, | |
null, | |
null, | |
300 | |
); | |
return new Process($cmd); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Transcode\FFMpeg\Output; | |
use Spatie\DataTransferObject\DataTransferObject; | |
class Frame extends DataTransferObject | |
{ | |
/** @var string */ | |
public $type; | |
/** @var float|int */ | |
public $pkt_pts_time; | |
/** @var float */ | |
public $best_effort_timestamp_time; | |
/** @var int */ | |
public $pkt_pos; | |
/** @var int */ | |
public $pkt_size; | |
/** @var string */ | |
public $pict_type; | |
public function __construct(array $parameters = []) | |
{ | |
foreach ($parameters as $parameter => $value) | |
{ | |
if (in_array($parameter, ['pkt_pts_time', 'best_effort_timestamp_time'])) { | |
$parameters[$parameter] = (float)$value; | |
} | |
if (in_array($parameter, ['pkt_pos', 'pkt_size'])) | |
{ | |
$parameters[$parameter] = (int)$value; | |
} | |
} | |
parent::__construct($parameters); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Http\Controllers\Transcode; | |
use App\Http\Controllers\Controller; | |
use App\Models\Video; | |
use App\Transcode\FFMpeg\Probe; | |
use App\Transcode\HlsIframePlaylist; | |
use Illuminate\Http\Request; | |
use Illuminate\Support\Facades\File; | |
class HlsController extends Controller | |
{ | |
public function getPlaylist(Request $request, string $id) | |
{ | |
$video = Video::whereId($id)->firstOrFail(); | |
$playlist = (new HlsIframePlaylist)->buildPlaylist($video); | |
return response($playlist, 200, [ | |
'Content-Type' => 'application/x-mpegURL', | |
]); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Transcode; | |
use App\Models\Video; | |
use App\Transcode\FFMpeg\Output\Frame; | |
class HlsIframePlaylist | |
{ | |
public function buildPlaylist(Video $video, $targetDuration = 10): string | |
{ | |
if ($video->playlist) { | |
return $video->playlist; | |
} | |
$iframeData = $this->createIframeSegments($video->getIframes()); | |
$playlist = <<<EOS | |
#EXTM3U | |
#EXT-X-TARGETDURATION:{$targetDuration} | |
#EXT-X-VERSION:4 | |
#EXT-X-MEDIA-SEQUENCE:0 | |
#EXT-X-I-FRAMES-ONLY | |
#EXT-X-PLAYLIST-TYPE:VOD | |
EOS; | |
foreach ($iframeData as $index => [$seg_duration, $pkt_size, $pkt_pos, $pkt_time]) { | |
$playlist .= $this->byteRangeChunk( | |
$seg_duration, | |
$pkt_size, | |
$pkt_pos, | |
$video->id . '.ts' | |
); | |
} | |
$playlist .= "\n#EXT-X-ENDLIST"; | |
$video->playlist = $playlist; | |
$video->save(); | |
return $playlist; | |
} | |
protected function createIframeSegments(array $packetsAndFrames) | |
{ | |
[$packets, $frames] = $packetsAndFrames; | |
$next_pkt_pts_time = null; | |
$iframeCount = count($frames); | |
$iframesData = []; | |
/** | |
* @var int $index | |
* @var Frame $iframe | |
*/ | |
foreach ($frames as $index => $iframe) { | |
$pkt_pts_time = $next_pkt_pts_time || $iframe->pkt_size; | |
if ($index < $iframeCount - 1) { | |
$extinf = bcsub($frames[$index + 1]->best_effort_timestamp_time, $iframe->best_effort_timestamp_time, 5); | |
} else { | |
$last_frame_time = end($frames)->best_effort_timestamp_time; | |
$extinf = bcsub($last_frame_time, $iframe->best_effort_timestamp_time, 5); | |
} | |
$iframesData[] = [ | |
$extinf, | |
$iframe->pkt_size, | |
$iframe->pkt_pos, | |
(float)number_format($pkt_pts_time, 5), | |
]; | |
} | |
return $iframesData; | |
} | |
protected function byteRangeChunk($duration, $size, $pos, $file) | |
{ | |
$byterange = "$size@$pos"; | |
return "\n#EXTINF:$duration," | |
. "\n#EXT-X-BYTERANGE:$byterange\n" | |
. route('hls.stream', [ | |
'video' => $file, | |
'byterange' => urlencode($byterange), | |
]); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Transcode\FFMpeg\Output; | |
use Spatie\DataTransferObject\DataTransferObject; | |
class Packet extends DataTransferObject | |
{ | |
/** @var string */ | |
public $type; | |
/** @var string */ | |
public $codec_type; | |
/** @var float */ | |
public $pts_time; | |
/** @var int */ | |
public $pos; | |
public function __construct(array $parameters = []) | |
{ | |
foreach ($parameters as $parameter => $value) { | |
if ($parameter === 'pts_time') { | |
$parameters[$parameter] = (float)$value; | |
} | |
if ($parameter === 'pos') { | |
$parameters[$parameter] = (int)$value; | |
} | |
} | |
parent::__construct($parameters); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#EXTM3U | |
#EXT-X-TARGETDURATION:10 | |
#EXT-X-VERSION:4 | |
#EXT-X-MEDIA-SEQUENCE:0 | |
#EXT-X-I-FRAMES-ONLY | |
#EXT-X-PLAYLIST-TYPE:VOD | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:1116@236997 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=1116%2540236997 | |
#EXTINF:7.90000, | |
#EXT-X-BYTERANGE:200477@2155641 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=200477%25402155641 | |
#EXTINF:3.86666, | |
#EXT-X-BYTERANGE:491651@7764158 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=491651%25407764158 | |
#EXTINF:7.30000, | |
#EXT-X-BYTERANGE:139243@9938214 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=139243%25409938214 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:547319@11385326 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=547319%254011385326 | |
#EXTINF:3.96666, | |
#EXT-X-BYTERANGE:536936@17259730 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=536936%254017259730 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:354768@19863234 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=354768%254019863234 | |
#EXTINF:4.03333, | |
#EXT-X-BYTERANGE:257759@22381467 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=257759%254022381467 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:397608@23582676 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=397608%254023582676 | |
#EXTINF:4.63333, | |
#EXT-X-BYTERANGE:105090@27059058 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=105090%254027059058 | |
#EXTINF:3.13333, | |
#EXT-X-BYTERANGE:140856@27818926 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=140856%254027818926 | |
#EXTINF:5.60000, | |
#EXT-X-BYTERANGE:331704@28534159 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=331704%254028534159 | |
#EXTINF:1.90000, | |
#EXT-X-BYTERANGE:234508@30585761 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=234508%254030585761 | |
#EXTINF:2.16666, | |
#EXT-X-BYTERANGE:421589@30984547 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=421589%254030984547 | |
#EXTINF:2.06666, | |
#EXT-X-BYTERANGE:98867@31675106 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=98867%254031675106 | |
#EXTINF:4.43333, | |
#EXT-X-BYTERANGE:285812@32420439 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=285812%254032420439 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:130415@35330810 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=130415%254035330810 | |
#EXTINF:1.43333, | |
#EXT-X-BYTERANGE:215534@37233295 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=215534%254037233295 | |
#EXTINF:1.66666, | |
#EXT-X-BYTERANGE:269017@37587388 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=269017%254037587388 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:235075@38475983 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=235075%254038475983 | |
#EXTINF:4.16666, | |
#EXT-X-BYTERANGE:463454@39786703 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=463454%254039786703 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:115667@41246719 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=115667%254041246719 | |
#EXTINF:5.73333, | |
#EXT-X-BYTERANGE:56969@43252190 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=56969%254043252190 | |
#EXTINF:2.90000, | |
#EXT-X-BYTERANGE:269913@45355451 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=269913%254045355451 | |
#EXTINF:2.60000, | |
#EXT-X-BYTERANGE:204815@46837960 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=204815%254046837960 | |
#EXTINF:3.40000, | |
#EXT-X-BYTERANGE:229270@47880429 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=229270%254047880429 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:253025@48789297 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=253025%254048789297 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:266944@51669856 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=266944%254051669856 | |
#EXTINF:2.83333, | |
#EXT-X-BYTERANGE:203013@55013957 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=203013%254055013957 | |
#EXTINF:2.73333, | |
#EXT-X-BYTERANGE:876612@55949169 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=876612%254055949169 | |
#EXTINF:6.90000, | |
#EXT-X-BYTERANGE:313572@57120897 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=313572%254057120897 | |
#EXTINF:4.83333, | |
#EXT-X-BYTERANGE:395026@59617927 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=395026%254059617927 | |
#EXTINF:1.46666, | |
#EXT-X-BYTERANGE:304506@60682166 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=304506%254060682166 | |
#EXTINF:1.00000, | |
#EXT-X-BYTERANGE:108734@61384299 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=108734%254061384299 | |
#EXTINF:2.93333, | |
#EXT-X-BYTERANGE:312382@61709061 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=312382%254061709061 | |
#EXTINF:1.76666, | |
#EXT-X-BYTERANGE:146588@62856106 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=146588%254062856106 | |
#EXTINF:1.56666, | |
#EXT-X-BYTERANGE:294259@63211538 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=294259%254063211538 | |
#EXTINF:6.56666, | |
#EXT-X-BYTERANGE:113003@63990927 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=113003%254063990927 | |
#EXTINF:1.60000, | |
#EXT-X-BYTERANGE:257551@65322540 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=257551%254065322540 | |
#EXTINF:2.76666, | |
#EXT-X-BYTERANGE:259248@66340007 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=259248%254066340007 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:271612@67205422 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=271612%254067205422 | |
#EXTINF:5.83333, | |
#EXT-X-BYTERANGE:155380@71409468 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=155380%254071409468 | |
#EXTINF:2.26666, | |
#EXT-X-BYTERANGE:96993@73607648 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=96993%254073607648 | |
#EXTINF:2.90000, | |
#EXT-X-BYTERANGE:127215@74942282 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=127215%254074942282 | |
#EXTINF:4.33333, | |
#EXT-X-BYTERANGE:188788@75541950 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=188788%254075541950 | |
#EXTINF:1.76666, | |
#EXT-X-BYTERANGE:159486@76826521 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=159486%254076826521 | |
#EXTINF:1.53333, | |
#EXT-X-BYTERANGE:191336@77173651 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=191336%254077173651 | |
#EXTINF:1.40000, | |
#EXT-X-BYTERANGE:151381@77832313 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=151381%254077832313 | |
#EXTINF:4.93333, | |
#EXT-X-BYTERANGE:157407@78218674 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=157407%254078218674 | |
#EXTINF:6.70000, | |
#EXT-X-BYTERANGE:161860@81597562 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=161860%254081597562 | |
#EXTINF:3.30000, | |
#EXT-X-BYTERANGE:95191@83539249 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=95191%254083539249 | |
#EXTINF:2.46666, | |
#EXT-X-BYTERANGE:682367@84261876 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=682367%254084261876 | |
#EXTINF:3.66666, | |
#EXT-X-BYTERANGE:128344@86235867 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=128344%254086235867 | |
#EXTINF:2.53333, | |
#EXT-X-BYTERANGE:612818@87557879 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=612818%254087557879 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:196327@88440986 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=196327%254088440986 | |
#EXTINF:1.23333, | |
#EXT-X-BYTERANGE:158218@90213316 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=158218%254090213316 | |
#EXTINF:1.46666, | |
#EXT-X-BYTERANGE:560512@90701653 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=560512%254090701653 | |
#EXTINF:7.26666, | |
#EXT-X-BYTERANGE:423157@92030120 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=423157%254092030120 | |
#EXTINF:1.23333, | |
#EXT-X-BYTERANGE:79874@95625809 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=79874%254095625809 | |
#EXTINF:1.66666, | |
#EXT-X-BYTERANGE:76807@95960562 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=76807%254095960562 | |
#EXTINF:1.66666, | |
#EXT-X-BYTERANGE:380137@96730376 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=380137%254096730376 | |
#EXTINF:1.66666, | |
#EXT-X-BYTERANGE:107783@97477701 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=107783%254097477701 | |
#EXTINF:1.66666, | |
#EXT-X-BYTERANGE:339474@97826092 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=339474%254097826092 | |
#EXTINF:0.83333, | |
#EXT-X-BYTERANGE:117720@98630659 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=117720%254098630659 | |
#EXTINF:1.66666, | |
#EXT-X-BYTERANGE:548266@98996533 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=548266%254098996533 | |
#EXTINF:1.66666, | |
#EXT-X-BYTERANGE:97775@100024357 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=97775%2540100024357 | |
#EXTINF:1.66666, | |
#EXT-X-BYTERANGE:250320@100437204 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=250320%2540100437204 | |
#EXTINF:1.66666, | |
#EXT-X-BYTERANGE:228358@101105883 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=228358%2540101105883 | |
#EXTINF:4.16666, | |
#EXT-X-BYTERANGE:184834@101619619 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=184834%2540101619619 | |
#EXTINF:5.63333, | |
#EXT-X-BYTERANGE:442199@102852538 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=442199%2540102852538 | |
#EXTINF:1.96666, | |
#EXT-X-BYTERANGE:81965@106472514 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=81965%2540106472514 | |
#EXTINF:1.76666, | |
#EXT-X-BYTERANGE:349620@107140271 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=349620%2540107140271 | |
#EXTINF:1.70000, | |
#EXT-X-BYTERANGE:338079@108230701 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=338079%2540108230701 | |
#EXTINF:3.30000, | |
#EXT-X-BYTERANGE:183802@109275321 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=183802%2540109275321 | |
#EXTINF:7.50000, | |
#EXT-X-BYTERANGE:581891@110626283 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=581891%2540110626283 | |
#EXTINF:3.33333, | |
#EXT-X-BYTERANGE:141960@112270274 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=141960%2540112270274 | |
#EXTINF:1.56666, | |
#EXT-X-BYTERANGE:234545@113742541 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=234545%2540113742541 | |
#EXTINF:8.10000, | |
#EXT-X-BYTERANGE:150285@114239742 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=150285%2540114239742 | |
#EXTINF:3.13333, | |
#EXT-X-BYTERANGE:405984@118388911 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=405984%2540118388911 | |
#EXTINF:4.16666, | |
#EXT-X-BYTERANGE:544118@120082061 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=544118%2540120082061 | |
#EXTINF:4.70000, | |
#EXT-X-BYTERANGE:273771@121727257 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=273771%2540121727257 | |
#EXTINF:2.00000, | |
#EXT-X-BYTERANGE:243914@124462924 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=243914%2540124462924 | |
#EXTINF:2.96666, | |
#EXT-X-BYTERANGE:158270@125643964 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=158270%2540125643964 | |
#EXTINF:1.53333, | |
#EXT-X-BYTERANGE:210035@127321208 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=210035%2540127321208 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:287673@127745739 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=287673%2540127745739 | |
#EXTINF:2.30000, | |
#EXT-X-BYTERANGE:242443@132239342 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=242443%2540132239342 | |
#EXTINF:5.43333, | |
#EXT-X-BYTERANGE:141449@133116714 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=141449%2540133116714 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:422815@134522096 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=422815%2540134522096 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:271537@139341026 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=271537%2540139341026 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:400137@145096828 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=400137%2540145096828 | |
#EXTINF:6.53333, | |
#EXT-X-BYTERANGE:352487@151079519 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=352487%2540151079519 | |
#EXTINF:1.40000, | |
#EXT-X-BYTERANGE:274085@155195915 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=274085%2540155195915 | |
#EXTINF:2.66666, | |
#EXT-X-BYTERANGE:316713@155982381 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=316713%2540155982381 | |
#EXTINF:2.96666, | |
#EXT-X-BYTERANGE:387155@156718059 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=387155%2540156718059 | |
#EXTINF:2.83333, | |
#EXT-X-BYTERANGE:299723@157896891 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=299723%2540157896891 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:222487@158765813 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=222487%2540158765813 | |
#EXTINF:1.30000, | |
#EXT-X-BYTERANGE:222564@160231852 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=222564%2540160231852 | |
#EXTINF:1.70000, | |
#EXT-X-BYTERANGE:118584@160667576 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=118584%2540160667576 | |
#EXTINF:2.00000, | |
#EXT-X-BYTERANGE:737474@161146119 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=737474%2540161146119 | |
#EXTINF:1.63333, | |
#EXT-X-BYTERANGE:168961@162104810 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=168961%2540162104810 | |
#EXTINF:3.56666, | |
#EXT-X-BYTERANGE:170163@162544078 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=170163%2540162544078 | |
#EXTINF:5.26666, | |
#EXT-X-BYTERANGE:47559@163702766 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=47559%2540163702766 | |
#EXTINF:5.46666, | |
#EXT-X-BYTERANGE:569518@164946795 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=569518%2540164946795 | |
#EXTINF:1.70000, | |
#EXT-X-BYTERANGE:205624@166615144 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=205624%2540166615144 | |
#EXTINF:4.00000, | |
#EXT-X-BYTERANGE:242783@167501684 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=242783%2540167501684 | |
#EXTINF:1.20000, | |
#EXT-X-BYTERANGE:217699@168990275 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=217699%2540168990275 | |
#EXTINF:2.70000, | |
#EXT-X-BYTERANGE:265375@169548777 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=265375%2540169548777 | |
#EXTINF:1.83333, | |
#EXT-X-BYTERANGE:182174@170319434 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=182174%2540170319434 | |
#EXTINF:2.46666, | |
#EXT-X-BYTERANGE:563374@171970269 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=563374%2540171970269 | |
#EXTINF:2.10000, | |
#EXT-X-BYTERANGE:67000@175500682 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=67000%2540175500682 | |
#EXTINF:1.06666, | |
#EXT-X-BYTERANGE:584933@176113759 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=584933%2540176113759 | |
#EXTINF:1.80000, | |
#EXT-X-BYTERANGE:219445@178581800 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=219445%2540178581800 | |
#EXTINF:4.40000, | |
#EXT-X-BYTERANGE:57660@179396574 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=57660%2540179396574 | |
#EXTINF:2.46666, | |
#EXT-X-BYTERANGE:70602@184211142 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=70602%2540184211142 | |
#EXTINF:4.43333, | |
#EXT-X-BYTERANGE:98644@185658397 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=98644%2540185658397 | |
#EXTINF:4.30000, | |
#EXT-X-BYTERANGE:145445@187014416 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=145445%2540187014416 | |
#EXTINF:6.86666, | |
#EXT-X-BYTERANGE:186215@191271724 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=186215%2540191271724 | |
#EXTINF:6.53333, | |
#EXT-X-BYTERANGE:234544@193190363 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=234544%2540193190363 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:141672@195043608 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=141672%2540195043608 | |
#EXTINF:4.70000, | |
#EXT-X-BYTERANGE:235319@201514671 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=235319%2540201514671 | |
#EXTINF:1.53333, | |
#EXT-X-BYTERANGE:148495@204788600 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=148495%2540204788600 | |
#EXTINF:7.33333, | |
#EXT-X-BYTERANGE:391939@205123957 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=391939%2540205123957 | |
#EXTINF:1.46666, | |
#EXT-X-BYTERANGE:178236@207300877 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=178236%2540207300877 | |
#EXTINF:2.16666, | |
#EXT-X-BYTERANGE:42612@207649470 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=42612%2540207649470 | |
#EXTINF:2.46666, | |
#EXT-X-BYTERANGE:175057@207921113 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=175057%2540207921113 | |
#EXTINF:4.23333, | |
#EXT-X-BYTERANGE:371430@208888453 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=371430%2540208888453 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:35283@209908619 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=35283%2540209908619 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:76435@211586766 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=76435%2540211586766 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:129695@214328553 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=129695%2540214328553 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:112518@217929090 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=112518%2540217929090 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:113725@221630213 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=113725%2540221630213 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:81391@224983266 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=81391%2540224983266 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:263083@229630445 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=263083%2540229630445 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:292508@234610910 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=292508%2540234610910 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:313745@241010226 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=313745%2540241010226 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:124565@246794050 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=124565%2540246794050 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:126713@250065934 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=126713%2540250065934 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:361475@256042112 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=361475%2540256042112 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:295967@263460657 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=295967%2540263460657 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:254226@269871512 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=254226%2540269871512 | |
#EXTINF:7.13333, | |
#EXT-X-BYTERANGE:29907@272417671 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=29907%2540272417671 | |
#EXTINF:8.33333, | |
#EXT-X-BYTERANGE:43273@272990149 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=43273%2540272990149 | |
#EXTINF:0.00000, | |
#EXT-X-BYTERANGE:51011@274397658 | |
http://127.0.0.1:8000/stream/bbb_sunflower_1080p_30fps_normal.mp4?byterange=51011%2540274397658 | |
#EXT-X-ENDLIST |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Transcode\FFMpeg; | |
use App\Transcode\FFMpeg\Output\AudioStreamOutput; | |
use App\Transcode\FFMpeg\Output\Frame; | |
use App\Transcode\FFMpeg\Output\Packet; | |
use App\Transcode\FFMpeg\Output\ProbeOutput; | |
use App\Transcode\FFMpeg\Output\VideoStreamFrameOutput; | |
use App\Transcode\FFMpeg\Output\VideoStreamOutput; | |
use Symfony\Component\Process\Exception\ProcessFailedException; | |
class Probe extends AbstractProcess | |
{ | |
public const TYPE_STREAMS = 'streams'; | |
public const TYPE_FORMAT = 'format'; | |
public const TYPE_VIDEO_FORMAT = 'video_format'; | |
/** | |
* @param string $filePath | |
* | |
* @return null|ProbeOutput | |
* @throws ProcessFailedException | |
* @throws \RuntimeException | |
*/ | |
public function format(string $filePath) | |
{ | |
return $this->probe($filePath, '-show_format', self::TYPE_FORMAT); | |
} | |
/** | |
* @param string $filePath | |
* | |
* @return (AudioStreamOutput|AudioStreamOutput)[]|null | |
*/ | |
public function streams(string $filePath) | |
{ | |
return $this->probe($filePath, '-show_streams', self::TYPE_STREAMS); | |
} | |
/** | |
* @param string $filePath | |
* | |
* @return array | |
*/ | |
public function frames(string $filePath) | |
{ | |
$flags = [ | |
'-loglevel', | |
'error', | |
'-select_streams', | |
'v', | |
'-skip_frame', | |
'nokey', | |
'-show_entries', | |
'packet=pts_time,codec_type,pos:frame=pict_type,pkt_pts_time,pkt_size,pkt_pos,best_effort_timestamp_time', | |
'-of', | |
'json', | |
]; | |
$probe = $this->probe($filePath, $flags, self::TYPE_VIDEO_FORMAT); | |
$probe = json_decode($probe, true); | |
$packets = []; | |
$frames = []; | |
foreach ($probe['packets_and_frames'] as $item) { | |
if ($item['type'] === 'packet' && $item['codec_type'] === 'video') { | |
$packets[] = new Packet($item); | |
} | |
if ($item['type'] === 'frame' && $item['pict_type'] === 'I') { | |
$frames[] = new Frame($item); | |
} | |
} | |
return [$packets, $frames]; | |
} | |
/** | |
* @param string $filePath | |
* @param string|array $command | |
* @param string $type | |
* @param bool $json | |
* | |
* @return ProbeOutput|VideoStreamFrameOutput|AudioStreamOutput|VideoStreamOutput|null | |
*/ | |
protected function probe(string $filePath, $command, string $type, $json = false) | |
{ | |
if (!is_file($filePath)) { | |
throw new \RuntimeException('File does not exist at ' . $filePath); | |
} | |
$args = [0 => $filePath, 1 => $command]; | |
if ($json) { | |
$args[] = '-print_format json'; | |
$args[] = 'json'; | |
} | |
$proc = $this->getProcess($args); | |
$proc->mustRun(); | |
switch ($type) { | |
case self::TYPE_FORMAT: | |
$outputParser = new ProbeFormatOutputParser(); | |
break; | |
case self::TYPE_STREAMS: | |
$outputParser = new ProbeStreamsOutputParser(); | |
break; | |
case self::TYPE_VIDEO_FORMAT: | |
$outputParser = new ProbeVideoFormatOutputParser(); | |
break; | |
} | |
if (!isset($outputParser)) { | |
return null; | |
} | |
return $outputParser->parse($proc->getOutput()); | |
} | |
protected function getBinaryPath(): string | |
{ | |
return config('transcoding.ffprobe_bin'); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment