1f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons/*
2f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons * Copyright (C) Texas Instruments - http://www.ti.com/
3f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons *
4f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons * Licensed under the Apache License, Version 2.0 (the "License");
5f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons * you may not use this file except in compliance with the License.
6f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons * You may obtain a copy of the License at
7f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons *
8f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons *      http://www.apache.org/licenses/LICENSE-2.0
9f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons *
10f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons * Unless required by applicable law or agreed to in writing, software
11f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons * distributed under the License is distributed on an "AS IS" BASIS,
12f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons * See the License for the specific language governing permissions and
14f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons * limitations under the License.
15f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons */
16f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
17f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons/**
18f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons* @file OMXReprocess.cpp
19f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons*
20f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons* This file contains functionality for handling reprocessing operations.
21f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons*
22f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons*/
23f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
24f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons#include "CameraHal.h"
25f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons#include "OMXCameraAdapter.h"
26f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons#include "ErrorUtils.h"
27f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
28f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
29f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmonsnamespace Ti {
30f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmonsnamespace Camera {
31f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
32f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmonsstatus_t OMXCameraAdapter::setParametersReprocess(const android::CameraParameters &params,
33f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                                                CameraBuffer* buffers,
34f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                                                BaseCameraAdapter::AdapterState state)
35f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons{
36f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    status_t ret = NO_ERROR;
37f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    int w, h, s;
38f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    OMX_COLOR_FORMATTYPE pixFormat;
39f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    OMXCameraPortParameters *portData;
40f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    const char* valstr;
41f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
42f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    LOG_FUNCTION_NAME;
43f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
44f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if (!buffers) {
45f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        CAMHAL_LOGE("invalid buffer array");
46f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        return BAD_VALUE;
47f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
48f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
49f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    portData = &mCameraAdapterParameters.mCameraPortParams[mCameraAdapterParameters.mVideoInPortIndex];
50f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
51f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    w = buffers[0].width;
52f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    h = buffers[0].height;
53f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    s = buffers[0].stride;
54f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
55f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    valstr = buffers[0].format;
56f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if (valstr != NULL) {
57f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        if(strcmp(valstr, android::CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
58f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            CAMHAL_LOGDA("YUV420SP format selected");
59f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            pixFormat = OMX_COLOR_FormatYUV420SemiPlanar;
60f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        } else if (strcmp(valstr, android::CameraParameters::PIXEL_FORMAT_BAYER_RGGB) == 0) {
61f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            CAMHAL_LOGDA("RAW Picture format selected");
62f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            pixFormat = OMX_COLOR_FormatRawBayer10bit;
63d68392bec3edfd02bc24f8be35d84acbbd62f02cEmilian Peev        } else if (strcmp(valstr, android::CameraParameters::PIXEL_FORMAT_YUV422I) == 0) {
64d68392bec3edfd02bc24f8be35d84acbbd62f02cEmilian Peev            CAMHAL_LOGDA("YUV422i Picture format selected");
65d68392bec3edfd02bc24f8be35d84acbbd62f02cEmilian Peev            pixFormat = OMX_COLOR_FormatCbYCrY;
66f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        } else {
67f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            CAMHAL_LOGDA("Format not supported, selecting YUV420SP by default");
68f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            pixFormat = OMX_COLOR_FormatYUV420SemiPlanar;
69f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        }
70f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    } else {
71f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        CAMHAL_LOGDA("Format not supported, selecting YUV420SP by default");
72f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        pixFormat = OMX_COLOR_FormatYUV420SemiPlanar;
73f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
74f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
75f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if ( (w != (int)portData->mWidth) || (h != (int)portData->mHeight) ||
76f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons         (s != (int) portData->mStride) || (pixFormat != portData->mColorFormat)) {
77f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        portData->mWidth = w;
78f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        portData->mHeight = h;
79f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
80d68392bec3edfd02bc24f8be35d84acbbd62f02cEmilian Peev        if ( ( OMX_COLOR_FormatRawBayer10bit == pixFormat ) ||
81d68392bec3edfd02bc24f8be35d84acbbd62f02cEmilian Peev             ( OMX_COLOR_FormatCbYCrY == pixFormat ) ) {
82f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            portData->mStride = w * 2;
83f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        } else {
84f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            portData->mStride = s;
85f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        }
86f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
87f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        portData->mColorFormat = pixFormat;
88f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
89f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        mPendingReprocessSettings |= SetFormat;
90f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
91f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
92f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    LOG_FUNCTION_NAME_EXIT;
93f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
94f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    return ret;
95f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons}
96f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
97f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmonsstatus_t OMXCameraAdapter::startReprocess()
98f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons{
99f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    status_t ret = NO_ERROR;
100f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    OMX_ERRORTYPE eError = OMX_ErrorNone;
101f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    OMXCameraPortParameters * portData = NULL;
102f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
103f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    LOG_FUNCTION_NAME;
104f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    CAMHAL_LOGD ("mReprocConfigured = %d", mReprocConfigured);
105f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if (!mReprocConfigured) {
106f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        return NO_ERROR;
107f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
108f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
109f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    portData = &mCameraAdapterParameters.mCameraPortParams[mCameraAdapterParameters.mVideoInPortIndex];
110f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
111f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    CAMHAL_LOGD ("mReprocConfigured = %d", mBurstFramesQueued);
112f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if (NO_ERROR == ret) {
113f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        android::AutoMutex lock(mBurstLock);
114f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
115f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        for ( int index = 0 ; index < portData->mMaxQueueable ; index++ ) {
11611308f1fc42a01958b9c13fad908141028fda9adEmilian Peev            CAMHAL_LOGDB("Queuing buffer on video input port - %p, offset: %d, length: %d",
11711308f1fc42a01958b9c13fad908141028fda9adEmilian Peev                         portData->mBufferHeader[index]->pBuffer,
11811308f1fc42a01958b9c13fad908141028fda9adEmilian Peev                         portData->mBufferHeader[index]->nOffset,
11911308f1fc42a01958b9c13fad908141028fda9adEmilian Peev                         portData->mBufferHeader[index]->nFilledLen);
120f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            portData->mStatus[index] = OMXCameraPortParameters::FILL;
121f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            eError = OMX_EmptyThisBuffer(mCameraAdapterParameters.mHandleComp,
122f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                    (OMX_BUFFERHEADERTYPE*)portData->mBufferHeader[index]);
123f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            GOTO_EXIT_IF((eError!=OMX_ErrorNone), eError);
124f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        }
125f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
126f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
127d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev#if PPM_INSTRUMENTATION || PPM_INSTRUMENTATION_ABS
128d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev            CameraHal::PPM("startReprocess buffers queued on video port: ", &mStartCapture);
129d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev#endif
130d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev
131f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    return (ret | Utils::ErrorUtils::omxToAndroidError(eError));
132f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
133f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason SimmonsEXIT:
134f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    CAMHAL_LOGEB("Exiting function %s because of ret %d eError=%x", __FUNCTION__, ret, eError);
135f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    performCleanupAfterError();
136f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    LOG_FUNCTION_NAME_EXIT;
137f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    return (ret | Utils::ErrorUtils::omxToAndroidError(eError));
138f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons}
139f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
140f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmonsstatus_t OMXCameraAdapter::stopReprocess()
141f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons{
142f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    LOG_FUNCTION_NAME;
143f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
144f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    status_t ret = NO_ERROR;
145f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    OMX_ERRORTYPE eError = OMX_ErrorNone;
146f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    OMXCameraPortParameters *portData = NULL;
147f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
148f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if (!mReprocConfigured) {
149f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        return NO_ERROR;
150f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
151f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
152f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    portData = &mCameraAdapterParameters.mCameraPortParams[mCameraAdapterParameters.mVideoInPortIndex];
153f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
154f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    // Disable port - send command and then free all buffers
155f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    ret = RegisterForEvent(mCameraAdapterParameters.mHandleComp,
156f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                                OMX_EventCmdComplete,
157f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                                OMX_CommandPortDisable,
158f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                                mCameraAdapterParameters.mVideoInPortIndex,
159f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                                mStopReprocSem);
160f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    eError = OMX_SendCommand(mCameraAdapterParameters.mHandleComp,
161f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                                OMX_CommandPortDisable,
162f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                                mCameraAdapterParameters.mVideoInPortIndex,
163f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                                NULL);
164f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if (portData) {
165f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        CAMHAL_LOGDB("Freeing buffers on reproc port - num: %d", portData->mNumBufs);
166f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        for (int index = 0 ; index < portData->mNumBufs ; index++) {
167f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            CAMHAL_LOGDB("Freeing buffer on reproc port - 0x%x",
168f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                         ( unsigned int ) portData->mBufferHeader[index]->pBuffer);
169f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            eError = OMX_FreeBuffer(mCameraAdapterParameters.mHandleComp,
170f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                                    mCameraAdapterParameters.mVideoInPortIndex,
171f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                                    (OMX_BUFFERHEADERTYPE*)portData->mBufferHeader[index]);
172f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            GOTO_EXIT_IF((eError!=OMX_ErrorNone), eError);
173f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        }
174f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
175f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    CAMHAL_LOGDA("Waiting for port disable");
176f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    ret = mStopReprocSem.WaitTimeout(OMX_CMD_TIMEOUT);
177f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if (mComponentState == OMX_StateInvalid) {
178f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        CAMHAL_LOGEA("Invalid State after Disable Image Port Exitting!!!");
179f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        goto EXIT;
180f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
181f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if (NO_ERROR == ret) {
182f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        CAMHAL_LOGDA("Port disabled");
183f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    } else {
184f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        ret |= RemoveEvent(mCameraAdapterParameters.mHandleComp,
185f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                           OMX_EventCmdComplete,
186f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                           OMX_CommandPortDisable,
187f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                           mCameraAdapterParameters.mVideoInPortIndex,
188f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                           NULL);
189f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        CAMHAL_LOGDA("Timeout expired on port disable");
190f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        goto EXIT;
191f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
192f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
193f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    deinitInternalBuffers(mCameraAdapterParameters.mVideoInPortIndex);
194f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
195f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    mReprocConfigured = false;
196f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
197f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason SimmonsEXIT:
198f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    CAMHAL_LOGEB("Exiting function %s because of ret %d eError=%x", __FUNCTION__, ret, eError);
199f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    LOG_FUNCTION_NAME_EXIT;
200f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    return (ret | Utils::ErrorUtils::omxToAndroidError(eError));
201f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons}
202f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
203f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmonsstatus_t OMXCameraAdapter::disableReprocess(){
204f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    status_t ret = NO_ERROR;
205f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    OMX_ERRORTYPE eError = OMX_ErrorNone;
206f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
207f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    // no-op..for now
208f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
209f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason SimmonsEXIT:
210f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    return (ret | Utils::ErrorUtils::omxToAndroidError(eError));
211f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons}
212f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
213f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmonsstatus_t OMXCameraAdapter::UseBuffersReprocess(CameraBuffer *bufArr, int num)
214f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons{
215f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    LOG_FUNCTION_NAME;
216f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
217f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    status_t ret = NO_ERROR;
218f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    OMX_ERRORTYPE eError = OMX_ErrorNone;
219f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    OMXCameraPortParameters *portData = NULL;
220f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
221f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    portData = &mCameraAdapterParameters.mCameraPortParams[mCameraAdapterParameters.mVideoInPortIndex];
222f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
223f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if ( 0 != mUseReprocessSem.Count() ) {
224f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        CAMHAL_LOGEB("Error mUseReprocessSem semaphore count %d", mUseReprocessSem.Count());
225f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        return BAD_VALUE;
226f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
227f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
228f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    CAMHAL_ASSERT(num > 0);
229f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
230f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if (mAdapterState == REPROCESS_STATE) {
231f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        stopReprocess();
232f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    } else if (mAdapterState == CAPTURE_STATE) {
233f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        stopImageCapture();
234f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        stopReprocess();
235f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
236f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
237d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev#if PPM_INSTRUMENTATION || PPM_INSTRUMENTATION_ABS
238d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev
239d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev    CameraHal::PPM("Reprocess stopping image capture and disabling image port: ", &bufArr->ppmStamp);
240d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev
241d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev#endif
242d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev
243f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    portData->mNumBufs = num;
244f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
245f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    // Configure
246f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    ret = setParametersReprocess(mParams, bufArr, mAdapterState);
247f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
248f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if (mReprocConfigured) {
249f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        if (mPendingReprocessSettings & ECaptureParamSettings) {
250f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            stopReprocess();
251f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        } else {
252f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            // Tap in port has been already configured.
253f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            return NO_ERROR;
254f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        }
255f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
256f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
257f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if (mPendingReprocessSettings & SetFormat) {
258f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        mPendingReprocessSettings &= ~SetFormat;
259f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        ret = setFormat(OMX_CAMERA_PORT_VIDEO_IN_VIDEO, *portData);
260f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        if ( ret != NO_ERROR ) {
261f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            CAMHAL_LOGEB("setFormat() failed %d", ret);
262f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            LOG_FUNCTION_NAME_EXIT;
263f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons            return ret;
264f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        }
265f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
266f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
267f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    // Configure DOMX to use either gralloc handles or vptrs
268f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    OMX_TI_PARAMUSENATIVEBUFFER domxUseGrallocHandles;
269f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    OMX_INIT_STRUCT_PTR (&domxUseGrallocHandles, OMX_TI_PARAMUSENATIVEBUFFER);
270f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
271f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    domxUseGrallocHandles.nPortIndex = mCameraAdapterParameters.mVideoInPortIndex;
272f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if (bufArr[0].type == CAMERA_BUFFER_ANW) {
273f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        CAMHAL_LOGD("Using ANW");
274f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        domxUseGrallocHandles.bEnable = OMX_TRUE;
275f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
276f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        // Need to allocate tiler reservation and state we are going to be using
277f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        // pagelist buffers. Assuming this happens when buffers if from anw
278f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        initInternalBuffers(mCameraAdapterParameters.mVideoInPortIndex);
279f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    } else {
280f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        CAMHAL_LOGD("Using ION");
281f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        domxUseGrallocHandles.bEnable = OMX_FALSE;
282f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
283f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    eError = OMX_SetParameter(mCameraAdapterParameters.mHandleComp,
284f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                            (OMX_INDEXTYPE)OMX_TI_IndexUseNativeBuffers, &domxUseGrallocHandles);
285f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if (eError!=OMX_ErrorNone) {
286f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        CAMHAL_LOGEB("OMX_SetParameter - %x", eError);
287f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
288f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    GOTO_EXIT_IF((eError!=OMX_ErrorNone), eError);
289f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
290d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev#if PPM_INSTRUMENTATION || PPM_INSTRUMENTATION_ABS
291d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev
292d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev    CameraHal::PPM("Reprocess configuration done: ", &bufArr->ppmStamp);
293d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev
294d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev#endif
295d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev
296f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    // Enable Port
297f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    ret = RegisterForEvent(mCameraAdapterParameters.mHandleComp,
298f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                           OMX_EventCmdComplete,
299f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                           OMX_CommandPortEnable,
300f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                           mCameraAdapterParameters.mVideoInPortIndex,
301f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                           mUseReprocessSem);
302f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    eError = OMX_SendCommand(mCameraAdapterParameters.mHandleComp,
303f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                             OMX_CommandPortEnable,
304f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                             mCameraAdapterParameters.mVideoInPortIndex,
305f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                             NULL);
306f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    GOTO_EXIT_IF(( eError != OMX_ErrorNone ), eError);
307f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
308f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    for (int index = 0 ; index < portData->mNumBufs ; index++)
309f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    {
310f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        OMX_BUFFERHEADERTYPE *pBufferHdr;
311f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        CAMHAL_LOGDB("OMX_UseBuffer Capture address: 0x%x, size = %d",
312f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                     (unsigned int)bufArr[index].opaque,
313f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                     (int)portData->mBufSize);
314f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
315f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        eError = OMX_UseBuffer(mCameraAdapterParameters.mHandleComp,
316f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                               &pBufferHdr,
317f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                               mCameraAdapterParameters.mVideoInPortIndex,
318f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                               0,
319f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                               portData->mBufSize,
320f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                               (OMX_U8*)camera_buffer_get_omx_ptr(&bufArr[index]));
321f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
322f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        CAMHAL_LOGDB("OMX_UseBuffer = 0x%x", eError);
323f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        GOTO_EXIT_IF(( eError != OMX_ErrorNone ), eError);
324f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
325f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        pBufferHdr->pAppPrivate = (OMX_PTR) &bufArr[index];
326f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        bufArr[index].index = index;
327f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        pBufferHdr->nSize = sizeof(OMX_BUFFERHEADERTYPE);
328f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        pBufferHdr->nVersion.s.nVersionMajor = 1 ;
329f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        pBufferHdr->nVersion.s.nVersionMinor = 1 ;
330f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        pBufferHdr->nVersion.s.nRevision = 0;
331f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        pBufferHdr->nVersion.s.nStep =  0;
33211308f1fc42a01958b9c13fad908141028fda9adEmilian Peev        pBufferHdr->nOffset = bufArr[index].offset;
33311308f1fc42a01958b9c13fad908141028fda9adEmilian Peev        pBufferHdr->nFilledLen = bufArr[index].actual_size;
334f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        portData->mBufferHeader[index] = pBufferHdr;
335f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
336f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
337f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    // Wait for port enable event
338f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    CAMHAL_LOGDA("Waiting for port enable");
339f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    ret = mUseReprocessSem.WaitTimeout(OMX_CMD_TIMEOUT);
340f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
341f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    // Error out if somethiing bad happened while we wait
342f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if (mComponentState == OMX_StateInvalid) {
343f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        CAMHAL_LOGEA("Invalid State while trying to enable port for reprocessing");
344f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        goto EXIT;
345f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
346f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
347f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if (ret == NO_ERROR) {
348f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        CAMHAL_LOGDA("Port enabled");
349f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    } else {
350f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        ret |= RemoveEvent(mCameraAdapterParameters.mHandleComp,
351f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                           OMX_EventCmdComplete,
352f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                           OMX_CommandPortEnable,
353f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                           mCameraAdapterParameters.mVideoInPortIndex,
354f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons                           NULL);
355f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        CAMHAL_LOGDA("Timeout expired on port enable");
356f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        goto EXIT;
357f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
358f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
359f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    mReprocConfigured = true;
360f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
361d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev#if PPM_INSTRUMENTATION || PPM_INSTRUMENTATION_ABS
362d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev
363d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev    CameraHal::PPM("Reprocess video port enabled and buffers registered: ", &bufArr->ppmStamp);
364d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev
365d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev#endif
366d2951b18ac3cc79c6a985ff86f64fcb03a562afcEmilian Peev
367f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    return (ret | Utils::ErrorUtils::omxToAndroidError(eError));
368f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
369f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason SimmonsEXIT:
370f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    CAMHAL_LOGEB("Exiting function %s because of ret %d eError=%x", __FUNCTION__, ret, eError);
371f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    // Release image buffers
372f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    if ( NULL != mReleaseImageBuffersCallback ) {
373f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons        mReleaseImageBuffersCallback(mReleaseData);
374f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    }
375f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    performCleanupAfterError();
376f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    LOG_FUNCTION_NAME_EXIT;
377f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons    return (ret | Utils::ErrorUtils::omxToAndroidError(eError));
378f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
379f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons}
380f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons
381f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons} // namespace Camera
382f7a4d11e9f710e2cd0592310ac1baecccb85f1d1Jason Simmons} // namespace Ti
383