// Copyright 2013 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 CONTENT_COMMON_GPU_MEDIA_EXYNOS_VIDEO_ENCODE_ACCELERATOR_H_
#define CONTENT_COMMON_GPU_MEDIA_EXYNOS_VIDEO_ENCODE_ACCELERATOR_H_
#include <list>
#include <vector>
#include "base/memory/linked_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread.h"
#include "content/common/content_export.h"
#include "media/video/video_encode_accelerator.h"
#include "ui/gfx/size.h"
namespace base {
class MessageLoopProxy;
} // namespace base
namespace media {
class BitstreamBuffer;
} // namespace media
namespace content {
// This class handles Exynos video encode acceleration by interfacing with the
// V4L2 devices exported by the Multi Format Codec and GScaler hardware blocks
// on the Exynos platform. The threading model of this class is the same as the
// ExynosVideoDecodeAccelerator (from which class this was designed).
class CONTENT_EXPORT ExynosVideoEncodeAccelerator
: public media::VideoEncodeAccelerator {
public:
ExynosVideoEncodeAccelerator();
virtual ~ExynosVideoEncodeAccelerator();
// media::VideoEncodeAccelerator implementation.
virtual bool Initialize(media::VideoFrame::Format format,
const gfx::Size& input_visible_size,
media::VideoCodecProfile output_profile,
uint32 initial_bitrate,
Client* client) OVERRIDE;
virtual void Encode(const scoped_refptr<media::VideoFrame>& frame,
bool force_keyframe) OVERRIDE;
virtual void UseOutputBitstreamBuffer(
const media::BitstreamBuffer& buffer) OVERRIDE;
virtual void RequestEncodingParametersChange(uint32 bitrate,
uint32 framerate) OVERRIDE;
virtual void Destroy() OVERRIDE;
static std::vector<media::VideoEncodeAccelerator::SupportedProfile>
GetSupportedProfiles();
private:
// Auto-destroy reference for BitstreamBuffer, for tracking buffers passed to
// this instance.
struct BitstreamBufferRef;
// Record for GSC input buffers.
struct GscInputRecord {
GscInputRecord();
bool at_device;
scoped_refptr<media::VideoFrame> frame;
};
// Record for GSC output buffers.
struct GscOutputRecord {
GscOutputRecord();
bool at_device;
int mfc_input;
};
// Record for MFC input buffers.
struct MfcInputRecord {
MfcInputRecord();
bool at_device;
int fd[2];
};
// Record for MFC output buffers.
struct MfcOutputRecord {
MfcOutputRecord();
bool at_device;
linked_ptr<BitstreamBufferRef> buffer_ref;
void* address;
size_t length;
};
enum {
kInitialFramerate = 30,
// These are rather subjectively tuned.
kGscInputBufferCount = 2,
kGscOutputBufferCount = 2,
kMfcOutputBufferCount = 2,
// MFC hardware does not report required output buffer size correctly.
// Use maximum theoretical size to avoid hanging the hardware.
kMfcOutputBufferSize = (2 * 1024 * 1024),
};
// Internal state of the encoder.
enum State {
kUninitialized, // Initialize() not yet called.
kInitialized, // Initialize() returned true; ready to start encoding.
kEncoding, // Encoding frames.
kError, // Error in encoder state.
};
//
// Encoding tasks, to be run on encode_thread_.
//
// Encode a GSC input buffer.
void EncodeTask(const scoped_refptr<media::VideoFrame>& frame,
bool force_keyframe);
// Add a BitstreamBuffer to the queue of buffers ready to be used for encoder
// output.
void UseOutputBitstreamBufferTask(scoped_ptr<BitstreamBufferRef> buffer_ref);
// Device destruction task.
void DestroyTask();
// Service I/O on the V4L2 devices. This task should only be scheduled from
// DevicePollTask().
void ServiceDeviceTask();
// Handle the various device queues.
void EnqueueGsc();
void DequeueGsc();
void EnqueueMfc();
void DequeueMfc();
// Enqueue a buffer on the corresponding queue. Returns false on fatal error.
bool EnqueueGscInputRecord();
bool EnqueueGscOutputRecord();
bool EnqueueMfcInputRecord();
bool EnqueueMfcOutputRecord();
// Attempt to start/stop device_poll_thread_.
bool StartDevicePoll();
bool StopDevicePoll();
// Set/clear the device poll interrupt (using device_poll_interrupt_fd_).
bool SetDevicePollInterrupt();
bool ClearDevicePollInterrupt();
//
// Device tasks, to be run on device_poll_thread_.
//
// The device task.
void DevicePollTask(unsigned int poll_fds);
//
// Safe from any thread.
//
// Error notification (using PostTask() to child thread, if necessary).
void NotifyError(Error error);
// Set the encoder_thread_ state (using PostTask to encoder thread, if
// necessary).
void SetEncoderState(State state);
//
// Other utility functions. Called on encoder_thread_, unless
// encoder_thread_ is not yet started, in which case the child thread can call
// these (e.g. in Initialize() or Destroy()).
//
// Change the parameters of encoding.
void RequestEncodingParametersChangeTask(uint32 bitrate, uint32 framerate);
// Create the buffers we need.
bool CreateGscInputBuffers();
bool CreateGscOutputBuffers();
bool SetMfcFormats();
bool InitMfcControls();
bool CreateMfcInputBuffers();
bool CreateMfcOutputBuffers();
// Destroy these buffers.
void DestroyGscInputBuffers();
void DestroyGscOutputBuffers();
void DestroyMfcInputBuffers();
void DestroyMfcOutputBuffers();
// Our original calling message loop for the child thread.
const scoped_refptr<base::MessageLoopProxy> child_message_loop_proxy_;
// WeakPtr<> pointing to |this| for use in posting tasks from the encoder or
// device worker threads back to the child thread. Because the worker threads
// are members of this class, any task running on those threads is guaranteed
// that this object is still alive. As a result, tasks posted from the child
// thread to the encoder or device thread should use base::Unretained(this),
// and tasks posted the other way should use |weak_this_|.
base::WeakPtrFactory<ExynosVideoEncodeAccelerator> weak_this_ptr_factory_;
base::WeakPtr<ExynosVideoEncodeAccelerator> weak_this_;
// To expose client callbacks from VideoEncodeAccelerator.
// NOTE: all calls to these objects *MUST* be executed on
// child_message_loop_proxy_.
scoped_ptr<base::WeakPtrFactory<Client> > client_ptr_factory_;
base::WeakPtr<Client> client_;
//
// Encoder state, owned and operated by encoder_thread_.
// Before encoder_thread_ has started, the encoder state is managed by
// the child (main) thread. After encoder_thread_ has started, the encoder
// thread should be the only one managing these.
//
// This thread services tasks posted from the VEA API entry points by the
// child thread and device service callbacks posted from the device thread.
base::Thread encoder_thread_;
// Encoder state.
State encoder_state_;
// The visible/allocated sizes of the input frame.
gfx::Size input_visible_size_;
gfx::Size input_allocated_size_;
// The visible/allocated sizes of the color-converted intermediate frame.
gfx::Size converted_visible_size_;
gfx::Size converted_allocated_size_;
// The logical visible size of the output frame.
gfx::Size output_visible_size_;
// The required byte size of output BitstreamBuffers.
size_t output_buffer_byte_size_;
// We need to provide the stream header with every keyframe, to allow
// midstream decoding restarts. Store it here.
scoped_ptr<uint8[]> stream_header_;
size_t stream_header_size_;
// V4L2 formats for input frames and the output stream.
uint32 input_format_fourcc_;
uint32 output_format_fourcc_;
// Video frames ready to be encoded.
std::list<scoped_refptr<media::VideoFrame> > encoder_input_queue_;
// GSC color conversion device.
int gsc_fd_;
// GSC input queue state.
bool gsc_input_streamon_;
// GSC input buffers enqueued to device.
int gsc_input_buffer_queued_count_;
// GSC input buffers ready to use; LIFO since we don't care about ordering.
std::vector<int> gsc_free_input_buffers_;
// Mapping of int index to GSC input buffer record.
std::vector<GscInputRecord> gsc_input_buffer_map_;
// GSC output queue state.
bool gsc_output_streamon_;
// GSC output buffers enqueued to device.
int gsc_output_buffer_queued_count_;
// GSC output buffers ready to use; LIFO since we don't care about ordering.
std::vector<int> gsc_free_output_buffers_;
// Mapping of int index to GSC output buffer record.
std::vector<GscOutputRecord> gsc_output_buffer_map_;
// MFC input buffers filled by GSC, waiting to be queued to MFC.
std::list<int> mfc_ready_input_buffers_;
// MFC video encoding device.
int mfc_fd_;
// MFC input queue state.
bool mfc_input_streamon_;
// MFC input buffers enqueued to device.
int mfc_input_buffer_queued_count_;
// MFC input buffers ready to use; LIFO since we don't care about ordering.
std::vector<int> mfc_free_input_buffers_;
// Mapping of int index to MFC input buffer record.
std::vector<MfcInputRecord> mfc_input_buffer_map_;
// MFC output queue state.
bool mfc_output_streamon_;
// MFC output buffers enqueued to device.
int mfc_output_buffer_queued_count_;
// MFC output buffers ready to use; LIFO since we don't care about ordering.
std::vector<int> mfc_free_output_buffers_;
// Mapping of int index to MFC output buffer record.
std::vector<MfcOutputRecord> mfc_output_buffer_map_;
// Bitstream buffers ready to be used to return encoded output, as a LIFO
// since we don't care about ordering.
std::vector<linked_ptr<BitstreamBufferRef> > encoder_output_queue_;
//
// The device polling thread handles notifications of V4L2 device changes.
// TODO(sheu): replace this thread with an TYPE_IO encoder_thread_.
//
// The thread.
base::Thread device_poll_thread_;
// eventfd fd to signal device poll thread when its poll() should be
// interrupted.
int device_poll_interrupt_fd_;
DISALLOW_COPY_AND_ASSIGN(ExynosVideoEncodeAccelerator);
};
} // namespace content
#endif // CONTENT_COMMON_GPU_MEDIA_EXYNOS_VIDEO_ENCODE_ACCELERATOR_H_