• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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