From 3a6957fca647dcf164450292dcdda6c65d079f66 Mon Sep 17 00:00:00 2001 From: Amin Date: Tue, 5 May 2020 00:14:43 +0430 Subject: [PATCH] Bug fixes and other minor improvements --- src/Filters/AudioDASHFilter.php | 2 +- src/Filters/AudioHLSFilter.php | 2 +- src/Filters/FormatFilter.php | 29 +++++++++++++++ src/Filters/HLSFilter.php | 58 ++++++++++++------------------ src/Filters/HLSFilterV2.php | 2 +- src/Filters/StreamToFileFilter.php | 7 ++-- src/Format/HEVC.php | 19 +++++++--- src/Format/StreamFormat.php | 9 ----- src/Format/VP9.php | 15 +++++--- src/Format/X264.php | 23 +++++++++--- src/StreamInterface.php | 7 ++++ src/Traits/Formats.php | 15 ++++---- src/Utiles.php | 9 +++-- tests/DASHFiltersTest.php | 12 +++---- 14 files changed, 132 insertions(+), 77 deletions(-) create mode 100644 src/Filters/FormatFilter.php diff --git a/src/Filters/AudioDASHFilter.php b/src/Filters/AudioDASHFilter.php index 78dadfe..f441626 100644 --- a/src/Filters/AudioDASHFilter.php +++ b/src/Filters/AudioDASHFilter.php @@ -24,6 +24,6 @@ class AudioDASHFilter extends StreamFilter */ public function streamFilter(StreamInterface $stream): void { - // TODO: Implement streamFilter() method. + // @TODO: Implement streamFilter() method. } } \ No newline at end of file diff --git a/src/Filters/AudioHLSFilter.php b/src/Filters/AudioHLSFilter.php index 9cbe17c..71470ef 100644 --- a/src/Filters/AudioHLSFilter.php +++ b/src/Filters/AudioHLSFilter.php @@ -15,7 +15,7 @@ use Streaming\StreamInterface; -class AudioHLSFilter extends StreamFilter +class AudioHLSFilter extends FormatFilter { /** diff --git a/src/Filters/FormatFilter.php b/src/Filters/FormatFilter.php new file mode 100644 index 0000000..e6d140a --- /dev/null +++ b/src/Filters/FormatFilter.php @@ -0,0 +1,29 @@ + $format->getVideoCodec(), + 'c:a' => $format->getAudioCodec(), + ]); + + $options = Utiles::arrayToFFmpegOpt( + array_merge($format->getInitialParameters() ?? [], $format->getAdditionalParameters() ?? []) + ); + + return array_merge($basic, $options); + } +} \ No newline at end of file diff --git a/src/Filters/HLSFilter.php b/src/Filters/HLSFilter.php index 9f7528f..f08cdfc 100644 --- a/src/Filters/HLSFilter.php +++ b/src/Filters/HLSFilter.php @@ -16,7 +16,7 @@ use Streaming\Representation; use Streaming\Utiles; -class HLSFilter extends StreamFilter +class HLSFilter extends FormatFilter { /** @var \Streaming\HLS */ private $hls; @@ -36,17 +36,6 @@ class HLSFilter extends StreamFilter /** @var string */ private $seg_filename; - /** - * @return array - */ - private function getFormats(): array - { - $format = ['-c:v', $this->hls->getFormat()->getVideoCodec()]; - $audio_format = $this->hls->getFormat()->getAudioCodec(); - - return $audio_format ? array_merge($format, ['-c:a', $audio_format]) : $format; - } - /** * @param Representation $rep * @param bool $not_last @@ -63,7 +52,7 @@ private function playlistPath(Representation $rep, bool $not_last): array */ private function getAudioBitrate(Representation $rep): array { - return $rep->getAudioKiloBitrate() ? ["-b:a", $rep->getAudioKiloBitrate() . "k"] : []; + return $rep->getAudioKiloBitrate() ? ["b:a" => $rep->getAudioKiloBitrate() . "k"] : []; } /** @@ -71,12 +60,12 @@ private function getAudioBitrate(Representation $rep): array */ private function getBaseURL(): array { - return $this->base_url ? ["-hls_base_url", $this->base_url] : []; + return $this->base_url ? ["hls_base_url" => $this->base_url] : []; } private function flags(): array { - return !empty($this->hls->getFlags()) ? ["-hls_flags", implode("+", $this->hls->getFlags())] : []; + return !empty($this->hls->getFlags()) ? ["hls_flags" => implode("+", $this->hls->getFlags())] : []; } /** @@ -84,7 +73,7 @@ private function flags(): array */ private function getKeyInfo(): array { - return $this->hls->getHlsKeyInfoFile() ? ["-hls_key_info_file", $this->hls->getHlsKeyInfoFile()] : []; + return $this->hls->getHlsKeyInfoFile() ? ["hls_key_info_file" => $this->hls->getHlsKeyInfoFile()] : []; } /** @@ -112,21 +101,22 @@ private function getSegmentFilename(Representation $rep): string */ private function initArgs(Representation $rep): array { - return [ - "-s:v", $rep->size2string(), - "-crf", "20", - "-sc_threshold", "0", - "-g", "48", - "-keyint_min", "48", - "-hls_list_size", $this->hls->getHlsListSize(), - "-hls_time", $this->hls->getHlsTime(), - "-hls_allow_cache", (int)$this->hls->isHlsAllowCache(), - "-b:v", $rep->getKiloBitrate() . "k", - "-maxrate", intval($rep->getKiloBitrate() * 1.2) . "k", - "-hls_segment_type", $this->hls->getHlsSegmentType(), - "-hls_fmp4_init_filename", $this->getInitFilename($rep), - "-hls_segment_filename", $this->getSegmentFilename($rep) + $init = [ + "hls_list_size" => $this->hls->getHlsListSize(), + "hls_time" => $this->hls->getHlsTime(), + "hls_allow_cache" => (int)$this->hls->isHlsAllowCache(), + "hls_segment_type" => $this->hls->getHlsSegmentType(), + "hls_fmp4_init_filename" => $this->getInitFilename($rep), + "hls_segment_filename" => $this->getSegmentFilename($rep), + "s:v" => $rep->size2string(), + "b:v" => $rep->getKiloBitrate() . "k" ]; + + return array_merge($init, + $this->getAudioBitrate($rep), + $this->getBaseURL(), + $this->flags(), + $this->getKeyInfo()); } /** @@ -137,12 +127,8 @@ private function getArgs(Representation $rep, bool $not_last): void { $this->filter = array_merge( $this->filter, - $this->getFormats(), - $this->initArgs($rep), - $this->getAudioBitrate($rep), - $this->getBaseURL(), - $this->flags(), - $this->getKeyInfo(), + $this->getFormatOptions($this->hls->getFormat()), + Utiles::arrayToFFmpegOpt($this->initArgs($rep)), Utiles::arrayToFFmpegOpt($this->hls->getAdditionalParams()), ["-strict", $this->hls->getStrict()], $this->playlistPath($rep, $not_last) diff --git a/src/Filters/HLSFilterV2.php b/src/Filters/HLSFilterV2.php index e8b834d..a3b7a51 100644 --- a/src/Filters/HLSFilterV2.php +++ b/src/Filters/HLSFilterV2.php @@ -13,7 +13,7 @@ use Streaming\StreamInterface; -class HLSFilterV2 extends StreamFilter +class HLSFilterV2 extends FormatFilter { /** * This is a new version of HLSFilter that the master playlist will be created by FFmpeg diff --git a/src/Filters/StreamToFileFilter.php b/src/Filters/StreamToFileFilter.php index d7ef00b..7a10ba9 100644 --- a/src/Filters/StreamToFileFilter.php +++ b/src/Filters/StreamToFileFilter.php @@ -15,7 +15,7 @@ use Streaming\StreamInterface; -class StreamToFileFilter extends StreamFilter +class StreamToFileFilter extends FormatFilter { /** @@ -24,6 +24,9 @@ class StreamToFileFilter extends StreamFilter */ public function streamFilter(StreamInterface $media): void { - $this->filter = array_merge(['-c', 'copy'], $media->getParams()); + $this->filter = array_merge( + $this->getFormatOptions($media->getFormat()), + $media->getParams() + ); } } \ No newline at end of file diff --git a/src/Format/HEVC.php b/src/Format/HEVC.php index e96af03..585b662 100644 --- a/src/Format/HEVC.php +++ b/src/Format/HEVC.php @@ -19,13 +19,24 @@ final class HEVC extends StreamFormat * HEVC constructor. * @param string $video_codec * @param string|null $audio_codec + * @param bool $default_init_opts */ - public function __construct(string $video_codec = 'libx265', string $audio_codec = null) + public function __construct(string $video_codec = 'libx265', string $audio_codec = 'aac', bool $default_init_opts = true) { - $this->setVideoCodec($video_codec); + $this + ->setVideoCodec($video_codec) + ->setAudioCodec($audio_codec); - if ($audio_codec) { - $this->setAudioCodec($audio_codec); + /** + * set the default value of h265 codec options + * see https://ffmpeg.org/ffmpeg-codecs.html#Options-29 for more information about options + */ + if ($default_init_opts) { + $this->setInitialParameters([ + 'keyint_min' => 25, + 'g' => 250, + 'sc_threshold' => 40 + ]); } } diff --git a/src/Format/StreamFormat.php b/src/Format/StreamFormat.php index f0988c4..6eec9d8 100644 --- a/src/Format/StreamFormat.php +++ b/src/Format/StreamFormat.php @@ -35,13 +35,4 @@ public function setAudioKiloBitrate($kiloBitrate) { throw new InvalidArgumentException("You can not set this option, use Representation instead"); } - - /** - * @param array $additionalParamaters - * @return DefaultVideo|void - */ - public function setAdditionalParameters($additionalParamaters) - { - throw new InvalidArgumentException("You can not set this option"); - } } \ No newline at end of file diff --git a/src/Format/VP9.php b/src/Format/VP9.php index dfe57bd..265562a 100644 --- a/src/Format/VP9.php +++ b/src/Format/VP9.php @@ -22,13 +22,20 @@ final class VP9 extends StreamFormat * VP9 constructor. * @param string $video_codec * @param string|null $audio_codec + * @param bool $default_init_opts */ - public function __construct(string $video_codec = 'libvpx-vp9', string $audio_codec = null) + public function __construct(string $video_codec = 'libvpx-vp9', string $audio_codec = 'aac', bool $default_init_opts = true) { - $this->setVideoCodec($video_codec); + $this + ->setVideoCodec($video_codec) + ->setAudioCodec($audio_codec); - if ($audio_codec) { - $this->setAudioCodec($audio_codec); + /** + * set the default value of h265 codec options + * see https://ffmpeg.org/ffmpeg-codecs.html#Options-26 for more information about options + */ + if ($default_init_opts) { + //@TODO: add default vp9 } } diff --git a/src/Format/X264.php b/src/Format/X264.php index 97df20b..58ad802 100644 --- a/src/Format/X264.php +++ b/src/Format/X264.php @@ -18,14 +18,27 @@ final class X264 extends StreamFormat /** * X264 constructor. * @param string $video_codec - * @param null $audio_codec + * @param string $audio_codec + * @param bool $default_init_opts */ - public function __construct($video_codec = 'libx264', $audio_codec = null) + public function __construct($video_codec = 'libx264', string $audio_codec = 'aac', bool $default_init_opts = true) { - $this->setVideoCodec($video_codec); + $this + ->setVideoCodec($video_codec) + ->setAudioCodec($audio_codec); - if ($audio_codec) { - $this->setAudioCodec($audio_codec); + /** + * set the default value of h264 codec options + * see https://ffmpeg.org/ffmpeg-codecs.html#Options-28 for more information about options + * return array + */ + if ($default_init_opts) { + $this->setInitialParameters([ + 'bf' => 1, + 'keyint_min' => 25, + 'g' => 250, + 'sc_threshold' => 40 + ]); } } diff --git a/src/StreamInterface.php b/src/StreamInterface.php index e50840a..f600a36 100644 --- a/src/StreamInterface.php +++ b/src/StreamInterface.php @@ -13,6 +13,8 @@ namespace Streaming; +use FFMpeg\Format\VideoInterface; + interface StreamInterface { /** @@ -20,6 +22,11 @@ interface StreamInterface */ public function getMedia(): Media; + /** + * @return VideoInterface + */ + public function getFormat(): VideoInterface; + /** * @param int $option * @return string diff --git a/src/Traits/Formats.php b/src/Traits/Formats.php index aad916e..4177f32 100644 --- a/src/Traits/Formats.php +++ b/src/Traits/Formats.php @@ -26,33 +26,36 @@ trait Formats /** * @param string $video_codec * @param string|null $audio_codec + * @param bool $default_init_opts * @return $this */ - public function x264(string $video_codec = 'libx264', string $audio_codec = null) + public function x264(string $video_codec = 'libx264', string $audio_codec = 'aac', bool $default_init_opts = true) { - $this->setFormat(new X264($video_codec, $audio_codec)); + $this->setFormat(new X264($video_codec, $audio_codec, $default_init_opts)); return $this; } /** * @param string $video_codec * @param string|null $audio_codec + * @param bool $default_init_opts * @return $this */ - public function hevc(string $video_codec = 'libx265', string $audio_codec = null) + public function hevc(string $video_codec = 'libx265', string $audio_codec = 'aac', bool $default_init_opts = true) { - $this->setFormat(new HEVC($video_codec, $audio_codec)); + $this->setFormat(new HEVC($video_codec, $audio_codec, $default_init_opts)); return $this; } /** * @param string $video_codec * @param string|null $audio_codec + * @param bool $default_init_opts * @return $this */ - public function vp9(string $video_codec = 'libvpx-vp9', string $audio_codec = null) + public function vp9(string $video_codec = 'libvpx-vp9', string $audio_codec = 'aac', bool $default_init_opts = true) { - $this->setFormat(new VP9($video_codec, $audio_codec)); + $this->setFormat(new VP9($video_codec, $audio_codec, $default_init_opts)); return $this; } diff --git a/src/Utiles.php b/src/Utiles.php index c9d143b..668ee1b 100644 --- a/src/Utiles.php +++ b/src/Utiles.php @@ -44,10 +44,15 @@ public static function arrayToFFmpegOpt(array $array, string $start_with = "-"): { $new = []; foreach ($array as $key => $value) { - array_push($new, $start_with . $key, $value); + if(is_string($key)){ + array_push($new, $start_with . $key, $value); + }else{ + $new = null; + break; + } } - return $new; + return $new ?? $array; } /** diff --git a/tests/DASHFiltersTest.php b/tests/DASHFiltersTest.php index bf6811c..f02228e 100644 --- a/tests/DASHFiltersTest.php +++ b/tests/DASHFiltersTest.php @@ -30,12 +30,12 @@ public function testGetApply() $this->assertEquals( [ - "-bf", "1", "-keyint_min", "120", "-g", "120", "-sc_threshold", "0", "-b_strategy", "0", "-use_timeline", - "1", "-use_template", "1", "-init_seg_name", "test_init_\$RepresentationID$.\$ext$", "-media_seg_name", - "test_chunk_\$RepresentationID\$_\$Number%05d$.\$ext$", "-seg_duration", "10", "-hls_playlist", "0", "-f", - "dash", "-map", "0", "-b:v:0", "103k", "-s:v:0", "256x144", "-map", "0", "-b:v:1", "138k", "-s:v:1", - "426x240", "-map", "0", "-b:v:2", "207k", "-s:v:2", "640x360", "-c:v", "libx265", "-adaptation_sets", - "id=0,streams=v id=1,streams=a", "-strict", "-2" + "-c:v", "libx265", "-c:a", "aac", "-keyint_min", "25", "-g", "250", "-sc_threshold", "40", + "-use_timeline", "1", "-use_template", "1", "-init_seg_name", "test_init_\$RepresentationID$.\$ext$", + "-media_seg_name", "test_chunk_\$RepresentationID\$_\$Number%05d$.\$ext$", "-seg_duration", "10", + "-hls_playlist", "0", "-f", "dash", "-adaptation_sets", "id=0,streams=v id=1,streams=a", "-map", "0", + "-s:v:0", "256x144", "-b:v:0", "103k", "-map", "0", "-s:v:1", "426x240", "-b:v:1", "138k", "-map", "0", + "-s:v:2", "640x360", "-b:v:2", "207k", "-strict", "-2" ], $apply); }