1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
media / base / video_transformation.cc [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "media/base/video_transformation.h"
#include <math.h>
#include <stddef.h>
#include <cmath>
#include "base/logging.h"
#include "base/notreached.h"
#include "base/numerics/angle_conversions.h"
#include "base/strings/string_number_conversions.h"
namespace media {
namespace {
template <size_t decimal_bits>
double FixedToFloatingPoint(int64_t i) {
return i / static_cast<double>(1 << decimal_bits);
}
} // namespace
std::string VideoRotationToString(VideoRotation rotation) {
switch (rotation) {
case VIDEO_ROTATION_0:
return "0°";
case VIDEO_ROTATION_90:
return "90°";
case VIDEO_ROTATION_180:
return "180°";
case VIDEO_ROTATION_270:
return "270°";
}
NOTREACHED();
}
bool operator==(const struct VideoTransformation& first,
const struct VideoTransformation& second) {
return first.rotation == second.rotation && first.mirrored == second.mirrored;
}
// static
VideoTransformation VideoTransformation::FromFFmpegDisplayMatrix(
const int32_t* matrix3x3) {
const int32_t matrix2x2[4] = {
matrix3x3[0],
matrix3x3[1],
matrix3x3[3],
matrix3x3[4],
};
return VideoTransformation(matrix2x2);
}
VideoTransformation::VideoTransformation(const int32_t matrix[4]) {
// Promote to int64_t to avoid abs(int32_min) being undefined.
const int64_t matrix64[4] = {matrix[0], matrix[1], matrix[2], matrix[3]};
// Rotation by angle Θ is represented in the matrix as:
// [ cos(Θ), -sin(Θ)]
// [ sin(Θ), cos(Θ)]
// A vertical flip is represented by the cosine's having opposite signs
// and a horizontal flip is represented by the sine's having the same sign.
// Check the matrix for validity
if (abs(matrix64[0]) != abs(matrix64[3]) ||
abs(matrix64[1]) != abs(matrix64[2])) {
rotation = VIDEO_ROTATION_0;
mirrored = false;
return;
}
double angle = base::RadToDeg(acos(FixedToFloatingPoint<16>(matrix64[0])));
double check_angle =
base::RadToDeg(asin(FixedToFloatingPoint<16>(matrix64[1])));
double offset = abs(abs(angle) - abs(check_angle));
while (offset >= 180.0)
offset -= 180.0;
if (offset > 1e-3) {
rotation = VIDEO_ROTATION_0;
mirrored = false;
return;
}
// Calculate angle offsets for rotation - rotating about the X axis
// can be expressed as a 180 degree rotation and a Y axis rotation
mirrored = false;
if (matrix64[0] != matrix64[3] && matrix64[0] != 0) {
mirrored = !mirrored;
angle += 180;
}
if (matrix64[1] == matrix64[3] && matrix64[1] != 0) {
mirrored = !mirrored;
}
// Normalize the angle
while (angle < 0)
angle += 360;
while (angle >= 360)
angle -= 360;
// 16 bits of fixed point decimal is enough to give 6 decimals of precision
// to cos(Θ). A delta of ±0.000001 causes acos(cos(Θ)) to differ by a minimum
// of 0.0002, which is why we only need to check that the angle is only
// accurate to within four decimal places. This is preferred to checking for
// a more precise accuracy, as the 'double' type is architecture dependent and
// there may be variance in floating point errors.
if (abs(angle - 0) < 1e-4) {
rotation = VIDEO_ROTATION_0;
} else if (abs(angle - 180) < 1e-4) {
rotation = VIDEO_ROTATION_180;
} else if (abs(angle - 90) < 1e-4) {
rotation = (check_angle > 0) ? VIDEO_ROTATION_90 : VIDEO_ROTATION_270;
} else {
rotation = VIDEO_ROTATION_0;
mirrored = false;
}
}
VideoTransformation::VideoTransformation(double rotation, bool mirrored) {
// `bounded_rotation` is an integer in (-360, 360).
double bounded_rotation = std::fmod(std::floor(rotation), 360);
// Add 360 to ensure non-negative, and another 45 to round up.
// `quarter_turns` is in [0, 8].
int quarter_turns = (static_cast<int>(bounded_rotation) + 360 + 45) / 90;
// Convert back to degrees.
int snapped_rotation = (quarter_turns % 4) * 90;
this->rotation = static_cast<VideoRotation>(snapped_rotation);
this->mirrored = mirrored;
}
VideoTransformation VideoTransformation::add(VideoTransformation delta) const {
int base_rotation = static_cast<int>(rotation);
int delta_rotation = static_cast<int>(delta.rotation);
if (mirrored) {
int combined_rotation = (base_rotation + (360 - delta_rotation)) % 360;
return VideoTransformation(static_cast<VideoRotation>(combined_rotation),
!delta.mirrored);
}
int combined_rotation = (base_rotation + delta_rotation) % 360;
return VideoTransformation(static_cast<VideoRotation>(combined_rotation),
delta.mirrored);
}
} // namespace media