1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17//#define LOG_NDEBUG 0 18#define LOG_TAG "SimpleSoftOMXComponent" 19#include <utils/Log.h> 20 21#include <media/stagefright/omx/SimpleSoftOMXComponent.h> 22#include <media/stagefright/foundation/ADebug.h> 23#include <media/stagefright/foundation/ALooper.h> 24#include <media/stagefright/foundation/AMessage.h> 25 26namespace android { 27 28SimpleSoftOMXComponent::SimpleSoftOMXComponent( 29 const char *name, 30 const OMX_CALLBACKTYPE *callbacks, 31 OMX_PTR appData, 32 OMX_COMPONENTTYPE **component) 33 : SoftOMXComponent(name, callbacks, appData, component), 34 mLooper(new ALooper), 35 mHandler(new AHandlerReflector<SimpleSoftOMXComponent>(this)), 36 mState(OMX_StateLoaded), 37 mTargetState(OMX_StateLoaded) { 38 mLooper->setName(name); 39 mLooper->registerHandler(mHandler); 40 41 mLooper->start( 42 false, // runOnCallingThread 43 false, // canCallJava 44 ANDROID_PRIORITY_VIDEO); 45} 46 47void SimpleSoftOMXComponent::prepareForDestruction() { 48 // The looper's queue may still contain messages referencing this 49 // object. Make sure those are flushed before returning so that 50 // a subsequent dlunload() does not pull out the rug from under us. 51 52 mLooper->unregisterHandler(mHandler->id()); 53 mLooper->stop(); 54} 55 56OMX_ERRORTYPE SimpleSoftOMXComponent::sendCommand( 57 OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data) { 58 CHECK(data == NULL); 59 60 sp<AMessage> msg = new AMessage(kWhatSendCommand, mHandler); 61 msg->setInt32("cmd", cmd); 62 msg->setInt32("param", param); 63 msg->post(); 64 65 return OMX_ErrorNone; 66} 67 68bool SimpleSoftOMXComponent::isSetParameterAllowed( 69 OMX_INDEXTYPE index, const OMX_PTR params) const { 70 if (mState == OMX_StateLoaded) { 71 return true; 72 } 73 74 OMX_U32 portIndex; 75 76 switch (index) { 77 case OMX_IndexParamPortDefinition: 78 { 79 const OMX_PARAM_PORTDEFINITIONTYPE *portDefs = 80 (const OMX_PARAM_PORTDEFINITIONTYPE *) params; 81 if (!isValidOMXParam(portDefs)) { 82 return false; 83 } 84 portIndex = portDefs->nPortIndex; 85 break; 86 } 87 88 case OMX_IndexParamAudioPcm: 89 { 90 const OMX_AUDIO_PARAM_PCMMODETYPE *pcmMode = 91 (const OMX_AUDIO_PARAM_PCMMODETYPE *) params; 92 if (!isValidOMXParam(pcmMode)) { 93 return false; 94 } 95 portIndex = pcmMode->nPortIndex; 96 break; 97 } 98 99 case OMX_IndexParamAudioAac: 100 { 101 const OMX_AUDIO_PARAM_AACPROFILETYPE *aacMode = 102 (const OMX_AUDIO_PARAM_AACPROFILETYPE *) params; 103 if (!isValidOMXParam(aacMode)) { 104 return false; 105 } 106 portIndex = aacMode->nPortIndex; 107 break; 108 } 109 110 default: 111 return false; 112 } 113 114 CHECK(portIndex < mPorts.size()); 115 116 return !mPorts.itemAt(portIndex).mDef.bEnabled; 117} 118 119OMX_ERRORTYPE SimpleSoftOMXComponent::getParameter( 120 OMX_INDEXTYPE index, OMX_PTR params) { 121 Mutex::Autolock autoLock(mLock); 122 return internalGetParameter(index, params); 123} 124 125OMX_ERRORTYPE SimpleSoftOMXComponent::setParameter( 126 OMX_INDEXTYPE index, const OMX_PTR params) { 127 Mutex::Autolock autoLock(mLock); 128 129 CHECK(isSetParameterAllowed(index, params)); 130 131 return internalSetParameter(index, params); 132} 133 134OMX_ERRORTYPE SimpleSoftOMXComponent::internalGetParameter( 135 OMX_INDEXTYPE index, OMX_PTR params) { 136 switch (index) { 137 case OMX_IndexParamPortDefinition: 138 { 139 OMX_PARAM_PORTDEFINITIONTYPE *defParams = 140 (OMX_PARAM_PORTDEFINITIONTYPE *)params; 141 142 if (!isValidOMXParam(defParams)) { 143 return OMX_ErrorBadParameter; 144 } 145 146 if (defParams->nPortIndex >= mPorts.size() 147 || defParams->nSize 148 != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) { 149 return OMX_ErrorUndefined; 150 } 151 152 const PortInfo *port = 153 &mPorts.itemAt(defParams->nPortIndex); 154 155 memcpy(defParams, &port->mDef, sizeof(port->mDef)); 156 157 return OMX_ErrorNone; 158 } 159 160 default: 161 return OMX_ErrorUnsupportedIndex; 162 } 163} 164 165OMX_ERRORTYPE SimpleSoftOMXComponent::internalSetParameter( 166 OMX_INDEXTYPE index, const OMX_PTR params) { 167 switch (index) { 168 case OMX_IndexParamPortDefinition: 169 { 170 OMX_PARAM_PORTDEFINITIONTYPE *defParams = 171 (OMX_PARAM_PORTDEFINITIONTYPE *)params; 172 173 if (!isValidOMXParam(defParams)) { 174 return OMX_ErrorBadParameter; 175 } 176 177 if (defParams->nPortIndex >= mPorts.size()) { 178 return OMX_ErrorBadPortIndex; 179 } 180 if (defParams->nSize != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) { 181 return OMX_ErrorUnsupportedSetting; 182 } 183 184 PortInfo *port = 185 &mPorts.editItemAt(defParams->nPortIndex); 186 187 // default behavior is that we only allow buffer size to increase 188 if (defParams->nBufferSize > port->mDef.nBufferSize) { 189 port->mDef.nBufferSize = defParams->nBufferSize; 190 } 191 192 if (defParams->nBufferCountActual < port->mDef.nBufferCountMin) { 193 ALOGW("component requires at least %u buffers (%u requested)", 194 port->mDef.nBufferCountMin, defParams->nBufferCountActual); 195 return OMX_ErrorUnsupportedSetting; 196 } 197 198 port->mDef.nBufferCountActual = defParams->nBufferCountActual; 199 return OMX_ErrorNone; 200 } 201 202 default: 203 return OMX_ErrorUnsupportedIndex; 204 } 205} 206 207OMX_ERRORTYPE SimpleSoftOMXComponent::useBuffer( 208 OMX_BUFFERHEADERTYPE **header, 209 OMX_U32 portIndex, 210 OMX_PTR appPrivate, 211 OMX_U32 size, 212 OMX_U8 *ptr) { 213 Mutex::Autolock autoLock(mLock); 214 CHECK_LT(portIndex, mPorts.size()); 215 216 PortInfo *port = &mPorts.editItemAt(portIndex); 217 if (size < port->mDef.nBufferSize) { 218 ALOGE("b/63522430, Buffer size is too small."); 219 android_errorWriteLog(0x534e4554, "63522430"); 220 return OMX_ErrorBadParameter; 221 } 222 223 *header = new OMX_BUFFERHEADERTYPE; 224 (*header)->nSize = sizeof(OMX_BUFFERHEADERTYPE); 225 (*header)->nVersion.s.nVersionMajor = 1; 226 (*header)->nVersion.s.nVersionMinor = 0; 227 (*header)->nVersion.s.nRevision = 0; 228 (*header)->nVersion.s.nStep = 0; 229 (*header)->pBuffer = ptr; 230 (*header)->nAllocLen = size; 231 (*header)->nFilledLen = 0; 232 (*header)->nOffset = 0; 233 (*header)->pAppPrivate = appPrivate; 234 (*header)->pPlatformPrivate = NULL; 235 (*header)->pInputPortPrivate = NULL; 236 (*header)->pOutputPortPrivate = NULL; 237 (*header)->hMarkTargetComponent = NULL; 238 (*header)->pMarkData = NULL; 239 (*header)->nTickCount = 0; 240 (*header)->nTimeStamp = 0; 241 (*header)->nFlags = 0; 242 (*header)->nOutputPortIndex = portIndex; 243 (*header)->nInputPortIndex = portIndex; 244 245 CHECK(mState == OMX_StateLoaded || port->mDef.bEnabled == OMX_FALSE); 246 247 CHECK_LT(port->mBuffers.size(), port->mDef.nBufferCountActual); 248 249 port->mBuffers.push(); 250 251 BufferInfo *buffer = 252 &port->mBuffers.editItemAt(port->mBuffers.size() - 1); 253 254 buffer->mHeader = *header; 255 buffer->mOwnedByUs = false; 256 257 if (port->mBuffers.size() == port->mDef.nBufferCountActual) { 258 port->mDef.bPopulated = OMX_TRUE; 259 checkTransitions(); 260 } 261 262 return OMX_ErrorNone; 263} 264 265OMX_ERRORTYPE SimpleSoftOMXComponent::allocateBuffer( 266 OMX_BUFFERHEADERTYPE **header, 267 OMX_U32 portIndex, 268 OMX_PTR appPrivate, 269 OMX_U32 size) { 270 OMX_U8 *ptr = new OMX_U8[size]; 271 272 OMX_ERRORTYPE err = 273 useBuffer(header, portIndex, appPrivate, size, ptr); 274 275 if (err != OMX_ErrorNone) { 276 delete[] ptr; 277 ptr = NULL; 278 279 return err; 280 } 281 282 CHECK((*header)->pPlatformPrivate == NULL); 283 (*header)->pPlatformPrivate = ptr; 284 285 return OMX_ErrorNone; 286} 287 288OMX_ERRORTYPE SimpleSoftOMXComponent::freeBuffer( 289 OMX_U32 portIndex, 290 OMX_BUFFERHEADERTYPE *header) { 291 Mutex::Autolock autoLock(mLock); 292 293 CHECK_LT(portIndex, mPorts.size()); 294 295 PortInfo *port = &mPorts.editItemAt(portIndex); 296 297#if 0 // XXX 298 CHECK((mState == OMX_StateIdle && mTargetState == OMX_StateLoaded) 299 || port->mDef.bEnabled == OMX_FALSE); 300#endif 301 302 bool found = false; 303 for (size_t i = 0; i < port->mBuffers.size(); ++i) { 304 BufferInfo *buffer = &port->mBuffers.editItemAt(i); 305 306 if (buffer->mHeader == header) { 307 CHECK(!buffer->mOwnedByUs); 308 309 if (header->pPlatformPrivate != NULL) { 310 // This buffer's data was allocated by us. 311 CHECK(header->pPlatformPrivate == header->pBuffer); 312 313 delete[] header->pBuffer; 314 header->pBuffer = NULL; 315 } 316 317 delete header; 318 header = NULL; 319 320 port->mBuffers.removeAt(i); 321 port->mDef.bPopulated = OMX_FALSE; 322 323 checkTransitions(); 324 325 found = true; 326 break; 327 } 328 } 329 330 CHECK(found); 331 332 return OMX_ErrorNone; 333} 334 335OMX_ERRORTYPE SimpleSoftOMXComponent::emptyThisBuffer( 336 OMX_BUFFERHEADERTYPE *buffer) { 337 sp<AMessage> msg = new AMessage(kWhatEmptyThisBuffer, mHandler); 338 msg->setPointer("header", buffer); 339 msg->post(); 340 341 return OMX_ErrorNone; 342} 343 344OMX_ERRORTYPE SimpleSoftOMXComponent::fillThisBuffer( 345 OMX_BUFFERHEADERTYPE *buffer) { 346 sp<AMessage> msg = new AMessage(kWhatFillThisBuffer, mHandler); 347 msg->setPointer("header", buffer); 348 msg->post(); 349 350 return OMX_ErrorNone; 351} 352 353OMX_ERRORTYPE SimpleSoftOMXComponent::getState(OMX_STATETYPE *state) { 354 Mutex::Autolock autoLock(mLock); 355 356 *state = mState; 357 358 return OMX_ErrorNone; 359} 360 361void SimpleSoftOMXComponent::onMessageReceived(const sp<AMessage> &msg) { 362 Mutex::Autolock autoLock(mLock); 363 uint32_t msgType = msg->what(); 364 ALOGV("msgType = %d", msgType); 365 switch (msgType) { 366 case kWhatSendCommand: 367 { 368 int32_t cmd, param; 369 CHECK(msg->findInt32("cmd", &cmd)); 370 CHECK(msg->findInt32("param", ¶m)); 371 372 onSendCommand((OMX_COMMANDTYPE)cmd, (OMX_U32)param); 373 break; 374 } 375 376 case kWhatEmptyThisBuffer: 377 case kWhatFillThisBuffer: 378 { 379 OMX_BUFFERHEADERTYPE *header; 380 CHECK(msg->findPointer("header", (void **)&header)); 381 382 CHECK(mState == OMX_StateExecuting && mTargetState == mState); 383 384 bool found = false; 385 size_t portIndex = (kWhatEmptyThisBuffer == msgType)? 386 header->nInputPortIndex: header->nOutputPortIndex; 387 PortInfo *port = &mPorts.editItemAt(portIndex); 388 389 for (size_t j = 0; j < port->mBuffers.size(); ++j) { 390 BufferInfo *buffer = &port->mBuffers.editItemAt(j); 391 392 if (buffer->mHeader == header) { 393 CHECK(!buffer->mOwnedByUs); 394 395 buffer->mOwnedByUs = true; 396 397 CHECK((msgType == kWhatEmptyThisBuffer 398 && port->mDef.eDir == OMX_DirInput) 399 || (port->mDef.eDir == OMX_DirOutput)); 400 401 port->mQueue.push_back(buffer); 402 onQueueFilled(portIndex); 403 404 found = true; 405 break; 406 } 407 } 408 409 CHECK(found); 410 break; 411 } 412 413 default: 414 TRESPASS(); 415 break; 416 } 417} 418 419void SimpleSoftOMXComponent::onSendCommand( 420 OMX_COMMANDTYPE cmd, OMX_U32 param) { 421 switch (cmd) { 422 case OMX_CommandStateSet: 423 { 424 onChangeState((OMX_STATETYPE)param); 425 break; 426 } 427 428 case OMX_CommandPortEnable: 429 case OMX_CommandPortDisable: 430 { 431 onPortEnable(param, cmd == OMX_CommandPortEnable); 432 break; 433 } 434 435 case OMX_CommandFlush: 436 { 437 onPortFlush(param, true /* sendFlushComplete */); 438 break; 439 } 440 441 default: 442 TRESPASS(); 443 break; 444 } 445} 446 447void SimpleSoftOMXComponent::onChangeState(OMX_STATETYPE state) { 448 ALOGV("%p requesting change from %d to %d", this, mState, state); 449 // We shouldn't be in a state transition already. 450 451 if (mState == OMX_StateLoaded 452 && mTargetState == OMX_StateIdle 453 && state == OMX_StateLoaded) { 454 // OMX specifically allows "canceling" a state transition from loaded 455 // to idle. Pretend we made it to idle, and go back to loaded 456 ALOGV("load->idle canceled"); 457 mState = mTargetState = OMX_StateIdle; 458 state = OMX_StateLoaded; 459 } 460 461 if (mState != mTargetState) { 462 ALOGE("State change to state %d requested while still transitioning from state %d to %d", 463 state, mState, mTargetState); 464 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 465 return; 466 } 467 468 switch (mState) { 469 case OMX_StateLoaded: 470 CHECK_EQ((int)state, (int)OMX_StateIdle); 471 break; 472 case OMX_StateIdle: 473 CHECK(state == OMX_StateLoaded || state == OMX_StateExecuting); 474 break; 475 case OMX_StateExecuting: 476 { 477 CHECK_EQ((int)state, (int)OMX_StateIdle); 478 479 for (size_t i = 0; i < mPorts.size(); ++i) { 480 onPortFlush(i, false /* sendFlushComplete */); 481 } 482 483 mState = OMX_StateIdle; 484 notify(OMX_EventCmdComplete, OMX_CommandStateSet, state, NULL); 485 break; 486 } 487 488 default: 489 TRESPASS(); 490 } 491 492 mTargetState = state; 493 494 checkTransitions(); 495} 496 497void SimpleSoftOMXComponent::onReset() { 498 // no-op 499} 500 501void SimpleSoftOMXComponent::onPortEnable(OMX_U32 portIndex, bool enable) { 502 CHECK_LT(portIndex, mPorts.size()); 503 504 PortInfo *port = &mPorts.editItemAt(portIndex); 505 CHECK_EQ((int)port->mTransition, (int)PortInfo::NONE); 506 CHECK(port->mDef.bEnabled == !enable); 507 508 if (port->mDef.eDir != OMX_DirOutput) { 509 ALOGE("Port enable/disable allowed only on output ports."); 510 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 511 android_errorWriteLog(0x534e4554, "29421804"); 512 return; 513 } 514 515 if (!enable) { 516 port->mDef.bEnabled = OMX_FALSE; 517 port->mTransition = PortInfo::DISABLING; 518 519 for (size_t i = 0; i < port->mBuffers.size(); ++i) { 520 BufferInfo *buffer = &port->mBuffers.editItemAt(i); 521 522 if (buffer->mOwnedByUs) { 523 buffer->mOwnedByUs = false; 524 525 if (port->mDef.eDir == OMX_DirInput) { 526 notifyEmptyBufferDone(buffer->mHeader); 527 } else { 528 CHECK_EQ(port->mDef.eDir, OMX_DirOutput); 529 notifyFillBufferDone(buffer->mHeader); 530 } 531 } 532 } 533 534 port->mQueue.clear(); 535 } else { 536 port->mTransition = PortInfo::ENABLING; 537 } 538 539 checkTransitions(); 540} 541 542void SimpleSoftOMXComponent::onPortFlush( 543 OMX_U32 portIndex, bool sendFlushComplete) { 544 if (portIndex == OMX_ALL) { 545 for (size_t i = 0; i < mPorts.size(); ++i) { 546 onPortFlush(i, sendFlushComplete); 547 } 548 549 if (sendFlushComplete) { 550 notify(OMX_EventCmdComplete, OMX_CommandFlush, OMX_ALL, NULL); 551 } 552 553 return; 554 } 555 556 CHECK_LT(portIndex, mPorts.size()); 557 558 PortInfo *port = &mPorts.editItemAt(portIndex); 559 // Ideally, the port should not in transitioning state when flushing. 560 // However, in error handling case, e.g., the client can't allocate buffers 561 // when it tries to re-enable the port, the port will be stuck in ENABLING. 562 // The client will then transition the component from Executing to Idle, 563 // which leads to flushing ports. At this time, it should be ok to notify 564 // the client of the error and still clear all buffers on the port. 565 if (port->mTransition != PortInfo::NONE) { 566 notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); 567 } 568 569 for (size_t i = 0; i < port->mBuffers.size(); ++i) { 570 BufferInfo *buffer = &port->mBuffers.editItemAt(i); 571 572 if (!buffer->mOwnedByUs) { 573 continue; 574 } 575 576 buffer->mHeader->nFilledLen = 0; 577 buffer->mHeader->nOffset = 0; 578 buffer->mHeader->nFlags = 0; 579 580 buffer->mOwnedByUs = false; 581 582 if (port->mDef.eDir == OMX_DirInput) { 583 notifyEmptyBufferDone(buffer->mHeader); 584 } else { 585 CHECK_EQ(port->mDef.eDir, OMX_DirOutput); 586 587 notifyFillBufferDone(buffer->mHeader); 588 } 589 } 590 591 port->mQueue.clear(); 592 593 if (sendFlushComplete) { 594 notify(OMX_EventCmdComplete, OMX_CommandFlush, portIndex, NULL); 595 596 onPortFlushCompleted(portIndex); 597 } 598} 599 600void SimpleSoftOMXComponent::checkTransitions() { 601 if (mState != mTargetState) { 602 bool transitionComplete = true; 603 604 if (mState == OMX_StateLoaded) { 605 CHECK_EQ((int)mTargetState, (int)OMX_StateIdle); 606 607 for (size_t i = 0; i < mPorts.size(); ++i) { 608 const PortInfo &port = mPorts.itemAt(i); 609 if (port.mDef.bEnabled == OMX_FALSE) { 610 continue; 611 } 612 613 if (port.mDef.bPopulated == OMX_FALSE) { 614 transitionComplete = false; 615 break; 616 } 617 } 618 } else if (mTargetState == OMX_StateLoaded) { 619 CHECK_EQ((int)mState, (int)OMX_StateIdle); 620 621 for (size_t i = 0; i < mPorts.size(); ++i) { 622 const PortInfo &port = mPorts.itemAt(i); 623 if (port.mDef.bEnabled == OMX_FALSE) { 624 continue; 625 } 626 627 size_t n = port.mBuffers.size(); 628 629 if (n > 0) { 630 CHECK_LE(n, port.mDef.nBufferCountActual); 631 632 if (n == port.mDef.nBufferCountActual) { 633 CHECK_EQ((int)port.mDef.bPopulated, (int)OMX_TRUE); 634 } else { 635 CHECK_EQ((int)port.mDef.bPopulated, (int)OMX_FALSE); 636 } 637 638 transitionComplete = false; 639 break; 640 } 641 } 642 } 643 644 if (transitionComplete) { 645 ALOGV("state transition from %d to %d complete", mState, mTargetState); 646 mState = mTargetState; 647 648 if (mState == OMX_StateLoaded) { 649 onReset(); 650 } 651 652 notify(OMX_EventCmdComplete, OMX_CommandStateSet, mState, NULL); 653 } else { 654 ALOGV("state transition from %d to %d not yet complete", mState, mTargetState); 655 } 656 } 657 658 for (size_t i = 0; i < mPorts.size(); ++i) { 659 PortInfo *port = &mPorts.editItemAt(i); 660 661 if (port->mTransition == PortInfo::DISABLING) { 662 if (port->mBuffers.empty()) { 663 ALOGV("Port %zu now disabled.", i); 664 665 port->mTransition = PortInfo::NONE; 666 notify(OMX_EventCmdComplete, OMX_CommandPortDisable, i, NULL); 667 668 onPortEnableCompleted(i, false /* enabled */); 669 } 670 } else if (port->mTransition == PortInfo::ENABLING) { 671 if (port->mDef.bPopulated == OMX_TRUE) { 672 ALOGV("Port %zu now enabled.", i); 673 674 port->mTransition = PortInfo::NONE; 675 port->mDef.bEnabled = OMX_TRUE; 676 notify(OMX_EventCmdComplete, OMX_CommandPortEnable, i, NULL); 677 678 onPortEnableCompleted(i, true /* enabled */); 679 } 680 } 681 } 682} 683 684void SimpleSoftOMXComponent::addPort(const OMX_PARAM_PORTDEFINITIONTYPE &def) { 685 CHECK_EQ(def.nPortIndex, mPorts.size()); 686 687 mPorts.push(); 688 PortInfo *info = &mPorts.editItemAt(mPorts.size() - 1); 689 info->mDef = def; 690 info->mTransition = PortInfo::NONE; 691} 692 693void SimpleSoftOMXComponent::onQueueFilled(OMX_U32 portIndex __unused) { 694} 695 696void SimpleSoftOMXComponent::onPortFlushCompleted(OMX_U32 portIndex __unused) { 697} 698 699void SimpleSoftOMXComponent::onPortEnableCompleted( 700 OMX_U32 portIndex __unused, bool enabled __unused) { 701} 702 703List<SimpleSoftOMXComponent::BufferInfo *> & 704SimpleSoftOMXComponent::getPortQueue(OMX_U32 portIndex) { 705 CHECK_LT(portIndex, mPorts.size()); 706 return mPorts.editItemAt(portIndex).mQueue; 707} 708 709SimpleSoftOMXComponent::PortInfo *SimpleSoftOMXComponent::editPortInfo( 710 OMX_U32 portIndex) { 711 CHECK_LT(portIndex, mPorts.size()); 712 return &mPorts.editItemAt(portIndex); 713} 714 715} // namespace android 716