/* * 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" #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) { 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. case SL_DATAFORMAT_PCM: pDataFormat->mPCM = *(SLDataFormat_PCM *)pFormat; do { // check the channel count // FIXME FCC_8 Android and 8-channel positional assumptions here switch (pDataFormat->mPCM.numChannels) { case 1: // mono case 2: // stereo case 3: // stereo + front center case 4: // QUAD case 5: // QUAD + front center case 6: // 5.1 case 7: // 5.1 + back center case 8: // 7.1 break; case 0: // unknown result = SL_RESULT_PARAMETER_INVALID; break; default: // multi-channel result = SL_RESULT_CONTENT_UNSUPPORTED; break; } 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 // FIXME FCC_8 Android and 8-channel positional assumptions here switch (pDataFormat->mPCM.channelMask) { case SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT: if (2 != pDataFormat->mPCM.numChannels) { result = SL_RESULT_PARAMETER_INVALID; } break; case SL_SPEAKER_FRONT_LEFT: case SL_SPEAKER_FRONT_RIGHT: case SL_SPEAKER_FRONT_CENTER: if (1 != pDataFormat->mPCM.numChannels) { result = SL_RESULT_PARAMETER_INVALID; } break; #ifdef ANDROID case SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT | SL_SPEAKER_FRONT_CENTER: if (3 != pDataFormat->mPCM.numChannels) { result = SL_RESULT_PARAMETER_INVALID; } break; case SL_ANDROID_SPEAKER_QUAD: if (4 != pDataFormat->mPCM.numChannels) { result = SL_RESULT_PARAMETER_INVALID; } break; case SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER: if (5 != pDataFormat->mPCM.numChannels) { result = SL_RESULT_PARAMETER_INVALID; } break; case SL_ANDROID_SPEAKER_5DOT1: if (6 != pDataFormat->mPCM.numChannels) { result = SL_RESULT_PARAMETER_INVALID; } break; case SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_BACK_CENTER: if (7 != pDataFormat->mPCM.numChannels) { result = SL_RESULT_PARAMETER_INVALID; } break; case SL_ANDROID_SPEAKER_7DOT1: if (8 != pDataFormat->mPCM.numChannels) { result = SL_RESULT_PARAMETER_INVALID; } break; #endif case 0: { // According to OpenSL ES 1.0.1 section 9.1.7 SLDataFormat_PCM, "a default // setting of zero indicates stereo format (i.e. the setting is equivalent to // SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT)." // // ANDROID SPECIFIC BEHAVIOR. // We fill in the appropriate mask to the number indicated by numChannels. // The default of front left rather than center for mono may be non-intuitive, // but the left channel is the first channel for stereo or multichannel content. SLuint32 mask = channelCountToMask(pDataFormat->mPCM.numChannels); if (mask == UNKNOWN_CHANNELMASK) { result = SL_RESULT_PARAMETER_INVALID; } else { pDataFormat->mPCM.channelMask = mask; } } break; default: result = SL_RESULT_PARAMETER_INVALID; break; } if (SL_RESULT_SUCCESS != result) { SL_LOGE("%s: channelMask=0x%x numChannels=%u", name, pDataFormat->mPCM.channelMask, pDataFormat->mPCM.numChannels); break; } // 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; // fall through 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); 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; // fall through 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); 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); }