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 FFMpeg\Coordinate\Dimension; |
||||||||||
15 | use FFMpeg\Exception\ExceptionInterface; |
||||||||||
16 | use FFMpeg\Format\VideoInterface; |
||||||||||
17 | use Streaming\Exception\InvalidArgumentException; |
||||||||||
18 | use Streaming\Exception\RuntimeException; |
||||||||||
19 | |||||||||||
20 | |||||||||||
21 | class AutoReps implements \IteratorAggregate |
||||||||||
22 | { |
||||||||||
23 | /** @var \FFMpeg\FFProbe\DataMapping\Stream $video */ |
||||||||||
24 | private $video; |
||||||||||
25 | |||||||||||
26 | /** @var \FFMpeg\FFProbe\DataMapping\Format $format */ |
||||||||||
27 | private $original_format; |
||||||||||
28 | |||||||||||
29 | /** |
||||||||||
30 | * regular video's heights |
||||||||||
31 | * |
||||||||||
32 | * @var array side_values |
||||||||||
33 | */ |
||||||||||
34 | private $sides = [144, 240, 360, 480, 720, 1080, 1440, 2160]; |
||||||||||
35 | |||||||||||
36 | /** @var array $k_bitrate */ |
||||||||||
37 | private $k_bitrate; |
||||||||||
38 | |||||||||||
39 | /** @var bool */ |
||||||||||
40 | private $sort = true; |
||||||||||
41 | |||||||||||
42 | /** @const VideoInterface */ |
||||||||||
43 | private $format; |
||||||||||
44 | |||||||||||
45 | /** |
||||||||||
46 | * AutoReps constructor. |
||||||||||
47 | * @param Media $media |
||||||||||
48 | * @param VideoInterface $format |
||||||||||
49 | * @param array|null $sides |
||||||||||
50 | * @param array|null $k_bitrate |
||||||||||
51 | */ |
||||||||||
52 | public function __construct(Media $media, VideoInterface $format, array $sides = null, array $k_bitrate = null) |
||||||||||
53 | { |
||||||||||
54 | $this->format = $format; |
||||||||||
55 | $this->video = $media->getStreams()->videos()->first(); |
||||||||||
0 ignored issues
–
show
Bug
introduced
by
![]() The method
videos() 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
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. ![]() It seems like
$media->getStreams()->videos()->first() of type FFMpeg\Media\Video or Streaming\Media is incompatible with the declared type FFMpeg\FFProbe\DataMapping\Stream of property $video .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() The method
getStreams() 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
![]() The method
first() 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
![]() The method
first() 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
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. ![]() |
|||||||||||
56 | $this->original_format = $media->getFormat(); |
||||||||||
0 ignored issues
–
show
It seems like
$media->getFormat() of type FFMpeg\Media\Video or Streaming\Media is incompatible with the declared type FFMpeg\FFProbe\DataMapping\Format of property $original_format .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() 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
![]() |
|||||||||||
57 | $this->sides($sides, $k_bitrate); |
||||||||||
58 | $this->kiloBitrate($k_bitrate); |
||||||||||
59 | } |
||||||||||
60 | |||||||||||
61 | /** |
||||||||||
62 | * Set sort order for reps |
||||||||||
63 | * @param bool $sort |
||||||||||
64 | */ |
||||||||||
65 | public function sort(bool $sort) |
||||||||||
66 | { |
||||||||||
67 | $this->sort = $sort; |
||||||||||
68 | } |
||||||||||
69 | |||||||||||
70 | /** |
||||||||||
71 | * @return Dimension |
||||||||||
72 | */ |
||||||||||
73 | private function getDimensions(): Dimension |
||||||||||
74 | { |
||||||||||
75 | try { |
||||||||||
76 | return $this->video->getDimensions(); |
||||||||||
77 | } catch (ExceptionInterface $e) { |
||||||||||
78 | throw new RuntimeException("Unable to extract dimensions.: " . $e->getMessage(), $e->getCode(), $e); |
||||||||||
79 | } |
||||||||||
80 | } |
||||||||||
81 | |||||||||||
82 | /** |
||||||||||
83 | * @return mixed |
||||||||||
84 | * @throws InvalidArgumentException |
||||||||||
85 | */ |
||||||||||
86 | private function getKiloBitRate(): int |
||||||||||
87 | { |
||||||||||
88 | if (!$this->video->has('bit_rate')) { |
||||||||||
89 | if (!$this->original_format->has('bit_rate')) { |
||||||||||
90 | throw new InvalidArgumentException("Unable to extract bitrate."); |
||||||||||
91 | } |
||||||||||
92 | |||||||||||
93 | return intval(($this->original_format->get('bit_rate') / 1024) * .9); |
||||||||||
94 | } |
||||||||||
95 | |||||||||||
96 | return (int)$this->video->get('bit_rate') / 1024; |
||||||||||
97 | } |
||||||||||
98 | |||||||||||
99 | /** |
||||||||||
100 | * @param array|null $k_bitrate_values |
||||||||||
101 | * @TODO: fix #79 |
||||||||||
102 | */ |
||||||||||
103 | private function kiloBitrate(?array $k_bitrate_values): void |
||||||||||
104 | { |
||||||||||
105 | $k_bit_rates = []; |
||||||||||
106 | $count_sides = count($this->sides); |
||||||||||
107 | |||||||||||
108 | if (!is_null($k_bitrate_values)) { |
||||||||||
0 ignored issues
–
show
|
|||||||||||
109 | if ($count_sides !== count($k_bitrate_values)) { |
||||||||||
110 | throw new InvalidArgumentException("The count of side value array must be the same as the count of kilo bitrate array"); |
||||||||||
111 | } |
||||||||||
112 | $this->k_bitrate = $k_bitrate_values; |
||||||||||
113 | return; |
||||||||||
114 | } |
||||||||||
115 | |||||||||||
116 | $k_bitrate_value = $this->getKiloBitRate(); |
||||||||||
117 | $divided_by = 1.5; |
||||||||||
118 | |||||||||||
119 | while ($count_sides) { |
||||||||||
120 | $k_bit_rates[] = (($k_bitrate = intval($k_bitrate_value / $divided_by)) < 64) ? 64 : $k_bitrate; |
||||||||||
121 | $divided_by += .5; |
||||||||||
122 | $count_sides--; |
||||||||||
123 | } |
||||||||||
124 | |||||||||||
125 | $this->k_bitrate = array_reverse($k_bit_rates); |
||||||||||
126 | } |
||||||||||
127 | |||||||||||
128 | /** |
||||||||||
129 | * @param int $height |
||||||||||
130 | * @return bool |
||||||||||
131 | */ |
||||||||||
132 | private function sideFilter(int $height): bool |
||||||||||
133 | { |
||||||||||
134 | return $height < $this->getDimensions()->getHeight(); |
||||||||||
135 | } |
||||||||||
136 | |||||||||||
137 | /** |
||||||||||
138 | * @param array|null $sides |
||||||||||
139 | * @param array|null $k_bitrate |
||||||||||
140 | */ |
||||||||||
141 | private function sides(?array $sides, ?array $k_bitrate): void |
||||||||||
142 | { |
||||||||||
143 | if (!is_null($sides) && is_null($k_bitrate)) { |
||||||||||
0 ignored issues
–
show
|
|||||||||||
144 | sort($sides); |
||||||||||
145 | } |
||||||||||
146 | |||||||||||
147 | $this->sides = array_values(array_filter($sides ?? $this->sides, [$this, 'sideFilter'])); |
||||||||||
148 | } |
||||||||||
149 | |||||||||||
150 | /** |
||||||||||
151 | * @param int $value |
||||||||||
152 | * @param string $side |
||||||||||
153 | * @return int |
||||||||||
154 | */ |
||||||||||
155 | private function computeSide(int $value, string $side): int |
||||||||||
156 | { |
||||||||||
157 | $ratio = clone $this->getDimensions()->getRatio(); |
||||||||||
158 | return call_user_func_array([$ratio, 'calculate' . $side], [$value, $this->format->getModulus()]); |
||||||||||
159 | } |
||||||||||
160 | |||||||||||
161 | /** |
||||||||||
162 | * @param $width |
||||||||||
163 | * @param $k_bitrate |
||||||||||
164 | * @param $height |
||||||||||
165 | * @return Representation |
||||||||||
166 | * @throws InvalidArgumentException |
||||||||||
167 | */ |
||||||||||
168 | private function addRep($k_bitrate, $width, $height): Representation |
||||||||||
169 | { |
||||||||||
170 | return (new Representation)->setKiloBitrate($k_bitrate)->setResize($width, $height); |
||||||||||
171 | } |
||||||||||
172 | |||||||||||
173 | /** |
||||||||||
174 | * @param array $reps |
||||||||||
175 | */ |
||||||||||
176 | private function sortReps(array &$reps): void |
||||||||||
177 | { |
||||||||||
178 | usort($reps, function (Representation $rep1, Representation $rep2) { |
||||||||||
179 | $ascending = $rep1->getKiloBitrate() > $rep2->getKiloBitrate(); |
||||||||||
180 | return $this->sort ? $ascending : !$ascending; |
||||||||||
181 | }); |
||||||||||
182 | } |
||||||||||
183 | |||||||||||
184 | /** |
||||||||||
185 | * @param bool $sort |
||||||||||
186 | * @return array |
||||||||||
187 | */ |
||||||||||
188 | public function getCalculatedReps(bool $sort = false): array |
||||||||||
189 | { |
||||||||||
190 | $reps = []; |
||||||||||
191 | foreach ($this->sides as $key => $height) { |
||||||||||
192 | array_push($reps, $this->addRep($this->k_bitrate[$key], $this->computeSide($height, 'Width'), $height)); |
||||||||||
193 | } |
||||||||||
194 | |||||||||||
195 | if ($sort) { |
||||||||||
196 | $this->sortReps($reps); |
||||||||||
197 | } |
||||||||||
198 | |||||||||||
199 | return $reps; |
||||||||||
200 | } |
||||||||||
201 | |||||||||||
202 | /** |
||||||||||
203 | * @return Representation |
||||||||||
204 | */ |
||||||||||
205 | public function getOriginalRep(): Representation |
||||||||||
206 | { |
||||||||||
207 | $dimension = $this->getDimensions(); |
||||||||||
208 | $width = $this->computeSide($dimension->getHeight(), 'Width'); |
||||||||||
209 | $height = $this->computeSide($dimension->getWidth(), 'Height'); |
||||||||||
210 | |||||||||||
211 | return $this->addRep($this->getKiloBitRate(), $width, $height); |
||||||||||
212 | } |
||||||||||
213 | |||||||||||
214 | /** |
||||||||||
215 | * @return array |
||||||||||
216 | */ |
||||||||||
217 | public function getSides(): array |
||||||||||
218 | { |
||||||||||
219 | return $this->sides; |
||||||||||
220 | } |
||||||||||
221 | |||||||||||
222 | /** |
||||||||||
223 | * @return array |
||||||||||
224 | */ |
||||||||||
225 | public function getKBitrate(): array |
||||||||||
226 | { |
||||||||||
227 | return $this->k_bitrate; |
||||||||||
228 | } |
||||||||||
229 | |||||||||||
230 | /** |
||||||||||
231 | * Retrieve an external iterator reps |
||||||||||
232 | * @return \Traversable An instance of an object implementing <b>Iterator</b> or <b>Traversable</b> |
||||||||||
233 | */ |
||||||||||
234 | public function getIterator() |
||||||||||
235 | { |
||||||||||
236 | $reps = $this->getCalculatedReps(); |
||||||||||
237 | array_push($reps, $this->getOriginalRep()); |
||||||||||
238 | $this->sortReps($reps); |
||||||||||
239 | |||||||||||
240 | return new \ArrayIterator($reps); |
||||||||||
241 | } |
||||||||||
242 | } |