AutoReps::sortReps()   A
last analyzed

Complexity

Conditions 2
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 2
eloc 3
nc 1
nop 1
dl 0
loc 5
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 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 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 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
introduced by
The condition is_null($k_bitrate_values) is always false.
Loading history...
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
introduced by
The condition is_null($k_bitrate) is always false.
Loading history...
introduced by
The condition is_null($sides) is always false.
Loading history...
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
}