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", &param));
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