blob: 7bb41bba94be84b8194ae05aa0c59188d2e3dff4 [file] [log] [blame]
// Copyright (c) 2012 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.
//
// This file contains an implementation of VideoDecoderAccelerator
// that utilizes the hardware video decoder present on the Exynos SoC.
#ifndef CONTENT_COMMON_GPU_MEDIA_EXYNOS_VIDEO_DECODE_ACCELERATOR_H_
#define CONTENT_COMMON_GPU_MEDIA_EXYNOS_VIDEO_DECODE_ACCELERATOR_H_
#include <list>
#include <vector>
#include "base/callback_forward.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
#include "base/threading/thread.h"
#include "content/common/content_export.h"
#include "content/common/gpu/media/video_decode_accelerator_impl.h"
#include "media/base/limits.h"
#include "media/base/video_decoder_config.h"
#include "ui/gfx/size.h"
#include "ui/gl/gl_bindings.h"
namespace base {
class MessageLoopProxy;
}
namespace content {
class H264Parser;
// This class handles Exynos video acceleration directly through the V4L2
// devices exported by the Multi Format Codec and GScaler hardware blocks.
//
// The threading model of this class is driven by the fact that it needs to
// interface two fundamentally different event queues -- the one Chromium
// provides through MessageLoop, and the one driven by the V4L2 devices which
// is waited on with epoll(). There are three threads involved in this class:
//
// * The child thread, which is the main GPU process thread which calls the
// media::VideoDecodeAccelerator entry points. Calls from this thread
// generally do not block (with the exception of Initialize() and Destroy()).
// They post tasks to the decoder_thread_, which actually services the task
// and calls back when complete through the
// media::VideoDecodeAccelerator::Client interface.
// * The decoder_thread_, owned by this class. It services API tasks, through
// the *Task() routines, as well as V4L2 device events, through
// ServiceDeviceTask(). Almost all state modification is done on this thread.
// * The device_poll_thread_, owned by this class. All it does is epoll() on
// the V4L2 in DevicePollTask() and schedule a ServiceDeviceTask() on the
// decoder_thread_ when something interesting happens.
// TODO(sheu): replace this thread with an TYPE_IO decoder_thread_.
//
// Note that this class has no locks! Everything's serviced on the
// decoder_thread_, so there are no synchronization issues.
// ... well, there are, but it's a matter of getting messages posted in the
// right order, not fiddling with locks.
class CONTENT_EXPORT ExynosVideoDecodeAccelerator
: public VideoDecodeAcceleratorImpl {
public:
ExynosVideoDecodeAccelerator(
EGLDisplay egl_display,
EGLContext egl_context,
Client* client,
const base::WeakPtr<Client>& io_client_,
const base::Callback<bool(void)>& make_context_current,
const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy);
virtual ~ExynosVideoDecodeAccelerator();
// media::VideoDecodeAccelerator implementation.
// Note: Initialize() and Destroy() are synchronous.
virtual bool Initialize(media::VideoCodecProfile profile) OVERRIDE;
virtual void Decode(const media::BitstreamBuffer& bitstream_buffer) OVERRIDE;
virtual void AssignPictureBuffers(
const std::vector<media::PictureBuffer>& buffers) OVERRIDE;
virtual void ReusePictureBuffer(int32 picture_buffer_id) OVERRIDE;
virtual void Flush() OVERRIDE;
virtual void Reset() OVERRIDE;
virtual void Destroy() OVERRIDE;
// VideoDecodeAcceleratorImpl implementation.
virtual bool CanDecodeOnIOThread() OVERRIDE;
// Do any necessary initialization before the sandbox is enabled.
static void PreSandboxInitialization();
// Lazily initialize static data after sandbox is enabled. Return false on
// init failure.
static bool PostSandboxInitialization();
private:
// These are rather subjectively tuned.
enum {
kMfcInputBufferCount = 8,
// TODO(posciak): determine MFC input buffer size based on level limits.
// See http://crbug.com/255116.
kMfcInputBufferMaxSize = 1024 * 1024,
kGscInputBufferCount = 4,
// Number of output buffers to use for each VDA stage above what's required
// by the decoder (e.g. DPB size, in H264). We need
// media::limits::kMaxVideoFrames to fill up the GpuVideoDecode pipeline,
// and +1 for a frame in transit.
kDpbOutputBufferExtraCount = media::limits::kMaxVideoFrames + 1,
};
// Internal state of the decoder.
enum State {
kUninitialized, // Initialize() not yet called.
kInitialized, // Initialize() returned true; ready to start decoding.
kDecoding, // DecodeBufferInitial() successful; decoding frames.
kResetting, // Presently resetting.
kAfterReset, // After Reset(), ready to start decoding again.
kChangingResolution, // Performing resolution change, all remaining
// pre-change frames decoded and processed.
kError, // Error in kDecoding state.
};
enum BufferId {
kFlushBufferId = -2 // Buffer id for flush buffer, queued by FlushTask().
};
// File descriptors we need to poll.
enum PollFds {
kPollMfc = (1 << 0),
kPollGsc = (1 << 1),
};
// Auto-destruction reference for BitstreamBuffer, for message-passing from
// Decode() to DecodeTask().
struct BitstreamBufferRef;
// Auto-destruction reference for an array of PictureBuffer, for
// message-passing from AssignPictureBuffers() to AssignPictureBuffersTask().
struct PictureBufferArrayRef;
// Auto-destruction reference for EGLSync (for message-passing).
struct EGLSyncKHRRef;
// Record for MFC input buffers.
struct MfcInputRecord {
MfcInputRecord();
~MfcInputRecord();
bool at_device; // held by device.
void* address; // mmap() address.
size_t length; // mmap() length.
off_t bytes_used; // bytes filled in the mmap() segment.
int32 input_id; // triggering input_id as given to Decode().
};
// Record for MFC output buffers.
struct MfcOutputRecord {
MfcOutputRecord();
~MfcOutputRecord();
bool at_device; // held by device.
size_t bytes_used[2]; // bytes used in each dmabuf.
void* address[2]; // mmap() address for each plane.
size_t length[2]; // mmap() length for each plane.
int32 input_id; // triggering input_id as given to Decode().
};
// Record for GSC input buffers.
struct GscInputRecord {
GscInputRecord();
~GscInputRecord();
bool at_device; // held by device.
int mfc_output; // MFC output buffer index to recycle when this input
// is complete.
};
// Record for GSC output buffers.
struct GscOutputRecord {
GscOutputRecord();
~GscOutputRecord();
bool at_device; // held by device.
bool at_client; // held by client.
int fd; // file descriptor from backing EGLImage.
EGLImageKHR egl_image; // backing EGLImage.
EGLSyncKHR egl_sync; // sync the compositor's use of the EGLImage.
int32 picture_id; // picture buffer id as returned to PictureReady().
};
//
// Decoding tasks, to be run on decode_thread_.
//
// Enqueue a BitstreamBuffer to decode. This will enqueue a buffer to the
// decoder_input_queue_, then queue a DecodeBufferTask() to actually decode
// the buffer.
void DecodeTask(const media::BitstreamBuffer& bitstream_buffer);
// Decode from the buffers queued in decoder_input_queue_. Calls
// DecodeBufferInitial() or DecodeBufferContinue() as appropriate.
void DecodeBufferTask();
// Advance to the next fragment that begins a frame.
bool AdvanceFrameFragment(const uint8* data, size_t size, size_t* endpos);
// Schedule another DecodeBufferTask() if we're behind.
void ScheduleDecodeBufferTaskIfNeeded();
// Return true if we should continue to schedule DecodeBufferTask()s after
// completion. Store the amount of input actually consumed in |endpos|.
bool DecodeBufferInitial(const void* data, size_t size, size_t* endpos);
bool DecodeBufferContinue(const void* data, size_t size);
// Accumulate data for the next frame to decode. May return false in
// non-error conditions; for example when pipeline is full and should be
// retried later.
bool AppendToInputFrame(const void* data, size_t size);
// Flush data for one decoded frame.
bool FlushInputFrame();
// Process an AssignPictureBuffers() API call. After this, the
// device_poll_thread_ can be started safely, since we have all our
// buffers.
void AssignPictureBuffersTask(scoped_ptr<PictureBufferArrayRef> pic_buffers);
// Service I/O on the V4L2 devices. This task should only be scheduled from
// DevicePollTask(). If |mfc_event_pending| is true, one or more events
// on MFC file descriptor are pending.
void ServiceDeviceTask(bool mfc_event_pending);
// Handle the various device queues.
void EnqueueMfc();
void DequeueMfc();
void EnqueueGsc();
void DequeueGsc();
// Handle incoming MFC events.
void DequeueMfcEvents();
// Enqueue a buffer on the corresponding queue.
bool EnqueueMfcInputRecord();
bool EnqueueMfcOutputRecord();
bool EnqueueGscInputRecord();
bool EnqueueGscOutputRecord();
// Process a ReusePictureBuffer() API call. The API call create an EGLSync
// object on the main (GPU process) thread; we will record this object so we
// can wait on it before reusing the buffer.
void ReusePictureBufferTask(int32 picture_buffer_id,
scoped_ptr<EGLSyncKHRRef> egl_sync_ref);
// Flush() task. Child thread should not submit any more buffers until it
// receives the NotifyFlushDone callback. This task will schedule an empty
// BitstreamBufferRef (with input_id == kFlushBufferId) to perform the flush.
void FlushTask();
// Notify the client of a flush completion, if required. This should be
// called any time a relevant queue could potentially be emptied: see
// function definition.
void NotifyFlushDoneIfNeeded();
// Reset() task. This task will schedule a ResetDoneTask() that will send
// the NotifyResetDone callback, then set the decoder state to kResetting so
// that all intervening tasks will drain.
void ResetTask();
// ResetDoneTask() will set the decoder state back to kAfterReset, so
// subsequent decoding can continue.
void ResetDoneTask();
// Device destruction task.
void DestroyTask();
// Attempt to start/stop device_poll_thread_.
bool StartDevicePoll();
// If |keep_mfc_input_state| is true, don't reset MFC input state; used during
// resolution change.
bool StopDevicePoll(bool keep_mfc_input_state);
// Set/clear the device poll interrupt (using device_poll_interrupt_fd_).
bool SetDevicePollInterrupt();
bool ClearDevicePollInterrupt();
void StartResolutionChangeIfNeeded();
void FinishResolutionChange();
void ResumeAfterResolutionChange();
// Try to get output format from MFC, detected after parsing the beginning
// of the stream. Sets |again| to true if more parsing is needed.
bool GetFormatInfo(struct v4l2_format* format, bool* again);
// Create MFC output and GSC input and output buffers for the given |format|.
bool CreateBuffersForFormat(const struct v4l2_format& format);
//
// 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 decoder_thread_ state (using PostTask to decoder thread, if
// necessary).
void SetDecoderState(State state);
//
// Other utility functions. Called on decoder_thread_, unless
// decoder_thread_ is not yet started, in which case the child thread can call
// these (e.g. in Initialize() or Destroy()).
//
// Create the buffers we need.
bool CreateMfcInputBuffers();
bool CreateMfcOutputBuffers();
bool CreateGscInputBuffers();
bool CreateGscOutputBuffers();
//
// Methods run on child thread.
//
// Destroy buffers.
void DestroyMfcInputBuffers();
void DestroyMfcOutputBuffers();
void DestroyGscInputBuffers();
void DestroyGscOutputBuffers();
void ResolutionChangeDestroyBuffers();
// Our original calling message loop for the child thread.
scoped_refptr<base::MessageLoopProxy> child_message_loop_proxy_;
// Message loop of the IO thread.
scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
// WeakPtr<> pointing to |this| for use in posting tasks from the decoder 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 decoder or device thread should use base::Unretained(this),
// and tasks posted the other way should use |weak_this_|.
base::WeakPtr<ExynosVideoDecodeAccelerator> weak_this_;
// To expose client callbacks from VideoDecodeAccelerator.
// NOTE: all calls to these objects *MUST* be executed on
// child_message_loop_proxy_.
base::WeakPtrFactory<Client> client_ptr_factory_;
base::WeakPtr<Client> client_;
// Callbacks to |io_client_| must be executed on |io_message_loop_proxy_|.
base::WeakPtr<Client> io_client_;
//
// Decoder state, owned and operated by decoder_thread_.
// Before decoder_thread_ has started, the decoder state is managed by
// the child (main) thread. After decoder_thread_ has started, the decoder
// thread should be the only one managing these.
//
// This thread services tasks posted from the VDA API entry points by the
// child thread and device service callbacks posted from the device thread.
base::Thread decoder_thread_;
// Decoder state machine state.
State decoder_state_;
// BitstreamBuffer we're presently reading.
scoped_ptr<BitstreamBufferRef> decoder_current_bitstream_buffer_;
// FlushTask() and ResetTask() should not affect buffers that have been
// queued afterwards. For flushing or resetting the pipeline then, we will
// delay these buffers until after the flush or reset completes.
int decoder_delay_bitstream_buffer_id_;
// MFC input buffer we're presently filling.
int decoder_current_input_buffer_;
// We track the number of buffer decode tasks we have scheduled, since each
// task execution should complete one buffer. If we fall behind (due to
// resource backpressure, etc.), we'll have to schedule more to catch up.
int decoder_decode_buffer_tasks_scheduled_;
// Picture buffers held by the client.
int decoder_frames_at_client_;
// Are we flushing?
bool decoder_flushing_;
// Got a notification from driver that it reached resolution change point
// in the stream.
bool resolution_change_pending_;
// Got a reset request while we were performing resolution change.
bool resolution_change_reset_pending_;
// Input queue for decoder_thread_: BitstreamBuffers in.
std::list<linked_ptr<BitstreamBufferRef> > decoder_input_queue_;
// For H264 decode, hardware requires that we send it frame-sized chunks.
// We'll need to parse the stream.
scoped_ptr<content::H264Parser> decoder_h264_parser_;
// Set if the decoder has a pending incomplete frame in an input buffer.
bool decoder_partial_frame_pending_;
//
// Hardware state and associated queues. Since decoder_thread_ services
// the hardware, decoder_thread_ owns these too.
//
// Completed decode buffers, waiting for MFC.
std::list<int> mfc_input_ready_queue_;
// MFC decode device.
int mfc_fd_;
// MFC input buffer state.
bool mfc_input_streamon_;
// MFC input buffers enqueued to device.
int mfc_input_buffer_queued_count_;
// Input buffers ready to use, as a 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 buffer state.
bool mfc_output_streamon_;
// MFC output buffers enqueued to device.
int mfc_output_buffer_queued_count_;
// Output buffers ready to use, as a 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_;
// Required size of MFC output buffers. Two sizes for two planes.
size_t mfc_output_buffer_size_[2];
uint32 mfc_output_buffer_pixelformat_;
// Required size of DPB for decoding.
int mfc_output_dpb_size_;
// Completed MFC outputs, waiting for GSC.
std::list<int> mfc_output_gsc_input_queue_;
// GSC decode device.
int gsc_fd_;
// GSC input buffer state.
bool gsc_input_streamon_;
// GSC input buffers enqueued to device.
int gsc_input_buffer_queued_count_;
// Input buffers ready to use, as a 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 buffer state.
bool gsc_output_streamon_;
// GSC output buffers enqueued to device.
int gsc_output_buffer_queued_count_;
// Output buffers ready to use. We need a FIFO here.
std::list<int> gsc_free_output_buffers_;
// Mapping of int index to GSC output buffer record.
std::vector<GscOutputRecord> gsc_output_buffer_map_;
// Output picture size.
gfx::Size frame_buffer_size_;
//
// The device polling thread handles notifications of V4L2 device changes.
//
// 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_;
//
// Other state, held by the child (main) thread.
//
// Make our context current before running any EGL entry points.
base::Callback<bool(void)> make_context_current_;
// EGL state
EGLDisplay egl_display_;
EGLContext egl_context_;
// The codec we'll be decoding for.
media::VideoCodecProfile video_profile_;
DISALLOW_COPY_AND_ASSIGN(ExynosVideoDecodeAccelerator);
};
} // namespace content
#endif // CONTENT_COMMON_GPU_MEDIA_EXYNOS_VIDEO_DECODE_ACCELERATOR_H_