OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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/omx_video_decode_accelerator.h" | |
6 | |
7 #include "base/stl_util-inl.h" | |
8 #include "base/string_util.h" | |
9 #include "content/common/gpu/gles2_texture_to_egl_image_translator.h" | |
10 #include "content/common/gpu/gpu_channel.h" | |
11 #include "media/base/bitstream_buffer.h" | |
12 #include "media/video/picture.h" | |
13 | |
14 enum { kNumPictureBuffers = 4 }; | |
15 | |
16 // Open the libnvomx here for now. | |
17 void* omx_handle = dlopen("libnvomx.so", RTLD_NOW); | |
18 | |
19 typedef OMX_ERRORTYPE (*OMXInit)(); | |
20 typedef OMX_ERRORTYPE (*OMXGetHandle)( | |
21 OMX_HANDLETYPE*, OMX_STRING, OMX_PTR, OMX_CALLBACKTYPE*); | |
22 typedef OMX_ERRORTYPE (*OMXGetComponentsOfRole)(OMX_STRING, OMX_U32*, OMX_U8**); | |
23 typedef OMX_ERRORTYPE (*OMXFreeHandle)(OMX_HANDLETYPE); | |
24 typedef OMX_ERRORTYPE (*OMXDeinit)(); | |
25 | |
26 OMXInit omx_init = reinterpret_cast<OMXInit>(dlsym(omx_handle, "OMX_Init")); | |
27 OMXGetHandle omx_gethandle = | |
28 reinterpret_cast<OMXGetHandle>(dlsym(omx_handle, "OMX_GetHandle")); | |
29 OMXGetComponentsOfRole omx_get_components_of_role = | |
30 reinterpret_cast<OMXGetComponentsOfRole>( | |
31 dlsym(omx_handle, "OMX_GetComponentsOfRole")); | |
32 OMXFreeHandle omx_free_handle = | |
33 reinterpret_cast<OMXFreeHandle>(dlsym(omx_handle, "OMX_FreeHandle")); | |
34 OMXDeinit omx_deinit = | |
35 reinterpret_cast<OMXDeinit>(dlsym(omx_handle, "OMX_Deinit")); | |
36 | |
37 static bool AreOMXFunctionPointersInitialized() { | |
38 return (omx_init && omx_gethandle && omx_get_components_of_role && | |
39 omx_free_handle && omx_deinit); | |
40 } | |
41 | |
42 OmxVideoDecodeAccelerator::OmxVideoDecodeAccelerator( | |
43 media::VideoDecodeAccelerator::Client* client, | |
44 MessageLoop* message_loop) | |
45 : message_loop_(message_loop), | |
46 component_handle_(NULL), | |
47 width_(-1), | |
48 height_(-1), | |
49 input_buffer_count_(0), | |
50 input_buffer_size_(0), | |
51 input_port_(0), | |
52 input_buffers_at_component_(0), | |
53 output_buffer_count_(0), | |
54 output_buffer_size_(0), | |
55 output_port_(0), | |
56 output_buffers_at_component_(0), | |
57 uses_egl_image_(false), | |
58 client_(client) { | |
59 if (!AreOMXFunctionPointersInitialized()) { | |
60 LOG(ERROR) << "Failed to load openmax library"; | |
61 return; | |
62 } | |
63 OMX_ERRORTYPE result = omx_init(); | |
64 if (result != OMX_ErrorNone) | |
65 LOG(ERROR) << "Failed to init OpenMAX core"; | |
66 } | |
67 | |
68 OmxVideoDecodeAccelerator::~OmxVideoDecodeAccelerator() { | |
69 DCHECK(free_input_buffers_.empty()); | |
70 DCHECK_EQ(0, input_buffers_at_component_); | |
71 DCHECK_EQ(0, output_buffers_at_component_); | |
72 DCHECK(output_pictures_.empty()); | |
73 } | |
74 | |
75 void OmxVideoDecodeAccelerator::GetConfigs( | |
76 const std::vector<uint32>& requested_configs, | |
77 std::vector<uint32>* matched_configs) { | |
78 // TODO(vhiremath@nvidia.com) use this properly | |
79 NOTIMPLEMENTED(); | |
80 } | |
81 | |
82 // This is to initialize the OMX data structures to default values. | |
83 template <typename T> | |
84 static void InitParam(const OmxVideoDecodeAccelerator& dec, T* param) { | |
85 memset(param, 0, sizeof(T)); | |
86 param->nVersion.nVersion = 0x00000101; | |
87 param->nSize = sizeof(T); | |
88 } | |
89 | |
90 bool OmxVideoDecodeAccelerator::Initialize(const std::vector<uint32>& config) { | |
91 // TODO(vhiremath@nvidia.com) get these actual values from config | |
92 // Assume qvga for now | |
93 width_ = 320; | |
94 height_ = 240; | |
95 | |
96 client_state_ = OMX_StateLoaded; | |
97 if (!CreateComponent()) { | |
98 StopOnError(); | |
99 return false; | |
100 } | |
101 // Transition component to Idle state | |
102 on_state_event_func_ = | |
103 &OmxVideoDecodeAccelerator::OnStateChangeLoadedToIdle; | |
104 if (!TransitionToState(OMX_StateIdle)) | |
105 return false; | |
106 | |
107 if (!AllocateInputBuffers()) { | |
108 LOG(ERROR) << "OMX_AllocateBuffer() Input buffer error"; | |
109 StopOnError(); | |
110 return false; | |
111 } | |
112 | |
113 // After AllocateInputBuffers ideally this should be AllocateOutputBuffers. | |
114 // Since in this case app provides the output buffers, | |
115 // we query this through ProvidePictureBuffers. | |
116 // This is call to ppapi to provide the output buffers initially. | |
117 // ProvidePictureBuffers will provide | |
118 // - SharedMemHandle in case of decoding to system memory. | |
119 // - Textures in case of decoding to egl-images. | |
120 | |
121 // Output buffers will be eventually handed to us via | |
122 // Assign{GLES,Sysmem}Buffers(). | |
123 output_buffer_count_ = kNumPictureBuffers; | |
124 client_->ProvidePictureBuffers( | |
125 output_buffer_count_, gfx::Size(width_, height_), | |
126 PICTUREBUFFER_MEMORYTYPE_GL_TEXTURE); | |
127 // TODO(fischman): we always ask for GLES buffers above. So why maintain the | |
128 // !uses_egl_image_ path in this class at all? Theoretically it could be | |
129 // useful for testing, but today there's no such testing. Consider ripping it | |
130 // out of this class and replacing AssignSysmemBuffers() with | |
131 // NOTIMPLEMENTED(). | |
132 return true; | |
133 } | |
134 | |
135 bool OmxVideoDecodeAccelerator::CreateComponent() { | |
136 OMX_CALLBACKTYPE omx_accelerator_callbacks = { | |
137 &OmxVideoDecodeAccelerator::EventHandler, | |
138 &OmxVideoDecodeAccelerator::EmptyBufferCallback, | |
139 &OmxVideoDecodeAccelerator::FillBufferCallback | |
140 }; | |
141 OMX_ERRORTYPE result = OMX_ErrorNone; | |
142 | |
143 // 1. Set the role and get all components of this role. | |
144 // TODO(vhiremath@nvidia.com) Get this role_name from the configs | |
145 // For now hard coding to avc. | |
146 const char* role_name = "video_decoder.avc"; | |
147 OMX_U32 num_roles = 0; | |
148 // Get all the components with this role. | |
149 result = (*omx_get_components_of_role)( | |
150 const_cast<OMX_STRING>(role_name), &num_roles, 0); | |
151 if (result != OMX_ErrorNone || num_roles == 0) { | |
152 LOG(ERROR) << "Unsupported Role: " << role_name; | |
153 StopOnError(); | |
154 return false; | |
155 } | |
156 | |
157 // We haven't seen HW that needs more yet, | |
158 // but there is no reason not to raise. | |
159 const OMX_U32 kMaxRolePerComponent = 3; | |
160 CHECK_LT(num_roles, kMaxRolePerComponent); | |
161 | |
162 scoped_array<scoped_array<OMX_U8> > component_names( | |
163 new scoped_array<OMX_U8>[num_roles]); | |
164 for (size_t i = 0; i < num_roles; ++i) | |
165 component_names[i].reset(new OMX_U8[OMX_MAX_STRINGNAME_SIZE]); | |
166 result = (*omx_get_components_of_role)( | |
167 const_cast<OMX_STRING>(role_name), | |
168 &num_roles, reinterpret_cast<OMX_U8**>(component_names.get())); | |
169 | |
170 // Use first component only. Copy the name of the first component | |
171 // so that we could free the memory. | |
172 std::string component_name; | |
173 if (result == OMX_ErrorNone) | |
174 component_name = reinterpret_cast<char*>(component_names[0].get()); | |
175 | |
176 if (result != OMX_ErrorNone || num_roles == 0) { | |
177 LOG(ERROR) << "Unsupported Role: " << component_name.c_str(); | |
178 StopOnError(); | |
179 return false; | |
180 } | |
181 | |
182 // 3. Get the handle to the component. | |
183 // After OMX_GetHandle(), the component is in loaded state. | |
184 OMX_STRING component = const_cast<OMX_STRING>(component_name.c_str()); | |
185 result = omx_gethandle(&component_handle_, component, this, | |
186 &omx_accelerator_callbacks); | |
187 if (result != OMX_ErrorNone) { | |
188 LOG(ERROR) << "Failed to Load the component: " << component; | |
189 StopOnError(); | |
190 return false; | |
191 } | |
192 | |
193 // 4. Get the port information. This will obtain information about the | |
194 // number of ports and index of the first port. | |
195 OMX_PORT_PARAM_TYPE port_param; | |
196 InitParam(*this, &port_param); | |
197 result = OMX_GetParameter(component_handle_, OMX_IndexParamVideoInit, | |
198 &port_param); | |
199 if ((result != OMX_ErrorNone) || (port_param.nPorts != 2)) { | |
200 LOG(ERROR) << "Failed to get Port Param"; | |
201 StopOnError(); | |
202 return false; | |
203 } | |
204 input_port_ = port_param.nStartPortNumber; | |
205 output_port_ = input_port_ + 1; | |
206 // 5. Set role for the component because components can have multiple roles. | |
207 OMX_PARAM_COMPONENTROLETYPE role_type; | |
208 InitParam(*this, &role_type); | |
209 base::strlcpy(reinterpret_cast<char*>(role_type.cRole), | |
210 role_name, | |
211 OMX_MAX_STRINGNAME_SIZE); | |
212 | |
213 result = OMX_SetParameter(component_handle_, | |
214 OMX_IndexParamStandardComponentRole, | |
215 &role_type); | |
216 if (result != OMX_ErrorNone) { | |
217 LOG(ERROR) << "Failed to Set Role"; | |
218 StopOnError(); | |
219 return false; | |
220 } | |
221 | |
222 // 7. Populate input-buffer-related members based on input port data. | |
223 OMX_PARAM_PORTDEFINITIONTYPE port_format; | |
224 InitParam(*this, &port_format); | |
225 port_format.nPortIndex = input_port_; | |
226 result = OMX_GetParameter(component_handle_, | |
227 OMX_IndexParamPortDefinition, | |
228 &port_format); | |
229 if (result != OMX_ErrorNone) { | |
230 LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed"; | |
231 StopOnError(); | |
232 return false; | |
233 } | |
234 if (OMX_DirInput != port_format.eDir) { | |
235 LOG(ERROR) << "Expected input port"; | |
236 StopOnError(); | |
237 return false; | |
238 } | |
239 input_buffer_count_ = port_format.nBufferCountActual; | |
240 input_buffer_size_ = port_format.nBufferSize; | |
241 | |
242 // OMX_IndexParamPortDefinition on output port to be done in | |
243 // AllocateOutputBuffers. | |
244 // Since at this point we dont know if we will be using system memory | |
245 // or egl-image for decoding. | |
246 // We get this info in AssignPictureBuffers() from plugin. | |
247 | |
248 return true; | |
249 } | |
250 | |
251 bool OmxVideoDecodeAccelerator::Decode( | |
252 const media::BitstreamBuffer& bitstream_buffer) { | |
253 DCHECK(!free_input_buffers_.empty()); | |
254 | |
255 if (!CanAcceptInput()) { | |
256 return false; | |
257 } | |
258 | |
259 OMX_BUFFERHEADERTYPE* omx_buffer = free_input_buffers_.front(); | |
260 free_input_buffers_.pop(); | |
261 | |
262 // Setup |omx_buffer|. | |
263 scoped_ptr<base::SharedMemory> shm( | |
264 new base::SharedMemory(bitstream_buffer.handle(), true)); | |
265 if (!shm->Map(bitstream_buffer.size())) { | |
266 LOG(ERROR) << "Failed to SharedMemory::Map()."; | |
267 return false; | |
268 } | |
269 omx_buffer->pBuffer = static_cast<OMX_U8*>(shm->memory()); | |
270 omx_buffer->nFilledLen = bitstream_buffer.size(); | |
271 omx_buffer->nAllocLen = omx_buffer->nFilledLen; | |
272 | |
273 omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS; | |
274 omx_buffer->nTimeStamp = 0; | |
275 | |
276 // Give this buffer to OMX. | |
277 OMX_ERRORTYPE result = OMX_ErrorNone; | |
278 result = OMX_EmptyThisBuffer(component_handle_, omx_buffer); | |
279 if (result != OMX_ErrorNone) { | |
280 LOG(ERROR) << "OMX_EmptyThisBuffer() failed with result " << result; | |
281 StopOnError(); | |
282 return false; | |
283 } | |
284 input_buffers_at_component_++; | |
285 // OMX_EmptyThisBuffer is a non blocking call and should | |
286 // not make any assumptions about its completion. | |
287 omx_buff_ids_.insert(std::make_pair( | |
288 omx_buffer, std::make_pair(shm.release(), bitstream_buffer.id()))); | |
289 return true; | |
290 } | |
291 | |
292 // NOTE: this is only partially-implemented as never unsets uses_egl_image_ once | |
293 // set. | |
294 void OmxVideoDecodeAccelerator::AssignGLESBuffers( | |
295 const std::vector<media::GLESBuffer>& buffers) { | |
296 uses_egl_image_ = true; | |
297 std::vector<media::BaseBuffer*> base_buffers(buffers.size()); | |
298 for (size_t i = 0; i < buffers.size(); ++i) | |
299 base_buffers[i] = new media::GLESBuffer(buffers[i]); | |
300 AssignBuffersHelper(base_buffers); | |
301 } | |
302 | |
303 void OmxVideoDecodeAccelerator::AssignSysmemBuffers( | |
304 const std::vector<media::SysmemBuffer>& buffers) { | |
305 DCHECK(!uses_egl_image_); | |
306 std::vector<media::BaseBuffer*> base_buffers(buffers.size()); | |
307 for (size_t i = 0; i < buffers.size(); ++i) | |
308 base_buffers[i] = new media::SysmemBuffer(buffers[i]); | |
309 AssignBuffersHelper(base_buffers); | |
310 } | |
311 | |
312 void OmxVideoDecodeAccelerator::AssignBuffersHelper( | |
313 const std::vector<media::BaseBuffer*>& buffers) { | |
314 assigned_picture_buffers_.insert( | |
315 assigned_picture_buffers_.end(), buffers.begin(), buffers.end()); | |
316 | |
317 if (assigned_picture_buffers_.size() < kNumPictureBuffers) | |
318 return; // get all the buffers first. | |
319 | |
320 // Obtain the information about the output port. | |
321 OMX_PARAM_PORTDEFINITIONTYPE port_format; | |
322 InitParam(*this, &port_format); | |
323 port_format.nPortIndex = output_port_; | |
324 OMX_ERRORTYPE result = OMX_GetParameter(component_handle_, | |
325 OMX_IndexParamPortDefinition, | |
326 &port_format); | |
327 if (result != OMX_ErrorNone) { | |
328 LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed"; | |
329 StopOnError(); | |
330 return; | |
331 } | |
332 if (OMX_DirOutput != port_format.eDir) { | |
333 LOG(ERROR) << "Expect Output Port"; | |
334 StopOnError(); | |
335 return; | |
336 } | |
337 | |
338 if (uses_egl_image_) { | |
339 port_format.nBufferCountActual = kNumPictureBuffers; | |
340 port_format.nBufferCountMin = kNumPictureBuffers; | |
341 output_buffer_count_ = kNumPictureBuffers; | |
342 | |
343 result = OMX_SetParameter(component_handle_, | |
344 OMX_IndexParamPortDefinition, | |
345 &port_format); | |
346 if (result != OMX_ErrorNone) { | |
347 LOG(ERROR) << "SetParameter(OMX_IndexParamPortDefinition) failed"; | |
348 StopOnError(); | |
349 return; | |
350 } | |
351 } else { | |
352 output_buffer_count_ = port_format.nBufferCountActual; | |
353 } | |
354 output_buffer_size_ = port_format.nBufferSize; | |
355 | |
356 if (!AllocateOutputBuffers()) { | |
357 LOG(ERROR) << "OMX_AllocateBuffer() Output buffer error"; | |
358 StopOnError(); | |
359 } | |
360 } | |
361 | |
362 void OmxVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) { | |
363 // TODO(vhiremath@nvidia.com) Avoid leaking of the picture buffer. | |
364 if (!CanFillBuffer()) | |
365 return; | |
366 | |
367 for (int i = 0; i < output_buffer_count_; ++i) { | |
368 if (picture_buffer_id != assigned_picture_buffers_[i]->id()) | |
369 continue; | |
370 output_buffers_at_component_++; | |
371 OMX_ERRORTYPE result = | |
372 OMX_FillThisBuffer(component_handle_, output_pictures_[i].second); | |
373 if (result != OMX_ErrorNone) { | |
374 LOG(ERROR) << "OMX_FillThisBuffer() failed with result " << result; | |
375 StopOnError(); | |
376 } | |
377 // Sent one buffer to omx. | |
378 return; | |
379 } | |
380 } | |
381 | |
382 void OmxVideoDecodeAccelerator::InitialFillBuffer() { | |
383 if (!CanFillBuffer()) | |
384 return; | |
385 | |
386 // Ask the decoder to fill the output buffers. | |
387 for (uint32 i = 0; i < output_pictures_.size(); ++i) { | |
388 OMX_BUFFERHEADERTYPE* omx_buffer = output_pictures_[i].second; | |
389 // clear EOS flag. | |
390 omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS; | |
391 omx_buffer->nOutputPortIndex = output_port_; | |
392 output_buffers_at_component_++; | |
393 OMX_ERRORTYPE result = OMX_FillThisBuffer(component_handle_, omx_buffer); | |
394 if (result != OMX_ErrorNone) { | |
395 LOG(ERROR) << "OMX_FillThisBuffer() failed with result " << result; | |
396 StopOnError(); | |
397 return; | |
398 } | |
399 } | |
400 } | |
401 | |
402 bool OmxVideoDecodeAccelerator::Flush() { | |
403 OMX_STATETYPE il_state; | |
404 OMX_GetState(component_handle_, &il_state); | |
405 DCHECK_EQ(il_state, OMX_StateExecuting); | |
406 if (il_state != OMX_StateExecuting) { | |
407 client_->NotifyFlushDone(); | |
408 return false; | |
409 } | |
410 on_buffer_flag_event_func_ = &OmxVideoDecodeAccelerator::FlushBegin; | |
411 | |
412 OMX_BUFFERHEADERTYPE* omx_buffer = free_input_buffers_.front(); | |
413 free_input_buffers_.pop(); | |
414 omx_buffer->nFilledLen = 0; | |
415 omx_buffer->nAllocLen = omx_buffer->nFilledLen; | |
416 omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS; | |
417 omx_buffer->nTimeStamp = 0; | |
418 // Give this buffer to OMX. | |
419 OMX_ERRORTYPE result = OMX_ErrorNone; | |
420 result = OMX_EmptyThisBuffer(component_handle_, omx_buffer); | |
421 if (result != OMX_ErrorNone) { | |
422 LOG(ERROR) << "OMX_EmptyThisBuffer() failed with result " << result; | |
423 StopOnError(); | |
424 return false; | |
425 } | |
426 input_buffers_at_component_++; | |
427 return true; | |
428 } | |
429 | |
430 void OmxVideoDecodeAccelerator::FlushBegin() { | |
431 VLOG(1) << "Starting actual flush for EOS"; | |
432 on_state_event_func_ = &OmxVideoDecodeAccelerator::PauseFromExecuting; | |
433 TransitionToState(OMX_StatePause); | |
434 } | |
435 | |
436 void OmxVideoDecodeAccelerator::PauseFromExecuting(OMX_STATETYPE ignored) { | |
437 on_state_event_func_ = NULL; | |
438 FlushIOPorts(); | |
439 } | |
440 | |
441 void OmxVideoDecodeAccelerator::FlushIOPorts() { | |
442 // TODO(vhiremath@nvidia.com) review again for trick modes. | |
443 VLOG(1) << "FlushIOPorts"; | |
444 | |
445 // Flush input port first. | |
446 on_flush_event_func_ = &OmxVideoDecodeAccelerator::PortFlushDone; | |
447 OMX_ERRORTYPE result; | |
448 result = OMX_SendCommand(component_handle_, | |
449 OMX_CommandFlush, | |
450 input_port_, 0); | |
451 if (result != OMX_ErrorNone) { | |
452 LOG(ERROR) << "OMX_SendCommand(OMX_CommandFlush) failed"; | |
453 StopOnError(); | |
454 return; | |
455 } | |
456 } | |
457 | |
458 void OmxVideoDecodeAccelerator::PortFlushDone(int port) { | |
459 DCHECK_NE(port, static_cast<int>(OMX_ALL)); | |
460 | |
461 if (port == input_port_) { | |
462 VLOG(1) << "Input Port had been flushed"; | |
463 DCHECK_EQ(input_buffers_at_component_, 0); | |
464 // Flush output port next. | |
465 OMX_ERRORTYPE result; | |
466 result = OMX_SendCommand(component_handle_, | |
467 OMX_CommandFlush, | |
468 output_port_, 0); | |
469 if (result != OMX_ErrorNone) { | |
470 LOG(ERROR) << "OMX_SendCommand(OMX_CommandFlush) failed"; | |
471 StopOnError(); | |
472 return; | |
473 } | |
474 return; | |
475 } | |
476 | |
477 if (port == output_port_) { | |
478 VLOG(1) << "Output Port had been flushed"; | |
479 DCHECK_EQ(output_buffers_at_component_, 0); | |
480 } | |
481 | |
482 client_state_ = OMX_StatePause; | |
483 // So Finally call OnPortCommandFlush which should | |
484 // internally call DismissPictureBuffer(); | |
485 OnPortCommandFlush(OMX_StateExecuting); | |
486 } | |
487 | |
488 bool OmxVideoDecodeAccelerator::Abort() { | |
489 // TODO(vhiremath@nvidia.com) | |
490 // Need more thinking on this to handle w.r.t OMX. | |
491 // There is no explicit UnInitialize call for this. | |
492 // Also review again for trick modes. | |
493 client_->NotifyAbortDone(); | |
494 return true; | |
495 } | |
496 | |
497 // Event callback during initialization to handle DoneStateSet to idle | |
498 void OmxVideoDecodeAccelerator::OnStateChangeLoadedToIdle(OMX_STATETYPE state) { | |
499 DCHECK_EQ(client_state_, OMX_StateLoaded); | |
500 DCHECK_EQ(OMX_StateIdle, state); | |
501 VLOG(1) << "OMX video decode engine is in Idle"; | |
502 | |
503 on_state_event_func_ = | |
504 &OmxVideoDecodeAccelerator::OnStateChangeIdleToExecuting; | |
505 if (!TransitionToState(OMX_StateExecuting)) | |
506 return; | |
507 } | |
508 | |
509 // Event callback during initialization to handle DoneStateSet to executing | |
510 void OmxVideoDecodeAccelerator::OnStateChangeIdleToExecuting( | |
511 OMX_STATETYPE state) { | |
512 DCHECK_EQ(OMX_StateExecuting, state); | |
513 VLOG(1) << "OMX video decode engine is in Executing"; | |
514 | |
515 client_state_ = OMX_StateExecuting; | |
516 on_state_event_func_ = NULL; | |
517 // This will kickoff the actual decoding | |
518 InitialFillBuffer(); | |
519 } | |
520 | |
521 // Send state transition command to component. | |
522 bool OmxVideoDecodeAccelerator::TransitionToState(OMX_STATETYPE new_state) { | |
523 OMX_ERRORTYPE result = OMX_SendCommand(component_handle_, | |
524 OMX_CommandStateSet, | |
525 new_state, 0); | |
526 if (result != OMX_ErrorNone) { | |
527 LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; | |
528 StopOnError(); | |
529 return false; | |
530 } | |
531 return true; | |
532 } | |
533 | |
534 void OmxVideoDecodeAccelerator::OnPortCommandFlush(OMX_STATETYPE state) { | |
535 DCHECK_EQ(state, OMX_StateExecuting); | |
536 | |
537 VLOG(1) << "Deinit from Executing"; | |
538 on_state_event_func_ = | |
539 &OmxVideoDecodeAccelerator::OnStateChangeExecutingToIdle; | |
540 TransitionToState(OMX_StateIdle); | |
541 for (int i = 0; i < output_buffer_count_; ++i) { | |
542 OutputPicture output_picture = output_pictures_[i]; | |
543 client_->DismissPictureBuffer(output_picture.first); | |
544 } | |
545 STLDeleteElements(&assigned_picture_buffers_); | |
546 } | |
547 | |
548 void OmxVideoDecodeAccelerator::OnStateChangeExecutingToIdle( | |
549 OMX_STATETYPE state) { | |
550 DCHECK_EQ(state, OMX_StateIdle); | |
551 | |
552 VLOG(1) << "Deinit from Idle"; | |
553 on_state_event_func_ = | |
554 &OmxVideoDecodeAccelerator::OnStateChangeIdleToLoaded; | |
555 TransitionToState(OMX_StateLoaded); | |
556 | |
557 if (!input_buffers_at_component_) | |
558 FreeInputBuffers(); | |
559 | |
560 if (!output_buffers_at_component_) | |
561 FreeOutputBuffers(); | |
562 } | |
563 | |
564 void OmxVideoDecodeAccelerator::OnStateChangeIdleToLoaded(OMX_STATETYPE state) { | |
565 DCHECK_EQ(state, OMX_StateLoaded); | |
566 | |
567 VLOG(1) << "Idle to Loaded"; | |
568 | |
569 if (component_handle_) { | |
570 OMX_ERRORTYPE result = (*omx_free_handle)(component_handle_); | |
571 if (result != OMX_ErrorNone) | |
572 LOG(ERROR) << "OMX_FreeHandle() error. Error code: " << result; | |
573 component_handle_ = NULL; | |
574 } | |
575 client_state_ = OMX_StateLoaded; | |
576 (*omx_deinit)(); | |
577 VLOG(1) << "OMX Deinit Clean exit done"; | |
578 client_->NotifyFlushDone(); | |
579 } | |
580 | |
581 void OmxVideoDecodeAccelerator::StopOnError() { | |
582 OMX_STATETYPE il_state; | |
583 OMX_GetState(component_handle_, &il_state); | |
584 client_state_ = OMX_StateInvalid; | |
585 switch (il_state) { | |
586 case OMX_StateExecuting: | |
587 OnPortCommandFlush(OMX_StateExecuting); | |
588 return; | |
589 case OMX_StateIdle: | |
590 OnStateChangeExecutingToIdle(OMX_StateIdle); | |
591 return; | |
592 case OMX_StateLoaded: | |
593 OnStateChangeIdleToLoaded(OMX_StateLoaded); | |
594 return; | |
595 default: | |
596 // LOG unexpected state or just ignore? | |
597 return; | |
598 } | |
599 } | |
600 | |
601 bool OmxVideoDecodeAccelerator::AllocateInputBuffers() { | |
602 scoped_array<uint8> data(new uint8[input_buffer_size_]); | |
603 | |
604 for (int i = 0; i < input_buffer_count_; ++i) { | |
605 OMX_BUFFERHEADERTYPE* buffer; | |
606 OMX_ERRORTYPE result = | |
607 OMX_UseBuffer(component_handle_, &buffer, input_port_, | |
608 this, input_buffer_size_, data.get()); | |
609 if (result != OMX_ErrorNone) | |
610 return false; | |
611 buffer->nInputPortIndex = input_port_; | |
612 buffer->nOffset = 0; | |
613 buffer->nFlags = 0; | |
614 free_input_buffers_.push(buffer); | |
615 } | |
616 return true; | |
617 } | |
618 | |
619 bool OmxVideoDecodeAccelerator::AllocateOutputBuffers() { | |
620 static Gles2TextureToEglImageTranslator* texture2eglImage_translator( | |
621 new Gles2TextureToEglImageTranslator(NULL, 0)); | |
622 | |
623 gfx::Size decoded_pixel_size(width_, height_); | |
624 gfx::Size visible_pixel_size(width_, height_); | |
625 // TODO(fischman): remove garbage bitstream buffer id's below (42 and 24) when | |
626 // the bitstream_buffer_id field is removed from Picture. | |
627 if (uses_egl_image_) { | |
628 for (uint32 i = 0; i < assigned_picture_buffers_.size(); i++) { | |
629 media::GLESBuffer* gles_buffer = | |
630 reinterpret_cast<media::GLESBuffer*>(assigned_picture_buffers_[i]); | |
631 OMX_BUFFERHEADERTYPE* omx_buffer; | |
632 void* egl = texture2eglImage_translator->TranslateToEglImage( | |
633 gles_buffer->texture_id()); | |
634 OMX_ERRORTYPE result = OMX_UseEGLImage( | |
635 component_handle_, &omx_buffer, output_port_, gles_buffer, egl); | |
636 if (result != OMX_ErrorNone) { | |
637 LOG(ERROR) << "OMX_UseEGLImage failed"; | |
638 return false; | |
639 } | |
640 omx_buffer->pAppPrivate = | |
641 new media::Picture(gles_buffer->id(), | |
642 42 /* garbage bitstreambuffer id */, | |
643 decoded_pixel_size, visible_pixel_size); | |
644 output_pictures_.push_back( | |
645 std::make_pair(assigned_picture_buffers_[i]->id(), omx_buffer)); | |
646 } | |
647 } else { | |
648 for (uint32 i = 0; i < assigned_picture_buffers_.size(); i++) { | |
649 media::SysmemBuffer* sysmem_buffer = | |
650 reinterpret_cast<media::SysmemBuffer*>(assigned_picture_buffers_[i]); | |
651 OMX_BUFFERHEADERTYPE* omx_buffer; | |
652 OMX_ERRORTYPE result = OMX_AllocateBuffer( | |
653 component_handle_, &omx_buffer, output_port_, NULL, | |
654 output_buffer_size_); | |
655 if (result != OMX_ErrorNone) | |
656 return false; | |
657 omx_buffer->pAppPrivate = new media::Picture( | |
658 sysmem_buffer->id(), | |
659 24 /* garbage bitstreambuffer id */, | |
660 decoded_pixel_size, visible_pixel_size); | |
661 output_pictures_.push_back( | |
662 std::make_pair(sysmem_buffer->id(), omx_buffer)); | |
663 } | |
664 } | |
665 return true; | |
666 } | |
667 | |
668 void OmxVideoDecodeAccelerator::FreeInputBuffers() { | |
669 // Calls to OMX to free buffers. | |
670 OMX_ERRORTYPE result; | |
671 OMX_BUFFERHEADERTYPE* omx_buffer; | |
672 while (!free_input_buffers_.empty()) { | |
673 omx_buffer = free_input_buffers_.front(); | |
674 free_input_buffers_.pop(); | |
675 result = OMX_FreeBuffer(component_handle_, input_port_, omx_buffer); | |
676 if (result != OMX_ErrorNone) { | |
677 LOG(ERROR) << "SendCommand(OMX_CommandPortDisable) failed"; | |
678 StopOnError(); | |
679 return; | |
680 } | |
681 } | |
682 } | |
683 | |
684 void OmxVideoDecodeAccelerator::FreeOutputBuffers() { | |
685 // Calls to OMX to free buffers. | |
686 OMX_ERRORTYPE result; | |
687 for (size_t i = 0; i < output_pictures_.size(); ++i) { | |
688 OMX_BUFFERHEADERTYPE* omx_buffer = output_pictures_[i].second; | |
689 CHECK(omx_buffer); | |
690 delete reinterpret_cast<media::Picture*>(omx_buffer->pAppPrivate); | |
691 result = OMX_FreeBuffer(component_handle_, output_port_, omx_buffer); | |
692 if (result != OMX_ErrorNone) { | |
693 LOG(ERROR) << "SendCommand(OMX_CommandPortDisable) failed"; | |
694 StopOnError(); | |
695 return; | |
696 } | |
697 } | |
698 output_pictures_.clear(); | |
699 } | |
700 | |
701 void OmxVideoDecodeAccelerator::OnPortSettingsChangedRun( | |
702 int port, OMX_INDEXTYPE index) { | |
703 // TODO(vhiremath@nvidia.com) visit again later | |
704 // Port settings changes can be called during run time | |
705 // changes in the resolution of video playback. | |
706 // In this case, the component detects PortSettingsChanged | |
707 // and sends the particular event to the IL-client. | |
708 // This needs to be handled in this method. | |
709 return; | |
710 } | |
711 | |
712 void OmxVideoDecodeAccelerator::FillBufferDoneTask( | |
713 OMX_BUFFERHEADERTYPE* buffer) { | |
714 DCHECK_GT(output_buffers_at_component_, 0); | |
715 output_buffers_at_component_--; | |
716 client_->PictureReady(*reinterpret_cast<media::Picture*>( | |
717 buffer->pAppPrivate)); | |
718 } | |
719 | |
720 void OmxVideoDecodeAccelerator::EmptyBufferDoneTask( | |
721 OMX_BUFFERHEADERTYPE* buffer) { | |
722 DCHECK_GT(input_buffers_at_component_, 0); | |
723 free_input_buffers_.push(buffer); | |
724 input_buffers_at_component_--; | |
725 if (buffer->nFlags & OMX_BUFFERFLAG_EOS) | |
726 return; | |
727 // Retrieve the corresponding BitstreamBuffer's id and notify the client of | |
728 // its completion. | |
729 OMXBufferIdMap::iterator it = omx_buff_ids_.find(buffer); | |
730 if (it == omx_buff_ids_.end()) { | |
731 LOG(ERROR) << "Unexpectedly failed to find a buffer id."; | |
732 StopOnError(); | |
733 return; | |
734 } | |
735 delete it->second.first; | |
736 client_->NotifyEndOfBitstreamBuffer(it->second.second); | |
737 omx_buff_ids_.erase(it); | |
738 } | |
739 | |
740 void OmxVideoDecodeAccelerator::EventHandlerCompleteTask(OMX_EVENTTYPE event, | |
741 OMX_U32 data1, | |
742 OMX_U32 data2) { | |
743 switch (event) { | |
744 case OMX_EventCmdComplete: { | |
745 // If the last command was successful, we have completed | |
746 // a state transition. So notify that we have done it | |
747 // accordingly. | |
748 OMX_COMMANDTYPE cmd = static_cast<OMX_COMMANDTYPE>(data1); | |
749 switch (cmd) { | |
750 case OMX_CommandPortDisable: { | |
751 if (on_port_disable_event_func_) | |
752 (this->*on_port_disable_event_func_)(static_cast<int>(data2)); | |
753 } | |
754 break; | |
755 case OMX_CommandPortEnable: { | |
756 if (on_port_enable_event_func_) | |
757 (this->*on_port_enable_event_func_)(static_cast<int>(data2)); | |
758 } | |
759 break; | |
760 case OMX_CommandStateSet: | |
761 (this->*on_state_event_func_)(static_cast<OMX_STATETYPE>(data2)); | |
762 break; | |
763 case OMX_CommandFlush: | |
764 (this->*on_flush_event_func_)(data2); | |
765 break; | |
766 default: | |
767 LOG(ERROR) << "Unknown command completed\n" << data1; | |
768 break; | |
769 } | |
770 break; | |
771 } | |
772 case OMX_EventError: | |
773 if (static_cast<OMX_ERRORTYPE>(data1) == OMX_ErrorInvalidState) | |
774 StopOnError(); | |
775 break; | |
776 case OMX_EventPortSettingsChanged: | |
777 // TODO(vhiremath@nvidia.com) remove this hack | |
778 // when all vendors observe same spec. | |
779 if (data1 < OMX_IndexComponentStartUnused) { | |
780 OnPortSettingsChangedRun(static_cast<int>(data1), | |
781 static_cast<OMX_INDEXTYPE>(data2)); | |
782 } else { | |
783 OnPortSettingsChangedRun(static_cast<int>(data2), | |
784 static_cast<OMX_INDEXTYPE>(data1)); | |
785 } | |
786 break; | |
787 case OMX_EventBufferFlag: | |
788 if (data1 == static_cast<OMX_U32>(output_port_)) { | |
789 (this->*on_buffer_flag_event_func_)(); | |
790 } | |
791 break; | |
792 default: | |
793 LOG(ERROR) << "Warning - Unknown event received\n"; | |
794 break; | |
795 } | |
796 } | |
797 | |
798 // static | |
799 OMX_ERRORTYPE OmxVideoDecodeAccelerator::EventHandler(OMX_HANDLETYPE component, | |
800 OMX_PTR priv_data, | |
801 OMX_EVENTTYPE event, | |
802 OMX_U32 data1, | |
803 OMX_U32 data2, | |
804 OMX_PTR event_data) { | |
805 OmxVideoDecodeAccelerator* decoder = | |
806 static_cast<OmxVideoDecodeAccelerator*>(priv_data); | |
807 DCHECK_EQ(component, decoder->component_handle_); | |
808 | |
809 decoder->message_loop_->PostTask( | |
810 FROM_HERE, | |
811 NewRunnableMethod(decoder, | |
812 &OmxVideoDecodeAccelerator::EventHandlerCompleteTask, | |
813 event, data1, data2)); | |
814 | |
815 return OMX_ErrorNone; | |
816 } | |
817 | |
818 // static | |
819 OMX_ERRORTYPE OmxVideoDecodeAccelerator::EmptyBufferCallback( | |
820 OMX_HANDLETYPE component, | |
821 OMX_PTR priv_data, | |
822 OMX_BUFFERHEADERTYPE* buffer) { | |
823 OmxVideoDecodeAccelerator* decoder = | |
824 static_cast<OmxVideoDecodeAccelerator*>(priv_data); | |
825 DCHECK_EQ(component, decoder->component_handle_); | |
826 | |
827 decoder->message_loop_->PostTask( | |
828 FROM_HERE, | |
829 NewRunnableMethod( | |
830 decoder, | |
831 &OmxVideoDecodeAccelerator::EmptyBufferDoneTask, buffer)); | |
832 return OMX_ErrorNone; | |
833 } | |
834 | |
835 // static | |
836 OMX_ERRORTYPE OmxVideoDecodeAccelerator::FillBufferCallback( | |
837 OMX_HANDLETYPE component, | |
838 OMX_PTR priv_data, | |
839 OMX_BUFFERHEADERTYPE* buffer) { | |
840 OmxVideoDecodeAccelerator* decoder = | |
841 static_cast<OmxVideoDecodeAccelerator*>(priv_data); | |
842 DCHECK_EQ(component, decoder->component_handle_); | |
843 | |
844 decoder->message_loop_->PostTask( | |
845 FROM_HERE, | |
846 NewRunnableMethod( | |
847 decoder, | |
848 &OmxVideoDecodeAccelerator::FillBufferDoneTask, buffer)); | |
849 return OMX_ErrorNone; | |
850 } | |
851 | |
852 bool OmxVideoDecodeAccelerator::CanAcceptInput() { | |
853 // We can't take input buffer when in error state. | |
854 return (client_state_ != OMX_StateInvalid && | |
855 client_state_ != OMX_StatePause && | |
856 client_state_ != OMX_StateLoaded); | |
857 } | |
858 | |
859 bool OmxVideoDecodeAccelerator::CanFillBuffer() { | |
860 // Make sure component is in the executing state and end-of-stream | |
861 // has not been reached. | |
862 OMX_ERRORTYPE result; | |
863 OMX_STATETYPE il_state; | |
864 if (client_state_ == OMX_StateLoaded) | |
865 return false; | |
866 result = OMX_GetState(component_handle_, &il_state); | |
867 if (result != OMX_ErrorNone) { | |
868 LOG(ERROR) << "SendCommand(OMX_CommandPortDisable) failed"; | |
869 StopOnError(); | |
870 return false; | |
871 } | |
872 return (il_state == OMX_StateExecuting); | |
873 } | |
874 | |
875 // Send command to disable/enable port. | |
876 void OmxVideoDecodeAccelerator::ChangePort( | |
877 OMX_COMMANDTYPE cmd, int port_index) { | |
878 OMX_ERRORTYPE result = OMX_SendCommand(component_handle_, | |
879 cmd, port_index, 0); | |
880 if (result != OMX_ErrorNone) { | |
881 LOG(ERROR) << "SendCommand(OMX_CommandPortDisable) failed"; | |
882 StopOnError(); | |
883 return; | |
884 } | |
885 } | |
886 | |
887 DISABLE_RUNNABLE_METHOD_REFCOUNT(OmxVideoDecodeAccelerator); | |
OLD | NEW |