• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //#define LOG_NDEBUG 0
6 #define LOG_TAG "V4L2EncodeComponent"
7 
8 #include <v4l2_codec2/components/V4L2EncodeComponent.h>
9 
10 #include <inttypes.h>
11 
12 #include <algorithm>
13 #include <utility>
14 
15 #include <C2AllocatorGralloc.h>
16 #include <C2PlatformSupport.h>
17 #include <C2Work.h>
18 #include <android/hardware/graphics/common/1.0/types.h>
19 #include <base/bind.h>
20 #include <base/bind_helpers.h>
21 #include <cutils/properties.h>
22 #include <log/log.h>
23 #include <media/stagefright/MediaDefs.h>
24 #include <ui/GraphicBuffer.h>
25 #include <ui/Size.h>
26 
27 #include <v4l2_codec2/common/Common.h>
28 #include <v4l2_codec2/common/EncodeHelpers.h>
29 #include <v4l2_codec2/common/FormatConverter.h>
30 #include <v4l2_codec2/common/VideoPixelFormat.h>
31 #include <v4l2_codec2/components/BitstreamBuffer.h>
32 #include <v4l2_codec2/components/V4L2EncodeInterface.h>
33 #include <v4l2_codec2/components/V4L2Encoder.h>
34 
35 using android::hardware::graphics::common::V1_0::BufferUsage;
36 
37 namespace android {
38 
39 namespace {
40 
41 const VideoPixelFormat kInputPixelFormat = VideoPixelFormat::NV12;
42 
43 // The peak bitrate in function of the target bitrate, used when the bitrate mode is VBR.
44 constexpr uint32_t kPeakBitrateMultiplier = 2u;
45 
46 // Get the video frame layout from the specified |inputBlock|.
47 // TODO(dstaessens): Clean up code extracting layout from a C2GraphicBlock.
getVideoFrameLayout(const C2ConstGraphicBlock & block,VideoPixelFormat * format)48 std::optional<std::vector<VideoFramePlane>> getVideoFrameLayout(const C2ConstGraphicBlock& block,
49                                                                 VideoPixelFormat* format) {
50     ALOGV("%s()", __func__);
51 
52     // Get the C2PlanarLayout from the graphics block. The C2GraphicView returned by block.map()
53     // needs to be released before calling getGraphicBlockInfo(), or the lockYCbCr() call will block
54     // Indefinitely.
55     C2PlanarLayout layout = block.map().get().layout();
56 
57     // The above layout() cannot fill layout information and memset 0 instead if the input format is
58     // IMPLEMENTATION_DEFINED and its backed format is RGB. We fill the layout by using
59     // ImplDefinedToRGBXMap in the case.
60     if (layout.type == C2PlanarLayout::TYPE_UNKNOWN) {
61         std::unique_ptr<ImplDefinedToRGBXMap> idMap = ImplDefinedToRGBXMap::Create(block);
62         if (idMap == nullptr) {
63             ALOGE("Unable to parse RGBX_8888 from IMPLEMENTATION_DEFINED");
64             return std::nullopt;
65         }
66         layout.type = C2PlanarLayout::TYPE_RGB;
67         // These parameters would be used in TYPE_GRB case below.
68         layout.numPlanes = 3;   // same value as in C2AllocationGralloc::map()
69         layout.rootPlanes = 1;  // same value as in C2AllocationGralloc::map()
70         layout.planes[C2PlanarLayout::PLANE_R].offset = idMap->offset();
71         layout.planes[C2PlanarLayout::PLANE_R].rowInc = idMap->rowInc();
72     }
73 
74     std::vector<uint32_t> offsets(layout.numPlanes, 0u);
75     std::vector<uint32_t> strides(layout.numPlanes, 0u);
76     switch (layout.type) {
77     case C2PlanarLayout::TYPE_YUV: {
78         android_ycbcr ycbcr = getGraphicBlockInfo(block);
79         offsets[C2PlanarLayout::PLANE_Y] =
80                 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ycbcr.y));
81         offsets[C2PlanarLayout::PLANE_U] =
82                 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ycbcr.cb));
83         offsets[C2PlanarLayout::PLANE_V] =
84                 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ycbcr.cr));
85         strides[C2PlanarLayout::PLANE_Y] = static_cast<uint32_t>(ycbcr.ystride);
86         strides[C2PlanarLayout::PLANE_U] = static_cast<uint32_t>(ycbcr.cstride);
87         strides[C2PlanarLayout::PLANE_V] = static_cast<uint32_t>(ycbcr.cstride);
88 
89         bool crcb = false;
90         if (offsets[C2PlanarLayout::PLANE_U] > offsets[C2PlanarLayout::PLANE_V]) {
91             // Swap offsets, no need to swap strides as they are identical for both chroma planes.
92             std::swap(offsets[C2PlanarLayout::PLANE_U], offsets[C2PlanarLayout::PLANE_V]);
93             crcb = true;
94         }
95 
96         bool semiplanar = false;
97         if (ycbcr.chroma_step >
98             offsets[C2PlanarLayout::PLANE_V] - offsets[C2PlanarLayout::PLANE_U]) {
99             semiplanar = true;
100         }
101 
102         if (!crcb && !semiplanar) {
103             *format = VideoPixelFormat::I420;
104         } else if (!crcb && semiplanar) {
105             *format = VideoPixelFormat::NV12;
106         } else if (crcb && !semiplanar) {
107             // HACK: pretend YV12 is I420 now since VEA only accepts I420. (YV12 will be used
108             //       for input byte-buffer mode).
109             // TODO(dstaessens): Is this hack still necessary now we're not using the VEA directly?
110             //format = VideoPixelFormat::YV12;
111             *format = VideoPixelFormat::I420;
112         } else {
113             *format = VideoPixelFormat::NV21;
114         }
115         break;
116     }
117     case C2PlanarLayout::TYPE_RGB: {
118         offsets[C2PlanarLayout::PLANE_R] = layout.planes[C2PlanarLayout::PLANE_R].offset;
119         strides[C2PlanarLayout::PLANE_R] =
120                 static_cast<uint32_t>(layout.planes[C2PlanarLayout::PLANE_R].rowInc);
121         *format = VideoPixelFormat::ARGB;
122         break;
123     }
124     default:
125         ALOGW("Unknown layout type: %u", static_cast<uint32_t>(layout.type));
126         return std::nullopt;
127     }
128 
129     std::vector<VideoFramePlane> planes;
130     for (uint32_t i = 0; i < layout.rootPlanes; ++i) {
131         // The mSize field is not used in our case, so we can safely set it to zero.
132         planes.push_back({strides[i], offsets[i], 0});
133     }
134     return planes;
135 }
136 
137 // Get the video frame stride for the specified |format| and |size|.
getVideoFrameStride(VideoPixelFormat format,ui::Size size)138 std::optional<uint32_t> getVideoFrameStride(VideoPixelFormat format, ui::Size size) {
139     // Fetch a graphic block from the pool to determine the stride.
140     std::shared_ptr<C2BlockPool> pool;
141     c2_status_t status = GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, nullptr, &pool);
142     if (status != C2_OK) {
143         ALOGE("Failed to get basic graphic block pool (err=%d)", status);
144         return std::nullopt;
145     }
146 
147     // Android HAL format doesn't have I420, we use YV12 instead and swap the U and V planes when
148     // converting to NV12. YCBCR_420_888 will allocate NV12 by minigbm.
149     HalPixelFormat halFormat = (format == VideoPixelFormat::I420) ? HalPixelFormat::YV12
150                                                                   : HalPixelFormat::YCBCR_420_888;
151 
152     std::shared_ptr<C2GraphicBlock> block;
153     status = pool->fetchGraphicBlock(size.width, size.height, static_cast<uint32_t>(halFormat),
154                                      C2MemoryUsage(C2MemoryUsage::CPU_READ), &block);
155     if (status != C2_OK) {
156         ALOGE("Failed to fetch graphic block (err=%d)", status);
157         return std::nullopt;
158     }
159 
160     const C2ConstGraphicBlock constBlock = block->share(C2Rect(size.width, size.height), C2Fence());
161     VideoPixelFormat pixelFormat;
162     std::optional<std::vector<VideoFramePlane>> planes =
163             getVideoFrameLayout(constBlock, &pixelFormat);
164     if (!planes || planes.value().empty()) {
165         ALOGE("Failed to get video frame layout from block");
166         return std::nullopt;
167     }
168 
169     return planes.value()[0].mStride;
170 }
171 
172 // Create an input frame from the specified graphic block.
CreateInputFrame(const C2ConstGraphicBlock & block,uint64_t index,int64_t timestamp)173 std::unique_ptr<V4L2Encoder::InputFrame> CreateInputFrame(const C2ConstGraphicBlock& block,
174                                                           uint64_t index, int64_t timestamp) {
175     VideoPixelFormat format;
176     std::optional<std::vector<VideoFramePlane>> planes = getVideoFrameLayout(block, &format);
177     if (!planes) {
178         ALOGE("Failed to get input block's layout");
179         return nullptr;
180     }
181 
182     std::vector<int> fds;
183     const C2Handle* const handle = block.handle();
184     for (int i = 0; i < handle->numFds; i++) {
185         fds.emplace_back(handle->data[i]);
186     }
187 
188     return std::make_unique<V4L2Encoder::InputFrame>(std::move(fds), std::move(planes.value()),
189                                                      format, index, timestamp);
190 }
191 
192 // Check whether the specified |profile| is an H.264 profile.
IsH264Profile(C2Config::profile_t profile)193 bool IsH264Profile(C2Config::profile_t profile) {
194     return (profile >= C2Config::PROFILE_AVC_BASELINE &&
195             profile <= C2Config::PROFILE_AVC_ENHANCED_MULTIVIEW_DEPTH_HIGH);
196 }
197 
198 }  // namespace
199 
200 // static
201 std::atomic<int32_t> V4L2EncodeComponent::sConcurrentInstances = 0;
202 
203 // static
create(C2String name,c2_node_id_t id,std::shared_ptr<C2ReflectorHelper> helper,C2ComponentFactory::ComponentDeleter deleter)204 std::shared_ptr<C2Component> V4L2EncodeComponent::create(
205         C2String name, c2_node_id_t id, std::shared_ptr<C2ReflectorHelper> helper,
206         C2ComponentFactory::ComponentDeleter deleter) {
207     ALOGV("%s(%s)", __func__, name.c_str());
208 
209     static const int32_t kMaxConcurrentInstances =
210             property_get_int32("ro.vendor.v4l2_codec2.encode_concurrent_instances", -1);
211 
212     static std::mutex mutex;
213     std::lock_guard<std::mutex> lock(mutex);
214     if (kMaxConcurrentInstances >= 0 && sConcurrentInstances.load() >= kMaxConcurrentInstances) {
215         ALOGW("Cannot create additional encoder, maximum number of instances reached: %d",
216               kMaxConcurrentInstances);
217         return nullptr;
218     }
219 
220     auto interface = std::make_shared<V4L2EncodeInterface>(name, std::move(helper));
221     if (interface->status() != C2_OK) {
222         ALOGE("Component interface initialization failed (error code %d)", interface->status());
223         return nullptr;
224     }
225 
226     return std::shared_ptr<C2Component>(new V4L2EncodeComponent(name, id, std::move(interface)),
227                                         deleter);
228 }
229 
V4L2EncodeComponent(C2String name,c2_node_id_t id,std::shared_ptr<V4L2EncodeInterface> interface)230 V4L2EncodeComponent::V4L2EncodeComponent(C2String name, c2_node_id_t id,
231                                          std::shared_ptr<V4L2EncodeInterface> interface)
232       : mName(name),
233         mId(id),
234         mInterface(std::move(interface)),
235         mComponentState(ComponentState::LOADED) {
236     ALOGV("%s(%s)", __func__, name.c_str());
237 
238     sConcurrentInstances.fetch_add(1, std::memory_order_relaxed);
239 }
240 
~V4L2EncodeComponent()241 V4L2EncodeComponent::~V4L2EncodeComponent() {
242     ALOGV("%s()", __func__);
243 
244     // Stop encoder thread and invalidate pointers if component wasn't stopped before destroying.
245     if (mEncoderThread.IsRunning()) {
246         mEncoderTaskRunner->PostTask(
247                 FROM_HERE, ::base::BindOnce(
248                                    [](::base::WeakPtrFactory<V4L2EncodeComponent>* weakPtrFactory) {
249                                        weakPtrFactory->InvalidateWeakPtrs();
250                                    },
251                                    &mWeakThisFactory));
252         mEncoderThread.Stop();
253     }
254 
255     sConcurrentInstances.fetch_sub(1, std::memory_order_relaxed);
256     ALOGV("%s(): done", __func__);
257 }
258 
start()259 c2_status_t V4L2EncodeComponent::start() {
260     ALOGV("%s()", __func__);
261 
262     // Lock while starting, to synchronize start/stop/reset/release calls.
263     std::lock_guard<std::mutex> lock(mComponentLock);
264 
265     // According to the specification start() should only be called in the LOADED state.
266     if (mComponentState != ComponentState::LOADED) {
267         return C2_BAD_STATE;
268     }
269 
270     if (!mEncoderThread.Start()) {
271         ALOGE("Failed to start encoder thread");
272         return C2_CORRUPTED;
273     }
274     mEncoderTaskRunner = mEncoderThread.task_runner();
275     mWeakThis = mWeakThisFactory.GetWeakPtr();
276 
277     // Initialize the encoder on the encoder thread.
278     ::base::WaitableEvent done;
279     bool success = false;
280     mEncoderTaskRunner->PostTask(
281             FROM_HERE, ::base::Bind(&V4L2EncodeComponent::startTask, mWeakThis, &success, &done));
282     done.Wait();
283 
284     if (!success) {
285         ALOGE("Failed to initialize encoder");
286         return C2_CORRUPTED;
287     }
288 
289     setComponentState(ComponentState::RUNNING);
290     return C2_OK;
291 }
292 
stop()293 c2_status_t V4L2EncodeComponent::stop() {
294     ALOGV("%s()", __func__);
295 
296     // Lock while stopping, to synchronize start/stop/reset/release calls.
297     std::lock_guard<std::mutex> lock(mComponentLock);
298 
299     if (mComponentState != ComponentState::RUNNING && mComponentState != ComponentState::ERROR) {
300         return C2_BAD_STATE;
301     }
302 
303     // Return immediately if the component is already stopped.
304     if (!mEncoderThread.IsRunning()) {
305         return C2_OK;
306     }
307 
308     // Wait for the component to stop.
309     ::base::WaitableEvent done;
310     mEncoderTaskRunner->PostTask(
311             FROM_HERE, ::base::BindOnce(&V4L2EncodeComponent::stopTask, mWeakThis, &done));
312     done.Wait();
313     mEncoderThread.Stop();
314 
315     setComponentState(ComponentState::LOADED);
316 
317     ALOGV("%s() - done", __func__);
318     return C2_OK;
319 }
320 
reset()321 c2_status_t V4L2EncodeComponent::reset() {
322     ALOGV("%s()", __func__);
323 
324     // The interface specification says: "This method MUST be supported in all (including tripped)
325     // states other than released".
326     if (mComponentState == ComponentState::UNLOADED) {
327         return C2_BAD_STATE;
328     }
329 
330     // TODO(dstaessens): Reset the component's interface to default values.
331     stop();
332 
333     return C2_OK;
334 }
335 
release()336 c2_status_t V4L2EncodeComponent::release() {
337     ALOGV("%s()", __func__);
338 
339     // The interface specification says: "This method MUST be supported in stopped state.", but the
340     // release method seems to be called in other states as well.
341     reset();
342 
343     setComponentState(ComponentState::UNLOADED);
344     return C2_OK;
345 }
346 
queue_nb(std::list<std::unique_ptr<C2Work>> * const items)347 c2_status_t V4L2EncodeComponent::queue_nb(std::list<std::unique_ptr<C2Work>>* const items) {
348     ALOGV("%s()", __func__);
349 
350     if (mComponentState != ComponentState::RUNNING) {
351         ALOGE("Trying to queue work item while component is not running");
352         return C2_BAD_STATE;
353     }
354 
355     while (!items->empty()) {
356         mEncoderTaskRunner->PostTask(FROM_HERE,
357                                      ::base::BindOnce(&V4L2EncodeComponent::queueTask, mWeakThis,
358                                                       std::move(items->front())));
359         items->pop_front();
360     }
361 
362     return C2_OK;
363 }
364 
drain_nb(drain_mode_t mode)365 c2_status_t V4L2EncodeComponent::drain_nb(drain_mode_t mode) {
366     ALOGV("%s()", __func__);
367 
368     if (mode == DRAIN_CHAIN) {
369         return C2_OMITTED;  // Tunneling is not supported for now.
370     }
371 
372     if (mComponentState != ComponentState::RUNNING) {
373         return C2_BAD_STATE;
374     }
375 
376     mEncoderTaskRunner->PostTask(
377             FROM_HERE, ::base::BindOnce(&V4L2EncodeComponent::drainTask, mWeakThis, mode));
378     return C2_OK;
379 }
380 
flush_sm(flush_mode_t mode,std::list<std::unique_ptr<C2Work>> * const flushedWork)381 c2_status_t V4L2EncodeComponent::flush_sm(flush_mode_t mode,
382                                           std::list<std::unique_ptr<C2Work>>* const flushedWork) {
383     ALOGV("%s()", __func__);
384 
385     if (mode != FLUSH_COMPONENT) {
386         return C2_OMITTED;  // Tunneling is not supported by now
387     }
388 
389     if (mComponentState != ComponentState::RUNNING) {
390         return C2_BAD_STATE;
391     }
392 
393     // Work that can be immediately discarded should be returned in |flushedWork|. This method may
394     // be momentarily blocking but must return within 5ms, which should give us enough time to
395     // immediately abandon all non-started work on the encoder thread. We can return all work that
396     // can't be immediately discarded using onWorkDone() later.
397     ::base::WaitableEvent done;
398     mEncoderTaskRunner->PostTask(FROM_HERE, ::base::BindOnce(&V4L2EncodeComponent::flushTask,
399                                                              mWeakThis, &done, flushedWork));
400     done.Wait();
401 
402     return C2_OK;
403 }
404 
announce_nb(const std::vector<C2WorkOutline> & items)405 c2_status_t V4L2EncodeComponent::announce_nb(const std::vector<C2WorkOutline>& items) {
406     return C2_OMITTED;  // Tunneling is not supported by now
407 }
408 
setListener_vb(const std::shared_ptr<Listener> & listener,c2_blocking_t mayBlock)409 c2_status_t V4L2EncodeComponent::setListener_vb(const std::shared_ptr<Listener>& listener,
410                                                 c2_blocking_t mayBlock) {
411     ALOG_ASSERT(mComponentState != ComponentState::UNLOADED);
412 
413     // Lock so we're sure the component isn't currently starting or stopping.
414     std::lock_guard<std::mutex> lock(mComponentLock);
415 
416     // If the encoder thread is not running it's safe to update the listener directly.
417     if (!mEncoderThread.IsRunning()) {
418         mListener = listener;
419         return C2_OK;
420     }
421 
422     // The listener should be updated before exiting this function. If called while the component is
423     // currently running we should be allowed to block, as we can only change the listener on the
424     // encoder thread.
425     ALOG_ASSERT(mayBlock == c2_blocking_t::C2_MAY_BLOCK);
426 
427     ::base::WaitableEvent done;
428     mEncoderTaskRunner->PostTask(FROM_HERE, ::base::BindOnce(&V4L2EncodeComponent::setListenerTask,
429                                                              mWeakThis, listener, &done));
430     done.Wait();
431 
432     return C2_OK;
433 }
434 
intf()435 std::shared_ptr<C2ComponentInterface> V4L2EncodeComponent::intf() {
436     return std::make_shared<SimpleInterface<V4L2EncodeInterface>>(mName.c_str(), mId, mInterface);
437 }
438 
startTask(bool * success,::base::WaitableEvent * done)439 void V4L2EncodeComponent::startTask(bool* success, ::base::WaitableEvent* done) {
440     ALOGV("%s()", __func__);
441     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
442 
443     *success = initializeEncoder();
444     done->Signal();
445 }
446 
stopTask(::base::WaitableEvent * done)447 void V4L2EncodeComponent::stopTask(::base::WaitableEvent* done) {
448     ALOGV("%s()", __func__);
449     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
450 
451     // Flushing the encoder will abort all pending work.
452     flush();
453 
454     mInputFormatConverter.reset();
455 
456     mEncoder.reset();
457     mOutputBlockPool.reset();
458 
459     // Invalidate all weak pointers so no more functions will be executed on the encoder thread.
460     mWeakThisFactory.InvalidateWeakPtrs();
461 
462     done->Signal();
463 }
464 
queueTask(std::unique_ptr<C2Work> work)465 void V4L2EncodeComponent::queueTask(std::unique_ptr<C2Work> work) {
466     ALOGV("%s()", __func__);
467     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
468     ALOG_ASSERT(mEncoder);
469 
470     // Currently only a single worklet per work item is supported. An input buffer should always be
471     // supplied unless this is a drain or CSD request.
472     ALOG_ASSERT(work->input.buffers.size() <= 1u && work->worklets.size() == 1u);
473 
474     // Set the default values for the output worklet.
475     work->worklets.front()->output.flags = static_cast<C2FrameData::flags_t>(0);
476     work->worklets.front()->output.buffers.clear();
477     work->worklets.front()->output.ordinal = work->input.ordinal;
478 
479     uint64_t index = work->input.ordinal.frameIndex.peeku();
480     int64_t timestamp = static_cast<int64_t>(work->input.ordinal.timestamp.peeku());
481     bool endOfStream = work->input.flags & C2FrameData::FLAG_END_OF_STREAM;
482     ALOGV("Queuing next encode (index: %" PRIu64 ", timestamp: %" PRId64 ", EOS: %d)", index,
483           timestamp, endOfStream);
484 
485     // The codec 2.0 framework might queue an empty CSD request, but this is currently not
486     // supported. We will return the CSD with the first encoded buffer work.
487     if (work->input.buffers.empty() && !endOfStream) {
488         ALOGV("Discarding empty CSD request");
489         reportWork(std::move(work));
490         return;
491     }
492 
493     // By the time we get an input buffer, the output block pool should be configured.
494     if (!mOutputBlockPool && !getBlockPool()) {
495         reportError(C2_CORRUPTED);
496         return;
497     }
498 
499     // If conversion is required but no free buffers are available we queue the work item.
500     if (mInputFormatConverter && !mInputFormatConverter->isReady()) {
501         ALOGV("Input format convertor ran out of buffers");
502         mInputConverterQueue.push(std::move(work));
503         return;
504     }
505 
506     // If we have data to encode send it to the encoder. If conversion is required we will first
507     // convert the data to the requested pixel format.
508     if (!work->input.buffers.empty()) {
509         C2ConstGraphicBlock inputBlock =
510                 work->input.buffers.front()->data().graphicBlocks().front();
511         if (mInputFormatConverter) {
512             ALOGV("Converting input block (index: %" PRIu64 ")", index);
513             c2_status_t status = C2_CORRUPTED;
514             inputBlock = mInputFormatConverter->convertBlock(index, inputBlock, &status);
515             if (status != C2_OK) {
516                 ALOGE("Failed to convert input block (index: %" PRIu64 ")", index);
517                 reportError(status);
518                 return;
519             }
520         }
521         if (!encode(inputBlock, index, timestamp)) {
522             return;
523         }
524     }
525 
526     mWorkQueue.push_back(std::move(work));
527     if (endOfStream) {
528         mEncoder->drain();
529     }
530 }
531 
drainTask(drain_mode_t)532 void V4L2EncodeComponent::drainTask(drain_mode_t /*drainMode*/) {
533     ALOGV("%s()", __func__);
534     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
535 
536     // We can only start draining if all work has been queued in the encoder, so we mark the last
537     // item waiting for conversion as EOS if required.
538     if (!mInputConverterQueue.empty()) {
539         C2Work* work = mInputConverterQueue.back().get();
540         work->input.flags = static_cast<C2FrameData::flags_t>(work->input.flags |
541                                                               C2FrameData::FLAG_END_OF_STREAM);
542         return;
543     }
544 
545     // Mark the last item in the output work queue as EOS, so we will only report it as finished
546     // after draining has completed.
547     if (!mWorkQueue.empty()) {
548         ALOGV("Starting drain and marking last item in output work queue as EOS");
549         C2Work* work = mWorkQueue.back().get();
550         work->input.flags = static_cast<C2FrameData::flags_t>(work->input.flags |
551                                                               C2FrameData::FLAG_END_OF_STREAM);
552         mEncoder->drain();
553     }
554 }
555 
onDrainDone(bool success)556 void V4L2EncodeComponent::onDrainDone(bool success) {
557     ALOGV("%s()", __func__);
558     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
559     ALOG_ASSERT(!mWorkQueue.empty());
560 
561     if (!success) {
562         ALOGE("draining the encoder failed");
563         reportError(C2_CORRUPTED);
564         return;
565     }
566 
567     // Find the first work item marked as EOS. This might not be the first item in the queue, as
568     // previous buffers in the queue might still be waiting for their associated input buffers.
569     auto it = std::find_if(
570             mWorkQueue.cbegin(), mWorkQueue.cend(), [](const std::unique_ptr<C2Work>& work) {
571                 return ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
572                         !(work->worklets.back()->output.flags & C2FrameData::FLAG_END_OF_STREAM));
573             });
574     if (it == mWorkQueue.end()) {
575         ALOGW("No EOS work item found in queue");
576         return;
577     }
578 
579     // Mark the item in the output work queue as EOS done.
580     C2Work* eosWork = it->get();
581     eosWork->worklets.back()->output.flags = C2FrameData::FLAG_END_OF_STREAM;
582 
583     // Draining is done which means all buffers on the device output queue have been returned, but
584     // not all buffers on the device input queue might have been returned yet.
585     if ((eosWork != mWorkQueue.front().get()) || !isWorkDone(*eosWork)) {
586         ALOGV("Draining done, waiting for input buffers to be returned");
587         return;
588     }
589 
590     ALOGV("Draining done");
591     reportWork(std::move(mWorkQueue.front()));
592     mWorkQueue.pop_front();
593 }
594 
flushTask(::base::WaitableEvent * done,std::list<std::unique_ptr<C2Work>> * const flushedWork)595 void V4L2EncodeComponent::flushTask(::base::WaitableEvent* done,
596                                     std::list<std::unique_ptr<C2Work>>* const flushedWork) {
597     ALOGV("%s()", __func__);
598     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
599 
600     // Move all work that can immediately be aborted to flushedWork, and notify the caller.
601     if (flushedWork) {
602         while (!mInputConverterQueue.empty()) {
603             std::unique_ptr<C2Work> work = std::move(mInputConverterQueue.front());
604             work->input.buffers.clear();
605             flushedWork->push_back(std::move(work));
606             mInputConverterQueue.pop();
607         }
608     }
609     done->Signal();
610 
611     flush();
612 }
613 
setListenerTask(const std::shared_ptr<Listener> & listener,::base::WaitableEvent * done)614 void V4L2EncodeComponent::setListenerTask(const std::shared_ptr<Listener>& listener,
615                                           ::base::WaitableEvent* done) {
616     ALOGV("%s()", __func__);
617     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
618 
619     mListener = listener;
620     done->Signal();
621 }
622 
initializeEncoder()623 bool V4L2EncodeComponent::initializeEncoder() {
624     ALOGV("%s()", __func__);
625     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
626     ALOG_ASSERT(!mInputFormatConverter);
627     ALOG_ASSERT(!mEncoder);
628 
629     mLastFrameTime = std::nullopt;
630 
631     // Get the requested profile and level.
632     C2Config::profile_t outputProfile = mInterface->getOutputProfile();
633 
634     // CSD only needs to be extracted when using an H.264 profile.
635     mExtractCSD = IsH264Profile(outputProfile);
636 
637     std::optional<uint8_t> h264Level;
638     if (IsH264Profile(outputProfile)) {
639         h264Level = c2LevelToV4L2Level(mInterface->getOutputLevel());
640     }
641 
642     // Get the stride used by the C2 framework, as this might be different from the stride used by
643     // the V4L2 encoder.
644     std::optional<uint32_t> stride =
645             getVideoFrameStride(kInputPixelFormat, mInterface->getInputVisibleSize());
646     if (!stride) {
647         ALOGE("Failed to get video frame stride");
648         reportError(C2_CORRUPTED);
649         return false;
650     }
651 
652     // Get the requested bitrate mode and bitrate. The C2 framework doesn't offer a parameter to
653     // configure the peak bitrate, so we use a multiple of the target bitrate.
654     mBitrateMode = mInterface->getBitrateMode();
655     if (property_get_bool("persist.vendor.v4l2_codec2.disable_vbr", false)) {
656         // NOTE: This is a workaround for b/235771157.
657         ALOGW("VBR is disabled on this device");
658         mBitrateMode = C2Config::BITRATE_CONST;
659     }
660 
661     mBitrate = mInterface->getBitrate();
662 
663     mEncoder = V4L2Encoder::create(
664             outputProfile, h264Level, mInterface->getInputVisibleSize(), *stride,
665             mInterface->getKeyFramePeriod(), mBitrateMode, mBitrate,
666             mBitrate * kPeakBitrateMultiplier,
667             ::base::BindRepeating(&V4L2EncodeComponent::fetchOutputBlock, mWeakThis),
668             ::base::BindRepeating(&V4L2EncodeComponent::onInputBufferDone, mWeakThis),
669             ::base::BindRepeating(&V4L2EncodeComponent::onOutputBufferDone, mWeakThis),
670             ::base::BindRepeating(&V4L2EncodeComponent::onDrainDone, mWeakThis),
671             ::base::BindRepeating(&V4L2EncodeComponent::reportError, mWeakThis, C2_CORRUPTED),
672             mEncoderTaskRunner);
673     if (!mEncoder) {
674         ALOGE("Failed to create V4L2Encoder (profile: %s)", profileToString(outputProfile));
675         return false;
676     }
677 
678     // Add an input format convertor if the device doesn't support the requested input format.
679     ALOGV("Creating input format convertor (%s)",
680           videoPixelFormatToString(mEncoder->inputFormat()).c_str());
681     mInputFormatConverter =
682             FormatConverter::Create(mEncoder->inputFormat(), mEncoder->visibleSize(),
683                                     V4L2Encoder::kInputBufferCount, mEncoder->codedSize());
684     if (!mInputFormatConverter) {
685         ALOGE("Failed to created input format convertor");
686         return false;
687     }
688 
689     return true;
690 }
691 
updateEncodingParameters()692 bool V4L2EncodeComponent::updateEncodingParameters() {
693     ALOGV("%s()", __func__);
694     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
695 
696     // Ask device to change bitrate if it's different from the currently configured bitrate. The C2
697     // framework doesn't offer a parameter to configure the peak bitrate, so we'll use a multiple of
698     // the target bitrate here. The peak bitrate is only used if the bitrate mode is set to VBR.
699     uint32_t bitrate = mInterface->getBitrate();
700     if (mBitrate != bitrate) {
701         ALOG_ASSERT(bitrate > 0u);
702         ALOGV("Setting bitrate to %u", bitrate);
703         if (!mEncoder->setBitrate(bitrate)) {
704             reportError(C2_CORRUPTED);
705             return false;
706         }
707         mBitrate = bitrate;
708 
709         if (mBitrateMode == C2Config::BITRATE_VARIABLE) {
710             ALOGV("Setting peak bitrate to %u", bitrate * kPeakBitrateMultiplier);
711             // TODO(b/190336806): Our stack doesn't support dynamic peak bitrate changes yet, ignore
712             // errors for now.
713             mEncoder->setPeakBitrate(bitrate * kPeakBitrateMultiplier);
714         }
715     }
716 
717     // Ask device to change framerate if it's different from the currently configured framerate.
718     uint32_t framerate = static_cast<uint32_t>(std::round(mInterface->getFramerate()));
719     if (mFramerate != framerate) {
720         ALOG_ASSERT(framerate > 0u);
721         ALOGV("Setting framerate to %u", framerate);
722         if (!mEncoder->setFramerate(framerate)) {
723             ALOGE("Requesting framerate change failed");
724             reportError(C2_CORRUPTED);
725             return false;
726         }
727         mFramerate = framerate;
728     }
729 
730     // Check whether an explicit key frame was requested, if so reset the key frame counter to
731     // immediately request a key frame.
732     C2StreamRequestSyncFrameTuning::output requestKeyFrame;
733     c2_status_t status = mInterface->query({&requestKeyFrame}, {}, C2_DONT_BLOCK, nullptr);
734     if (status != C2_OK) {
735         ALOGE("Failed to query interface for key frame request (error code: %d)", status);
736         reportError(status);
737         return false;
738     }
739     if (requestKeyFrame.value == C2_TRUE) {
740         mEncoder->requestKeyframe();
741         requestKeyFrame.value = C2_FALSE;
742         std::vector<std::unique_ptr<C2SettingResult>> failures;
743         status = mInterface->config({&requestKeyFrame}, C2_MAY_BLOCK, &failures);
744         if (status != C2_OK) {
745             ALOGE("Failed to reset key frame request on interface (error code: %d)", status);
746             reportError(status);
747             return false;
748         }
749     }
750 
751     return true;
752 }
753 
encode(C2ConstGraphicBlock block,uint64_t index,int64_t timestamp)754 bool V4L2EncodeComponent::encode(C2ConstGraphicBlock block, uint64_t index, int64_t timestamp) {
755     ALOGV("%s()", __func__);
756     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
757     ALOG_ASSERT(mEncoder);
758 
759     ALOGV("Encoding input block (index: %" PRIu64 ", timestamp: %" PRId64 ", size: %dx%d)", index,
760           timestamp, block.width(), block.height());
761 
762     // Dynamically adjust framerate based on the frame's timestamp if required.
763     constexpr int64_t kMaxFramerateDiff = 5;
764     if (mLastFrameTime && (timestamp > *mLastFrameTime)) {
765         int64_t newFramerate = std::max(
766                 static_cast<int64_t>(std::round(1000000.0 / (timestamp - *mLastFrameTime))),
767                 static_cast<int64_t>(1LL));
768         if (abs(mFramerate - newFramerate) > kMaxFramerateDiff) {
769             ALOGV("Adjusting framerate to %" PRId64 " based on frame timestamps", newFramerate);
770             mInterface->setFramerate(static_cast<uint32_t>(newFramerate));
771         }
772     }
773     mLastFrameTime = timestamp;
774 
775     // Update dynamic encoding parameters (bitrate, framerate, key frame) if requested.
776     if (!updateEncodingParameters()) return false;
777 
778     // Create an input frame from the graphic block.
779     std::unique_ptr<V4L2Encoder::InputFrame> frame = CreateInputFrame(block, index, timestamp);
780     if (!frame) {
781         ALOGE("Failed to create video frame from input block (index: %" PRIu64
782               ", timestamp: %" PRId64 ")",
783               index, timestamp);
784         reportError(C2_CORRUPTED);
785         return false;
786     }
787 
788     if (!mEncoder->encode(std::move(frame))) {
789         return false;
790     }
791 
792     return true;
793 }
794 
flush()795 void V4L2EncodeComponent::flush() {
796     ALOGV("%s()", __func__);
797     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
798 
799     mEncoder->flush();
800 
801     // Report all queued work items as aborted.
802     std::list<std::unique_ptr<C2Work>> abortedWorkItems;
803     while (!mInputConverterQueue.empty()) {
804         std::unique_ptr<C2Work> work = std::move(mInputConverterQueue.front());
805         work->result = C2_NOT_FOUND;
806         work->input.buffers.clear();
807         abortedWorkItems.push_back(std::move(work));
808         mInputConverterQueue.pop();
809     }
810     while (!mWorkQueue.empty()) {
811         std::unique_ptr<C2Work> work = std::move(mWorkQueue.front());
812         // Return buffer to the input format convertor if required.
813         if (mInputFormatConverter && work->input.buffers.empty()) {
814             mInputFormatConverter->returnBlock(work->input.ordinal.frameIndex.peeku());
815         }
816         work->result = C2_NOT_FOUND;
817         work->input.buffers.clear();
818         abortedWorkItems.push_back(std::move(work));
819         mWorkQueue.pop_front();
820     }
821     if (!abortedWorkItems.empty()) {
822         mListener->onWorkDone_nb(weak_from_this(), std::move(abortedWorkItems));
823     }
824 }
825 
fetchOutputBlock(uint32_t size,std::unique_ptr<BitstreamBuffer> * buffer)826 void V4L2EncodeComponent::fetchOutputBlock(uint32_t size,
827                                            std::unique_ptr<BitstreamBuffer>* buffer) {
828     ALOGV("Fetching linear block (size: %u)", size);
829     std::shared_ptr<C2LinearBlock> block;
830     c2_status_t status = mOutputBlockPool->fetchLinearBlock(
831             size,
832             C2MemoryUsage(C2MemoryUsage::CPU_READ |
833                           static_cast<uint64_t>(BufferUsage::VIDEO_ENCODER)),
834             &block);
835     if (status != C2_OK) {
836         ALOGE("Failed to fetch linear block (error: %d)", status);
837         reportError(status);
838     }
839 
840     *buffer = std::make_unique<BitstreamBuffer>(std::move(block), 0, size);
841 }
842 
onInputBufferDone(uint64_t index)843 void V4L2EncodeComponent::onInputBufferDone(uint64_t index) {
844     ALOGV("%s(): Input buffer done (index: %" PRIu64 ")", __func__, index);
845     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
846     ALOG_ASSERT(mEncoder);
847 
848     // There are no guarantees the input buffers are returned in order, so we need to find the work
849     // item which this buffer belongs to.
850     C2Work* work = getWorkByIndex(index);
851     if (!work) {
852         ALOGE("Failed to find work associated with input buffer %" PRIu64, index);
853         reportError(C2_CORRUPTED);
854         return;
855     }
856 
857     // We're done using the input block, release reference to return the block to the client.
858     LOG_ASSERT(!work->input.buffers.empty());
859     work->input.buffers.front().reset();
860 
861     // Return the block to the convertor if required. If we have buffers awaiting conversion, we can
862     // now attempt to convert and encode them again.
863     if (mInputFormatConverter) {
864         c2_status_t status = mInputFormatConverter->returnBlock(index);
865         if (status != C2_OK) {
866             reportError(status);
867             return;
868         }
869         while (!mInputConverterQueue.empty() && mInputFormatConverter->isReady()) {
870             std::unique_ptr<C2Work> work = std::move(mInputConverterQueue.front());
871             mInputConverterQueue.pop();
872             queueTask(std::move(work));
873         }
874     }
875 
876     // Return all completed work items. The work item might have been waiting for it's input buffer
877     // to be returned, in which case we can report it as completed now. As input buffers are not
878     // necessarily returned in order we might be able to return multiple ready work items now.
879     while (!mWorkQueue.empty() && isWorkDone(*mWorkQueue.front())) {
880         reportWork(std::move(mWorkQueue.front()));
881         mWorkQueue.pop_front();
882     }
883 }
884 
onOutputBufferDone(size_t dataSize,int64_t timestamp,bool keyFrame,std::unique_ptr<BitstreamBuffer> buffer)885 void V4L2EncodeComponent::onOutputBufferDone(size_t dataSize, int64_t timestamp, bool keyFrame,
886                                              std::unique_ptr<BitstreamBuffer> buffer) {
887     ALOGV("%s(): output buffer done (timestamp: %" PRId64 ", size: %zu, keyframe: %d)", __func__,
888           timestamp, dataSize, keyFrame);
889     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
890     ALOG_ASSERT(buffer->dmabuf);
891 
892     C2ConstLinearBlock constBlock =
893             buffer->dmabuf->share(buffer->dmabuf->offset(), dataSize, C2Fence());
894 
895     // If no CSD (content-specific-data, e.g. SPS for H.264) has been submitted yet, we expect this
896     // output block to contain CSD. We only submit the CSD once, even if it's attached to each key
897     // frame.
898     if (mExtractCSD) {
899         ALOGV("No CSD submitted yet, extracting CSD");
900         std::unique_ptr<C2StreamInitDataInfo::output> csd;
901         C2ReadView view = constBlock.map().get();
902         if (!extractCSDInfo(&csd, view.data(), view.capacity())) {
903             ALOGE("Failed to extract CSD");
904             reportError(C2_CORRUPTED);
905             return;
906         }
907 
908         // Attach the CSD to the first item in our output work queue.
909         LOG_ASSERT(!mWorkQueue.empty());
910         C2Work* work = mWorkQueue.front().get();
911         work->worklets.front()->output.configUpdate.push_back(std::move(csd));
912         mExtractCSD = false;
913     }
914 
915     // Get the work item associated with the timestamp.
916     C2Work* work = getWorkByTimestamp(timestamp);
917     if (!work) {
918         // It's possible we got an empty CSD request with timestamp 0, which we currently just
919         // discard.
920         if (timestamp != 0) {
921             reportError(C2_CORRUPTED);
922         }
923         return;
924     }
925 
926     std::shared_ptr<C2Buffer> linearBuffer = C2Buffer::CreateLinearBuffer(std::move(constBlock));
927     if (!linearBuffer) {
928         ALOGE("Failed to create linear buffer from block");
929         reportError(C2_CORRUPTED);
930         return;
931     }
932 
933     if (keyFrame) {
934         linearBuffer->setInfo(
935                 std::make_shared<C2StreamPictureTypeMaskInfo::output>(0u, C2Config::SYNC_FRAME));
936     }
937     work->worklets.front()->output.buffers.emplace_back(std::move(linearBuffer));
938 
939     // We can report the work item as completed if its associated input buffer has also been
940     // released. As output buffers are not necessarily returned in order we might be able to return
941     // multiple ready work items now.
942     while (!mWorkQueue.empty() && isWorkDone(*mWorkQueue.front())) {
943         reportWork(std::move(mWorkQueue.front()));
944         mWorkQueue.pop_front();
945     }
946 }
947 
getWorkByIndex(uint64_t index)948 C2Work* V4L2EncodeComponent::getWorkByIndex(uint64_t index) {
949     ALOGV("%s(): getting work item (index: %" PRIu64 ")", __func__, index);
950     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
951 
952     auto it = std::find_if(mWorkQueue.begin(), mWorkQueue.end(),
953                            [index](const std::unique_ptr<C2Work>& w) {
954                                return w->input.ordinal.frameIndex.peeku() == index;
955                            });
956     if (it == mWorkQueue.end()) {
957         ALOGE("Failed to find work (index: %" PRIu64 ")", index);
958         return nullptr;
959     }
960     return it->get();
961 }
962 
getWorkByTimestamp(int64_t timestamp)963 C2Work* V4L2EncodeComponent::getWorkByTimestamp(int64_t timestamp) {
964     ALOGV("%s(): getting work item (timestamp: %" PRId64 ")", __func__, timestamp);
965     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
966     ALOG_ASSERT(timestamp >= 0);
967 
968     // Find the work with specified timestamp by looping over the output work queue. This should be
969     // very fast as the output work queue will never be longer then a few items. Ignore empty work
970     // items that are marked as EOS, as their timestamp might clash with other work items.
971     auto it = std::find_if(
972             mWorkQueue.begin(), mWorkQueue.end(), [timestamp](const std::unique_ptr<C2Work>& w) {
973                 return !(w->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
974                        w->input.ordinal.timestamp.peeku() == static_cast<uint64_t>(timestamp);
975             });
976     if (it == mWorkQueue.end()) {
977         ALOGE("Failed to find work (timestamp: %" PRIu64 ")", timestamp);
978         return nullptr;
979     }
980     return it->get();
981 }
982 
isWorkDone(const C2Work & work) const983 bool V4L2EncodeComponent::isWorkDone(const C2Work& work) const {
984     ALOGV("%s()", __func__);
985     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
986 
987     if ((work.input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
988         !(work.worklets.front()->output.flags & C2FrameData::FLAG_END_OF_STREAM)) {
989         ALOGV("Work item %" PRIu64 " is marked as EOS but draining has not finished yet",
990               work.input.ordinal.frameIndex.peeku());
991         return false;
992     }
993 
994     if (!work.input.buffers.empty() && work.input.buffers.front()) {
995         ALOGV("Input buffer associated with work item %" PRIu64 " not returned yet",
996               work.input.ordinal.frameIndex.peeku());
997         return false;
998     }
999 
1000     // If the work item had an input buffer to be encoded, it should have an output buffer set.
1001     if (!work.input.buffers.empty() && work.worklets.front()->output.buffers.empty()) {
1002         ALOGV("Output buffer associated with work item %" PRIu64 " not returned yet",
1003               work.input.ordinal.frameIndex.peeku());
1004         return false;
1005     }
1006 
1007     return true;
1008 }
1009 
reportWork(std::unique_ptr<C2Work> work)1010 void V4L2EncodeComponent::reportWork(std::unique_ptr<C2Work> work) {
1011     ALOG_ASSERT(work);
1012     ALOGV("%s(): Reporting work item as finished (index: %llu, timestamp: %llu)", __func__,
1013           work->input.ordinal.frameIndex.peekull(), work->input.ordinal.timestamp.peekull());
1014     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
1015 
1016     work->result = C2_OK;
1017     work->workletsProcessed = static_cast<uint32_t>(work->worklets.size());
1018 
1019     std::list<std::unique_ptr<C2Work>> finishedWorkList;
1020     finishedWorkList.emplace_back(std::move(work));
1021     mListener->onWorkDone_nb(weak_from_this(), std::move(finishedWorkList));
1022 }
1023 
getBlockPool()1024 bool V4L2EncodeComponent::getBlockPool() {
1025     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
1026 
1027     auto sharedThis = weak_from_this().lock();
1028     if (!sharedThis) {
1029         ALOGI("%s(): V4L2EncodeComponent instance is already destroyed", __func__);
1030         return false;
1031     }
1032 
1033     C2BlockPool::local_id_t poolId = mInterface->getBlockPoolId();
1034     if (poolId == C2BlockPool::BASIC_LINEAR) {
1035         ALOGW("Using unoptimized linear block pool");
1036     }
1037     c2_status_t status = GetCodec2BlockPool(poolId, std::move(sharedThis), &mOutputBlockPool);
1038     if (status != C2_OK || !mOutputBlockPool) {
1039         ALOGE("Failed to get output block pool, error: %d", status);
1040         return false;
1041     }
1042     return true;
1043 }
1044 
reportError(c2_status_t error)1045 void V4L2EncodeComponent::reportError(c2_status_t error) {
1046     ALOGV("%s()", __func__);
1047     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
1048 
1049     // TODO(dstaessens): Report all pending work items as finished upon failure.
1050     std::lock_guard<std::mutex> lock(mComponentLock);
1051     if (mComponentState != ComponentState::ERROR) {
1052         setComponentState(ComponentState::ERROR);
1053         mListener->onError_nb(weak_from_this(), static_cast<uint32_t>(error));
1054     }
1055 }
1056 
setComponentState(ComponentState state)1057 void V4L2EncodeComponent::setComponentState(ComponentState state) {
1058     // Check whether the state change is valid.
1059     switch (state) {
1060     case ComponentState::UNLOADED:
1061         ALOG_ASSERT(mComponentState == ComponentState::LOADED);
1062         break;
1063     case ComponentState::LOADED:
1064         ALOG_ASSERT(mComponentState == ComponentState::UNLOADED ||
1065                     mComponentState == ComponentState::RUNNING ||
1066                     mComponentState == ComponentState::ERROR);
1067         break;
1068     case ComponentState::RUNNING:
1069         ALOG_ASSERT(mComponentState == ComponentState::LOADED);
1070         break;
1071     case ComponentState::ERROR:
1072         break;
1073     }
1074 
1075     ALOGV("Changed component state from %s to %s", componentStateToString(mComponentState),
1076           componentStateToString(state));
1077     mComponentState = state;
1078 }
1079 
componentStateToString(V4L2EncodeComponent::ComponentState state)1080 const char* V4L2EncodeComponent::componentStateToString(V4L2EncodeComponent::ComponentState state) {
1081     switch (state) {
1082     case ComponentState::UNLOADED:
1083         return "UNLOADED";
1084     case ComponentState::LOADED:
1085         return "LOADED";
1086     case ComponentState::RUNNING:
1087         return "RUNNING";
1088     case ComponentState::ERROR:
1089         return "ERROR";
1090     }
1091 }
1092 
1093 }  // namespace android
1094