OLD | NEW |
---|---|
(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_ | |
OLD | NEW |