Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(192)

Side by Side Diff: content/common/gpu/media/exynos_video_decode_accelerator.h

Issue 11198060: VDA implementation for Exynos, using V4L2 (Closed) Base URL: https://git.chromium.org/git/chromium/src@git-svn
Patch Set: Replaced one LOG() with DLOG() per posciak@ Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // This file contains an implementation of VideoDecoderAccelerator
6 // that utilizes hardware video decoder present on the Exynos SoC.
Ami GONE FROM CHROMIUM 2013/01/04 01:57:05 s/hardware/the hardware/
sheu 2013/01/05 00:51:49 Done.
7
8 #ifndef CONTENT_COMMON_GPU_MEDIA_EXYNOS_VIDEO_DECODE_ACCELERATOR_H_
9 #define CONTENT_COMMON_GPU_MEDIA_EXYNOS_VIDEO_DECODE_ACCELERATOR_H_
10
11 #include <deque>
12 #include <vector>
13
14 #include "base/callback_forward.h"
15 #include "base/memory/linked_ptr.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/threading/thread.h"
18 #include "content/common/content_export.h"
19 #include "media/base/video_decoder_config.h"
20 #include "media/video/video_decode_accelerator.h"
21 #include "third_party/angle/include/EGL/egl.h"
22 #include "third_party/angle/include/EGL/eglext.h"
23 #include "ui/gfx/size.h"
24
25 namespace base {
26 class MessageLoopProxy;
27 }
28
29 namespace content {
30 class H264Parser;
31
32 // This class handles Exynos video acceleration directly through the V4L2
33 // devices exported by the Multi Format Codec and GScaler hardware blocks.
34 //
35 // The threading model of this class is driven by the fact that it needs to
36 // interface two fundamentally different event queues -- the one Chromium
37 // provides through MessageLoop, and the one driven by the V4L2 devices which
38 // is waited on with epoll(). There are three threads involved in this class:
39 //
40 // * The child thread, which is the main GPU process thread which calls the
41 // media::VideoDecodeAccelerator entry points. Calls from this thread
42 // generally do not block (with the exception of Initialize() and Destroy()).
43 // They post tasks to the decoder_thread_, which actually services the task
44 // and calls back when complete through the
45 // media::VideoDecodeAccelerator::Client interface.
46 // * The decoder_thread_, owned by this class. It services API tasks, through
47 // the *Task() routines, as well as V4L2 device events, through
48 // ServiceDeviceTask(). Almost all state modification is done on this thread.
49 // * The device_poll_thread_, owned by this class. All it does is epoll() on
50 // the V4L2 in DevicePollTask() and schedule a ServiceDeviceTask() on the
51 // decoder_thread_ when something interesting happens.
Ami GONE FROM CHROMIUM 2013/01/04 01:57:05 Can this thread be eliminated entirely (and with i
sheu 2013/01/05 00:51:49 That's an interesting idea actually, but in the in
52 //
53 // Note that this class has no locks! Everything's serviced on the
54 // decoder_thread_, so there are no synchronization issues.
55 // ... well, there are, but it's a matter of getting messages posted in the
56 // right order, not fiddling with locks.
57 class CONTENT_EXPORT ExynosVideoDecodeAccelerator :
58 public media::VideoDecodeAccelerator {
59 public:
60 ExynosVideoDecodeAccelerator(
61 EGLDisplay egl_display,
62 EGLContext egl_context,
63 Client* client,
64 const base::Callback<bool(void)>& make_context_current);
65 virtual ~ExynosVideoDecodeAccelerator();
66
67 // media::VideoDecodeAccelerator implementation.
68 // Note: Initialize() and Destroy() are synchronous.
69 virtual bool Initialize(media::VideoCodecProfile profile) OVERRIDE;
70 virtual void Decode(const media::BitstreamBuffer& bitstream_buffer) OVERRIDE;
71 virtual void AssignPictureBuffers(
72 const std::vector<media::PictureBuffer>& buffers) OVERRIDE;
73 virtual void ReusePictureBuffer(int32 picture_buffer_id) OVERRIDE;
74 virtual void Flush() OVERRIDE;
75 virtual void Reset() OVERRIDE;
76 virtual void Destroy() OVERRIDE;
77
78 // Do any necessary initialization before the sandbox is enabled.
79 static void PreSandboxInitialization();
80
81 // Lazily initialize static data after sandbox is enabled. Return false on
82 // init failure.
83 static bool PostSandboxInitialization();
84
85 private:
86 // These are rather subjectively tuned.
87 enum {
88 kMfcInputBufferCount = 8,
89 kMfcOutputBufferExtraCount = 5, // number of buffers above request by V4L2.
90 kMfcInputBufferMaxSize = 512 * 1024,
91 kGscInputBufferCount = 6,
92 kGscOutputBufferCount = 6,
93 };
94
95 // Internal state of the decoder.
96 enum State {
97 kUninitialized, // Initialize() not yet called.
98 kInitialized, // Initialize() called. Ready to start decoding.
Ami GONE FROM CHROMIUM 2013/01/04 01:57:05 s/called/returned true/
sheu 2013/01/05 00:51:49 Done.
99 kDecoding, // DecodeBufferInitial() successful; decoding frames.
100 kResetting, // Presently resetting.
101 kAfterReset, // After Reset(), ready to start decoding again.
102 kError, // Error in kDecoding state.
103 };
104
105 // File descriptors we need to poll.
106 enum PollFds {
107 kPollMfc = (1 << 0),
108 kPollGsc = (1 << 1),
109 };
110
111 // Auto-destruction reference for BitstreamBuffer, for message-passing from
112 // Decode() to DecodeTask().
113 struct BitstreamBufferRef {
Ami GONE FROM CHROMIUM 2013/01/04 01:57:05 This and the other inner classes could as well mov
sheu 2013/01/05 00:51:49 That's a good idea actually; I wasn't aware that y
114 BitstreamBufferRef(
115 base::WeakPtr<Client>& client,
116 scoped_refptr<base::MessageLoopProxy>& client_message_loop_proxy,
117 base::SharedMemory* shm,
118 size_t size,
119 int32 input_id);
120 ~BitstreamBufferRef();
121 const base::WeakPtr<Client> client;
122 const scoped_refptr<base::MessageLoopProxy> client_message_loop_proxy;
123 const scoped_ptr<base::SharedMemory> shm;
124 const size_t size;
125 off_t bytes_used;
126 const int32 input_id;
127 };
128
129 // Auto-destruction reference for an array of PictureBuffer, for
130 // message-passing from AssignPictureBuffers() to AssignPictureBuffersTask().
131 struct PictureBufferArrayRef {
132 PictureBufferArrayRef(EGLDisplay egl_display, EGLImageKHR egl_images[],
133 int egl_image_fds[], int32 client_ids[],
134 int egl_images_count);
135 ~PictureBufferArrayRef();
136 EGLDisplay const egl_display;
137 const scoped_ptr<EGLImageKHR[]> egl_images;
Ami GONE FROM CHROMIUM 2013/01/04 01:57:05 does scoped_ptr<[]> really work? IOW does it call
sheu 2013/01/05 00:51:49 I figured I'd just hold one count for all of them.
138 const scoped_ptr<int[]> egl_image_fds;
139 const scoped_ptr<int32[]> client_ids;
140 const int egl_images_count;
141 };
142
143 // Auto-destruction reference for EGLSync (for message-passing).
144 struct EGLSyncKHRRef {
145 EGLSyncKHRRef(EGLDisplay egl_display, EGLSyncKHR egl_sync);
146 ~EGLSyncKHRRef();
147 EGLDisplay const egl_display;
148 EGLSyncKHR egl_sync;
149 };
Ami GONE FROM CHROMIUM 2013/01/04 01:57:05 append newline before next comment
sheu 2013/01/05 00:51:49 Done.
150 // Record for MFC input buffers.
151 struct MfcInputRecord {
152 MfcInputRecord();
153 ~MfcInputRecord();
154 bool at_device; // held by device.
155 void* address; // mmap() address.
156 size_t length; // mmap() length.
157 off_t bytes_used; // bytes filled in the mmap() segment.
158 int32 input_id; // triggering input_id as given to Decode().
159 };
160
161 // Record for MFC output buffers.
162 struct MfcOutputRecord {
163 MfcOutputRecord();
164 ~MfcOutputRecord();
165 bool at_device; // held by device.
166 size_t bytes_used[2]; // bytes used in each dmabuf.
167 void* address[2]; // mmap() address for each plane.
Ami GONE FROM CHROMIUM 2013/01/04 01:57:05 I wish there was a different word to "plane" here
Pawel Osciak 2013/01/04 02:29:27 It is just that (well, almost), those two are a Y-
168 size_t length[2]; // mmap() length for each plane.
169 int32 input_id; // triggering input_id as given to Decode().
170 };
171
172 // Record for GSC input buffers.
173 struct GscInputRecord {
174 GscInputRecord();
175 ~GscInputRecord();
176 bool at_device; // held by device.
177 int mfc_output; // MFC output buffer index to recycle when this input
178 // is complete
179 };
180
181 // Record for GSC output buffers.
182 struct GscOutputRecord {
183 GscOutputRecord();
184 ~GscOutputRecord();
185 bool at_device; // held by device.
186 bool at_client; // held by client.
Ami GONE FROM CHROMIUM 2013/01/04 21:38:55 These two fields could be replaced with a single f
187 int fd; // file descriptor from backing EGLImage.
188 EGLImageKHR egl_image; // backing EGLImage.
189 EGLSyncKHR egl_sync; // sync the compositor's use of the EGLImage.
190 int32 picture_id; // picture ID as returned to PictureReady().
Ami GONE FROM CHROMIUM 2013/01/04 01:57:05 picture _buffer_ id?
sheu 2013/01/05 00:51:49 Done.
191 };
192
193 //
194 // Decoding tasks, to be run on decode_thread_.
195 //
196
197 // Enqueue a BitstreamBuffer to decode. This will enqueue a
198 // DecodeBufferTask() to actually decode the buffer.
199 void DecodeTask(scoped_ptr<BitstreamBufferRef> bitstream_record);
200
201 // Decode from the queued BitstreamBuffers. Calls DecodeBufferInitial()
202 // or DecodeBufferContinue().
203 void DecodeBufferTask();
204 // Find the extents of one frame fragment to push to HW.
205 bool FindFrameFragment(const void* data, size_t size, size_t* endpos);
206 // Schedule another DecodeBufferTask() if we're behind.
207 void ScheduleDecodeBufferTaskIfNeeded();
208
209 // Return true if we should continue to schedule DecodeBufferTask()s after
210 // completion. Store the amount of input actually consumed in |endpos|.
211 bool DecodeBufferInitial(const void* data, size_t size, size_t* endpos);
212 bool DecodeBufferContinue(const void* data, size_t size, size_t* endpos);
213
214 // Helpers: accumulate and flush data for one decoded frame.
215 bool AppendToInputFrame(const void* data, size_t size);
216 bool FlushInputFrame();
217
218 // Process an AssignPictureBuffers() API call. After this, the
219 // device_poll_thread_ can be started safely, since we have all our
220 // buffers.
221 void AssignPictureBuffersTask(scoped_ptr<PictureBufferArrayRef> pic_buffers);
222
223 // Service I/O on the V4L2 devices. This task should only be scheduled from
224 // DevicePollTask().
225 void ServiceDeviceTask();
226 // Handle the various device queues.
227 void EnqueueMfc();
228 void DequeueMfc();
229 void EnqueueGsc();
230 void DequeueGsc();
231 // Notify the client of a flush completion, maybe.
232 void NotifyFlushDoneIfNeeded();
233 // Enqueue a buffer on the corresponding queue.
234 bool EnqueueMfcInputRecord();
235 bool EnqueueMfcOutputRecord();
236 bool EnqueueGscInputRecord();
237 bool EnqueueGscOutputRecord();
238
239 // Process a ReusePictureBuffer() API call. The API call create an EGLSync
240 // object on the main (GPU process) thread; we will record this object so we
241 // can wait on it before reusing the buffer.
242 void ReusePictureBufferTask(int32 picture_buffer_id,
243 scoped_ptr<EGLSyncKHRRef> egl_sync_ref);
244
245 // Flush() task. Child thread should not submit any more buffers until it
246 // receives the NotifyFlushDone callback.
247 void FlushTask();
248
249 // Reset() task. This task will schedule a ResetDoneTask() that will send
250 // the NotifyResetDone callback, then set the decoder state to kResetting so
251 // that all intervening tasks will drain.
252 void ResetTask();
253 void ResetDoneTask();
254
255 // Device destruction task.
256 void DestroyTask();
257
258 // Attempt to start/stop device_poll_thread_.
259 bool StartDevicePoll();
260 bool StopDevicePoll();
261 // Set/clear the device poll interrupt (using device_poll_interrupt_fd_).
262 bool SetDevicePollInterrupt();
263 bool ClearDevicePollInterrupt();
264
265 //
266 // Device tasks, to be run on device_poll_thread_.
267 //
268
269 // The device task.
270 void DevicePollTask(unsigned int poll_fds);
271
272 //
273 // Safe from any thread.
274 //
275
276 // Error notification (using PostTask() to child thread, if necessary).
277 void NotifyError(Error error);
278
279 // Set the decoder_thread_ state (using PostTask to decoder thread, if
280 // necessary).
281 void SetDecoderState(State state);
282
283 //
284 // Other utility functions. Called on decoder_thread_, unless
285 // decoder_thread_ is not yet started, in which case the child thread can call
286 // these (e.g. in Initialize() or Destroy()).
Ami GONE FROM CHROMIUM 2013/01/04 01:57:05 Can it happen that Initialize() didn't return true
sheu 2013/01/05 00:51:49 That's pretty much it.
287 //
288
289 // Create the buffers we need.
290 bool CreateMfcInputBuffers();
291 bool CreateMfcOutputBuffers();
292 bool CreateGscInputBuffers();
293 bool CreateGscOutputBuffers();
294
295 // Destroy these buffers.
296 void DestroyMfcInputBuffers();
297 void DestroyMfcOutputBuffers();
298 void DestroyGscInputBuffers();
299 void DestroyGscOutputBuffers();
300
301 // Our original calling message loop for the child thread.
302 scoped_refptr<base::MessageLoopProxy> child_message_loop_proxy_;
Ami GONE FROM CHROMIUM 2013/01/04 01:57:05 How is this different from ChildThread::current()-
sheu 2013/01/05 00:51:49 Robustness? I'm caching the proxy (not the messag
303
304 // WeakPtr<> pointing to |this| for use in posting tasks from the decoder or
305 // device worker threads back to the child thread. Because the worker threads
306 // are members of this class, any task running on those threads is guaranteed
307 // that this object is still alive. As a result, tasks posted from the child
308 // thread to the decoder or device thread should use base::Unretained(this),
309 // and tasks posted the other way should use |weak_this_|.
310 base::WeakPtr<ExynosVideoDecodeAccelerator> weak_this_;
311
312 // To expose client callbacks from VideoDecodeAccelerator.
313 // NOTE: all calls to these objects *MUST* be executed on
314 // child_message_loop_proxy_.
315 base::WeakPtrFactory<Client> client_ptr_factory_;
316 base::WeakPtr<Client> client_;
317
318 //
319 // Decoder state, owned and operated by decoder_thread_.
320 // Before decoder_thread_ has started, the decoder state is managed by
321 // the child (main) thread. After decoder_thread_ has started, the decoder
322 // thread should be the only one managing these.
323 //
324
325 // This thread services tasks posted from the VDA API entry points by the
326 // child thread and device service callbacks posted from the device thread.
327 base::Thread decoder_thread_;
328 // Decoder state machine state.
329 State decoder_state_;
330 // Bitstream buffer we're presently reading.
331 scoped_ptr<BitstreamBufferRef> decoder_current_bitstream_buffer_;
332 // MFC input buffer we're presently filling.
333 int decoder_current_input_buffer_;
334 // We track the number of buffer decode tasks we have scheduled, since each
335 // task execution should complete one buffer. If we fall behind (due to
336 // resource backpressure, etc.), we'll have to schedule more to catch up.
337 int decoder_decode_buffer_tasks_scheduled_;
338 // Buffers held by the client.
Ami GONE FROM CHROMIUM 2013/01/04 01:57:05 picturebuffers ?
sheu 2013/01/05 00:51:49 Done.
339 int decoder_frames_at_client_;
340 // Are we waiting for a flush notification?
341 bool decoder_notify_flush_requested_;
342 // Input queue for decoder_thread_: BitstreamBuffers in.
343 std::deque<linked_ptr<BitstreamBufferRef> > decoder_input_queue_;
Ami GONE FROM CHROMIUM 2013/01/04 01:57:05 here and elsewhere I wonder at the use of deque<>.
sheu 2013/01/05 00:51:49 Done.
344 // For H264 decode, hardware requires that we send it frame-sized chunks.
345 // We'll need to parse the stream.
346 scoped_ptr<content::H264Parser> decoder_h264_parser_;
347
348 //
349 // Hardware state and associated queues. Since decoder_thread_ services
350 // the hardware, decoder_thread_ owns these too.
351 //
352
353 // Completed decode buffers, waiting for MFC.
354 std::deque<int> mfc_input_ready_queue_;
355
356 // MFC decode device.
357 int mfc_fd_;
358
359 // MFC input buffer state.
360 bool mfc_input_streamon_;
361 // MFC input buffers, total.
362 int mfc_input_buffer_count_;
363 // MFC input buffers enqueued to device.
364 int mfc_input_buffer_queued_count_;
365 // Input buffers ready to use, as a FIFO since we don't care about ordering.
Ami GONE FROM CHROMIUM 2013/01/04 01:57:05 s/FIFO/LIFO/ I think, here and at l.376 and l.396.
sheu 2013/01/05 00:51:49 Done.
366 std::vector<int> mfc_free_input_buffers_;
367 // Mapping of int index to MFC input buffer record.
368 std::vector<MfcInputRecord> mfc_input_buffer_map_;
369
370 // MFC output buffer state.
371 bool mfc_output_streamon_;
372 // MFC output buffers, total.
373 int mfc_output_buffer_count_;
374 // MFC output buffers enqueued to device.
375 int mfc_output_buffer_queued_count_;
376 // Output buffers ready to use, as a FIFO since we don't care about ordering.
377 std::vector<int> mfc_free_output_buffers_;
378 // Mapping of int index to MFC output buffer record.
379 std::vector<MfcOutputRecord> mfc_output_buffer_map_;
380 // Required size of MFC output buffers. Two sizes for two planes.
381 size_t mfc_output_buffer_size_[2];
382 uint32 mfc_output_buffer_pixelformat_;
383
384 // Completed MFC outputs, waiting for GSC.
385 std::deque<int> mfc_output_gsc_input_queue_;
386
387 // GSC decode device.
388 int gsc_fd_;
389
390 // GSC input buffer state.
391 bool gsc_input_streamon_;
392 // GSC input buffers, total.
393 int gsc_input_buffer_count_;
394 // GSC input buffers enqueued to device.
395 int gsc_input_buffer_queued_count_;
396 // Input buffers ready to use, as a FIFO since we don't care about ordering.
397 std::vector<int> gsc_free_input_buffers_;
398 // Mapping of int index to GSC input buffer record.
399 std::vector<GscInputRecord> gsc_input_buffer_map_;
400
401 // GSC output buffer state.
402 bool gsc_output_streamon_;
403 // GSC output buffers, total.
404 int gsc_output_buffer_count_;
405 // GSC output buffers enqueued to device.
406 int gsc_output_buffer_queued_count_;
407 // Output buffers ready to use. We need a LIFO here.
Ami GONE FROM CHROMIUM 2013/01/04 01:57:05 Ironically, this is a FIFO :)
sheu 2013/01/05 00:51:49 Done.
408 std::deque<int> gsc_free_output_buffers_;
409 // Mapping of int index to GSC output buffer record.
410 std::vector<GscOutputRecord> gsc_output_buffer_map_;
411
412 // Output picture size.
413 gfx::Size frame_buffer_size_;
414
415 //
416 // The device polling thread handles notifications of V4L2 device changes.
417 //
418
419 // The thread.
420 base::Thread device_poll_thread_;
421 // eventfd fd to signal device poll thread when its poll() should be
422 // interrupted.
423 int device_poll_interrupt_fd_;
424
425 //
426 // Other state, held by the child (main) thread.
427 //
428
429 // Make our context current before running any EGL entry points.
430 base::Callback<bool(void)> make_context_current_;
431
432 // EGL state
433 EGLDisplay egl_display_;
434 EGLContext egl_context_;
435
436 // The codec we'll be decoding for.
437 media::VideoCodecProfile video_profile_;
438
439 DISALLOW_COPY_AND_ASSIGN(ExynosVideoDecodeAccelerator);
440 };
441
442 } // namespace content
443
444 #endif // CONTENT_COMMON_GPU_MEDIA_EXYNOS_VIDEO_DECODE_ACCELERATOR_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698