Skip to content

Commit

Permalink
HLS bugfix
Browse files Browse the repository at this point in the history
  • Loading branch information
pascalbaljet committed Dec 24, 2020
1 parent bb93e2c commit a22d394
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 33 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

All Notable changes to `pbmedia/laravel-ffmpeg` will be documented in this file

## 7.5.1 - 2020-12-24

### Added

- Support for codec in HLS playlist
- Fixed bitrate bug in HLS playlist

## 7.5.0 - 2020-12-22

### Added
Expand Down
26 changes: 25 additions & 1 deletion src/Exporters/HLSExporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use FFMpeg\Format\VideoInterface;
use Illuminate\Support\Collection;
use ProtoneMedia\LaravelFFMpeg\Filesystem\Disk;
use ProtoneMedia\LaravelFFMpeg\Filesystem\Media;
use ProtoneMedia\LaravelFFMpeg\MediaOpener;

class HLSExporter extends MediaExporter
Expand Down Expand Up @@ -142,13 +143,27 @@ private function applyFiltersCallback(callable $filtersCallback, int $formatKey)
return $outs;
}

public static function generateMasterPlaylistFilename($key): string
{
return "master_playlist_guide_{$key}.m3u8";
}

private function cleanupMasterPlaylistGuides(Media $media)
{
$this->pendingFormats->map(function ($formatAndCallback, $key) use ($media) {
$media->getDisk()->delete(
$media->getDirectory() . static::generateMasterPlaylistFilename($key)
);
});
}

