Passed
Push — master ( ac0e9d...b23da5 )
by Amin
04:21
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 Streaming\Exception\InvalidArgumentException;
17
use Streaming\Exception\RuntimeException;
18
use Streaming\Format\VideoInterface;
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 $height
151
     * @return int
152
     */
153
    private function computeWidth(int $height): int
154
    {
155
        return $this->getDimensions()->getRatio()->calculateWidth($height, $this->format->getModulus());
156
    }
157
158
    /**
159
     * @param $width
160
     * @param $k_bitrate
161
     * @param $height
162
     * @return Representation
163
     * @throws InvalidArgumentException
164
     */
165
    private function addRep($k_bitrate, $width, $height): Representation
166
    {
167
        return (new Representation)->setKiloBitrate($k_bitrate)->setResize($width, $height);
168
    }
169
170
    /**
171
     * @param array $reps
172
     */
173
    private function sortReps(array &$reps): void
174
    {
175
        usort($reps, function (Representation $rep1, Representation $rep2) {
176
            $ascending = $rep1->getKiloBitrate() > $rep2->getKiloBitrate();
177
            return $this->sort === "asc" ? $ascending : !$ascending;
178
        });
179
    }
180
181
    /**
182
     * @param bool $sort
183
     * @return array
184
     */
185
    public function getCalculatedReps(bool $sort = false): array
186
    {
187
        $reps = [];
188
        foreach ($this->sides as $key => $height) {
189
            array_push($reps, $this->addRep($this->k_bitrate[$key], $this->computeWidth($height), $height));
190
        }
191
192
        if ($sort) {
193
            $this->sortReps($reps);
194
        }
195
196
        return $reps;
197
    }
198
199
    /**
200
     * @return Representation
201
     */
202
    public function getOriginalRep(): Representation
203
    {
204
        $dimension = $this->getDimensions();
205
        return $this->addRep($this->getKiloBitRate(), $dimension->getWidth(), $dimension->getHeight());
206
    }
207
208
    /**
209
     * @return array
210
     */
211
    public function getSides(): array
212
    {
213
        return $this->sides;
214
    }
215
216
    /**
217
     * @return array
218
     */
219
    public function getKBitrate(): array
220
    {
221
        return $this->k_bitrate;
222
    }
223
224
    /**
225
     * Retrieve an external iterator reps
226
     * @return \Traversable An instance of an object implementing <b>Iterator</b> or <b>Traversable</b>
227
     */
228
    public function getIterator()
229
    {
230
        $reps = $this->getCalculatedReps();
231
        array_push($reps, $this->getOriginalRep());
232
        $this->sortReps($reps);
233
234
        return new \ArrayIterator($reps);
235
    }
236
}