Failed Conditions
Push — master ( bd9fc8...d384bc )
by Sébastien
03:00
created

InterlacementGuess::isInterlacedTff()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 6
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Soluble\MediaTools\Detection;
6
7
use Soluble\MediaTools\Filter\Video\EmptyVideoFilter;
8
use Soluble\MediaTools\Filter\Video\VideoFilterInterface;
9
use Soluble\MediaTools\Filter\Video\YadifVideoFilter;
10
11
class InterlacementGuess
12
{
13
    /**
14
     * Default interlacing detection threshold
15
     * 20% frames detected interlaced is a sufficient
16
     * threshold to detect interlacing.
17
     */
18
    public const INTERLACING_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
    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::INTERLACING_DETECTION_THRESHOLD
46
    ) {
47
        $this->detection_threshold = $detection_threshold;
48
        $detected_frames           = [
49
            self::MODE_INTERLACED_TFF => $nb_frames_interlaced_tff,
50
            self::MODE_INTERLACED_BFF => $nb_frames_interlaced_bff,
51
            self::MODE_PROGRESSIVE    => $nb_frames_progressive,
52
            self::MODE_UNDETERMINED   => $nb_frames_undetermined,
53
        ];
54
        arsort($detected_frames, SORT_NUMERIC);
55
        $this->detected_frames = $detected_frames;
56
        $this->total_frames    = (int) array_sum(array_values($this->detected_frames));
57
        $this->percent_frames  = [];
58
        foreach ($this->detected_frames as $key => $value) {
59
            $this->percent_frames[$key] = $value / $this->total_frames;
60
        }
61
    }
62
63
    /**
64
     * @return float[]
65
     */
66
    public function getStats(): array
67
    {
68
        return $this->percent_frames;
69
    }
70
71
    public function getBestGuess(?float $threshold = null): string
72
    {
73
        $min_pct = $threshold !== null ? $threshold : $this->detection_threshold;
74
        reset($this->detected_frames);
75
        $bestGuessKey = (string) key($this->detected_frames);
76
        if ($this->percent_frames[$bestGuessKey] >= $min_pct) {
77
            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
    public function isInterlacedTff(?float $threshold = null): bool
92
    {
93
        $min_pct = $threshold !== null ? $threshold : $this->detection_threshold;
94
95
        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
    public function isInterlacedBff(?float $threshold = null): bool
107
    {
108
        $min_pct = $threshold !== null ? $threshold : $this->detection_threshold;
109
110
        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
    public function isInterlaced(?float $threshold = null): bool
122
    {
123
        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
     * @param float|null $threshold
142
     *
143
     * @return EmptyVideoFilter|YadifVideoFilter
144
     */
145
    public function getDeinterlaceVideoFilter(?float $threshold = null): VideoFilterInterface
146
    {
147
        if (!$this->isInterlaced($threshold)) {
148
            return new EmptyVideoFilter();
149
        }
150
        $parity = YadifVideoFilter::DEFAULT_PARITY;
151
        if ($this->isInterlacedBff($threshold)) {
152
            // parity=1,  bff - Assume the bottom field is first.
153
            $parity = 1;
154
        } elseif ($this->isInterlacedTff($threshold)) {
155
            // parity=0,  tff - Assume the top field is first.
156
            $parity = 0;
157
        }
158
159
        return new YadifVideoFilter(YadifVideoFilter::DEFAULT_MODE, $parity, YadifVideoFilter::DEFAULT_DEINT);
160
    }
161
}
162