blob: 8685fcea1e9308f30b7f5db526e2249445dbf8c9 [file] [log] [blame]
Johny Lin7c4cb522017-06-26 16:10:24 +08001// Copyright 2017 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 "C2VDAComponent"
7
Hirokazu Honda4b97b372018-01-21 23:20:59 +00008#ifdef V4L2_CODEC2_ARC
9#include <C2VDAAdaptorProxy.h>
10#else
Johny Linc6d0b6f2017-12-19 15:47:06 +080011#include <C2VDAAdaptor.h>
Hirokazu Honda4b97b372018-01-21 23:20:59 +000012#endif
13
Lajos Molnard18cc342017-10-17 07:58:02 -070014#define __C2_GENERATE_GLOBAL_VARS__
Pin-chih Lin906956a2018-05-22 14:12:59 +080015#include <C2VDAAllocatorStore.h>
Hirokazu Hondaca17a062018-07-31 10:48:56 +090016#include <C2VDAComponent.h>
17#include <C2VDAPixelFormat.h>
18#include <C2VDASupport.h> // to getParamReflector from vda store
Pin-chih Lin906956a2018-05-22 14:12:59 +080019#include <C2VdaBqBlockPool.h>
Pin-chih Lin585324e2018-07-25 16:24:54 +080020#include <C2VdaPooledBlockPool.h>
Johny Lin8a2118e2018-02-08 16:29:25 +080021
Pin-chih Lin3aff30b2018-10-02 21:11:01 +080022#include <h264_parser.h>
23
Pin-chih Lin906956a2018-05-22 14:12:59 +080024#include <C2AllocatorGralloc.h>
Johny Lin8a2118e2018-02-08 16:29:25 +080025#include <C2ComponentFactory.h>
Pin-chih Lin85e1ec62018-05-16 16:17:16 +080026#include <C2PlatformSupport.h>
Pin-chih Lin3aff30b2018-10-02 21:11:01 +080027#include <Codec2Mapper.h>
Hirokazu Hondadecfca12018-01-23 01:59:09 +090028
Johny Linc6d0b6f2017-12-19 15:47:06 +080029#include <base/bind.h>
30#include <base/bind_helpers.h>
Johny Lin7c4cb522017-06-26 16:10:24 +080031
32#include <media/stagefright/MediaDefs.h>
Pin-chih Lin3aff30b2018-10-02 21:11:01 +080033#include <media/stagefright/foundation/ColorUtils.h>
Johny Lin7c4cb522017-06-26 16:10:24 +080034#include <utils/Log.h>
35#include <utils/misc.h>
36
Johny Linc6d0b6f2017-12-19 15:47:06 +080037#include <inttypes.h>
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +080038#include <string.h>
Johny Linc6d0b6f2017-12-19 15:47:06 +080039#include <algorithm>
Hirokazu Honda3f3c8a42018-05-09 22:25:19 +080040#include <string>
Johny Linc6d0b6f2017-12-19 15:47:06 +080041
Johny Lina891f0e2017-12-19 10:25:27 +080042#define UNUSED(expr) \
43 do { \
44 (void)(expr); \
45 } while (0)
Johny Lin7c4cb522017-06-26 16:10:24 +080046
47namespace android {
48
49namespace {
50
Johny Lin52bffe12017-09-19 14:26:06 +080051// Mask against 30 bits to avoid (undefined) wraparound on signed integer.
Lajos Molnar2a2b9012018-01-23 17:24:57 -080052int32_t frameIndexToBitstreamId(c2_cntr64_t frameIndex) {
53 return static_cast<int32_t>(frameIndex.peeku() & 0x3FFFFFFF);
Johny Lin7c4cb522017-06-26 16:10:24 +080054}
55
Pin-chih Lin2fea0712018-05-14 19:57:14 +080056// Use basic graphic block pool/allocator as default.
57const C2BlockPool::local_id_t kDefaultOutputBlockPool = C2BlockPool::BASIC_GRAPHIC;
58
Pin-chih Lin226ec462018-03-28 20:15:26 +080059const C2String kH264DecoderName = "c2.vda.avc.decoder";
60const C2String kVP8DecoderName = "c2.vda.vp8.decoder";
61const C2String kVP9DecoderName = "c2.vda.vp9.decoder";
Hirokazu Honda3f3c8a42018-05-09 22:25:19 +080062const C2String kH264SecureDecoderName = "c2.vda.avc.decoder.secure";
63const C2String kVP8SecureDecoderName = "c2.vda.vp8.decoder.secure";
64const C2String kVP9SecureDecoderName = "c2.vda.vp9.decoder.secure";
Johny Lin7c4cb522017-06-26 16:10:24 +080065
Pin-chih Lin906956a2018-05-22 14:12:59 +080066const uint32_t kDpbOutputBufferExtraCount = 3; // Use the same number as ACodec.
67const int kDequeueRetryDelayUs = 10000; // Wait time of dequeue buffer retry in microseconds.
Pin-chih Lin81a44bb2018-08-17 17:54:00 +080068const int32_t kAllocateBufferMaxRetries = 10; // Max retry time for fetchGraphicBlock timeout.
Johny Lin9be52702017-11-15 20:52:31 +080069} // namespace
Johny Lin7c4cb522017-06-26 16:10:24 +080070
Pin-chih Lindb84a6c2018-09-13 21:38:50 +080071static c2_status_t adaptorResultToC2Status(VideoDecodeAcceleratorAdaptor::Result result) {
72 switch (result) {
73 case VideoDecodeAcceleratorAdaptor::Result::SUCCESS:
74 return C2_OK;
75 case VideoDecodeAcceleratorAdaptor::Result::ILLEGAL_STATE:
76 ALOGE("Got error: ILLEGAL_STATE");
77 return C2_BAD_STATE;
78 case VideoDecodeAcceleratorAdaptor::Result::INVALID_ARGUMENT:
79 ALOGE("Got error: INVALID_ARGUMENT");
80 return C2_BAD_VALUE;
81 case VideoDecodeAcceleratorAdaptor::Result::UNREADABLE_INPUT:
82 ALOGE("Got error: UNREADABLE_INPUT");
83 return C2_BAD_VALUE;
84 case VideoDecodeAcceleratorAdaptor::Result::PLATFORM_FAILURE:
85 ALOGE("Got error: PLATFORM_FAILURE");
86 return C2_CORRUPTED;
87 case VideoDecodeAcceleratorAdaptor::Result::INSUFFICIENT_RESOURCES:
88 ALOGE("Got error: INSUFFICIENT_RESOURCES");
89 return C2_NO_MEMORY;
90 default:
91 ALOGE("Unrecognizable adaptor result (value = %d)...", result);
92 return C2_CORRUPTED;
93 }
94}
95
Pin-chih Lin3aff30b2018-10-02 21:11:01 +080096// static
97C2R C2VDAComponent::IntfImpl::ProfileLevelSetter(bool mayBlock,
98 C2P<C2StreamProfileLevelInfo::input>& info) {
99 (void)mayBlock;
100 return info.F(info.v.profile)
101 .validatePossible(info.v.profile)
102 .plus(info.F(info.v.level).validatePossible(info.v.level));
103}
104
105// static
106C2R C2VDAComponent::IntfImpl::SizeSetter(bool mayBlock,
107 C2P<C2StreamPictureSizeInfo::output>& videoSize) {
108 (void)mayBlock;
109 // TODO: maybe apply block limit?
110 return videoSize.F(videoSize.v.width)
111 .validatePossible(videoSize.v.width)
112 .plus(videoSize.F(videoSize.v.height).validatePossible(videoSize.v.height));
113}
114
115// static
116template <typename T>
117C2R C2VDAComponent::IntfImpl::DefaultColorAspectsSetter(bool mayBlock, C2P<T>& def) {
118 (void)mayBlock;
119 if (def.v.range > C2Color::RANGE_OTHER) {
120 def.set().range = C2Color::RANGE_OTHER;
121 }
122 if (def.v.primaries > C2Color::PRIMARIES_OTHER) {
123 def.set().primaries = C2Color::PRIMARIES_OTHER;
124 }
125 if (def.v.transfer > C2Color::TRANSFER_OTHER) {
126 def.set().transfer = C2Color::TRANSFER_OTHER;
127 }
128 if (def.v.matrix > C2Color::MATRIX_OTHER) {
129 def.set().matrix = C2Color::MATRIX_OTHER;
130 }
131 return C2R::Ok();
132}
133
134// static
135C2R C2VDAComponent::IntfImpl::MergedColorAspectsSetter(
136 bool mayBlock, C2P<C2StreamColorAspectsInfo::output>& merged,
137 const C2P<C2StreamColorAspectsTuning::output>& def,
138 const C2P<C2StreamColorAspectsInfo::input>& coded) {
139 (void)mayBlock;
140 // Take coded values for all specified fields, and default values for unspecified ones.
141 merged.set().range = coded.v.range == RANGE_UNSPECIFIED ? def.v.range : coded.v.range;
142 merged.set().primaries =
143 coded.v.primaries == PRIMARIES_UNSPECIFIED ? def.v.primaries : coded.v.primaries;
144 merged.set().transfer =
145 coded.v.transfer == TRANSFER_UNSPECIFIED ? def.v.transfer : coded.v.transfer;
146 merged.set().matrix = coded.v.matrix == MATRIX_UNSPECIFIED ? def.v.matrix : coded.v.matrix;
147 return C2R::Ok();
148}
149
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +0800150C2VDAComponent::IntfImpl::IntfImpl(C2String name, const std::shared_ptr<C2ReflectorHelper>& helper)
151 : C2InterfaceHelper(helper), mInitStatus(C2_OK) {
152 setDerivedInstance(this);
153
Johny Lin9be52702017-11-15 20:52:31 +0800154 // TODO(johnylin): use factory function to determine whether V4L2 stream or slice API is.
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +0800155 char inputMime[128];
Hirokazu Honda3f3c8a42018-05-09 22:25:19 +0800156 if (name == kH264DecoderName || name == kH264SecureDecoderName) {
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +0800157 strcpy(inputMime, MEDIA_MIMETYPE_VIDEO_AVC);
Pin-chih Lin3aff30b2018-10-02 21:11:01 +0800158 mInputCodec = InputCodec::H264;
Pin-chih Line50c6ef2018-10-02 09:35:15 +0800159 addParameter(
160 DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
161 .withDefault(new C2StreamProfileLevelInfo::input(
162 0u, C2Config::PROFILE_AVC_MAIN, C2Config::LEVEL_AVC_4))
163 .withFields(
164 {C2F(mProfileLevel, profile)
165 .oneOf({C2Config::PROFILE_AVC_BASELINE,
166 C2Config::PROFILE_AVC_CONSTRAINED_BASELINE,
167 C2Config::PROFILE_AVC_MAIN,
168 C2Config::PROFILE_AVC_HIGH,
169 C2Config::PROFILE_AVC_CONSTRAINED_HIGH}),
170 C2F(mProfileLevel, level)
171 .oneOf({C2Config::LEVEL_AVC_1, C2Config::LEVEL_AVC_1B,
172 C2Config::LEVEL_AVC_1_1, C2Config::LEVEL_AVC_1_2,
173 C2Config::LEVEL_AVC_1_3, C2Config::LEVEL_AVC_2,
174 C2Config::LEVEL_AVC_2_1, C2Config::LEVEL_AVC_2_2,
175 C2Config::LEVEL_AVC_3, C2Config::LEVEL_AVC_3_1,
176 C2Config::LEVEL_AVC_3_2, C2Config::LEVEL_AVC_4,
177 C2Config::LEVEL_AVC_4_1, C2Config::LEVEL_AVC_4_2,
178 C2Config::LEVEL_AVC_5, C2Config::LEVEL_AVC_5_1,
179 C2Config::LEVEL_AVC_5_2})})
Pin-chih Lin3aff30b2018-10-02 21:11:01 +0800180 .withSetter(ProfileLevelSetter)
Pin-chih Line50c6ef2018-10-02 09:35:15 +0800181 .build());
Hirokazu Honda3f3c8a42018-05-09 22:25:19 +0800182 } else if (name == kVP8DecoderName || name == kVP8SecureDecoderName) {
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +0800183 strcpy(inputMime, MEDIA_MIMETYPE_VIDEO_VP8);
Pin-chih Lin3aff30b2018-10-02 21:11:01 +0800184 mInputCodec = InputCodec::VP8;
Pin-chih Line50c6ef2018-10-02 09:35:15 +0800185 addParameter(DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
186 .withConstValue(new C2StreamProfileLevelInfo::input(
187 0u, C2Config::PROFILE_UNUSED, C2Config::LEVEL_UNUSED))
188 .build());
Hirokazu Honda3f3c8a42018-05-09 22:25:19 +0800189 } else if (name == kVP9DecoderName || name == kVP9SecureDecoderName) {
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +0800190 strcpy(inputMime, MEDIA_MIMETYPE_VIDEO_VP9);
Pin-chih Lin3aff30b2018-10-02 21:11:01 +0800191 mInputCodec = InputCodec::VP9;
Pin-chih Line50c6ef2018-10-02 09:35:15 +0800192 addParameter(
193 DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
194 .withDefault(new C2StreamProfileLevelInfo::input(
195 0u, C2Config::PROFILE_VP9_0, C2Config::LEVEL_VP9_5))
196 .withFields({C2F(mProfileLevel, profile).oneOf({C2Config::PROFILE_VP9_0}),
197 C2F(mProfileLevel, level)
198 .oneOf({C2Config::LEVEL_VP9_1, C2Config::LEVEL_VP9_1_1,
199 C2Config::LEVEL_VP9_2, C2Config::LEVEL_VP9_2_1,
200 C2Config::LEVEL_VP9_3, C2Config::LEVEL_VP9_3_1,
201 C2Config::LEVEL_VP9_4, C2Config::LEVEL_VP9_4_1,
202 C2Config::LEVEL_VP9_5})})
Pin-chih Lin3aff30b2018-10-02 21:11:01 +0800203 .withSetter(ProfileLevelSetter)
Pin-chih Line50c6ef2018-10-02 09:35:15 +0800204 .build());
Johny Lin9be52702017-11-15 20:52:31 +0800205 } else {
206 ALOGE("Invalid component name: %s", name.c_str());
207 mInitStatus = C2_BAD_VALUE;
208 return;
209 }
Johny Lin7c4cb522017-06-26 16:10:24 +0800210 // Get supported profiles from VDA.
Johny Lin9be52702017-11-15 20:52:31 +0800211 // TODO: re-think the suitable method of getting supported profiles for both pure Android and
212 // ARC++.
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +0800213 media::VideoDecodeAccelerator::SupportedProfiles supportedProfiles;
Hirokazu Honda4b97b372018-01-21 23:20:59 +0000214#ifdef V4L2_CODEC2_ARC
Pin-chih Lin3aff30b2018-10-02 21:11:01 +0800215 supportedProfiles = arc::C2VDAAdaptorProxy::GetSupportedProfiles(mInputCodec);
Hirokazu Honda4b97b372018-01-21 23:20:59 +0000216#else
Pin-chih Lin3aff30b2018-10-02 21:11:01 +0800217 supportedProfiles = C2VDAAdaptor::GetSupportedProfiles(mInputCodec);
Hirokazu Honda4b97b372018-01-21 23:20:59 +0000218#endif
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +0800219 if (supportedProfiles.empty()) {
Pin-chih Lin3aff30b2018-10-02 21:11:01 +0800220 ALOGE("No supported profile from input codec: %d", mInputCodec);
Johny Lin9be52702017-11-15 20:52:31 +0800221 mInitStatus = C2_BAD_VALUE;
222 return;
223 }
Johny Lin7c4cb522017-06-26 16:10:24 +0800224
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +0800225 mCodecProfile = supportedProfiles[0].profile;
Johny Lin7c4cb522017-06-26 16:10:24 +0800226
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +0800227 auto minSize = supportedProfiles[0].min_resolution;
228 auto maxSize = supportedProfiles[0].max_resolution;
Johny Lin7c4cb522017-06-26 16:10:24 +0800229
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +0800230 addParameter(
231 DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
232 .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2FormatCompressed))
233 .build());
Johny Lin7c4cb522017-06-26 16:10:24 +0800234
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +0800235 addParameter(DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
236 .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2FormatVideo))
237 .build());
Johny Lin7c4cb522017-06-26 16:10:24 +0800238
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +0800239 addParameter(
240 DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
241 .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(inputMime))
242 .build());
Johny Lin7c4cb522017-06-26 16:10:24 +0800243
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +0800244 addParameter(DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
245 .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
246 MEDIA_MIMETYPE_VIDEO_RAW))
247 .build());
Johny Lin7c4cb522017-06-26 16:10:24 +0800248
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +0800249 addParameter(DefineParam(mSize, C2_PARAMKEY_STREAM_PICTURE_SIZE)
250 .withDefault(new C2StreamPictureSizeInfo::output(0u, 176, 144))
251 .withFields({
252 C2F(mSize, width).inRange(minSize.width(), maxSize.width(), 16),
253 C2F(mSize, height).inRange(minSize.height(), maxSize.height(), 16),
254 })
Pin-chih Lin3aff30b2018-10-02 21:11:01 +0800255 .withSetter(SizeSetter)
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +0800256 .build());
Pin-chih Lin2fea0712018-05-14 19:57:14 +0800257
Hirokazu Hondad41df2d2018-09-26 12:39:21 +0900258 // App may set a smaller value for maximum of input buffer size than actually required
259 // by mistake. C2VDAComponent overrides it if the value specified by app is smaller than
260 // the calculated value in MaxSizeCalculator().
261 // This value is the default maximum of linear buffer size (kLinearBufferSize) in
262 // CCodecBufferChannel.cpp.
263 constexpr static size_t kLinearBufferSize = 1048576;
264 struct LocalCalculator {
265 static C2R MaxSizeCalculator(bool mayBlock, C2P<C2StreamMaxBufferSizeInfo::input>& me,
266 const C2P<C2StreamPictureSizeInfo::output>& size) {
267 (void)mayBlock;
268 // TODO: Need larger size?
269 me.set().value = kLinearBufferSize;
270 const uint32_t width = size.v.width;
271 const uint32_t height = size.v.height;
272 // Enlarge the input buffer for 4k video
273 if ((width > 1920 && height > 1080)) {
274 me.set().value = 4 * kLinearBufferSize;
275 }
276 return C2R::Ok();
277 }
278 };
279 addParameter(DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
280 .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, kLinearBufferSize))
281 .withFields({
282 C2F(mMaxInputSize, value).any(),
283 })
284 .calculatedAs(LocalCalculator::MaxSizeCalculator, mSize)
285 .build());
286
Hirokazu Honda3f3c8a42018-05-09 22:25:19 +0800287 bool secureMode = name.find(".secure") != std::string::npos;
288 C2Allocator::id_t inputAllocators[] = {secureMode ? C2VDAAllocatorStore::SECURE_LINEAR
289 : C2PlatformAllocatorStore::ION};
Pin-chih Linb4ef9392018-07-31 16:41:15 +0800290
291 C2Allocator::id_t outputAllocators[] = {C2VDAAllocatorStore::V4L2_BUFFERPOOL};
292
293 C2Allocator::id_t surfaceAllocator = secureMode ? C2VDAAllocatorStore::SECURE_GRAPHIC
294 : C2VDAAllocatorStore::V4L2_BUFFERQUEUE;
Pin-chih Lin2fea0712018-05-14 19:57:14 +0800295
296 addParameter(
297 DefineParam(mInputAllocatorIds, C2_PARAMKEY_INPUT_ALLOCATORS)
298 .withConstValue(C2PortAllocatorsTuning::input::AllocShared(inputAllocators))
299 .build());
300
301 addParameter(
302 DefineParam(mOutputAllocatorIds, C2_PARAMKEY_OUTPUT_ALLOCATORS)
303 .withConstValue(C2PortAllocatorsTuning::output::AllocShared(outputAllocators))
304 .build());
305
Pin-chih Linb4ef9392018-07-31 16:41:15 +0800306 addParameter(DefineParam(mOutputSurfaceAllocatorId, C2_PARAMKEY_OUTPUT_SURFACE_ALLOCATOR)
307 .withConstValue(new C2PortSurfaceAllocatorTuning::output(surfaceAllocator))
308 .build());
309
Pin-chih Lin2fea0712018-05-14 19:57:14 +0800310 C2BlockPool::local_id_t outputBlockPools[] = {kDefaultOutputBlockPool};
311
312 addParameter(
313 DefineParam(mOutputBlockPoolIds, C2_PARAMKEY_OUTPUT_BLOCK_POOLS)
314 .withDefault(C2PortBlockPoolsTuning::output::AllocShared(outputBlockPools))
315 .withFields({C2F(mOutputBlockPoolIds, m.values[0]).any(),
316 C2F(mOutputBlockPoolIds, m.values).inRange(0, 1)})
317 .withSetter(Setter<C2PortBlockPoolsTuning::output>::NonStrictValuesWithNoDeps)
318 .build());
Pin-chih Lin3aff30b2018-10-02 21:11:01 +0800319
320 addParameter(
321 DefineParam(mDefaultColorAspects, C2_PARAMKEY_DEFAULT_COLOR_ASPECTS)
322 .withDefault(new C2StreamColorAspectsTuning::output(
323 0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
324 C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
325 .withFields(
326 {C2F(mDefaultColorAspects, range)
327 .inRange(C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
328 C2F(mDefaultColorAspects, primaries)
329 .inRange(C2Color::PRIMARIES_UNSPECIFIED,
330 C2Color::PRIMARIES_OTHER),
331 C2F(mDefaultColorAspects, transfer)
332 .inRange(C2Color::TRANSFER_UNSPECIFIED,
333 C2Color::TRANSFER_OTHER),
334 C2F(mDefaultColorAspects, matrix)
335 .inRange(C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)})
336 .withSetter(DefaultColorAspectsSetter)
337 .build());
338
339 addParameter(
340 DefineParam(mCodedColorAspects, C2_PARAMKEY_VUI_COLOR_ASPECTS)
341 .withDefault(new C2StreamColorAspectsInfo::input(
342 0u, C2Color::RANGE_LIMITED, C2Color::PRIMARIES_UNSPECIFIED,
343 C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
344 .withFields(
345 {C2F(mCodedColorAspects, range)
346 .inRange(C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
347 C2F(mCodedColorAspects, primaries)
348 .inRange(C2Color::PRIMARIES_UNSPECIFIED,
349 C2Color::PRIMARIES_OTHER),
350 C2F(mCodedColorAspects, transfer)
351 .inRange(C2Color::TRANSFER_UNSPECIFIED,
352 C2Color::TRANSFER_OTHER),
353 C2F(mCodedColorAspects, matrix)
354 .inRange(C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)})
355 .withSetter(DefaultColorAspectsSetter)
356 .build());
357
358 addParameter(
359 DefineParam(mColorAspects, C2_PARAMKEY_COLOR_ASPECTS)
360 .withDefault(new C2StreamColorAspectsInfo::output(
361 0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
362 C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
363 .withFields(
364 {C2F(mColorAspects, range)
365 .inRange(C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
366 C2F(mColorAspects, primaries)
367 .inRange(C2Color::PRIMARIES_UNSPECIFIED,
368 C2Color::PRIMARIES_OTHER),
369 C2F(mColorAspects, transfer)
370 .inRange(C2Color::TRANSFER_UNSPECIFIED,
371 C2Color::TRANSFER_OTHER),
372 C2F(mColorAspects, matrix)
373 .inRange(C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)})
374 .withSetter(MergedColorAspectsSetter, mDefaultColorAspects, mCodedColorAspects)
375 .build());
Johny Lin7c4cb522017-06-26 16:10:24 +0800376}
377
378////////////////////////////////////////////////////////////////////////////////
Johny Lina891f0e2017-12-19 10:25:27 +0800379#define EXPECT_STATE_OR_RETURN_ON_ERROR(x) \
380 do { \
Johny Lin52bffe12017-09-19 14:26:06 +0800381 if (mComponentState == ComponentState::ERROR) return; \
Johny Lina891f0e2017-12-19 10:25:27 +0800382 CHECK_EQ(mComponentState, ComponentState::x); \
Johny Lin52bffe12017-09-19 14:26:06 +0800383 } while (0)
384
Johny Lina891f0e2017-12-19 10:25:27 +0800385#define EXPECT_RUNNING_OR_RETURN_ON_ERROR() \
386 do { \
387 if (mComponentState == ComponentState::ERROR) return; \
Johny Lin52bffe12017-09-19 14:26:06 +0800388 CHECK_NE(mComponentState, ComponentState::UNINITIALIZED); \
389 } while (0)
390
Johny Linac6e2702018-01-17 12:11:52 +0800391C2VDAComponent::VideoFormat::VideoFormat(HalPixelFormat pixelFormat, uint32_t minNumBuffers,
Johny Lin52bffe12017-09-19 14:26:06 +0800392 media::Size codedSize, media::Rect visibleRect)
Johny Lina891f0e2017-12-19 10:25:27 +0800393 : mPixelFormat(pixelFormat),
394 mMinNumBuffers(minNumBuffers),
395 mCodedSize(codedSize),
396 mVisibleRect(visibleRect) {}
Johny Lin7c4cb522017-06-26 16:10:24 +0800397
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +0800398C2VDAComponent::C2VDAComponent(C2String name, c2_node_id_t id,
399 const std::shared_ptr<C2ReflectorHelper>& helper)
400 : mIntfImpl(std::make_shared<IntfImpl>(name, helper)),
401 mIntf(std::make_shared<SimpleInterface<IntfImpl>>(name.c_str(), id, mIntfImpl)),
Johny Lina891f0e2017-12-19 10:25:27 +0800402 mThread("C2VDAComponentThread"),
Pin-chih Lin906956a2018-05-22 14:12:59 +0800403 mDequeueThread("C2VDAComponentDequeueThread"),
Johny Lina891f0e2017-12-19 10:25:27 +0800404 mVDAInitResult(VideoDecodeAcceleratorAdaptor::Result::ILLEGAL_STATE),
405 mComponentState(ComponentState::UNINITIALIZED),
Pin-chih Lin617d7632018-05-30 16:48:07 +0800406 mPendingOutputEOS(false),
Pin-chih Lin3aff30b2018-10-02 21:11:01 +0800407 mPendingColorAspectsChange(false),
408 mPendingColorAspectsChangeFrameIndex(0),
Johny Lina891f0e2017-12-19 10:25:27 +0800409 mCodecProfile(media::VIDEO_CODEC_PROFILE_UNKNOWN),
410 mState(State::UNLOADED),
411 mWeakThisFactory(this) {
Johny Lin9be52702017-11-15 20:52:31 +0800412 // TODO(johnylin): the client may need to know if init is failed.
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +0800413 if (mIntfImpl->status() != C2_OK) {
414 ALOGE("Component interface init failed (err code = %d)", mIntfImpl->status());
Johny Lin9be52702017-11-15 20:52:31 +0800415 return;
416 }
Hirokazu Honda3f3c8a42018-05-09 22:25:19 +0800417
418 mSecureMode = name.find(".secure") != std::string::npos;
Johny Lin744cff12017-08-16 16:56:25 +0800419 if (!mThread.Start()) {
Johny Lin9be52702017-11-15 20:52:31 +0800420 ALOGE("Component thread failed to start.");
Johny Lin744cff12017-08-16 16:56:25 +0800421 return;
422 }
423 mTaskRunner = mThread.task_runner();
Pin-chih Lin2a00c242018-03-28 20:41:50 +0800424 mState.store(State::LOADED);
Johny Lin7c4cb522017-06-26 16:10:24 +0800425}
426
427C2VDAComponent::~C2VDAComponent() {
Johny Lin744cff12017-08-16 16:56:25 +0800428 if (mThread.IsRunning()) {
Johny Lina891f0e2017-12-19 10:25:27 +0800429 mTaskRunner->PostTask(FROM_HERE,
Pin-chih Lin448c0c72018-05-22 14:17:53 +0800430 ::base::Bind(&C2VDAComponent::onDestroy, ::base::Unretained(this)));
Johny Lin744cff12017-08-16 16:56:25 +0800431 mThread.Stop();
432 }
Johny Lin744cff12017-08-16 16:56:25 +0800433}
434
Johny Lin744cff12017-08-16 16:56:25 +0800435void C2VDAComponent::onDestroy() {
436 DCHECK(mTaskRunner->BelongsToCurrentThread());
437 ALOGV("onDestroy");
Johny Lin52bffe12017-09-19 14:26:06 +0800438 if (mVDAAdaptor.get()) {
439 mVDAAdaptor->destroy();
440 mVDAAdaptor.reset(nullptr);
441 }
Pin-chih Lin906956a2018-05-22 14:12:59 +0800442 stopDequeueThread();
Johny Lin744cff12017-08-16 16:56:25 +0800443}
444
Pin-chih Lin448c0c72018-05-22 14:17:53 +0800445void C2VDAComponent::onStart(media::VideoCodecProfile profile, ::base::WaitableEvent* done) {
Johny Lin744cff12017-08-16 16:56:25 +0800446 DCHECK(mTaskRunner->BelongsToCurrentThread());
447 ALOGV("onStart");
Hirokazu Hondace275062017-10-30 11:34:57 +0900448 CHECK_EQ(mComponentState, ComponentState::UNINITIALIZED);
Pin-chih Lin878832b2018-04-18 20:33:13 +0800449
450#ifdef V4L2_CODEC2_ARC
451 mVDAAdaptor.reset(new arc::C2VDAAdaptorProxy());
452#else
453 mVDAAdaptor.reset(new C2VDAAdaptor());
454#endif
455
Hirokazu Honda3f3c8a42018-05-09 22:25:19 +0800456 mVDAInitResult = mVDAAdaptor->initialize(profile, mSecureMode, this);
Johny Lin744cff12017-08-16 16:56:25 +0800457 if (mVDAInitResult == VideoDecodeAcceleratorAdaptor::Result::SUCCESS) {
458 mComponentState = ComponentState::STARTED;
459 }
Johny Lin52bffe12017-09-19 14:26:06 +0800460
Pin-chih Lin3aff30b2018-10-02 21:11:01 +0800461 if (!mSecureMode && mIntfImpl->getInputCodec() == InputCodec::H264) {
462 // Get default color aspects on start.
463 updateColorAspects();
464 mPendingColorAspectsChange = false;
465 }
466
Johny Lin744cff12017-08-16 16:56:25 +0800467 done->Signal();
468}
469
Johny Lin52bffe12017-09-19 14:26:06 +0800470void C2VDAComponent::onQueueWork(std::unique_ptr<C2Work> work) {
471 DCHECK(mTaskRunner->BelongsToCurrentThread());
Johny Lin3760d5f2018-01-31 14:40:13 +0800472 ALOGV("onQueueWork: flags=0x%x, index=%llu, timestamp=%llu", work->input.flags,
473 work->input.ordinal.frameIndex.peekull(), work->input.ordinal.timestamp.peekull());
Johny Lin52bffe12017-09-19 14:26:06 +0800474 EXPECT_RUNNING_OR_RETURN_ON_ERROR();
Johny Lin3760d5f2018-01-31 14:40:13 +0800475
476 uint32_t drainMode = NO_DRAIN;
477 if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
478 drainMode = DRAIN_COMPONENT_WITH_EOS;
479 }
480 mQueue.push({std::move(work), drainMode});
Johny Lin52bffe12017-09-19 14:26:06 +0800481 // TODO(johnylin): set a maximum size of mQueue and check if mQueue is already full.
482
483 mTaskRunner->PostTask(FROM_HERE,
Pin-chih Lin448c0c72018-05-22 14:17:53 +0800484 ::base::Bind(&C2VDAComponent::onDequeueWork, ::base::Unretained(this)));
Johny Lin52bffe12017-09-19 14:26:06 +0800485}
486
487void C2VDAComponent::onDequeueWork() {
488 DCHECK(mTaskRunner->BelongsToCurrentThread());
489 ALOGV("onDequeueWork");
490 EXPECT_RUNNING_OR_RETURN_ON_ERROR();
491 if (mQueue.empty()) {
492 return;
493 }
Pin-chih Lin29d50d92018-05-30 14:03:52 +0800494 if (mComponentState == ComponentState::DRAINING ||
495 mComponentState == ComponentState::FLUSHING) {
496 ALOGV("Temporarily stop dequeueing works since component is draining/flushing.");
Johny Lin52bffe12017-09-19 14:26:06 +0800497 return;
498 }
499 if (mComponentState != ComponentState::STARTED) {
500 ALOGE("Work queue should be empty if the component is not in STARTED state.");
501 return;
502 }
503
504 // Dequeue a work from mQueue.
Johny Lin3760d5f2018-01-31 14:40:13 +0800505 std::unique_ptr<C2Work> work(std::move(mQueue.front().mWork));
506 auto drainMode = mQueue.front().mDrainMode;
Johny Lin52bffe12017-09-19 14:26:06 +0800507 mQueue.pop();
508
Pin-chih Linca72eff2018-06-12 11:22:04 +0800509 CHECK_LE(work->input.buffers.size(), 1u);
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +0800510 bool isEmptyCSDWork = false;
511 // Use frameIndex as bitstreamId.
512 int32_t bitstreamId = frameIndexToBitstreamId(work->input.ordinal.frameIndex);
Pin-chih Linca72eff2018-06-12 11:22:04 +0800513 if (work->input.buffers.empty()) {
Pin-chih Lin3fe0c8d2018-09-11 12:17:53 +0800514 // Client may queue a work with no input buffer for either it's EOS or empty CSD, otherwise
515 // every work must have one input buffer.
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +0800516 isEmptyCSDWork = work->input.flags & C2FrameData::FLAG_CODEC_CONFIG;
517 CHECK(drainMode != NO_DRAIN || isEmptyCSDWork);
Pin-chih Lin3fe0c8d2018-09-11 12:17:53 +0800518 // Emplace a nullptr to unify the check for work done.
519 ALOGV("Got a work with no input buffer! Emplace a nullptr inside.");
520 work->input.buffers.emplace_back(nullptr);
Pin-chih Linca72eff2018-06-12 11:22:04 +0800521 } else {
522 // If input.buffers is not empty, the buffer should have meaningful content inside.
523 C2ConstLinearBlock linearBlock = work->input.buffers.front()->data().linearBlocks().front();
524 CHECK_GT(linearBlock.size(), 0u);
Pin-chih Lin3aff30b2018-10-02 21:11:01 +0800525
526 // Call parseCodedColorAspects() to try to parse color aspects from bitstream only if:
527 // 1) This is non-secure decoding.
528 // 2) This is H264 codec.
529 // 3) This input is CSD buffer (with flags FLAG_CODEC_CONFIG).
530 if (!mSecureMode && (mIntfImpl->getInputCodec() == InputCodec::H264) &&
531 (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG)) {
532 if (parseCodedColorAspects(linearBlock)) {
533 // Record current frame index, color aspects should be updated only for output
534 // buffers whose frame indices are not less than this one.
535 mPendingColorAspectsChange = true;
536 mPendingColorAspectsChangeFrameIndex = work->input.ordinal.frameIndex.peeku();
537 }
538 }
Johny Lin3760d5f2018-01-31 14:40:13 +0800539 // Send input buffer to VDA for decode.
Johny Lin52bffe12017-09-19 14:26:06 +0800540 sendInputBufferToAccelerator(linearBlock, bitstreamId);
541 }
542
Johny Lin3760d5f2018-01-31 14:40:13 +0800543 CHECK_EQ(work->worklets.size(), 1u);
544 work->worklets.front()->output.flags = static_cast<C2FrameData::flags_t>(0);
545 work->worklets.front()->output.buffers.clear();
546 work->worklets.front()->output.ordinal = work->input.ordinal;
547
548 if (drainMode != NO_DRAIN) {
Johny Lin52bffe12017-09-19 14:26:06 +0800549 mVDAAdaptor->flush();
550 mComponentState = ComponentState::DRAINING;
Pin-chih Lin617d7632018-05-30 16:48:07 +0800551 mPendingOutputEOS = drainMode == DRAIN_COMPONENT_WITH_EOS;
Johny Lin52bffe12017-09-19 14:26:06 +0800552 }
553
Johny Lin3760d5f2018-01-31 14:40:13 +0800554 // Put work to mPendingWorks.
Johny Lin52bffe12017-09-19 14:26:06 +0800555 mPendingWorks.emplace_back(std::move(work));
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +0800556 if (isEmptyCSDWork) {
557 // Directly report the empty CSD work as finished.
558 reportWorkIfFinished(bitstreamId);
559 }
Johny Lin52bffe12017-09-19 14:26:06 +0800560
561 if (!mQueue.empty()) {
Pin-chih Lin448c0c72018-05-22 14:17:53 +0800562 mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onDequeueWork,
563 ::base::Unretained(this)));
Johny Lin52bffe12017-09-19 14:26:06 +0800564 }
565}
566
567void C2VDAComponent::onInputBufferDone(int32_t bitstreamId) {
568 DCHECK(mTaskRunner->BelongsToCurrentThread());
569 ALOGV("onInputBufferDone: bitstream id=%d", bitstreamId);
570 EXPECT_RUNNING_OR_RETURN_ON_ERROR();
571
572 C2Work* work = getPendingWorkByBitstreamId(bitstreamId);
573 if (!work) {
574 reportError(C2_CORRUPTED);
575 return;
576 }
577
Pin-chih Lincd56fe72018-05-04 10:29:23 +0800578 // When the work is done, the input buffer shall be reset by component.
579 work->input.buffers.front().reset();
Johny Lin52bffe12017-09-19 14:26:06 +0800580
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +0800581 reportWorkIfFinished(bitstreamId);
Johny Lin52bffe12017-09-19 14:26:06 +0800582}
583
Pin-chih Lin585324e2018-07-25 16:24:54 +0800584void C2VDAComponent::onOutputBufferReturned(std::shared_ptr<C2GraphicBlock> block,
585 uint32_t poolId) {
Johny Lin52bffe12017-09-19 14:26:06 +0800586 DCHECK(mTaskRunner->BelongsToCurrentThread());
Pin-chih Lin585324e2018-07-25 16:24:54 +0800587 ALOGV("onOutputBufferReturned: pool id=%u", poolId);
Pin-chih Lin9bf0e962018-03-08 23:36:17 +0800588 if (mComponentState == ComponentState::UNINITIALIZED) {
589 // Output buffer is returned from client after component is stopped. Just let the buffer be
590 // released.
591 return;
592 }
Johny Lin52bffe12017-09-19 14:26:06 +0800593
Pin-chih Lin81a44bb2018-08-17 17:54:00 +0800594 if (block->width() != static_cast<uint32_t>(mOutputFormat.mCodedSize.width()) ||
595 block->height() != static_cast<uint32_t>(mOutputFormat.mCodedSize.height())) {
596 // Output buffer is returned after we changed output resolution. Just let the buffer be
597 // released.
598 ALOGV("Discard obsolete graphic block: pool id=%u", poolId);
599 return;
600 }
601
Pin-chih Lin585324e2018-07-25 16:24:54 +0800602 GraphicBlockInfo* info = getGraphicBlockByPoolId(poolId);
Johny Lin52bffe12017-09-19 14:26:06 +0800603 if (!info) {
604 reportError(C2_CORRUPTED);
605 return;
606 }
607 CHECK_EQ(info->mState, GraphicBlockInfo::State::OWNED_BY_CLIENT);
Pin-chih Lin585324e2018-07-25 16:24:54 +0800608 info->mGraphicBlock = std::move(block);
Johny Lin52bffe12017-09-19 14:26:06 +0800609 info->mState = GraphicBlockInfo::State::OWNED_BY_COMPONENT;
610
611 if (mPendingOutputFormat) {
612 tryChangeOutputFormat();
613 } else {
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +0800614 // Do not pass the ownership to accelerator if this buffer will still be reused under
615 // |mPendingBuffersToWork|.
616 auto existingFrame = std::find_if(
617 mPendingBuffersToWork.begin(), mPendingBuffersToWork.end(),
618 [id = info->mBlockId](const OutputBufferInfo& o) { return o.mBlockId == id; });
619 bool ownByAccelerator = existingFrame == mPendingBuffersToWork.end();
620 sendOutputBufferToAccelerator(info, ownByAccelerator);
621 sendOutputBufferToWorkIfAny(false /* dropIfUnavailable */);
Johny Lin52bffe12017-09-19 14:26:06 +0800622 }
623}
624
625void C2VDAComponent::onOutputBufferDone(int32_t pictureBufferId, int32_t bitstreamId) {
626 DCHECK(mTaskRunner->BelongsToCurrentThread());
627 ALOGV("onOutputBufferDone: picture id=%d, bitstream id=%d", pictureBufferId, bitstreamId);
628 EXPECT_RUNNING_OR_RETURN_ON_ERROR();
629
Johny Lin52bffe12017-09-19 14:26:06 +0800630 GraphicBlockInfo* info = getGraphicBlockById(pictureBufferId);
631 if (!info) {
632 reportError(C2_CORRUPTED);
633 return;
634 }
Johny Lin52bffe12017-09-19 14:26:06 +0800635
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +0800636 if (info->mState == GraphicBlockInfo::State::OWNED_BY_ACCELERATOR) {
637 info->mState = GraphicBlockInfo::State::OWNED_BY_COMPONENT;
Pin-chih Lin3aff30b2018-10-02 21:11:01 +0800638 }
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +0800639 mPendingBuffersToWork.push_back({bitstreamId, pictureBufferId});
640 sendOutputBufferToWorkIfAny(false /* dropIfUnavailable */);
641}
Johny Lin52bffe12017-09-19 14:26:06 +0800642
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +0800643void C2VDAComponent::sendOutputBufferToWorkIfAny(bool dropIfUnavailable) {
644 DCHECK(mTaskRunner->BelongsToCurrentThread());
645
646 while (!mPendingBuffersToWork.empty()) {
647 auto nextBuffer = mPendingBuffersToWork.front();
648 GraphicBlockInfo* info = getGraphicBlockById(nextBuffer.mBlockId);
649 CHECK_NE(info->mState, GraphicBlockInfo::State::OWNED_BY_ACCELERATOR);
650
651 C2Work* work = getPendingWorkByBitstreamId(nextBuffer.mBitstreamId);
652 if (!work) {
653 reportError(C2_CORRUPTED);
654 return;
655 }
656
657 if (info->mState == GraphicBlockInfo::State::OWNED_BY_CLIENT) {
658 // This buffer is the existing frame and still owned by client.
659 if (!dropIfUnavailable &&
660 std::find(mUndequeuedBlockIds.begin(), mUndequeuedBlockIds.end(),
661 nextBuffer.mBlockId) == mUndequeuedBlockIds.end()) {
662 ALOGV("Still waiting for existing frame returned from client...");
663 return;
664 }
665 ALOGV("Drop this frame...");
666 sendOutputBufferToAccelerator(info, false /* ownByAccelerator */);
667 work->worklets.front()->output.flags = C2FrameData::FLAG_DROP_FRAME;
668 } else {
669 // This buffer is ready to push into the corresponding work.
670 // Output buffer will be passed to client soon along with mListener->onWorkDone_nb().
671 info->mState = GraphicBlockInfo::State::OWNED_BY_CLIENT;
672 mBuffersInClient++;
673 updateUndequeuedBlockIds(info->mBlockId);
674
675 // Attach output buffer to the work corresponded to bitstreamId.
676 C2ConstGraphicBlock constBlock = info->mGraphicBlock->share(
677 C2Rect(mOutputFormat.mVisibleRect.width(),
678 mOutputFormat.mVisibleRect.height()),
679 C2Fence());
680 MarkBlockPoolDataAsShared(constBlock);
681
682 std::shared_ptr<C2Buffer> buffer = C2Buffer::CreateGraphicBuffer(std::move(constBlock));
683 if (mPendingColorAspectsChange &&
684 work->input.ordinal.frameIndex.peeku() >= mPendingColorAspectsChangeFrameIndex) {
685 updateColorAspects();
686 mPendingColorAspectsChange = false;
687 }
688 if (mCurrentColorAspects) {
689 buffer->setInfo(mCurrentColorAspects);
690 }
691 work->worklets.front()->output.buffers.emplace_back(std::move(buffer));
692 info->mGraphicBlock.reset();
693 }
694 reportWorkIfFinished(nextBuffer.mBitstreamId);
695 mPendingBuffersToWork.pop_front();
696 }
697}
698
699void C2VDAComponent::updateUndequeuedBlockIds(int32_t blockId) {
700 // The size of |mUndequedBlockIds| will always be the minimum buffer count for display.
701 mUndequeuedBlockIds.push_back(blockId);
702 mUndequeuedBlockIds.pop_front();
Johny Lin52bffe12017-09-19 14:26:06 +0800703}
704
Johny Lin3760d5f2018-01-31 14:40:13 +0800705void C2VDAComponent::onDrain(uint32_t drainMode) {
Johny Lin52bffe12017-09-19 14:26:06 +0800706 DCHECK(mTaskRunner->BelongsToCurrentThread());
Johny Lin3760d5f2018-01-31 14:40:13 +0800707 ALOGV("onDrain: mode = %u", drainMode);
708 EXPECT_RUNNING_OR_RETURN_ON_ERROR();
Johny Lin52bffe12017-09-19 14:26:06 +0800709
Johny Lin52bffe12017-09-19 14:26:06 +0800710 if (!mQueue.empty()) {
Johny Lin3760d5f2018-01-31 14:40:13 +0800711 // Mark last queued work as "drain-till-here" by setting drainMode. Do not change drainMode
712 // if last work already has one.
713 if (mQueue.back().mDrainMode == NO_DRAIN) {
714 mQueue.back().mDrainMode = drainMode;
Johny Lin52bffe12017-09-19 14:26:06 +0800715 }
Johny Lin3760d5f2018-01-31 14:40:13 +0800716 } else if (!mPendingWorks.empty()) {
717 // Neglect drain request if component is not in STARTED mode. Otherwise, enters DRAINING
718 // mode and signal VDA flush immediately.
719 if (mComponentState == ComponentState::STARTED) {
720 mVDAAdaptor->flush();
721 mComponentState = ComponentState::DRAINING;
Pin-chih Lin617d7632018-05-30 16:48:07 +0800722 mPendingOutputEOS = drainMode == DRAIN_COMPONENT_WITH_EOS;
Johny Lin3760d5f2018-01-31 14:40:13 +0800723 } else {
724 ALOGV("Neglect drain. Component in state: %d", mComponentState);
725 }
Johny Lin52bffe12017-09-19 14:26:06 +0800726 } else {
727 // Do nothing.
728 ALOGV("No buffers in VDA, drain takes no effect.");
729 }
730}
731
732void C2VDAComponent::onDrainDone() {
733 DCHECK(mTaskRunner->BelongsToCurrentThread());
734 ALOGV("onDrainDone");
735 if (mComponentState == ComponentState::DRAINING) {
736 mComponentState = ComponentState::STARTED;
737 } else if (mComponentState == ComponentState::STOPPING) {
738 // The client signals stop right before VDA notifies drain done. Let stop process goes.
739 return;
Pin-chih Lin617d7632018-05-30 16:48:07 +0800740 } else if (mComponentState != ComponentState::FLUSHING) {
741 // It is reasonable to get onDrainDone in FLUSHING, which means flush is already signaled
742 // and component should still expect onFlushDone callback from VDA.
Johny Lin52bffe12017-09-19 14:26:06 +0800743 ALOGE("Unexpected state while onDrainDone(). State=%d", mComponentState);
744 reportError(C2_BAD_STATE);
745 return;
746 }
747
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +0800748 // Drop all pending existing frames and return all finished works before drain done.
749 sendOutputBufferToWorkIfAny(true /* dropIfUnavailable */);
750 CHECK(mPendingBuffersToWork.empty());
751
Pin-chih Lin617d7632018-05-30 16:48:07 +0800752 if (mPendingOutputEOS) {
Johny Lin3760d5f2018-01-31 14:40:13 +0800753 // Return EOS work.
754 reportEOSWork();
755 }
756 // mPendingWorks must be empty after draining is finished.
757 CHECK(mPendingWorks.empty());
758
Johny Lin52bffe12017-09-19 14:26:06 +0800759 // Work dequeueing was stopped while component draining. Restart it.
760 mTaskRunner->PostTask(FROM_HERE,
Pin-chih Lin448c0c72018-05-22 14:17:53 +0800761 ::base::Bind(&C2VDAComponent::onDequeueWork, ::base::Unretained(this)));
Johny Lin52bffe12017-09-19 14:26:06 +0800762}
763
764void C2VDAComponent::onFlush() {
765 DCHECK(mTaskRunner->BelongsToCurrentThread());
766 ALOGV("onFlush");
Pin-chih Lin617d7632018-05-30 16:48:07 +0800767 if (mComponentState == ComponentState::FLUSHING ||
768 mComponentState == ComponentState::STOPPING) {
769 return; // Ignore other flush request when component is flushing or stopping.
Pin-chih Lin2a00c242018-03-28 20:41:50 +0800770 }
Pin-chih Lin617d7632018-05-30 16:48:07 +0800771 EXPECT_RUNNING_OR_RETURN_ON_ERROR();
Johny Lin52bffe12017-09-19 14:26:06 +0800772
773 mVDAAdaptor->reset();
Pin-chih Lin617d7632018-05-30 16:48:07 +0800774 // Pop all works in mQueue and put into mAbandonedWorks.
Johny Lin52bffe12017-09-19 14:26:06 +0800775 while (!mQueue.empty()) {
Pin-chih Lin617d7632018-05-30 16:48:07 +0800776 mAbandonedWorks.emplace_back(std::move(mQueue.front().mWork));
Johny Lin52bffe12017-09-19 14:26:06 +0800777 mQueue.pop();
778 }
779 mComponentState = ComponentState::FLUSHING;
780}
781
Pin-chih Lin448c0c72018-05-22 14:17:53 +0800782void C2VDAComponent::onStop(::base::WaitableEvent* done) {
Johny Lin744cff12017-08-16 16:56:25 +0800783 DCHECK(mTaskRunner->BelongsToCurrentThread());
784 ALOGV("onStop");
Johny Lin52bffe12017-09-19 14:26:06 +0800785 EXPECT_RUNNING_OR_RETURN_ON_ERROR();
786
Pin-chih Lina95fb652018-05-03 21:08:14 +0800787 // Do not request VDA reset again before the previous one is done. If reset is already sent by
788 // onFlush(), just regard the following NotifyResetDone callback as for stopping.
789 if (mComponentState != ComponentState::FLUSHING) {
790 mVDAAdaptor->reset();
791 }
792
Pin-chih Lin617d7632018-05-30 16:48:07 +0800793 // Pop all works in mQueue and put into mAbandonedWorks.
Johny Lin52bffe12017-09-19 14:26:06 +0800794 while (!mQueue.empty()) {
Pin-chih Lin617d7632018-05-30 16:48:07 +0800795 mAbandonedWorks.emplace_back(std::move(mQueue.front().mWork));
Johny Lin52bffe12017-09-19 14:26:06 +0800796 mQueue.pop();
797 }
798
Johny Lin744cff12017-08-16 16:56:25 +0800799 mStopDoneEvent = done; // restore done event which shoud be signaled in onStopDone().
800 mComponentState = ComponentState::STOPPING;
801}
802
Johny Lin52bffe12017-09-19 14:26:06 +0800803void C2VDAComponent::onResetDone() {
Johny Lin744cff12017-08-16 16:56:25 +0800804 DCHECK(mTaskRunner->BelongsToCurrentThread());
Johny Lin52bffe12017-09-19 14:26:06 +0800805 if (mComponentState == ComponentState::ERROR) {
806 return;
807 }
808 if (mComponentState == ComponentState::FLUSHING) {
809 onFlushDone();
810 } else if (mComponentState == ComponentState::STOPPING) {
811 onStopDone();
812 } else {
813 reportError(C2_CORRUPTED);
814 }
815}
816
817void C2VDAComponent::onFlushDone() {
818 ALOGV("onFlushDone");
819 reportAbandonedWorks();
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +0800820 mPendingBuffersToWork.clear();
Johny Lin52bffe12017-09-19 14:26:06 +0800821 mComponentState = ComponentState::STARTED;
Pin-chih Lin29d50d92018-05-30 14:03:52 +0800822
823 // Work dequeueing was stopped while component flushing. Restart it.
824 mTaskRunner->PostTask(FROM_HERE,
825 ::base::Bind(&C2VDAComponent::onDequeueWork, ::base::Unretained(this)));
Johny Lin52bffe12017-09-19 14:26:06 +0800826}
827
828void C2VDAComponent::onStopDone() {
Johny Lin744cff12017-08-16 16:56:25 +0800829 ALOGV("onStopDone");
Johny Lin744cff12017-08-16 16:56:25 +0800830 CHECK(mStopDoneEvent);
Johny Lin52bffe12017-09-19 14:26:06 +0800831
Johny Lin52bffe12017-09-19 14:26:06 +0800832 // TODO(johnylin): At this moment, there may be C2Buffer still owned by client, do we need to
833 // do something for them?
834 reportAbandonedWorks();
835 mPendingOutputFormat.reset();
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +0800836 mPendingBuffersToWork.clear();
Johny Lin52bffe12017-09-19 14:26:06 +0800837 if (mVDAAdaptor.get()) {
838 mVDAAdaptor->destroy();
839 mVDAAdaptor.reset(nullptr);
840 }
841
Pin-chih Lin906956a2018-05-22 14:12:59 +0800842 stopDequeueThread();
Pin-chih Lin1a350e82018-09-21 14:17:40 +0800843 mGraphicBlocks.clear();
Pin-chih Lin906956a2018-05-22 14:12:59 +0800844
Johny Lin744cff12017-08-16 16:56:25 +0800845 mStopDoneEvent->Signal();
846 mStopDoneEvent = nullptr;
847 mComponentState = ComponentState::UNINITIALIZED;
Johny Lin7c4cb522017-06-26 16:10:24 +0800848}
849
Johny Lina891f0e2017-12-19 10:25:27 +0800850c2_status_t C2VDAComponent::setListener_vb(const std::shared_ptr<C2Component::Listener>& listener,
851 c2_blocking_t mayBlock) {
Lajos Molnar2737fb62017-12-19 09:46:42 -0800852 UNUSED(mayBlock);
Johny Lin52bffe12017-09-19 14:26:06 +0800853 // TODO(johnylin): API says this method must be supported in all states, however I'm quite not
854 // sure what is the use case.
Pin-chih Lin2a00c242018-03-28 20:41:50 +0800855 if (mState.load() != State::LOADED) {
Johny Lin52bffe12017-09-19 14:26:06 +0800856 return C2_BAD_STATE;
857 }
Lajos Molnarb78444e2017-11-27 16:08:31 -0800858 mListener = listener;
Johny Lin52bffe12017-09-19 14:26:06 +0800859 return C2_OK;
860}
861
862void C2VDAComponent::sendInputBufferToAccelerator(const C2ConstLinearBlock& input,
863 int32_t bitstreamId) {
864 ALOGV("sendInputBufferToAccelerator");
865 int dupFd = dup(input.handle()->data[0]);
866 if (dupFd < 0) {
Johny Lina891f0e2017-12-19 10:25:27 +0800867 ALOGE("Failed to dup(%d) input buffer (bitstreamId=%d), errno=%d", input.handle()->data[0],
868 bitstreamId, errno);
Johny Lin52bffe12017-09-19 14:26:06 +0800869 reportError(C2_CORRUPTED);
870 return;
871 }
Johny Lina891f0e2017-12-19 10:25:27 +0800872 ALOGV("Decode bitstream ID: %d, offset: %u size: %u", bitstreamId, input.offset(),
873 input.size());
Johny Lin52bffe12017-09-19 14:26:06 +0800874 mVDAAdaptor->decode(bitstreamId, dupFd, input.offset(), input.size());
875}
876
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +0800877std::deque<std::unique_ptr<C2Work>>::iterator C2VDAComponent::findPendingWorkByBitstreamId(
878 int32_t bitstreamId) {
879 return std::find_if(mPendingWorks.begin(), mPendingWorks.end(),
880 [bitstreamId](const std::unique_ptr<C2Work>& w) {
881 return frameIndexToBitstreamId(w->input.ordinal.frameIndex) ==
882 bitstreamId;
883 });
884}
Johny Lin52bffe12017-09-19 14:26:06 +0800885
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +0800886C2Work* C2VDAComponent::getPendingWorkByBitstreamId(int32_t bitstreamId) {
887 auto workIter = findPendingWorkByBitstreamId(bitstreamId);
Johny Lin52bffe12017-09-19 14:26:06 +0800888 if (workIter == mPendingWorks.end()) {
889 ALOGE("Can't find pending work by bitstream ID: %d", bitstreamId);
890 return nullptr;
891 }
892 return workIter->get();
893}
894
Johny Lin52bffe12017-09-19 14:26:06 +0800895C2VDAComponent::GraphicBlockInfo* C2VDAComponent::getGraphicBlockById(int32_t blockId) {
Johny Lina891f0e2017-12-19 10:25:27 +0800896 if (blockId < 0 || blockId >= static_cast<int32_t>(mGraphicBlocks.size())) {
Johny Lin52bffe12017-09-19 14:26:06 +0800897 ALOGE("getGraphicBlockById failed: id=%d", blockId);
898 return nullptr;
899 }
900 return &mGraphicBlocks[blockId];
901}
902
Pin-chih Lin585324e2018-07-25 16:24:54 +0800903C2VDAComponent::GraphicBlockInfo* C2VDAComponent::getGraphicBlockByPoolId(uint32_t poolId) {
Pin-chih Lin906956a2018-05-22 14:12:59 +0800904 auto blockIter = std::find_if(mGraphicBlocks.begin(), mGraphicBlocks.end(),
Pin-chih Lin585324e2018-07-25 16:24:54 +0800905 [poolId](const GraphicBlockInfo& gb) {
906 return gb.mPoolId == poolId;
Pin-chih Lin906956a2018-05-22 14:12:59 +0800907 });
908
909 if (blockIter == mGraphicBlocks.end()) {
Pin-chih Lin585324e2018-07-25 16:24:54 +0800910 ALOGE("getGraphicBlockByPoolId failed: poolId=%u", poolId);
Pin-chih Lin906956a2018-05-22 14:12:59 +0800911 return nullptr;
912 }
913 return &(*blockIter);
914}
915
Johny Lin52bffe12017-09-19 14:26:06 +0800916void C2VDAComponent::onOutputFormatChanged(std::unique_ptr<VideoFormat> format) {
917 DCHECK(mTaskRunner->BelongsToCurrentThread());
918 ALOGV("onOutputFormatChanged");
919 EXPECT_RUNNING_OR_RETURN_ON_ERROR();
920
921 ALOGV("New output format(pixel_format=0x%x, min_num_buffers=%u, coded_size=%s, crop_rect=%s)",
Johny Linac6e2702018-01-17 12:11:52 +0800922 static_cast<uint32_t>(format->mPixelFormat), format->mMinNumBuffers,
923 format->mCodedSize.ToString().c_str(), format->mVisibleRect.ToString().c_str());
Johny Lin52bffe12017-09-19 14:26:06 +0800924
925 for (auto& info : mGraphicBlocks) {
926 if (info.mState == GraphicBlockInfo::State::OWNED_BY_ACCELERATOR)
927 info.mState = GraphicBlockInfo::State::OWNED_BY_COMPONENT;
928 }
929
930 CHECK(!mPendingOutputFormat);
931 mPendingOutputFormat = std::move(format);
932 tryChangeOutputFormat();
933}
934
935void C2VDAComponent::tryChangeOutputFormat() {
936 DCHECK(mTaskRunner->BelongsToCurrentThread());
937 ALOGV("tryChangeOutputFormat");
938 CHECK(mPendingOutputFormat);
939
Pin-chih Lin81a44bb2018-08-17 17:54:00 +0800940 // At this point, all output buffers should not be owned by accelerator. The component is not
941 // able to know when a client will release all owned output buffers by now. But it is ok to
942 // leave them to client since componenet won't own those buffers anymore.
943 // TODO(johnylin): we may also set a parameter for component to keep dequeueing buffers and
944 // change format only after the component owns most buffers. This may prevent
945 // too many buffers are still on client's hand while component starts to
946 // allocate more buffers. However, it leads latency on output format change.
Johny Lin52bffe12017-09-19 14:26:06 +0800947 for (const auto& info : mGraphicBlocks) {
Pin-chih Lin81a44bb2018-08-17 17:54:00 +0800948 CHECK(info.mState != GraphicBlockInfo::State::OWNED_BY_ACCELERATOR);
Johny Lin52bffe12017-09-19 14:26:06 +0800949 }
950
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +0800951 // Drop all pending existing frames and return all finished works before changing output format.
952 sendOutputBufferToWorkIfAny(true /* dropIfUnavailable */);
953 CHECK(mPendingBuffersToWork.empty());
954
Johny Linac6e2702018-01-17 12:11:52 +0800955 CHECK_EQ(mPendingOutputFormat->mPixelFormat, HalPixelFormat::YCbCr_420_888);
Johny Lin52bffe12017-09-19 14:26:06 +0800956
957 mOutputFormat.mPixelFormat = mPendingOutputFormat->mPixelFormat;
958 mOutputFormat.mMinNumBuffers = mPendingOutputFormat->mMinNumBuffers;
959 mOutputFormat.mCodedSize = mPendingOutputFormat->mCodedSize;
960
961 setOutputFormatCrop(mPendingOutputFormat->mVisibleRect);
Johny Lin52bffe12017-09-19 14:26:06 +0800962
Johny Linac6e2702018-01-17 12:11:52 +0800963 c2_status_t err = allocateBuffersFromBlockAllocator(
964 mPendingOutputFormat->mCodedSize,
965 static_cast<uint32_t>(mPendingOutputFormat->mPixelFormat));
Johny Lin52bffe12017-09-19 14:26:06 +0800966 if (err != C2_OK) {
967 reportError(err);
968 return;
969 }
970
971 for (auto& info : mGraphicBlocks) {
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +0800972 sendOutputBufferToAccelerator(&info, true /* ownByAccelerator */);
Johny Lin52bffe12017-09-19 14:26:06 +0800973 }
974 mPendingOutputFormat.reset();
975}
976
977c2_status_t C2VDAComponent::allocateBuffersFromBlockAllocator(const media::Size& size,
Johny Linac6e2702018-01-17 12:11:52 +0800978 uint32_t pixelFormat) {
Johny Lin52bffe12017-09-19 14:26:06 +0800979 ALOGV("allocateBuffersFromBlockAllocator(%s, 0x%x)", size.ToString().c_str(), pixelFormat);
980
Pin-chih Lin62585f02018-05-29 21:00:03 +0800981 stopDequeueThread();
982
Johny Lin52bffe12017-09-19 14:26:06 +0800983 size_t bufferCount = mOutputFormat.mMinNumBuffers + kDpbOutputBufferExtraCount;
984
985 // Allocate the output buffers.
986 mVDAAdaptor->assignPictureBuffers(bufferCount);
987
Pin-chih Lin2fea0712018-05-14 19:57:14 +0800988 // Get block pool ID configured from the client.
Pin-chih Lin62585f02018-05-29 21:00:03 +0800989 std::shared_ptr<C2BlockPool> blockPool;
Pin-chih Lin2fea0712018-05-14 19:57:14 +0800990 auto poolId = mIntfImpl->getBlockPoolId();
Johny Lin52bffe12017-09-19 14:26:06 +0800991 ALOGI("Using C2BlockPool ID = %" PRIu64 " for allocating output buffers", poolId);
Pin-chih Lin62585f02018-05-29 21:00:03 +0800992 auto err = GetCodec2BlockPool(poolId, shared_from_this(), &blockPool);
993 if (err != C2_OK) {
994 ALOGE("Graphic block allocator is invalid");
995 reportError(err);
996 return err;
Johny Lin52bffe12017-09-19 14:26:06 +0800997 }
998
999 mGraphicBlocks.clear();
Pin-chih Lin906956a2018-05-22 14:12:59 +08001000
Pin-chih Lin585324e2018-07-25 16:24:54 +08001001 bool useBufferQueue = blockPool->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE;
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +08001002 size_t minBuffersForDisplay = 0;
Pin-chih Lin585324e2018-07-25 16:24:54 +08001003 if (useBufferQueue) {
1004 ALOGV("Bufferqueue-backed block pool is used.");
Pin-chih Lin5ab26132018-05-09 22:25:19 +08001005 // Set requested buffer count to C2VdaBqBlockPool.
1006 std::shared_ptr<C2VdaBqBlockPool> bqPool =
Pin-chih Lin62585f02018-05-29 21:00:03 +08001007 std::static_pointer_cast<C2VdaBqBlockPool>(blockPool);
Pin-chih Lin5ab26132018-05-09 22:25:19 +08001008 if (bqPool) {
1009 err = bqPool->requestNewBufferSet(static_cast<int32_t>(bufferCount));
Pin-chih Lin585324e2018-07-25 16:24:54 +08001010 if (err != C2_OK) {
1011 ALOGE("failed to request new buffer set to block pool: %d", err);
Pin-chih Lin5ab26132018-05-09 22:25:19 +08001012 reportError(err);
1013 return err;
1014 }
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +08001015 err = bqPool->getMinBuffersForDisplay(&minBuffersForDisplay);
1016 if (err != C2_OK) {
1017 ALOGE("failed to query minimum undequeued buffer count from block pool: %d", err);
1018 reportError(err);
1019 return err;
1020 }
Pin-chih Lin5ab26132018-05-09 22:25:19 +08001021 } else {
1022 ALOGE("static_pointer_cast C2VdaBqBlockPool failed...");
1023 reportError(C2_CORRUPTED);
1024 return C2_CORRUPTED;
Pin-chih Lin906956a2018-05-22 14:12:59 +08001025 }
Pin-chih Lin585324e2018-07-25 16:24:54 +08001026 } else {
1027 ALOGV("Bufferpool-backed block pool is used.");
1028 // Set requested buffer count to C2VdaPooledBlockPool.
1029 std::shared_ptr<C2VdaPooledBlockPool> bpPool =
1030 std::static_pointer_cast<C2VdaPooledBlockPool>(blockPool);
1031 if (bpPool) {
1032 err = bpPool->requestNewBufferSet(static_cast<int32_t>(bufferCount));
1033 if (err != C2_OK) {
1034 ALOGE("failed to request new buffer set to block pool: %d", err);
1035 reportError(err);
1036 return err;
1037 }
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +08001038 minBuffersForDisplay = 0; // no undequeued buffer restriction for bufferpool.
Pin-chih Lin585324e2018-07-25 16:24:54 +08001039 } else {
1040 ALOGE("static_pointer_cast C2VdaPooledBlockPool failed...");
1041 reportError(C2_CORRUPTED);
1042 return C2_CORRUPTED;
1043 }
Pin-chih Lin906956a2018-05-22 14:12:59 +08001044 }
1045
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +08001046 ALOGV("Minimum undequeued buffer count = %zu", minBuffersForDisplay);
1047 mUndequeuedBlockIds.resize(minBuffersForDisplay, -1);
1048
Johny Lin52bffe12017-09-19 14:26:06 +08001049 for (size_t i = 0; i < bufferCount; ++i) {
1050 std::shared_ptr<C2GraphicBlock> block;
Hirokazu Honda3f3c8a42018-05-09 22:25:19 +08001051 C2MemoryUsage usage = {
1052 mSecureMode ? C2MemoryUsage::READ_PROTECTED : C2MemoryUsage::CPU_READ, 0};
Pin-chih Lin81a44bb2018-08-17 17:54:00 +08001053
1054 int32_t retries_left = kAllocateBufferMaxRetries;
1055 err = C2_NO_INIT;
1056 while (err != C2_OK) {
1057 err = blockPool->fetchGraphicBlock(size.width(), size.height(), pixelFormat, usage,
1058 &block);
1059 if (err == C2_TIMED_OUT && retries_left > 0) {
1060 ALOGD("allocate buffer timeout, %d retry time(s) left...", retries_left);
1061 retries_left--;
1062 } else if (err != C2_OK) {
1063 mGraphicBlocks.clear();
1064 ALOGE("failed to allocate buffer: %d", err);
1065 reportError(err);
1066 return err;
1067 }
Johny Lin52bffe12017-09-19 14:26:06 +08001068 }
Pin-chih Lin81a44bb2018-08-17 17:54:00 +08001069
Pin-chih Lin585324e2018-07-25 16:24:54 +08001070 uint32_t poolId;
1071 if (useBufferQueue) {
1072 err = C2VdaBqBlockPool::getPoolIdFromGraphicBlock(block, &poolId);
1073 } else { // use bufferpool
1074 err = C2VdaPooledBlockPool::getPoolIdFromGraphicBlock(block, &poolId);
1075 }
1076 if (err != C2_OK) {
1077 mGraphicBlocks.clear();
1078 ALOGE("failed to getPoolIdFromGraphicBlock: %d", err);
1079 reportError(err);
1080 return err;
1081 }
Hirokazu Honda3f3c8a42018-05-09 22:25:19 +08001082 if (mSecureMode) {
1083 appendSecureOutputBuffer(std::move(block), poolId);
1084 } else {
1085 appendOutputBuffer(std::move(block), poolId);
1086 }
Johny Lin52bffe12017-09-19 14:26:06 +08001087 }
1088 mOutputFormat.mMinNumBuffers = bufferCount;
Pin-chih Lin906956a2018-05-22 14:12:59 +08001089
Pin-chih Lin02faf852018-10-24 14:20:53 +08001090 if (!startDequeueThread(size, pixelFormat, std::move(blockPool),
1091 true /* resetBuffersInClient */)) {
Pin-chih Lin906956a2018-05-22 14:12:59 +08001092 reportError(C2_CORRUPTED);
1093 return C2_CORRUPTED;
1094 }
Johny Lin52bffe12017-09-19 14:26:06 +08001095 return C2_OK;
1096}
1097
Pin-chih Lin585324e2018-07-25 16:24:54 +08001098void C2VDAComponent::appendOutputBuffer(std::shared_ptr<C2GraphicBlock> block, uint32_t poolId) {
Johny Lin52bffe12017-09-19 14:26:06 +08001099 GraphicBlockInfo info;
Johny Lin8a2118e2018-02-08 16:29:25 +08001100 info.mBlockId = static_cast<int32_t>(mGraphicBlocks.size());
Johny Lin52bffe12017-09-19 14:26:06 +08001101 info.mGraphicBlock = std::move(block);
Pin-chih Lin585324e2018-07-25 16:24:54 +08001102 info.mPoolId = poolId;
Johny Lin52bffe12017-09-19 14:26:06 +08001103
Johny Lin727e9572018-01-24 21:04:06 +08001104 C2ConstGraphicBlock constBlock = info.mGraphicBlock->share(
1105 C2Rect(info.mGraphicBlock->width(), info.mGraphicBlock->height()), C2Fence());
1106
1107 const C2GraphicView& view = constBlock.map().get();
Johny Lin52bffe12017-09-19 14:26:06 +08001108 const uint8_t* const* data = view.data();
1109 CHECK_NE(data, nullptr);
Lajos Molnara0c6bbd2018-01-03 12:53:15 -08001110 const C2PlanarLayout& layout = view.layout();
Johny Lin52bffe12017-09-19 14:26:06 +08001111
Johny Lin8a2118e2018-02-08 16:29:25 +08001112 ALOGV("allocate graphic buffer: %p, id: %d, size: %dx%d", info.mGraphicBlock->handle(),
Johny Lina891f0e2017-12-19 10:25:27 +08001113 info.mBlockId, info.mGraphicBlock->width(), info.mGraphicBlock->height());
Johny Lin52bffe12017-09-19 14:26:06 +08001114
1115 // get offset from data pointers
Lajos Molnara0c6bbd2018-01-03 12:53:15 -08001116 uint32_t offsets[C2PlanarLayout::MAX_NUM_PLANES];
Johny Lin52bffe12017-09-19 14:26:06 +08001117 auto baseAddress = reinterpret_cast<intptr_t>(data[0]);
Lajos Molnara0c6bbd2018-01-03 12:53:15 -08001118 for (uint32_t i = 0; i < layout.numPlanes; ++i) {
Johny Lin52bffe12017-09-19 14:26:06 +08001119 auto planeAddress = reinterpret_cast<intptr_t>(data[i]);
1120 offsets[i] = static_cast<uint32_t>(planeAddress - baseAddress);
1121 }
1122
Johny Linac6e2702018-01-17 12:11:52 +08001123 bool crcb = false;
Johny Lin3a3eb602018-01-03 16:46:13 +08001124 if (layout.numPlanes == 3 &&
1125 offsets[C2PlanarLayout::PLANE_U] > offsets[C2PlanarLayout::PLANE_V]) {
1126 // YCrCb format
1127 std::swap(offsets[C2PlanarLayout::PLANE_U], offsets[C2PlanarLayout::PLANE_V]);
Johny Linac6e2702018-01-17 12:11:52 +08001128 crcb = true;
Johny Lin3a3eb602018-01-03 16:46:13 +08001129 }
1130
Johny Linac6e2702018-01-17 12:11:52 +08001131 bool semiplanar = false;
Johny Lin3a3eb602018-01-03 16:46:13 +08001132 uint32_t passedNumPlanes = layout.numPlanes;
1133 if (layout.planes[C2PlanarLayout::PLANE_U].colInc == 2) { // chroma_step
1134 // Semi-planar format
1135 passedNumPlanes--;
Johny Linac6e2702018-01-17 12:11:52 +08001136 semiplanar = true;
Johny Lin3a3eb602018-01-03 16:46:13 +08001137 }
1138
1139 for (uint32_t i = 0; i < passedNumPlanes; ++i) {
Lajos Molnara0c6bbd2018-01-03 12:53:15 -08001140 ALOGV("plane %u: stride: %d, offset: %u", i, layout.planes[i].rowInc, offsets[i]);
Johny Lin52bffe12017-09-19 14:26:06 +08001141 }
Hirokazu Hondaca17a062018-07-31 10:48:56 +09001142 info.mPixelFormat = resolveBufferFormat(crcb, semiplanar);
Johny Linac6e2702018-01-17 12:11:52 +08001143 ALOGV("HAL pixel format: 0x%x", static_cast<uint32_t>(info.mPixelFormat));
1144
Pin-chih Lin448c0c72018-05-22 14:17:53 +08001145 ::base::ScopedFD passedHandle(dup(info.mGraphicBlock->handle()->data[0]));
Johny Lin52bffe12017-09-19 14:26:06 +08001146 if (!passedHandle.is_valid()) {
1147 ALOGE("Failed to dup(%d), errno=%d", info.mGraphicBlock->handle()->data[0], errno);
1148 reportError(C2_CORRUPTED);
1149 return;
1150 }
1151 std::vector<VideoFramePlane> passedPlanes;
Johny Lin3a3eb602018-01-03 16:46:13 +08001152 for (uint32_t i = 0; i < passedNumPlanes; ++i) {
Lajos Molnara0c6bbd2018-01-03 12:53:15 -08001153 CHECK_GT(layout.planes[i].rowInc, 0);
1154 passedPlanes.push_back({offsets[i], static_cast<uint32_t>(layout.planes[i].rowInc)});
Johny Lin52bffe12017-09-19 14:26:06 +08001155 }
1156 info.mHandle = std::move(passedHandle);
1157 info.mPlanes = std::move(passedPlanes);
1158
1159 mGraphicBlocks.push_back(std::move(info));
1160}
1161
Hirokazu Honda3f3c8a42018-05-09 22:25:19 +08001162void C2VDAComponent::appendSecureOutputBuffer(std::shared_ptr<C2GraphicBlock> block,
1163 uint32_t poolId) {
1164#ifdef V4L2_CODEC2_ARC
1165 const C2Handle* const handle = block->handle();
1166 const int handleFd = handle->data[0];
1167 ::base::ScopedFD passedHandle(dup(handleFd));
1168 if (!passedHandle.is_valid()) {
1169 ALOGE("Failed to dup(%d), errno=%d", handleFd, errno);
1170 reportError(C2_CORRUPTED);
1171 return;
1172 }
1173
Hirokazu Hondaca17a062018-07-31 10:48:56 +09001174 android::HalPixelFormat pixelFormat = getPlatformPixelFormat();
1175 if (pixelFormat == android::HalPixelFormat::UNKNOWN) {
1176 ALOGE("Failed to get pixel format on platform.");
1177 reportError(C2_CORRUPTED);
1178 return;
1179 }
1180 CHECK(pixelFormat == android::HalPixelFormat::YV12 ||
1181 pixelFormat == android::HalPixelFormat::NV12);
Hirokazu Honda3f3c8a42018-05-09 22:25:19 +08001182 ALOGV("HAL pixel format: 0x%x", static_cast<uint32_t>(pixelFormat));
1183
1184 GraphicBlockInfo info;
1185 info.mBlockId = static_cast<int32_t>(mGraphicBlocks.size());
1186 info.mGraphicBlock = std::move(block);
1187 info.mPoolId = poolId;
1188 info.mHandle = std::move(passedHandle);
1189 info.mPixelFormat = pixelFormat;
1190 // In secure mode, since planes are not referred in Chrome side, empty plane is valid.
1191 info.mPlanes.clear();
1192 mGraphicBlocks.push_back(std::move(info));
1193#else
1194 ALOGE("appendSecureOutputBuffer() is not supported...");
1195 reportError(C2_OMITTED);
1196#endif // V4L2_CODEC2_ARC
1197}
1198
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +08001199void C2VDAComponent::sendOutputBufferToAccelerator(GraphicBlockInfo* info, bool ownByAccelerator) {
1200 DCHECK(mTaskRunner->BelongsToCurrentThread());
1201 ALOGV("sendOutputBufferToAccelerator index=%d ownByAccelerator=%d", info->mBlockId,
1202 ownByAccelerator);
1203
1204 if (ownByAccelerator) {
1205 CHECK_EQ(info->mState, GraphicBlockInfo::State::OWNED_BY_COMPONENT);
1206 info->mState = GraphicBlockInfo::State::OWNED_BY_ACCELERATOR;
1207 }
Johny Lin52bffe12017-09-19 14:26:06 +08001208
1209 // is_valid() is true for the first time the buffer is passed to VDA. In that case, VDA needs to
1210 // import the buffer first.
1211 if (info->mHandle.is_valid()) {
Johny Linac6e2702018-01-17 12:11:52 +08001212 mVDAAdaptor->importBufferForPicture(info->mBlockId, info->mPixelFormat,
1213 info->mHandle.release(), info->mPlanes);
Johny Lin52bffe12017-09-19 14:26:06 +08001214 } else {
1215 mVDAAdaptor->reusePictureBuffer(info->mBlockId);
1216 }
1217}
1218
Pin-chih Lin3aff30b2018-10-02 21:11:01 +08001219bool C2VDAComponent::parseCodedColorAspects(const C2ConstLinearBlock& input) {
1220 C2ReadView view = input.map().get();
1221 const uint8_t* data = view.data();
1222 const uint32_t size = view.capacity();
1223
1224 std::unique_ptr<media::H264Parser> h264Parser = std::make_unique<media::H264Parser>();
1225 h264Parser->SetStream(data, static_cast<off_t>(size));
1226 media::H264NALU nalu;
1227 media::H264Parser::Result parRes = h264Parser->AdvanceToNextNALU(&nalu);
1228 if (parRes != media::H264Parser::kEOStream && parRes != media::H264Parser::kOk) {
1229 ALOGE("H264 AdvanceToNextNALU error: %d", static_cast<int>(parRes));
1230 return false;
1231 }
1232 if (nalu.nal_unit_type != media::H264NALU::kSPS) {
1233 ALOGV("NALU is not SPS");
1234 return false;
1235 }
1236
1237 int spsId;
1238 parRes = h264Parser->ParseSPS(&spsId);
1239 if (parRes != media::H264Parser::kEOStream && parRes != media::H264Parser::kOk) {
1240 ALOGE("H264 ParseSPS error: %d", static_cast<int>(parRes));
1241 return false;
1242 }
1243
1244 // Parse ISO color aspects from H264 SPS bitstream.
1245 const media::H264SPS* sps = h264Parser->GetSPS(spsId);
1246 if (!sps->colour_description_present_flag) {
1247 ALOGV("No Color Description in SPS");
1248 return false;
1249 }
1250 int32_t primaries = sps->colour_primaries;
1251 int32_t transfer = sps->transfer_characteristics;
1252 int32_t coeffs = sps->matrix_coefficients;
1253 bool fullRange = sps->video_full_range_flag;
1254
1255 // Convert ISO color aspects to ColorUtils::ColorAspects.
1256 ColorAspects colorAspects;
1257 ColorUtils::convertIsoColorAspectsToCodecAspects(primaries, transfer, coeffs, fullRange,
1258 colorAspects);
1259 ALOGV("Parsed ColorAspects from bitstream: (R:%d, P:%d, M:%d, T:%d)", colorAspects.mRange,
1260 colorAspects.mPrimaries, colorAspects.mMatrixCoeffs, colorAspects.mTransfer);
1261
1262 // Map ColorUtils::ColorAspects to C2StreamColorAspectsInfo::input parameter.
1263 C2StreamColorAspectsInfo::input codedAspects = {0u};
1264 if (!C2Mapper::map(colorAspects.mPrimaries, &codedAspects.primaries)) {
1265 codedAspects.primaries = C2Color::PRIMARIES_UNSPECIFIED;
1266 }
1267 if (!C2Mapper::map(colorAspects.mRange, &codedAspects.range)) {
1268 codedAspects.range = C2Color::RANGE_UNSPECIFIED;
1269 }
1270 if (!C2Mapper::map(colorAspects.mMatrixCoeffs, &codedAspects.matrix)) {
1271 codedAspects.matrix = C2Color::MATRIX_UNSPECIFIED;
1272 }
1273 if (!C2Mapper::map(colorAspects.mTransfer, &codedAspects.transfer)) {
1274 codedAspects.transfer = C2Color::TRANSFER_UNSPECIFIED;
1275 }
1276 // Configure to interface.
1277 std::vector<std::unique_ptr<C2SettingResult>> failures;
1278 c2_status_t status = mIntfImpl->config({&codedAspects}, C2_MAY_BLOCK, &failures);
1279 if (status != C2_OK) {
1280 ALOGE("Failed to config color aspects to interface, error: %d", status);
1281 return false;
1282 }
1283 return true;
1284}
1285
1286c2_status_t C2VDAComponent::updateColorAspects() {
1287 ALOGV("updateColorAspects");
1288 std::unique_ptr<C2StreamColorAspectsInfo::output> colorAspects =
1289 std::make_unique<C2StreamColorAspectsInfo::output>(
1290 0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
1291 C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED);
1292 c2_status_t status = mIntfImpl->query({colorAspects.get()}, {}, C2_DONT_BLOCK, nullptr);
1293 if (status != C2_OK) {
1294 ALOGE("Failed to query color aspects, error: %d", status);
1295 return status;
1296 }
1297 mCurrentColorAspects = std::move(colorAspects);
1298 return C2_OK;
1299}
1300
Johny Lin52bffe12017-09-19 14:26:06 +08001301void C2VDAComponent::onVisibleRectChanged(const media::Rect& cropRect) {
1302 DCHECK(mTaskRunner->BelongsToCurrentThread());
1303 ALOGV("onVisibleRectChanged");
1304 EXPECT_RUNNING_OR_RETURN_ON_ERROR();
1305
1306 // We should make sure there is no pending output format change. That is, the input cropRect is
1307 // corresponding to current output format.
1308 CHECK(mPendingOutputFormat == nullptr);
1309 setOutputFormatCrop(cropRect);
1310}
1311
1312void C2VDAComponent::setOutputFormatCrop(const media::Rect& cropRect) {
1313 ALOGV("setOutputFormatCrop(%dx%d)", cropRect.width(), cropRect.height());
Pin-chih Lin8c974202018-04-12 16:47:41 +08001314 // This visible rect should be set as crop window for each C2ConstGraphicBlock passed to
1315 // framework.
Johny Lin52bffe12017-09-19 14:26:06 +08001316 mOutputFormat.mVisibleRect = cropRect;
Lajos Molnarb78444e2017-11-27 16:08:31 -08001317}
1318
Pin-chih Lin02faf852018-10-24 14:20:53 +08001319void C2VDAComponent::onSurfaceChanged() {
1320 DCHECK(mTaskRunner->BelongsToCurrentThread());
1321 ALOGV("onSurfaceChanged");
1322
1323 if (mComponentState == ComponentState::UNINITIALIZED) {
1324 return; // Component is already stopped, no need to update graphic blocks.
1325 }
1326
1327 stopDequeueThread();
1328
1329 // Get block pool ID configured from the client.
1330 std::shared_ptr<C2BlockPool> blockPool;
1331 auto blockPoolId = mIntfImpl->getBlockPoolId();
1332 ALOGI("Retrieving C2BlockPool ID = %" PRIu64 " for updating output buffers", blockPoolId);
1333 auto err = GetCodec2BlockPool(blockPoolId, shared_from_this(), &blockPool);
1334 if (err != C2_OK) {
1335 ALOGE("Graphic block allocator is invalid");
1336 reportError(err);
1337 return;
1338 }
1339 if (blockPool->getAllocatorId() != C2PlatformAllocatorStore::BUFFERQUEUE) {
1340 ALOGE("Only Bufferqueue-backed block pool would need to change surface.");
1341 reportError(C2_CORRUPTED);
1342 return;
1343 }
1344
1345 std::shared_ptr<C2VdaBqBlockPool> bqPool =
1346 std::static_pointer_cast<C2VdaBqBlockPool>(blockPool);
1347 if (!bqPool) {
1348 ALOGE("static_pointer_cast C2VdaBqBlockPool failed...");
1349 reportError(C2_CORRUPTED);
1350 return;
1351 }
1352
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +08001353 size_t minBuffersForDisplay = 0;
1354 err = bqPool->getMinBuffersForDisplay(&minBuffersForDisplay);
1355 if (err != C2_OK) {
1356 ALOGE("failed to query minimum undequeued buffer count from block pool: %d", err);
1357 reportError(err);
1358 return;
1359 }
1360 ALOGV("Minimum undequeued buffer count = %zu", minBuffersForDisplay);
1361 mUndequeuedBlockIds.resize(minBuffersForDisplay, -1);
1362
Pin-chih Lin02faf852018-10-24 14:20:53 +08001363 for (auto& info : mGraphicBlocks) {
1364 bool willCancel = (info.mGraphicBlock == nullptr);
1365 uint32_t oldSlot = info.mPoolId;
1366 ALOGV("Updating graphic block #%d: slot = %u, willCancel = %d", info.mBlockId, oldSlot,
1367 willCancel);
1368 uint32_t newSlot;
1369 std::shared_ptr<C2GraphicBlock> block;
1370 err = bqPool->updateGraphicBlock(willCancel, oldSlot, &newSlot, &block);
1371 if (err == C2_CANCELED) {
1372 // There may be a chance that a task in task runner before onSurfaceChange triggers
1373 // output format change. If so, block pool will return C2_CANCELED and no need to
1374 // updateGraphicBlock anymore.
1375 return;
1376 }
1377 if (err != C2_OK) {
1378 ALOGE("failed to update graphic block from block pool: %d", err);
1379 reportError(err);
1380 return;
1381 }
1382
1383 // Update slot index.
1384 info.mPoolId = newSlot;
1385 // Update C2GraphicBlock if |willCancel| is false. Note that although the old C2GraphicBlock
1386 // will be released, the block pool data destructor won't do detachBuffer to new surface
1387 // because the producer ID is not matched.
1388 if (!willCancel) {
1389 info.mGraphicBlock = std::move(block);
1390 }
1391 }
1392
1393 if (!startDequeueThread(mOutputFormat.mCodedSize,
1394 static_cast<uint32_t>(mOutputFormat.mPixelFormat), std::move(blockPool),
1395 false /* resetBuffersInClient */)) {
1396 reportError(C2_CORRUPTED);
1397 }
1398}
1399
Lajos Molnarb78444e2017-11-27 16:08:31 -08001400c2_status_t C2VDAComponent::queue_nb(std::list<std::unique_ptr<C2Work>>* const items) {
Pin-chih Lin2a00c242018-03-28 20:41:50 +08001401 if (mState.load() != State::RUNNING) {
Johny Lin52bffe12017-09-19 14:26:06 +08001402 return C2_BAD_STATE;
1403 }
1404 while (!items->empty()) {
Johny Lina891f0e2017-12-19 10:25:27 +08001405 mTaskRunner->PostTask(FROM_HERE,
Pin-chih Lin448c0c72018-05-22 14:17:53 +08001406 ::base::Bind(&C2VDAComponent::onQueueWork, ::base::Unretained(this),
1407 ::base::Passed(&items->front())));
Johny Lin52bffe12017-09-19 14:26:06 +08001408 items->pop_front();
1409 }
1410 return C2_OK;
Johny Lin7c4cb522017-06-26 16:10:24 +08001411}
1412
Lajos Molnarb78444e2017-11-27 16:08:31 -08001413c2_status_t C2VDAComponent::announce_nb(const std::vector<C2WorkOutline>& items) {
Johny Lin7c4cb522017-06-26 16:10:24 +08001414 UNUSED(items);
Lajos Molnarc81d2322017-11-15 16:23:43 -08001415 return C2_OMITTED; // Tunneling is not supported by now
Johny Lin7c4cb522017-06-26 16:10:24 +08001416}
1417
Johny Lina891f0e2017-12-19 10:25:27 +08001418c2_status_t C2VDAComponent::flush_sm(flush_mode_t mode,
1419 std::list<std::unique_ptr<C2Work>>* const flushedWork) {
Johny Lin52bffe12017-09-19 14:26:06 +08001420 if (mode != FLUSH_COMPONENT) {
Lajos Molnarc81d2322017-11-15 16:23:43 -08001421 return C2_OMITTED; // Tunneling is not supported by now
Johny Lin52bffe12017-09-19 14:26:06 +08001422 }
Pin-chih Lin2a00c242018-03-28 20:41:50 +08001423 if (mState.load() != State::RUNNING) {
Johny Lin52bffe12017-09-19 14:26:06 +08001424 return C2_BAD_STATE;
1425 }
Pin-chih Lin448c0c72018-05-22 14:17:53 +08001426 mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onFlush,
1427 ::base::Unretained(this)));
Johny Lin52bffe12017-09-19 14:26:06 +08001428 // Instead of |flushedWork|, abandoned works will be returned via onWorkDone_nb() callback.
1429 return C2_OK;
Johny Lin7c4cb522017-06-26 16:10:24 +08001430}
1431
Lajos Molnarb78444e2017-11-27 16:08:31 -08001432c2_status_t C2VDAComponent::drain_nb(drain_mode_t mode) {
Johny Lin3760d5f2018-01-31 14:40:13 +08001433 if (mode != DRAIN_COMPONENT_WITH_EOS && mode != DRAIN_COMPONENT_NO_EOS) {
Lajos Molnarc81d2322017-11-15 16:23:43 -08001434 return C2_OMITTED; // Tunneling is not supported by now
Johny Lin52bffe12017-09-19 14:26:06 +08001435 }
Pin-chih Lin2a00c242018-03-28 20:41:50 +08001436 if (mState.load() != State::RUNNING) {
Johny Lin52bffe12017-09-19 14:26:06 +08001437 return C2_BAD_STATE;
1438 }
Pin-chih Lin448c0c72018-05-22 14:17:53 +08001439 mTaskRunner->PostTask(FROM_HERE,
1440 ::base::Bind(&C2VDAComponent::onDrain, ::base::Unretained(this),
1441 static_cast<uint32_t>(mode)));
Johny Lin52bffe12017-09-19 14:26:06 +08001442 return C2_OK;
Johny Lin7c4cb522017-06-26 16:10:24 +08001443}
1444
Lajos Molnarb78444e2017-11-27 16:08:31 -08001445c2_status_t C2VDAComponent::start() {
Pin-chih Lin2a00c242018-03-28 20:41:50 +08001446 // Use mStartStopLock to block other asynchronously start/stop calls.
1447 std::lock_guard<std::mutex> lock(mStartStopLock);
1448
1449 if (mState.load() != State::LOADED) {
Johny Lin744cff12017-08-16 16:56:25 +08001450 return C2_BAD_STATE; // start() is only supported when component is in LOADED state.
1451 }
1452
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +08001453 mCodecProfile = mIntfImpl->getCodecProfile();
1454 ALOGI("get parameter: mCodecProfile = %d", static_cast<int>(mCodecProfile));
1455
Pin-chih Lin448c0c72018-05-22 14:17:53 +08001456 ::base::WaitableEvent done(::base::WaitableEvent::ResetPolicy::AUTOMATIC,
1457 ::base::WaitableEvent::InitialState::NOT_SIGNALED);
1458 mTaskRunner->PostTask(FROM_HERE,
1459 ::base::Bind(&C2VDAComponent::onStart, ::base::Unretained(this),
1460 mCodecProfile, &done));
Johny Lin744cff12017-08-16 16:56:25 +08001461 done.Wait();
Pin-chih Lindb84a6c2018-09-13 21:38:50 +08001462 c2_status_t c2Status = adaptorResultToC2Status(mVDAInitResult);
1463 if (c2Status != C2_OK) {
1464 ALOGE("Failed to start component due to VDA error...");
1465 return c2Status;
Johny Lin744cff12017-08-16 16:56:25 +08001466 }
Pin-chih Lin2a00c242018-03-28 20:41:50 +08001467 mState.store(State::RUNNING);
Johny Lin744cff12017-08-16 16:56:25 +08001468 return C2_OK;
Johny Lin7c4cb522017-06-26 16:10:24 +08001469}
1470
Lajos Molnarb78444e2017-11-27 16:08:31 -08001471c2_status_t C2VDAComponent::stop() {
Pin-chih Lin2a00c242018-03-28 20:41:50 +08001472 // Use mStartStopLock to block other asynchronously start/stop calls.
1473 std::lock_guard<std::mutex> lock(mStartStopLock);
1474
1475 auto state = mState.load();
1476 if (!(state == State::RUNNING || state == State::ERROR)) {
1477 return C2_OK; // Component is already in stopped state.
Johny Lin744cff12017-08-16 16:56:25 +08001478 }
1479
Pin-chih Lin448c0c72018-05-22 14:17:53 +08001480 ::base::WaitableEvent done(::base::WaitableEvent::ResetPolicy::AUTOMATIC,
1481 ::base::WaitableEvent::InitialState::NOT_SIGNALED);
Johny Lin744cff12017-08-16 16:56:25 +08001482 mTaskRunner->PostTask(FROM_HERE,
Pin-chih Lin448c0c72018-05-22 14:17:53 +08001483 ::base::Bind(&C2VDAComponent::onStop, ::base::Unretained(this), &done));
Johny Lin744cff12017-08-16 16:56:25 +08001484 done.Wait();
Pin-chih Lin2a00c242018-03-28 20:41:50 +08001485 mState.store(State::LOADED);
Johny Lin744cff12017-08-16 16:56:25 +08001486 return C2_OK;
Johny Lin7c4cb522017-06-26 16:10:24 +08001487}
1488
Lajos Molnar2737fb62017-12-19 09:46:42 -08001489c2_status_t C2VDAComponent::reset() {
Johny Lina891f0e2017-12-19 10:25:27 +08001490 return stop();
Johny Lin52bffe12017-09-19 14:26:06 +08001491 // TODO(johnylin): reset is different than stop that it could be called in any state.
1492 // TODO(johnylin): when reset is called, set ComponentInterface to default values.
Johny Lin7c4cb522017-06-26 16:10:24 +08001493}
1494
Lajos Molnar2737fb62017-12-19 09:46:42 -08001495c2_status_t C2VDAComponent::release() {
Pin-chih Lin2a00c242018-03-28 20:41:50 +08001496 return reset();
Johny Lin7c4cb522017-06-26 16:10:24 +08001497}
1498
1499std::shared_ptr<C2ComponentInterface> C2VDAComponent::intf() {
1500 return mIntf;
1501}
1502
Johny Linac6e2702018-01-17 12:11:52 +08001503void C2VDAComponent::providePictureBuffers(uint32_t minNumBuffers, const media::Size& codedSize) {
1504 // Always use fexible pixel 420 format YCbCr_420_888 in Android.
Johny Lin52bffe12017-09-19 14:26:06 +08001505 // Uses coded size for crop rect while it is not available.
Johny Linac6e2702018-01-17 12:11:52 +08001506 auto format = std::make_unique<VideoFormat>(HalPixelFormat::YCbCr_420_888, minNumBuffers,
1507 codedSize, media::Rect(codedSize));
Johny Lin52bffe12017-09-19 14:26:06 +08001508
1509 // Set mRequestedVisibleRect to default.
1510 mRequestedVisibleRect = media::Rect();
1511
Pin-chih Lin448c0c72018-05-22 14:17:53 +08001512 mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onOutputFormatChanged,
1513 ::base::Unretained(this),
1514 ::base::Passed(&format)));
Johny Lin7c4cb522017-06-26 16:10:24 +08001515}
1516
Johny Lin744cff12017-08-16 16:56:25 +08001517void C2VDAComponent::dismissPictureBuffer(int32_t pictureBufferId) {
1518 UNUSED(pictureBufferId);
Johny Lin52bffe12017-09-19 14:26:06 +08001519 // no ops
Johny Lin7c4cb522017-06-26 16:10:24 +08001520}
1521
Johny Lina891f0e2017-12-19 10:25:27 +08001522void C2VDAComponent::pictureReady(int32_t pictureBufferId, int32_t bitstreamId,
1523 const media::Rect& cropRect) {
Johny Lin744cff12017-08-16 16:56:25 +08001524 UNUSED(pictureBufferId);
1525 UNUSED(bitstreamId);
Johny Lin52bffe12017-09-19 14:26:06 +08001526
1527 if (mRequestedVisibleRect != cropRect) {
1528 mRequestedVisibleRect = cropRect;
Pin-chih Lin448c0c72018-05-22 14:17:53 +08001529 mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onVisibleRectChanged,
1530 ::base::Unretained(this), cropRect));
Johny Lin52bffe12017-09-19 14:26:06 +08001531 }
1532
Pin-chih Lin448c0c72018-05-22 14:17:53 +08001533 mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onOutputBufferDone,
1534 ::base::Unretained(this),
1535 pictureBufferId, bitstreamId));
Johny Lin7c4cb522017-06-26 16:10:24 +08001536}
1537
Johny Lin744cff12017-08-16 16:56:25 +08001538void C2VDAComponent::notifyEndOfBitstreamBuffer(int32_t bitstreamId) {
Pin-chih Lin448c0c72018-05-22 14:17:53 +08001539 mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onInputBufferDone,
1540 ::base::Unretained(this), bitstreamId));
Johny Lin7c4cb522017-06-26 16:10:24 +08001541}
1542
1543void C2VDAComponent::notifyFlushDone() {
Johny Lin52bffe12017-09-19 14:26:06 +08001544 mTaskRunner->PostTask(FROM_HERE,
Pin-chih Lin448c0c72018-05-22 14:17:53 +08001545 ::base::Bind(&C2VDAComponent::onDrainDone, ::base::Unretained(this)));
Johny Lin7c4cb522017-06-26 16:10:24 +08001546}
1547
1548void C2VDAComponent::notifyResetDone() {
Johny Lin744cff12017-08-16 16:56:25 +08001549 mTaskRunner->PostTask(FROM_HERE,
Pin-chih Lin448c0c72018-05-22 14:17:53 +08001550 ::base::Bind(&C2VDAComponent::onResetDone, ::base::Unretained(this)));
Johny Lin7c4cb522017-06-26 16:10:24 +08001551}
1552
1553void C2VDAComponent::notifyError(VideoDecodeAcceleratorAdaptor::Result error) {
Pin-chih Lindb84a6c2018-09-13 21:38:50 +08001554 ALOGE("Got notifyError from VDA...");
1555 c2_status_t err = adaptorResultToC2Status(error);
1556 if (err == C2_OK) {
1557 ALOGW("Shouldn't get SUCCESS err code in NotifyError(). Skip it...");
Johny Lin52bffe12017-09-19 14:26:06 +08001558 return;
1559 }
1560 reportError(err);
1561}
1562
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +08001563void C2VDAComponent::reportWorkIfFinished(int32_t bitstreamId) {
Johny Lin52bffe12017-09-19 14:26:06 +08001564 DCHECK(mTaskRunner->BelongsToCurrentThread());
Johny Lin52bffe12017-09-19 14:26:06 +08001565
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +08001566 auto workIter = findPendingWorkByBitstreamId(bitstreamId);
1567 if (workIter == mPendingWorks.end()) {
1568 reportError(C2_CORRUPTED);
1569 return;
Johny Lin52bffe12017-09-19 14:26:06 +08001570 }
1571
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +08001572 // EOS work will not be reported here. reportEOSWork() does it.
1573 auto work = workIter->get();
1574 if (isWorkDone(work)) {
1575 if (work->worklets.front()->output.flags & C2FrameData::FLAG_DROP_FRAME) {
1576 // TODO: actually framework does not handle FLAG_DROP_FRAME, use C2_NOT_FOUND result to
1577 // let framework treat this as flushed work.
1578 work->result = C2_NOT_FOUND;
1579 } else {
1580 work->result = C2_OK;
1581 }
1582 work->workletsProcessed = static_cast<uint32_t>(work->worklets.size());
1583
1584 ALOGV("Reported finished work index=%llu", work->input.ordinal.frameIndex.peekull());
1585 std::list<std::unique_ptr<C2Work>> finishedWorks;
1586 finishedWorks.emplace_back(std::move(*workIter));
Johny Lin52bffe12017-09-19 14:26:06 +08001587 mListener->onWorkDone_nb(shared_from_this(), std::move(finishedWorks));
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +08001588 mPendingWorks.erase(workIter);
Johny Lin52bffe12017-09-19 14:26:06 +08001589 }
1590}
1591
1592bool C2VDAComponent::isWorkDone(const C2Work* work) const {
Pin-chih Lin3fe0c8d2018-09-11 12:17:53 +08001593 if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
1594 // This is EOS work and should be processed by reportEOSWork().
Pin-chih Linca72eff2018-06-12 11:22:04 +08001595 return false;
1596 }
Pin-chih Lincd56fe72018-05-04 10:29:23 +08001597 if (work->input.buffers.front()) {
Johny Lin3760d5f2018-01-31 14:40:13 +08001598 // Input buffer is still owned by VDA.
Johny Lin3760d5f2018-01-31 14:40:13 +08001599 return false;
1600 }
Pin-chih Lin617d7632018-05-30 16:48:07 +08001601 if (mPendingOutputEOS && mPendingWorks.size() == 1u) {
1602 // If mPendingOutputEOS is true, the last returned work should be marked EOS flag and
1603 // returned by reportEOSWork() instead.
Johny Lin3760d5f2018-01-31 14:40:13 +08001604 return false;
Johny Lin52bffe12017-09-19 14:26:06 +08001605 }
Pin-chih Lin41db9632018-09-11 15:41:39 +08001606 if (!(work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) &&
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +08001607 !(work->worklets.front()->output.flags & C2FrameData::FLAG_DROP_FRAME) &&
Pin-chih Lin41db9632018-09-11 15:41:39 +08001608 work->worklets.front()->output.buffers.empty()) {
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +08001609 // Unless the input is CSD or the output is dropped, this work is not done because the
1610 // output buffer is not returned from VDA yet.
Pin-chih Lin41db9632018-09-11 15:41:39 +08001611 return false;
Johny Lin52bffe12017-09-19 14:26:06 +08001612 }
Pin-chih Lin2dcfa1a2018-11-28 14:25:09 +08001613 return true; // This work is done.
Johny Lin52bffe12017-09-19 14:26:06 +08001614}
1615
Johny Lin3760d5f2018-01-31 14:40:13 +08001616void C2VDAComponent::reportEOSWork() {
1617 ALOGV("reportEOSWork");
1618 DCHECK(mTaskRunner->BelongsToCurrentThread());
1619 // In this moment all works prior to EOS work should be done and returned to listener.
1620 if (mPendingWorks.size() != 1u) { // only EOS work left
1621 ALOGE("It shouldn't have remaining works in mPendingWorks except EOS work.");
1622 reportError(C2_CORRUPTED);
1623 return;
1624 }
1625
Pin-chih Lin617d7632018-05-30 16:48:07 +08001626 mPendingOutputEOS = false;
1627
Johny Lin3760d5f2018-01-31 14:40:13 +08001628 std::unique_ptr<C2Work> eosWork(std::move(mPendingWorks.front()));
1629 mPendingWorks.pop_front();
Pin-chih Linca72eff2018-06-12 11:22:04 +08001630 if (!eosWork->input.buffers.empty()) {
1631 eosWork->input.buffers.front().reset();
1632 }
Johny Lin3760d5f2018-01-31 14:40:13 +08001633 eosWork->result = C2_OK;
1634 eosWork->workletsProcessed = static_cast<uint32_t>(eosWork->worklets.size());
1635 eosWork->worklets.front()->output.flags = C2FrameData::FLAG_END_OF_STREAM;
1636
1637 std::list<std::unique_ptr<C2Work>> finishedWorks;
1638 finishedWorks.emplace_back(std::move(eosWork));
1639 mListener->onWorkDone_nb(shared_from_this(), std::move(finishedWorks));
1640}
1641
Johny Lin52bffe12017-09-19 14:26:06 +08001642void C2VDAComponent::reportAbandonedWorks() {
1643 DCHECK(mTaskRunner->BelongsToCurrentThread());
Lajos Molnarfa7abe52018-01-25 21:48:00 -08001644 std::list<std::unique_ptr<C2Work>> abandonedWorks;
Johny Lin52bffe12017-09-19 14:26:06 +08001645
1646 while (!mPendingWorks.empty()) {
1647 std::unique_ptr<C2Work> work(std::move(mPendingWorks.front()));
1648 mPendingWorks.pop_front();
1649
Pin-chih Lin388dba42018-03-20 21:06:39 +08001650 // TODO: correlate the definition of flushed work result to framework.
1651 work->result = C2_NOT_FOUND;
Pin-chih Linca72eff2018-06-12 11:22:04 +08001652 // When the work is abandoned, buffer in input.buffers shall reset by component.
1653 if (!work->input.buffers.empty()) {
1654 work->input.buffers.front().reset();
1655 }
Johny Lin52bffe12017-09-19 14:26:06 +08001656 abandonedWorks.emplace_back(std::move(work));
1657 }
1658
Pin-chih Lin617d7632018-05-30 16:48:07 +08001659 for (auto& work : mAbandonedWorks) {
1660 // TODO: correlate the definition of flushed work result to framework.
1661 work->result = C2_NOT_FOUND;
Pin-chih Linca72eff2018-06-12 11:22:04 +08001662 // When the work is abandoned, buffer in input.buffers shall reset by component.
1663 if (!work->input.buffers.empty()) {
1664 work->input.buffers.front().reset();
1665 }
Pin-chih Lin617d7632018-05-30 16:48:07 +08001666 abandonedWorks.emplace_back(std::move(work));
1667 }
1668 mAbandonedWorks.clear();
1669
1670 // Pending EOS work will be abandoned here due to component flush if any.
1671 mPendingOutputEOS = false;
1672
Johny Lin52bffe12017-09-19 14:26:06 +08001673 if (!abandonedWorks.empty()) {
1674 mListener->onWorkDone_nb(shared_from_this(), std::move(abandonedWorks));
1675 }
1676}
1677
1678void C2VDAComponent::reportError(c2_status_t error) {
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +08001679 mListener->onError_nb(shared_from_this(), static_cast<uint32_t>(error));
Johny Lin7c4cb522017-06-26 16:10:24 +08001680}
1681
Pin-chih Lin62585f02018-05-29 21:00:03 +08001682bool C2VDAComponent::startDequeueThread(const media::Size& size, uint32_t pixelFormat,
Pin-chih Lin02faf852018-10-24 14:20:53 +08001683 std::shared_ptr<C2BlockPool> blockPool,
1684 bool resetBuffersInClient) {
Pin-chih Lin906956a2018-05-22 14:12:59 +08001685 CHECK(!mDequeueThread.IsRunning());
1686 if (!mDequeueThread.Start()) {
1687 ALOGE("failed to start dequeue thread!!");
1688 return false;
1689 }
1690 mDequeueLoopStop.store(false);
Pin-chih Lin02faf852018-10-24 14:20:53 +08001691 if (resetBuffersInClient) {
1692 mBuffersInClient.store(0u);
1693 }
Pin-chih Lin906956a2018-05-22 14:12:59 +08001694 mDequeueThread.task_runner()->PostTask(
Pin-chih Lin448c0c72018-05-22 14:17:53 +08001695 FROM_HERE, ::base::Bind(&C2VDAComponent::dequeueThreadLoop, ::base::Unretained(this),
Pin-chih Lin62585f02018-05-29 21:00:03 +08001696 size, pixelFormat, std::move(blockPool)));
Pin-chih Lin906956a2018-05-22 14:12:59 +08001697 return true;
1698}
1699
1700void C2VDAComponent::stopDequeueThread() {
1701 if (mDequeueThread.IsRunning()) {
1702 mDequeueLoopStop.store(true);
1703 mDequeueThread.Stop();
1704 }
1705}
1706
Pin-chih Lin62585f02018-05-29 21:00:03 +08001707void C2VDAComponent::dequeueThreadLoop(const media::Size& size, uint32_t pixelFormat,
1708 std::shared_ptr<C2BlockPool> blockPool) {
Pin-chih Lin906956a2018-05-22 14:12:59 +08001709 ALOGV("dequeueThreadLoop starts");
1710 DCHECK(mDequeueThread.task_runner()->BelongsToCurrentThread());
1711
1712 while (!mDequeueLoopStop.load()) {
1713 if (mBuffersInClient.load() == 0) {
1714 ::usleep(kDequeueRetryDelayUs); // wait for retry
1715 continue;
1716 }
1717 std::shared_ptr<C2GraphicBlock> block;
Hirokazu Honda3f3c8a42018-05-09 22:25:19 +08001718 C2MemoryUsage usage = {
1719 mSecureMode ? C2MemoryUsage::READ_PROTECTED : C2MemoryUsage::CPU_READ, 0};
Pin-chih Lin62585f02018-05-29 21:00:03 +08001720 auto err = blockPool->fetchGraphicBlock(size.width(), size.height(), pixelFormat, usage,
1721 &block);
Pin-chih Lin906956a2018-05-22 14:12:59 +08001722 if (err == C2_TIMED_OUT) {
Pin-chih Lin02faf852018-10-24 14:20:53 +08001723 // Mutexes often do not care for FIFO. Practically the thread who is locking the mutex
1724 // usually will be granted to lock again right thereafter. To make this loop not too
1725 // bossy, the simpliest way is to add a short delay to the next time acquiring the
1726 // lock. TODO (b/118354314): replace this if there is better solution.
1727 ::usleep(1);
Pin-chih Lin906956a2018-05-22 14:12:59 +08001728 continue; // wait for retry
1729 }
Pin-chih Lin02faf852018-10-24 14:20:53 +08001730 if (err == C2_BAD_STATE) {
1731 ALOGV("Got informed from block pool surface is changed.");
1732 mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onSurfaceChanged,
1733 ::base::Unretained(this)));
1734 break; // terminate the loop, will be resumed after onSurfaceChanged().
1735 }
Pin-chih Lin906956a2018-05-22 14:12:59 +08001736 if (err == C2_OK) {
Pin-chih Lin585324e2018-07-25 16:24:54 +08001737 uint32_t poolId;
1738 if (blockPool->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
1739 err = C2VdaBqBlockPool::getPoolIdFromGraphicBlock(block, &poolId);
1740 } else { // bufferpool
1741 err = C2VdaPooledBlockPool::getPoolIdFromGraphicBlock(block, &poolId);
1742 }
1743
1744 if (err != C2_OK) {
1745 ALOGE("dequeueThreadLoop got error on getPoolIdFromGraphicBlock: %d", err);
1746 break;
1747 }
1748 mTaskRunner->PostTask(FROM_HERE,
1749 ::base::Bind(&C2VDAComponent::onOutputBufferReturned,
1750 ::base::Unretained(this), std::move(block), poolId));
Pin-chih Lin906956a2018-05-22 14:12:59 +08001751 mBuffersInClient--;
1752 } else {
1753 ALOGE("dequeueThreadLoop got error: %d", err);
1754 break;
1755 }
1756 }
1757 ALOGV("dequeueThreadLoop terminates");
1758}
1759
Hirokazu Hondadecfca12018-01-23 01:59:09 +09001760class C2VDAComponentFactory : public C2ComponentFactory {
Johny Lin52bffe12017-09-19 14:26:06 +08001761public:
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +08001762 C2VDAComponentFactory(C2String decoderName)
1763 : mDecoderName(decoderName),
1764 mReflector(std::static_pointer_cast<C2ReflectorHelper>(
1765 GetCodec2VDAComponentStore()->getParamReflector())){};
Hirokazu Hondadecfca12018-01-23 01:59:09 +09001766
1767 c2_status_t createComponent(c2_node_id_t id, std::shared_ptr<C2Component>* const component,
1768 ComponentDeleter deleter) override {
1769 UNUSED(deleter);
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +08001770 *component = std::shared_ptr<C2Component>(new C2VDAComponent(mDecoderName, id, mReflector));
Hirokazu Hondadecfca12018-01-23 01:59:09 +09001771 return C2_OK;
Johny Lin52bffe12017-09-19 14:26:06 +08001772 }
Hirokazu Hondadecfca12018-01-23 01:59:09 +09001773 c2_status_t createInterface(c2_node_id_t id,
1774 std::shared_ptr<C2ComponentInterface>* const interface,
1775 InterfaceDeleter deleter) override {
1776 UNUSED(deleter);
1777 *interface =
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +08001778 std::shared_ptr<C2ComponentInterface>(new SimpleInterface<C2VDAComponent::IntfImpl>(
1779 mDecoderName.c_str(), id,
1780 std::make_shared<C2VDAComponent::IntfImpl>(mDecoderName, mReflector)));
Hirokazu Hondadecfca12018-01-23 01:59:09 +09001781 return C2_OK;
1782 }
1783 ~C2VDAComponentFactory() override = default;
1784
1785private:
1786 const C2String mDecoderName;
Pin-chih Lin1ca0d0b2018-05-04 13:58:06 +08001787 std::shared_ptr<C2ReflectorHelper> mReflector;
Johny Lin52bffe12017-09-19 14:26:06 +08001788};
Johny Lin7c4cb522017-06-26 16:10:24 +08001789} // namespace android
1790
Hirokazu Honda3f3c8a42018-05-09 22:25:19 +08001791extern "C" ::C2ComponentFactory* CreateC2VDAH264Factory(bool secureMode) {
1792 ALOGV("in %s (secureMode=%d)", __func__, secureMode);
1793 return secureMode ? new ::android::C2VDAComponentFactory(android::kH264SecureDecoderName)
1794 : new ::android::C2VDAComponentFactory(android::kH264DecoderName);
Johny Lin7c4cb522017-06-26 16:10:24 +08001795}
1796
Lajos Molnar93e4f222018-02-22 17:05:17 -08001797extern "C" void DestroyC2VDAH264Factory(::C2ComponentFactory* factory) {
Hirokazu Hondadecfca12018-01-23 01:59:09 +09001798 ALOGV("in %s", __func__);
1799 delete factory;
1800}
1801
Hirokazu Honda3f3c8a42018-05-09 22:25:19 +08001802extern "C" ::C2ComponentFactory* CreateC2VDAVP8Factory(bool secureMode) {
1803 ALOGV("in %s (secureMode=%d)", __func__, secureMode);
1804 return secureMode ? new ::android::C2VDAComponentFactory(android::kVP8SecureDecoderName)
1805 : new ::android::C2VDAComponentFactory(android::kVP8DecoderName);
Hirokazu Hondadecfca12018-01-23 01:59:09 +09001806}
1807
Lajos Molnar93e4f222018-02-22 17:05:17 -08001808extern "C" void DestroyC2VDAVP8Factory(::C2ComponentFactory* factory) {
Hirokazu Hondadecfca12018-01-23 01:59:09 +09001809 ALOGV("in %s", __func__);
1810 delete factory;
1811}
1812
Hirokazu Honda3f3c8a42018-05-09 22:25:19 +08001813extern "C" ::C2ComponentFactory* CreateC2VDAVP9Factory(bool secureMode) {
1814 ALOGV("in %s (secureMode=%d)", __func__, secureMode);
1815 return secureMode ? new ::android::C2VDAComponentFactory(android::kVP9SecureDecoderName)
1816 : new ::android::C2VDAComponentFactory(android::kVP9DecoderName);
Hirokazu Hondadecfca12018-01-23 01:59:09 +09001817}
1818
Lajos Molnar93e4f222018-02-22 17:05:17 -08001819extern "C" void DestroyC2VDAVP9Factory(::C2ComponentFactory* factory) {
Hirokazu Hondadecfca12018-01-23 01:59:09 +09001820 ALOGV("in %s", __func__);
1821 delete factory;
Johny Lin7c4cb522017-06-26 16:10:24 +08001822}