1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // #define LOG_NDEBUG 0
6 #define LOG_TAG "VideoFrame"
7
8 #include <string.h>
9
10 #include "video_frame.h"
11
12 #include <log/log.h>
13
14 namespace android {
15
16 namespace {
17
CopyWindow(const uint8_t * src,uint8_t * dst,size_t stride,size_t width,size_t height,size_t inc)18 void CopyWindow(const uint8_t* src, uint8_t* dst, size_t stride, size_t width, size_t height,
19 size_t inc) {
20 if (inc == 1 && stride == width) {
21 // Could copy by plane.
22 memcpy(dst, src, width * height);
23 return;
24 }
25
26 if (inc == 1) {
27 // Could copy by row.
28 for (size_t row = 0; row < height; ++row) {
29 memcpy(dst, src, width);
30 dst += width;
31 src += stride;
32 }
33 return;
34 }
35
36 // inc != 1, copy by pixel.
37 for (size_t row = 0; row < height; ++row) {
38 for (size_t col = 0; col < width; ++col) {
39 memcpy(dst, src, 1);
40 dst++;
41 src += inc;
42 }
43 src += (stride - width) * inc;
44 }
45 }
46
47 } // namespace
48
49 // static
Create(const uint8_t * data,size_t data_size,const Size & coded_size,const Size & visible_size,int32_t color_format)50 std::unique_ptr<VideoFrame> VideoFrame::Create(const uint8_t* data, size_t data_size,
51 const Size& coded_size, const Size& visible_size,
52 int32_t color_format) {
53 if (coded_size.IsEmpty() || visible_size.IsEmpty() || (visible_size.width > coded_size.width) ||
54 (visible_size.height > coded_size.height) || (coded_size.width % 2 != 0) ||
55 (coded_size.height % 2 != 0) || (visible_size.width % 2 != 0) ||
56 (visible_size.height % 2 != 0)) {
57 ALOGE("Size are not valid: coded: %dx%d, visible: %dx%d", coded_size.width,
58 coded_size.height, visible_size.width, visible_size.height);
59 return nullptr;
60 }
61
62 if (color_format != YUV_420_PLANAR && color_format != YUV_420_FLEXIBLE &&
63 color_format != HAL_PIXEL_FORMAT_YV12 && color_format != HAL_PIXEL_FORMAT_NV12) {
64 ALOGE("color_format is unknown: 0x%x", color_format);
65 return nullptr;
66 }
67
68 if (data_size < coded_size.width * coded_size.height * 3 / 2) {
69 ALOGE("data_size(=%zu) is not enough for coded_size(=%dx%d)", data_size, coded_size.width,
70 coded_size.height);
71 return nullptr;
72 }
73 // We've found in ARC++P H264 decoding, |data_size| of some output buffers are
74 // bigger than the area which |coded_size| needs (not observed on other codec
75 // and ARC++N).
76 // TODO(johnylin): find the root cause (b/130398258)
77 if (data_size > coded_size.width * coded_size.height * 3 / 2) {
78 ALOGV("data_size(=%zu) is bigger than the area coded_size(=%dx%d) needs.", data_size,
79 coded_size.width, coded_size.height);
80 }
81
82 return std::unique_ptr<VideoFrame>(
83 new VideoFrame(data, coded_size, visible_size, color_format));
84 }
85
VideoFrame(const uint8_t * data,const Size & coded_size,const Size & visible_size,int32_t color_format)86 VideoFrame::VideoFrame(const uint8_t* data, const Size& coded_size, const Size& visible_size,
87 int32_t color_format)
88 : data_(data),
89 coded_size_(coded_size),
90 visible_size_(visible_size),
91 color_format_(color_format) {
92 frame_data_[0] =
93 std::unique_ptr<uint8_t[]>(new uint8_t[visible_size_.width * visible_size_.height]());
94 frame_data_[1] = std::unique_ptr<uint8_t[]>(
95 new uint8_t[visible_size_.width * visible_size_.height / 4]());
96 frame_data_[2] = std::unique_ptr<uint8_t[]>(
97 new uint8_t[visible_size_.width * visible_size_.height / 4]());
98 if (IsFlexibleFormat()) {
99 ALOGV("Cannot convert video frame now, should be done later by matching HAL "
100 "format.");
101 return;
102 }
103 CopyAndConvertToI420Frame(color_format_);
104 }
105
IsFlexibleFormat() const106 bool VideoFrame::IsFlexibleFormat() const {
107 return color_format_ == YUV_420_FLEXIBLE;
108 }
109
CopyAndConvertToI420Frame(int32_t curr_format)110 void VideoFrame::CopyAndConvertToI420Frame(int32_t curr_format) {
111 size_t stride = coded_size_.width;
112 size_t slice_height = coded_size_.height;
113 size_t width = visible_size_.width;
114 size_t height = visible_size_.height;
115
116 const uint8_t* src = data_;
117 CopyWindow(src, frame_data_[0].get(), stride, width, height, 1); // copy Y
118 src += stride * slice_height;
119
120 switch (curr_format) {
121 case YUV_420_PLANAR:
122 CopyWindow(src, frame_data_[1].get(), stride / 2, width / 2, height / 2,
123 1); // copy U
124 src += stride * slice_height / 4;
125 CopyWindow(src, frame_data_[2].get(), stride / 2, width / 2, height / 2,
126 1); // copy V
127 return;
128 case HAL_PIXEL_FORMAT_NV12:
129 // NV12: semiplanar = true, crcb_swap = false.
130 CopyWindow(src, frame_data_[1].get(), stride / 2, width / 2, height / 2,
131 2); // copy U
132 src++;
133 CopyWindow(src, frame_data_[2].get(), stride / 2, width / 2, height / 2,
134 2); // copy V
135 return;
136 case HAL_PIXEL_FORMAT_YV12:
137 // YV12: semiplanar = false, crcb_swap = true.
138 CopyWindow(src, frame_data_[2].get(), stride / 2, width / 2, height / 2,
139 1); // copy V
140 src += stride * slice_height / 4;
141 CopyWindow(src, frame_data_[1].get(), stride / 2, width / 2, height / 2,
142 1); // copy U
143 return;
144 default:
145 ALOGE("Unknown format: 0x%x", curr_format);
146 return;
147 }
148 }
149
MatchHalFormatByGoldenMD5(const std::string & golden)150 bool VideoFrame::MatchHalFormatByGoldenMD5(const std::string& golden) {
151 if (!IsFlexibleFormat()) return true;
152
153 // Try to match with HAL_PIXEL_FORMAT_NV12 first.
154 int32_t format_candidates[2] = {HAL_PIXEL_FORMAT_NV12, HAL_PIXEL_FORMAT_YV12};
155 for (int32_t format : format_candidates) {
156 CopyAndConvertToI420Frame(format);
157 color_format_ = format;
158 std::string frame_md5 = ComputeMD5FromFrame();
159 if (!strcmp(frame_md5.c_str(), golden.c_str())) {
160 ALOGV("Matched YUV Flexible to HAL pixel format: 0x%x", format);
161 return true;
162 } else {
163 ALOGV("Tried HAL pixel format: 0x%x un-matched (%s vs %s)", format, frame_md5.c_str(),
164 golden.c_str());
165 }
166 }
167
168 // Change back to flexible format.
169 color_format_ = YUV_420_FLEXIBLE;
170 return false;
171 }
172
ComputeMD5FromFrame() const173 std::string VideoFrame::ComputeMD5FromFrame() const {
174 if (IsFlexibleFormat()) {
175 ALOGE("Cannot compute MD5 with format YUV_420_FLEXIBLE");
176 return std::string();
177 }
178
179 MD5Context context;
180 MD5Init(&context);
181 MD5Update(&context, std::string(reinterpret_cast<const char*>(frame_data_[0].get()),
182 visible_size_.width * visible_size_.height));
183 MD5Update(&context, std::string(reinterpret_cast<const char*>(frame_data_[1].get()),
184 visible_size_.width * visible_size_.height / 4));
185 MD5Update(&context, std::string(reinterpret_cast<const char*>(frame_data_[2].get()),
186 visible_size_.width * visible_size_.height / 4));
187 MD5Digest digest;
188 MD5Final(&digest, &context);
189 return MD5DigestToBase16(digest);
190 }
191
VerifyMD5(const std::string & golden)192 bool VideoFrame::VerifyMD5(const std::string& golden) {
193 if (IsFlexibleFormat()) {
194 // Color format is YUV_420_FLEXIBLE and we haven't match its HAL pixel
195 // format yet. Try to match now.
196 if (!MatchHalFormatByGoldenMD5(golden)) {
197 ALOGE("Failed to match any HAL format");
198 return false;
199 }
200 } else {
201 std::string md5 = ComputeMD5FromFrame();
202 if (strcmp(md5.c_str(), golden.c_str())) {
203 ALOGE("MD5 mismatched. expect: %s, got: %s", golden.c_str(), md5.c_str());
204 return false;
205 }
206 }
207 return true;
208 }
209
WriteFrame(std::ofstream * output_file) const210 bool VideoFrame::WriteFrame(std::ofstream* output_file) const {
211 if (IsFlexibleFormat()) {
212 ALOGE("Cannot write frame with format YUV_420_FLEXIBLE");
213 return false;
214 }
215
216 output_file->write(reinterpret_cast<const char*>(frame_data_[0].get()),
217 visible_size_.width * visible_size_.height);
218 output_file->write(reinterpret_cast<const char*>(frame_data_[1].get()),
219 visible_size_.width * visible_size_.height / 4);
220 output_file->write(reinterpret_cast<const char*>(frame_data_[2].get()),
221 visible_size_.width * visible_size_.height / 4);
222 return output_file->good();
223 }
224
225 } // namespace android
226