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