blob: 8a9459c4da859555810854598936e252b18dbdd2 [file] [log] [blame]
// Copyright 2020 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_V4L2_CODEC2_COMPONENTS_V4L2_ENCODE_COMPONENT_H
#define ANDROID_V4L2_CODEC2_COMPONENTS_V4L2_ENCODE_COMPONENT_H
#include <atomic>
#include <memory>
#include <optional>
#include <C2Component.h>
#include <C2ComponentFactory.h>
#include <C2Config.h>
#include <C2Enum.h>
#include <C2Param.h>
#include <C2ParamDef.h>
#include <SimpleC2Interface.h>
#include <base/files/scoped_file.h>
#include <base/memory/scoped_refptr.h>
#include <base/single_thread_task_runner.h>
#include <base/synchronization/waitable_event.h>
#include <base/threading/thread.h>
#include <util/C2InterfaceHelper.h>
#include <size.h>
#include <v4l2_codec2/common/FormatConverter.h>
#include <v4l2_codec2/components/V4L2EncodeInterface.h>
#include <video_frame_layout.h>
namespace media {
class V4L2Device;
class V4L2ReadableBuffer;
class V4L2Queue;
} // namespace media
namespace android {
struct VideoFramePlane;
class V4L2EncodeComponent : public C2Component,
public std::enable_shared_from_this<V4L2EncodeComponent> {
public:
// Create a new instance of the V4L2EncodeComponent.
static std::shared_ptr<C2Component> create(C2String name, c2_node_id_t id,
std::shared_ptr<C2ReflectorHelper> helper,
C2ComponentFactory::ComponentDeleter deleter);
virtual ~V4L2EncodeComponent() override;
// Implementation of the C2Component interface.
c2_status_t start() override;
c2_status_t stop() override;
c2_status_t reset() override;
c2_status_t release() override;
c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) override;
c2_status_t drain_nb(drain_mode_t mode) override;
c2_status_t flush_sm(flush_mode_t mode,
std::list<std::unique_ptr<C2Work>>* const flushedWork) override;
c2_status_t announce_nb(const std::vector<C2WorkOutline>& items) override;
c2_status_t setListener_vb(const std::shared_ptr<Listener>& listener,
c2_blocking_t mayBlock) override;
std::shared_ptr<C2ComponentInterface> intf() override;
private:
class InputFrame {
public:
// Create an input frame from a C2GraphicBlock.
static std::unique_ptr<InputFrame> Create(const C2ConstGraphicBlock& block);
~InputFrame() = default;
const std::vector<::base::ScopedFD>& getFDs() const { return mFds; }
private:
InputFrame(std::vector<::base::ScopedFD> fds) : mFds(std::move(fds)) {}
const std::vector<::base::ScopedFD> mFds;
};
// Possible component states.
enum class ComponentState {
UNLOADED, // Initial state of component.
LOADED, // The component is stopped, ready to start running.
RUNNING, // The component is currently running.
ERROR, // An error occurred.
};
// Possible encoder states.
enum class EncoderState {
UNINITIALIZED, // Not initialized yet or initialization failed.
WAITING_FOR_INPUT, // Waiting for work to be queued.
WAITING_FOR_INPUT_BUFFERS, // Waiting for V4L2 input queue buffers.
ENCODING, // Queuing input buffers.
DRAINING, // Flushing encoder.
ERROR, // encoder encountered an error.
};
V4L2EncodeComponent(C2String name, c2_node_id_t id,
std::shared_ptr<V4L2EncodeInterface> interface);
V4L2EncodeComponent(const V4L2EncodeComponent&) = delete;
V4L2EncodeComponent& operator=(const V4L2EncodeComponent&) = delete;
// Initialize the encoder on the encoder thread.
void startTask(bool* success, ::base::WaitableEvent* done);
// Destroy the encoder on the encoder thread.
void stopTask(::base::WaitableEvent* done);
// Queue a new encode work item on the encoder thread.
void queueTask(std::unique_ptr<C2Work> work);
// Drain all currently scheduled work on the encoder thread. The encoder will process all
// scheduled work and mark the last item as EOS, before processing any new work.
void drainTask(drain_mode_t drainMode);
// Called on the encoder thread when a drain is completed.
void onDrainDone(bool done);
// Flush all currently scheduled work on the encoder thread. The encoder will abort all
// scheduled work items, work that can be immediately aborted will be placed in |flushedWork|.
void flushTask(::base::WaitableEvent* done,
std::list<std::unique_ptr<C2Work>>* const flushedWork);
// Set the component listener on the encoder thread.
void setListenerTask(const std::shared_ptr<Listener>& listener, ::base::WaitableEvent* done);
// Initialize the V4L2 device for encoding with the requested configuration.
bool initializeEncoder();
// Configure input format on the V4L2 device.
bool configureInputFormat(media::VideoPixelFormat inputFormat);
// Configure output format on the V4L2 device.
bool configureOutputFormat(media::VideoCodecProfile outputProfile);
// Configure required and optional controls on the V4L2 device.
bool configureDevice(media::VideoCodecProfile outputProfile,
std::optional<const uint8_t> outputH264Level);
// Update the |mBitrate| and |mFramerate| currently configured on the V4L2 device, to match the
// values requested by the codec 2.0 framework.
bool updateEncodingParameters();
// Schedule the next encode operation on the V4L2 device.
void scheduleNextEncodeTask();
// Encode the specified |block| with corresponding |index| and |timestamp|.
bool encode(C2ConstGraphicBlock block, uint64_t index, int64_t timestamp);
// Drain the encoder.
void drain();
// Flush the encoder.
void flush();
// Fetch a new output buffer from the output block pool.
std::shared_ptr<C2LinearBlock> fetchOutputBlock();
// Called on the encoder thread when the encoder is done using an input buffer.
void onInputBufferDone(uint64_t index);
// Called on the encoder thread when an output buffer is ready.
void onOutputBufferDone(uint32_t payloadSize, bool keyFrame, int64_t timestamp,
std::shared_ptr<C2LinearBlock> outputBlock);
// Helper function to find a work item in the output work queue by index.
C2Work* getWorkByIndex(uint64_t index);
// Helper function to find a work item in the output work queue by timestamp.
C2Work* getWorkByTimestamp(int64_t timestamp);
// Helper function to determine if the specified |work| item is finished.
bool isWorkDone(const C2Work& work) const;
// Notify the listener the specified |work| item is finished.
void reportWork(std::unique_ptr<C2Work> work);
// Attempt to start the V4L2 device poller.
bool startDevicePoll();
// Attempt to stop the V4L2 device poller.
bool stopDevicePoll();
// Called by the V4L2 device poller on the |mEncoderTaskRunner| whenever an error occurred.
void onPollError();
// Service I/O on the V4L2 device, called by the V4L2 device poller on the |mEncoderTaskRunner|.
void serviceDeviceTask(bool event);
// Enqueue an input buffer to be encoded on the device input queue. Returns whether the
// operation was successful.
bool enqueueInputBuffer(std::unique_ptr<InputFrame> frame, media::VideoPixelFormat format,
const std::vector<VideoFramePlane>& planes, int64_t index,
int64_t timestamp);
// Enqueue an output buffer to store the encoded bitstream on the device output queue. Returns
// whether the operation was successful.
bool enqueueOutputBuffer();
// Dequeue an input buffer the V4L2 device has finished encoding on the device input queue.
// Returns whether a buffer could be dequeued.
bool dequeueInputBuffer();
// Dequeue an output buffer containing the encoded bitstream from the device output queue. The
// bitstream is copied into another buffer that is sent to the client, after which the output
// buffer is returned to the queue. Returns whether the operation was successful.
bool dequeueOutputBuffer();
// Create input buffers on the V4L2 device input queue.
bool createInputBuffers();
// Create output buffers on the V4L2 device output queue.
bool createOutputBuffers();
// Destroy the input buffers on the V4L2 device input queue.
void destroyInputBuffers();
// Destroy the output buffers on the V4L2 device output queue.
void destroyOutputBuffers();
// Copy the encoded data stream in |outputBuffer| to the specified |outputBlock|. If required
// stream headers will be injected into key frames. Returns the size in bytes of the resulting
// output block.
size_t copyIntoOutputBuffer(scoped_refptr<media::V4L2ReadableBuffer> outputBuffer,
const C2LinearBlock& outputBlock);
// Notify the client an error occurred and switch to the error state.
void reportError(c2_status_t error);
// Change the state of the component.
void setComponentState(ComponentState state);
// Change the state of the encoder, only called on the encoder thread.
void setEncoderState(EncoderState state);
// Get the specified component |state| as string.
static const char* componentStateToString(ComponentState state);
// Get the specified encoder |state| as string.
static const char* encoderStateToString(EncoderState state);
// The component's registered name.
const C2String mName;
// The component's id, provided by the C2 framework upon initialization.
const c2_node_id_t mId = 0;
// The component's interface implementation.
const std::shared_ptr<V4L2EncodeInterface> mInterface;
// Mutex used by the component to synchronize start/stop/reset/release calls, as the codec 2.0
// API can be accessed from any thread.
std::mutex mComponentLock;
// The component's listener to be notified when events occur, only accessed on encoder thread.
std::shared_ptr<Listener> mListener;
// The V4L2 device used to interact with the driver, only accessed on encoder thread.
scoped_refptr<media::V4L2Device> mDevice;
scoped_refptr<media::V4L2Queue> mInputQueue;
scoped_refptr<media::V4L2Queue> mOutputQueue;
// The video stream's visible size.
media::Size mVisibleSize;
// The video stream's coded size.
media::Size mInputCodedSize;
// The input layout configured on the V4L2 device.
std::optional<media::VideoFrameLayout> mInputLayout;
// An input format convertor will be used if the device doesn't support the video's format.
std::unique_ptr<FormatConverter> mInputFormatConverter;
// Required output buffer byte size.
uint32_t mOutputBufferSize = 0;
// The bitrate currently configured on the v4l2 device.
uint32_t mBitrate = 0;
// The framerate currently configured on the v4l2 device.
uint32_t mFramerate = 0;
// How often we want to request the V4L2 device to create a key frame.
uint32_t mKeyFramePeriod = 0;
// Key frame counter, a key frame will be requested each time it reaches zero.
uint32_t mKeyFrameCounter = 0;
// Whether we need to manually cache and prepend the SPS and PPS to each IDR frame. When
// encoding H.264 we prepend each IDR with SPS and PPS for resilience. Some devices support this
// via the V4L2_CID_MPEG_VIDEO_H264_SPS_PPS_BEFORE_IDR control. For devices without support for
// this control we cache the latest SPS and PPS and manually inject them into the stream before
// every IDR.
bool mInjectParamsBeforeIDR = false;
// The latest cached SPS (without H.264 start code).
std::vector<uint8_t> mCachedSPS;
// The latest cached PPS (without H.264 start code).
std::vector<uint8_t> mCachedPPS;
// Whether we extracted and submitted CSD (codec-specific data, e.g. H.264 SPS) to the framework.
bool mCSDSubmitted = false;
// The queue of encode work items to be processed.
std::queue<std::unique_ptr<C2Work>> mInputWorkQueue;
// The queue of encode work items currently being processed.
std::deque<std::unique_ptr<C2Work>> mOutputWorkQueue;
// List of work item indices and frames associated with each buffer in the device input queue.
std::vector<std::pair<int64_t, std::unique_ptr<InputFrame>>> mInputBuffersMap;
// The output block pool.
std::shared_ptr<C2BlockPool> mOutputBlockPool;
// The component state, accessible from any thread as C2Component interface is not thread-safe.
std::atomic<ComponentState> mComponentState;
// The current state of the encoder, only accessed on the encoder thread.
EncoderState mEncoderState = EncoderState::UNINITIALIZED;
// The encoder thread on which all interaction with the V4L2 device is performed.
::base::Thread mEncoderThread{"V4L2EncodeComponentThread"};
// The task runner on the encoder thread.
scoped_refptr<::base::SequencedTaskRunner> mEncoderTaskRunner;
// The WeakPtrFactory used to get weak pointers of this.
::base::WeakPtr<V4L2EncodeComponent> mWeakThis;
::base::WeakPtrFactory<V4L2EncodeComponent> mWeakThisFactory{this};
};
} // namespace android
#endif // ANDROID_V4L2_CODEC2_COMPONENTS_V4L2_ENCODE_COMPONENT_H