Passed
Push — master ( a6a132...f48b58 )
by Pascal
03:05
created

HLSExporter::getCommand()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace ProtoneMedia\LaravelFFMpeg\Exporters;
4
5
use Closure;
6
use FFMpeg\Format\FormatInterface;
7
use FFMpeg\Format\Video\DefaultVideo;
8
use FFMpeg\Format\VideoInterface;
9
use Illuminate\Support\Collection;
10
use ProtoneMedia\LaravelFFMpeg\Filesystem\Disk;
11
use ProtoneMedia\LaravelFFMpeg\MediaOpener;
12
13
class HLSExporter extends MediaExporter
14
{
15
    /**
16
     * @var integer
17
     */
18
    private $segmentLength = 10;
19
20
    /**
21
    * @var integer
22
    */
23
    private $keyFrameInterval = 48;
24
25
    /**
26
     * @var \Illuminate\Support\Collection
27
     */
28
    private $pendingFormats;
29
30
    /**
31
     * @var \ProtoneMedia\LaravelFFMpeg\Exporters\PlaylistGenerator
32
     */
33
    private $playlistGenerator;
34
35
    /**
36
     * @var \Closure
37
     */
38
    private $segmentFilenameGenerator = null;
39
40
    public function setSegmentLength(int $length): self
41
    {
42
        $this->segmentLength = $length;
43
44
        return $this;
45
    }
46
47
    public function setKeyFrameInterval(int $interval): self
48
    {
49
        $this->keyFrameInterval = $interval;
50
51
        return $this;
52
    }
53
54
    public function withPlaylistGenerator(PlaylistGenerator $playlistGenerator): self
55
    {
56
        $this->playlistGenerator = $playlistGenerator;
57
58
        return $this;
59
    }
60
61
    private function getPlaylistGenerator(): PlaylistGenerator
62
    {
63
        return $this->playlistGenerator ?: new HLSPlaylistGenerator;
64
    }
65
66
    public function useSegmentFilenameGenerator(Closure $callback): self
67
    {
68
        $this->segmentFilenameGenerator = $callback;
69
70
        return $this;
71
    }
72
73
    private function getSegmentFilenameGenerator(): callable
74
    {
75
        return $this->segmentFilenameGenerator ?: function ($name, $format, $key, $segments, $playlist) {
76
            $segments("{$name}_{$key}_{$format->getKiloBitrate()}_%05d.ts");
77
            $playlist("{$name}_{$key}_{$format->getKiloBitrate()}.m3u8");
78
        };
79
    }
80
81
    private function getSegmentPatternAndFormatPlaylistPath(string $baseName, VideoInterface $format, int $key): array
82
    {
83
        $segmentsPattern    = null;
84
        $formatPlaylistPath = null;
85
86
        call_user_func(
87
            $this->getSegmentFilenameGenerator(),
88
            $baseName,
89
            $format,
90
            $key,
91
            function ($path) use (&$segmentsPattern) {
92
                $segmentsPattern = $path;
93
            },
94
            function ($path) use (&$formatPlaylistPath) {
95
                $formatPlaylistPath = $path;
96
            }
97
        );
98
99
        return [$segmentsPattern, $formatPlaylistPath];
100
    }
101
102
    private function addHLSParametersToFormat(DefaultVideo $format, string $segmentsPattern, Disk $disk)
103
    {
104
        $format->setAdditionalParameters([
105
            '-sc_threshold',
106
            '0',
107
            '-g',
108
            $this->keyFrameInterval,
109
            '-hls_playlist_type',
110
            'vod',
111
            '-hls_time',
112
            $this->segmentLength,
113
            '-hls_segment_filename',
114
            $disk->makeMedia($segmentsPattern)->getLocalPath(),
115
        ]);
116
    }
117
118
    private function applyFiltersCallback(callable $filtersCallback, int $formatKey): array
119
    {
120
        $filtersCallback(
121
            $hlsVideoFilters = new HLSVideoFilters($this->driver, $formatKey)
122
        );
123
124
        $filterCount = $hlsVideoFilters->count();
125
126
        $outs = [$filterCount ? HLSVideoFilters::glue($formatKey, $filterCount) : '0:v'];
127
128
        if ($this->getAudioStream()) {
0 ignored issues
show
Bug introduced by
The method getAudioStream() does not exist on ProtoneMedia\LaravelFFMpeg\Exporters\HLSExporter. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

128
        if ($this->/** @scrutinizer ignore-call */ getAudioStream()) {
Loading history...
129
            $outs[] = '0:a';
130
        }
131
132
        return $outs;
133
    }
134
135
    private function prepareSaving(string $path = null): Collection
136
    {
137
        $media = $this->getDisk()->makeMedia($path);
0 ignored issues
show
Bug introduced by
It seems like $path can also be of type null; however, parameter $path of ProtoneMedia\LaravelFFMp...ystem\Disk::makeMedia() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

137
        $media = $this->getDisk()->makeMedia(/** @scrutinizer ignore-type */ $path);
Loading history...
138
139
        $baseName = $media->getDirectory() . $media->getFilenameWithoutExtension();
140
141
        return $this->pendingFormats->map(function ($formatAndCallback, $key) use ($baseName) {
142
            $disk = $this->getDisk()->clone();
143
144
            [$format, $filtersCallback] = $formatAndCallback;
145
146
            [$segmentsPattern, $formatPlaylistPath] = $this->getSegmentPatternAndFormatPlaylistPath(
147
                $baseName,
148
                $format,
149
                $key
150
            );
151
152
            $this->addHLSParametersToFormat($format, $segmentsPattern, $disk);
153
154
            if ($filtersCallback) {
155
                $outs = $this->applyFiltersCallback($filtersCallback, $key);
156
            }
157
158
            $this->addFormatOutputMapping($format, $disk->makeMedia($formatPlaylistPath), $outs ?? ['0']);
159
160
            return $this->getDisk()->makeMedia($formatPlaylistPath);
161
        });
162
    }
163
164
    public function getCommand(string $path = null)
165
    {
166
        $this->prepareSaving($path);
167
168
        return parent::getCommand(null);
169
    }
170
171
    public function save(string $path = null): MediaOpener
172
    {
173
        return $this->prepareSaving($path)->pipe(function ($playlistMedia) use ($path) {
174
            $result = parent::save();
175
176
            $playlist = $this->getPlaylistGenerator()->get(
177
                $playlistMedia->all(),
178
                $this->driver->fresh()
179
            );
180
181
            $this->getDisk()->put($path, $playlist);
0 ignored issues
show
Bug introduced by
The method put() does not exist on ProtoneMedia\LaravelFFMpeg\Filesystem\Disk. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

181
            $this->getDisk()->/** @scrutinizer ignore-call */ put($path, $playlist);
Loading history...
182
183
            return $result;
184
        });
185
    }
186
187
    public function addFormat(FormatInterface $format, callable $filtersCallback = null): self
188
    {
189
        if (!$this->pendingFormats) {
190
            $this->pendingFormats = new Collection;
191
        }
192
193
        $this->pendingFormats->push([$format, $filtersCallback]);
194
195
        return $this;
196
    }
197
}
198