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 #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 | |
OLD | NEW |