Passed
Push — master ( d1942c...ad5186 )
by Sébastien
03:08
created

InterlaceGuess::isInterlacedTff()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Soluble\MediaTools\Video\Detection;
6
7
use Soluble\MediaTools\Video\Filter\EmptyVideoFilter;
8
use Soluble\MediaTools\Video\Filter\VideoFilterInterface;
9
use Soluble\MediaTools\Video\Filter\YadifVideoFilter;
10
11
class InterlaceGuess
12
{
13
    /**
14
     * Default interlacing detection threshold
15
     * 20% frames detected interlaced is a sufficient
16
     * threshold to detect interlacing.
17
     */
18
    public const DEFAULT_DETECTION_THRESHOLD = 0.2;
19
20
    public const MODE_INTERLACED_BFF = 'INTERLACED_BFF';
21
    public const MODE_INTERLACED_TFF = 'INTERLACED_TFF';
22
    public const MODE_PROGRESSIVE    = 'PROGRESSIVE';
23
    public const MODE_UNDETERMINED   = 'UNDETERMINED';
24
25
    /** @var float */
26
    protected $detection_threshold;
27
28
    /** @var int */
29
    protected $total_frames;
30
31
    /** @var array<string, int> */
32
    protected $detected_frames;
33
34
    /** @var array<string, float> */
35
    protected $percent_frames;
36
37
    /**
38
     * @param float $detection_threshold in percent: i.e 0.8, 0.6...
39
     */
40 1
    public function __construct(
41
        int $nb_frames_interlaced_tff,
42
        int $nb_frames_interlaced_bff,
43
        int $nb_frames_progressive,
44
        int $nb_frames_undetermined,
45
        float $detection_threshold = self::DEFAULT_DETECTION_THRESHOLD
46
    ) {
47 1
        $this->detection_threshold = $detection_threshold;
48
        $detected_frames           = [
49 1
            self::MODE_INTERLACED_TFF => $nb_frames_interlaced_tff,
50 1
            self::MODE_INTERLACED_BFF => $nb_frames_interlaced_bff,
51 1
            self::MODE_PROGRESSIVE    => $nb_frames_progressive,
52 1
            self::MODE_UNDETERMINED   => $nb_frames_undetermined,
53
        ];
54 1
        arsort($detected_frames, SORT_NUMERIC);
55 1
        $this->detected_frames = $detected_frames;
56 1
        $this->total_frames    = (int) array_sum(array_values($this->detected_frames));
57 1
        $this->percent_frames  = [];
58 1
        foreach ($this->detected_frames as $key => $value) {
59 1
            $this->percent_frames[$key] = $value / $this->total_frames;
60
        }
61 1
    }
62
63
    /**
64
     * @return float[]
65
     */
66 1
    public function getStats(): array
67
    {
68 1
        return $this->percent_frames;
69
    }
70
71 1
    public function getBestGuess(?float $threshold = null): string
72
    {
73 1
        $min_pct = $threshold !== null ? $threshold : $this->detection_threshold;
74 1
        reset($this->detected_frames);
75 1
        $bestGuessKey = (string) key($this->detected_frames);
76 1
        if ($this->percent_frames[$bestGuessKey] >= $min_pct) {
77 1
            return $bestGuessKey;
78
        }
79
80
        return self::MODE_UNDETERMINED;
81
    }
82
83
    /**
84
     * Whether the video seems to be interlaced in TFF (top field first)
85
     * within a certain probability threshold.
86
     *
87
     * @param float|null $threshold
88
     *
89
     * @return bool
90
     */
91 1
    public function isInterlacedTff(?float $threshold = null): bool
92
    {
93 1
        $min_pct = $threshold !== null ? $threshold : $this->detection_threshold;
94
95 1
        return $this->percent_frames[self::MODE_INTERLACED_TFF] >= $min_pct;
96
    }
97
98
    /**
99
     * Whether the video seems to be interlaced in BFF (bottom field first)
100
     * within a certain probability threshold.
101
     *
102
     * @param float|null $threshold
103
     *
104
     * @return bool
105
     */
106 1
    public function isInterlacedBff(?float $threshold = null): bool
107
    {
108 1
        $min_pct = $threshold !== null ? $threshold : $this->detection_threshold;
109
110 1
        return $this->percent_frames[self::MODE_INTERLACED_BFF] >= $min_pct;
111
    }
112
113
    /**
114
     * Whether the video seems to be interlaced either in BFF (bottom field first)
115
     * or TFF (top field first) within a certain probability threshold.
116
     *
117
     * @param float|null $threshold
118
     *
119
     * @return bool
120
     */
121 1
    public function isInterlaced(?float $threshold = null): bool
122
    {
123 1
        return $this->isInterlacedBff($threshold) || $this->isInterlacedTff($threshold);
124
    }
125
126
    public function isProgressive(?float $threshold = null): bool
127
    {
128
        $min_pct = $threshold !== null ? $threshold : $this->detection_threshold;
129
130
        return $this->percent_frames[self::MODE_PROGRESSIVE] >= $min_pct;
131
    }
132
133
    public function isUndetermined(?float $threshold = null): bool
134
    {
135
        $min_pct = $threshold !== null ? $threshold : $this->detection_threshold;
136
137
        return $this->percent_frames[self::MODE_UNDETERMINED] >= $min_pct;
138
    }
139
140
    /**
141
     * @see https://ffmpeg.org/ffmpeg-filters.html (section yadif)
142
     * @see https://askubuntu.com/a/867203
143
     *
144
     * @param float|null $threshold
145
     *
146
     * @return EmptyVideoFilter|YadifVideoFilter
147
     */
148
    public function getDeinterlaceVideoFilter(?float $threshold = null): VideoFilterInterface
149
    {
150
        if (!$this->isInterlaced($threshold)) {
151
            return new EmptyVideoFilter();
152
        }
153
        $parity = YadifVideoFilter::DEFAULT_PARITY;
154
        if ($this->isInterlacedBff($threshold)) {
155
            // parity=1,  bff - Assume the bottom field is first.
156
            $parity = 1;
157
        } elseif ($this->isInterlacedTff($threshold)) {
158
            // parity=0,  tff - Assume the top field is first.
159
            $parity = 0;
160
        }
161
162
        return new YadifVideoFilter(YadifVideoFilter::DEFAULT_MODE, $parity, YadifVideoFilter::DEFAULT_DEINT);
163
    }
164
}
165