1 /*
2 * Copyright (C) 2017 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 "SimpleC2Component"
19 #include <log/log.h>
20
21 #include <android/hardware_buffer.h>
22 #include <cutils/properties.h>
23 #include <media/stagefright/foundation/AMessage.h>
24
25 #include <inttypes.h>
26
27 #include <C2Config.h>
28 #include <C2Debug.h>
29 #include <C2PlatformSupport.h>
30 #include <Codec2BufferUtils.h>
31 #include <Codec2CommonUtils.h>
32 #include <SimpleC2Component.h>
33
34 namespace android {
35 constexpr uint8_t kNeutralUVBitDepth8 = 128;
36 constexpr uint16_t kNeutralUVBitDepth10 = 512;
37
convertYUV420Planar8ToYV12(uint8_t * dstY,uint8_t * dstU,uint8_t * dstV,const uint8_t * srcY,const uint8_t * srcU,const uint8_t * srcV,size_t srcYStride,size_t srcUStride,size_t srcVStride,size_t dstYStride,size_t dstUVStride,uint32_t width,uint32_t height,bool isMonochrome)38 void convertYUV420Planar8ToYV12(uint8_t *dstY, uint8_t *dstU, uint8_t *dstV, const uint8_t *srcY,
39 const uint8_t *srcU, const uint8_t *srcV, size_t srcYStride,
40 size_t srcUStride, size_t srcVStride, size_t dstYStride,
41 size_t dstUVStride, uint32_t width, uint32_t height,
42 bool isMonochrome) {
43 for (size_t i = 0; i < height; ++i) {
44 memcpy(dstY, srcY, width);
45 srcY += srcYStride;
46 dstY += dstYStride;
47 }
48
49 if (isMonochrome) {
50 // Fill with neutral U/V values.
51 for (size_t i = 0; i < (height + 1) / 2; ++i) {
52 memset(dstV, kNeutralUVBitDepth8, (width + 1) / 2);
53 memset(dstU, kNeutralUVBitDepth8, (width + 1) / 2);
54 dstV += dstUVStride;
55 dstU += dstUVStride;
56 }
57 return;
58 }
59
60 for (size_t i = 0; i < (height + 1) / 2; ++i) {
61 memcpy(dstV, srcV, (width + 1) / 2);
62 srcV += srcVStride;
63 dstV += dstUVStride;
64 }
65
66 for (size_t i = 0; i < (height + 1) / 2; ++i) {
67 memcpy(dstU, srcU, (width + 1) / 2);
68 srcU += srcUStride;
69 dstU += dstUVStride;
70 }
71 }
72
convertYUV420Planar16ToY410(uint32_t * dst,const uint16_t * srcY,const uint16_t * srcU,const uint16_t * srcV,size_t srcYStride,size_t srcUStride,size_t srcVStride,size_t dstStride,size_t width,size_t height)73 void convertYUV420Planar16ToY410(uint32_t *dst, const uint16_t *srcY, const uint16_t *srcU,
74 const uint16_t *srcV, size_t srcYStride, size_t srcUStride,
75 size_t srcVStride, size_t dstStride, size_t width, size_t height) {
76 // Converting two lines at a time, slightly faster
77 for (size_t y = 0; y < height; y += 2) {
78 uint32_t *dstTop = (uint32_t *)dst;
79 uint32_t *dstBot = (uint32_t *)(dst + dstStride);
80 uint16_t *ySrcTop = (uint16_t *)srcY;
81 uint16_t *ySrcBot = (uint16_t *)(srcY + srcYStride);
82 uint16_t *uSrc = (uint16_t *)srcU;
83 uint16_t *vSrc = (uint16_t *)srcV;
84
85 uint32_t u01, v01, y01, y23, y45, y67, uv0, uv1;
86 size_t x = 0;
87 for (; x < width - 3; x += 4) {
88 u01 = *((uint32_t *)uSrc);
89 uSrc += 2;
90 v01 = *((uint32_t *)vSrc);
91 vSrc += 2;
92
93 y01 = *((uint32_t *)ySrcTop);
94 ySrcTop += 2;
95 y23 = *((uint32_t *)ySrcTop);
96 ySrcTop += 2;
97 y45 = *((uint32_t *)ySrcBot);
98 ySrcBot += 2;
99 y67 = *((uint32_t *)ySrcBot);
100 ySrcBot += 2;
101
102 uv0 = (u01 & 0x3FF) | ((v01 & 0x3FF) << 20);
103 uv1 = (u01 >> 16) | ((v01 >> 16) << 20);
104
105 *dstTop++ = 3 << 30 | ((y01 & 0x3FF) << 10) | uv0;
106 *dstTop++ = 3 << 30 | ((y01 >> 16) << 10) | uv0;
107 *dstTop++ = 3 << 30 | ((y23 & 0x3FF) << 10) | uv1;
108 *dstTop++ = 3 << 30 | ((y23 >> 16) << 10) | uv1;
109
110 *dstBot++ = 3 << 30 | ((y45 & 0x3FF) << 10) | uv0;
111 *dstBot++ = 3 << 30 | ((y45 >> 16) << 10) | uv0;
112 *dstBot++ = 3 << 30 | ((y67 & 0x3FF) << 10) | uv1;
113 *dstBot++ = 3 << 30 | ((y67 >> 16) << 10) | uv1;
114 }
115
116 // There should be at most 2 more pixels to process. Note that we don't
117 // need to consider odd case as the buffer is always aligned to even.
118 if (x < width) {
119 u01 = *uSrc;
120 v01 = *vSrc;
121 y01 = *((uint32_t *)ySrcTop);
122 y45 = *((uint32_t *)ySrcBot);
123 uv0 = (u01 & 0x3FF) | ((v01 & 0x3FF) << 20);
124 *dstTop++ = ((y01 & 0x3FF) << 10) | uv0;
125 *dstTop++ = ((y01 >> 16) << 10) | uv0;
126 *dstBot++ = ((y45 & 0x3FF) << 10) | uv0;
127 *dstBot++ = ((y45 >> 16) << 10) | uv0;
128 }
129
130 srcY += srcYStride * 2;
131 srcU += srcUStride;
132 srcV += srcVStride;
133 dst += dstStride * 2;
134 }
135 }
136
137 namespace {
138
FillMissingColorAspects(std::shared_ptr<const C2ColorAspectsStruct> aspects,int32_t width,int32_t height)139 static C2ColorAspectsStruct FillMissingColorAspects(
140 std::shared_ptr<const C2ColorAspectsStruct> aspects,
141 int32_t width, int32_t height) {
142 C2ColorAspectsStruct _aspects;
143 if (aspects) {
144 _aspects = *aspects;
145 }
146
147 // use matrix for conversion
148 if (_aspects.matrix == C2Color::MATRIX_UNSPECIFIED) {
149 // if not specified, deduce matrix from primaries
150 if (_aspects.primaries == C2Color::PRIMARIES_UNSPECIFIED) {
151 // if those are also not specified, deduce primaries first from transfer, then from
152 // width and height
153 if (_aspects.transfer == C2Color::TRANSFER_ST2084
154 || _aspects.transfer == C2Color::TRANSFER_HLG) {
155 _aspects.primaries = C2Color::PRIMARIES_BT2020;
156 } else if (width >= 3840 || height >= 3840 || width * (int64_t)height >= 3840 * 1634) {
157 // TODO: stagefright defaults to BT.2020 for UHD, but perhaps we should default to
158 // BT.709 for non-HDR 10-bit UHD content
159 // (see media/libstagefright/foundation/ColorUtils.cpp)
160 _aspects.primaries = C2Color::PRIMARIES_BT2020;
161 } else if ((width <= 720 && height <= 576)
162 || (height <= 720 && width <= 576)) {
163 // note: it does not actually matter whether to use 525 or 625 here as the
164 // conversion is the same
165 _aspects.primaries = C2Color::PRIMARIES_BT601_625;
166 } else {
167 _aspects.primaries = C2Color::PRIMARIES_BT709;
168 }
169 }
170
171 switch (_aspects.primaries) {
172 case C2Color::PRIMARIES_BT601_525:
173 case C2Color::PRIMARIES_BT601_625:
174 _aspects.matrix = C2Color::MATRIX_BT601;
175 break;
176
177 case C2Color::PRIMARIES_BT709:
178 _aspects.matrix = C2Color::MATRIX_BT709;
179 break;
180
181 case C2Color::PRIMARIES_BT2020:
182 default:
183 _aspects.matrix = C2Color::MATRIX_BT2020;
184 }
185 }
186
187 return _aspects;
188 }
189
190 // matrix conversion coefficients
191 // (see media/libstagefright/colorconverter/ColorConverter.cpp for more details)
192 struct Coeffs {
193 int32_t _y, _b_u, _g_u, _g_v, _r_v, _c16;
194 };
195
GetCoeffsForAspects(const C2ColorAspectsStruct & aspects)196 static const struct Coeffs GetCoeffsForAspects(const C2ColorAspectsStruct &aspects) {
197 bool isFullRange = aspects.range == C2Color::RANGE_FULL;
198
199 switch (aspects.matrix) {
200 case C2Color::MATRIX_BT601:
201 /**
202 * BT.601: K_R = 0.299; K_B = 0.114
203 */
204 if (isFullRange) {
205 return Coeffs { 1024, 1436, 352, 731, 1815, 0 };
206 } else {
207 return Coeffs { 1196, 1639, 402, 835, 2072, 64 };
208 }
209 break;
210
211 case C2Color::MATRIX_BT709:
212 /**
213 * BT.709: K_R = 0.2126; K_B = 0.0722
214 */
215 if (isFullRange) {
216 return Coeffs { 1024, 1613, 192, 479, 1900, 0 };
217 } else {
218 return Coeffs { 1196, 1841, 219, 547, 2169, 64 };
219 }
220 break;
221
222 case C2Color::MATRIX_BT2020:
223 default:
224 /**
225 * BT.2020: K_R = 0.2627; K_B = 0.0593
226 */
227 if (isFullRange) {
228 return Coeffs { 1024, 1510, 169, 585, 1927, 0 };
229 } else {
230 return Coeffs { 1196, 1724, 192, 668, 2200, 64 };
231 }
232 }
233 }
234
235 }
236
237 #define CLIP3(min, v, max) (((v) < (min)) ? (min) : (((max) > (v)) ? (v) : (max)))
convertYUV420Planar16ToRGBA1010102(uint32_t * dst,const uint16_t * srcY,const uint16_t * srcU,const uint16_t * srcV,size_t srcYStride,size_t srcUStride,size_t srcVStride,size_t dstStride,size_t width,size_t height,std::shared_ptr<const C2ColorAspectsStruct> aspects)238 void convertYUV420Planar16ToRGBA1010102(
239 uint32_t *dst, const uint16_t *srcY, const uint16_t *srcU,
240 const uint16_t *srcV, size_t srcYStride, size_t srcUStride,
241 size_t srcVStride, size_t dstStride, size_t width,
242 size_t height,
243 std::shared_ptr<const C2ColorAspectsStruct> aspects) {
244
245 C2ColorAspectsStruct _aspects = FillMissingColorAspects(aspects, width, height);
246
247 struct Coeffs coeffs = GetCoeffsForAspects(_aspects);
248
249 int32_t _y = coeffs._y;
250 int32_t _b_u = coeffs._b_u;
251 int32_t _neg_g_u = -coeffs._g_u;
252 int32_t _neg_g_v = -coeffs._g_v;
253 int32_t _r_v = coeffs._r_v;
254 int32_t _c16 = coeffs._c16;
255
256 // Converting two lines at a time, slightly faster
257 for (size_t y = 0; y < height; y += 2) {
258 uint32_t *dstTop = (uint32_t *)dst;
259 uint32_t *dstBot = (uint32_t *)(dst + dstStride);
260 uint16_t *ySrcTop = (uint16_t *)srcY;
261 uint16_t *ySrcBot = (uint16_t *)(srcY + srcYStride);
262 uint16_t *uSrc = (uint16_t *)srcU;
263 uint16_t *vSrc = (uint16_t *)srcV;
264
265 for (size_t x = 0; x < width; x += 2) {
266 int32_t u, v, y00, y01, y10, y11;
267 u = *uSrc - 512;
268 uSrc += 1;
269 v = *vSrc - 512;
270 vSrc += 1;
271
272 y00 = *ySrcTop - _c16;
273 ySrcTop += 1;
274 y01 = *ySrcTop - _c16;
275 ySrcTop += 1;
276 y10 = *ySrcBot - _c16;
277 ySrcBot += 1;
278 y11 = *ySrcBot - _c16;
279 ySrcBot += 1;
280
281 int32_t u_b = u * _b_u;
282 int32_t u_g = u * _neg_g_u;
283 int32_t v_g = v * _neg_g_v;
284 int32_t v_r = v * _r_v;
285
286 int32_t yMult, b, g, r;
287 yMult = y00 * _y + 512;
288 b = (yMult + u_b) / 1024;
289 g = (yMult + v_g + u_g) / 1024;
290 r = (yMult + v_r) / 1024;
291 b = CLIP3(0, b, 1023);
292 g = CLIP3(0, g, 1023);
293 r = CLIP3(0, r, 1023);
294 *dstTop++ = 3 << 30 | (b << 20) | (g << 10) | r;
295
296 yMult = y01 * _y + 512;
297 b = (yMult + u_b) / 1024;
298 g = (yMult + v_g + u_g) / 1024;
299 r = (yMult + v_r) / 1024;
300 b = CLIP3(0, b, 1023);
301 g = CLIP3(0, g, 1023);
302 r = CLIP3(0, r, 1023);
303 *dstTop++ = 3 << 30 | (b << 20) | (g << 10) | r;
304
305 yMult = y10 * _y + 512;
306 b = (yMult + u_b) / 1024;
307 g = (yMult + v_g + u_g) / 1024;
308 r = (yMult + v_r) / 1024;
309 b = CLIP3(0, b, 1023);
310 g = CLIP3(0, g, 1023);
311 r = CLIP3(0, r, 1023);
312 *dstBot++ = 3 << 30 | (b << 20) | (g << 10) | r;
313
314 yMult = y11 * _y + 512;
315 b = (yMult + u_b) / 1024;
316 g = (yMult + v_g + u_g) / 1024;
317 r = (yMult + v_r) / 1024;
318 b = CLIP3(0, b, 1023);
319 g = CLIP3(0, g, 1023);
320 r = CLIP3(0, r, 1023);
321 *dstBot++ = 3 << 30 | (b << 20) | (g << 10) | r;
322 }
323
324 srcY += srcYStride * 2;
325 srcU += srcUStride;
326 srcV += srcVStride;
327 dst += dstStride * 2;
328 }
329 }
330
convertYUV420Planar16ToY410OrRGBA1010102(uint32_t * dst,const uint16_t * srcY,const uint16_t * srcU,const uint16_t * srcV,size_t srcYStride,size_t srcUStride,size_t srcVStride,size_t dstStride,size_t width,size_t height,std::shared_ptr<const C2ColorAspectsStruct> aspects)331 void convertYUV420Planar16ToY410OrRGBA1010102(
332 uint32_t *dst, const uint16_t *srcY,
333 const uint16_t *srcU, const uint16_t *srcV,
334 size_t srcYStride, size_t srcUStride,
335 size_t srcVStride, size_t dstStride, size_t width, size_t height,
336 std::shared_ptr<const C2ColorAspectsStruct> aspects) {
337 if (isAtLeastT()) {
338 convertYUV420Planar16ToRGBA1010102(dst, srcY, srcU, srcV, srcYStride, srcUStride,
339 srcVStride, dstStride, width, height, aspects);
340 } else {
341 convertYUV420Planar16ToY410(dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride,
342 dstStride, width, height);
343 }
344 }
345
convertYUV420Planar16ToYV12(uint8_t * dstY,uint8_t * dstU,uint8_t * dstV,const uint16_t * srcY,const uint16_t * srcU,const uint16_t * srcV,size_t srcYStride,size_t srcUStride,size_t srcVStride,size_t dstYStride,size_t dstUVStride,size_t width,size_t height,bool isMonochrome)346 void convertYUV420Planar16ToYV12(uint8_t *dstY, uint8_t *dstU, uint8_t *dstV, const uint16_t *srcY,
347 const uint16_t *srcU, const uint16_t *srcV, size_t srcYStride,
348 size_t srcUStride, size_t srcVStride, size_t dstYStride,
349 size_t dstUVStride, size_t width, size_t height,
350 bool isMonochrome) {
351 for (size_t y = 0; y < height; ++y) {
352 for (size_t x = 0; x < width; ++x) {
353 dstY[x] = (uint8_t)(srcY[x] >> 2);
354 }
355 srcY += srcYStride;
356 dstY += dstYStride;
357 }
358
359 if (isMonochrome) {
360 // Fill with neutral U/V values.
361 for (size_t y = 0; y < (height + 1) / 2; ++y) {
362 memset(dstV, kNeutralUVBitDepth8, (width + 1) / 2);
363 memset(dstU, kNeutralUVBitDepth8, (width + 1) / 2);
364 dstV += dstUVStride;
365 dstU += dstUVStride;
366 }
367 return;
368 }
369
370 for (size_t y = 0; y < (height + 1) / 2; ++y) {
371 for (size_t x = 0; x < (width + 1) / 2; ++x) {
372 dstU[x] = (uint8_t)(srcU[x] >> 2);
373 dstV[x] = (uint8_t)(srcV[x] >> 2);
374 }
375 srcU += srcUStride;
376 srcV += srcVStride;
377 dstU += dstUVStride;
378 dstV += dstUVStride;
379 }
380 }
381
convertYUV420Planar16ToP010(uint16_t * dstY,uint16_t * dstUV,const uint16_t * srcY,const uint16_t * srcU,const uint16_t * srcV,size_t srcYStride,size_t srcUStride,size_t srcVStride,size_t dstYStride,size_t dstUVStride,size_t width,size_t height,bool isMonochrome)382 void convertYUV420Planar16ToP010(uint16_t *dstY, uint16_t *dstUV, const uint16_t *srcY,
383 const uint16_t *srcU, const uint16_t *srcV, size_t srcYStride,
384 size_t srcUStride, size_t srcVStride, size_t dstYStride,
385 size_t dstUVStride, size_t width, size_t height,
386 bool isMonochrome) {
387 for (size_t y = 0; y < height; ++y) {
388 for (size_t x = 0; x < width; ++x) {
389 dstY[x] = srcY[x] << 6;
390 }
391 srcY += srcYStride;
392 dstY += dstYStride;
393 }
394
395 if (isMonochrome) {
396 // Fill with neutral U/V values.
397 for (size_t y = 0; y < (height + 1) / 2; ++y) {
398 for (size_t x = 0; x < (width + 1) / 2; ++x) {
399 dstUV[2 * x] = kNeutralUVBitDepth10 << 6;
400 dstUV[2 * x + 1] = kNeutralUVBitDepth10 << 6;
401 }
402 dstUV += dstUVStride;
403 }
404 return;
405 }
406
407 for (size_t y = 0; y < (height + 1) / 2; ++y) {
408 for (size_t x = 0; x < (width + 1) / 2; ++x) {
409 dstUV[2 * x] = srcU[x] << 6;
410 dstUV[2 * x + 1] = srcV[x] << 6;
411 }
412 srcU += srcUStride;
413 srcV += srcVStride;
414 dstUV += dstUVStride;
415 }
416 }
pop_front()417 std::unique_ptr<C2Work> SimpleC2Component::WorkQueue::pop_front() {
418 std::unique_ptr<C2Work> work = std::move(mQueue.front().work);
419 mQueue.pop_front();
420 return work;
421 }
422
push_back(std::unique_ptr<C2Work> work)423 void SimpleC2Component::WorkQueue::push_back(std::unique_ptr<C2Work> work) {
424 mQueue.push_back({ std::move(work), NO_DRAIN });
425 }
426
empty() const427 bool SimpleC2Component::WorkQueue::empty() const {
428 return mQueue.empty();
429 }
430
clear()431 void SimpleC2Component::WorkQueue::clear() {
432 mQueue.clear();
433 }
434
drainMode() const435 uint32_t SimpleC2Component::WorkQueue::drainMode() const {
436 return mQueue.front().drainMode;
437 }
438
markDrain(uint32_t drainMode)439 void SimpleC2Component::WorkQueue::markDrain(uint32_t drainMode) {
440 mQueue.push_back({ nullptr, drainMode });
441 }
442
443 ////////////////////////////////////////////////////////////////////////////////
444
WorkHandler()445 SimpleC2Component::WorkHandler::WorkHandler() : mRunning(false) {}
446
setComponent(const std::shared_ptr<SimpleC2Component> & thiz)447 void SimpleC2Component::WorkHandler::setComponent(
448 const std::shared_ptr<SimpleC2Component> &thiz) {
449 mThiz = thiz;
450 }
451
Reply(const sp<AMessage> & msg,int32_t * err=nullptr)452 static void Reply(const sp<AMessage> &msg, int32_t *err = nullptr) {
453 sp<AReplyToken> replyId;
454 CHECK(msg->senderAwaitsResponse(&replyId));
455 sp<AMessage> reply = new AMessage;
456 if (err) {
457 reply->setInt32("err", *err);
458 }
459 reply->postReply(replyId);
460 }
461
onMessageReceived(const sp<AMessage> & msg)462 void SimpleC2Component::WorkHandler::onMessageReceived(const sp<AMessage> &msg) {
463 std::shared_ptr<SimpleC2Component> thiz = mThiz.lock();
464 if (!thiz) {
465 ALOGD("component not yet set; msg = %s", msg->debugString().c_str());
466 sp<AReplyToken> replyId;
467 if (msg->senderAwaitsResponse(&replyId)) {
468 sp<AMessage> reply = new AMessage;
469 reply->setInt32("err", C2_CORRUPTED);
470 reply->postReply(replyId);
471 }
472 return;
473 }
474
475 switch (msg->what()) {
476 case kWhatProcess: {
477 if (mRunning) {
478 if (thiz->processQueue()) {
479 (new AMessage(kWhatProcess, this))->post();
480 }
481 } else {
482 ALOGV("Ignore process message as we're not running");
483 }
484 break;
485 }
486 case kWhatInit: {
487 int32_t err = thiz->onInit();
488 Reply(msg, &err);
489 [[fallthrough]];
490 }
491 case kWhatStart: {
492 mRunning = true;
493 break;
494 }
495 case kWhatStop: {
496 int32_t err = thiz->onStop();
497 thiz->mOutputBlockPool.reset();
498 Reply(msg, &err);
499 break;
500 }
501 case kWhatReset: {
502 thiz->onReset();
503 thiz->mOutputBlockPool.reset();
504 mRunning = false;
505 Reply(msg);
506 break;
507 }
508 case kWhatRelease: {
509 thiz->onRelease();
510 thiz->mOutputBlockPool.reset();
511 mRunning = false;
512 Reply(msg);
513 break;
514 }
515 default: {
516 ALOGD("Unrecognized msg: %d", msg->what());
517 break;
518 }
519 }
520 }
521
522 class SimpleC2Component::BlockingBlockPool : public C2BlockPool {
523 public:
BlockingBlockPool(const std::shared_ptr<C2BlockPool> & base)524 BlockingBlockPool(const std::shared_ptr<C2BlockPool>& base): mBase{base} {}
525
getLocalId() const526 virtual local_id_t getLocalId() const override {
527 return mBase->getLocalId();
528 }
529
getAllocatorId() const530 virtual C2Allocator::id_t getAllocatorId() const override {
531 return mBase->getAllocatorId();
532 }
533
fetchLinearBlock(uint32_t capacity,C2MemoryUsage usage,std::shared_ptr<C2LinearBlock> * block)534 virtual c2_status_t fetchLinearBlock(
535 uint32_t capacity,
536 C2MemoryUsage usage,
537 std::shared_ptr<C2LinearBlock>* block) {
538 c2_status_t status;
539 do {
540 status = mBase->fetchLinearBlock(capacity, usage, block);
541 } while (status == C2_BLOCKING);
542 return status;
543 }
544
fetchCircularBlock(uint32_t capacity,C2MemoryUsage usage,std::shared_ptr<C2CircularBlock> * block)545 virtual c2_status_t fetchCircularBlock(
546 uint32_t capacity,
547 C2MemoryUsage usage,
548 std::shared_ptr<C2CircularBlock>* block) {
549 c2_status_t status;
550 do {
551 status = mBase->fetchCircularBlock(capacity, usage, block);
552 } while (status == C2_BLOCKING);
553 return status;
554 }
555
fetchGraphicBlock(uint32_t width,uint32_t height,uint32_t format,C2MemoryUsage usage,std::shared_ptr<C2GraphicBlock> * block)556 virtual c2_status_t fetchGraphicBlock(
557 uint32_t width, uint32_t height, uint32_t format,
558 C2MemoryUsage usage,
559 std::shared_ptr<C2GraphicBlock>* block) {
560 c2_status_t status;
561 do {
562 status = mBase->fetchGraphicBlock(width, height, format, usage,
563 block);
564 } while (status == C2_BLOCKING);
565 return status;
566 }
567
568 private:
569 std::shared_ptr<C2BlockPool> mBase;
570 };
571
572 ////////////////////////////////////////////////////////////////////////////////
573
574 namespace {
575
576 struct DummyReadView : public C2ReadView {
DummyReadViewandroid::__anon1c7701d10211::DummyReadView577 DummyReadView() : C2ReadView(C2_NO_INIT) {}
578 };
579
580 } // namespace
581
SimpleC2Component(const std::shared_ptr<C2ComponentInterface> & intf)582 SimpleC2Component::SimpleC2Component(
583 const std::shared_ptr<C2ComponentInterface> &intf)
584 : mDummyReadView(DummyReadView()),
585 mIntf(intf),
586 mLooper(new ALooper),
587 mHandler(new WorkHandler) {
588 mLooper->setName(intf->getName().c_str());
589 (void)mLooper->registerHandler(mHandler);
590 mLooper->start(false, false, ANDROID_PRIORITY_VIDEO);
591 }
592
~SimpleC2Component()593 SimpleC2Component::~SimpleC2Component() {
594 mLooper->unregisterHandler(mHandler->id());
595 (void)mLooper->stop();
596 }
597
setListener_vb(const std::shared_ptr<C2Component::Listener> & listener,c2_blocking_t mayBlock)598 c2_status_t SimpleC2Component::setListener_vb(
599 const std::shared_ptr<C2Component::Listener> &listener, c2_blocking_t mayBlock) {
600 mHandler->setComponent(shared_from_this());
601
602 Mutexed<ExecState>::Locked state(mExecState);
603 if (state->mState == RUNNING) {
604 if (listener) {
605 return C2_BAD_STATE;
606 } else if (!mayBlock) {
607 return C2_BLOCKING;
608 }
609 }
610 state->mListener = listener;
611 // TODO: wait for listener change to have taken place before returning
612 // (e.g. if there is an ongoing listener callback)
613 return C2_OK;
614 }
615
queue_nb(std::list<std::unique_ptr<C2Work>> * const items)616 c2_status_t SimpleC2Component::queue_nb(std::list<std::unique_ptr<C2Work>> * const items) {
617 {
618 Mutexed<ExecState>::Locked state(mExecState);
619 if (state->mState != RUNNING) {
620 return C2_BAD_STATE;
621 }
622 }
623 bool queueWasEmpty = false;
624 {
625 Mutexed<WorkQueue>::Locked queue(mWorkQueue);
626 queueWasEmpty = queue->empty();
627 while (!items->empty()) {
628 queue->push_back(std::move(items->front()));
629 items->pop_front();
630 }
631 }
632 if (queueWasEmpty) {
633 (new AMessage(WorkHandler::kWhatProcess, mHandler))->post();
634 }
635 return C2_OK;
636 }
637
announce_nb(const std::vector<C2WorkOutline> & items)638 c2_status_t SimpleC2Component::announce_nb(const std::vector<C2WorkOutline> &items) {
639 (void)items;
640 return C2_OMITTED;
641 }
642
flush_sm(flush_mode_t flushMode,std::list<std::unique_ptr<C2Work>> * const flushedWork)643 c2_status_t SimpleC2Component::flush_sm(
644 flush_mode_t flushMode, std::list<std::unique_ptr<C2Work>>* const flushedWork) {
645 (void)flushMode;
646 {
647 Mutexed<ExecState>::Locked state(mExecState);
648 if (state->mState != RUNNING) {
649 return C2_BAD_STATE;
650 }
651 }
652 {
653 Mutexed<WorkQueue>::Locked queue(mWorkQueue);
654 queue->incGeneration();
655 // TODO: queue->splicedBy(flushedWork, flushedWork->end());
656 while (!queue->empty()) {
657 std::unique_ptr<C2Work> work = queue->pop_front();
658 if (work) {
659 flushedWork->push_back(std::move(work));
660 }
661 }
662 while (!queue->pending().empty()) {
663 flushedWork->push_back(std::move(queue->pending().begin()->second));
664 queue->pending().erase(queue->pending().begin());
665 }
666 }
667
668 return C2_OK;
669 }
670
drain_nb(drain_mode_t drainMode)671 c2_status_t SimpleC2Component::drain_nb(drain_mode_t drainMode) {
672 if (drainMode == DRAIN_CHAIN) {
673 return C2_OMITTED;
674 }
675 {
676 Mutexed<ExecState>::Locked state(mExecState);
677 if (state->mState != RUNNING) {
678 return C2_BAD_STATE;
679 }
680 }
681 bool queueWasEmpty = false;
682 {
683 Mutexed<WorkQueue>::Locked queue(mWorkQueue);
684 queueWasEmpty = queue->empty();
685 queue->markDrain(drainMode);
686 }
687 if (queueWasEmpty) {
688 (new AMessage(WorkHandler::kWhatProcess, mHandler))->post();
689 }
690
691 return C2_OK;
692 }
693
start()694 c2_status_t SimpleC2Component::start() {
695 Mutexed<ExecState>::Locked state(mExecState);
696 if (state->mState == RUNNING) {
697 return C2_BAD_STATE;
698 }
699 bool needsInit = (state->mState == UNINITIALIZED);
700 state.unlock();
701 if (needsInit) {
702 sp<AMessage> reply;
703 (new AMessage(WorkHandler::kWhatInit, mHandler))->postAndAwaitResponse(&reply);
704 int32_t err;
705 CHECK(reply->findInt32("err", &err));
706 if (err != C2_OK) {
707 return (c2_status_t)err;
708 }
709 } else {
710 (new AMessage(WorkHandler::kWhatStart, mHandler))->post();
711 }
712 state.lock();
713 state->mState = RUNNING;
714 return C2_OK;
715 }
716
stop()717 c2_status_t SimpleC2Component::stop() {
718 ALOGV("stop");
719 {
720 Mutexed<ExecState>::Locked state(mExecState);
721 if (state->mState != RUNNING) {
722 return C2_BAD_STATE;
723 }
724 state->mState = STOPPED;
725 }
726 {
727 Mutexed<WorkQueue>::Locked queue(mWorkQueue);
728 queue->clear();
729 queue->pending().clear();
730 }
731 sp<AMessage> reply;
732 (new AMessage(WorkHandler::kWhatStop, mHandler))->postAndAwaitResponse(&reply);
733 int32_t err;
734 CHECK(reply->findInt32("err", &err));
735 if (err != C2_OK) {
736 return (c2_status_t)err;
737 }
738 return C2_OK;
739 }
740
reset()741 c2_status_t SimpleC2Component::reset() {
742 ALOGV("reset");
743 {
744 Mutexed<ExecState>::Locked state(mExecState);
745 state->mState = UNINITIALIZED;
746 }
747 {
748 Mutexed<WorkQueue>::Locked queue(mWorkQueue);
749 queue->clear();
750 queue->pending().clear();
751 }
752 sp<AMessage> reply;
753 (new AMessage(WorkHandler::kWhatReset, mHandler))->postAndAwaitResponse(&reply);
754 return C2_OK;
755 }
756
release()757 c2_status_t SimpleC2Component::release() {
758 ALOGV("release");
759 sp<AMessage> reply;
760 (new AMessage(WorkHandler::kWhatRelease, mHandler))->postAndAwaitResponse(&reply);
761 return C2_OK;
762 }
763
intf()764 std::shared_ptr<C2ComponentInterface> SimpleC2Component::intf() {
765 return mIntf;
766 }
767
768 namespace {
769
vec(std::unique_ptr<C2Work> & work)770 std::list<std::unique_ptr<C2Work>> vec(std::unique_ptr<C2Work> &work) {
771 std::list<std::unique_ptr<C2Work>> ret;
772 ret.push_back(std::move(work));
773 return ret;
774 }
775
776 } // namespace
777
finish(uint64_t frameIndex,std::function<void (const std::unique_ptr<C2Work> &)> fillWork)778 void SimpleC2Component::finish(
779 uint64_t frameIndex, std::function<void(const std::unique_ptr<C2Work> &)> fillWork) {
780 std::unique_ptr<C2Work> work;
781 {
782 Mutexed<WorkQueue>::Locked queue(mWorkQueue);
783 if (queue->pending().count(frameIndex) == 0) {
784 ALOGW("unknown frame index: %" PRIu64, frameIndex);
785 return;
786 }
787 work = std::move(queue->pending().at(frameIndex));
788 queue->pending().erase(frameIndex);
789 }
790 if (work) {
791 fillWork(work);
792 std::shared_ptr<C2Component::Listener> listener = mExecState.lock()->mListener;
793 listener->onWorkDone_nb(shared_from_this(), vec(work));
794 ALOGV("returning pending work");
795 }
796 }
797
cloneAndSend(uint64_t frameIndex,const std::unique_ptr<C2Work> & currentWork,std::function<void (const std::unique_ptr<C2Work> &)> fillWork)798 void SimpleC2Component::cloneAndSend(
799 uint64_t frameIndex,
800 const std::unique_ptr<C2Work> ¤tWork,
801 std::function<void(const std::unique_ptr<C2Work> &)> fillWork) {
802 std::unique_ptr<C2Work> work(new C2Work);
803 if (currentWork->input.ordinal.frameIndex == frameIndex) {
804 work->input.flags = currentWork->input.flags;
805 work->input.ordinal = currentWork->input.ordinal;
806 } else {
807 Mutexed<WorkQueue>::Locked queue(mWorkQueue);
808 if (queue->pending().count(frameIndex) == 0) {
809 ALOGW("unknown frame index: %" PRIu64, frameIndex);
810 return;
811 }
812 work->input.flags = queue->pending().at(frameIndex)->input.flags;
813 work->input.ordinal = queue->pending().at(frameIndex)->input.ordinal;
814 }
815 work->worklets.emplace_back(new C2Worklet);
816 if (work) {
817 fillWork(work);
818 std::shared_ptr<C2Component::Listener> listener = mExecState.lock()->mListener;
819 listener->onWorkDone_nb(shared_from_this(), vec(work));
820 ALOGV("cloned and sending work");
821 }
822 }
823
processQueue()824 bool SimpleC2Component::processQueue() {
825 std::unique_ptr<C2Work> work;
826 uint64_t generation;
827 int32_t drainMode;
828 bool isFlushPending = false;
829 bool hasQueuedWork = false;
830 {
831 Mutexed<WorkQueue>::Locked queue(mWorkQueue);
832 if (queue->empty()) {
833 return false;
834 }
835
836 generation = queue->generation();
837 drainMode = queue->drainMode();
838 isFlushPending = queue->popPendingFlush();
839 work = queue->pop_front();
840 hasQueuedWork = !queue->empty();
841 }
842 if (isFlushPending) {
843 ALOGV("processing pending flush");
844 c2_status_t err = onFlush_sm();
845 if (err != C2_OK) {
846 ALOGD("flush err: %d", err);
847 // TODO: error
848 }
849 }
850
851 if (!mOutputBlockPool) {
852 c2_status_t err = [this] {
853 // TODO: don't use query_vb
854 C2StreamBufferTypeSetting::output outputFormat(0u);
855 std::vector<std::unique_ptr<C2Param>> params;
856 c2_status_t err = intf()->query_vb(
857 { &outputFormat },
858 { C2PortBlockPoolsTuning::output::PARAM_TYPE },
859 C2_DONT_BLOCK,
860 ¶ms);
861 if (err != C2_OK && err != C2_BAD_INDEX) {
862 ALOGD("query err = %d", err);
863 return err;
864 }
865 C2BlockPool::local_id_t poolId =
866 outputFormat.value == C2BufferData::GRAPHIC
867 ? C2BlockPool::BASIC_GRAPHIC
868 : C2BlockPool::BASIC_LINEAR;
869 if (params.size()) {
870 C2PortBlockPoolsTuning::output *outputPools =
871 C2PortBlockPoolsTuning::output::From(params[0].get());
872 if (outputPools && outputPools->flexCount() >= 1) {
873 poolId = outputPools->m.values[0];
874 }
875 }
876
877 std::shared_ptr<C2BlockPool> blockPool;
878 err = GetCodec2BlockPool(poolId, shared_from_this(), &blockPool);
879 ALOGD("Using output block pool with poolID %llu => got %llu - %d",
880 (unsigned long long)poolId,
881 (unsigned long long)(
882 blockPool ? blockPool->getLocalId() : 111000111),
883 err);
884 if (err == C2_OK) {
885 mOutputBlockPool = std::make_shared<BlockingBlockPool>(blockPool);
886 }
887 return err;
888 }();
889 if (err != C2_OK) {
890 Mutexed<ExecState>::Locked state(mExecState);
891 std::shared_ptr<C2Component::Listener> listener = state->mListener;
892 state.unlock();
893 listener->onError_nb(shared_from_this(), err);
894 return hasQueuedWork;
895 }
896 }
897
898 if (!work) {
899 c2_status_t err = drain(drainMode, mOutputBlockPool);
900 if (err != C2_OK) {
901 Mutexed<ExecState>::Locked state(mExecState);
902 std::shared_ptr<C2Component::Listener> listener = state->mListener;
903 state.unlock();
904 listener->onError_nb(shared_from_this(), err);
905 }
906 return hasQueuedWork;
907 }
908
909 {
910 std::vector<C2Param *> updates;
911 for (const std::unique_ptr<C2Param> ¶m: work->input.configUpdate) {
912 if (param) {
913 updates.emplace_back(param.get());
914 }
915 }
916 if (!updates.empty()) {
917 std::vector<std::unique_ptr<C2SettingResult>> failures;
918 c2_status_t err = intf()->config_vb(updates, C2_MAY_BLOCK, &failures);
919 ALOGD("applied %zu configUpdates => %s (%d)", updates.size(), asString(err), err);
920 }
921 }
922
923 ALOGV("start processing frame #%" PRIu64, work->input.ordinal.frameIndex.peeku());
924 // If input buffer list is not empty, it means we have some input to process on.
925 // However, input could be a null buffer. In such case, clear the buffer list
926 // before making call to process().
927 if (!work->input.buffers.empty() && !work->input.buffers[0]) {
928 ALOGD("Encountered null input buffer. Clearing the input buffer");
929 work->input.buffers.clear();
930 }
931 process(work, mOutputBlockPool);
932 ALOGV("processed frame #%" PRIu64, work->input.ordinal.frameIndex.peeku());
933 Mutexed<WorkQueue>::Locked queue(mWorkQueue);
934 if (queue->generation() != generation) {
935 ALOGD("work form old generation: was %" PRIu64 " now %" PRIu64,
936 queue->generation(), generation);
937 work->result = C2_NOT_FOUND;
938 queue.unlock();
939
940 Mutexed<ExecState>::Locked state(mExecState);
941 std::shared_ptr<C2Component::Listener> listener = state->mListener;
942 state.unlock();
943 listener->onWorkDone_nb(shared_from_this(), vec(work));
944 return hasQueuedWork;
945 }
946 if (work->workletsProcessed != 0u) {
947 queue.unlock();
948 Mutexed<ExecState>::Locked state(mExecState);
949 ALOGV("returning this work");
950 std::shared_ptr<C2Component::Listener> listener = state->mListener;
951 state.unlock();
952 listener->onWorkDone_nb(shared_from_this(), vec(work));
953 } else {
954 ALOGV("queue pending work");
955 work->input.buffers.clear();
956 std::unique_ptr<C2Work> unexpected;
957
958 uint64_t frameIndex = work->input.ordinal.frameIndex.peeku();
959 if (queue->pending().count(frameIndex) != 0) {
960 unexpected = std::move(queue->pending().at(frameIndex));
961 queue->pending().erase(frameIndex);
962 }
963 (void)queue->pending().insert({ frameIndex, std::move(work) });
964
965 queue.unlock();
966 if (unexpected) {
967 ALOGD("unexpected pending work");
968 unexpected->result = C2_CORRUPTED;
969 Mutexed<ExecState>::Locked state(mExecState);
970 std::shared_ptr<C2Component::Listener> listener = state->mListener;
971 state.unlock();
972 listener->onWorkDone_nb(shared_from_this(), vec(unexpected));
973 }
974 }
975 return hasQueuedWork;
976 }
977
getHalPixelFormatForBitDepth10(bool allowRGBA1010102)978 int SimpleC2Component::getHalPixelFormatForBitDepth10(bool allowRGBA1010102) {
979 // Save supported hal pixel formats for bit depth of 10, the first time this is called
980 if (!mBitDepth10HalPixelFormats.size()) {
981 std::vector<int> halPixelFormats;
982 halPixelFormats.push_back(HAL_PIXEL_FORMAT_YCBCR_P010);
983
984 // since allowRGBA1010102 can chance in each call, but mBitDepth10HalPixelFormats
985 // is populated only once, allowRGBA1010102 is not considered at this stage.
986 halPixelFormats.push_back(HAL_PIXEL_FORMAT_RGBA_1010102);
987
988 for (int halPixelFormat : halPixelFormats) {
989 if (isHalPixelFormatSupported((AHardwareBuffer_Format)halPixelFormat)) {
990 mBitDepth10HalPixelFormats.push_back(halPixelFormat);
991 }
992 }
993 // Add YV12 in the end as a fall-back option
994 mBitDepth10HalPixelFormats.push_back(HAL_PIXEL_FORMAT_YV12);
995 }
996 // From Android T onwards, HAL_PIXEL_FORMAT_RGBA_1010102 corresponds to true
997 // RGBA 1010102 format unlike earlier versions where it was used to represent
998 // YUVA 1010102 data
999 if (!isAtLeastT()) {
1000 // When RGBA1010102 is not allowed and if the first supported hal pixel is format is
1001 // HAL_PIXEL_FORMAT_RGBA_1010102, then return HAL_PIXEL_FORMAT_YV12
1002 if (!allowRGBA1010102 && mBitDepth10HalPixelFormats[0] == HAL_PIXEL_FORMAT_RGBA_1010102) {
1003 return HAL_PIXEL_FORMAT_YV12;
1004 }
1005 }
1006 // Return the first entry from supported formats
1007 return mBitDepth10HalPixelFormats[0];
1008 }
createLinearBuffer(const std::shared_ptr<C2LinearBlock> & block,size_t offset,size_t size)1009 std::shared_ptr<C2Buffer> SimpleC2Component::createLinearBuffer(
1010 const std::shared_ptr<C2LinearBlock> &block, size_t offset, size_t size) {
1011 return C2Buffer::CreateLinearBuffer(block->share(offset, size, ::C2Fence()));
1012 }
1013
createGraphicBuffer(const std::shared_ptr<C2GraphicBlock> & block,const C2Rect & crop)1014 std::shared_ptr<C2Buffer> SimpleC2Component::createGraphicBuffer(
1015 const std::shared_ptr<C2GraphicBlock> &block, const C2Rect &crop) {
1016 return C2Buffer::CreateGraphicBuffer(block->share(crop, ::C2Fence()));
1017 }
1018
1019 } // namespace android
1020