| /* | |
| * Copyright (C) 2010 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| /** Data locator, data format, data source, and data sink support */ | |
| #include "sles_allinclusive.h" | |
| #ifdef ANDROID // FIXME This file should be portable | |
| #include "android/channels.h" | |
| #include "data.h" | |
| #endif | |
| /** \brief Check a data locator and make local deep copy */ | |
| static SLresult checkDataLocator(const char *name, void *pLocator, DataLocator *pDataLocator, | |
| SLuint32 allowedDataLocatorMask) | |
| { | |
| assert(NULL != name && NULL != pDataLocator); | |
| SLresult result = SL_RESULT_SUCCESS; | |
| SLuint32 locatorType; | |
| if (NULL == pLocator) { | |
| pDataLocator->mLocatorType = locatorType = SL_DATALOCATOR_NULL; | |
| } else { | |
| locatorType = *(SLuint32 *)pLocator; | |
| switch (locatorType) { | |
| case SL_DATALOCATOR_ADDRESS: | |
| pDataLocator->mAddress = *(SLDataLocator_Address *)pLocator; | |
| // if length is greater than zero, then the address must be non-NULL | |
| if ((0 < pDataLocator->mAddress.length) && (NULL == pDataLocator->mAddress.pAddress)) { | |
| SL_LOGE("%s: pAddress=NULL", name); | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| break; | |
| case SL_DATALOCATOR_BUFFERQUEUE: | |
| #ifdef ANDROID | |
| // This is an alias that is _not_ converted; the rest of the code must check for both | |
| // locator types. That's because it is only an alias for audio players, not audio recorder | |
| // objects so we have to remember the distinction. | |
| case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE: | |
| #endif | |
| pDataLocator->mBufferQueue = *(SLDataLocator_BufferQueue *)pLocator; | |
| // number of buffers must be specified, there is no default value, and can't be too big | |
| if (!((1 <= pDataLocator->mBufferQueue.numBuffers) && | |
| (pDataLocator->mBufferQueue.numBuffers <= 255))) { | |
| SL_LOGE("%s: numBuffers=%u", name, pDataLocator->mBufferQueue.numBuffers); | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| break; | |
| case SL_DATALOCATOR_IODEVICE: | |
| { | |
| pDataLocator->mIODevice = *(SLDataLocator_IODevice *)pLocator; | |
| SLuint32 deviceType = pDataLocator->mIODevice.deviceType; | |
| SLObjectItf device = pDataLocator->mIODevice.device; | |
| if (NULL != device) { | |
| pDataLocator->mIODevice.deviceID = 0; | |
| SLuint32 expectedObjectID; | |
| switch (deviceType) { | |
| case SL_IODEVICE_LEDARRAY: | |
| expectedObjectID = SL_OBJECTID_LEDDEVICE; | |
| break; | |
| case SL_IODEVICE_VIBRA: | |
| expectedObjectID = SL_OBJECTID_VIBRADEVICE; | |
| break; | |
| case XA_IODEVICE_CAMERA: | |
| expectedObjectID = XA_OBJECTID_CAMERADEVICE; | |
| break; | |
| case XA_IODEVICE_RADIO: | |
| expectedObjectID = XA_OBJECTID_RADIODEVICE; | |
| break; | |
| // audio input and audio output cannot be specified via objects | |
| case SL_IODEVICE_AUDIOINPUT: | |
| // case SL_IODEVICE_AUDIOOUTPUT: // does not exist in 1.0.1, added in 1.1 | |
| default: | |
| SL_LOGE("%s: deviceType=%u", name, deviceType); | |
| pDataLocator->mIODevice.device = NULL; | |
| expectedObjectID = 0; | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| if (result == SL_RESULT_SUCCESS) { | |
| // check that device has the correct object ID and is realized, | |
| // and acquire a strong reference to it | |
| result = AcquireStrongRef((IObject *) device, expectedObjectID); | |
| if (SL_RESULT_SUCCESS != result) { | |
| SL_LOGE("%s: locatorType=IODEVICE, but device field %p has wrong " \ | |
| "object ID or is not realized", name, device); | |
| pDataLocator->mIODevice.device = NULL; | |
| } | |
| } | |
| } else { | |
| SLuint32 deviceID = pDataLocator->mIODevice.deviceID; | |
| switch (deviceType) { | |
| case SL_IODEVICE_LEDARRAY: | |
| if (SL_DEFAULTDEVICEID_LED != deviceID) { | |
| SL_LOGE("%s: invalid LED deviceID=%u", name, deviceID); | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| break; | |
| case SL_IODEVICE_VIBRA: | |
| if (SL_DEFAULTDEVICEID_VIBRA != deviceID) { | |
| SL_LOGE("%s: invalid vibra deviceID=%u", name, deviceID); | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| break; | |
| case SL_IODEVICE_AUDIOINPUT: | |
| if (SL_DEFAULTDEVICEID_AUDIOINPUT != deviceID) { | |
| SL_LOGE("%s: invalid audio input deviceID=%u", name, deviceID); | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| break; | |
| case XA_IODEVICE_RADIO: | |
| // no default device ID for radio; see Khronos bug XXXX | |
| break; | |
| case XA_IODEVICE_CAMERA: | |
| if (XA_DEFAULTDEVICEID_CAMERA != deviceID) { | |
| SL_LOGE("%s: invalid audio input deviceID=%u", name, deviceID); | |
| result = XA_RESULT_PARAMETER_INVALID; | |
| } | |
| break; | |
| // case SL_IODEVICE_AUDIOOUTPUT: | |
| // does not exist in 1.0.1, added in 1.1 | |
| // break; | |
| default: | |
| SL_LOGE("%s: deviceType=%u is invalid", name, deviceType); | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| } | |
| } | |
| break; | |
| case SL_DATALOCATOR_MIDIBUFFERQUEUE: | |
| pDataLocator->mMIDIBufferQueue = *(SLDataLocator_MIDIBufferQueue *)pLocator; | |
| if (0 == pDataLocator->mMIDIBufferQueue.tpqn) { | |
| pDataLocator->mMIDIBufferQueue.tpqn = 192; | |
| } | |
| // number of buffers must be specified, there is no default value, and can't be too big | |
| if (!((1 <= pDataLocator->mMIDIBufferQueue.numBuffers) && | |
| (pDataLocator->mMIDIBufferQueue.numBuffers <= 255))) { | |
| SL_LOGE("%s: SLDataLocator_MIDIBufferQueue.numBuffers=%d", name, | |
| pDataLocator->mMIDIBufferQueue.numBuffers); | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| break; | |
| case SL_DATALOCATOR_OUTPUTMIX: | |
| pDataLocator->mOutputMix = *(SLDataLocator_OutputMix *)pLocator; | |
| // check that output mix object has the correct object ID and is realized, | |
| // and acquire a strong reference to it | |
| result = AcquireStrongRef((IObject *) pDataLocator->mOutputMix.outputMix, | |
| SL_OBJECTID_OUTPUTMIX); | |
| if (SL_RESULT_SUCCESS != result) { | |
| SL_LOGE("%s: locatorType=SL_DATALOCATOR_OUTPUTMIX, but outputMix field %p does " \ | |
| "not refer to an SL_OBJECTID_OUTPUTMIX or the output mix is not realized", \ | |
| name, pDataLocator->mOutputMix.outputMix); | |
| pDataLocator->mOutputMix.outputMix = NULL; | |
| } | |
| break; | |
| case XA_DATALOCATOR_NATIVEDISPLAY: | |
| pDataLocator->mNativeDisplay = *(XADataLocator_NativeDisplay *)pLocator; | |
| // hWindow is NDK C ANativeWindow * and hDisplay must be NULL | |
| if (pDataLocator->mNativeDisplay.hWindow == NULL) { | |
| SL_LOGE("%s: hWindow must be non-NULL ANativeWindow *", name); | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| if (pDataLocator->mNativeDisplay.hDisplay != NULL) { | |
| SL_LOGE("%s: hDisplay must be NULL, but is %p", name, | |
| pDataLocator->mNativeDisplay.hDisplay); | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| break; | |
| case SL_DATALOCATOR_URI: | |
| { | |
| pDataLocator->mURI = *(SLDataLocator_URI *)pLocator; | |
| if (NULL == pDataLocator->mURI.URI) { | |
| SL_LOGE("%s: invalid URI=NULL", name); | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } else { | |
| // NTH verify URI address for validity | |
| size_t len = strlen((const char *) pDataLocator->mURI.URI); | |
| SLchar *myURI = (SLchar *) malloc(len + 1); | |
| if (NULL == myURI) { | |
| result = SL_RESULT_MEMORY_FAILURE; | |
| } else { | |
| memcpy(myURI, pDataLocator->mURI.URI, len + 1); | |
| // Verify that another thread didn't change the NUL-terminator after we used it | |
| // to determine length of string to copy. It's OK if the string became shorter. | |
| if ('\0' != myURI[len]) { | |
| free(myURI); | |
| myURI = NULL; | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| } | |
| pDataLocator->mURI.URI = myURI; | |
| } | |
| } | |
| break; | |
| #ifdef ANDROID | |
| case SL_DATALOCATOR_ANDROIDFD: | |
| { | |
| pDataLocator->mFD = *(SLDataLocator_AndroidFD *)pLocator; | |
| SL_LOGV("%s: fd=%d offset=%lld length=%lld", name, pDataLocator->mFD.fd, | |
| pDataLocator->mFD.offset, pDataLocator->mFD.length); | |
| // NTH check against process fd limit | |
| if (0 > pDataLocator->mFD.fd) { | |
| SL_LOGE("%s: fd=%d\n", name, pDataLocator->mFD.fd); | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| break; | |
| } | |
| case SL_DATALOCATOR_ANDROIDBUFFERQUEUE: | |
| { | |
| pDataLocator->mABQ = *(SLDataLocator_AndroidBufferQueue*)pLocator; | |
| // number of buffers must be specified, there is no default value, and can't be too big | |
| if (!((1 <= pDataLocator->mBufferQueue.numBuffers) && | |
| (pDataLocator->mBufferQueue.numBuffers <= 255))) { | |
| SL_LOGE("%s: numBuffers=%u", name, pDataLocator->mABQ.numBuffers); | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| break; | |
| } | |
| #endif | |
| case SL_DATALOCATOR_NULL: // a NULL pointer is allowed, but not a pointer to NULL | |
| default: | |
| SL_LOGE("%s: locatorType=%u", name, locatorType); | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| // Verify that another thread didn't change the locatorType field after we used it | |
| // to determine sizeof struct to copy. | |
| if ((SL_RESULT_SUCCESS == result) && (locatorType != pDataLocator->mLocatorType)) { | |
| SL_LOGE("%s: locatorType changed from %u to %u", name, locatorType, | |
| pDataLocator->mLocatorType); | |
| result = SL_RESULT_PRECONDITIONS_VIOLATED; | |
| } | |
| } | |
| // Verify that the data locator type is allowed in this context | |
| if (SL_RESULT_SUCCESS == result) { | |
| SLuint32 actualMask; | |
| switch (locatorType) { | |
| case SL_DATALOCATOR_NULL: | |
| case SL_DATALOCATOR_URI: | |
| case SL_DATALOCATOR_ADDRESS: | |
| case SL_DATALOCATOR_IODEVICE: | |
| case SL_DATALOCATOR_OUTPUTMIX: | |
| case XA_DATALOCATOR_NATIVEDISPLAY: | |
| case SL_DATALOCATOR_BUFFERQUEUE: | |
| case SL_DATALOCATOR_MIDIBUFFERQUEUE: | |
| actualMask = 1L << locatorType; | |
| break; | |
| #ifdef ANDROID | |
| case SL_DATALOCATOR_ANDROIDFD: | |
| case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE: | |
| case SL_DATALOCATOR_ANDROIDBUFFERQUEUE: | |
| actualMask = 0x100L << (locatorType - SL_DATALOCATOR_ANDROIDFD); | |
| break; | |
| #endif | |
| default: | |
| assert(false); | |
| actualMask = 0L; | |
| break; | |
| } | |
| if (!(allowedDataLocatorMask & actualMask)) { | |
| SL_LOGE("%s: data locator type 0x%x not allowed", name, locatorType); | |
| result = SL_RESULT_CONTENT_UNSUPPORTED; | |
| } | |
| } | |
| return result; | |
| } | |
| /** \brief Free the local deep copy of a data locator */ | |
| static void freeDataLocator(DataLocator *pDataLocator) | |
| { | |
| switch (pDataLocator->mLocatorType) { | |
| case SL_DATALOCATOR_NULL: | |
| case SL_DATALOCATOR_ADDRESS: | |
| case SL_DATALOCATOR_BUFFERQUEUE: | |
| case SL_DATALOCATOR_MIDIBUFFERQUEUE: | |
| case XA_DATALOCATOR_NATIVEDISPLAY: | |
| break; | |
| case SL_DATALOCATOR_URI: | |
| if (NULL != pDataLocator->mURI.URI) { | |
| free(pDataLocator->mURI.URI); | |
| pDataLocator->mURI.URI = NULL; | |
| } | |
| pDataLocator->mURI.URI = NULL; | |
| break; | |
| case SL_DATALOCATOR_IODEVICE: | |
| if (NULL != pDataLocator->mIODevice.device) { | |
| ReleaseStrongRef((IObject *) pDataLocator->mIODevice.device); | |
| pDataLocator->mIODevice.device = NULL; | |
| } | |
| break; | |
| case SL_DATALOCATOR_OUTPUTMIX: | |
| if (NULL != pDataLocator->mOutputMix.outputMix) { | |
| ReleaseStrongRef((IObject *) pDataLocator->mOutputMix.outputMix); | |
| pDataLocator->mOutputMix.outputMix = NULL; | |
| } | |
| break; | |
| #ifdef ANDROID | |
| case SL_DATALOCATOR_ANDROIDBUFFERQUEUE: | |
| case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE: | |
| case SL_DATALOCATOR_ANDROIDFD: | |
| break; | |
| #endif | |
| default: | |
| // an invalid data locator is caught earlier when making the copy | |
| assert(false); | |
| break; | |
| } | |
| } | |
| /** \brief Check a data format and make local deep copy */ | |
| static SLresult checkDataFormat(const char *name, void *pFormat, DataFormat *pDataFormat, | |
| SLuint32 allowedDataFormatMask, SLboolean isOutputFormat) | |
| { | |
| assert(NULL != name && NULL != pDataFormat); | |
| SLresult result = SL_RESULT_SUCCESS; | |
| const SLuint32 *df_representation = NULL; // pointer to representation field, if it exists | |
| SLuint32 formatType; | |
| if (NULL == pFormat) { | |
| pDataFormat->mFormatType = formatType = SL_DATAFORMAT_NULL; | |
| } else { | |
| formatType = *(SLuint32 *)pFormat; | |
| switch (formatType) { | |
| case SL_ANDROID_DATAFORMAT_PCM_EX: | |
| pDataFormat->mPCMEx.representation = | |
| ((SLAndroidDataFormat_PCM_EX *)pFormat)->representation; | |
| switch (pDataFormat->mPCMEx.representation) { | |
| case SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT: | |
| case SL_ANDROID_PCM_REPRESENTATION_UNSIGNED_INT: | |
| case SL_ANDROID_PCM_REPRESENTATION_FLOAT: | |
| df_representation = &pDataFormat->mPCMEx.representation; | |
| break; | |
| default: | |
| SL_LOGE("%s: unsupported representation: %d", name, | |
| pDataFormat->mPCMEx.representation); | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| break; | |
| } | |
| // SL_ANDROID_DATAFORMAT_PCM_EX - fall through to next test. | |
| FALLTHROUGH_INTENDED; | |
| case SL_DATAFORMAT_PCM: | |
| pDataFormat->mPCM = *(SLDataFormat_PCM *)pFormat; | |
| do { | |
| if (pDataFormat->mPCM.numChannels == 0) { | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } else if (pDataFormat->mPCM.numChannels > SL_ANDROID_SPEAKER_COUNT_MAX) { | |
| result = SL_RESULT_CONTENT_UNSUPPORTED; | |
| } | |
| if (SL_RESULT_SUCCESS != result) { | |
| SL_LOGE("%s: numChannels=%u", name, (unsigned) pDataFormat->mPCM.numChannels); | |
| break; | |
| } | |
| // check the sampling rate | |
| if (pDataFormat->mPCM.samplesPerSec == 0) { | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } else if (pDataFormat->mPCM.samplesPerSec < SL_SAMPLINGRATE_8 || | |
| pDataFormat->mPCM.samplesPerSec > SL_SAMPLINGRATE_192) { | |
| result = SL_RESULT_CONTENT_UNSUPPORTED; | |
| } | |
| if (SL_RESULT_SUCCESS != result) { | |
| SL_LOGE("%s: samplesPerSec=%u", name, pDataFormat->mPCM.samplesPerSec); | |
| break; | |
| } | |
| // check the container bit depth and representation | |
| switch (pDataFormat->mPCM.containerSize) { | |
| case 8: | |
| if (df_representation != NULL && | |
| *df_representation != SL_ANDROID_PCM_REPRESENTATION_UNSIGNED_INT) { | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| break; | |
| case 16: | |
| case 24: | |
| if (df_representation != NULL && | |
| *df_representation != SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT) { | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| break; | |
| case 32: | |
| if (df_representation != NULL | |
| && *df_representation != SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT | |
| && *df_representation != SL_ANDROID_PCM_REPRESENTATION_FLOAT) { | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| break; | |
| default: | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| break; | |
| } | |
| if (SL_RESULT_SUCCESS != result) { | |
| SL_LOGE("%s: containerSize=%u", name, pDataFormat->mPCM.containerSize); | |
| break; | |
| } | |
| // sample size cannot be zero, and container size cannot be less than sample size | |
| if (pDataFormat->mPCM.bitsPerSample == 0 || | |
| pDataFormat->mPCM.containerSize < pDataFormat->mPCM.bitsPerSample) { | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } | |
| if (SL_RESULT_SUCCESS != result) { | |
| SL_LOGE("%s: containerSize=%u, bitsPerSample=%u", name, | |
| (unsigned) pDataFormat->mPCM.containerSize, | |
| (unsigned) pDataFormat->mPCM.bitsPerSample); | |
| break; | |
| } | |
| // check the channel mask | |
| SL_LOGV("%s: Requested channel mask of 0x%x for %d channel audio", | |
| name, | |
| pDataFormat->mPCM.channelMask, | |
| pDataFormat->mPCM.numChannels); | |
| if (pDataFormat->mPCM.channelMask == 0) { | |
| // We can derive the channel mask from the channel count, | |
| // but issue a warning--the automatic mask generation | |
| // makes a lot of assumptions that may or may not be what | |
| // the app was expecting. | |
| SLuint32 mask = isOutputFormat | |
| ? sles_channel_out_mask_from_count(pDataFormat->mPCM.numChannels) | |
| : sles_channel_in_mask_from_count(pDataFormat->mPCM.numChannels); | |
| if (mask == SL_ANDROID_UNKNOWN_CHANNELMASK) { | |
| SL_LOGE("No channel mask specified and no default mapping for" | |
| "requested speaker count of %u", pDataFormat->mPCM.numChannels); | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } else { | |
| pDataFormat->mPCM.channelMask = mask; | |
| SL_LOGW("No channel mask specified; Using mask %#x based on requested" | |
| "speaker count of %u", | |
| pDataFormat->mPCM.channelMask, | |
| pDataFormat->mPCM.numChannels); | |
| } | |
| } | |
| SLuint32 mask = pDataFormat->mPCM.channelMask; | |
| SLuint32 count = sles_channel_count_from_mask(mask); | |
| if (count != pDataFormat->mPCM.numChannels) { | |
| SL_LOGE("%s: requested %d channels but mask (0x%x) has %d channel bits set", | |
| name, | |
| pDataFormat->mPCM.numChannels, | |
| mask, | |
| count); | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| break; | |
| } | |
| SL_LOGV("%s: final channel mask is 0x%x", name, pDataFormat->mPCM.channelMask); | |
| // check the endianness / byte order | |
| switch (pDataFormat->mPCM.endianness) { | |
| case SL_BYTEORDER_LITTLEENDIAN: | |
| case SL_BYTEORDER_BIGENDIAN: | |
| break; | |
| // native is proposed but not yet in spec | |
| default: | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| break; | |
| } | |
| if (SL_RESULT_SUCCESS != result) { | |
| SL_LOGE("%s: endianness=%u", name, (unsigned) pDataFormat->mPCM.endianness); | |
| break; | |
| } | |
| // here if all checks passed successfully | |
| } while(0); | |
| break; | |
| case SL_DATAFORMAT_MIME: | |
| pDataFormat->mMIME = *(SLDataFormat_MIME *)pFormat; | |
| if (NULL != pDataFormat->mMIME.mimeType) { | |
| // NTH check address for validity | |
| size_t len = strlen((const char *) pDataFormat->mMIME.mimeType); | |
| SLchar *myMIME = (SLchar *) malloc(len + 1); | |
| if (NULL == myMIME) { | |
| result = SL_RESULT_MEMORY_FAILURE; | |
| } else { | |
| memcpy(myMIME, pDataFormat->mMIME.mimeType, len + 1); | |
| // make sure MIME string was not modified asynchronously | |
| if ('\0' != myMIME[len]) { | |
| free(myMIME); | |
| myMIME = NULL; | |
| result = SL_RESULT_PRECONDITIONS_VIOLATED; | |
| } | |
| } | |
| pDataFormat->mMIME.mimeType = myMIME; | |
| } | |
| break; | |
| case XA_DATAFORMAT_RAWIMAGE: | |
| pDataFormat->mRawImage = *(XADataFormat_RawImage *)pFormat; | |
| switch (pDataFormat->mRawImage.colorFormat) { | |
| case XA_COLORFORMAT_MONOCHROME: | |
| case XA_COLORFORMAT_8BITRGB332: | |
| case XA_COLORFORMAT_12BITRGB444: | |
| case XA_COLORFORMAT_16BITARGB4444: | |
| case XA_COLORFORMAT_16BITARGB1555: | |
| case XA_COLORFORMAT_16BITRGB565: | |
| case XA_COLORFORMAT_16BITBGR565: | |
| case XA_COLORFORMAT_18BITRGB666: | |
| case XA_COLORFORMAT_18BITARGB1665: | |
| case XA_COLORFORMAT_19BITARGB1666: | |
| case XA_COLORFORMAT_24BITRGB888: | |
| case XA_COLORFORMAT_24BITBGR888: | |
| case XA_COLORFORMAT_24BITARGB1887: | |
| case XA_COLORFORMAT_25BITARGB1888: | |
| case XA_COLORFORMAT_32BITBGRA8888: | |
| case XA_COLORFORMAT_32BITARGB8888: | |
| case XA_COLORFORMAT_YUV411PLANAR: | |
| case XA_COLORFORMAT_YUV420PLANAR: | |
| case XA_COLORFORMAT_YUV420SEMIPLANAR: | |
| case XA_COLORFORMAT_YUV422PLANAR: | |
| case XA_COLORFORMAT_YUV422SEMIPLANAR: | |
| case XA_COLORFORMAT_YCBYCR: | |
| case XA_COLORFORMAT_YCRYCB: | |
| case XA_COLORFORMAT_CBYCRY: | |
| case XA_COLORFORMAT_CRYCBY: | |
| case XA_COLORFORMAT_YUV444INTERLEAVED: | |
| case XA_COLORFORMAT_RAWBAYER8BIT: | |
| case XA_COLORFORMAT_RAWBAYER10BIT: | |
| case XA_COLORFORMAT_RAWBAYER8BITCOMPRESSED: | |
| case XA_COLORFORMAT_L2: | |
| case XA_COLORFORMAT_L4: | |
| case XA_COLORFORMAT_L8: | |
| case XA_COLORFORMAT_L16: | |
| case XA_COLORFORMAT_L24: | |
| case XA_COLORFORMAT_L32: | |
| case XA_COLORFORMAT_18BITBGR666: | |
| case XA_COLORFORMAT_24BITARGB6666: | |
| case XA_COLORFORMAT_24BITABGR6666: | |
| break; | |
| case XA_COLORFORMAT_UNUSED: | |
| default: | |
| result = XA_RESULT_PARAMETER_INVALID; | |
| SL_LOGE("%s: unsupported color format %d", name, | |
| pDataFormat->mRawImage.colorFormat); | |
| break; | |
| } | |
| // no checks for height, width, or stride | |
| break; | |
| default: | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| SL_LOGE("%s: formatType=%u", name, (unsigned) formatType); | |
| break; | |
| } | |
| // make sure format type was not modified asynchronously | |
| if ((SL_RESULT_SUCCESS == result) && (formatType != pDataFormat->mFormatType)) { | |
| SL_LOGE("%s: formatType changed from %u to %u", name, formatType, | |
| pDataFormat->mFormatType); | |
| result = SL_RESULT_PRECONDITIONS_VIOLATED; | |
| } | |
| } | |
| // Verify that the data format type is allowed in this context | |
| if (SL_RESULT_SUCCESS == result) { | |
| SLuint32 actualMask; | |
| switch (formatType) { | |
| case SL_DATAFORMAT_NULL: | |
| case SL_DATAFORMAT_MIME: | |
| case SL_DATAFORMAT_PCM: | |
| case SL_ANDROID_DATAFORMAT_PCM_EX: | |
| case XA_DATAFORMAT_RAWIMAGE: | |
| actualMask = 1L << formatType; | |
| break; | |
| default: | |
| assert(false); | |
| actualMask = 0L; | |
| break; | |
| } | |
| if (!(allowedDataFormatMask & actualMask)) { | |
| SL_LOGE("%s: data format %d not allowed", name, formatType); | |
| result = SL_RESULT_CONTENT_UNSUPPORTED; | |
| } | |
| } | |
| return result; | |
| } | |
| /** \brief Check interface ID compatibility with respect to a particular source | |
| * and sink data locator format | |
| */ | |
| SLresult checkSourceSinkVsInterfacesCompatibility(const DataLocatorFormat *pSrcDataLocatorFormat, | |
| const DataLocatorFormat *pSinkDataLocatorFormat, | |
| const ClassTable *clazz, unsigned requiredMask) { | |
| int index; | |
| switch (pSrcDataLocatorFormat->mLocator.mLocatorType) { | |
| case SL_DATALOCATOR_URI: | |
| #ifdef ANDROID | |
| case SL_DATALOCATOR_ANDROIDFD: | |
| #endif | |
| // URIs and FD can be sources when "playing" to an OutputMix or a Buffer Queue for decode | |
| // so we don't prevent the retrieval of the BufferQueue interfaces for those sources | |
| switch (pSinkDataLocatorFormat->mLocator.mLocatorType) { | |
| case SL_DATALOCATOR_BUFFERQUEUE: | |
| #ifdef ANDROID | |
| case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE: | |
| #endif | |
| break; | |
| default: | |
| // can't require SLBufferQueueItf or its alias SLAndroidSimpleBufferQueueItf | |
| // if the data sink is not a buffer queue | |
| index = clazz->mMPH_to_index[MPH_BUFFERQUEUE]; | |
| #ifdef ANDROID | |
| assert(index == clazz->mMPH_to_index[MPH_ANDROIDSIMPLEBUFFERQUEUE]); | |
| #endif | |
| if (0 <= index) { | |
| if (requiredMask & (1 << index)) { | |
| SL_LOGE("can't require SL_IID_BUFFERQUEUE " | |
| #ifdef ANDROID | |
| "or SL_IID_ANDROIDSIMPLEBUFFERQUEUE " | |
| #endif | |
| "with a non-buffer queue data sink"); | |
| return SL_RESULT_FEATURE_UNSUPPORTED; | |
| } | |
| } | |
| break; | |
| } | |
| break; | |
| case SL_DATALOCATOR_BUFFERQUEUE: | |
| #ifdef ANDROID | |
| case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE: | |
| #endif | |
| // can't require SLSeekItf if data source is a buffer queue | |
| index = clazz->mMPH_to_index[MPH_SEEK]; | |
| if (0 <= index) { | |
| if (requiredMask & (1 << index)) { | |
| SL_LOGE("can't require SL_IID_SEEK with a buffer queue data source"); | |
| return SL_RESULT_FEATURE_UNSUPPORTED; | |
| } | |
| } | |
| // can't require SLMuteSoloItf if data source is a mono buffer queue | |
| index = clazz->mMPH_to_index[MPH_MUTESOLO]; | |
| if (0 <= index) { | |
| if ((requiredMask & (1 << index)) && | |
| (SL_DATAFORMAT_PCM == pSrcDataLocatorFormat->mFormat.mFormatType) && | |
| (1 == pSrcDataLocatorFormat->mFormat.mPCM.numChannels)) { | |
| SL_LOGE("can't require SL_IID_MUTESOLO with a mono buffer queue data source"); | |
| return SL_RESULT_FEATURE_UNSUPPORTED; | |
| } | |
| } | |
| break; | |
| #ifdef ANDROID | |
| case SL_DATALOCATOR_ANDROIDBUFFERQUEUE: | |
| // can't require SLSeekItf if data source is an Android buffer queue | |
| index = clazz->mMPH_to_index[MPH_SEEK]; | |
| if (0 <= index) { | |
| if (requiredMask & (1 << index)) { | |
| SL_LOGE("can't require SL_IID_SEEK with a SL_DATALOCATOR_ANDROIDBUFFERQUEUE "\ | |
| "source"); | |
| return SL_RESULT_FEATURE_UNSUPPORTED; | |
| } | |
| } | |
| switch (pSinkDataLocatorFormat->mLocator.mLocatorType) { | |
| // for use-case AAC decode from SLAndroidBufferQueueItf with AAC ADTS data | |
| case SL_DATALOCATOR_BUFFERQUEUE: | |
| case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE: | |
| break; | |
| // for use-case audio playback from SLAndroidBufferQueueItf with MP2TS data | |
| case SL_DATALOCATOR_OUTPUTMIX: | |
| break; | |
| default: | |
| SL_LOGE("Invalid sink for SL_DATALOCATOR_ANDROIDBUFFERQUEUE source"); | |
| return SL_RESULT_FEATURE_UNSUPPORTED; | |
| break; | |
| } | |
| break; | |
| #endif | |
| case SL_DATALOCATOR_ADDRESS: | |
| case SL_DATALOCATOR_MIDIBUFFERQUEUE: | |
| case XA_DATALOCATOR_NATIVEDISPLAY: | |
| // any special checks here??? | |
| default: | |
| // can't require SLBufferQueueItf or its alias SLAndroidSimpleBufferQueueItf | |
| // if the data source is not a buffer queue | |
| index = clazz->mMPH_to_index[MPH_BUFFERQUEUE]; | |
| #ifdef ANDROID | |
| assert(index == clazz->mMPH_to_index[MPH_ANDROIDSIMPLEBUFFERQUEUE]); | |
| #endif | |
| if (0 <= index) { | |
| if (requiredMask & (1 << index)) { | |
| SL_LOGE("can't require SL_IID_BUFFERQUEUE " | |
| #ifdef ANDROID | |
| "or SL_IID_ANDROIDSIMPLEBUFFERQUEUE " | |
| #endif | |
| "with a non-buffer queue data source"); | |
| return SL_RESULT_FEATURE_UNSUPPORTED; | |
| } | |
| } | |
| break; | |
| } | |
| return SL_RESULT_SUCCESS; | |
| } | |
| /** \brief Free the local deep copy of a data format */ | |
| static void freeDataFormat(DataFormat *pDataFormat) | |
| { | |
| switch (pDataFormat->mFormatType) { | |
| case SL_DATAFORMAT_MIME: | |
| if (NULL != pDataFormat->mMIME.mimeType) { | |
| free(pDataFormat->mMIME.mimeType); | |
| pDataFormat->mMIME.mimeType = NULL; | |
| } | |
| break; | |
| case SL_ANDROID_DATAFORMAT_PCM_EX: | |
| case SL_DATAFORMAT_PCM: | |
| case XA_DATAFORMAT_RAWIMAGE: | |
| case SL_DATAFORMAT_NULL: | |
| break; | |
| default: | |
| // an invalid data format is caught earlier during the copy | |
| assert(false); | |
| break; | |
| } | |
| } | |
| /** \brief Check a data source and make local deep copy */ | |
| SLresult checkDataSource(const char *name, const SLDataSource *pDataSrc, | |
| DataLocatorFormat *pDataLocatorFormat, SLuint32 allowedDataLocatorMask, | |
| SLuint32 allowedDataFormatMask) | |
| { | |
| assert(NULL != name && NULL != pDataLocatorFormat); | |
| pDataLocatorFormat->u.mSource.pLocator = &pDataLocatorFormat->mLocator; | |
| pDataLocatorFormat->u.mSource.pFormat = &pDataLocatorFormat->mFormat; | |
| if (NULL == pDataSrc) { | |
| pDataLocatorFormat->mLocator.mLocatorType = SL_DATALOCATOR_NULL; | |
| pDataLocatorFormat->mFormat.mFormatType = SL_DATAFORMAT_NULL; | |
| if ((allowedDataLocatorMask & DATALOCATOR_MASK_NULL) && | |
| (allowedDataFormatMask & DATAFORMAT_MASK_NULL)) { | |
| return SL_RESULT_SUCCESS; | |
| } | |
| SL_LOGE("%s: data source cannot be NULL", name); | |
| return SL_RESULT_PARAMETER_INVALID; | |
| } | |
| SLDataSource myDataSrc = *pDataSrc; | |
| SLresult result; | |
| result = checkDataLocator(name, myDataSrc.pLocator, &pDataLocatorFormat->mLocator, | |
| allowedDataLocatorMask); | |
| if (SL_RESULT_SUCCESS != result) { | |
| return result; | |
| } | |
| switch (pDataLocatorFormat->mLocator.mLocatorType) { | |
| case SL_DATALOCATOR_URI: | |
| allowedDataFormatMask &= DATAFORMAT_MASK_MIME; | |
| break; | |
| case SL_DATALOCATOR_ADDRESS: | |
| case SL_DATALOCATOR_BUFFERQUEUE: | |
| allowedDataFormatMask &= DATAFORMAT_MASK_PCM | DATAFORMAT_MASK_PCM_EX; | |
| break; | |
| // Per the spec, the pFormat field is ignored in some cases | |
| case SL_DATALOCATOR_IODEVICE: | |
| myDataSrc.pFormat = NULL; | |
| FALLTHROUGH_INTENDED; | |
| case SL_DATALOCATOR_NULL: | |
| case SL_DATALOCATOR_MIDIBUFFERQUEUE: | |
| allowedDataFormatMask &= DATAFORMAT_MASK_NULL; | |
| break; | |
| case SL_DATALOCATOR_OUTPUTMIX: | |
| case XA_DATALOCATOR_NATIVEDISPLAY: | |
| allowedDataFormatMask = DATAFORMAT_MASK_NONE; | |
| break; | |
| #ifdef ANDROID | |
| case SL_DATALOCATOR_ANDROIDFD: | |
| allowedDataFormatMask &= DATAFORMAT_MASK_MIME; | |
| break; | |
| case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE: | |
| allowedDataFormatMask &= DATAFORMAT_MASK_PCM | DATAFORMAT_MASK_PCM_EX; | |
| break; | |
| case SL_DATALOCATOR_ANDROIDBUFFERQUEUE: | |
| allowedDataFormatMask &= DATAFORMAT_MASK_MIME;; | |
| break; | |
| #endif | |
| default: | |
| // invalid data locator type is caught earlier | |
| assert(false); | |
| allowedDataFormatMask = DATAFORMAT_MASK_NONE; | |
| break; | |
| } | |
| result = checkDataFormat(name, | |
| myDataSrc.pFormat, | |
| &pDataLocatorFormat->mFormat, | |
| allowedDataFormatMask, | |
| SL_BOOLEAN_TRUE /*isOutputFormat*/); | |
| if (SL_RESULT_SUCCESS != result) { | |
| freeDataLocator(&pDataLocatorFormat->mLocator); | |
| return result; | |
| } | |
| return SL_RESULT_SUCCESS; | |
| } | |
| /** \brief Check a data sink and make local deep copy */ | |
| SLresult checkDataSink(const char *name, const SLDataSink *pDataSink, | |
| DataLocatorFormat *pDataLocatorFormat, SLuint32 allowedDataLocatorMask, | |
| SLuint32 allowedDataFormatMask) | |
| { | |
| assert(NULL != name && NULL != pDataLocatorFormat); | |
| pDataLocatorFormat->u.mSink.pLocator = &pDataLocatorFormat->mLocator; | |
| pDataLocatorFormat->u.mSink.pFormat = &pDataLocatorFormat->mFormat; | |
| if (NULL == pDataSink) { | |
| pDataLocatorFormat->mLocator.mLocatorType = SL_DATALOCATOR_NULL; | |
| pDataLocatorFormat->mFormat.mFormatType = SL_DATAFORMAT_NULL; | |
| if ((allowedDataLocatorMask & DATALOCATOR_MASK_NULL) && | |
| (allowedDataFormatMask & DATAFORMAT_MASK_NULL)) { | |
| return SL_RESULT_SUCCESS; | |
| } | |
| SL_LOGE("%s: data sink cannot be NULL", name); | |
| return SL_RESULT_PARAMETER_INVALID; | |
| } | |
| SLDataSink myDataSink = *pDataSink; | |
| SLresult result; | |
| result = checkDataLocator(name, myDataSink.pLocator, &pDataLocatorFormat->mLocator, | |
| allowedDataLocatorMask); | |
| if (SL_RESULT_SUCCESS != result) { | |
| return result; | |
| } | |
| switch (pDataLocatorFormat->mLocator.mLocatorType) { | |
| case SL_DATALOCATOR_URI: | |
| allowedDataFormatMask &= DATAFORMAT_MASK_MIME; | |
| break; | |
| case SL_DATALOCATOR_ADDRESS: | |
| case SL_DATALOCATOR_BUFFERQUEUE: | |
| allowedDataFormatMask &= DATAFORMAT_MASK_PCM | DATAFORMAT_MASK_PCM_EX; | |
| break; | |
| // Per the spec, the pFormat field is ignored in some cases | |
| case SL_DATALOCATOR_IODEVICE: | |
| case SL_DATALOCATOR_OUTPUTMIX: | |
| case XA_DATALOCATOR_NATIVEDISPLAY: | |
| myDataSink.pFormat = NULL; | |
| FALLTHROUGH_INTENDED; | |
| case SL_DATALOCATOR_NULL: | |
| case SL_DATALOCATOR_MIDIBUFFERQUEUE: | |
| allowedDataFormatMask &= DATAFORMAT_MASK_NULL; | |
| break; | |
| #ifdef ANDROID | |
| case SL_DATALOCATOR_ANDROIDFD: | |
| allowedDataFormatMask = DATAFORMAT_MASK_NONE; | |
| break; | |
| case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE: | |
| allowedDataFormatMask &= DATAFORMAT_MASK_PCM | DATAFORMAT_MASK_PCM_EX; | |
| break; | |
| case SL_DATALOCATOR_ANDROIDBUFFERQUEUE: | |
| allowedDataFormatMask = DATAFORMAT_MASK_NONE; | |
| break; | |
| #endif | |
| default: | |
| // invalid data locator type is caught earlier | |
| assert(false); | |
| allowedDataFormatMask = DATAFORMAT_MASK_NONE; | |
| break; | |
| } | |
| result = checkDataFormat(name, | |
| myDataSink.pFormat, | |
| &pDataLocatorFormat->mFormat, | |
| allowedDataFormatMask, | |
| SL_BOOLEAN_FALSE /*isOutputFormat*/); | |
| if (SL_RESULT_SUCCESS != result) { | |
| freeDataLocator(&pDataLocatorFormat->mLocator); | |
| return result; | |
| } | |
| return SL_RESULT_SUCCESS; | |
| } | |
| /** \brief Free the local deep copy of a data locator format */ | |
| void freeDataLocatorFormat(DataLocatorFormat *dlf) | |
| { | |
| assert(NULL != dlf); | |
| freeDataLocator(&dlf->mLocator); | |
| freeDataFormat(&dlf->mFormat); | |
| } |