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

Side by Side Diff: content/common/gpu/media/omx_video_decode_accelerator.cc

Issue 21584002: Delete dead code: OmxVideoDecodeAccelerator is unused. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Re-removing both OWNERS & README Created 7 years, 4 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 | Annotate | Revision Log
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 #include "content/common/gpu/media/omx_video_decode_accelerator.h"
6
7 #include "base/bind.h"
8 #include "base/debug/trace_event.h"
9 #include "base/logging.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "content/common/gpu/gpu_channel.h"
13 #include "content/common/gpu/media/gles2_texture_to_egl_image_translator.h"
14 #include "media/base/bitstream_buffer.h"
15 #include "media/video/picture.h"
16
17 namespace content {
18
19 // Helper typedef for input buffers. This is used as the pAppPrivate field of
20 // OMX_BUFFERHEADERTYPEs of input buffers, to point to the data associated with
21 // them.
22 typedef std::pair<scoped_ptr<base::SharedMemory>, int32> SharedMemoryAndId;
23
24 enum { kNumPictureBuffers = 8 };
25
26 // Delay between polling for texture sync status. 5ms feels like a good
27 // compromise, allowing some decoding ahead (up to 3 frames/vsync) to compensate
28 // for more difficult frames.
29 enum { kSyncPollDelayMs = 5 };
30
31 void* omx_handle = NULL;
32
33 typedef OMX_ERRORTYPE (*OMXInit)();
34 typedef OMX_ERRORTYPE (*OMXGetHandle)(
35 OMX_HANDLETYPE*, OMX_STRING, OMX_PTR, OMX_CALLBACKTYPE*);
36 typedef OMX_ERRORTYPE (*OMXGetComponentsOfRole)(OMX_STRING, OMX_U32*, OMX_U8**);
37 typedef OMX_ERRORTYPE (*OMXFreeHandle)(OMX_HANDLETYPE);
38 typedef OMX_ERRORTYPE (*OMXDeinit)();
39
40 OMXInit omx_init = NULL;
41 OMXGetHandle omx_gethandle = NULL;
42 OMXGetComponentsOfRole omx_get_components_of_role = NULL;
43 OMXFreeHandle omx_free_handle = NULL;
44 OMXDeinit omx_deinit = NULL;
45
46 // Maps h264-related Profile enum values to OMX_VIDEO_AVCPROFILETYPE values.
47 static OMX_U32 MapH264ProfileToOMXAVCProfile(uint32 profile) {
48 switch (profile) {
49 case media::H264PROFILE_BASELINE:
50 return OMX_VIDEO_AVCProfileBaseline;
51 case media::H264PROFILE_MAIN:
52 return OMX_VIDEO_AVCProfileMain;
53 case media::H264PROFILE_EXTENDED:
54 return OMX_VIDEO_AVCProfileExtended;
55 case media::H264PROFILE_HIGH:
56 return OMX_VIDEO_AVCProfileHigh;
57 case media::H264PROFILE_HIGH10PROFILE:
58 return OMX_VIDEO_AVCProfileHigh10;
59 case media::H264PROFILE_HIGH422PROFILE:
60 return OMX_VIDEO_AVCProfileHigh422;
61 case media::H264PROFILE_HIGH444PREDICTIVEPROFILE:
62 return OMX_VIDEO_AVCProfileHigh444;
63 // Below enums don't have equivalent enum in Openmax.
64 case media::H264PROFILE_SCALABLEBASELINE:
65 case media::H264PROFILE_SCALABLEHIGH:
66 case media::H264PROFILE_STEREOHIGH:
67 case media::H264PROFILE_MULTIVIEWHIGH:
68 // Nvidia OMX video decoder requires the same resources (as that of the
69 // High profile) in every profile higher to the Main profile.
70 return OMX_VIDEO_AVCProfileHigh444;
71 default:
72 NOTREACHED();
73 return OMX_VIDEO_AVCProfileMax;
74 }
75 }
76
77 // Helper macros for dealing with failure. If |result| evaluates false, emit
78 // |log| to ERROR, register |error| with the decoder, and return |ret_val|
79 // (which may be omitted for functions that return void).
80 #define RETURN_ON_FAILURE(result, log, error, ret_val) \
81 do { \
82 if (!(result)) { \
83 DLOG(ERROR) << log; \
84 StopOnError(error); \
85 return ret_val; \
86 } \
87 } while (0)
88
89 // OMX-specific version of RETURN_ON_FAILURE which compares with OMX_ErrorNone.
90 #define RETURN_ON_OMX_FAILURE(omx_result, log, error, ret_val) \
91 RETURN_ON_FAILURE( \
92 ((omx_result) == OMX_ErrorNone), \
93 log << ", OMX result: 0x" << std::hex << omx_result, \
94 error, ret_val)
95
96 // static
97 bool OmxVideoDecodeAccelerator::pre_sandbox_init_done_ = false;
98
99 class OmxVideoDecodeAccelerator::PictureSyncObject {
100 public:
101 // Create a sync object and insert into the GPU command stream.
102 PictureSyncObject(EGLDisplay egl_display);
103 ~PictureSyncObject();
104
105 bool IsSynced();
106
107 private:
108 EGLSyncKHR egl_sync_obj_;
109 EGLDisplay egl_display_;
110 };
111
112 OmxVideoDecodeAccelerator::PictureSyncObject::PictureSyncObject(
113 EGLDisplay egl_display)
114 : egl_display_(egl_display) {
115 DCHECK(egl_display_ != EGL_NO_DISPLAY);
116
117 egl_sync_obj_ = eglCreateSyncKHR(egl_display_, EGL_SYNC_FENCE_KHR, NULL);
118 DCHECK_NE(egl_sync_obj_, EGL_NO_SYNC_KHR);
119 }
120
121 OmxVideoDecodeAccelerator::PictureSyncObject::~PictureSyncObject() {
122 eglDestroySyncKHR(egl_display_, egl_sync_obj_);
123 }
124
125 bool OmxVideoDecodeAccelerator::PictureSyncObject::IsSynced() {
126 EGLint value = EGL_UNSIGNALED_KHR;
127 EGLBoolean ret = eglGetSyncAttribKHR(
128 egl_display_, egl_sync_obj_, EGL_SYNC_STATUS_KHR, &value);
129 DCHECK(ret) << "Failed getting sync object state.";
130
131 return value == EGL_SIGNALED_KHR;
132 }
133
134 OmxVideoDecodeAccelerator::OmxVideoDecodeAccelerator(
135 EGLDisplay egl_display, EGLContext egl_context,
136 media::VideoDecodeAccelerator::Client* client,
137 const base::Callback<bool(void)>& make_context_current)
138 : message_loop_(base::MessageLoop::current()),
139 component_handle_(NULL),
140 weak_this_(base::AsWeakPtr(this)),
141 init_begun_(false),
142 client_state_(OMX_StateMax),
143 current_state_change_(NO_TRANSITION),
144 input_buffer_count_(0),
145 input_buffer_size_(0),
146 input_port_(0),
147 input_buffers_at_component_(0),
148 output_port_(0),
149 output_buffers_at_component_(0),
150 egl_display_(egl_display),
151 egl_context_(egl_context),
152 make_context_current_(make_context_current),
153 client_ptr_factory_(client),
154 client_(client_ptr_factory_.GetWeakPtr()),
155 codec_(UNKNOWN),
156 h264_profile_(OMX_VIDEO_AVCProfileMax),
157 component_name_is_nvidia_(false) {
158 static bool omx_functions_initialized = PostSandboxInitialization();
159 RETURN_ON_FAILURE(omx_functions_initialized,
160 "Failed to load openmax library", PLATFORM_FAILURE,);
161 RETURN_ON_OMX_FAILURE(omx_init(), "Failed to init OpenMAX core",
162 PLATFORM_FAILURE,);
163 }
164
165 OmxVideoDecodeAccelerator::~OmxVideoDecodeAccelerator() {
166 DCHECK_EQ(message_loop_, base::MessageLoop::current());
167 DCHECK(free_input_buffers_.empty());
168 DCHECK_EQ(0, input_buffers_at_component_);
169 DCHECK_EQ(0, output_buffers_at_component_);
170 DCHECK(pictures_.empty());
171 }
172
173 // This is to initialize the OMX data structures to default values.
174 template <typename T>
175 static void InitParam(const OmxVideoDecodeAccelerator& dec, T* param) {
176 memset(param, 0, sizeof(T));
177 param->nVersion.nVersion = 0x00000101;
178 param->nSize = sizeof(T);
179 }
180
181 bool OmxVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile) {
182 DCHECK_EQ(message_loop_, base::MessageLoop::current());
183
184 if (profile >= media::H264PROFILE_MIN && profile <= media::H264PROFILE_MAX) {
185 codec_ = H264;
186 h264_profile_ = MapH264ProfileToOMXAVCProfile(profile);
187 RETURN_ON_FAILURE(h264_profile_ != OMX_VIDEO_AVCProfileMax,
188 "Unexpected profile", INVALID_ARGUMENT, false);
189 } else if (profile == media::VP8PROFILE_MAIN) {
190 codec_ = VP8;
191 } else {
192 RETURN_ON_FAILURE(false, "Unsupported profile: " << profile,
193 INVALID_ARGUMENT, false);
194 }
195
196 // We need the context to be initialized to query extensions.
197 RETURN_ON_FAILURE(make_context_current_.Run(),
198 "Failed make context current",
199 PLATFORM_FAILURE,
200 false);
201 RETURN_ON_FAILURE(gfx::g_driver_egl.ext.b_EGL_KHR_fence_sync,
202 "Platform does not support EGL_KHR_fence_sync",
203 PLATFORM_FAILURE,
204 false);
205
206 if (!CreateComponent()) // Does its own RETURN_ON_FAILURE dances.
207 return false;
208
209 DCHECK_EQ(current_state_change_, NO_TRANSITION);
210 current_state_change_ = INITIALIZING;
211 BeginTransitionToState(OMX_StateIdle);
212
213 if (!AllocateInputBuffers()) // Does its own RETURN_ON_FAILURE dances.
214 return false;
215 if (!AllocateFakeOutputBuffers()) // Does its own RETURN_ON_FAILURE dances.
216 return false;
217
218 init_begun_ = true;
219 return true;
220 }
221
222 bool OmxVideoDecodeAccelerator::CreateComponent() {
223 DCHECK_EQ(message_loop_, base::MessageLoop::current());
224 OMX_CALLBACKTYPE omx_accelerator_callbacks = {
225 &OmxVideoDecodeAccelerator::EventHandler,
226 &OmxVideoDecodeAccelerator::EmptyBufferCallback,
227 &OmxVideoDecodeAccelerator::FillBufferCallback
228 };
229
230 // TODO(vhiremath@nvidia.com) Get this role_name from the configs
231 // For now hard-coding.
232 OMX_STRING role_name = codec_ == H264 ?
233 const_cast<OMX_STRING>("video_decoder.avc") :
234 const_cast<OMX_STRING>("video_decoder.vpx");
235 // Get the first component for this role and set the role on it.
236 OMX_U32 num_components = 1;
237 std::string component(OMX_MAX_STRINGNAME_SIZE, '\0');
238 char* component_as_array = string_as_array(&component);
239 OMX_ERRORTYPE result = omx_get_components_of_role(
240 role_name, &num_components,
241 reinterpret_cast<OMX_U8**>(&component_as_array));
242 RETURN_ON_OMX_FAILURE(result, "Unsupported role: " << role_name,
243 PLATFORM_FAILURE, false);
244 RETURN_ON_FAILURE(num_components == 1, "No components for: " << role_name,
245 PLATFORM_FAILURE, false);
246 component_name_is_nvidia_ = StartsWithASCII(
247 component, "OMX.Nvidia", true);
248
249 // Get the handle to the component.
250 result = omx_gethandle(
251 &component_handle_,
252 reinterpret_cast<OMX_STRING>(string_as_array(&component)),
253 this, &omx_accelerator_callbacks);
254 RETURN_ON_OMX_FAILURE(result,
255 "Failed to OMX_GetHandle on: " << component,
256 PLATFORM_FAILURE, false);
257 client_state_ = OMX_StateLoaded;
258
259 texture_to_egl_image_translator_.reset(new Gles2TextureToEglImageTranslator(
260 StartsWithASCII(component, "OMX.SEC.", true)));
261
262 // Get the port information. This will obtain information about the number of
263 // ports and index of the first port.
264 OMX_PORT_PARAM_TYPE port_param;
265 InitParam(*this, &port_param);
266 result = OMX_GetParameter(component_handle_, OMX_IndexParamVideoInit,
267 &port_param);
268 RETURN_ON_FAILURE(result == OMX_ErrorNone && port_param.nPorts == 2,
269 "Failed to get Port Param: " << result << ", "
270 << port_param.nPorts,
271 PLATFORM_FAILURE, false);
272 input_port_ = port_param.nStartPortNumber;
273 output_port_ = input_port_ + 1;
274
275 // Set role for the component because components can have multiple roles.
276 OMX_PARAM_COMPONENTROLETYPE role_type;
277 InitParam(*this, &role_type);
278 base::strlcpy(reinterpret_cast<char*>(role_type.cRole),
279 role_name,
280 OMX_MAX_STRINGNAME_SIZE);
281
282 result = OMX_SetParameter(component_handle_,
283 OMX_IndexParamStandardComponentRole,
284 &role_type);
285 RETURN_ON_OMX_FAILURE(result, "Failed to Set Role",
286 PLATFORM_FAILURE, false);
287
288 // Populate input-buffer-related members based on input port data.
289 OMX_PARAM_PORTDEFINITIONTYPE port_format;
290 InitParam(*this, &port_format);
291 port_format.nPortIndex = input_port_;
292 result = OMX_GetParameter(component_handle_,
293 OMX_IndexParamPortDefinition,
294 &port_format);
295 RETURN_ON_OMX_FAILURE(result,
296 "GetParameter(OMX_IndexParamPortDefinition) failed",
297 PLATFORM_FAILURE, false);
298 RETURN_ON_FAILURE(OMX_DirInput == port_format.eDir, "Expected input port",
299 PLATFORM_FAILURE, false);
300
301 input_buffer_count_ = port_format.nBufferCountActual;
302 input_buffer_size_ = port_format.nBufferSize;
303
304 // Verify output port conforms to our expectations.
305 InitParam(*this, &port_format);
306 port_format.nPortIndex = output_port_;
307 result = OMX_GetParameter(component_handle_,
308 OMX_IndexParamPortDefinition,
309 &port_format);
310 RETURN_ON_OMX_FAILURE(result,
311 "GetParameter(OMX_IndexParamPortDefinition) failed",
312 PLATFORM_FAILURE, false);
313 RETURN_ON_FAILURE(OMX_DirOutput == port_format.eDir, "Expect Output Port",
314 PLATFORM_FAILURE, false);
315
316 // Set output port parameters.
317 port_format.nBufferCountActual = kNumPictureBuffers;
318 // Force an OMX_EventPortSettingsChanged event to be sent once we know the
319 // stream's real dimensions (which can only happen once some Decode() work has
320 // been done).
321 port_format.format.video.nFrameWidth = -1;
322 port_format.format.video.nFrameHeight = -1;
323 result = OMX_SetParameter(component_handle_,
324 OMX_IndexParamPortDefinition,
325 &port_format);
326 RETURN_ON_OMX_FAILURE(result,
327 "SetParameter(OMX_IndexParamPortDefinition) failed",
328 PLATFORM_FAILURE, false);
329 return true;
330 }
331
332 void OmxVideoDecodeAccelerator::Decode(
333 const media::BitstreamBuffer& bitstream_buffer) {
334 TRACE_EVENT1("Video Decoder", "OVDA::Decode",
335 "Buffer id", bitstream_buffer.id());
336 DCHECK_EQ(message_loop_, base::MessageLoop::current());
337
338 if (current_state_change_ == RESETTING ||
339 !queued_bitstream_buffers_.empty() ||
340 free_input_buffers_.empty()) {
341 queued_bitstream_buffers_.push_back(bitstream_buffer);
342 return;
343 }
344
345 RETURN_ON_FAILURE((current_state_change_ == NO_TRANSITION ||
346 current_state_change_ == FLUSHING) &&
347 (client_state_ == OMX_StateIdle ||
348 client_state_ == OMX_StateExecuting),
349 "Call to Decode() during invalid state or transition: "
350 << current_state_change_ << ", " << client_state_,
351 ILLEGAL_STATE,);
352
353 OMX_BUFFERHEADERTYPE* omx_buffer = free_input_buffers_.front();
354 free_input_buffers_.pop();
355
356 if (bitstream_buffer.id() == -1 && bitstream_buffer.size() == 0) {
357 // Cook up an empty buffer w/ EOS set and feed it to OMX.
358 omx_buffer->nFilledLen = 0;
359 omx_buffer->nAllocLen = omx_buffer->nFilledLen;
360 omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS;
361 omx_buffer->nTimeStamp = -2;
362 OMX_ERRORTYPE result = OMX_EmptyThisBuffer(component_handle_, omx_buffer);
363 RETURN_ON_OMX_FAILURE(result, "OMX_EmptyThisBuffer() failed",
364 PLATFORM_FAILURE,);
365 input_buffers_at_component_++;
366 return;
367 }
368
369 // Setup |omx_buffer|.
370 scoped_ptr<base::SharedMemory> shm(
371 new base::SharedMemory(bitstream_buffer.handle(), true));
372 RETURN_ON_FAILURE(shm->Map(bitstream_buffer.size()),
373 "Failed to SharedMemory::Map()", UNREADABLE_INPUT,);
374
375 SharedMemoryAndId* input_buffer_details = new SharedMemoryAndId();
376 input_buffer_details->first.reset(shm.release());
377 input_buffer_details->second = bitstream_buffer.id();
378 DCHECK(!omx_buffer->pAppPrivate);
379 omx_buffer->pAppPrivate = input_buffer_details;
380 omx_buffer->pBuffer =
381 static_cast<OMX_U8*>(input_buffer_details->first->memory());
382 omx_buffer->nFilledLen = bitstream_buffer.size();
383 omx_buffer->nAllocLen = omx_buffer->nFilledLen;
384 omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS;
385 // Abuse the header's nTimeStamp field to propagate the bitstream buffer ID to
386 // the output buffer's nTimeStamp field, so we can report it back to the
387 // client in PictureReady().
388 omx_buffer->nTimeStamp = bitstream_buffer.id();
389
390 // Give this buffer to OMX.
391 OMX_ERRORTYPE result = OMX_EmptyThisBuffer(component_handle_, omx_buffer);
392 RETURN_ON_OMX_FAILURE(result, "OMX_EmptyThisBuffer() failed",
393 PLATFORM_FAILURE,);
394 input_buffers_at_component_++;
395 }
396
397 void OmxVideoDecodeAccelerator::AssignPictureBuffers(
398 const std::vector<media::PictureBuffer>& buffers) {
399 DCHECK_EQ(message_loop_, base::MessageLoop::current());
400
401 // If we are resetting/destroying/erroring, don't bother, as
402 // OMX_FillThisBuffer will fail anyway. In case we're in the middle of
403 // closing, this will put the Accelerator in ERRORING mode, which has the
404 // unwanted side effect of not going through the OMX_FreeBuffers path and
405 // leaks memory.
406 if (current_state_change_ == RESETTING ||
407 current_state_change_ == DESTROYING ||
408 current_state_change_ == ERRORING)
409 return;
410
411 RETURN_ON_FAILURE(CanFillBuffer(), "Can't fill buffer", ILLEGAL_STATE,);
412 RETURN_ON_FAILURE((kNumPictureBuffers == buffers.size()),
413 "Failed to provide requested picture buffers. (Got " << buffers.size() <<
414 ", requested " << kNumPictureBuffers << ")", INVALID_ARGUMENT,);
415
416 DCHECK_EQ(output_buffers_at_component_, 0);
417 DCHECK_EQ(fake_output_buffers_.size(), 0U);
418 DCHECK_EQ(pictures_.size(), 0U);
419
420 if (!make_context_current_.Run())
421 return;
422
423 for (size_t i = 0; i < buffers.size(); ++i) {
424 EGLImageKHR egl_image =
425 texture_to_egl_image_translator_->TranslateToEglImage(
426 egl_display_, egl_context_,
427 buffers[i].texture_id(),
428 last_requested_picture_buffer_dimensions_);
429 CHECK(pictures_.insert(std::make_pair(
430 buffers[i].id(), OutputPicture(buffers[i], NULL, egl_image))).second);
431 }
432
433 // These do their own RETURN_ON_FAILURE dances.
434 if (!AllocateOutputBuffers())
435 return;
436 if (!SendCommandToPort(OMX_CommandPortEnable, output_port_))
437 return;
438 }
439
440 void OmxVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) {
441 DCHECK_EQ(message_loop_, base::MessageLoop::current());
442 TRACE_EVENT1("Video Decoder", "OVDA::ReusePictureBuffer",
443 "Picture id", picture_buffer_id);
444 scoped_ptr<PictureSyncObject> egl_sync_obj(
445 new PictureSyncObject(egl_display_));
446
447 // Start checking sync status periodically.
448 CheckPictureStatus(picture_buffer_id, egl_sync_obj.Pass());
449 }
450
451 void OmxVideoDecodeAccelerator::CheckPictureStatus(
452 int32 picture_buffer_id,
453 scoped_ptr<PictureSyncObject> egl_sync_obj) {
454 DCHECK_EQ(message_loop_, base::MessageLoop::current());
455
456 // It's possible for this task to never run if the message loop is
457 // stopped. In that case we may never call QueuePictureBuffer().
458 // This is fine though, because all pictures, irrespective of their state,
459 // are in pictures_ map and that's what will be used to do the clean up.
460 if (!egl_sync_obj->IsSynced()) {
461 base::MessageLoop::current()->PostDelayedTask(FROM_HERE, base::Bind(
462 &OmxVideoDecodeAccelerator::CheckPictureStatus, weak_this_,
463 picture_buffer_id, base::Passed(&egl_sync_obj)),
464 base::TimeDelta::FromMilliseconds(kSyncPollDelayMs));
465 return;
466 }
467
468 // Synced successfully. Queue the buffer for reuse.
469 QueuePictureBuffer(picture_buffer_id);
470 }
471
472 void OmxVideoDecodeAccelerator::QueuePictureBuffer(int32 picture_buffer_id) {
473 DCHECK_EQ(message_loop_, base::MessageLoop::current());
474
475 // During port-flushing, do not call OMX FillThisBuffer.
476 if (current_state_change_ == RESETTING) {
477 queued_picture_buffer_ids_.push_back(picture_buffer_id);
478 return;
479 }
480
481 // We might have started destroying while waiting for the picture. It's safe
482 // to drop it here, because we will free all the pictures regardless of their
483 // state using the pictures_ map.
484 if (!CanFillBuffer())
485 return;
486
487 OutputPictureById::iterator it = pictures_.find(picture_buffer_id);
488 RETURN_ON_FAILURE(it != pictures_.end(),
489 "Missing picture buffer id: " << picture_buffer_id,
490 INVALID_ARGUMENT,);
491 OutputPicture& output_picture = it->second;
492
493 ++output_buffers_at_component_;
494 OMX_ERRORTYPE result =
495 OMX_FillThisBuffer(component_handle_, output_picture.omx_buffer_header);
496 RETURN_ON_OMX_FAILURE(result, "OMX_FillThisBuffer() failed",
497 PLATFORM_FAILURE,);
498 }
499
500 void OmxVideoDecodeAccelerator::Flush() {
501 DCHECK_EQ(message_loop_, base::MessageLoop::current());
502 DCHECK_EQ(current_state_change_, NO_TRANSITION);
503 DCHECK_EQ(client_state_, OMX_StateExecuting);
504 current_state_change_ = FLUSHING;
505
506 Decode(media::BitstreamBuffer(-1, base::SharedMemoryHandle(), 0));
507 }
508
509 void OmxVideoDecodeAccelerator::OnReachedEOSInFlushing() {
510 DCHECK_EQ(message_loop_, base::MessageLoop::current());
511 DCHECK_EQ(client_state_, OMX_StateExecuting);
512 current_state_change_ = NO_TRANSITION;
513 if (client_)
514 client_->NotifyFlushDone();
515 }
516
517 void OmxVideoDecodeAccelerator::FlushIOPorts() {
518 DCHECK_EQ(message_loop_, base::MessageLoop::current());
519
520 // Flush input port first.
521 if (!SendCommandToPort(OMX_CommandFlush, input_port_))
522 return;
523 }
524
525 void OmxVideoDecodeAccelerator::InputPortFlushDone() {
526 DCHECK_EQ(message_loop_, base::MessageLoop::current());
527 DCHECK_EQ(input_buffers_at_component_, 0);
528 if (!SendCommandToPort(OMX_CommandFlush, output_port_))
529 return;
530 }
531
532 void OmxVideoDecodeAccelerator::OutputPortFlushDone() {
533 DCHECK_EQ(message_loop_, base::MessageLoop::current());
534 DCHECK_EQ(output_buffers_at_component_, 0);
535 BeginTransitionToState(OMX_StateExecuting);
536 }
537
538 void OmxVideoDecodeAccelerator::Reset() {
539 DCHECK_EQ(message_loop_, base::MessageLoop::current());
540 DCHECK_EQ(current_state_change_, NO_TRANSITION);
541 DCHECK_EQ(client_state_, OMX_StateExecuting);
542 current_state_change_ = RESETTING;
543 BeginTransitionToState(OMX_StatePause);
544 }
545
546 void OmxVideoDecodeAccelerator::Destroy() {
547 DCHECK_EQ(message_loop_, base::MessageLoop::current());
548
549 scoped_ptr<OmxVideoDecodeAccelerator> deleter(this);
550 client_ptr_factory_.InvalidateWeakPtrs();
551
552 if (current_state_change_ == ERRORING ||
553 current_state_change_ == DESTROYING) {
554 return;
555 }
556
557 DCHECK(current_state_change_ == NO_TRANSITION ||
558 current_state_change_ == FLUSHING ||
559 current_state_change_ == RESETTING) << current_state_change_;
560
561 // If we were never initializeed there's no teardown to do.
562 if (client_state_ == OMX_StateMax)
563 return;
564 // If we can already call OMX_FreeHandle, simply do so.
565 if (client_state_ == OMX_StateInvalid || client_state_ == OMX_StateLoaded) {
566 ShutdownComponent();
567 return;
568 }
569 DCHECK(client_state_ == OMX_StateExecuting ||
570 client_state_ == OMX_StateIdle ||
571 client_state_ == OMX_StatePause);
572 current_state_change_ = DESTROYING;
573 BeginTransitionToState(OMX_StateIdle);
574 BusyLoopInDestroying(deleter.Pass());
575 }
576
577 void OmxVideoDecodeAccelerator::BeginTransitionToState(
578 OMX_STATETYPE new_state) {
579 DCHECK_EQ(message_loop_, base::MessageLoop::current());
580 if (new_state != OMX_StateInvalid)
581 DCHECK_NE(current_state_change_, NO_TRANSITION);
582 if (current_state_change_ == ERRORING)
583 return;
584 OMX_ERRORTYPE result = OMX_SendCommand(
585 component_handle_, OMX_CommandStateSet, new_state, 0);
586 RETURN_ON_OMX_FAILURE(result, "SendCommand(OMX_CommandStateSet) failed",
587 PLATFORM_FAILURE,);
588 }
589
590 void OmxVideoDecodeAccelerator::OnReachedIdleInInitializing() {
591 DCHECK_EQ(client_state_, OMX_StateLoaded);
592 client_state_ = OMX_StateIdle;
593 // Query the resources with the component.
594 if (component_name_is_nvidia_) {
595 OMX_INDEXTYPE extension_index;
596 OMX_ERRORTYPE result = OMX_GetExtensionIndex(
597 component_handle_,
598 const_cast<char*>("OMX.Nvidia.index.config.checkresources"),
599 &extension_index);
600 RETURN_ON_OMX_FAILURE(result,
601 "Failed to get the extension",
602 PLATFORM_FAILURE,);
603 OMX_VIDEO_PARAM_PROFILELEVELTYPE video_profile_level;
604 InitParam(*this, &video_profile_level);
605 DCHECK_EQ(codec_, H264);
606 video_profile_level.eProfile = h264_profile_;
607 result = OMX_SetConfig(component_handle_, extension_index,
608 &video_profile_level);
609 RETURN_ON_OMX_FAILURE(result,
610 "Resource Allocation failed",
611 PLATFORM_FAILURE,);
612
613 // The OMX spec doesn't say whether (0,0) is bottom-left or top-left, but
614 // NVIDIA OMX implementation used with this class chooses the opposite
615 // of other APIs used for HW decode (DXVA, OS/X, VAAPI). So we request
616 // a mirror here to avoid having to track Y-orientation throughout the
617 // stack (particularly unattractive because this is exposed to ppapi
618 // plugin authors and NaCl programs).
619 OMX_CONFIG_MIRRORTYPE mirror_config;
620 InitParam(*this, &mirror_config);
621 result = OMX_GetConfig(component_handle_,
622 OMX_IndexConfigCommonMirror, &mirror_config);
623 RETURN_ON_OMX_FAILURE(result, "Failed to get mirror", PLATFORM_FAILURE,);
624 mirror_config.eMirror = OMX_MirrorVertical;
625 result = OMX_SetConfig(component_handle_,
626 OMX_IndexConfigCommonMirror, &mirror_config);
627 RETURN_ON_OMX_FAILURE(result, "Failed to set mirror", PLATFORM_FAILURE,);
628 }
629 BeginTransitionToState(OMX_StateExecuting);
630 }
631
632 void OmxVideoDecodeAccelerator::OnReachedExecutingInInitializing() {
633 DCHECK_EQ(client_state_, OMX_StateIdle);
634 client_state_ = OMX_StateExecuting;
635 current_state_change_ = NO_TRANSITION;
636
637 // Request filling of our fake buffers to trigger decode processing. In
638 // reality as soon as any data is decoded these will get dismissed due to
639 // dimension mismatch.
640 for (std::set<OMX_BUFFERHEADERTYPE*>::iterator it =
641 fake_output_buffers_.begin();
642 it != fake_output_buffers_.end(); ++it) {
643 OMX_BUFFERHEADERTYPE* buffer = *it;
644 OMX_ERRORTYPE result = OMX_FillThisBuffer(component_handle_, buffer);
645 RETURN_ON_OMX_FAILURE(result, "OMX_FillThisBuffer()", PLATFORM_FAILURE,);
646 ++output_buffers_at_component_;
647 }
648
649 if (client_)
650 client_->NotifyInitializeDone();
651 }
652
653 void OmxVideoDecodeAccelerator::OnReachedPauseInResetting() {
654 DCHECK_EQ(client_state_, OMX_StateExecuting);
655 client_state_ = OMX_StatePause;
656 FlushIOPorts();
657 }
658
659 void OmxVideoDecodeAccelerator::DecodeQueuedBitstreamBuffers() {
660 BitstreamBufferList buffers;
661 buffers.swap(queued_bitstream_buffers_);
662 if (current_state_change_ == DESTROYING ||
663 current_state_change_ == ERRORING) {
664 return;
665 }
666 for (size_t i = 0; i < buffers.size(); ++i)
667 Decode(buffers[i]);
668 }
669
670 void OmxVideoDecodeAccelerator::OnReachedExecutingInResetting() {
671 DCHECK_EQ(client_state_, OMX_StatePause);
672 client_state_ = OMX_StateExecuting;
673 current_state_change_ = NO_TRANSITION;
674 if (!client_)
675 return;
676
677 // Drain queues of input & output buffers held during the reset.
678 DecodeQueuedBitstreamBuffers();
679 for (size_t i = 0; i < queued_picture_buffer_ids_.size(); ++i)
680 ReusePictureBuffer(queued_picture_buffer_ids_[i]);
681 queued_picture_buffer_ids_.clear();
682
683 client_->NotifyResetDone();
684 }
685
686 // Alert: HORROR ahead! OMX shutdown is an asynchronous dance but our clients
687 // enjoy the fire-and-forget nature of a synchronous Destroy() call that
688 // ensures no further callbacks are made. Since the interface between OMX
689 // callbacks and this class is a MessageLoop, we need to ensure the loop
690 // outlives the shutdown dance, even during process shutdown. We do this by
691 // repeatedly enqueuing a no-op task until shutdown is complete, since
692 // MessageLoop's shutdown drains pending tasks.
693 void OmxVideoDecodeAccelerator::BusyLoopInDestroying(
694 scoped_ptr<OmxVideoDecodeAccelerator> self) {
695 if (!component_handle_) return;
696 // Can't use PostDelayedTask here because MessageLoop doesn't drain delayed
697 // tasks. Instead we sleep for 5ms. Really.
698 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(5));
699 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
700 &OmxVideoDecodeAccelerator::BusyLoopInDestroying,
701 base::Unretained(this), base::Passed(&self)));
702 }
703
704 void OmxVideoDecodeAccelerator::OnReachedIdleInDestroying() {
705 DCHECK(client_state_ == OMX_StateExecuting ||
706 client_state_ == OMX_StateIdle ||
707 client_state_ == OMX_StatePause);
708 client_state_ = OMX_StateIdle;
709
710 // Note that during the Executing -> Idle transition, the OMX spec guarantees
711 // buffers have been returned to the client, so we don't need to do an
712 // explicit FlushIOPorts().
713
714 BeginTransitionToState(OMX_StateLoaded);
715
716 FreeOMXBuffers();
717 }
718
719 void OmxVideoDecodeAccelerator::OnReachedLoadedInDestroying() {
720 DCHECK_EQ(client_state_, OMX_StateIdle);
721 client_state_ = OMX_StateLoaded;
722 current_state_change_ = NO_TRANSITION;
723 ShutdownComponent();
724 }
725
726 void OmxVideoDecodeAccelerator::OnReachedInvalidInErroring() {
727 client_state_ = OMX_StateInvalid;
728 FreeOMXBuffers();
729 ShutdownComponent();
730 }
731
732 void OmxVideoDecodeAccelerator::ShutdownComponent() {
733 OMX_ERRORTYPE result = omx_free_handle(component_handle_);
734 if (result != OMX_ErrorNone)
735 DLOG(ERROR) << "OMX_FreeHandle() error. Error code: " << result;
736 client_state_ = OMX_StateMax;
737 omx_deinit();
738 // Allow BusyLoopInDestroying to exit and delete |this|.
739 component_handle_ = NULL;
740 }
741
742 void OmxVideoDecodeAccelerator::StopOnError(
743 media::VideoDecodeAccelerator::Error error) {
744 DCHECK_EQ(message_loop_, base::MessageLoop::current());
745
746 if (current_state_change_ == ERRORING)
747 return;
748
749 if (client_ && init_begun_)
750 client_->NotifyError(error);
751 client_ptr_factory_.InvalidateWeakPtrs();
752
753 if (client_state_ == OMX_StateInvalid || client_state_ == OMX_StateMax)
754 return;
755
756 BeginTransitionToState(OMX_StateInvalid);
757 current_state_change_ = ERRORING;
758 }
759
760 bool OmxVideoDecodeAccelerator::AllocateInputBuffers() {
761 DCHECK_EQ(message_loop_, base::MessageLoop::current());
762 for (int i = 0; i < input_buffer_count_; ++i) {
763 OMX_BUFFERHEADERTYPE* buffer;
764 // While registering the buffer header we use fake buffer information
765 // (length 0, at memory address 0x1) to fake out the "safety" check in
766 // OMX_UseBuffer. When it comes time to actually use this header in Decode
767 // we set these fields to their real values (for the duration of that
768 // Decode).
769 OMX_ERRORTYPE result =
770 OMX_UseBuffer(component_handle_, &buffer, input_port_,
771 NULL, /* pAppPrivate gets set in Decode(). */
772 0, reinterpret_cast<OMX_U8*>(0x1));
773 RETURN_ON_OMX_FAILURE(result, "OMX_UseBuffer() Input buffer error",
774 PLATFORM_FAILURE, false);
775 buffer->nInputPortIndex = input_port_;
776 buffer->nOffset = 0;
777 buffer->nFlags = 0;
778 free_input_buffers_.push(buffer);
779 }
780 return true;
781 }
782
783 bool OmxVideoDecodeAccelerator::AllocateFakeOutputBuffers() {
784 // Fill the component with fake output buffers.
785 for (unsigned int i = 0; i < kNumPictureBuffers; ++i) {
786 OMX_BUFFERHEADERTYPE* buffer;
787 OMX_ERRORTYPE result;
788 result = OMX_AllocateBuffer(component_handle_, &buffer, output_port_,
789 NULL, 0);
790 RETURN_ON_OMX_FAILURE(result, "OMX_AllocateBuffer failed",
791 PLATFORM_FAILURE, false);
792 buffer->pAppPrivate = NULL;
793 buffer->nTimeStamp = -1;
794 buffer->nOutputPortIndex = output_port_;
795 CHECK(fake_output_buffers_.insert(buffer).second);
796 }
797 return true;
798 }
799
800 bool OmxVideoDecodeAccelerator::AllocateOutputBuffers() {
801 DCHECK_EQ(message_loop_, base::MessageLoop::current());
802
803 DCHECK(!pictures_.empty());
804 for (OutputPictureById::iterator it = pictures_.begin();
805 it != pictures_.end(); ++it) {
806 media::PictureBuffer& picture_buffer = it->second.picture_buffer;
807 OMX_BUFFERHEADERTYPE** omx_buffer = &it->second.omx_buffer_header;
808 DCHECK(!*omx_buffer);
809 OMX_ERRORTYPE result = OMX_UseEGLImage(
810 component_handle_, omx_buffer, output_port_, &picture_buffer,
811 it->second.egl_image);
812 RETURN_ON_OMX_FAILURE(result, "OMX_UseEGLImage", PLATFORM_FAILURE, false);
813 // Here we set a garbage bitstream buffer id, and then overwrite it before
814 // passing to PictureReady.
815 int garbage_bitstream_buffer_id = -1;
816 (*omx_buffer)->pAppPrivate =
817 new media::Picture(picture_buffer.id(), garbage_bitstream_buffer_id);
818 }
819 return true;
820 }
821
822 void OmxVideoDecodeAccelerator::FreeOMXBuffers() {
823 DCHECK_EQ(message_loop_, base::MessageLoop::current());
824 bool failure_seen = false;
825 while (!free_input_buffers_.empty()) {
826 OMX_BUFFERHEADERTYPE* omx_buffer = free_input_buffers_.front();
827 free_input_buffers_.pop();
828 OMX_ERRORTYPE result =
829 OMX_FreeBuffer(component_handle_, input_port_, omx_buffer);
830 if (result != OMX_ErrorNone) {
831 DLOG(ERROR) << "OMX_FreeBuffer failed: 0x" << std::hex << result;
832 failure_seen = true;
833 }
834 }
835 for (OutputPictureById::iterator it = pictures_.begin();
836 it != pictures_.end(); ++it) {
837 OMX_BUFFERHEADERTYPE* omx_buffer = it->second.omx_buffer_header;
838 DCHECK(omx_buffer);
839 delete reinterpret_cast<media::Picture*>(omx_buffer->pAppPrivate);
840 OMX_ERRORTYPE result =
841 OMX_FreeBuffer(component_handle_, output_port_, omx_buffer);
842 if (result != OMX_ErrorNone) {
843 DLOG(ERROR) << "OMX_FreeBuffer failed: 0x" << std::hex << result;
844 failure_seen = true;
845 }
846 texture_to_egl_image_translator_->DestroyEglImage(egl_display_,
847 it->second.egl_image);
848 if (client_)
849 client_->DismissPictureBuffer(it->first);
850 }
851 pictures_.clear();
852
853 // Delete pending fake_output_buffers_
854 for (std::set<OMX_BUFFERHEADERTYPE*>::iterator it =
855 fake_output_buffers_.begin();
856 it != fake_output_buffers_.end(); ++it) {
857 OMX_BUFFERHEADERTYPE* buffer = *it;
858 OMX_ERRORTYPE result =
859 OMX_FreeBuffer(component_handle_, output_port_, buffer);
860 if (result != OMX_ErrorNone) {
861 DLOG(ERROR) << "OMX_FreeBuffer failed: 0x" << std::hex << result;
862 failure_seen = true;
863 }
864 }
865 fake_output_buffers_.clear();
866
867 // Dequeue pending queued_picture_buffer_ids_
868 if (client_) {
869 for (size_t i = 0; i < queued_picture_buffer_ids_.size(); ++i)
870 client_->DismissPictureBuffer(queued_picture_buffer_ids_[i]);
871 }
872 queued_picture_buffer_ids_.clear();
873
874 RETURN_ON_FAILURE(!failure_seen, "OMX_FreeBuffer", PLATFORM_FAILURE,);
875 }
876
877 void OmxVideoDecodeAccelerator::OnOutputPortDisabled() {
878 DCHECK_EQ(message_loop_, base::MessageLoop::current());
879 OMX_PARAM_PORTDEFINITIONTYPE port_format;
880 InitParam(*this, &port_format);
881 port_format.nPortIndex = output_port_;
882 OMX_ERRORTYPE result = OMX_GetParameter(
883 component_handle_, OMX_IndexParamPortDefinition, &port_format);
884 RETURN_ON_OMX_FAILURE(result, "OMX_GetParameter", PLATFORM_FAILURE,);
885 DCHECK_LE(port_format.nBufferCountMin, kNumPictureBuffers);
886
887 // TODO(fischman): to support mid-stream resize, need to free/dismiss any
888 // |pictures_| we already have. Make sure that the shutdown-path agrees with
889 // this (there's already freeing logic there, which should not be duplicated).
890
891 // Request picture buffers to be handed to the component.
892 // ProvidePictureBuffers() will trigger AssignPictureBuffers, which ultimately
893 // assigns the textures to the component and re-enables the port.
894 const OMX_VIDEO_PORTDEFINITIONTYPE& vformat = port_format.format.video;
895 last_requested_picture_buffer_dimensions_.SetSize(vformat.nFrameWidth,
896 vformat.nFrameHeight);
897 if (client_) {
898 client_->ProvidePictureBuffers(
899 kNumPictureBuffers,
900 gfx::Size(vformat.nFrameWidth, vformat.nFrameHeight),
901 GL_TEXTURE_2D);
902 }
903 }
904
905 void OmxVideoDecodeAccelerator::OnOutputPortEnabled() {
906 DCHECK_EQ(message_loop_, base::MessageLoop::current());
907
908 if (current_state_change_ == RESETTING) {
909 for (OutputPictureById::iterator it = pictures_.begin();
910 it != pictures_.end(); ++it) {
911 queued_picture_buffer_ids_.push_back(it->first);
912 }
913 return;
914 }
915
916 if (!CanFillBuffer()) {
917 StopOnError(ILLEGAL_STATE);
918 return;
919 }
920
921 // Provide output buffers to decoder.
922 for (OutputPictureById::iterator it = pictures_.begin();
923 it != pictures_.end(); ++it) {
924 OMX_BUFFERHEADERTYPE* omx_buffer = it->second.omx_buffer_header;
925 DCHECK(omx_buffer);
926 // Clear EOS flag.
927 omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS;
928 omx_buffer->nOutputPortIndex = output_port_;
929 ++output_buffers_at_component_;
930 OMX_ERRORTYPE result = OMX_FillThisBuffer(component_handle_, omx_buffer);
931 RETURN_ON_OMX_FAILURE(result, "OMX_FillThisBuffer() failed",
932 PLATFORM_FAILURE,);
933 }
934 }
935
936 void OmxVideoDecodeAccelerator::FillBufferDoneTask(
937 OMX_BUFFERHEADERTYPE* buffer) {
938
939 media::Picture* picture =
940 reinterpret_cast<media::Picture*>(buffer->pAppPrivate);
941 int picture_buffer_id = picture ? picture->picture_buffer_id() : -1;
942 TRACE_EVENT2("Video Decoder", "OVDA::FillBufferDoneTask",
943 "Buffer id", buffer->nTimeStamp,
944 "Picture id", picture_buffer_id);
945 DCHECK_EQ(message_loop_, base::MessageLoop::current());
946 DCHECK_GT(output_buffers_at_component_, 0);
947 --output_buffers_at_component_;
948
949 // If we are destroying and then get a fillbuffer callback, calling into any
950 // openmax function will put us in error mode, so bail now. In the RESETTING
951 // case we still need to enqueue the picture ids but have to avoid giving
952 // them to the client (this is handled below).
953 if (current_state_change_ == DESTROYING ||
954 current_state_change_ == ERRORING)
955 return;
956
957 if (fake_output_buffers_.size() && fake_output_buffers_.count(buffer)) {
958 size_t erased = fake_output_buffers_.erase(buffer);
959 DCHECK_EQ(erased, 1U);
960 OMX_ERRORTYPE result =
961 OMX_FreeBuffer(component_handle_, output_port_, buffer);
962 RETURN_ON_OMX_FAILURE(result, "OMX_FreeBuffer failed", PLATFORM_FAILURE,);
963 return;
964 }
965 DCHECK(!fake_output_buffers_.size());
966
967 // When the EOS picture is delivered back to us, notify the client and reuse
968 // the underlying picturebuffer.
969 if (buffer->nFlags & OMX_BUFFERFLAG_EOS) {
970 buffer->nFlags &= ~OMX_BUFFERFLAG_EOS;
971 OnReachedEOSInFlushing();
972 ReusePictureBuffer(picture_buffer_id);
973 return;
974 }
975
976 // During the transition from Executing to Idle, and during port-flushing, all
977 // pictures are sent back through here. Avoid giving them to the client.
978 if (current_state_change_ == RESETTING) {
979 queued_picture_buffer_ids_.push_back(picture_buffer_id);
980 return;
981 }
982
983 DCHECK(picture);
984 // See Decode() for an explanation of this abuse of nTimeStamp.
985 picture->set_bitstream_buffer_id(buffer->nTimeStamp);
986 if (client_)
987 client_->PictureReady(*picture);
988 }
989
990 void OmxVideoDecodeAccelerator::EmptyBufferDoneTask(
991 OMX_BUFFERHEADERTYPE* buffer) {
992 TRACE_EVENT1("Video Decoder", "OVDA::EmptyBufferDoneTask",
993 "Buffer id", buffer->nTimeStamp);
994 DCHECK_EQ(message_loop_, base::MessageLoop::current());
995 DCHECK_GT(input_buffers_at_component_, 0);
996 free_input_buffers_.push(buffer);
997 input_buffers_at_component_--;
998 if (buffer->nFlags & OMX_BUFFERFLAG_EOS)
999 return;
1000
1001 // Retrieve the corresponding BitstreamBuffer's id and notify the client of
1002 // its completion.
1003 SharedMemoryAndId* input_buffer_details =
1004 reinterpret_cast<SharedMemoryAndId*>(buffer->pAppPrivate);
1005 DCHECK(input_buffer_details);
1006 buffer->pAppPrivate = NULL;
1007 if (client_)
1008 client_->NotifyEndOfBitstreamBuffer(input_buffer_details->second);
1009 delete input_buffer_details;
1010
1011 DecodeQueuedBitstreamBuffers();
1012 }
1013
1014 void OmxVideoDecodeAccelerator::DispatchStateReached(OMX_STATETYPE reached) {
1015 DCHECK_EQ(message_loop_, base::MessageLoop::current());
1016 switch (current_state_change_) {
1017 case INITIALIZING:
1018 switch (reached) {
1019 case OMX_StateIdle:
1020 OnReachedIdleInInitializing();
1021 return;
1022 case OMX_StateExecuting:
1023 OnReachedExecutingInInitializing();
1024 return;
1025 default:
1026 NOTREACHED() << "Unexpected state in INITIALIZING: " << reached;
1027 return;
1028 }
1029 case RESETTING:
1030 switch (reached) {
1031 case OMX_StatePause:
1032 OnReachedPauseInResetting();
1033 return;
1034 case OMX_StateExecuting:
1035 OnReachedExecutingInResetting();
1036 return;
1037 default:
1038 NOTREACHED() << "Unexpected state in RESETTING: " << reached;
1039 return;
1040 }
1041 case DESTROYING:
1042 switch (reached) {
1043 case OMX_StatePause:
1044 case OMX_StateExecuting:
1045 // Because Destroy() can interrupt an in-progress Reset(),
1046 // we might arrive at these states after current_state_change_ was
1047 // overwritten with DESTROYING. That's fine though - we already have
1048 // the state transition for Destroy() queued up at the component, so
1049 // we treat this as a no-op.
1050 return;
1051 case OMX_StateIdle:
1052 OnReachedIdleInDestroying();
1053 return;
1054 case OMX_StateLoaded:
1055 OnReachedLoadedInDestroying();
1056 return;
1057 default:
1058 NOTREACHED() << "Unexpected state in DESTROYING: " << reached;
1059 return;
1060 }
1061 case ERRORING:
1062 switch (reached) {
1063 case OMX_StateInvalid:
1064 OnReachedInvalidInErroring();
1065 return;
1066 default:
1067 NOTREACHED() << "Unexpected state in ERRORING: " << reached;
1068 return;
1069 }
1070 default:
1071 NOTREACHED() << "Unexpected state in " << current_state_change_
1072 << ": " << reached;
1073 }
1074 }
1075
1076 void OmxVideoDecodeAccelerator::EventHandlerCompleteTask(OMX_EVENTTYPE event,
1077 OMX_U32 data1,
1078 OMX_U32 data2) {
1079 DCHECK_EQ(message_loop_, base::MessageLoop::current());
1080 switch (event) {
1081 case OMX_EventCmdComplete:
1082 switch (data1) {
1083 case OMX_CommandPortDisable:
1084 DCHECK_EQ(data2, output_port_);
1085 OnOutputPortDisabled();
1086 return;
1087 case OMX_CommandPortEnable:
1088 DCHECK_EQ(data2, output_port_);
1089 OnOutputPortEnabled();
1090 return;
1091 case OMX_CommandStateSet:
1092 DispatchStateReached(static_cast<OMX_STATETYPE>(data2));
1093 return;
1094 case OMX_CommandFlush:
1095 if (current_state_change_ == DESTROYING ||
1096 current_state_change_ == ERRORING) {
1097 return;
1098 }
1099 DCHECK_EQ(current_state_change_, RESETTING);
1100 if (data2 == input_port_)
1101 InputPortFlushDone();
1102 else if (data2 == output_port_)
1103 OutputPortFlushDone();
1104 else
1105 NOTREACHED() << "Unexpected port flushed: " << data2;
1106 return;
1107 default:
1108 RETURN_ON_FAILURE(false, "Unknown command completed: " << data1,
1109 PLATFORM_FAILURE,);
1110 }
1111 return;
1112 case OMX_EventError:
1113 if (current_state_change_ != DESTROYING &&
1114 current_state_change_ != ERRORING) {
1115 RETURN_ON_FAILURE(false, "EventError: 0x" << std::hex << data1,
1116 PLATFORM_FAILURE,);
1117 }
1118 return;
1119 case OMX_EventPortSettingsChanged:
1120 if ((data2 == OMX_IndexParamPortDefinition) || // Tegra2/3
1121 (data2 == 0)) { // Exynos SEC-OMX; http://crosbug.com/p/11665
1122 DCHECK_EQ(data1, output_port_);
1123 // This event is only used for output resize; kick off handling that by
1124 // pausing the output port.
1125 SendCommandToPort(OMX_CommandPortDisable, output_port_);
1126 } else if (data1 == output_port_ &&
1127 data2 == OMX_IndexConfigCommonOutputCrop) {
1128 // TODO(vjain): Handle video crop rect.
1129 } else if (data1 == output_port_ &&
1130 data2 == OMX_IndexConfigCommonScale) {
1131 // TODO(ashokm@nvidia.com): Handle video SAR change.
1132 } else {
1133 RETURN_ON_FAILURE(false,
1134 "Unexpected EventPortSettingsChanged: "
1135 << data1 << ", " << data2,
1136 PLATFORM_FAILURE,);
1137 }
1138 return;
1139 case OMX_EventBufferFlag:
1140 if (data1 == output_port_) {
1141 // In case of Destroy() interrupting Flush().
1142 if (current_state_change_ == DESTROYING)
1143 return;
1144 DCHECK_EQ(current_state_change_, FLUSHING);
1145 // Do nothing; rely on the EOS picture delivery to notify the client.
1146 } else {
1147 RETURN_ON_FAILURE(false,
1148 "Unexpected OMX_EventBufferFlag: "
1149 << data1 << ", " << data2,
1150 PLATFORM_FAILURE,);
1151 }
1152 return;
1153 default:
1154 RETURN_ON_FAILURE(false, "Unexpected unhandled event: " << event,
1155 PLATFORM_FAILURE,);
1156 }
1157 }
1158
1159 // static
1160 void OmxVideoDecodeAccelerator::PreSandboxInitialization() {
1161 DCHECK(!pre_sandbox_init_done_);
1162 omx_handle = dlopen("libOmxCore.so", RTLD_NOW);
1163 pre_sandbox_init_done_ = omx_handle != NULL;
1164 }
1165
1166 // static
1167 bool OmxVideoDecodeAccelerator::PostSandboxInitialization() {
1168 if (!pre_sandbox_init_done_)
1169 return false;
1170
1171 omx_init = reinterpret_cast<OMXInit>(dlsym(omx_handle, "OMX_Init"));
1172 omx_gethandle =
1173 reinterpret_cast<OMXGetHandle>(dlsym(omx_handle, "OMX_GetHandle"));
1174 omx_get_components_of_role =
1175 reinterpret_cast<OMXGetComponentsOfRole>(
1176 dlsym(omx_handle, "OMX_GetComponentsOfRole"));
1177 omx_free_handle =
1178 reinterpret_cast<OMXFreeHandle>(dlsym(omx_handle, "OMX_FreeHandle"));
1179 omx_deinit =
1180 reinterpret_cast<OMXDeinit>(dlsym(omx_handle, "OMX_Deinit"));
1181
1182 return (omx_init && omx_gethandle && omx_get_components_of_role &&
1183 omx_free_handle && omx_deinit);
1184 }
1185
1186 // static
1187 OMX_ERRORTYPE OmxVideoDecodeAccelerator::EventHandler(OMX_HANDLETYPE component,
1188 OMX_PTR priv_data,
1189 OMX_EVENTTYPE event,
1190 OMX_U32 data1,
1191 OMX_U32 data2,
1192 OMX_PTR event_data) {
1193 // Called on the OMX thread.
1194 OmxVideoDecodeAccelerator* decoder =
1195 static_cast<OmxVideoDecodeAccelerator*>(priv_data);
1196 DCHECK_EQ(component, decoder->component_handle_);
1197 decoder->message_loop_->PostTask(FROM_HERE, base::Bind(
1198 &OmxVideoDecodeAccelerator::EventHandlerCompleteTask,
1199 decoder->weak_this(), event, data1, data2));
1200 return OMX_ErrorNone;
1201 }
1202
1203 // static
1204 OMX_ERRORTYPE OmxVideoDecodeAccelerator::EmptyBufferCallback(
1205 OMX_HANDLETYPE component,
1206 OMX_PTR priv_data,
1207 OMX_BUFFERHEADERTYPE* buffer) {
1208 TRACE_EVENT1("Video Decoder", "OVDA::EmptyBufferCallback",
1209 "Buffer id", buffer->nTimeStamp);
1210 // Called on the OMX thread.
1211 OmxVideoDecodeAccelerator* decoder =
1212 static_cast<OmxVideoDecodeAccelerator*>(priv_data);
1213 DCHECK_EQ(component, decoder->component_handle_);
1214 decoder->message_loop_->PostTask(FROM_HERE, base::Bind(
1215 &OmxVideoDecodeAccelerator::EmptyBufferDoneTask, decoder->weak_this(),
1216 buffer));
1217 return OMX_ErrorNone;
1218 }
1219
1220 // static
1221 OMX_ERRORTYPE OmxVideoDecodeAccelerator::FillBufferCallback(
1222 OMX_HANDLETYPE component,
1223 OMX_PTR priv_data,
1224 OMX_BUFFERHEADERTYPE* buffer) {
1225 media::Picture* picture =
1226 reinterpret_cast<media::Picture*>(buffer->pAppPrivate);
1227 int picture_buffer_id = picture ? picture->picture_buffer_id() : -1;
1228 TRACE_EVENT2("Video Decoder", "OVDA::FillBufferCallback",
1229 "Buffer id", buffer->nTimeStamp,
1230 "Picture id", picture_buffer_id);
1231 // Called on the OMX thread.
1232 OmxVideoDecodeAccelerator* decoder =
1233 static_cast<OmxVideoDecodeAccelerator*>(priv_data);
1234 DCHECK_EQ(component, decoder->component_handle_);
1235 decoder->message_loop_->PostTask(FROM_HERE, base::Bind(
1236 &OmxVideoDecodeAccelerator::FillBufferDoneTask, decoder->weak_this(),
1237 buffer));
1238 return OMX_ErrorNone;
1239 }
1240
1241 bool OmxVideoDecodeAccelerator::CanFillBuffer() {
1242 DCHECK_EQ(message_loop_, base::MessageLoop::current());
1243 const CurrentStateChange csc = current_state_change_;
1244 const OMX_STATETYPE cs = client_state_;
1245 return (csc != DESTROYING && csc != ERRORING && csc != RESETTING) &&
1246 (cs == OMX_StateIdle || cs == OMX_StateExecuting || cs == OMX_StatePause);
1247 }
1248
1249 bool OmxVideoDecodeAccelerator::SendCommandToPort(
1250 OMX_COMMANDTYPE cmd, int port_index) {
1251 DCHECK_EQ(message_loop_, base::MessageLoop::current());
1252 OMX_ERRORTYPE result = OMX_SendCommand(component_handle_,
1253 cmd, port_index, 0);
1254 RETURN_ON_OMX_FAILURE(result, "SendCommand() failed" << cmd,
1255 PLATFORM_FAILURE, false);
1256 return true;
1257 }
1258
1259 } // namespace content
OLDNEW
« no previous file with comments | « content/common/gpu/media/omx_video_decode_accelerator.h ('k') | content/common/gpu/media/video_decode_accelerator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698