xref: /aosp_15_r20/external/v4l2_codec2/common/EncodeHelpers.cpp (revision 0ec5a0ec62797f775085659156625e7f1bdb369f)
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