1 // Copyright 2020 The Chromium 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 "EncodeHelpers"
7
8 #include <v4l2_codec2/common/EncodeHelpers.h>
9
10 #include <linux/v4l2-controls.h>
11
12 #include <C2AllocatorGralloc.h>
13 #include <cutils/native_handle.h>
14 #include <ui/GraphicBuffer.h>
15 #include <utils/Log.h>
16
17 #include <v4l2_codec2/common/H264NalParser.h>
18
19 namespace android {
20
21 namespace {
22
23 // Android frameworks needs 4 bytes start code.
24 constexpr uint8_t kH264StartCode[] = {0x00, 0x00, 0x00, 0x01};
25 constexpr size_t kH264StartCodeSize = 4;
26
27 // Copy an H.264 NAL unit with size |srcSize| (without a start code) into a buffer with size
28 // |dstSize|. An H.264 start code is prepended to the NAL unit. After copying |dst| is adjusted to
29 // point to the address immediately following the copied data, and the |dstSize| is updated to
30 // reflect the remaining destination buffer size.
copyNALUPrependingStartCode(const uint8_t * src,size_t srcSize,uint8_t ** dst,size_t * dstSize)31 bool copyNALUPrependingStartCode(const uint8_t* src, size_t srcSize, uint8_t** dst,
32 size_t* dstSize) {
33 size_t naluSize = srcSize + kH264StartCodeSize;
34 if (naluSize > *dstSize) {
35 ALOGE("Couldn't copy NAL unit, not enough space in destination buffer");
36 return false;
37 }
38 memcpy(*dst, kH264StartCode, kH264StartCodeSize);
39 memcpy(*dst + kH264StartCodeSize, src, srcSize);
40 *dst += naluSize;
41 *dstSize -= naluSize;
42 return true;
43 }
44
45 } // namespace
46
c2LevelToV4L2Level(C2Config::level_t level)47 uint8_t c2LevelToV4L2Level(C2Config::level_t level) {
48 switch (level) {
49 case C2Config::LEVEL_AVC_1:
50 return V4L2_MPEG_VIDEO_H264_LEVEL_1_0;
51 case C2Config::LEVEL_AVC_1B:
52 return V4L2_MPEG_VIDEO_H264_LEVEL_1B;
53 case C2Config::LEVEL_AVC_1_1:
54 return V4L2_MPEG_VIDEO_H264_LEVEL_1_1;
55 case C2Config::LEVEL_AVC_1_2:
56 return V4L2_MPEG_VIDEO_H264_LEVEL_1_2;
57 case C2Config::LEVEL_AVC_1_3:
58 return V4L2_MPEG_VIDEO_H264_LEVEL_1_3;
59 case C2Config::LEVEL_AVC_2:
60 return V4L2_MPEG_VIDEO_H264_LEVEL_2_0;
61 case C2Config::LEVEL_AVC_2_1:
62 return V4L2_MPEG_VIDEO_H264_LEVEL_2_1;
63 case C2Config::LEVEL_AVC_2_2:
64 return V4L2_MPEG_VIDEO_H264_LEVEL_2_2;
65 case C2Config::LEVEL_AVC_3:
66 return V4L2_MPEG_VIDEO_H264_LEVEL_3_0;
67 case C2Config::LEVEL_AVC_3_1:
68 return V4L2_MPEG_VIDEO_H264_LEVEL_3_1;
69 case C2Config::LEVEL_AVC_3_2:
70 return V4L2_MPEG_VIDEO_H264_LEVEL_3_2;
71 case C2Config::LEVEL_AVC_4:
72 return V4L2_MPEG_VIDEO_H264_LEVEL_4_0;
73 case C2Config::LEVEL_AVC_4_1:
74 return V4L2_MPEG_VIDEO_H264_LEVEL_4_1;
75 case C2Config::LEVEL_AVC_4_2:
76 return V4L2_MPEG_VIDEO_H264_LEVEL_4_2;
77 case C2Config::LEVEL_AVC_5:
78 return V4L2_MPEG_VIDEO_H264_LEVEL_5_0;
79 case C2Config::LEVEL_AVC_5_1:
80 return V4L2_MPEG_VIDEO_H264_LEVEL_5_1;
81 default:
82 ALOGE("Unrecognizable C2 level (value = 0x%x)...", level);
83 return 0;
84 }
85 }
86
getGraphicBlockInfo(const C2ConstGraphicBlock & block)87 android_ycbcr getGraphicBlockInfo(const C2ConstGraphicBlock& block) {
88 uint32_t width, height, format, stride, igbp_slot, generation;
89 uint64_t usage, igbp_id;
90 android::_UnwrapNativeCodec2GrallocMetadata(block.handle(), &width, &height, &format, &usage,
91 &stride, &generation, &igbp_id, &igbp_slot);
92 native_handle_t* grallocHandle = android::UnwrapNativeCodec2GrallocHandle(block.handle());
93 sp<GraphicBuffer> buf = new GraphicBuffer(grallocHandle, GraphicBuffer::CLONE_HANDLE, width,
94 height, format, 1, usage, stride);
95 native_handle_delete(grallocHandle);
96
97 // Pass SW flag so that ARCVM returns the guest buffer dimensions instead
98 // of the host buffer dimensions. This means we will have to convert the
99 // return value from ptrs to buffer offsets ourselves.
100 android_ycbcr ycbcr = {};
101 int32_t status = buf->lockYCbCr(GRALLOC_USAGE_SW_READ_OFTEN, &ycbcr);
102 if (status != OK) ALOGE("lockYCbCr is failed: %d", (int)status);
103 buf->unlock();
104
105 uintptr_t y = reinterpret_cast<uintptr_t>(ycbcr.y);
106 ycbcr.y = nullptr;
107 ycbcr.cb = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ycbcr.cb) - y);
108 ycbcr.cr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ycbcr.cr) - y);
109
110 return ycbcr;
111 }
112
extractSPSPPS(const uint8_t * data,size_t length,std::vector<uint8_t> * sps,std::vector<uint8_t> * pps)113 bool extractSPSPPS(const uint8_t* data, size_t length, std::vector<uint8_t>* sps,
114 std::vector<uint8_t>* pps) {
115 bool foundSPS = false;
116 bool foundPPS = false;
117 H264NalParser parser(data, length);
118 while (!(foundSPS && foundPPS) && parser.locateNextNal()) {
119 switch (parser.type()) {
120 case H264NalParser::kSPSType:
121 sps->resize(parser.length());
122 memcpy(sps->data(), parser.data(), parser.length());
123 foundSPS = true;
124 break;
125 case H264NalParser::kPPSType:
126 pps->resize(parser.length());
127 memcpy(pps->data(), parser.data(), parser.length());
128 foundPPS = true;
129 break;
130 }
131 }
132 return foundSPS && foundPPS;
133 }
134
extractCSDInfo(std::unique_ptr<C2StreamInitDataInfo::output> * const csd,const uint8_t * data,size_t length)135 bool extractCSDInfo(std::unique_ptr<C2StreamInitDataInfo::output>* const csd, const uint8_t* data,
136 size_t length) {
137 csd->reset();
138
139 std::vector<uint8_t> sps;
140 std::vector<uint8_t> pps;
141 if (!extractSPSPPS(data, length, &sps, &pps)) {
142 return false;
143 }
144
145 size_t configDataLength = sps.size() + pps.size() + (2u * kH264StartCodeSize);
146 ALOGV("Extracted codec config data: length=%zu", configDataLength);
147
148 *csd = C2StreamInitDataInfo::output::AllocUnique(configDataLength, 0u);
149 uint8_t* csdBuffer = (*csd)->m.value;
150 return copyNALUPrependingStartCode(sps.data(), sps.size(), &csdBuffer, &configDataLength) &&
151 copyNALUPrependingStartCode(pps.data(), pps.size(), &csdBuffer, &configDataLength);
152 }
153
prependSPSPPSToIDR(const uint8_t * src,size_t srcSize,uint8_t * dst,size_t dstSize,std::vector<uint8_t> * sps,std::vector<uint8_t> * pps)154 size_t prependSPSPPSToIDR(const uint8_t* src, size_t srcSize, uint8_t* dst, size_t dstSize,
155 std::vector<uint8_t>* sps, std::vector<uint8_t>* pps) {
156 bool foundStreamParams = false;
157 size_t remainingDstSize = dstSize;
158 H264NalParser parser(src, srcSize);
159 while (parser.locateNextNal()) {
160 switch (parser.type()) {
161 case H264NalParser::kSPSType:
162 // SPS found, copy to cache.
163 ALOGV("Found SPS (length %zu)", parser.length());
164 sps->resize(parser.length());
165 memcpy(sps->data(), parser.data(), parser.length());
166 foundStreamParams = true;
167 break;
168 case H264NalParser::kPPSType:
169 // PPS found, copy to cache.
170 ALOGV("Found PPS (length %zu)", parser.length());
171 pps->resize(parser.length());
172 memcpy(pps->data(), parser.data(), parser.length());
173 foundStreamParams = true;
174 break;
175 case H264NalParser::kIDRType:
176 ALOGV("Found IDR (length %zu)", parser.length());
177 if (foundStreamParams) {
178 ALOGV("Not injecting SPS and PPS before IDR, already present");
179 break;
180 }
181
182 // Prepend the cached SPS and PPS to the IDR NAL unit.
183 if (sps->empty() || pps->empty()) {
184 ALOGE("No cached SPS or PPS NAL unit available to inject before IDR");
185 return 0;
186 }
187 if (!copyNALUPrependingStartCode(sps->data(), sps->size(), &dst, &remainingDstSize)) {
188 ALOGE("Not enough space to inject SPS NAL unit before IDR");
189 return 0;
190 }
191 if (!copyNALUPrependingStartCode(pps->data(), pps->size(), &dst, &remainingDstSize)) {
192 ALOGE("Not enough space to inject PPS NAL unit before IDR");
193 return 0;
194 }
195
196 ALOGV("Stream header injected before IDR");
197 break;
198 }
199
200 // Copy the NAL unit to the new output buffer.
201 if (!copyNALUPrependingStartCode(parser.data(), parser.length(), &dst, &remainingDstSize)) {
202 ALOGE("NAL unit does not fit in the provided output buffer");
203 return 0;
204 }
205 }
206
207 return dstSize - remainingDstSize;
208 }
209
210 } // namespace android
211