Passed
Push — master ( b23da5...cc1845 )
by Amin
04:10
created

AutoReps::getKiloBitRate()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 3
eloc 5
nc 3
nop 0
dl 0
loc 11
rs 10
c 1
b 1
f 0
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 string */
40
    private $sort = "asc";
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 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

55
        $this->video = $media->getStreams()->/** @scrutinizer ignore-call */ videos()->first();
Loading history...
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 ignore-call  annotation

55
        $this->video = $media->getStreams()->/** @scrutinizer ignore-call */ videos()->first();

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...
Documentation Bug introduced by
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..

Loading history...
Bug introduced by
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 ignore-call  annotation

55
        $this->video = $media->/** @scrutinizer ignore-call */ getStreams()->videos()->first();
Loading history...
Bug introduced by
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 ignore-call  annotation

55
        $this->video = $media->getStreams()->videos()->/** @scrutinizer ignore-call */ first();
Loading history...
Bug introduced by
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 ignore-call  annotation

55
        $this->video = $media->getStreams()->videos()->/** @scrutinizer ignore-call */ first();

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...
56
        $this->original_format = $media->getFormat();
0 ignored issues
show
Documentation Bug introduced by
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..

Loading history...
Bug introduced by
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

56
        /** @scrutinizer ignore-call */ 
57
        $this->original_format = $media->getFormat();
Loading history...
57
        $this->sides($sides, $k_bitrate);
58
        $this->kiloBitrate($k_bitrate);
59
    }
60
61
    /**
62
     * Set sort order for reps
63
     * @param string $sort
64
     */
65
    public function sort(string $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
     */
102
    private function kiloBitrate(?array $k_bitrate_values): void
103
    {
104
        $k_bit_rates = [];
105
        $count_sides = count($this->sides);
106
107
        if (!is_null($k_bitrate_values)) {
0 ignored issues
show
introduced by
The condition is_null($k_bitrate_values) is always false.
Loading history...
108
            if ($count_sides !== count($k_bitrate_values)) {
109
                throw new InvalidArgumentException("The count of side value array must be the same as the count of kilo bitrate array");
110
            }
111
            $this->k_bitrate = $k_bitrate_values;
112
            return;
113
        }
114
115
        $k_bitrate_value = $this->getKiloBitRate();
116
        $divided_by = 1.5;
117
118
        while ($count_sides) {
119
            $k_bit_rates[] = (($k_bitrate = intval($k_bitrate_value / $divided_by)) < 64) ? 64 : $k_bitrate;
120
            $divided_by += .5;
121
            $count_sides--;
122
        }
123
124
        $this->k_bitrate = array_reverse($k_bit_rates);
125
    }
126
127
    /**
128
     * @param int $height
129
     * @return bool
130
     */
131
    private function sideFilter(int $height): bool
132
    {
133
        return $height < $this->getDimensions()->getHeight();
134
    }
135
136
    /**
137
     * @param array|null $sides
138
     * @param array|null $k_bitrate
139
     */
140
    private function sides(?array $sides, ?array $k_bitrate): void
141
    {
142
        if (!is_null($sides) && is_null($k_bitrate)) {
0 ignored issues
show
introduced by
The condition is_null($sides) is always false.
Loading history...
introduced by
The condition is_null($k_bitrate) is always false.
Loading history...
143
            sort($sides);
144
        }
145
146
        $this->sides = array_values(array_filter($sides ?? $this->sides, [$this, 'sideFilter']));
147
    }
148
149
    /**
150
     * @param int $value
151
     * @param string $side
152
     * @return int
153
     */
154
    private function computeSide(int $value, string $side): int
155
    {
156
        $ratio = clone $this->getDimensions()->getRatio();
157
        return call_user_func_array([$ratio, 'calculate' . $side], [$value, $this->format->getModulus()]);
158
    }
159
160
    /**
161
     * @param $width
162
     * @param $k_bitrate
163
     * @param $height
164
     * @return Representation
165
     * @throws InvalidArgumentException
166
     */
167
    private function addRep($k_bitrate, $width, $height): Representation
168
    {
169
        return (new Representation)->setKiloBitrate($k_bitrate)->setResize($width, $height);
170
    }
171
172
    /**
173
     * @param array $reps
174
     */
175
    private function sortReps(array &$reps): void
176
    {
177
        usort($reps, function (Representation $rep1, Representation $rep2) {
178
            $ascending = $rep1->getKiloBitrate() > $rep2->getKiloBitrate();
179
            return $this->sort === "asc" ? $ascending : !$ascending;
180
        });
181
    }
182
183
    /**
184
     * @param bool $sort
185
     * @return array
186
     */
187
    public function getCalculatedReps(bool $sort = false): array
188
    {
189
        $reps = [];
190
        foreach ($this->sides as $key => $height) {
191
            array_push($reps, $this->addRep($this->k_bitrate[$key], $this->computeSide($height, 'Width'), $height));
192
        }
193
194
        if ($sort) {
195
            $this->sortReps($reps);
196
        }
197
198
        return $reps;
199
    }
200
201
    /**
202
     * @return Representation
203
     */
204
    public function getOriginalRep(): Representation
205
    {
206
        $dimension = $this->getDimensions();
207
        $width = $this->computeSide($dimension->getHeight(), 'Width');
208
        $height = $this->computeSide($dimension->getWidth(), 'Height');
209
210
        return $this->addRep($this->getKiloBitRate(), $width, $height);
211
    }
212
213
    /**
214
     * @return array
215
     */
216
    public function getSides(): array
217
    {
218
        return $this->sides;
219
    }
220
221
    /**
222
     * @return array
223
     */
224
    public function getKBitrate(): array
225
    {
226
        return $this->k_bitrate;
227
    }
228
229
    /**
230
     * Retrieve an external iterator reps
231
     * @return \Traversable An instance of an object implementing <b>Iterator</b> or <b>Traversable</b>
232
     */
233
    public function getIterator()
234
    {
235
        $reps = $this->getCalculatedReps();
236
        array_push($reps, $this->getOriginalRep());
237
        $this->sortReps($reps);
238
239
        return new \ArrayIterator($reps);
240
    }
241
}