Issues (64)

src/HLS.php (6 issues)

1
<?php
2
3
/**
4
 * This file is part of the PHP-FFmpeg-video-streaming package.
5
 *
6
 * (c) Amin Yazdanpanah <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Streaming;
13
14
use Streaming\Filters\HLSFilter;
15
use Streaming\Filters\StreamFilterInterface;
16
17
class HLS extends Streaming
18
{
19
20
    /** @var string */
21
    private $hls_time = 10;
22
23
    /** @var bool */
24
    private $hls_allow_cache = true;
25
26
    /** @var string */
27
    private $hls_key_info_file = "";
28
29
    /** @var string */
30
    private $seg_sub_directory = "";
31
32
    /** @var string */
33
    private $hls_base_url = "";
34
35
    /** @var int */
36
    private $hls_list_size = 0;
37
38
    /** @var bool */
39
    private $tmp_key_info_file = false;
40
41
    /** @var string */
42
    public $master_playlist;
43
44
    /** @var string */
45
    private $hls_segment_type = 'mpegts';
46
47
    /** @var string */
48
    private $hls_fmp4_init_filename = "init.mp4";
49
50
    /** @var array */
51
    private $stream_des = [];
52
53
    /** @var array */
54
    private $flags = [];
55
56
    /** @var array */
57
    private $subtitles = [];
58
59
    /**
60
     * @return string
61
     */
62
    public function getSegSubDirectory(): string
63
    {
64
        return $this->seg_sub_directory;
65
    }
66
67
    /**
68
     * @param string $seg_sub_directory
69
     * @return HLS
70
     */
71
    public function setSegSubDirectory(string $seg_sub_directory)
72
    {
73
        $this->seg_sub_directory = $seg_sub_directory;
74
        return $this;
75
    }
76
77
    /**
78
     * @param string $hls_time
79
     * @return HLS
80
     */
81
    public function setHlsTime(string $hls_time): HLS
82
    {
83
        $this->hls_time = $hls_time;
84
        return $this;
85
    }
86
87
    /**
88
     * @return string
89
     */
90
    public function getHlsTime(): string
91
    {
92
        return $this->hls_time;
93
    }
94
95
    /**
96
     * @param bool $hls_allow_cache
97
     * @return HLS
98
     */
99
    public function setHlsAllowCache(bool $hls_allow_cache): HLS
100
    {
101
        $this->hls_allow_cache = $hls_allow_cache;
102
        return $this;
103
    }
104
105
    /**
106
     * @return bool
107
     */
108
    public function isHlsAllowCache(): bool
109
    {
110
        return $this->hls_allow_cache;
111
    }
112
113
    /**
114
     * @param string $hls_key_info_file
115
     * @return HLS
116
     */
117
    public function setHlsKeyInfoFile(string $hls_key_info_file): HLS
118
    {
119
        $this->hls_key_info_file = $hls_key_info_file;
120
        return $this;
121
    }
122
123
    /**
124
     * @param string $save_to
125
     * @param string $url
126
     * @param int $key_rotation_period
127
     * @param string $search
128
     * @param int $length
129
     * @return HLS
130
     */
131
    public function encryption(string $save_to, string $url, int $key_rotation_period = 0, string $search = ".ts' for writing", int $length = 16): HLS
132
    {
133
        $key_info = HLSKeyInfo::create($save_to, $url);
134
        $key_info->setLength($length);
135
136
        if ($key_rotation_period > 0) {
137
            $key_info->rotateKey($this->getMedia()->getFFMpegDriver(), $key_rotation_period, $search);
0 ignored issues
show
$this->getMedia()->getFFMpegDriver() of type FFMpeg\Media\Video|Streaming\Media is incompatible with the type FFMpeg\Driver\FFMpegDriver expected by parameter $driver of Streaming\HLSKeyInfo::rotateKey(). ( Ignorable by Annotation )

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

137
            $key_info->rotateKey(/** @scrutinizer ignore-type */ $this->getMedia()->getFFMpegDriver(), $key_rotation_period, $search);
Loading history...
The method getFFMpegDriver() does not exist on Streaming\Media. 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

137
            $key_info->rotateKey($this->getMedia()->/** @scrutinizer ignore-call */ getFFMpegDriver(), $key_rotation_period, $search);
Loading history...
138
            array_push($this->flags, HLSFlag::PERIODIC_REKEY);
139
        }
140
141
        $this->setHlsKeyInfoFile((string)$key_info);
142
        $this->tmp_key_info_file = true;
143
144
        return $this;
145
    }
146
147
    public function subtitle(HLSSubtitle $subtitle)
148
    {
149
        array_push($this->subtitles, $subtitle);
150
        return $this;
151
    }
152
153
    /**
154
     * @param array $subtitles
155
     * @return HLS
156
     */
157
    public function subtitles(array $subtitles): HLS
158
    {
159
        array_walk($subtitles, [$this, 'subtitle']);
160
        return $this;
161
    }
162
163
    /**
164
     * @return string
165
     */
166
    public function getHlsKeyInfoFile(): string
167
    {
168
        return $this->hls_key_info_file;
169
    }
170
171
    /**
172
     * @param string $hls_base_url
173
     * @return HLS
174
     */
175
    public function setHlsBaseUrl(string $hls_base_url): HLS
176
    {
177
        $this->hls_base_url = $hls_base_url;
178
        return $this;
179
    }
180
181
    /**
182
     * @return string
183
     */
