Passed
Push — master ( a5ac83...5a62c8 )
by Sébastien
02:13
created

VideoInfo   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 221
Duplicated Lines 0 %

Test Coverage

Coverage 93.67%

Importance

Changes 0
Metric Value
wmc 28
eloc 69
dl 0
loc 221
ccs 74
cts 79
cp 0.9367
rs 10
c 0
b 0
f 0

18 Methods

Rating   Name   Duplication   Size   Complexity  
A getDimensions() 0 5 1
A getMetadata() 0 3 1
A getWidth() 0 5 1
A getDuration() 0 3 1
A getNbFrames() 0 5 1
A getVideoBitrate() 0 5 1
A getFormatName() 0 3 1
A countStreams() 0 3 1
A getFile() 0 3 1
A __construct() 0 10 2
A getHeight() 0 5 1
A getFileSize() 0 11 2
A createFromFFProbeJson() 0 11 3
A getStreamMetadataByType() 0 11 2
A getAudioStreamMetadata() 0 3 1
A getVideoStreamMetadata() 0 3 1
A getAudioBitrate() 0 5 1
B getMetadataByStreamType() 0 28 6
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @see       https://github.com/soluble-io/soluble-mediatools for the canonical repository
7
 *
8
 * @copyright Copyright (c) 2018-2019 Sébastien Vanvelthem. (https://github.com/belgattitude)
9
 * @license   https://github.com/soluble-io/soluble-mediatools/blob/master/LICENSE.md MIT
10
 */
11
12
namespace Soluble\MediaTools\Video;
13
14
use Soluble\MediaTools\Common\Exception\IOException;
15
use Soluble\MediaTools\Common\Exception\JsonParseException;
16
use Soluble\MediaTools\Video\Exception\InvalidArgumentException;
17
use Soluble\MediaTools\Video\Exception\UnexpectedValueException;
18
19
class VideoInfo implements VideoInfoInterface
20
{
21
    /** @var array<string, mixed> */
22
    private $metadata;
23
24
    /** @var string */
25
    private $file;
26
27
    /** @var array|null */
28
    private $metadataByStreamType;
29
30
    /**
31
     * @param string $fileName reference to filename
32
     * @param array  $metadata metadata as parsed from ffprobe --json
33
     *
34
     * @throws IOException
35
     */
36 13
    public function __construct(string $fileName, array $metadata)
37
    {
38 13
        if (!file_exists($fileName)) {
39 1
            throw new IOException(sprintf(
40 1
                'File %s does not exists',
41 1
                $this->file
42
            ));
43
        }
44 12
        $this->metadata = $metadata;
45 12
        $this->file     = $fileName;
46 12
    }
47
48
    /**
49
     * @throws JsonParseException if json is invalid
50
     */
51 5
    public static function createFromFFProbeJson(string $fileName, string $ffprobeJson): self
52
    {
53 5
        if (trim($ffprobeJson) === '') {
54 1
            throw new JsonParseException('Cannot parse empty json string');
55
        }
56 4
        $decoded = json_decode($ffprobeJson, true);
57 4
        if ($decoded === null) {
58 1
            throw new JsonParseException('Cannot parse json');
59
        }
60
61 3
        return new self($fileName, $decoded);
62
    }
63
64 2
    public function getFile(): string
65
    {
66 2
        return $this->file;
67
    }
68
69
    /**
70
     * @throws IOException
71
     */
72 2
    public function getFileSize(): int
73
    {
74 2
        $size = @filesize($this->file);
75 2
        if ($size === false) {
76 1
            throw new IOException(sprintf(
77 1
                'Cannot get filesize of file %s',
78 1
                $this->file
79
            ));
80
        }
81
82 1
        return $size;
83
    }
84
85
    /**
86
     * Format name as returned by ffprobe.
87
     */
88 2
    public function getFormatName(): string
89
    {
90 2
        return $this->metadata['format']['format_name'];
91
    }
92
93 2
    public function countStreams(): int
94
    {
95 2
        return $this->metadata['format']['nb_streams'];
96
    }
97
98 1
    public function getMetadata(): array
99
    {
100 1
        return $this->metadata;
101
    }
102
103 2
    public function getDuration(): float
104
    {
105 2
        return (float) ($this->metadata['format']['duration'] ?? 0.0);
106
    }
107
108
    /**
109
     * @throws UnexpectedValueException
110
     */
111 2
    public function getDimensions(): array
112
    {
113
        return [
114 2
            'width'  => $this->getWidth(),
115 2
            'height' => $this->getHeight(),
116
        ];
117
    }
118
119
    /**
120
     * @throws UnexpectedValueException
121
     */
122 3
    public function getWidth(): int
123
    {
124 3
        $videoStream = $this->getVideoStreamMetadata()[0] ?? [];
125
126 3
        return (int) ($videoStream['width'] ?? 0);
127
    }
128
129
    /**
130
     * @throws UnexpectedValueException
131
     */
132 3
    public function getHeight(): int
133
    {
134 3
        $videoStream = $this->getVideoStreamMetadata()[0] ?? [];
135
136 3
        return (int) ($videoStream['height'] ?? 0);
137
    }
138
139
    /**
140
     * @throws UnexpectedValueException
141
     */
142 2
    public function getNbFrames(): int
143
    {
144 2
        $videoStream = $this->getVideoStreamMetadata()[0] ?? [];
145
146 2
        return (int) ($videoStream['nb_frames'] ?? 0);
147
    }
148
149
    /**
150
     * Convenience method to get first video stream bitrate.
151
     *
152
     * @throws UnexpectedValueException
153
     */
154 1
    public function getVideoBitrate(): int
155
    {
156 1
        $videoStream = $this->getVideoStreamMetadata()[0] ?? [];
157
158 1
        return (int) ($videoStream['bit_rate'] ?? 0);
159
    }
160
161
    /**
162
     * Convenience method to get first audio stream bitrate.
163
     *
164
     * @throws UnexpectedValueException
165
     */
166 1
    public function getAudioBitrate(): int
167
    {
168 1
        $videoStream = $this->getVideoStreamMetadata()[0] ?? [];
169
170 1
        return (int) ($videoStream['bit_rate'] ?? 0);
171
    }
172
173
    /**
174
     * @throws UnexpectedValueException
175
     */
176
    public function getAudioStreamMetadata(): array
177
    {
178
        return $this->getStreamMetadataByType(self::STREAM_TYPE_AUDIO);
179
    }
180
181
    /**
182
     * @throws UnexpectedValueException
183
     */
184 5
    public function getVideoStreamMetadata(): ?array
185
    {
186 5
        return $this->getStreamMetadataByType(self::STREAM_TYPE_VIDEO);
187
    }
188
189
    /**
190
     * @throws UnexpectedValueException
191
     *
192
     * @param string $streamType any of self::SUPPORTED_STREAM_TYPES
193
     *
194
     * @return array<string, mixed>
195
     */
196 6
    public function getStreamMetadataByType(string $streamType): array
197
    {
198 6
        if (!in_array($streamType, self::SUPPORTED_STREAM_TYPES, true)) {
199 1
            throw new InvalidArgumentException(sprintf(
200 1
               'Invalid usage, unsupported param $streamType given: %s',
201
               $streamType
202
            ));
203
        }
204 5
        $md = $this->getMetadataByStreamType();
205
206 5
        return $md[$streamType];
207
    }
208
209
    /**
210
     * @throws UnexpectedValueException
211
     */
212 5
    private function getMetadataByStreamType(): array
213
    {
214 5
        if ($this->metadataByStreamType === null) {
215
            $streams = [
216 5
                self::STREAM_TYPE_VIDEO => [],
217 5
                self::STREAM_TYPE_AUDIO => [],
218 5
                self::STREAM_TYPE_DATA  => [],
219
            ];
220 5
            foreach ($this->metadata['streams'] as $stream) {
221 5
                $type = mb_strtolower($stream['codec_type']);
222
                switch ($type) {
223 5
                    case self::STREAM_TYPE_VIDEO:
224 5
                        $streams[self::STREAM_TYPE_VIDEO][] = $stream;
225 5
                        break;
226 5
                    case self::STREAM_TYPE_AUDIO:
227 5
                        $streams[self::STREAM_TYPE_AUDIO][] = $stream;
228 5
                        break;
229
                    case self::STREAM_TYPE_DATA:
230
                        $streams[self::STREAM_TYPE_DATA][] = $stream;
231
                        break;
232
                    default:
233 5
                        throw new UnexpectedValueException(sprintf('Does not support codec_type "%s"', $type));
234
                }
235
            }
236 5
            $this->metadataByStreamType = $streams;
237
        }
238
239 5
        return $this->metadataByStreamType;
1 ignored issue
show
Bug Best Practice introduced by
The expression return $this->metadataByStreamType could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
240
    }
241
}
242