| 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 |