184
    public function getHlsBaseUrl(): string
185
    {
186
        return $this->hls_base_url;
187
    }
188
189
    /**
190
     * @param int $hls_list_size
191
     * @return HLS
192
     */
193
    public function setHlsListSize(int $hls_list_size): HLS
194
    {
195
        $this->hls_list_size = $hls_list_size;
196
        return $this;
197
    }
198
199
    /**
200
     * @return int
201
     */
202
    public function getHlsListSize(): int
203
    {
204
        return $this->hls_list_size;
205
    }
206
207
    /**
208
     * @param string $master_playlist
209
     * @param array $stream_des
210
     * @return HLS
211
     */
212
    public function setMasterPlaylist(string $master_playlist, array $stream_des = []): HLS
213
    {
214
        $this->master_playlist = $master_playlist;
215
        $this->stream_des = $stream_des;
216
217
        return $this;
218
    }
219
220
    /**
221
     * @return HLS
222
     */
223
    public function fragmentedMP4(): HLS
224
    {
225
        $this->setHlsSegmentType("fmp4");
226
        return $this;
227
    }
228
229
    /**
230
     * @param string $hls_segment_type
231
     * @return HLS
232
     */
233
    public function setHlsSegmentType(string $hls_segment_type): HLS
234
    {
235
        $this->hls_segment_type = $hls_segment_type;
236
        return $this;
237
    }
238
239
    /**
240
     * @return string
241
     */
242
    public function getHlsSegmentType(): string
243
    {
244
        return $this->hls_segment_type;
245
    }
246
247
    /**
248
     * @param string $hls_fmp4_init_filename
249
     * @return HLS
250
     */
251
    public function setHlsFmp4InitFilename(string $hls_fmp4_init_filename): HLS
252
    {
253
        $this->hls_fmp4_init_filename = $hls_fmp4_init_filename;
254
        return $this;
255
    }
256
257
    /**
258
     * @return string
259
     */
260
    public function getHlsFmp4InitFilename(): string
261
    {
262
        return $this->hls_fmp4_init_filename;
263
    }
264
265
    /**
266
     * @param array $flags
267
     * @return HLS
268
     */
269
    public function setFlags(array $flags): HLS
270
    {
271
        $this->flags = array_merge($this->flags, $flags);
272
        return $this;
273
    }
274
275
    /**
276
     * @return array
277
     */
278
    public function getFlags(): array
279
    {
280
        return $this->flags;
281
    }
282
283
    /**
284
     * @return HLSFilter
285
     */
286
    protected function getFilter(): StreamFilterInterface
287
    {
288
        return new HLSFilter($this);
289
    }
290
291
    /**
292
     * @return string
293
     */
294
    protected function getPath(): string
295
    {
296
        $path = $this->getFilePath();
297
        $reps = $this->getRepresentations();
298
299
        if(!empty($this->subtitles)){
300
            $this->generateSubs($path);
301
        }
302
303
        $this->savePlaylist($path . ".m3u8");
304
305
        return $path . "_" . $reps->end()->getHeight() . "p.m3u8";
306
    }
307
308
    /**
309
     * @param $path
310
     */
311
    public function savePlaylist(string $path): void
312
    {
313
        $mater_playlist = new HLSPlaylist($this);
314
        $mater_playlist->save($this->master_playlist ?? $path, $this->stream_des);
315
    }
316
317
    /**
318
     * @param string $path
319
     */
320
    private function generateSubs(string $path)
321
    {
322
        $this->stream_des = array_merge($this->stream_des, [PHP_EOL]);
323
324
        foreach ($this->subtitles as $subtitle) {
325
            if($subtitle instanceof HLSSubtitle){
326
                $subtitle->generateM3U8File("{$path}_subtitles_{$subtitle->getLanguageCode()}.m3u8", $this->getDuration());
327
                array_push($this->stream_des, (string)$subtitle);
328
            }
329
        }
330
        array_push($this->stream_des, PHP_EOL);
331
332
        $this->getRepresentations()->map(function (Representation $rep){
333
            return $rep->setHlsStreamInfo(["SUBTITLES" => "\"" . $this->subtitles[0]->getGroupId() . "\""]);
334
        });
335
    }
336
337
    /**
338
     * @return float
339
     */
340
    private function getDuration():float
341
    {
342
        return $this->getMedia()->getFormat()->get("duration", 0);
0 ignored issues
show
The method get() does not exist on FFMpeg\Media\Video. ( Ignorable by Annotation )

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

342
        return $this->getMedia()->getFormat()->/** @scrutinizer ignore-call */ get("duration", 0);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug Best Practice introduced by
The expression return $this->getMedia()...t()->get('duration', 0) returns the type FFMpeg\Media\Video|Streaming\Media which is incompatible with the type-hinted return double.
Loading history...
The method get() does not exist on Streaming\Media. 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

342
        return $this->getMedia()->getFormat()->/** @scrutinizer ignore-call */ get("duration", 0);
Loading history...
The method getFormat() does not exist on Streaming\Media. 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

342
        return $this->getMedia()->/** @scrutinizer ignore-call */ getFormat()->get("duration", 0);
Loading history...
343
    }
344
345
    /**
346
     * Clear key info file if is a temp file
347
     */
348
    public function __destruct()
349
    {
350
        if ($this->tmp_key_info_file) {
351
            File::remove($this->getHlsKeyInfoFile());
352
        }
353
354
        parent::__destruct();
355
    }
356
}