xref: /aosp_15_r20/frameworks/av/media/libstagefright/codecs/on2/dec/SoftVPX.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "SoftVPX"
19 #include <utils/Log.h>
20 #include <utils/misc.h>
21 #include "OMX_VideoExt.h"
22 
23 #include "SoftVPX.h"
24 
25 #include <media/stagefright/foundation/ADebug.h>
26 #include <media/stagefright/MediaDefs.h>
27 
28 
29 namespace android {
30 
31 // Only need to declare the highest supported profile and level here.
32 static const CodecProfileLevel kVP9ProfileLevels[] = {
33     { OMX_VIDEO_VP9Profile0, OMX_VIDEO_VP9Level5 },
34     { OMX_VIDEO_VP9Profile2, OMX_VIDEO_VP9Level5 },
35     { OMX_VIDEO_VP9Profile2HDR, OMX_VIDEO_VP9Level5 },
36     { OMX_VIDEO_VP9Profile2HDR10Plus, OMX_VIDEO_VP9Level5 },
37 };
38 
SoftVPX(const char * name,const char * componentRole,OMX_VIDEO_CODINGTYPE codingType,const OMX_CALLBACKTYPE * callbacks,OMX_PTR appData,OMX_COMPONENTTYPE ** component)39 SoftVPX::SoftVPX(
40         const char *name,
41         const char *componentRole,
42         OMX_VIDEO_CODINGTYPE codingType,
43         const OMX_CALLBACKTYPE *callbacks,
44         OMX_PTR appData,
45         OMX_COMPONENTTYPE **component)
46     : SoftVideoDecoderOMXComponent(
47             name, componentRole, codingType,
48             codingType == OMX_VIDEO_CodingVP8 ? NULL : kVP9ProfileLevels,
49             codingType == OMX_VIDEO_CodingVP8 ?  0 : NELEM(kVP9ProfileLevels),
50             320 /* width */, 240 /* height */, callbacks, appData, component),
51       mMode(codingType == OMX_VIDEO_CodingVP8 ? MODE_VP8 : MODE_VP9),
52       mEOSStatus(INPUT_DATA_AVAILABLE),
53       mCtx(NULL),
54       mFrameParallelMode(false),
55       mTimeStampIdx(0),
56       mImg(NULL) {
57     // arbitrary from avc/hevc as vpx does not specify a min compression ratio
58     const size_t kMinCompressionRatio = mMode == MODE_VP8 ? 2 : 4;
59     const char *mime = mMode == MODE_VP8 ? MEDIA_MIMETYPE_VIDEO_VP8 : MEDIA_MIMETYPE_VIDEO_VP9;
60     const size_t kMaxOutputBufferSize = 2048 * 2048 * 3 / 2;
61     initPorts(
62             kNumBuffers, kMaxOutputBufferSize / kMinCompressionRatio /* inputBufferSize */,
63             kNumBuffers, mime, kMinCompressionRatio);
64     CHECK_EQ(initDecoder(), (status_t)OK);
65 }
66 
~SoftVPX()67 SoftVPX::~SoftVPX() {
68     destroyDecoder();
69 }
70 
GetCPUCoreCount()71 static int GetCPUCoreCount() {
72     int cpuCoreCount = 1;
73 #if defined(_SC_NPROCESSORS_ONLN)
74     cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
75 #else
76     // _SC_NPROC_ONLN must be defined...
77     cpuCoreCount = sysconf(_SC_NPROC_ONLN);
78 #endif
79     CHECK(cpuCoreCount >= 1);
80     ALOGV("Number of CPU cores: %d", cpuCoreCount);
81     return cpuCoreCount;
82 }
83 
supportDescribeHdrStaticInfo()84 bool SoftVPX::supportDescribeHdrStaticInfo() {
85     return true;
86 }
87 
supportDescribeHdr10PlusInfo()88 bool SoftVPX::supportDescribeHdr10PlusInfo() {
89     return true;
90 }
91 
initDecoder()92 status_t SoftVPX::initDecoder() {
93     mCtx = new vpx_codec_ctx_t;
94     vpx_codec_err_t vpx_err;
95     vpx_codec_dec_cfg_t cfg;
96     vpx_codec_flags_t flags;
97     memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t));
98     memset(&flags, 0, sizeof(vpx_codec_flags_t));
99     cfg.threads = GetCPUCoreCount();
100 
101     if (mFrameParallelMode) {
102         flags |= VPX_CODEC_USE_FRAME_THREADING;
103     }
104 
105     if ((vpx_err = vpx_codec_dec_init(
106                 (vpx_codec_ctx_t *)mCtx,
107                  mMode == MODE_VP8 ? &vpx_codec_vp8_dx_algo : &vpx_codec_vp9_dx_algo,
108                  &cfg, flags))) {
109         ALOGE("on2 decoder failed to initialize. (%d)", vpx_err);
110         return UNKNOWN_ERROR;
111     }
112 
113     return OK;
114 }
115 
destroyDecoder()116 status_t SoftVPX::destroyDecoder() {
117     vpx_codec_destroy((vpx_codec_ctx_t *)mCtx);
118     delete (vpx_codec_ctx_t *)mCtx;
119     mCtx = NULL;
120     return OK;
121 }
122 
outputBuffers(bool flushDecoder,bool display,bool eos,bool * portWillReset)123 bool SoftVPX::outputBuffers(bool flushDecoder, bool display, bool eos, bool *portWillReset) {
124     List<BufferInfo *> &outQueue = getPortQueue(1);
125     BufferInfo *outInfo = NULL;
126     OMX_BUFFERHEADERTYPE *outHeader = NULL;
127     vpx_codec_iter_t iter = NULL;
128 
129     if (flushDecoder && mFrameParallelMode) {
130         // Flush decoder by passing NULL data ptr and 0 size.
131         // Ideally, this should never fail.
132         if (vpx_codec_decode((vpx_codec_ctx_t *)mCtx, NULL, 0, NULL, 0)) {
133             ALOGE("Failed to flush on2 decoder.");
134             return false;
135         }
136     }
137 
138     if (!display) {
139         if (!flushDecoder) {
140             ALOGE("Invalid operation.");
141             return false;
142         }
143         // Drop all the decoded frames in decoder.
144         while ((mImg = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter))) {
145         }
146         return true;
147     }
148 
149     while (!outQueue.empty()) {
150         if (mImg == NULL) {
151             mImg = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter);
152             if (mImg == NULL) {
153                 break;
154             }
155         }
156         uint32_t width = mImg->d_w;
157         uint32_t height = mImg->d_h;
158         outInfo = *outQueue.begin();
159         outHeader = outInfo->mHeader;
160         CHECK(mImg->fmt == VPX_IMG_FMT_I420 || mImg->fmt == VPX_IMG_FMT_I42016);
161         OMX_COLOR_FORMATTYPE outputColorFormat = OMX_COLOR_FormatYUV420Planar;
162         int32_t bpp = 1;
163         if (mImg->fmt == VPX_IMG_FMT_I42016) {
164             outputColorFormat = OMX_COLOR_FormatYUV420Planar16;
165             bpp = 2;
166         }
167         handlePortSettingsChange(portWillReset, width, height, outputColorFormat);
168         if (*portWillReset) {
169             return true;
170         }
171 
172         outHeader->nOffset = 0;
173         outHeader->nFlags = 0;
174         outHeader->nFilledLen = (outputBufferWidth() * outputBufferHeight() * bpp * 3) / 2;
175         PrivInfo *privInfo = (PrivInfo *)mImg->user_priv;
176         outHeader->nTimeStamp = privInfo->mTimeStamp;
177         if (privInfo->mHdr10PlusInfo != nullptr) {
178             queueOutputFrameConfig(privInfo->mHdr10PlusInfo);
179         }
180 
181         if (outputBufferSafe(outHeader)) {
182             uint8_t *dst = outHeader->pBuffer;
183             const uint8_t *srcY = (const uint8_t *)mImg->planes[VPX_PLANE_Y];
184             const uint8_t *srcU = (const uint8_t *)mImg->planes[VPX_PLANE_U];
185             const uint8_t *srcV = (const uint8_t *)mImg->planes[VPX_PLANE_V];
186             size_t srcYStride = mImg->stride[VPX_PLANE_Y];
187             size_t srcUStride = mImg->stride[VPX_PLANE_U];
188             size_t srcVStride = mImg->stride[VPX_PLANE_V];
189             copyYV12FrameToOutputBuffer(dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride);
190         } else {
191             outHeader->nFilledLen = 0;
192         }
193 
194         mImg = NULL;
195         outInfo->mOwnedByUs = false;
196         outQueue.erase(outQueue.begin());
197         outInfo = NULL;
198         notifyFillBufferDone(outHeader);
199         outHeader = NULL;
200     }
201 
202     if (!eos) {
203         return true;
204     }
205 
206     if (!outQueue.empty()) {
207         outInfo = *outQueue.begin();
208         outQueue.erase(outQueue.begin());
209         outHeader = outInfo->mHeader;
210         outHeader->nTimeStamp = 0;
211         outHeader->nFilledLen = 0;
212         outHeader->nFlags = OMX_BUFFERFLAG_EOS;
213         outInfo->mOwnedByUs = false;
214         notifyFillBufferDone(outHeader);
215         mEOSStatus = OUTPUT_FRAMES_FLUSHED;
216     }
217     return true;
218 }
219 
outputBufferSafe(OMX_BUFFERHEADERTYPE * outHeader)220 bool SoftVPX::outputBufferSafe(OMX_BUFFERHEADERTYPE *outHeader) {
221     uint32_t width = outputBufferWidth();
222     uint32_t height = outputBufferHeight();
223     uint64_t nFilledLen = width;
224     nFilledLen *= height;
225     if (nFilledLen > UINT32_MAX / 3) {
226         ALOGE("b/29421675, nFilledLen overflow %llu w %u h %u",
227                 (unsigned long long)nFilledLen, width, height);
228         android_errorWriteLog(0x534e4554, "29421675");
229         return false;
230     } else if (outHeader->nAllocLen < outHeader->nFilledLen) {
231         ALOGE("b/27597103, buffer too small");
232         android_errorWriteLog(0x534e4554, "27597103");
233         return false;
234     }
235 
236     return true;
237 }
238 
onQueueFilled(OMX_U32)239 void SoftVPX::onQueueFilled(OMX_U32 /* portIndex */) {
240     if (mOutputPortSettingsChange != NONE || mEOSStatus == OUTPUT_FRAMES_FLUSHED) {
241         return;
242     }
243 
244     List<BufferInfo *> &inQueue = getPortQueue(0);
245     List<BufferInfo *> &outQueue = getPortQueue(1);
246     bool EOSseen = false;
247     bool portWillReset = false;
248 
249     while ((mEOSStatus == INPUT_EOS_SEEN || !inQueue.empty())
250             && !outQueue.empty()) {
251         // Output the pending frames that left from last port reset or decoder flush.
252         if (mEOSStatus == INPUT_EOS_SEEN || mImg != NULL) {
253             if (!outputBuffers(
254                      mEOSStatus == INPUT_EOS_SEEN, true /* display */,
255                      mEOSStatus == INPUT_EOS_SEEN, &portWillReset)) {
256                 ALOGE("on2 decoder failed to output frame.");
257                 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
258                 return;
259             }
260             if (portWillReset || mEOSStatus == OUTPUT_FRAMES_FLUSHED ||
261                     mEOSStatus == INPUT_EOS_SEEN) {
262                 return;
263             }
264             // Continue as outQueue may be empty now.
265             continue;
266         }
267 
268         BufferInfo *inInfo = *inQueue.begin();
269         OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
270 
271         // Software VP9 Decoder does not need the Codec Specific Data (CSD)
272         // (specified in http://www.webmproject.org/vp9/profiles/). Ignore it if
273         // it was passed.
274         if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
275             // Only ignore CSD buffer for VP9.
276             if (mMode == MODE_VP9) {
277                 inQueue.erase(inQueue.begin());
278                 inInfo->mOwnedByUs = false;
279                 notifyEmptyBufferDone(inHeader);
280                 continue;
281             } else {
282                 // Tolerate the CSD buffer for VP8. This is a workaround
283                 // for b/28689536.
284                 ALOGW("WARNING: Got CSD buffer for VP8.");
285             }
286         }
287 
288         mPrivInfo[mTimeStampIdx].mTimeStamp = inHeader->nTimeStamp;
289 
290         if (inInfo->mFrameConfig) {
291             mPrivInfo[mTimeStampIdx].mHdr10PlusInfo = dequeueInputFrameConfig();
292         } else {
293             mPrivInfo[mTimeStampIdx].mHdr10PlusInfo.clear();
294         }
295 
296         if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
297             mEOSStatus = INPUT_EOS_SEEN;
298             EOSseen = true;
299         }
300 
301         if (inHeader->nFilledLen > 0) {
302             vpx_codec_err_t err = vpx_codec_decode(
303                     (vpx_codec_ctx_t *)mCtx, inHeader->pBuffer + inHeader->nOffset,
304                     inHeader->nFilledLen, &mPrivInfo[mTimeStampIdx], 0);
305             if (err == VPX_CODEC_OK) {
306                 inInfo->mOwnedByUs = false;
307                 inQueue.erase(inQueue.begin());
308                 inInfo = NULL;
309                 notifyEmptyBufferDone(inHeader);
310                 inHeader = NULL;
311             } else {
312                 ALOGE("on2 decoder failed to decode frame. err: %d", err);
313                 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
314                 return;
315             }
316         }
317 
318         mTimeStampIdx = (mTimeStampIdx + 1) % kNumBuffers;
319 
320         if (!outputBuffers(
321                  EOSseen /* flushDecoder */, true /* display */, EOSseen, &portWillReset)) {
322             ALOGE("on2 decoder failed to output frame.");
323             notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
324             return;
325         }
326         if (portWillReset) {
327             return;
328         }
329     }
330 }
331 
onPortFlushCompleted(OMX_U32 portIndex)332 void SoftVPX::onPortFlushCompleted(OMX_U32 portIndex) {
333     if (portIndex == kInputPortIndex) {
334         bool portWillReset = false;
335         if (!outputBuffers(
336                  true /* flushDecoder */, false /* display */, false /* eos */, &portWillReset)) {
337             ALOGE("Failed to flush decoder.");
338             notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
339             return;
340         }
341         mEOSStatus = INPUT_DATA_AVAILABLE;
342     }
343 }
344 
onReset()345 void SoftVPX::onReset() {
346     bool portWillReset = false;
347     if (!outputBuffers(
348              true /* flushDecoder */, false /* display */, false /* eos */, &portWillReset)) {
349         ALOGW("Failed to flush decoder. Try to hard reset decoder");
350         destroyDecoder();
351         initDecoder();
352     }
353     mEOSStatus = INPUT_DATA_AVAILABLE;
354 }
355 
356 }  // namespace android
357 
358 __attribute__((cfi_canonical_jump_table))
createSoftOMXComponent(const char * name,const OMX_CALLBACKTYPE * callbacks,OMX_PTR appData,OMX_COMPONENTTYPE ** component)359 android::SoftOMXComponent *createSoftOMXComponent(
360         const char *name, const OMX_CALLBACKTYPE *callbacks,
361         OMX_PTR appData, OMX_COMPONENTTYPE **component) {
362     if (!strcmp(name, "OMX.google.vp8.decoder")) {
363         return new android::SoftVPX(
364                 name, "video_decoder.vp8", OMX_VIDEO_CodingVP8,
365                 callbacks, appData, component);
366     } else if (!strcmp(name, "OMX.google.vp9.decoder")) {
367         return new android::SoftVPX(
368                 name, "video_decoder.vp9", OMX_VIDEO_CodingVP9,
369                 callbacks, appData, component);
370     } else {
371         CHECK(!"Unknown component");
372     }
373     return NULL;
374 }
375