Index: content/common/gpu/media/exynos_video_decode_accelerator.h |
diff --git a/content/common/gpu/media/exynos_video_decode_accelerator.h b/content/common/gpu/media/exynos_video_decode_accelerator.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1fafd8a73dba2426aae1b360841e385f875995a6 |
--- /dev/null |
+++ b/content/common/gpu/media/exynos_video_decode_accelerator.h |
@@ -0,0 +1,427 @@ |
+// 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 "media/base/video_decoder_config.h" |
+#include "media/video/video_decode_accelerator.h" |
+#include "third_party/angle/include/EGL/egl.h" |
+#include "third_party/angle/include/EGL/eglext.h" |
+#include "ui/gfx/size.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. |
Ami GONE FROM CHROMIUM
2013/01/11 00:46:33
Please add a TODO to kill this thread (TODO in thi
sheu
2013/01/11 10:41:42
Done.
|
+// |
+// 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 media::VideoDecodeAccelerator { |
+ public: |
+ ExynosVideoDecodeAccelerator( |
+ EGLDisplay egl_display, |
+ EGLContext egl_context, |
+ Client* client, |
+ const base::Callback<bool(void)>& make_context_current); |
+ 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; |
+ |
+ // 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, |
+ kMfcOutputBufferExtraCount = 5, // number of buffers above request by V4L2. |
+ kMfcInputBufferMaxSize = 512 * 1024, |
+ kGscInputBufferCount = 6, |
+ kGscOutputBufferCount = 6, |
+ }; |
+ |
+ // Internal state of the decoder. |
+ enum State { |
+ kUninitialized, // Initialize() not yet called. |
+ kInitialized, // Initialize() returned. Ready to start decoding. |
Ami GONE FROM CHROMIUM
2013/01/11 00:46:33
s/returned/returned true/
sheu
2013/01/11 10:41:42
Done.
|
+ kDecoding, // DecodeBufferInitial() successful; decoding frames. |
+ kResetting, // Presently resetting. |
+ kAfterReset, // After Reset(), ready to start decoding again. |
+ 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 |
+ // DecodeBufferTask() to actually decode the buffer. |
+ void DecodeTask(scoped_ptr<BitstreamBufferRef> bitstream_record); |
+ |
+ // Decode from the queued BitstreamBuffers. Calls DecodeBufferInitial() |
+ // or DecodeBufferContinue(). |
+ void DecodeBufferTask(); |
+ // Find the extents of one frame fragment to push to HW. |
+ bool FindFrameFragment(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, size_t* endpos); |
+ |
+ // 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(). |
+ void ServiceDeviceTask(); |
+ // Handle the various device queues. |
+ void EnqueueMfc(); |
+ void DequeueMfc(); |
+ void EnqueueGsc(); |
+ void DequeueGsc(); |
+ // Notify the client of a flush completion, maybe. |
+ void NotifyFlushDoneIfNeeded(); |
+ // 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. |
+ void FlushTask(); |
+ |
+ // 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(); |
+ 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 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(); |
+ |
+ // Destroy these buffers. |
+ void DestroyMfcInputBuffers(); |
+ void DestroyMfcOutputBuffers(); |
+ void DestroyGscInputBuffers(); |
+ void DestroyGscOutputBuffers(); |
+ |
+ // Our original calling message loop for the child thread. |
+ scoped_refptr<base::MessageLoopProxy> child_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_; |
+ |
+ // |
+ // 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_; |
+ // BitstreamBuffer that we should delay decoding, during Reset() or Flush(). |
+ 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_; |
+ // 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_; |
+ |
+ // |
+ // 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, total. |
+ int mfc_input_buffer_count_; |
+ // 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, total. |
+ int mfc_output_buffer_count_; |
+ // 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_; |
+ |
+ // 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, total. |
+ int gsc_input_buffer_count_; |
+ // 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, total. |
+ int gsc_output_buffer_count_; |
+ // 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_ |