blob: 254b9b0fca68b0a4bbb78cefe27d2c61a475ad41 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ANDROID_C2_VDA_COMPONENT_H
#define ANDROID_C2_VDA_COMPONENT_H
#include <VideoDecodeAcceleratorAdaptor.h>
#include <rect.h>
#include <size.h>
#include <video_codecs.h>
#include <video_decode_accelerator.h>
#include <C2Component.h>
#include <C2Config.h>
#include <C2Enum.h>
#include <C2Param.h>
#include <C2ParamDef.h>
#include <SimpleC2Interface.h>
#include <util/C2InterfaceHelper.h>
#include <base/macros.h>
#include <base/memory/ref_counted.h>
#include <base/single_thread_task_runner.h>
#include <base/synchronization/waitable_event.h>
#include <base/threading/thread.h>
#include <atomic>
#include <deque>
#include <map>
#include <mutex>
#include <queue>
#include <unordered_map>
namespace android {
class C2VDAComponent : public C2Component,
public VideoDecodeAcceleratorAdaptor::Client,
public std::enable_shared_from_this<C2VDAComponent> {
public:
class IntfImpl : public C2InterfaceHelper {
public:
IntfImpl(C2String name, const std::shared_ptr<C2ReflectorHelper>& helper);
// interfaces for C2VDAComponent
c2_status_t status() const { return mInitStatus; }
media::VideoCodecProfile getCodecProfile() const { return mCodecProfile; }
C2BlockPool::local_id_t getBlockPoolId() const { return mOutputBlockPoolIds->m.values[0]; }
private:
// The input format kind; should be C2FormatCompressed.
std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
// The output format kind; should be C2FormatVideo.
std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
// The MIME type of input port.
std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
// The MIME type of output port; should be MEDIA_MIMETYPE_VIDEO_RAW.
std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
// Decoded video size for output.
std::shared_ptr<C2StreamPictureSizeInfo::output> mSize;
// The suggested usage of input buffer allocator ID.
std::shared_ptr<C2PortAllocatorsTuning::input> mInputAllocatorIds;
// The suggested usage of output buffer allocator ID.
std::shared_ptr<C2PortAllocatorsTuning::output> mOutputAllocatorIds;
// The suggested usage of output buffer allocator ID with surface.
std::shared_ptr<C2PortSurfaceAllocatorTuning::output> mOutputSurfaceAllocatorId;
// Compnent uses this ID to fetch corresponding output block pool from platform.
std::shared_ptr<C2PortBlockPoolsTuning::output> mOutputBlockPoolIds;
c2_status_t mInitStatus;
media::VideoCodecProfile mCodecProfile;
};
C2VDAComponent(C2String name, c2_node_id_t id,
const std::shared_ptr<C2ReflectorHelper>& helper);
virtual ~C2VDAComponent() override;
// Implementation of C2Component interface
virtual c2_status_t setListener_vb(const std::shared_ptr<Listener>& listener,
c2_blocking_t mayBlock) override;
virtual c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) override;
virtual c2_status_t announce_nb(const std::vector<C2WorkOutline>& items) override;
virtual c2_status_t flush_sm(flush_mode_t mode,
std::list<std::unique_ptr<C2Work>>* const flushedWork) override;
virtual c2_status_t drain_nb(drain_mode_t mode) override;
virtual c2_status_t start() override;
virtual c2_status_t stop() override;
virtual c2_status_t reset() override;
virtual c2_status_t release() override;
virtual std::shared_ptr<C2ComponentInterface> intf() override;
// Implementation of VideDecodeAcceleratorAdaptor::Client interface
virtual void providePictureBuffers(uint32_t minNumBuffers,
const media::Size& codedSize) override;
virtual void dismissPictureBuffer(int32_t pictureBufferId) override;
virtual void pictureReady(int32_t pictureBufferId, int32_t bitstreamId,
const media::Rect& cropRect) override;
virtual void notifyEndOfBitstreamBuffer(int32_t bitstreamId) override;
virtual void notifyFlushDone() override;
virtual void notifyResetDone() override;
virtual void notifyError(VideoDecodeAcceleratorAdaptor::Result error) override;
private:
// The state machine enumeration on parent thread.
enum class State : int32_t {
// The initial state of component. State will change to LOADED after the component is
// created.
UNLOADED,
// The component is stopped. State will change to RUNNING when start() is called by
// framework.
LOADED,
// The component is running, State will change to LOADED when stop() or reset() is called by
// framework.
RUNNING,
// The component is in error state.
ERROR,
};
// The state machine enumeration on component thread.
enum class ComponentState : int32_t {
// This is the initial state until VDA initialization returns successfully.
UNINITIALIZED,
// VDA initialization returns successfully. VDA is ready to make progress.
STARTED,
// onDrain() is called. VDA is draining. Component will hold on queueing works until
// onDrainDone().
DRAINING,
// onFlush() is called. VDA is flushing. State will change to STARTED after onFlushDone().
FLUSHING,
// onStop() is called. VDA is shutting down. State will change to UNINITIALIZED after
// onStopDone().
STOPPING,
// onError() is called.
ERROR,
};
// This constant is used to tell apart from drain_mode_t enumerations in C2Component.h, which
// means no drain request.
// Note: this value must be different than all enumerations in drain_mode_t.
static constexpr uint32_t NO_DRAIN = ~0u;
// Internal struct for work queue.
struct WorkEntry {
std::unique_ptr<C2Work> mWork;
uint32_t mDrainMode = NO_DRAIN;
};
// Internal struct to keep the information of a specific graphic block.
struct GraphicBlockInfo {
enum class State {
OWNED_BY_COMPONENT, // Owned by this component.
OWNED_BY_ACCELERATOR, // Owned by video decode accelerator.
OWNED_BY_CLIENT, // Owned by client.
};
// The ID of this block used for accelerator.
int32_t mBlockId = -1;
// The ID of this block used in block pool. It indicates slot index for bufferqueue-backed
// block pool, and buffer ID of BufferPoolData for bufferpool block pool.
uint32_t mPoolId = 0;
State mState = State::OWNED_BY_COMPONENT;
// Graphic block buffer allocated from allocator. The graphic block should be owned until
// it is passed to client.
std::shared_ptr<C2GraphicBlock> mGraphicBlock;
// HAL pixel format used while importing to VDA.
HalPixelFormat mPixelFormat;
// The handle dupped from graphic block for importing to VDA.
::base::ScopedFD mHandle;
// VideoFramePlane information for importing to VDA.
std::vector<VideoFramePlane> mPlanes;
};
struct VideoFormat {
HalPixelFormat mPixelFormat = HalPixelFormat::UNKNOWN;
uint32_t mMinNumBuffers = 0;
media::Size mCodedSize;
media::Rect mVisibleRect;
VideoFormat() {}
VideoFormat(HalPixelFormat pixelFormat, uint32_t minNumBuffers, media::Size codedSize,
media::Rect visibleRect);
};
// These tasks should be run on the component thread |mThread|.
void onDestroy();
void onStart(media::VideoCodecProfile profile, ::base::WaitableEvent* done);
void onQueueWork(std::unique_ptr<C2Work> work);
void onDequeueWork();
void onInputBufferDone(int32_t bitstreamId);
void onOutputBufferDone(int32_t pictureBufferId, int32_t bitstreamId);
void onDrain(uint32_t drainMode);
void onDrainDone();
void onFlush();
void onStop(::base::WaitableEvent* done);
void onResetDone();
void onFlushDone();
void onStopDone();
void onOutputFormatChanged(std::unique_ptr<VideoFormat> format);
void onVisibleRectChanged(const media::Rect& cropRect);
void onOutputBufferReturned(std::shared_ptr<C2GraphicBlock> block, uint32_t poolId);
// Send input buffer to accelerator with specified bitstream id.
void sendInputBufferToAccelerator(const C2ConstLinearBlock& input, int32_t bitstreamId);
// Send output buffer to accelerator.
void sendOutputBufferToAccelerator(GraphicBlockInfo* info);
// Set crop rectangle infomation to output format.
void setOutputFormatCrop(const media::Rect& cropRect);
// Helper function to get the specified GraphicBlockInfo object by its id.
GraphicBlockInfo* getGraphicBlockById(int32_t blockId);
// Helper function to get the specified GraphicBlockInfo object by its pool id.
GraphicBlockInfo* getGraphicBlockByPoolId(uint32_t poolId);
// Helper function to get the specified work in mPendingWorks by bitstream id.
C2Work* getPendingWorkByBitstreamId(int32_t bitstreamId);
// Try to apply the output format change.
void tryChangeOutputFormat();
// Allocate output buffers (graphic blocks) from block allocator.
c2_status_t allocateBuffersFromBlockAllocator(const media::Size& size, uint32_t pixelFormat);
// Append allocated buffer (graphic block) to mGraphicBlocks.
void appendOutputBuffer(std::shared_ptr<C2GraphicBlock> block, uint32_t poolId);
// Append allocated buffer (graphic block) to mGraphicBlocks in secure mode.
void appendSecureOutputBuffer(std::shared_ptr<C2GraphicBlock> block, uint32_t poolId);
// Check for finished works in mPendingWorks. If any, make onWorkDone call to listener.
void reportFinishedWorkIfAny();
// Make onWorkDone call to listener for reporting EOS work in mPendingWorks.
void reportEOSWork();
// Abandon all works in mPendingWorks and mAbandonedWorks.
void reportAbandonedWorks();
// Make onError call to listener for reporting errors.
void reportError(c2_status_t error);
// Helper function to determine if the work is finished.
bool isWorkDone(const C2Work* work) const;
// Start dequeue thread, return true on success.
bool startDequeueThread(const media::Size& size, uint32_t pixelFormat,
std::shared_ptr<C2BlockPool> blockPool);
// Stop dequeue thread.
void stopDequeueThread();
// The rountine task running on dequeue thread.
void dequeueThreadLoop(const media::Size& size, uint32_t pixelFormat,
std::shared_ptr<C2BlockPool> blockPool);
// The pointer of component interface implementation.
std::shared_ptr<IntfImpl> mIntfImpl;
// The pointer of component interface.
const std::shared_ptr<C2ComponentInterface> mIntf;
// The pointer of component listener.
std::shared_ptr<Listener> mListener;
// The main component thread.
::base::Thread mThread;
// The task runner on component thread.
scoped_refptr<::base::SingleThreadTaskRunner> mTaskRunner;
// The dequeue buffer loop thread.
::base::Thread mDequeueThread;
// The stop signal for dequeue loop which should be atomic (toggled by main thread).
std::atomic<bool> mDequeueLoopStop;
// The count of buffers owned by client which should be atomic.
std::atomic<uint32_t> mBuffersInClient;
// The following members should be utilized on component thread |mThread|.
// The initialization result retrieved from VDA.
VideoDecodeAcceleratorAdaptor::Result mVDAInitResult;
// The pointer of VideoDecodeAcceleratorAdaptor.
std::unique_ptr<VideoDecodeAcceleratorAdaptor> mVDAAdaptor;
// The done event pointer of stop procedure. It should be restored in onStop() and signaled in
// onStopDone().
::base::WaitableEvent* mStopDoneEvent;
// The state machine on component thread.
ComponentState mComponentState;
// The indicator of draining with EOS. This should be always set along with component going to
// DRAINING state, and will be unset either after reportEOSWork() (EOS is outputted), or
// reportAbandonedWorks() (drain is cancelled and works are abandoned).
bool mPendingOutputEOS;
// The vector of storing allocated output graphic block information.
std::vector<GraphicBlockInfo> mGraphicBlocks;
// The work queue. Works are queued along with drain mode from component API queue_nb and
// dequeued by the decode process of component.
std::queue<WorkEntry> mQueue;
// Store all pending works. The dequeued works are placed here until they are finished and then
// sent out by onWorkDone call to listener.
std::deque<std::unique_ptr<C2Work>> mPendingWorks;
// Store all abandoned works. When component gets flushed/stopped, remaining works in queue are
// dumped here and sent out by onWorkDone call to listener after flush/stop is finished.
std::vector<std::unique_ptr<C2Work>> mAbandonedWorks;
// Store the visible rect provided from VDA. If this is changed, component should issue a
// visible size change event.
media::Rect mRequestedVisibleRect;
// The current output format.
VideoFormat mOutputFormat;
// The pending output format. We need to wait until all buffers are returned back to apply the
// format change.
std::unique_ptr<VideoFormat> mPendingOutputFormat;
// Record the timestamp of the last output buffer. This is used to determine if the work is
// finished.
int64_t mLastOutputTimestamp;
// The indicator of whether component is in secure mode.
bool mSecureMode;
// The following members should be utilized on parent thread.
// The input codec profile which is configured in component interface.
media::VideoCodecProfile mCodecProfile;
// The state machine on parent thread which should be atomic.
std::atomic<State> mState;
// The mutex lock to synchronize start/stop/reset/release calls.
std::mutex mStartStopLock;
// The WeakPtrFactory for getting weak pointer of this.
::base::WeakPtrFactory<C2VDAComponent> mWeakThisFactory;
DISALLOW_COPY_AND_ASSIGN(C2VDAComponent);
};
} // namespace android
#endif // ANDROID_C2_VDA_COMPONENT_H