| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 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 <algorithm> | |
| 6 #include <string> | |
| 7 | |
| 8 #include "base/callback.h" | |
| 9 #include "base/logging.h" | |
| 10 #include "base/message_loop.h" | |
| 11 #include "base/stl_util-inl.h" | |
| 12 #include "base/string_util.h" | |
| 13 #include "media/omx/omx_codec.h" | |
| 14 #include "media/base/buffers.h" | |
| 15 | |
| 16 namespace media { | |
| 17 | |
| 18 #if !defined(COMPILER_MSVC) | |
| 19 int const OmxCodec::kEosBuffer; | |
| 20 #endif | |
| 21 | |
| 22 template <typename T> | |
| 23 static void ResetPortHeader(const OmxCodec& dec, T* param) { | |
| 24 memset(param, 0, sizeof(T)); | |
| 25 param->nVersion.nVersion = dec.current_omx_spec_version(); | |
| 26 param->nSize = sizeof(T); | |
| 27 } | |
| 28 | |
| 29 OmxCodec::OmxCodec(MessageLoop* message_loop) | |
| 30 : input_buffer_count_(0), | |
| 31 input_buffer_size_(0), | |
| 32 input_port_(0), | |
| 33 input_eos_(false), | |
| 34 output_buffer_count_(0), | |
| 35 output_buffer_size_(0), | |
| 36 output_port_(0), | |
| 37 output_eos_(false), | |
| 38 state_(kEmpty), | |
| 39 next_state_(kEmpty), | |
| 40 component_handle_(NULL), | |
| 41 message_loop_(message_loop) { | |
| 42 } | |
| 43 | |
| 44 OmxCodec::~OmxCodec() { | |
| 45 DCHECK(state_ == kError || state_ == kEmpty); | |
| 46 DCHECK_EQ(0u, input_buffers_.size()); | |
| 47 DCHECK_EQ(0u, output_buffers_.size()); | |
| 48 DCHECK(available_input_buffers_.empty()); | |
| 49 DCHECK(pending_input_queue_.empty()); | |
| 50 DCHECK(processing_input_queue_.empty()); | |
| 51 } | |
| 52 | |
| 53 void OmxCodec::Setup(OmxConfigurator* configurator, | |
| 54 FeedDoneCallback* feed_done_callback, | |
| 55 FillDoneCallback* fill_done_callback) { | |
| 56 DCHECK_EQ(kEmpty, state_); | |
| 57 CHECK(configurator); | |
| 58 configurator_ = configurator; | |
| 59 feed_done_callback_.reset(feed_done_callback); | |
| 60 fill_done_callback_.reset(fill_done_callback); | |
| 61 } | |
| 62 | |
| 63 void OmxCodec::SetErrorCallback(Callback* callback) { | |
| 64 DCHECK_EQ(kEmpty, state_); | |
| 65 error_callback_.reset(callback); | |
| 66 } | |
| 67 | |
| 68 void OmxCodec::SetFormatCallback(FormatCallback* callback) { | |
| 69 DCHECK_EQ(kEmpty, state_); | |
| 70 format_callback_.reset(callback); | |
| 71 } | |
| 72 | |
| 73 void OmxCodec::Start() { | |
| 74 CHECK(configurator_); | |
| 75 | |
| 76 message_loop_->PostTask( | |
| 77 FROM_HERE, | |
| 78 NewRunnableMethod(this, &OmxCodec::StartTask)); | |
| 79 } | |
| 80 | |
| 81 void OmxCodec::Stop(Callback* callback) { | |
| 82 message_loop_->PostTask( | |
| 83 FROM_HERE, | |
| 84 NewRunnableMethod(this, &OmxCodec::StopTask, callback)); | |
| 85 } | |
| 86 | |
| 87 void OmxCodec::Feed(scoped_refptr<Buffer> buffer) { | |
| 88 message_loop_->PostTask( | |
| 89 FROM_HERE, | |
| 90 NewRunnableMethod(this, &OmxCodec::FeedTask, buffer)); | |
| 91 } | |
| 92 | |
| 93 void OmxCodec::Flush(Callback* callback) { | |
| 94 callback->Run(); | |
| 95 delete callback; | |
| 96 } | |
| 97 | |
| 98 OmxCodec::State OmxCodec::GetState() const { | |
| 99 return state_; | |
| 100 } | |
| 101 | |
| 102 void OmxCodec::SetState(State state) { | |
| 103 state_ = state; | |
| 104 } | |
| 105 | |
| 106 OmxCodec::State OmxCodec::GetNextState() const { | |
| 107 return next_state_; | |
| 108 } | |
| 109 | |
| 110 void OmxCodec::SetNextState(State state) { | |
| 111 next_state_ = state; | |
| 112 } | |
| 113 | |
| 114 void OmxCodec::StartTask() { | |
| 115 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 116 | |
| 117 StateTransitionTask(kLoaded); | |
| 118 } | |
| 119 | |
| 120 void OmxCodec::StopTask(Callback* callback) { | |
| 121 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 122 | |
| 123 stop_callback_.reset(callback); | |
| 124 | |
| 125 if (GetState() == kError) { | |
| 126 DoneStop(); | |
| 127 return; | |
| 128 } | |
| 129 | |
| 130 FreeInputQueue(); | |
| 131 | |
| 132 // TODO(hclam): We should wait for all output buffers to come back from | |
| 133 // output sink to proceed to stop. The proper way to do this is | |
| 134 // transition to a StopWaitingForBuffers state and wait until all buffers | |
| 135 // are received to proceed. | |
| 136 | |
| 137 if (GetState() == kExecuting) | |
| 138 StateTransitionTask(kIdle); | |
| 139 // TODO(hclam): The following two transitions may not be correct. | |
| 140 else if (GetState() == kPortSettingDisable) | |
| 141 StateTransitionTask(kIdle); | |
| 142 else if (GetState() == kPortSettingEnable) | |
| 143 StateTransitionTask(kIdle); | |
| 144 else if (GetState() == kIdle) | |
| 145 StateTransitionTask(kLoaded); | |
| 146 else if (GetState() == kLoaded) | |
| 147 StateTransitionTask(kEmpty); | |
| 148 } | |
| 149 | |
| 150 void OmxCodec::FeedTask(scoped_refptr<Buffer> buffer) { | |
| 151 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 152 | |
| 153 if (!CanAcceptInput()) { | |
| 154 feed_done_callback_->Run(buffer); | |
| 155 return; | |
| 156 } | |
| 157 | |
| 158 // Queue this input buffer. | |
| 159 pending_input_queue_.push(buffer); | |
| 160 | |
| 161 // Try to feed buffers into the decoder. | |
| 162 EmptyBufferTask(); | |
| 163 } | |
| 164 | |
| 165 // This method assumes OMX_AllocateBuffer() will allocate | |
| 166 // buffer internally. If this is not the case we need to | |
| 167 // call OMX_UseBuffer() to allocate buffer manually and | |
| 168 // assign to the headers. | |
| 169 bool OmxCodec::AllocateInputBuffers() { | |
| 170 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 171 | |
| 172 uint8* data = new uint8[input_buffer_size_]; | |
| 173 scoped_array<uint8> data_deleter(data); | |
| 174 | |
| 175 for (int i = 0; i < input_buffer_count_; ++i) { | |
| 176 OMX_BUFFERHEADERTYPE* buffer; | |
| 177 OMX_ERRORTYPE error = | |
| 178 OMX_UseBuffer(component_handle_, &buffer, input_port_, | |
| 179 NULL, input_buffer_size_, data); | |
| 180 if (error != OMX_ErrorNone) | |
| 181 return false; | |
| 182 input_buffers_.push_back(buffer); | |
| 183 available_input_buffers_.push(buffer); | |
| 184 } | |
| 185 return true; | |
| 186 } | |
| 187 | |
| 188 // This method assumes OMX_AllocateBuffer() will allocate buffer | |
| 189 // header internally. In additional to that memory that holds the | |
| 190 // header, the same method call will allocate memory for holding | |
| 191 // output data. If we use EGL images for holding output data, | |
| 192 // the memory allocation will be done externally. | |
| 193 bool OmxCodec::AllocateOutputBuffers() { | |
| 194 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 195 | |
| 196 for (int i = 0; i < output_buffer_count_; ++i) { | |
| 197 OMX_BUFFERHEADERTYPE* buffer; | |
| 198 OMX_ERRORTYPE error = | |
| 199 OMX_AllocateBuffer(component_handle_, &buffer, output_port_, | |
| 200 NULL, output_buffer_size_); | |
| 201 if (error != OMX_ErrorNone) | |
| 202 return false; | |
| 203 output_buffers_.push_back(buffer); | |
| 204 } | |
| 205 | |
| 206 return true; | |
| 207 } | |
| 208 | |
| 209 void OmxCodec::FreeInputBuffers() { | |
| 210 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 211 | |
| 212 // Calls to OMX to free buffers. | |
| 213 for (size_t i = 0; i < input_buffers_.size(); ++i) | |
| 214 OMX_FreeBuffer(component_handle_, input_port_, input_buffers_[i]); | |
| 215 input_buffers_.clear(); | |
| 216 | |
| 217 // Empty available buffer queue. | |
| 218 while (!available_input_buffers_.empty()) { | |
| 219 available_input_buffers_.pop(); | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 void OmxCodec::FreeOutputBuffers() { | |
| 224 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 225 | |
| 226 // Calls to OMX to free buffers. | |
| 227 for (size_t i = 0; i < output_buffers_.size(); ++i) | |
| 228 OMX_FreeBuffer(component_handle_, output_port_, output_buffers_[i]); | |
| 229 output_buffers_.clear(); | |
| 230 } | |
| 231 | |
| 232 void OmxCodec::FreeInputQueue() { | |
| 233 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 234 | |
| 235 while (!pending_input_queue_.empty()) { | |
| 236 scoped_refptr<Buffer> buffer = pending_input_queue_.front(); | |
| 237 feed_done_callback_->Run(buffer); | |
| 238 pending_input_queue_.pop(); | |
| 239 } | |
| 240 | |
| 241 while (!processing_input_queue_.empty()) { | |
| 242 processing_input_queue_.pop(); | |
| 243 } | |
| 244 } | |
| 245 | |
| 246 // Sequence of actions in this transition: | |
| 247 // | |
| 248 // 1. Initialize OMX (To be removed.) | |
| 249 // 2. Map role name to component name. | |
| 250 // 3. Get handle of the OMX component | |
| 251 // 4. Get the port information. | |
| 252 // 5. Set role for the component. | |
| 253 // 6. Input/output ports media format configuration. | |
| 254 // 7. Obtain the information about the input port. | |
| 255 // 8. Obtain the information about the output port. | |
| 256 void OmxCodec::Transition_EmptyToLoaded() { | |
| 257 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 258 DCHECK_EQ(kEmpty, GetState()); | |
| 259 | |
| 260 static OMX_CALLBACKTYPE callback = { | |
| 261 &EventHandler, | |
| 262 &EmptyBufferCallback, | |
| 263 &FillBufferCallback | |
| 264 }; | |
| 265 | |
| 266 // 1. Initialize the OpenMAX Core. | |
| 267 // TODO(hclam): move this out. | |
| 268 OMX_ERRORTYPE omxresult = OMX_Init(); | |
| 269 if (omxresult != OMX_ErrorNone) { | |
| 270 LOG(ERROR) << "Failed to Init OpenMAX core"; | |
| 271 StateTransitionTask(kError); | |
| 272 return; | |
| 273 } | |
| 274 | |
| 275 // 2. Map role name to component name. | |
| 276 std::string role_name = configurator_->GetRoleName(); | |
| 277 OMX_U32 roles = 0; | |
| 278 omxresult = OMX_GetComponentsOfRole( | |
| 279 const_cast<OMX_STRING>(role_name.c_str()), | |
| 280 &roles, 0); | |
| 281 if (omxresult != OMX_ErrorNone || roles == 0) { | |
| 282 LOG(ERROR) << "Unsupported Role: " << role_name.c_str(); | |
| 283 StateTransitionTask(kError); | |
| 284 return; | |
| 285 } | |
| 286 const OMX_U32 kMaxRolePerComponent = 20; | |
| 287 CHECK(roles < kMaxRolePerComponent); | |
| 288 | |
| 289 OMX_U8** component_names = new OMX_U8*[roles]; | |
| 290 const int kMaxComponentNameLength = 256; | |
| 291 for (size_t i = 0; i < roles; ++i) | |
| 292 component_names[i] = new OMX_U8[kMaxComponentNameLength]; | |
| 293 | |
| 294 omxresult = OMX_GetComponentsOfRole( | |
| 295 const_cast<OMX_STRING>(role_name.c_str()), | |
| 296 &roles, component_names); | |
| 297 | |
| 298 // Use first component only. Copy the name of the first component | |
| 299 // so that we could free the memory. | |
| 300 std::string component_name; | |
| 301 if (omxresult == OMX_ErrorNone) | |
| 302 component_name = reinterpret_cast<char*>(component_names[0]); | |
| 303 | |
| 304 for (size_t i = 0; i < roles; ++i) | |
| 305 delete [] component_names[i]; | |
| 306 delete [] component_names; | |
| 307 | |
| 308 if (omxresult != OMX_ErrorNone || roles == 0) { | |
| 309 LOG(ERROR) << "Unsupported Role: " << role_name.c_str(); | |
| 310 StateTransitionTask(kError); | |
| 311 return; | |
| 312 } | |
| 313 | |
| 314 // 3. Get the handle to the component. After OMX_GetHandle(), | |
| 315 // the component is in loaded state. | |
| 316 OMX_STRING component = const_cast<OMX_STRING>(component_name.c_str()); | |
| 317 OMX_HANDLETYPE handle = reinterpret_cast<OMX_HANDLETYPE>(component_handle_); | |
| 318 omxresult = OMX_GetHandle(&handle, component, this, &callback); | |
| 319 component_handle_ = reinterpret_cast<OMX_COMPONENTTYPE*>(handle); | |
| 320 if (omxresult != OMX_ErrorNone) { | |
| 321 LOG(ERROR) << "Failed to Load the component: " << component; | |
| 322 StateTransitionTask(kError); | |
| 323 return; | |
| 324 } | |
| 325 | |
| 326 // 4. Get the port information. This will obtain information about the | |
| 327 // number of ports and index of the first port. | |
| 328 OMX_PORT_PARAM_TYPE port_param; | |
| 329 ResetPortHeader(*this, &port_param); | |
| 330 omxresult = OMX_GetParameter(component_handle_, OMX_IndexParamVideoInit, | |
| 331 &port_param); | |
| 332 if (omxresult != OMX_ErrorNone) { | |
| 333 LOG(ERROR) << "Failed to get Port Param"; | |
| 334 StateTransitionTask(kError); | |
| 335 return; | |
| 336 } | |
| 337 input_port_ = port_param.nStartPortNumber; | |
| 338 output_port_ = input_port_ + 1; | |
| 339 | |
| 340 // 5. Set role for the component because our component could | |
| 341 // have multiple roles. | |
| 342 OMX_PARAM_COMPONENTROLETYPE role_type; | |
| 343 ResetPortHeader(*this, &role_type); | |
| 344 base::strlcpy(reinterpret_cast<char*>(role_type.cRole), | |
| 345 role_name.c_str(), | |
| 346 OMX_MAX_STRINGNAME_SIZE); | |
| 347 role_type.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0'; | |
| 348 omxresult = OMX_SetParameter(component_handle_, | |
| 349 OMX_IndexParamStandardComponentRole, | |
| 350 &role_type); | |
| 351 if (omxresult != OMX_ErrorNone) { | |
| 352 LOG(ERROR) << "Failed to Set Role"; | |
| 353 StateTransitionTask(kError); | |
| 354 return; | |
| 355 } | |
| 356 | |
| 357 // 6. Input/output ports media format configuration. | |
| 358 if (!ConfigureIOPorts()) { | |
| 359 LOG(ERROR) << "Media format configurations failed"; | |
| 360 StateTransitionTask(kError); | |
| 361 return; | |
| 362 } | |
| 363 | |
| 364 // 7. Obtain the information about the input port. | |
| 365 // This will have the new mini buffer count in |port_format.nBufferCountMin|. | |
| 366 // Save this value to input_buf_count. | |
| 367 OMX_PARAM_PORTDEFINITIONTYPE port_format; | |
| 368 ResetPortHeader(*this, &port_format); | |
| 369 port_format.nPortIndex = input_port_; | |
| 370 omxresult = OMX_GetParameter(component_handle_, | |
| 371 OMX_IndexParamPortDefinition, | |
| 372 &port_format); | |
| 373 if (omxresult != OMX_ErrorNone) { | |
| 374 LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed"; | |
| 375 StateTransitionTask(kError); | |
| 376 return; | |
| 377 } | |
| 378 if (OMX_DirInput != port_format.eDir) { | |
| 379 LOG(ERROR) << "Expected input port"; | |
| 380 StateTransitionTask(kError); | |
| 381 return; | |
| 382 } | |
| 383 input_buffer_count_ = port_format.nBufferCountMin; | |
| 384 input_buffer_size_ = port_format.nBufferSize; | |
| 385 | |
| 386 // 8. Obtain the information about the output port. | |
| 387 ResetPortHeader(*this, &port_format); | |
| 388 port_format.nPortIndex = output_port_; | |
| 389 omxresult = OMX_GetParameter(component_handle_, | |
| 390 OMX_IndexParamPortDefinition, | |
| 391 &port_format); | |
| 392 if (omxresult != OMX_ErrorNone) { | |
| 393 LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed"; | |
| 394 StateTransitionTask(kError); | |
| 395 return; | |
| 396 } | |
| 397 if (OMX_DirOutput != port_format.eDir) { | |
| 398 LOG(ERROR) << "Expect Output Port"; | |
| 399 StateTransitionTask(kError); | |
| 400 return; | |
| 401 } | |
| 402 output_buffer_count_ = port_format.nBufferCountMin; | |
| 403 output_buffer_size_ = port_format.nBufferSize; | |
| 404 | |
| 405 // After we have done all the configurations, we are considered loaded. | |
| 406 DoneStateTransitionTask(); | |
| 407 } | |
| 408 | |
| 409 // Sequence of actions in this transition: | |
| 410 // | |
| 411 // 1. Send command to Idle state. | |
| 412 // 2. Allocate buffers for input port. | |
| 413 // 3. Allocate buffers for output port. | |
| 414 void OmxCodec::Transition_LoadedToIdle() { | |
| 415 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 416 DCHECK_EQ(kLoaded, GetState()); | |
| 417 | |
| 418 // 1. Sets decoder to idle state. | |
| 419 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
| 420 OMX_CommandStateSet, | |
| 421 OMX_StateIdle, 0); | |
| 422 if (omxresult != OMX_ErrorNone) { | |
| 423 LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; | |
| 424 StateTransitionTask(kError); | |
| 425 return; | |
| 426 } | |
| 427 | |
| 428 // 2. Allocate buffer for the input port. | |
| 429 if (!AllocateInputBuffers()) { | |
| 430 LOG(ERROR) << "OMX_AllocateBuffer() Input buffer error"; | |
| 431 StateTransitionTask(kError); | |
| 432 return; | |
| 433 } | |
| 434 | |
| 435 // 3. Allocate buffer for the output port. | |
| 436 if (!AllocateOutputBuffers()) { | |
| 437 LOG(ERROR) << "OMX_AllocateBuffer() Output buffer error"; | |
| 438 StateTransitionTask(kError); | |
| 439 return; | |
| 440 } | |
| 441 } | |
| 442 | |
| 443 // Sequence of actions in this transition: | |
| 444 // | |
| 445 // 1. Send command to Executing state. | |
| 446 void OmxCodec::Transition_IdleToExecuting() { | |
| 447 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 448 DCHECK_EQ(kIdle, GetState()); | |
| 449 | |
| 450 // Transist to executing state. | |
| 451 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
| 452 OMX_CommandStateSet, | |
| 453 OMX_StateExecuting, 0); | |
| 454 if (omxresult != OMX_ErrorNone) { | |
| 455 LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; | |
| 456 StateTransitionTask(kError); | |
| 457 return; | |
| 458 } | |
| 459 | |
| 460 // Simulate a format change. | |
| 461 ReportFormatChange(configurator_->input_format(), | |
| 462 configurator_->output_format()); | |
| 463 } | |
| 464 | |
| 465 // Sequence of actions in this transition: | |
| 466 // | |
| 467 // 1. Send command to disable output port. | |
| 468 // 2. Free buffers of the output port. | |
| 469 void OmxCodec::Transition_ExecutingToDisable() { | |
| 470 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 471 DCHECK_EQ(kExecuting, GetState()); | |
| 472 | |
| 473 // Send DISABLE command. | |
| 474 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
| 475 OMX_CommandPortDisable, | |
| 476 output_port_, 0); | |
| 477 if (omxresult != OMX_ErrorNone) { | |
| 478 LOG(ERROR) << "SendCommand(OMX_CommandPortDisable) failed"; | |
| 479 StateTransitionTask(kError); | |
| 480 return; | |
| 481 } | |
| 482 | |
| 483 // Free output Buffer. | |
| 484 FreeOutputBuffers(); | |
| 485 } | |
| 486 | |
| 487 // Sequence of actions in this transition: | |
| 488 // | |
| 489 // 1. Send command to enable output port. | |
| 490 // 2. Get parameter of the output port. | |
| 491 // 3. Allocate buffers for the output port. | |
| 492 void OmxCodec::Transition_DisableToEnable() { | |
| 493 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 494 DCHECK_EQ(kPortSettingDisable, GetState()); | |
| 495 | |
| 496 // Send Enable command. | |
| 497 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
| 498 OMX_CommandPortEnable, | |
| 499 output_port_, 0); | |
| 500 if (omxresult != OMX_ErrorNone) { | |
| 501 LOG(ERROR) << "SendCommand(OMX_CommandPortEnable) failed"; | |
| 502 StateTransitionTask(kError); | |
| 503 return; | |
| 504 } | |
| 505 | |
| 506 // AllocateBuffers. | |
| 507 OMX_PARAM_PORTDEFINITIONTYPE port_format; | |
| 508 ResetPortHeader(*this, &port_format); | |
| 509 port_format.nPortIndex = output_port_; | |
| 510 omxresult = OMX_GetParameter(component_handle_, OMX_IndexParamPortDefinition, | |
| 511 &port_format); | |
| 512 if (omxresult != OMX_ErrorNone) { | |
| 513 LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed"; | |
| 514 StateTransitionTask(kError); | |
| 515 return; | |
| 516 } | |
| 517 if (OMX_DirOutput != port_format.eDir) { | |
| 518 LOG(ERROR) << "Expected Output Port"; | |
| 519 StateTransitionTask(kError); | |
| 520 return; | |
| 521 } | |
| 522 | |
| 523 // Update the output format. | |
| 524 // TODO(jiesun): check if the format really change. ( we had omit some | |
| 525 // information such as frame rate / bit rate / vbv buffer info now. ) | |
| 526 OmxConfigurator::MediaFormat input_format, output_format; | |
| 527 output_format.video_header.height = port_format.format.video.nFrameHeight; | |
| 528 output_format.video_header.width = port_format.format.video.nFrameWidth; | |
| 529 output_format.video_header.stride = port_format.format.video.nStride; | |
| 530 input_format.video_header.height = output_format.video_header.height; | |
| 531 input_format.video_header.width = output_format.video_header.width; | |
| 532 input_format.video_header.stride = output_format.video_header.stride; | |
| 533 ReportFormatChange(input_format, output_format); | |
| 534 | |
| 535 // Update the ports in buffer. | |
| 536 output_buffer_count_ = port_format.nBufferCountActual; | |
| 537 output_buffer_size_ = port_format.nBufferSize; | |
| 538 if (!AllocateOutputBuffers()) { | |
| 539 LOG(ERROR) << "OMX_AllocateBuffer() Output buffer error"; | |
| 540 StateTransitionTask(kError); | |
| 541 return; | |
| 542 } | |
| 543 } | |
| 544 | |
| 545 // Sequence of actions in this transition: | |
| 546 // | |
| 547 // 1. Send command to Idle state. | |
| 548 void OmxCodec::Transition_DisableToIdle() { | |
| 549 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 550 DCHECK_EQ(kPortSettingDisable, GetState()); | |
| 551 | |
| 552 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
| 553 OMX_CommandStateSet, | |
| 554 OMX_StateIdle, 0); | |
| 555 if (omxresult != OMX_ErrorNone) { | |
| 556 LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; | |
| 557 StateTransitionTask(kError); | |
| 558 return; | |
| 559 } | |
| 560 } | |
| 561 | |
| 562 // Sequence of actions in this transition: | |
| 563 // | |
| 564 // This transition does nothing. | |
| 565 void OmxCodec::Transition_EnableToExecuting() { | |
| 566 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 567 DCHECK_EQ(kPortSettingEnable, GetState()); | |
| 568 | |
| 569 // This transition is fake, nothing to do here. | |
| 570 DoneStateTransitionTask(); | |
| 571 } | |
| 572 | |
| 573 void OmxCodec::Transition_EnableToIdle() { | |
| 574 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 575 DCHECK_EQ(kPortSettingEnable, GetState()); | |
| 576 | |
| 577 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
| 578 OMX_CommandStateSet, | |
| 579 OMX_StateIdle, 0); | |
| 580 if (omxresult != OMX_ErrorNone) { | |
| 581 LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; | |
| 582 StateTransitionTask(kError); | |
| 583 return; | |
| 584 } | |
| 585 } | |
| 586 | |
| 587 // Sequence of actions in this transition: | |
| 588 // | |
| 589 // 1. Send command to Idle state. | |
| 590 void OmxCodec::Transition_ExecutingToIdle() { | |
| 591 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 592 DCHECK_EQ(kExecuting, GetState()); | |
| 593 | |
| 594 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
| 595 OMX_CommandStateSet, | |
| 596 OMX_StateIdle, 0); | |
| 597 if (omxresult != OMX_ErrorNone) { | |
| 598 LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; | |
| 599 StateTransitionTask(kError); | |
| 600 return; | |
| 601 } | |
| 602 } | |
| 603 | |
| 604 // Sequence of actions in this transition: | |
| 605 // | |
| 606 // 1. Send command to Loaded state | |
| 607 // 2. Free input buffers | |
| 608 // 2. Free output buffers | |
| 609 void OmxCodec::Transition_IdleToLoaded() { | |
| 610 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 611 DCHECK_EQ(kIdle, GetState()); | |
| 612 | |
| 613 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
| 614 OMX_CommandStateSet, | |
| 615 OMX_StateLoaded, 0); | |
| 616 if (omxresult != OMX_ErrorNone) { | |
| 617 LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; | |
| 618 StateTransitionTask(kError); | |
| 619 return; | |
| 620 } | |
| 621 | |
| 622 FreeInputBuffers(); | |
| 623 FreeOutputBuffers(); | |
| 624 } | |
| 625 | |
| 626 // Sequence of actions in this transition: | |
| 627 // | |
| 628 // 1. Free decoder handle | |
| 629 // 2. Uninitialize OMX (TODO(hclam): Remove this.) | |
| 630 void OmxCodec::Transition_LoadedToEmpty() { | |
| 631 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 632 DCHECK_EQ(kLoaded, GetState()); | |
| 633 | |
| 634 // Free the decoder handle. | |
| 635 OMX_ERRORTYPE result = OMX_FreeHandle(component_handle_); | |
| 636 if (result != OMX_ErrorNone) { | |
| 637 LOG(ERROR) << "Terminate: OMX_FreeHandle() error. " | |
| 638 << "Error code: " << result; | |
| 639 } | |
| 640 component_handle_ = NULL; | |
| 641 | |
| 642 // Deinit OpenMAX | |
| 643 // TODO(hclam): move this out. | |
| 644 OMX_Deinit(); | |
| 645 | |
| 646 DoneStateTransitionTask(); | |
| 647 } | |
| 648 | |
| 649 // Sequence of actions in this transition: | |
| 650 // | |
| 651 // 1. Disable input port | |
| 652 // 2. Disable output port | |
| 653 // 3. Free input buffer | |
| 654 // 4. Free output buffer | |
| 655 // 5. Free decoder handle | |
| 656 // 6. Uninitialize OMX (TODO(hclam): Remove this.) | |
| 657 void OmxCodec::Transition_Error() { | |
| 658 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 659 DCHECK_NE(kError, GetState()); | |
| 660 | |
| 661 State old_state = GetState(); | |
| 662 SetState(kError); | |
| 663 | |
| 664 // If we are going to error state in the following states, we need to | |
| 665 // send a command to disable ports for us to free buffers. | |
| 666 if (old_state == kExecuting || old_state == kIdle || | |
| 667 old_state == kPortSettingEnable || old_state == kPortSettingDisable) { | |
| 668 DCHECK(component_handle_); | |
| 669 OMX_SendCommand(component_handle_, OMX_CommandPortDisable, input_port_, 0); | |
| 670 OMX_SendCommand(component_handle_, OMX_CommandPortDisable, output_port_, 0); | |
| 671 } | |
| 672 | |
| 673 // Free input and output buffers. | |
| 674 FreeInputBuffers(); | |
| 675 FreeOutputBuffers(); | |
| 676 | |
| 677 // Free input queues. | |
| 678 FreeInputQueue(); | |
| 679 | |
| 680 // Free decoder handle. | |
| 681 if (component_handle_) { | |
| 682 OMX_ERRORTYPE result = OMX_FreeHandle(component_handle_); | |
| 683 if (result != OMX_ErrorNone) | |
| 684 LOG(ERROR) << "OMX_FreeHandle() error. Error code: " << result; | |
| 685 component_handle_ = NULL; | |
| 686 } | |
| 687 | |
| 688 // Deinit OpenMAX. | |
| 689 OMX_Deinit(); | |
| 690 | |
| 691 DoneStateTransitionTask(); | |
| 692 } | |
| 693 | |
| 694 void OmxCodec::PostStateTransitionTask(State new_state) { | |
| 695 message_loop_->PostTask( | |
| 696 FROM_HERE, | |
| 697 NewRunnableMethod(this, | |
| 698 &OmxCodec::StateTransitionTask, new_state)); | |
| 699 } | |
| 700 | |
| 701 void OmxCodec::StateTransitionTask(State new_state) { | |
| 702 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 703 | |
| 704 if (GetState() == kError) | |
| 705 return; | |
| 706 | |
| 707 // Save the next state. | |
| 708 SetNextState(new_state); | |
| 709 | |
| 710 // The following list defines all the possible state transitions | |
| 711 // for this object: | |
| 712 // | |
| 713 // TRANSITIONS | |
| 714 // 1. Empty -> Loaded | |
| 715 // 2. Loaded -> Idle | |
| 716 // 3. Idle -> Executing | |
| 717 // 4. Executing -> Disable | |
| 718 // 5. Executing -> Idle | |
| 719 // 6. Disable -> Enable | |
| 720 // 7. Disable -> Idle | |
| 721 // 8. Enable -> Executing | |
| 722 // 9. Enable -> Idle | |
| 723 // 10. Idle -> Loaded | |
| 724 // 11. Loaded -> Empty (TODO(hclam): To stopped instead.) | |
| 725 // 12. *ANYTHING* -> Error | |
| 726 if (GetState() == kEmpty && new_state == kLoaded) | |
| 727 Transition_EmptyToLoaded(); | |
| 728 else if (GetState() == kLoaded && new_state == kIdle) | |
| 729 Transition_LoadedToIdle(); | |
| 730 else if (GetState() == kIdle && new_state == kExecuting) | |
| 731 Transition_IdleToExecuting(); | |
| 732 else if (GetState() == kExecuting && new_state == kPortSettingDisable) | |
| 733 Transition_ExecutingToDisable(); | |
| 734 else if (GetState() == kPortSettingDisable && new_state == kPortSettingEnable) | |
| 735 Transition_DisableToEnable(); | |
| 736 else if (GetState() == kPortSettingDisable && new_state == kIdle) | |
| 737 Transition_DisableToIdle(); | |
| 738 else if (GetState() == kPortSettingEnable && new_state == kExecuting) | |
| 739 Transition_EnableToExecuting(); | |
| 740 else if (GetState() == kPortSettingEnable && new_state == kIdle) | |
| 741 Transition_EnableToIdle(); | |
| 742 else if (GetState() == kExecuting && new_state == kIdle) | |
| 743 Transition_ExecutingToIdle(); | |
| 744 else if (GetState() == kIdle && new_state == kLoaded) | |
| 745 Transition_IdleToLoaded(); | |
| 746 else if (GetState() == kLoaded && new_state == kEmpty) | |
| 747 Transition_LoadedToEmpty(); | |
| 748 else if (new_state == kError) | |
| 749 Transition_Error(); | |
| 750 } | |
| 751 | |
| 752 void OmxCodec::PostDoneStateTransitionTask() { | |
| 753 message_loop_->PostTask( | |
| 754 FROM_HERE, | |
| 755 NewRunnableMethod(this, &OmxCodec::DoneStateTransitionTask)); | |
| 756 } | |
| 757 | |
| 758 void OmxCodec::DoneStateTransitionTask() { | |
| 759 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 760 | |
| 761 if (GetState() == kError) { | |
| 762 ReportError(); | |
| 763 return; | |
| 764 } | |
| 765 | |
| 766 // Save the current state and completes the transition. | |
| 767 State old_state = GetState(); | |
| 768 SetState(GetNextState()); | |
| 769 | |
| 770 // The following list is to perform a state transition automatically | |
| 771 // based on the last transition done: | |
| 772 // | |
| 773 // LAST TRANSITION NEXT TRANSITION | |
| 774 // | |
| 775 // 1. Empty -> Loaded Laoded -> Idle | |
| 776 // 2. Loaded -> Idle Idle -> Executing | |
| 777 // 3. Idle -> Executing | |
| 778 // | |
| 779 // Because of the above reoute, once we kick start the transition | |
| 780 // from empty to loaded, this method will automatically route it | |
| 781 // executing eventually. | |
| 782 // | |
| 783 // The following sequence is for transition to the stopped state. | |
| 784 // | |
| 785 // LAST TRANSITION NEXT TRANSITION | |
| 786 // | |
| 787 // 4. Executing -> Idle Idle -> Loaded | |
| 788 // 5. Idle -> Loaded Loaded -> Empty | |
| 789 // TODO(hclam): should go to Stopped instead of Empty. | |
| 790 // | |
| 791 // During dynamic port seeting, the route of state transition is: | |
| 792 // | |
| 793 // LAST TRANSITION NEXT TRANSITION | |
| 794 // | |
| 795 // 6. Executing -> Disable Disable -> Enable | |
| 796 // 7. Disable -> Enable Enable -> Executing | |
| 797 if (old_state == kEmpty && GetState() == kLoaded) | |
| 798 StateTransitionTask(kIdle); | |
| 799 else if (old_state == kLoaded && GetState() == kIdle) | |
| 800 StateTransitionTask(kExecuting); | |
| 801 else if (old_state == kIdle && GetState() == kExecuting) { | |
| 802 // TODO(hclam): It is a little too late to issue read requests. | |
| 803 // This seems to introduce some latencies. | |
| 804 InitialEmptyBuffer(); | |
| 805 InitialFillBuffer(); | |
| 806 } | |
| 807 else if (old_state == kExecuting && GetState() == kPortSettingDisable) | |
| 808 StateTransitionTask(kPortSettingEnable); | |
| 809 else if (old_state == kPortSettingDisable && GetState() == kPortSettingEnable) | |
| 810 StateTransitionTask(kExecuting); | |
| 811 else if (old_state == kPortSettingEnable && GetState() == kExecuting) | |
| 812 InitialFillBuffer(); | |
| 813 else if (old_state == kPortSettingDisable && GetState() == kIdle) | |
| 814 StateTransitionTask(kLoaded); | |
| 815 else if (old_state == kPortSettingEnable && GetState() == kIdle) | |
| 816 StateTransitionTask(kLoaded); | |
| 817 else if (old_state == kExecuting && GetState() == kIdle) | |
| 818 StateTransitionTask(kLoaded); | |
| 819 else if (old_state == kIdle && GetState() == kLoaded) | |
| 820 StateTransitionTask(kEmpty); | |
| 821 else if (old_state == kLoaded && GetState() == kEmpty) | |
| 822 DoneStop(); | |
| 823 else { | |
| 824 NOTREACHED() << "Invalid state transition"; | |
| 825 } | |
| 826 } | |
| 827 | |
| 828 void OmxCodec::DoneStop() { | |
| 829 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 830 | |
| 831 if (!stop_callback_.get()) | |
| 832 return; | |
| 833 stop_callback_->Run(); | |
| 834 stop_callback_.reset(); | |
| 835 } | |
| 836 | |
| 837 void OmxCodec::ReportError() { | |
| 838 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 839 | |
| 840 if (!error_callback_.get()) | |
| 841 return; | |
| 842 error_callback_->Run(); | |
| 843 error_callback_.reset(); | |
| 844 } | |
| 845 | |
| 846 void OmxCodec::ReportFormatChange( | |
| 847 const OmxConfigurator::MediaFormat& input_format, | |
| 848 const OmxConfigurator::MediaFormat& output_format) { | |
| 849 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 850 | |
| 851 if (!format_callback_.get()) | |
| 852 return; | |
| 853 format_callback_->Run(input_format, output_format); | |
| 854 } | |
| 855 | |
| 856 bool OmxCodec::ConfigureIOPorts() { | |
| 857 OMX_PARAM_PORTDEFINITIONTYPE input_port_def, output_port_def; | |
| 858 OMX_ERRORTYPE omxresult = OMX_ErrorNone; | |
| 859 // Get default input port definition. | |
| 860 ResetPortHeader(*this, &input_port_def); | |
| 861 input_port_def.nPortIndex = input_port_; | |
| 862 omxresult = OMX_GetParameter(component_handle_, | |
| 863 OMX_IndexParamPortDefinition, | |
| 864 &input_port_def); | |
| 865 if (omxresult != OMX_ErrorNone) { | |
| 866 LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) " | |
| 867 << "for input port failed"; | |
| 868 return false; | |
| 869 } | |
| 870 if (OMX_DirInput != input_port_def.eDir) { | |
| 871 LOG(ERROR) << "Expected Input Port"; | |
| 872 return false; | |
| 873 } | |
| 874 | |
| 875 // Get default output port definition. | |
| 876 ResetPortHeader(*this, &output_port_def); | |
| 877 output_port_def.nPortIndex = output_port_; | |
| 878 omxresult = OMX_GetParameter(component_handle_, | |
| 879 OMX_IndexParamPortDefinition, | |
| 880 &output_port_def); | |
| 881 if (omxresult != OMX_ErrorNone) { | |
| 882 LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) " | |
| 883 << "for output port failed"; | |
| 884 return false; | |
| 885 } | |
| 886 if (OMX_DirOutput != output_port_def.eDir) { | |
| 887 LOG(ERROR) << "Expected Output Port"; | |
| 888 return false; | |
| 889 } | |
| 890 | |
| 891 return configurator_->ConfigureIOPorts(component_handle_, | |
| 892 &input_port_def, &output_port_def); | |
| 893 } | |
| 894 | |
| 895 bool OmxCodec::CanEmptyBuffer() { | |
| 896 // We can call empty buffer while we are in executing or enabling / disabling | |
| 897 // the output port. | |
| 898 return (GetState() == kExecuting || GetState() == kPortSettingDisable || | |
| 899 GetState() == kPortSettingEnable) && | |
| 900 (GetNextState() == kExecuting || GetNextState() == kPortSettingDisable || | |
| 901 GetNextState() == kPortSettingEnable); | |
| 902 } | |
| 903 | |
| 904 bool OmxCodec::CanFillBuffer() { | |
| 905 // Make sure that we are staying in the executing state and end-of-stream | |
| 906 // has not been reached. | |
| 907 return GetState() == kExecuting && GetState() == GetNextState(); | |
| 908 } | |
| 909 | |
| 910 bool OmxCodec::CanAcceptInput() { | |
| 911 // We can't take input buffer when in error state. | |
| 912 // TODO(hclam): Reject when in stopped state. | |
| 913 return GetState() != kError; | |
| 914 } | |
| 915 | |
| 916 bool OmxCodec::CanAcceptOutput() { | |
| 917 // Don't output request when in error state. | |
| 918 // TODO(hclam): Reject when in stopped state. | |
| 919 return GetState() != kError; | |
| 920 } | |
| 921 | |
| 922 void OmxCodec::EmptyBufferCompleteTask(OMX_BUFFERHEADERTYPE* buffer) { | |
| 923 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 924 | |
| 925 if (!CanEmptyBuffer()) | |
| 926 return; | |
| 927 | |
| 928 scoped_refptr<Buffer> stored_buffer = processing_input_queue_.front(); | |
| 929 processing_input_queue_.pop(); | |
| 930 | |
| 931 DCHECK_EQ(const_cast<OMX_U8*>(stored_buffer.get()->GetData()), | |
| 932 buffer->pBuffer); | |
| 933 | |
| 934 feed_done_callback_->Run(stored_buffer); | |
| 935 | |
| 936 // Enqueue the available buffer beacuse the decoder has consumed it. | |
| 937 available_input_buffers_.push(buffer); | |
| 938 | |
| 939 // Try to feed more data into the decoder. | |
| 940 EmptyBufferTask(); | |
| 941 } | |
| 942 | |
| 943 void OmxCodec::EmptyBufferTask() { | |
| 944 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 945 | |
| 946 if (!CanEmptyBuffer()) | |
| 947 return; | |
| 948 | |
| 949 // Loop for all available input data and input buffer for the | |
| 950 // decoder. When input has reached EOS we need to stop. | |
| 951 while (!pending_input_queue_.empty() && | |
| 952 !available_input_buffers_.empty() && | |
| 953 !input_eos_) { | |
| 954 scoped_refptr<Buffer> buffer = pending_input_queue_.front(); | |
| 955 pending_input_queue_.pop(); | |
| 956 processing_input_queue_.push(buffer); | |
| 957 | |
| 958 OMX_BUFFERHEADERTYPE* omx_buffer = available_input_buffers_.front(); | |
| 959 available_input_buffers_.pop(); | |
| 960 | |
| 961 input_eos_ = buffer->IsEndOfStream(); | |
| 962 | |
| 963 // setup |omx_buffer|. | |
| 964 omx_buffer->nInputPortIndex = input_port_; | |
| 965 omx_buffer->nOffset = 0; | |
| 966 omx_buffer->nFlags = 0; | |
| 967 omx_buffer->pBuffer = const_cast<OMX_U8*>(buffer.get()->GetData()); | |
| 968 omx_buffer->nFilledLen = buffer.get()->GetDataSize(); | |
| 969 omx_buffer->nAllocLen = omx_buffer->nFilledLen; | |
| 970 omx_buffer->pAppPrivate = this; | |
| 971 omx_buffer->nFlags |= input_eos_ ? OMX_BUFFERFLAG_EOS : 0; | |
| 972 omx_buffer->nTimeStamp = buffer->GetTimestamp().InMilliseconds(); | |
| 973 | |
| 974 // Give this buffer to OMX. | |
| 975 OMX_ERRORTYPE ret = OMX_EmptyThisBuffer(component_handle_, omx_buffer); | |
| 976 if (ret != OMX_ErrorNone) { | |
| 977 LOG(ERROR) << "OMX_EmptyThisBuffer() failed with result " << ret; | |
| 978 StateTransitionTask(kError); | |
| 979 return; | |
| 980 } | |
| 981 } | |
| 982 } | |
| 983 | |
| 984 void OmxCodec::FillBufferCompleteTask(OMX_BUFFERHEADERTYPE* buffer) { | |
| 985 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 986 | |
| 987 // If we are not in a right state to receive this buffer then return | |
| 988 // immediately. | |
| 989 // This condition is hit when we disable the output port and we are | |
| 990 // not in executing state. In that case ignore the buffer. | |
| 991 // TODO(hclam): We should count the number of buffers received with | |
| 992 // this condition to make sure the disable command has completed. | |
| 993 if (!CanFillBuffer()) | |
| 994 return; | |
| 995 | |
| 996 // This buffer is received with decoded frame. Enqueue it and make it | |
| 997 // ready to be consumed by reads. | |
| 998 int buffer_id = kEosBuffer; | |
| 999 for (size_t i = 0; output_buffers_.size(); ++i) { | |
| 1000 if (output_buffers_[i] == buffer) { | |
| 1001 buffer_id = i; | |
| 1002 break; | |
| 1003 } | |
| 1004 } | |
| 1005 | |
| 1006 // If the buffer received from the component doesn't exist in our | |
| 1007 // list then we have an error. | |
| 1008 if (buffer_id == kEosBuffer) { | |
| 1009 LOG(ERROR) << "Received an unknown output buffer"; | |
| 1010 StateTransitionTask(kError); | |
| 1011 return; | |
| 1012 } | |
| 1013 | |
| 1014 // Determine if the buffer received is a end-of-stream buffer. If | |
| 1015 // the condition is true then assign a EOS id to the buffer. | |
| 1016 if (buffer->nFlags & OMX_BUFFERFLAG_EOS || !buffer->nFilledLen) { | |
| 1017 buffer_id = kEosBuffer; | |
| 1018 output_eos_ = true; | |
| 1019 } | |
| 1020 output_buffers_ready_.push(buffer_id); | |
| 1021 | |
| 1022 // Try to fulfill one read request. | |
| 1023 FulfillOneRead(); | |
| 1024 } | |
| 1025 | |
| 1026 void OmxCodec::FulfillOneRead() { | |
| 1027 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 1028 | |
| 1029 if (!output_buffers_ready_.empty()) { | |
| 1030 int buffer_id = output_buffers_ready_.front(); | |
| 1031 output_buffers_ready_.pop(); | |
| 1032 | |
| 1033 // If the buffer is real then save it to the in-use list. | |
| 1034 // Otherwise if it is an end-of-stream buffer then just drop it. | |
| 1035 if (buffer_id != kEosBuffer) { | |
| 1036 fill_done_callback_->Run(output_buffers_[buffer_id]); | |
| 1037 BufferUsedCallback(buffer_id); //hack, we will change this really soon. | |
| 1038 } else { | |
| 1039 fill_done_callback_->Run(static_cast<OMX_BUFFERHEADERTYPE*>(NULL)); | |
| 1040 } | |
| 1041 } | |
| 1042 } | |
| 1043 | |
| 1044 void OmxCodec::BufferUsedCallback(int buffer_id) { | |
| 1045 // If this method is called on the message loop where OmxCodec belongs, we | |
| 1046 // execute the task directly to save posting another task. | |
| 1047 if (message_loop_ == MessageLoop::current()) { | |
| 1048 BufferUsedTask(buffer_id); | |
| 1049 return; | |
| 1050 } | |
| 1051 | |
| 1052 message_loop_->PostTask( | |
| 1053 FROM_HERE, | |
| 1054 NewRunnableMethod(this, &OmxCodec::BufferUsedTask, buffer_id)); | |
| 1055 } | |
| 1056 | |
| 1057 // Handling end-of-stream: | |
| 1058 // Note that after we first receive the end-of-stream, we'll continue | |
| 1059 // to call FillThisBuffer() with the next buffer receievd from | |
| 1060 // OmxOutputSink. The end result is we'll call at most | |
| 1061 // |output_buffer_count_| of FillThisBuffer() that are expected to | |
| 1062 // receive end-of-stream buffers from OpenMAX. | |
| 1063 // It is possible to not submit FillThisBuffer() after the first | |
| 1064 // end-of-stream buffer is received from OpenMAX, but that will complicate | |
| 1065 // the logic and so we rely on OpenMAX to do the right thing. | |
| 1066 void OmxCodec::BufferUsedTask(int buffer_id) { | |
| 1067 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 1068 | |
| 1069 // Make sure an end-of-stream buffer id is not received here. | |
| 1070 CHECK(buffer_id != kEosBuffer); | |
| 1071 | |
| 1072 // We'll try to issue more FillThisBuffer() to the decoder. | |
| 1073 // If we can't do it now then just return. | |
| 1074 if (!CanFillBuffer()) | |
| 1075 return; | |
| 1076 | |
| 1077 CHECK(buffer_id >= 0 && | |
| 1078 buffer_id < static_cast<int>(output_buffers_.size())); | |
| 1079 OMX_BUFFERHEADERTYPE* omx_buffer = output_buffers_[buffer_id]; | |
| 1080 | |
| 1081 omx_buffer->nOutputPortIndex = output_port_; | |
| 1082 omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS; | |
| 1083 omx_buffer->pAppPrivate = this; | |
| 1084 OMX_ERRORTYPE ret = OMX_FillThisBuffer(component_handle_, omx_buffer); | |
| 1085 if (OMX_ErrorNone != ret) { | |
| 1086 LOG(ERROR) << "OMX_FillThisBuffer() failed with result " << ret; | |
| 1087 StateTransitionTask(kError); | |
| 1088 return; | |
| 1089 } | |
| 1090 } | |
| 1091 | |
| 1092 void OmxCodec::InitialEmptyBuffer() { | |
| 1093 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 1094 | |
| 1095 if (!CanEmptyBuffer()) | |
| 1096 return; | |
| 1097 | |
| 1098 // Use EmptyBuffer() to use available input buffers to feed decoder. | |
| 1099 EmptyBufferTask(); | |
| 1100 } | |
| 1101 | |
| 1102 void OmxCodec::InitialFillBuffer() { | |
| 1103 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 1104 | |
| 1105 if (!CanFillBuffer()) | |
| 1106 return; | |
| 1107 | |
| 1108 // Ask the decoder to fill the output buffers. | |
| 1109 for (size_t i = 0; i < output_buffers_.size(); ++i) { | |
| 1110 OMX_BUFFERHEADERTYPE* omx_buffer = output_buffers_[i]; | |
| 1111 omx_buffer->nOutputPortIndex = output_port_; | |
| 1112 // Need to clear the EOS flag. | |
| 1113 omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS; | |
| 1114 omx_buffer->pAppPrivate = this; | |
| 1115 OMX_ERRORTYPE ret = OMX_FillThisBuffer(component_handle_, omx_buffer); | |
| 1116 | |
| 1117 if (OMX_ErrorNone != ret) { | |
| 1118 LOG(ERROR) << "OMX_FillThisBuffer() failed with result " << ret; | |
| 1119 StateTransitionTask(kError); | |
| 1120 return; | |
| 1121 } | |
| 1122 } | |
| 1123 } | |
| 1124 | |
| 1125 void OmxCodec::EventHandlerInternal(OMX_HANDLETYPE component, | |
| 1126 OMX_EVENTTYPE event, | |
| 1127 OMX_U32 data1, | |
| 1128 OMX_U32 data2, | |
| 1129 OMX_PTR event_data) { | |
| 1130 switch(event) { | |
| 1131 case OMX_EventCmdComplete: { | |
| 1132 // If the last command was successful, we have completed | |
| 1133 // a state transition. So notify that we have done it | |
| 1134 // accordingly. | |
| 1135 OMX_COMMANDTYPE cmd = static_cast<OMX_COMMANDTYPE>(data1); | |
| 1136 if (cmd == OMX_CommandPortEnable) { | |
| 1137 PostDoneStateTransitionTask(); | |
| 1138 } else if (cmd == OMX_CommandPortDisable) { | |
| 1139 PostDoneStateTransitionTask(); | |
| 1140 } else if (cmd == OMX_CommandStateSet) { | |
| 1141 PostDoneStateTransitionTask(); | |
| 1142 } else { | |
| 1143 LOG(ERROR) << "Unknown command completed\n"; | |
| 1144 } | |
| 1145 break; | |
| 1146 } | |
| 1147 case OMX_EventError: | |
| 1148 if (OMX_ErrorInvalidState == (OMX_ERRORTYPE)data1) { | |
| 1149 // TODO(hclam): what to do here? | |
| 1150 } | |
| 1151 PostStateTransitionTask(kError); | |
| 1152 break; | |
| 1153 case OMX_EventPortSettingsChanged: | |
| 1154 PostStateTransitionTask(kPortSettingDisable); | |
| 1155 break; | |
| 1156 default: | |
| 1157 LOG(ERROR) << "Warning - Unknown event received\n"; | |
| 1158 break; | |
| 1159 } | |
| 1160 } | |
| 1161 | |
| 1162 void OmxCodec::EmptyBufferCallbackInternal( | |
| 1163 OMX_HANDLETYPE component, | |
| 1164 OMX_BUFFERHEADERTYPE* buffer) { | |
| 1165 message_loop_->PostTask( | |
| 1166 FROM_HERE, | |
| 1167 NewRunnableMethod(this, | |
| 1168 &OmxCodec::EmptyBufferCompleteTask, buffer)); | |
| 1169 } | |
| 1170 | |
| 1171 void OmxCodec::FillBufferCallbackInternal( | |
| 1172 OMX_HANDLETYPE component, | |
| 1173 OMX_BUFFERHEADERTYPE* buffer) { | |
| 1174 message_loop_->PostTask( | |
| 1175 FROM_HERE, | |
| 1176 NewRunnableMethod(this, | |
| 1177 &OmxCodec::FillBufferCompleteTask, buffer)); | |
| 1178 } | |
| 1179 | |
| 1180 // static | |
| 1181 OMX_ERRORTYPE OmxCodec::EventHandler(OMX_HANDLETYPE component, | |
| 1182 OMX_PTR priv_data, | |
| 1183 OMX_EVENTTYPE event, | |
| 1184 OMX_U32 data1, | |
| 1185 OMX_U32 data2, | |
| 1186 OMX_PTR event_data) { | |
| 1187 OmxCodec* decoder = static_cast<OmxCodec*>(priv_data); | |
| 1188 decoder->EventHandlerInternal(component, event, data1, data2, event_data); | |
| 1189 return OMX_ErrorNone; | |
| 1190 } | |
| 1191 | |
| 1192 // static | |
| 1193 OMX_ERRORTYPE OmxCodec::EmptyBufferCallback( | |
| 1194 OMX_HANDLETYPE component, | |
| 1195 OMX_PTR priv_data, | |
| 1196 OMX_BUFFERHEADERTYPE* buffer) { | |
| 1197 OmxCodec* decoder = static_cast<OmxCodec*>(priv_data); | |
| 1198 decoder->EmptyBufferCallbackInternal(component, buffer); | |
| 1199 return OMX_ErrorNone; | |
| 1200 } | |
| 1201 | |
| 1202 // static | |
| 1203 OMX_ERRORTYPE OmxCodec::FillBufferCallback( | |
| 1204 OMX_HANDLETYPE component, | |
| 1205 OMX_PTR priv_data, | |
| 1206 OMX_BUFFERHEADERTYPE* buffer) { | |
| 1207 OmxCodec* decoder = static_cast<OmxCodec*>(priv_data); | |
| 1208 decoder->FillBufferCallbackInternal(component, buffer); | |
| 1209 return OMX_ErrorNone; | |
| 1210 } | |
| 1211 | |
| 1212 } // namespace media | |
| OLD | NEW |