private function prepareSaving(string $path = null): Collection
{
$media = $this->getDisk()->makeMedia($path);

$baseName = $media->getDirectory() . $media->getFilenameWithoutExtension();

return $this->pendingFormats->map(function ($formatAndCallback, $key) use ($baseName) {
return $this->pendingFormats->map(function ($formatAndCallback, $key) use ($baseName, $media) {
$disk = $this->getDisk()->clone();

[$format, $filtersCallback] = $formatAndCallback;
Expand All @@ -161,6 +176,14 @@ private function prepareSaving(string $path = null): Collection

$this->addHLSParametersToFormat($format, $segmentsPattern, $disk);

$format->setAdditionalParameters(array_merge(
$format->getAdditionalParameters(),
[
'-master_pl_name',
$this->generateMasterPlaylistFilename($key),
]
));

if ($filtersCallback) {
$outs = $this->applyFiltersCallback($filtersCallback, $key);
}
Expand Down Expand Up @@ -193,6 +216,7 @@ public function save(string $path = null): MediaOpener
$this->getDisk()->put($path, $playlist);

$this->replaceAbsolutePathsHLSEncryption($playlistMedia);
$this->cleanupMasterPlaylistGuides($playlistMedia->first());
$this->cleanupHLSEncryption();

return $result;
Expand Down
40 changes: 16 additions & 24 deletions src/Exporters/HLSPlaylistGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,18 @@

namespace ProtoneMedia\LaravelFFMpeg\Exporters;

use Exception;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use ProtoneMedia\LaravelFFMpeg\Drivers\PHPFFMpeg;
use ProtoneMedia\LaravelFFMpeg\Filesystem\Media;
use ProtoneMedia\LaravelFFMpeg\Http\DynamicHLSPlaylist;
use ProtoneMedia\LaravelFFMpeg\MediaOpener;

class HLSPlaylistGenerator implements PlaylistGenerator
{
const PLAYLIST_START = '#EXTM3U';
const PLAYLIST_END = '#EXT-X-ENDLIST';

private function getBandwidth(MediaOpener $media)
{
return $media->getFormat()->get('bit_rate');
}

private function getResolution(MediaOpener $media)
{
try {
$dimensions = optional($media->getVideoStream())->getDimensions();
} catch (Exception $exception) {
return null;
}

return "{$dimensions->getWidth()}x{$dimensions->getHeight()}";
}

private function getFrameRate(MediaOpener $media)
{
$mediaStream = $media->getVideoStream();
Expand All @@ -43,22 +27,30 @@ private function getFrameRate(MediaOpener $media)
return $frameRate ? number_format($frameRate, 3, '.', '') : null;
}

private function getStreamInfoLine(Media $playlistMedia, string $key): string
{
$masterPlaylist = $playlistMedia->getDisk()->get(
$playlistMedia->getDirectory() . HLSExporter::generateMasterPlaylistFilename($key, $playlistMedia)
);

$lines = DynamicHLSPlaylist::parseLines($masterPlaylist)->filter();

return $lines->get($lines->search($playlistMedia->getFilename()) - 1);
}

public function get(array $playlistMedia, PHPFFMpeg $driver): string
{
return Collection::make($playlistMedia)->map(function (Media $playlistMedia) use ($driver) {
return Collection::make($playlistMedia)->map(function (Media $playlistMedia, $key) use ($driver) {
$media = (new MediaOpener($playlistMedia->getDisk(), $driver))
->openWithInputOptions($playlistMedia->getPath(), ['-allowed_extensions', 'ALL']);

$streamInfo = [
"#EXT-X-STREAM-INF:BANDWIDTH={$this->getBandwidth($media)}",
"RESOLUTION={$this->getResolution($media)}",
];
$streamInfoLine = $this->getStreamInfoLine($playlistMedia, $key);

if ($frameRate = $this->getFrameRate($media)) {
$streamInfo[] = "FRAME-RATE={$frameRate}";
$streamInfoLine .= ",FRAME-RATE={$frameRate}";
}

return [implode(',', $streamInfo), $playlistMedia->getFilename()];
return [$streamInfoLine, $playlistMedia->getFilename()];
})->collapse()
->prepend(static::PLAYLIST_START)
->push(static::PLAYLIST_END)
Expand Down
2 changes: 1 addition & 1 deletion src/Http/DynamicHLSPlaylist.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ private function resolvePlaylistFilename(string $playlist): string
* @param string $lines
* @return \Illuminate\Support\Collection
*/
private static function parseLines(string $lines): Collection
public static function parseLines(string $lines): Collection
{
return Collection::make(preg_split('/\n|\r\n?/', $lines));
}
Expand Down
4 changes: 2 additions & 2 deletions tests/EncryptedHlsExportTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ public function it_can_export_a_single_media_file_into_an_encryped_hls_export()

$pattern = '/' . implode("\n", [
'#EXTM3U',
'#EXT-X-STREAM-INF:BANDWIDTH=[0-9]+,RESOLUTION=1920x1080,FRAME-RATE=25.000',
HlsExportTest::streamInfoPattern('1920x1080'),
'adaptive_0_250.m3u8',
'#EXT-X-ENDLIST',
]) . '/';

$this->assertEquals(1, preg_match($pattern, $playlist));
$this->assertEquals(1, preg_match($pattern, $playlist), "Playlist mismatch:" . PHP_EOL . $playlist);

$encryptedPlaylist = Storage::disk('local')->get('adaptive_0_250.m3u8');

Expand Down
16 changes: 11 additions & 5 deletions tests/HlsExportTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@

class HlsExportTest extends TestCase
{
public static function streamInfoPattern($resolution): string
{
return '#EXT-X-STREAM-INF:BANDWIDTH=[0-9]{4,},RESOLUTION=' . $resolution . ',CODECS="[a-zA-Z0-9,.]+",FRAME-RATE=25.000';
}

/** @test */
public function it_can_export_a_single_media_file_into_a_hls_export()
{
Expand Down Expand Up @@ -51,11 +56,11 @@ public function it_can_export_a_single_media_file_into_a_hls_export()

$pattern = '/' . implode("\n", [
'#EXTM3U',
'#EXT-X-STREAM-INF:BANDWIDTH=[0-9]+,RESOLUTION=1920x1080,FRAME-RATE=25.000',
static::streamInfoPattern('1920x1080'),
'adaptive_0_250.m3u8',
'#EXT-X-STREAM-INF:BANDWIDTH=[0-9]+,RESOLUTION=1920x1080,FRAME-RATE=25.000',
static::streamInfoPattern('1920x1080'),
'adaptive_1_1000.m3u8',
'#EXT-X-STREAM-INF:BANDWIDTH=[0-9]+,RESOLUTION=1920x1080,FRAME-RATE=25.000',
static::streamInfoPattern('1920x1080'),
'adaptive_2_4000.m3u8',
'#EXT-X-ENDLIST',
]) . '/';
Expand Down Expand Up @@ -108,6 +113,7 @@ public function it_can_export_a_single_media_file_into_a_subdirectory_on_a_remot
$this->assertTrue(Storage::disk('local')->has('sub/dir/adaptive.m3u8'));
$this->assertTrue(Storage::disk('local')->has('sub/dir/adaptive_0_250.m3u8'));
$this->assertTrue(Storage::disk('local')->has('sub/dir/adaptive_1_1000.m3u8'));
$this->assertFalse(Storage::disk('local')->has('sub/dir/master_playlist_guide_0.m3u8'));

$masterPlaylist = Storage::disk('local')->get('sub/dir/adaptive.m3u8');
$lowPlaylist = Storage::disk('local')->get('sub/dir/adaptive_0_250.m3u8');
Expand Down Expand Up @@ -149,9 +155,9 @@ public function it_can_use_a_custom_format_for_the_segment_naming()

$pattern = '/' . implode("\n", [
'#EXTM3U',
'#EXT-X-STREAM-INF:BANDWIDTH=[0-9]+,RESOLUTION=1920x1080,FRAME-RATE=25.000',
static::streamInfoPattern('1920x1080'),
'NadaptiveB250K0.m3u8',
'#EXT-X-STREAM-INF:BANDWIDTH=[0-9]+,RESOLUTION=1920x1080,FRAME-RATE=25.000',
static::streamInfoPattern('1920x1080'),
'NadaptiveB1000K1.m3u8',
'#EXT-X-ENDLIST',
]) . '/';
Expand Down

0 comments on commit a22d394

Please sign in to comment.