android/MediaPlayer_to_android.cpp \
android/OutputMix_to_android.cpp \
android/VideoCodec_to_android.cpp \
+ android/BufferQueueSource.cpp \
android/CallbackProtector.cpp \
+ android/AacBqToPcmCbRenderer.cpp \
android/android_AudioSfDecoder.cpp \
android/android_AudioToCbRenderer.cpp \
android/android_GenericMediaPlayer.cpp\
android/android_LocAVPlayer.cpp \
android/android_StreamPlayer.cpp \
android/android_Effect.cpp \
+ android/util/AacAdtsExtractor.cpp \
autogen/IID_to_MPH.c \
objects/C3DGroup.c \
objects/CAudioPlayer.c \
--- /dev/null
+/*
+ * Copyright (C) 2011 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.
+ */
+
+//#define USE_LOG SLAndroidLogLevel_Debug
+
+#include "sles_allinclusive.h"
+#include "android/include/AacBqToPcmCbRenderer.h"
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+// ADTS header size is 7, but frame size information ends on byte 6 (when counting from byte 1)
+#define ADTS_HEADER_SIZE_UP_TO_FRAMESIZE 6
+
+/**
+ * Returns the size of an AAC ADTS frame.
+ * Note that if the returned value + offset > size, it means that a partial frame starts at that
+ * offset, but this function will still return the size of the full frame.
+ * @param data pointer to the compressed audio data
+ * @param offset offset in bytes relative to data of where the frame is supposed to start
+ * @param size the size in bytes of the data block starting at data
+ * @return the size in bytes of the AAC ADTS frame starting at the given offset of the given
+ * memory address, 0 if the frame couldn't be parsed.
+ */
+static size_t getAdtsFrameSize(const uint8_t *data, off64_t offset, size_t size) {
+ size_t frameSize = 0;
+
+ if (!(offset + ADTS_HEADER_SIZE_UP_TO_FRAMESIZE < size)) {
+ SL_LOGE("AacBqToPcmCbRenderer::getAdtsFrameSize() returns 0 (can't read syncword or header)"
+ );
+ return 0;
+ }
+
+ const uint8_t *syncword = data + offset;
+ if ((syncword[0] != 0xff) || ((syncword[1] & 0xf6) != 0xf0)) {
+ SL_LOGE("AacBqToPcmCbRenderer::getAdtsFrameSize() returns 0 (wrong syncword)");
+ return 0;
+ }
+
+ const uint8_t protectionAbsent = data[offset+1] & 0x1;
+
+ const uint8_t* header = data + offset + 3;
+ frameSize = (header[0] & 0x3) << 11 | header[1] << 3 | header[2] >> 5;
+ // the frame size read already contains the size of the header, so no need to add it here
+ frameSize += protectionAbsent ? 0 : 2;
+
+ SL_LOGV("AacBqToPcmCbRenderer::getAdtsFrameSize() returns %u", frameSize);
+
+ return frameSize;
+}
+
+/**
+ * Returns whether a block of memory starts and ends on AAC ADTS frame boundaries
+ * @param data pointer to the compressed audio data
+ * @param size the size in bytes of the data block to validate
+ * @return true if there is AAC ADTS data, and it starts and ends on frame boundaries,
+ * false otherwise
+ */
+bool AacBqToPcmCbRenderer::validateBufferStartEndOnFrameBoundaries(void* data, size_t size)
+{
+ off64_t offset = 0;
+ size_t frameSize = 0;
+
+ if ((NULL == data) || (size == 0)) {
+ SL_LOGE("No ADTS to validate");
+ return false;
+ }
+
+ while (offset < size) {
+ if ((frameSize = getAdtsFrameSize((uint8_t *)data, offset, size)) == 0) {
+ SL_LOGE("found ADTS frame of size 0 at offset %llu", offset);
+ return false;
+ }
+ //SL_LOGV("last good offset %llu", offset);
+ offset += frameSize;
+ if (offset > size) {
+ SL_LOGE("found incomplete ADTS frame at end of data");
+ return false;
+ }
+ }
+ if (offset != size) { SL_LOGE("ADTS parsing error: reached end of incomplete frame"); }
+ assert(offset == size);
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------------
+AacBqToPcmCbRenderer::AacBqToPcmCbRenderer(AudioPlayback_Parameters* params) :
+ AudioToCbRenderer(params),
+ mBqSource(0)
+{
+ SL_LOGD("AacBqToPcmCbRenderer::AacBqToPcmCbRenderer()");
+
+}
+
+
+AacBqToPcmCbRenderer::~AacBqToPcmCbRenderer() {
+ SL_LOGD("AacBqToPcmCbRenderer::~AacBqToPcmCbRenderer()");
+
+}
+
+
+//--------------------------------------------------
+void AacBqToPcmCbRenderer::registerSourceQueueCallback(
+ const void* user, void *context, const void *caller) {
+ SL_LOGD("AacBqToPcmCbRenderer::registerQueueCallback");
+
+ Mutex::Autolock _l(mBqSourceLock);
+
+ mBqSource = new BufferQueueSource(user, context, caller);
+
+ CHECK(mBqSource != 0);
+ SL_LOGD("AacBqToPcmCbRenderer::registerSourceQueueCallback end");
+}
+
+
+//--------------------------------------------------
+// Event handlers
+void AacBqToPcmCbRenderer::onPrepare() {
+ SL_LOGD("AacBqToPcmCbRenderer::onPrepare()");
+ Mutex::Autolock _l(mBufferSourceLock);
+
+ // Initialize the PCM format info with the known parameters before the start of the decode
+ {
+ android::Mutex::Autolock autoLock(mPcmFormatLock);
+ mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_BITSPERSAMPLE] = SL_PCMSAMPLEFORMAT_FIXED_16;
+ mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_CONTAINERSIZE] = 16;
+ //FIXME not true on all platforms
+ mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_ENDIANNESS] = SL_BYTEORDER_LITTLEENDIAN;
+ mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_CHANNELMASK] = 0;
+ // initialization with the default values: they will be replaced by the actual values
+ // once the decoder has figured them out
+ mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_NUMCHANNELS] = mChannelCount;
+ mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_SAMPLESPERSEC] = mSampleRateHz;
+ }
+
+ sp<DataSource> dataSource;
+ {
+ Mutex::Autolock _l(mBqSourceLock);
+ dataSource = mBqSource;
+ }
+ if (dataSource == 0) {
+ SL_LOGE("AacBqToPcmCbRenderer::onPrepare(): Error no data source");
+ notifyPrepared(MEDIA_ERROR_BASE);
+ return;
+ }
+
+ sp<MediaExtractor> extractor = new AacAdtsExtractor(dataSource);
+ if (extractor == 0) {
+ SL_LOGE("AacBqToPcmCbRenderer::onPrepare: Could not instantiate AAC extractor.");
+ notifyPrepared(ERROR_UNSUPPORTED);
+ return;
+ }
+
+ // only decoding a single track of data
+ const size_t kTrackToDecode = 0;
+
+ sp<MediaSource> source = extractor->getTrack(kTrackToDecode);
+ if (source == 0) {
+ SL_LOGE("AacBqToPcmCbRenderer::onPrepare: error getting source from extractor");
+ notifyPrepared(ERROR_UNSUPPORTED);
+ return;
+ }
+ sp<MetaData> meta = extractor->getTrackMetaData(kTrackToDecode);
+
+ // the audio content is not raw PCM, so we need a decoder
+ OMXClient client;
+ CHECK_EQ(client.connect(), (status_t)OK);
+
+ source = OMXCodec::Create(
+ client.interface(), meta, false /* createEncoder */,
+ source);
+
+ if (source == NULL) {
+ SL_LOGE("AudioSfDecoder::onPrepare: Could not instantiate decoder.");
+ notifyPrepared(ERROR_UNSUPPORTED);
+ return;
+ }
+
+ meta = source->getFormat();
+
+ SL_LOGD("AacBqToPcmCbRenderer::onPrepare() after instantiating decoder");
+
+ if (source->start() != OK) {
+ SL_LOGE("AacBqToPcmCbRenderer::onPrepare() Failed to start source/decoder.");
+ notifyPrepared(MEDIA_ERROR_BASE);
+ return;
+ }
+
+ //---------------------------------
+ CHECK(meta->findInt32(kKeyChannelCount, &mChannelCount));
+ int32_t sr;
+ CHECK(meta->findInt32(kKeySampleRate, &sr));
+ mSampleRateHz = (uint32_t) sr;
+ {
+ android::Mutex::Autolock autoLock(mPcmFormatLock);
+ mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_SAMPLESPERSEC] = mSampleRateHz;
+ mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_NUMCHANNELS] = mChannelCount;
+ }
+ SL_LOGV("AacBqToPcmCbRenderer::onPrepare() channel count=%d SR=%d",
+ mChannelCount, mSampleRateHz);
+
+ //---------------------------------
+ // The data source, and audio source (a decoder) are ready to be used
+ mDataSource = dataSource;
+ mAudioSource = source;
+ mAudioSourceStarted = true;
+
+ //-------------------------------------
+ // signal successful completion of prepare
+ mStateFlags |= kFlagPrepared;
+
+ GenericPlayer::onPrepare();
+
+ SL_LOGD("AacBqToPcmCbRenderer::onPrepare() done, mStateFlags=0x%x", mStateFlags);
+}
+
+} // namespace android
#include "android/android_AudioToCbRenderer.h"
#include "android/android_StreamPlayer.h"
#include "android/android_LocAVPlayer.h"
+#include "android/include/AacBqToPcmCbRenderer.h"
#include <system/audio.h>
// SL_DATALOCATOR_URI / SL_DATALOCATOR_OUTPUTMIX
// SL_DATALOCATOR_ANDROIDFD / SL_DATALOCATOR_OUTPUTMIX
// SL_DATALOCATOR_ANDROIDBUFFERQUEUE / SL_DATALOCATOR_OUTPUTMIX
+ // SL_DATALOCATOR_ANDROIDBUFFERQUEUE / SL_DATALOCATOR_BUFFERQUEUE
// SL_DATALOCATOR_URI / SL_DATALOCATOR_BUFFERQUEUE
// SL_DATALOCATOR_ANDROIDFD / SL_DATALOCATOR_BUFFERQUEUE
// SL_DATALOCATOR_URI / SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
type = AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE;
break;
+ // AAC ADTS Android buffer queue decoded to PCM in a buffer queue
+ case SL_DATALOCATOR_ANDROIDBUFFERQUEUE:
+ type = AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE;
+ break;
+
default:
SL_LOGE("Source data locator 0x%x not supported with SL_DATALOCATOR_BUFFERQUEUE sink",
(unsigned)sourceLocatorType);
case SL_DATAFORMAT_MIME:
{
SLDataFormat_MIME *df_mime = (SLDataFormat_MIME *) pAudioSrc->pFormat;
- if (SL_CONTAINERTYPE_MPEG_TS != df_mime->containerType) {
+ if (NULL == df_mime) {
+ SL_LOGE("MIME type null invalid");
+ return SL_RESULT_CONTENT_UNSUPPORTED;
+ }
+ SL_LOGD("source MIME is %s", (char*)df_mime->mimeType);
+ switch(df_mime->containerType) {
+ case SL_CONTAINERTYPE_MPEG_TS:
+ if (strcasecmp((char*)df_mime->mimeType, ANDROID_MIME_MP2TS)) {
+ SL_LOGE("Invalid MIME (%s) for container SL_CONTAINERTYPE_MPEG_TS, expects %s",
+ (char*)df_mime->mimeType, ANDROID_MIME_MP2TS);
+ return SL_RESULT_CONTENT_UNSUPPORTED;
+ }
+ break;
+ case SL_CONTAINERTYPE_RAW:
+ case SL_CONTAINERTYPE_AAC:
+ if (strcasecmp((char*)df_mime->mimeType, ANDROID_MIME_AACADTS) &&
+ strcasecmp((char*)df_mime->mimeType,
+ ANDROID_MIME_AACADTS_ANDROID_FRAMEWORK)) {
+ SL_LOGE("Invalid MIME (%s) for container type %d, expects %s",
+ (char*)df_mime->mimeType, df_mime->containerType,
+ ANDROID_MIME_AACADTS);
+ return SL_RESULT_CONTENT_UNSUPPORTED;
+ }
+ break;
+ default:
SL_LOGE("Cannot create player with SL_DATALOCATOR_ANDROIDBUFFERQUEUE data source "
- "that is not fed MPEG-2 TS data");
+ "that is not fed MPEG-2 TS data or AAC ADTS data");
return SL_RESULT_CONTENT_UNSUPPORTED;
}
}
android::AudioToCbRenderer* decoder = new android::AudioToCbRenderer(&app);
pAudioPlayer->mAPlayer = decoder;
+ // configures the callback for the sink buffer queue
decoder->setDataPushListener(adecoder_writeToBufferQueue, (void*)pAudioPlayer);
+ // configures the callback for the notifications coming from the SF code
decoder->init(sfplayer_handlePrefetchEvent, (void*)pAudioPlayer);
switch (pAudioPlayer->mDataSource.mLocator.mLocatorType) {
}
break;
//-----------------------------------
+ // AacBqToPcmCbRenderer
+ case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE: {
+ object_lock_exclusive(&pAudioPlayer->mObject);
+
+ AudioPlayback_Parameters app;
+ app.sessionId = pAudioPlayer->mSessionId;
+ app.streamType = pAudioPlayer->mStreamType;
+ app.trackcb = NULL;
+ app.trackcbUser = NULL;
+ android::AacBqToPcmCbRenderer* bqtobq = new android::AacBqToPcmCbRenderer(&app);
+ // configures the callback for the sink buffer queue
+ bqtobq->setDataPushListener(adecoder_writeToBufferQueue, (void*)pAudioPlayer);
+ pAudioPlayer->mAPlayer = bqtobq;
+ // configures the callback for the notifications coming from the SF code,
+ // but also implicitly configures the AndroidBufferQueue from which ADTS data is read
+ pAudioPlayer->mAPlayer->init(sfplayer_handlePrefetchEvent, (void*)pAudioPlayer);
+
+ object_unlock_exclusive(&pAudioPlayer->mObject);
+ }
+ break;
+ //-----------------------------------
default:
SL_LOGE(ERROR_PLAYERREALIZE_UNEXPECTED_OBJECT_TYPE_D, pAudioPlayer->mAndroidObjType);
result = SL_RESULT_INTERNAL_ERROR;
case AUDIOPLAYER_FROM_URIFD: // intended fall-through
case AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: // intended fall-through
- case AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE:
+ case AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE: // intended fall-through
+ case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE:
pAudioPlayer->mAPlayer.clear();
break;
//-----------------------------------
case AUDIOPLAYER_FROM_URIFD: // intended fall-through
case AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: // intended fall-through
- case AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE:
+ case AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE: // intended fall-through
+ case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE:
// FIXME report and use the return code to the lock mechanism, which is where play state
// changes are updated (see object_unlock_exclusive_attributes())
aplayer_setPlayState(ap->mAPlayer, playState, &(ap->mAndroidObjState));
//-----------------------------------------------------------------------------
-void android_audioPlayer_androidBufferQueue_registerCallback_l(CAudioPlayer *ap) {
- if ((ap->mAndroidObjType == AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE) && (ap->mAPlayer != 0)) {
- android::StreamPlayer* splr = static_cast<android::StreamPlayer*>(ap->mAPlayer.get());
- splr->registerQueueCallback(
- (const void*)ap, true /*userIsAudioPlayer*/,
- ap->mAndroidBufferQueue.mContext,
- (const void*)&(ap->mAndroidBufferQueue.mItf));
+SLresult android_audioPlayer_androidBufferQueue_registerCallback_l(CAudioPlayer *ap) {
+ SLresult result = SL_RESULT_SUCCESS;
+ assert(ap->mAPlayer != 0);
+ switch (ap->mAndroidObjType) {
+ case AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: {
+ android::StreamPlayer* splr = static_cast<android::StreamPlayer*>(ap->mAPlayer.get());
+ splr->registerQueueCallback(
+ (const void*)ap /*user*/, true /*userIsAudioPlayer*/,
+ ap->mAndroidBufferQueue.mContext /*context*/,
+ (const void*)&(ap->mAndroidBufferQueue.mItf) /*caller*/);
+ } break;
+ case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE: {
+ android::AacBqToPcmCbRenderer* dec =
+ static_cast<android::AacBqToPcmCbRenderer*>(ap->mAPlayer.get());
+ dec->registerSourceQueueCallback((const void*)ap /*user*/,
+ ap->mAndroidBufferQueue.mContext /*context*/,
+ (const void*)&(ap->mAndroidBufferQueue.mItf) /*caller*/);
+ } break;
+ default:
+ SL_LOGE("Error registering AndroidBufferQueue callback: unexpected object type %d",
+ ap->mAndroidObjType);
+ result = SL_RESULT_INTERNAL_ERROR;
+ break;
}
+ return result;
}
//-----------------------------------------------------------------------------
* Android Buffer Queue
****************************/
/* must be called with a lock on pAudioPlayer->mThis */
-extern void android_audioPlayer_androidBufferQueue_registerCallback_l(CAudioPlayer *pAudioPlayer);
+extern SLresult android_audioPlayer_androidBufferQueue_registerCallback_l(
+ CAudioPlayer *pAudioPlayer);
/* must be called with a lock on pAudioPlayer->mThis */
extern void android_audioPlayer_androidBufferQueue_clear_l(CAudioPlayer *pAudioPlayer);
/* must be called with a lock on pAudioPlayer->mThis */
--- /dev/null
+/*
+ * Copyright (C) 2011 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.
+ */
+
+//#define USE_LOG SLAndroidLogLevel_Verbose
+
+#include "sles_allinclusive.h"
+#include "android/BufferQueueSource.h"
+
+#include <media/stagefright/MediaDebug.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+namespace android {
+
+
+const SLuint32 BufferQueueSource::kItemProcessed[NB_BUFFEREVENT_ITEM_FIELDS] = {
+ SL_ANDROID_ITEMKEY_BUFFERQUEUEEVENT, // item key
+ sizeof(SLuint32), // item size
+ SL_ANDROIDBUFFERQUEUEEVENT_PROCESSED // item data
+};
+
+
+BufferQueueSource::BufferQueueSource(const void* user, void *context, const void *caller) :
+ mAndroidBufferQueueSource(NULL),
+ mStreamToBqOffset(0)
+{
+ if (NULL != user) {
+ mAndroidBufferQueueSource = &((CAudioPlayer*)user)->mAndroidBufferQueue;
+ } else {
+ SL_LOGE("Can't create BufferQueueSource with NULL user");
+ }
+
+}
+
+
+BufferQueueSource::~BufferQueueSource() {
+ SL_LOGD("BufferQueueSource::~BufferQueueSource");
+}
+
+
+//--------------------------------------------------------------------------
+status_t BufferQueueSource::initCheck() const {
+ return mAndroidBufferQueueSource != NULL ? OK : NO_INIT;
+}
+
+ssize_t BufferQueueSource::readAt(off64_t offset, void *data, size_t size) {
+ SL_LOGD("BufferQueueSource::readAt(offset=%lld, data=%p, size=%d)", offset, data, size);
+
+ ssize_t readSize = 0;
+ slAndroidBufferQueueCallback callback = NULL;
+ void* pBufferContext, *pBufferData, *callbackPContext = NULL;
+ AdvancedBufferHeader *oldFront = NULL;
+ uint32_t dataSize, dataUsed = 0;
+
+ interface_lock_exclusive(mAndroidBufferQueueSource);
+
+ if (mAndroidBufferQueueSource->mState.count == 0) {
+ readSize = 0;
+ } else {
+ assert(mAndroidBufferQueueSource->mFront != mAndroidBufferQueueSource->mRear);
+
+ oldFront = mAndroidBufferQueueSource->mFront;
+ AdvancedBufferHeader *newFront = &oldFront[1];
+
+ // consume events when starting to read data from a buffer for the first time
+ if (oldFront->mDataSizeConsumed == 0) {
+ if (oldFront->mItems.mAdtsCmdData.mAdtsCmdCode & ANDROID_ADTSEVENT_EOS) {
+ // FIXME handle EOS
+ SL_LOGE("---> FIXME: handle ANDROID_ADTSEVENT_EOS <---");
+ }
+ oldFront->mItems.mAdtsCmdData.mAdtsCmdCode = ANDROID_ADTSEVENT_NONE;
+ }
+
+ // where to read from
+ char *pSrc = NULL;
+ // can this read operation cause us to call the buffer queue callback
+ bool queueCallbackCandidate = false;
+
+ //assert(mStreamToBqOffset <= offset);
+ CHECK(mStreamToBqOffset <= offset);
+
+ if (offset + size <= mStreamToBqOffset + oldFront->mDataSize) {
+ pSrc = ((char*)oldFront->mDataBuffer) + (offset - mStreamToBqOffset);
+
+ if (offset - mStreamToBqOffset + size == oldFront->mDataSize) {
+ // consumed buffer entirely
+ oldFront->mDataSizeConsumed = oldFront->mDataSize;
+ mStreamToBqOffset += oldFront->mDataSize;
+ queueCallbackCandidate = true;
+
+ // move queue to next buffer
+ if (newFront == &mAndroidBufferQueueSource->
+ mBufferArray[mAndroidBufferQueueSource->mNumBuffers + 1]) {
+ // reached the end, circle back
+ newFront = mAndroidBufferQueueSource->mBufferArray;
+ }
+ mAndroidBufferQueueSource->mFront = newFront;
+ // update the queue state
+ mAndroidBufferQueueSource->mState.count--;
+ mAndroidBufferQueueSource->mState.index++;
+ SL_LOGV("BufferQueueSource moving to next buffer");
+ }
+ }
+
+ // consume data: copy to given destination
+ if (NULL != pSrc) {
+ memcpy(data, pSrc, size);
+ readSize = size;
+ } else {
+ readSize = 0;
+ }
+
+ if (queueCallbackCandidate) {
+ // data has been consumed, and the buffer queue state has been updated
+ // we will notify the client if applicable
+ if (mAndroidBufferQueueSource->mCallbackEventsMask &
+ SL_ANDROIDBUFFERQUEUEEVENT_PROCESSED) {
+ callback = mAndroidBufferQueueSource->mCallback;
+ // save callback data while under lock
+ callbackPContext = mAndroidBufferQueueSource->mContext;
+ pBufferContext = (void *)oldFront->mBufferContext;
+ pBufferData = (void *)oldFront->mDataBuffer;
+ dataSize = oldFront->mDataSize;
+ dataUsed = oldFront->mDataSizeConsumed;
+ }
+ }
+ }
+
+ interface_unlock_exclusive(mAndroidBufferQueueSource);
+
+ // notify client
+ if (NULL != callback) {
+ (*callback)(&mAndroidBufferQueueSource->mItf, callbackPContext,
+ pBufferContext, pBufferData, dataSize, dataUsed,
+ // no messages during playback other than marking the buffer as processed
+ (const SLAndroidBufferItem*)(&kItemProcessed) /* pItems */,
+ NB_BUFFEREVENT_ITEM_FIELDS * sizeof(SLuint32) /* itemsLength */ );
+ }
+
+ return readSize;
+}
+
+
+status_t BufferQueueSource::getSize(off64_t *size) {
+ SL_LOGD("BufferQueueSource::getSize()");
+ // we're streaming, we don't know how much there is
+ *size = 0;
+ return OK;
+}
+
+} // namespace android
--- /dev/null
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef BUFFERQUEUE_SOURCE_H_
+#define BUFFERQUEUE_SOURCE_H_
+
+#include <stdio.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/threads.h>
+#include <drm/DrmManagerClient.h>
+#include "include/AacAdtsExtractor.h"
+
+// number of SLuint32 fields to store a buffer event message in an item, by mapping each
+// to the item key (SLuint32), the item size (SLuint32), and the item data (mask on SLuint32)
+#define NB_BUFFEREVENT_ITEM_FIELDS 3
+
+namespace android {
+
+// a Stagefright DataSource that pulls data from an AndroidBufferQueue
+
+class BufferQueueSource : public DataSource {
+public:
+
+ // store an item structure to indicate a processed buffer
+ static const SLuint32 kItemProcessed[NB_BUFFEREVENT_ITEM_FIELDS];
+
+ BufferQueueSource(const void* user, void *context, const void *caller);
+
+ virtual status_t initCheck() const;
+
+ virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+
+ virtual status_t getSize(off64_t *size);
+
+ virtual ~BufferQueueSource();
+
+private:
+ // the Android Buffer Queue from which data is consumed
+ IAndroidBufferQueue* mAndroidBufferQueueSource;
+
+ // a monotonically increasing offset used to translate an offset from the beginning
+ // of the stream, to an offset in each buffer from the buffer queue source
+ off64_t mStreamToBqOffset;
+
+ BufferQueueSource(const BufferQueueSource &);
+ BufferQueueSource &operator=(const BufferQueueSource &);
+};
+
+} // namespace android
+
+#endif // BUFFERQUEUE_SOURCE_H_
+
* limitations under the License.
*/
-//#define USE_LOG SLAndroidLogLevel_Debug
+//#define USE_LOG SLAndroidLogLevel_Verbose
#include "sles_allinclusive.h"
#include "android/android_AudioSfDecoder.h"
namespace android {
-// keep in sync with the entries of kPcmDecodeMetadataKeys[] defined in android_AudioSfDecoder.h
-#define ANDROID_KEY_INDEX_PCMFORMAT_NUMCHANNELS 0
-#define ANDROID_KEY_INDEX_PCMFORMAT_SAMPLESPERSEC 1
-#define ANDROID_KEY_INDEX_PCMFORMAT_BITSPERSAMPLE 2
-#define ANDROID_KEY_INDEX_PCMFORMAT_CONTAINERSIZE 3
-#define ANDROID_KEY_INDEX_PCMFORMAT_CHANNELMASK 4
-#define ANDROID_KEY_INDEX_PCMFORMAT_ENDIANNESS 5
-
//--------------------------------------------------------------------------------------------------
AudioSfDecoder::AudioSfDecoder(const AudioPlayback_Parameters* params) : GenericPlayer(params),
mDataSource(0),
// signal successful completion of prepare
mStateFlags |= kFlagPrepared;
+
GenericPlayer::onPrepare();
SL_LOGD("AudioSfDecoder::onPrepare() done, mStateFlags=0x%x", mStateFlags);
}
if ((mAudioSource != 0) && mAudioSourceStarted) {
sp<MetaData> meta = mAudioSource->getFormat();
- CHECK(meta->findInt32(kKeyChannelCount, &mChannelCount));
+ SL_LOGV("old sample rate = %d, channel count = %d", mSampleRateHz, mChannelCount);
- SL_LOGV("old sample rate = %d", mSampleRateHz);
+ CHECK(meta->findInt32(kKeyChannelCount, &mChannelCount));
int32_t sr;
CHECK(meta->findInt32(kKeySampleRate, &sr));
mSampleRateHz = (uint32_t) sr;
- SL_LOGV("found new sample rate = %d", mSampleRateHz);
+ SL_LOGV("format changed: new sample rate = %d, channel count = %d",
+ mSampleRateHz, mChannelCount);
{
android::Mutex::Autolock autoLock(mPcmFormatLock);
* limitations under the License.
*/
+#ifndef AUDIO_SF_DECODER_H_
+#define AUDIO_SF_DECODER_H_
+
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/FileSource.h>
//--------------------------------------------------------------------------------------------------
namespace android {
+// keep in sync with the entries of kPcmDecodeMetadataKeys[]
+#define ANDROID_KEY_INDEX_PCMFORMAT_NUMCHANNELS 0
+#define ANDROID_KEY_INDEX_PCMFORMAT_SAMPLESPERSEC 1
+#define ANDROID_KEY_INDEX_PCMFORMAT_BITSPERSAMPLE 2
+#define ANDROID_KEY_INDEX_PCMFORMAT_CONTAINERSIZE 3
+#define ANDROID_KEY_INDEX_PCMFORMAT_CHANNELMASK 4
+#define ANDROID_KEY_INDEX_PCMFORMAT_ENDIANNESS 5
+
// to keep in sync with the ANDROID_KEY_INDEX_PCMFORMAT_* constants in android_AudioSfDecoder.cpp
static const char* const kPcmDecodeMetadataKeys[] = {
ANDROID_KEY_PCMFORMAT_NUMCHANNELS, ANDROID_KEY_PCMFORMAT_SAMPLESPERSEC,
// mutex used to protect the decode buffer, the audio source and its running state
Mutex mBufferSourceLock;
-private:
-
void notifyPrepared(status_t prepareRes);
int64_t mSeekTimeMsec;
// protects mPcmFormatKeyCount and mPcmFormatValues
Mutex mPcmFormatLock;
+private:
bool wantPrefetch();
CacheStatus_t getCacheRemaining(bool *eos);
int64_t getPositionUsec(); // ANDROID_UNKNOWN_TIME if unknown
};
} // namespace android
+
+#endif // AUDIO_SF_DECODER_H_
* limitations under the License.
*/
+#ifndef AUDIO_TO_CB_RENDERER_H_
+#define AUDIO_TO_CB_RENDERER_H_
+
#include "android/android_AudioSfDecoder.h"
//--------------------------------------------------------------------------------------------------
};
} // namespace android
+
+#endif //AUDIO_TO_CB_RENDERER_H_
// audio recorder, recording from an input device data source, streamed into a
// PCM buffer queue data sink
AUDIORECORDER_FROM_MIC_TO_PCM_BUFFERQUEUE = 6,
+ // audio player, decoding from an Android buffer queue with ADTS data,
+ // to a buffer queue data sink in PCM format
+ AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE = 7,
NUM_AUDIOPLAYER_MAP_TYPES
};
// buffer marks a format change with previous TS data, resume display as soon as possible
#define ANDROID_MP2TSEVENT_FORMAT_CHANGE ((SLuint32) 0x1 << 3)
+/**
+ * Event mask for AAC ADTS events associated with ADTS data
+ */
+#define ANDROID_ADTSEVENT_NONE ANDROID_MP2TSEVENT_NONE
+// buffer is at End Of Stream
+#define ANDROID_ADTSEVENT_EOS ANDROID_MP2TSEVENT_EOS
/**
* Additional metadata keys
enum AndroidBufferType_type {
kAndroidBufferTypeInvalid = ((SLuint16) 0x0),
kAndroidBufferTypeMpeg2Ts = ((SLuint16) 0x1),
+ kAndroidBufferTypeAacadts = ((SLuint16) 0x2),
};
/**
+ * MIME types required for data in Android Buffer Queues
+ */
+#define ANDROID_MIME_MP2TS "video/mp2ts"
+// the MIME type used elsewhere in the Android framework for AAC ADTS
+#define ANDROID_MIME_AACADTS_ANDROID_FRAMEWORK "audio/aac-adts"
+// the MIME type applications should use for AAC ADTS
+#define ANDROID_MIME_AACADTS "audio/vnd.android.aac-adts"
+
+/**
* Notification thresholds relative to content duration in the cache
*/
#define DURATION_CACHED_HIGH_MS 30000 // 30s
--- /dev/null
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef AAC_ADTS_EXTRACTOR_H_
+#define AAC_ADTS_EXTRACTOR_H_
+
+#include <utils/Vector.h>
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/String8.h>
+
+namespace android {
+
+struct AMessage;
+class String8;
+
+
+class AacAdtsSource : public MediaSource {
+public:
+ AacAdtsSource(const sp<DataSource> &source,
+ const sp<MetaData> &meta,
+ //const Vector<uint64_t> &offset_vector,
+ int64_t frame_duration_us);
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+protected:
+ virtual ~AacAdtsSource();
+
+private:
+ static const size_t kMaxFrameSize;
+ sp<DataSource> mDataSource;
+ sp<MetaData> mMeta;
+
+ off64_t mOffset;
+ int64_t mCurrentTimeUs;
+ bool mStarted;
+ MediaBufferGroup *mGroup;
+
+ int64_t mFrameDurationUs;
+
+ AacAdtsSource(const AacAdtsSource &);
+ AacAdtsSource &operator=(const AacAdtsSource &);
+};
+
+
+class AacAdtsExtractor : public MediaExtractor {
+public:
+ AacAdtsExtractor(const sp<DataSource> &source);
+
+ virtual size_t countTracks();
+ virtual sp<MediaSource> getTrack(size_t index);
+ virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+
+ virtual sp<MetaData> getMetaData();
+
+protected:
+ virtual ~AacAdtsExtractor();
+
+private:
+ sp<DataSource> mDataSource;
+ sp<MetaData> mMeta;
+ status_t mInitCheck;
+
+ int64_t mFrameDurationUs;
+
+ AacAdtsExtractor(const AacAdtsExtractor &);
+ AacAdtsExtractor &operator=(const AacAdtsExtractor &);
+
+};
+
+} // namespace android
+
+#endif // AAC_ADTS_EXTRACTOR_H_
--- /dev/null
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef AAC_BQ_TO_PCM_H_
+#define AAC_BQ_TO_PCM_H_
+
+#include "android/android_AudioToCbRenderer.h"
+#include "android/BufferQueueSource.h"
+#include "android/include/AacAdtsExtractor.h"
+
+//--------------------------------------------------------------------------------------------------
+namespace android {
+
+// Class to receive AAC ADTS data through an Android Buffer Queue, decode it and output
+// the PCM samples through a registered callback (just like its superclass, AudioToCbRenderer).
+// The implementation mainly overrides AudioSfDecoder::onPrepare() for the specificities
+// of the data source creation, but all other behavior remains the same (e.g. PCM format metadata)
+
+class AacBqToPcmCbRenderer : public AudioToCbRenderer
+{
+public:
+
+ AacBqToPcmCbRenderer(AudioPlayback_Parameters* params);
+ virtual ~AacBqToPcmCbRenderer();
+
+ void registerSourceQueueCallback(const void* user, void *context, const void *caller);
+
+ // verifies the given memory starts and ends on ADTS frame boundaries.
+ // This is for instance used whenever ADTS data is being enqueued through an
+ // SL / XA AndroidBufferQueue interface so only parseable ADTS data goes in
+ // the buffer queue, and no ADTS frame is stored across two buffers.
+ static bool validateBufferStartEndOnFrameBoundaries(void* data, size_t size);
+
+protected:
+
+ // Async event handlers (called from GenericPlayer's event loop)
+ virtual void onPrepare();
+
+
+private:
+ // mutex used to protect mBqSource
+ Mutex mBqSourceLock;
+ sp<BufferQueueSource> mBqSource;
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(AacBqToPcmCbRenderer);
+
+};
+
+} // namespace android
+
+#endif //AAC_BQ_TO_PCM_H_
--- /dev/null
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#include "sllog.h"
+#include <utils/Log.h>
+
+#include "android/include/AacAdtsExtractor.h"
+#include "include/avc_utils.h"
+
+
+namespace android {
+
+#define ADTS_HEADER_LENGTH 7
+// ADTS header size is 7, but frame size information ends on byte 6 (when counting from byte 1)
+#define ADTS_HEADER_SIZE_UP_TO_FRAMESIZE 6
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Returns the sample rate based on the sampling frequency index
+static uint32_t get_sample_rate(const uint8_t sf_index)
+{
+ static const uint32_t sample_rates[] =
+ {
+ 96000, 88200, 64000, 48000, 44100, 32000,
+ 24000, 22050, 16000, 12000, 11025, 8000
+ };
+
+ if (sf_index < sizeof(sample_rates) / sizeof(sample_rates[0])) {
+ return sample_rates[sf_index];
+ }
+
+ return 0;
+}
+
+static size_t getFrameSize(const sp<DataSource> &source, off64_t offset) {
+ size_t frameSize = 0;
+
+ uint8_t syncHeader[ADTS_HEADER_SIZE_UP_TO_FRAMESIZE];
+ const uint8_t *syncword = syncHeader;
+ const uint8_t *header = syncHeader + 3;
+
+ if (source->readAt(offset, &syncHeader, ADTS_HEADER_SIZE_UP_TO_FRAMESIZE)
+ != ADTS_HEADER_SIZE_UP_TO_FRAMESIZE) {
+ SL_LOGE("AacAdtsExtractor:: getFrameSize() returns 0 (syncword and header read error)");
+ return 0;
+ }
+
+ if ((syncword[0] != 0xff) || ((syncword[1] & 0xf6) != 0xf0)) {
+ SL_LOGE("AacAdtsExtractor:: getFrameSize() returns 0 (syncword pb)");
+ return 0;
+ }
+
+ const uint8_t protectionAbsent = syncword[1] & 0x1;
+
+ frameSize = (header[0] & 0x3) << 11 | header[1] << 3 | header[2] >> 5;
+ // the frame size read already contains the size of the ADTS header, so no need to add it here
+ frameSize += protectionAbsent ? 0 : 2;
+
+ //SL_LOGV("AacAdtsExtractor:: getFrameSize() returns %u", frameSize);
+
+ return frameSize;
+}
+
+
+AacAdtsExtractor::AacAdtsExtractor(const sp<DataSource> &source)
+ : mDataSource(source),
+ mInitCheck(NO_INIT),
+ mFrameDurationUs(0) {
+
+ // difference with framework's AAC Extractor: we have already validated the data
+ // upon enqueueing, so no need to sniff the data:
+ // String8 mimeType;
+ // float confidence;
+ // if (!SniffAAC(mDataSource, &mimeType, &confidence, NULL)) {
+ // return;
+ // }
+
+ uint8_t profile, sf_index, channel, header[2];
+ if (mDataSource->readAt(2, &header, 2) < 2) {
+ return;
+ }
+
+ profile = (header[0] >> 6) & 0x3;
+ sf_index = (header[0] >> 2) & 0xf;
+ uint32_t sr = get_sample_rate(sf_index);
+
+ if (sr == 0) {
+ return;
+ }
+ channel = (header[0] & 0x1) << 2 | (header[1] >> 6);
+
+ SL_LOGV("AacAdtsExtractor has found sr=%d channel=%d", sr, channel);
+
+ mMeta = MakeAACCodecSpecificData(profile, sf_index, channel);
+
+ off64_t offset = 0;
+ off64_t streamSize, numFrames = 0;
+ size_t frameSize = 0;
+ int64_t duration = 0;
+
+ if (mDataSource->getSize(&streamSize) == OK) {
+ while (offset < streamSize) {
+ if ((frameSize = getFrameSize(mDataSource, offset)) == 0) {
+ //SL_LOGV("AacAdtsExtractor() querying framesize at offset=%lld", offset);
+ return;
+ }
+
+ offset += frameSize;
+ numFrames ++;
+ }
+
+ // Round up and get the duration
+ mFrameDurationUs = (1024 * 1000000ll + (sr - 1)) / sr;
+ duration = numFrames * mFrameDurationUs;
+ mMeta->setInt64(kKeyDuration, duration);
+ }
+
+ mInitCheck = OK;
+
+}
+
+
+AacAdtsExtractor::~AacAdtsExtractor() {
+}
+
+
+sp<MetaData> AacAdtsExtractor::getMetaData() {
+ sp<MetaData> meta = new MetaData;
+
+ if (mInitCheck != OK) {
+ return meta;
+ }
+
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC_ADTS);
+
+ return meta;
+}
+
+
+size_t AacAdtsExtractor::countTracks() {
+ return mInitCheck == OK ? 1 : 0;
+}
+
+
+sp<MediaSource> AacAdtsExtractor::getTrack(size_t index) {
+ if (mInitCheck != OK || index != 0) {
+ return NULL;
+ }
+
+ return new AacAdtsSource(mDataSource, mMeta, mFrameDurationUs);
+}
+
+
+sp<MetaData> AacAdtsExtractor::getTrackMetaData(size_t index, uint32_t flags) {
+ if (mInitCheck != OK || index != 0) {
+ return NULL;
+ }
+
+ return mMeta;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+// 8192 = 2^13, 13bit AAC frame size (in bytes)
+const size_t AacAdtsSource::kMaxFrameSize = 8192;
+
+AacAdtsSource::AacAdtsSource(
+ const sp<DataSource> &source, const sp<MetaData> &meta,
+ int64_t frame_duration_us)
+ : mDataSource(source),
+ mMeta(meta),
+ mOffset(0),
+ mCurrentTimeUs(0),
+ mStarted(false),
+ mGroup(NULL),
+ mFrameDurationUs(frame_duration_us) {
+}
+
+
+AacAdtsSource::~AacAdtsSource() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+
+status_t AacAdtsSource::start(MetaData *params) {
+ CHECK(!mStarted);
+
+ mOffset = 0;
+ mCurrentTimeUs = 0;
+ mGroup = new MediaBufferGroup;
+ mGroup->add_buffer(new MediaBuffer(kMaxFrameSize));
+ mStarted = true;
+
+ return OK;
+}
+
+
+status_t AacAdtsSource::stop() {
+ CHECK(mStarted);
+
+ delete mGroup;
+ mGroup = NULL;
+
+ mStarted = false;
+ return OK;
+}
+
+
+sp<MetaData> AacAdtsSource::getFormat() {
+ return mMeta;
+}
+
+
+status_t AacAdtsSource::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ *out = NULL;
+
+ int64_t seekTimeUs;
+ ReadOptions::SeekMode mode;
+ if (options && options->getSeekTo(&seekTimeUs, &mode)) {
+ // difference with framework's AAC Extractor: no seeking
+ SL_LOGE("Can't seek in AAC ADTS buffer queue");
+ }
+
+ size_t frameSize, frameSizeWithoutHeader;
+ SL_LOGV("AacAdtsSource::read() offset=%lld", mOffset);
+ if ((frameSize = getFrameSize(mDataSource, mOffset)) == 0) {
+ LOGE("AacAdtsSource::read() returns EOS");
+ return ERROR_END_OF_STREAM;
+ // FIXME if we return EOS here, verify we can restart decoding when we get new data
+ // with start()
+ //LOGE("AacAdtsSource::read() should return EOS, but returning OK");
+ //return OK;
+ }
+
+ MediaBuffer *buffer;
+ status_t err = mGroup->acquire_buffer(&buffer);
+ if (err != OK) {
+ return err;
+ }
+
+ frameSizeWithoutHeader = frameSize - ADTS_HEADER_LENGTH;
+ size_t readSize = mDataSource->readAt(mOffset + ADTS_HEADER_LENGTH, buffer->data(),
+ frameSizeWithoutHeader);
+ //SL_LOGV("AacAdtsSource::read() readAt returned %u bytes", readSize);
+ if (readSize != (ssize_t)frameSizeWithoutHeader) {
+ SL_LOGW("AacAdtsSource::read() readSize != frameSizeWithoutHeader");
+ buffer->release();
+ buffer = NULL;
+ return ERROR_IO;
+ }
+
+ buffer->set_range(0, frameSizeWithoutHeader);
+ buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs);
+ buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+
+ mOffset += frameSize;
+ mCurrentTimeUs += mFrameDurationUs;
+
+ *out = buffer;
+ return OK;
+}
+
+} // namespace android
#ifdef ANDROID
case SL_DATALOCATOR_ANDROIDBUFFERQUEUE:
+ // if the source is SLAndroidBufferQueueItf for AAC decode, the sink must be a buffer queue
+ switch (pSinkDataLocatorFormat->mLocator.mLocatorType) {
+ case SL_DATALOCATOR_BUFFERQUEUE:
+ case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
+ break;
+ default:
+ // FIXME more mph index business to worry about?
+ // FIXME does this break OpenMAX AL?
+ SL_LOGE("Source is SL_DATALOCATOR_ANDROIDBUFFERQUEUE, sink is not a buffer queue");
+ return SL_RESULT_FEATURE_UNSUPPORTED;
+ break;
+ }
+ break;
#endif
case SL_DATALOCATOR_ADDRESS:
case SL_DATALOCATOR_MIDIBUFFERQUEUE:
/* AndroidBufferQueue implementation */
-#include "sles_allinclusive.h"
+//#define USE_LOG SLAndroidLogLevel_Verbose
+#include "sles_allinclusive.h"
+// for AAC ADTS verification on enqueue:
+#include "android/include/AacBqToPcmCbRenderer.h"
/**
* Determine the state of the audio player or audio recorder associated with a buffer queue.
switch (InterfaceToObjectID(thiz)) {
case SL_OBJECTID_AUDIOPLAYER:
- result = SL_RESULT_SUCCESS;
- android_audioPlayer_androidBufferQueue_registerCallback_l((CAudioPlayer*) thiz->mThis);
+ result = android_audioPlayer_androidBufferQueue_registerCallback_l(
+ (CAudioPlayer*) thiz->mThis);
break;
case XA_OBJECTID_MEDIAPLAYER:
SL_LOGV("IAndroidBufferQueue_RegisterCallback()");
result = SL_RESULT_SUCCESS;
+ //FIXME return error code
android_Player_androidBufferQueue_registerCallback_l((CMediaPlayer*) thiz->mThis);
break;
default:
SLuint32 itemsLength)
{
SL_ENTER_INTERFACE
+ SL_LOGD("IAndroidBufferQueue_Enqueue pData=%p dataLength=%d", pData, dataLength);
if ( ((NULL == pData) || (0 == dataLength))
&& ((NULL == pItems) || (0 == itemsLength))) {
if (dataLength % MPEG2_TS_BLOCK_SIZE == 0) {
break;
}
- // intended fall-through if test failed
SL_LOGE("Error enqueueing MPEG-2 TS data: size must be a multiple of %d (block size)",
MPEG2_TS_BLOCK_SIZE);
+ result = SL_RESULT_PARAMETER_INVALID;
+ SL_LEAVE_INTERFACE
+ break;
+ case kAndroidBufferTypeAacadts:
+ // FIXME allow commands as for mp2ts
+ if (!android::AacBqToPcmCbRenderer::validateBufferStartEndOnFrameBoundaries(
+ pData, dataLength)) {
+ SL_LOGE("Error enqueueing ADTS data: data must start and end on frame boundaries");
+ result = SL_RESULT_PARAMETER_INVALID;
+ SL_LEAVE_INTERFACE
+ }
+ break;
case kAndroidBufferTypeInvalid:
default:
result = SL_RESULT_PARAMETER_INVALID;
#include "sles_allinclusive.h"
+
+/* Utility functions */
+
+static SLresult initializeBufferQueueMembers(CAudioPlayer *ap) {
+ // inline allocation of circular mArray, up to a typical max
+ if (BUFFER_HEADER_TYPICAL >= ap->mBufferQueue.mNumBuffers) {
+ ap->mBufferQueue.mArray = ap->mBufferQueue.mTypical;
+ } else {
+ // Avoid possible integer overflow during multiplication; this arbitrary
+ // maximum is big enough to not interfere with real applications, but
+ // small enough to not overflow.
+ if (ap->mBufferQueue.mNumBuffers >= 256) {
+ return SL_RESULT_MEMORY_FAILURE;
+ }
+ ap->mBufferQueue.mArray = (BufferHeader *)
+ malloc((ap->mBufferQueue.mNumBuffers + 1) * sizeof(BufferHeader));
+ if (NULL == ap->mBufferQueue.mArray) {
+ return SL_RESULT_MEMORY_FAILURE;
+ }
+ }
+ ap->mBufferQueue.mFront = ap->mBufferQueue.mArray;
+ ap->mBufferQueue.mRear = ap->mBufferQueue.mArray;
+ return SL_RESULT_SUCCESS;
+}
+
+#ifdef ANDROID
+static SLresult initializeAndroidBufferQueueMembers(CAudioPlayer *ap) {
+ // Avoid possible integer overflow during multiplication; this arbitrary
+ // maximum is big enough to not interfere with real applications, but
+ // small enough to not overflow.
+ if (ap->mAndroidBufferQueue.mNumBuffers >= 256) {
+ return SL_RESULT_MEMORY_FAILURE;
+ }
+ ap->mAndroidBufferQueue.mBufferArray = (AdvancedBufferHeader *)
+ malloc( (ap->mAndroidBufferQueue.mNumBuffers + 1) * sizeof(AdvancedBufferHeader));
+ if (NULL == ap->mAndroidBufferQueue.mBufferArray) {
+ return SL_RESULT_MEMORY_FAILURE;
+ } else {
+
+ // initialize ABQ buffer type
+ // assert below has been checked in android_audioPlayer_checkSourceSink
+ assert(SL_DATAFORMAT_MIME == ap->mDataSource.mFormat.mFormatType);
+ switch(ap->mDataSource.mFormat.mMIME.containerType) {
+ case SL_CONTAINERTYPE_MPEG_TS:
+ ap->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeMpeg2Ts;
+ break;
+ case SL_CONTAINERTYPE_AAC:
+ case SL_CONTAINERTYPE_RAW: {
+ const char* mime = (char*)ap->mDataSource.mFormat.mMIME.mimeType;
+ if ((mime != NULL) && !(strcasecmp(mime, ANDROID_MIME_AACADTS) &&
+ strcasecmp(mime, ANDROID_MIME_AACADTS_ANDROID_FRAMEWORK))) {
+ ap->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeAacadts;
+ } else {
+ ap->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeInvalid;
+ SL_LOGE("CreateAudioPlayer: Invalid buffer type in Android Buffer Queue");
+ return SL_RESULT_CONTENT_UNSUPPORTED;
+ }
+ } break;
+ default:
+ ap->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeInvalid;
+ SL_LOGE("CreateAudioPlayer: Invalid buffer type in Android Buffer Queue");
+ return SL_RESULT_CONTENT_UNSUPPORTED;
+ }
+
+ // initialize ABQ memory
+ for (SLuint16 i=0 ; i<(ap->mAndroidBufferQueue.mNumBuffers + 1) ; i++) {
+ AdvancedBufferHeader *pBuf = &ap->mAndroidBufferQueue.mBufferArray[i];
+ pBuf->mDataBuffer = NULL;
+ pBuf->mDataSize = 0;
+ pBuf->mDataSizeConsumed = 0;
+ pBuf->mBufferContext = NULL;
+ pBuf->mBufferState = SL_ANDROIDBUFFERQUEUEEVENT_NONE;
+ switch (ap->mAndroidBufferQueue.mBufferType) {
+ case kAndroidBufferTypeMpeg2Ts:
+ pBuf->mItems.mTsCmdData.mTsCmdCode = ANDROID_MP2TSEVENT_NONE;
+ pBuf->mItems.mTsCmdData.mPts = 0;
+ break;
+ case kAndroidBufferTypeAacadts:
+ pBuf->mItems.mTsCmdData.mTsCmdCode = ANDROID_ADTSEVENT_NONE;
+ break;
+ default:
+ return SL_RESULT_CONTENT_UNSUPPORTED;
+ }
+ }
+ ap->mAndroidBufferQueue.mFront = ap->mAndroidBufferQueue.mBufferArray;
+ ap->mAndroidBufferQueue.mRear = ap->mAndroidBufferQueue.mBufferArray;
+ }
+
+ return SL_RESULT_SUCCESS;
+}
+#endif
+
+
static SLresult IEngine_CreateLEDDevice(SLEngineItf self, SLObjectItf *pDevice, SLuint32 deviceID,
SLuint32 numInterfaces, const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
{
// we have already range-checked the value down to a smaller width
SLuint16 nbBuffers = 0;
bool usesAdvancedBufferHeaders = false;
+ bool usesSimpleBufferQueue = false;
+ // creating an AudioPlayer which decodes AAC ADTS buffers to a PCM buffer queue
+ // will cause usesAdvancedBufferHeaders and usesSimpleBufferQueue to be true
switch (thiz->mDataSource.mLocator.mLocatorType) {
case SL_DATALOCATOR_BUFFERQUEUE:
#ifdef ANDROID
case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
#endif
+ usesSimpleBufferQueue = true;
nbBuffers = (SLuint16) thiz->mDataSource.mLocator.mBufferQueue.numBuffers;
assert(SL_DATAFORMAT_PCM == thiz->mDataSource.mFormat.mFormatType);
thiz->mNumChannels = thiz->mDataSource.mFormat.mPCM.numChannels;
break;
#ifdef ANDROID
case SL_DATALOCATOR_ANDROIDBUFFERQUEUE:
- nbBuffers = (SLuint16) thiz->mDataSource.mLocator.mABQ.numBuffers;
usesAdvancedBufferHeaders = true;
+ nbBuffers = (SLuint16) thiz->mDataSource.mLocator.mABQ.numBuffers;
thiz->mAndroidBufferQueue.mNumBuffers = nbBuffers;
break;
#endif
switch(thiz->mDataSink.mLocator.mLocatorType) {
case SL_DATALOCATOR_BUFFERQUEUE:
case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
+ usesSimpleBufferQueue = true;
nbBuffers = thiz->mDataSink.mLocator.mBufferQueue.numBuffers;
assert(SL_DATAFORMAT_PCM == thiz->mDataSink.mFormat.mFormatType);
// FIXME The values specified by the app are meaningless. We get the
if (usesAdvancedBufferHeaders) {
#ifdef ANDROID
// locator is SL_DATALOCATOR_ANDROIDBUFFERQUEUE
- // Avoid possible integer overflow during multiplication; this arbitrary
- // maximum is big enough to not interfere with real applications, but
- // small enough to not overflow.
- if (thiz->mAndroidBufferQueue.mNumBuffers >= 256) {
- result = SL_RESULT_MEMORY_FAILURE;
- break;
- }
- thiz->mAndroidBufferQueue.mBufferArray = (AdvancedBufferHeader *)
- malloc( (thiz->mAndroidBufferQueue.mNumBuffers + 1)
- * sizeof(AdvancedBufferHeader));
- if (NULL == thiz->mAndroidBufferQueue.mBufferArray) {
- result = SL_RESULT_MEMORY_FAILURE;
- break;
- } else {
-
- // initialize ABQ buffer type
- // assert below has been checked in android_audioPlayer_checkSourceSink
- assert(SL_DATAFORMAT_MIME == thiz->mDataSource.mFormat.mFormatType);
- if (SL_CONTAINERTYPE_MPEG_TS ==
- thiz->mDataSource.mFormat.mMIME.containerType) {
- thiz->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeMpeg2Ts;
- } else {
- thiz->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeInvalid;
- SL_LOGE("Invalid buffer type in Android Buffer Queue");
- result = SL_RESULT_CONTENT_UNSUPPORTED;
- }
-
- // initialize ABQ memory
- for (SLuint16 i=0 ; i<(thiz->mAndroidBufferQueue.mNumBuffers + 1) ;
- i++) {
- thiz->mAndroidBufferQueue.mBufferArray[i].mDataBuffer = NULL;
- thiz->mAndroidBufferQueue.mBufferArray[i].mDataSize = 0;
- thiz->mAndroidBufferQueue.mBufferArray[i].mDataSizeConsumed = 0;
- thiz->mAndroidBufferQueue.mBufferArray[i].mBufferContext = NULL;
- thiz->mAndroidBufferQueue.mBufferArray[i].mBufferState =
- SL_ANDROIDBUFFERQUEUEEVENT_NONE;
- switch (thiz->mAndroidBufferQueue.mBufferType) {
- case kAndroidBufferTypeMpeg2Ts:
- thiz->mAndroidBufferQueue.mBufferArray[i].mItems.mTsCmdData.
- mTsCmdCode = ANDROID_MP2TSEVENT_NONE;
- thiz->mAndroidBufferQueue.mBufferArray[i].mItems.mTsCmdData.
- mPts = 0;
- break;
- default:
- result = SL_RESULT_CONTENT_UNSUPPORTED;
- break;
- }
- }
- thiz->mAndroidBufferQueue.mFront =
- thiz->mAndroidBufferQueue.mBufferArray;
- thiz->mAndroidBufferQueue.mRear =
- thiz->mAndroidBufferQueue.mBufferArray;
- }
+ result = initializeAndroidBufferQueueMembers(thiz);
#else
assert(false);
#endif
- } else {
+ }
+
+ if (usesSimpleBufferQueue) {
// locator is SL_DATALOCATOR_BUFFERQUEUE
// or SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
- // inline allocation of circular mArray, up to a typical max
- if (BUFFER_HEADER_TYPICAL >= thiz->mBufferQueue.mNumBuffers) {
- thiz->mBufferQueue.mArray = thiz->mBufferQueue.mTypical;
- } else {
- // Avoid possible integer overflow during multiplication; this arbitrary
- // maximum is big enough to not interfere with real applications, but
- // small enough to not overflow.
- if (thiz->mBufferQueue.mNumBuffers >= 256) {
- result = SL_RESULT_MEMORY_FAILURE;
- break;
- }
- thiz->mBufferQueue.mArray = (BufferHeader *) malloc((thiz->mBufferQueue.
- mNumBuffers + 1) * sizeof(BufferHeader));
- if (NULL == thiz->mBufferQueue.mArray) {
- result = SL_RESULT_MEMORY_FAILURE;
- break;
- }
- }
- thiz->mBufferQueue.mFront = thiz->mBufferQueue.mArray;
- thiz->mBufferQueue.mRear = thiz->mBufferQueue.mArray;
+ result = initializeBufferQueueMembers(thiz);
}
// used to store the data source of our audio player
SLAuint64 mPts;
} Mpeg2TsCommands;
+// Holds information about all commands that can be passed alongside an AAC ADTS buffer
+// Is used with buffers of type kAndroidBufferTypeAacadts
+typedef struct {
+ SLuint32 mAdtsCmdCode;
+} AdtsCommands;
+
// Union of the different structures to hold items stored in an AdvancedBufferHeader
// when an item comes from an AndroidBufferQueue as the data source, it's a command
// when an item is output to an AndroidBufferQueue as the data sink, it's a message (or metadata)
typedef union {
Mpeg2TsCommands mTsCmdData;
+ AdtsCommands mAdtsCmdData;
} AdvancedBufferItems;
// AdvancedBufferHeader describes each element of an AndroidBufferQueue, other than the data