Bug 5238515 AndroidBufferQueue miscellaneous
AndroidBufferQueue:
- errors found by setItems are now hard errors, and cause Enqueue to
return an error result (e.g. PARAMETER_INVALID or PRECONDITIONS_VIOLATED)
- disallow EOS with non-zero data
- disallow Enqueue after EOS
- Enqueue checks MPEG-2 sync byte of first packet to reduce chance of downstream failures
- use MPEG-2 terminology "packet" instead of "block"
- Clear and init don't need to erase buffer content, as it is inaccessible
- put placeholder in IAndroidBufferQueue_SetCallbackEventsMask
for additional events beyond SL_ANDROIDBUFFERQUEUEEVENT_PROCESSED
- comment unused field mBufferState in AdvancedBufferHeader
- fix a minor typo
- add dump method, #if 0 out by default
Change-Id: I11921e3784bfdb30e2cebaa1dabb705ea5ab0b92
diff --git a/src/android/android_defs.h b/src/android/android_defs.h
index 59f772a..609ef28 100644
--- a/src/android/android_defs.h
+++ b/src/android/android_defs.h
@@ -62,7 +62,8 @@
#define PLAYER_FD_FIND_FILE_SIZE ((int64_t)0xFFFFFFFFFFFFFFFFll)
-#define MPEG2_TS_BLOCK_SIZE 188
+#define MPEG2_TS_PACKET_SIZE 188
+#define MPEG2_TS_PACKET_SYNC 0x47
struct AudioPlayback_Parameters {
int streamType;
diff --git a/src/itf/IAndroidBufferQueue.c b/src/itf/IAndroidBufferQueue.c
index 76ab546..b0f79fa 100644
--- a/src/itf/IAndroidBufferQueue.c
+++ b/src/itf/IAndroidBufferQueue.c
@@ -23,7 +23,7 @@
#include "android/include/AacBqToPcmCbRenderer.h"
/**
- * Determine the state of the audio player or audio recorder associated with a buffer queue.
+ * Determine the state of the audio player or media player associated with a buffer queue.
* Note that PLAYSTATE and RECORDSTATE values are equivalent (where PLAYING == RECORDING).
*/
@@ -51,8 +51,9 @@
* parse and set the items associated with the given buffer, based on the buffer type,
* which determines the set of authorized items and format
*/
-static void setItems(const SLAndroidBufferItem *pItems, SLuint32 itemsLength,
- SLuint16 bufferType, AdvancedBufferHeader *pBuff)
+static SLresult setItems(SLuint32 dataLength,
+ const SLAndroidBufferItem *pItems, SLuint32 itemsLength,
+ SLuint16 bufferType, AdvancedBufferHeader *pBuff, bool *pEOS)
{
// reset item structure based on type
switch (bufferType) {
@@ -67,7 +68,7 @@
default:
// shouldn't happen, but just in case clear out the item structure
memset(&pBuff->mItems, 0, sizeof(AdvancedBufferItems));
- return;
+ return SL_RESULT_INTERNAL_ERROR;
}
// process all items in the array; if no items then we break out of loop immediately
@@ -75,16 +76,16 @@
// remaining length must be large enough for one full item without any associated data
if (itemsLength < sizeof(SLAndroidBufferItem)) {
- SL_LOGE("Partial item at end of array ignored");
- break;
+ SL_LOGE("Partial item at end of array");
+ return SL_RESULT_PARAMETER_INVALID;
}
itemsLength -= sizeof(SLAndroidBufferItem);
// remaining length must be large enough for data with current item and alignment padding
SLuint32 itemDataSizeWithAlignmentPadding = (pItems->itemSize + 3) & ~3;
if (itemsLength < itemDataSizeWithAlignmentPadding) {
- SL_LOGE("Partial item data at end of array ignored");
- break;
+ SL_LOGE("Partial item data at end of array");
+ return SL_RESULT_PARAMETER_INVALID;
}
itemsLength -= itemDataSizeWithAlignmentPadding;
@@ -98,8 +99,8 @@
pBuff->mItems.mTsCmdData.mTsCmdCode |= ANDROID_MP2TSEVENT_EOS;
//SL_LOGD("Found EOS event=%d", pBuff->mItems.mTsCmdData.mTsCmdCode);
if (pItems->itemSize != 0) {
- SL_LOGE("Invalid item parameter size %u for EOS, ignoring value",
- pItems->itemSize);
+ SL_LOGE("Invalid item parameter size %u for EOS", pItems->itemSize);
+ return SL_RESULT_PARAMETER_INVALID;
}
break;
@@ -112,25 +113,23 @@
pBuff->mItems.mTsCmdData.mPts = *((SLAuint64*)pItems->itemData);
//SL_LOGD("Found PTS=%lld", pBuff->mItems.mTsCmdData.mPts);
} else {
- SL_LOGE("Invalid item parameter size %u for MPEG-2 PTS, ignoring value",
- pItems->itemSize);
- pBuff->mItems.mTsCmdData.mTsCmdCode |= ANDROID_MP2TSEVENT_DISCONTINUITY;
+ SL_LOGE("Invalid item parameter size %u for MPEG-2 PTS", pItems->itemSize);
+ return SL_RESULT_PARAMETER_INVALID;
}
break;
case SL_ANDROID_ITEMKEY_FORMAT_CHANGE:
pBuff->mItems.mTsCmdData.mTsCmdCode |= ANDROID_MP2TSEVENT_FORMAT_CHANGE;
if (pItems->itemSize != 0) {
- SL_LOGE("Invalid item parameter size %u for format change, ignoring value",
- pItems->itemSize);
+ SL_LOGE("Invalid item parameter size %u for format change", pItems->itemSize);
+ return SL_RESULT_PARAMETER_INVALID;
}
break;
default:
// unknown item key
- SL_LOGE("Unknown item key %u with size %u ignored", pItems->itemKey,
- pItems->itemSize);
- break;
+ SL_LOGE("Unknown item key %u with size %u", pItems->itemKey, pItems->itemSize);
+ return SL_RESULT_PARAMETER_INVALID;
}// switch (pItems->itemKey)
} break;
@@ -141,16 +140,15 @@
case SL_ANDROID_ITEMKEY_EOS:
pBuff->mItems.mAdtsCmdData.mAdtsCmdCode |= ANDROID_ADTSEVENT_EOS;
if (pItems->itemSize != 0) {
- SL_LOGE("Invalid item parameter size %u for EOS, ignoring value",
- pItems->itemSize);
+ SL_LOGE("Invalid item parameter size %u for EOS", pItems->itemSize);
+ return SL_RESULT_PARAMETER_INVALID;
}
break;
default:
// unknown item key
- SL_LOGE("Unknown item key %u with size %u ignored", pItems->itemKey,
- pItems->itemSize);
- break;
+ SL_LOGE("Unknown item key %u with size %u", pItems->itemKey, pItems->itemSize);
+ return SL_RESULT_PARAMETER_INVALID;
}// switch (pItems->itemKey)
} break;
@@ -158,7 +156,7 @@
case kAndroidBufferTypeInvalid:
default:
// not reachable as we checked this earlier
- return;
+ return SL_RESULT_INTERNAL_ERROR;
}// switch (bufferType)
@@ -174,30 +172,43 @@
// supported Mpeg2Ts commands are mutually exclusive
switch (pBuff->mItems.mTsCmdData.mTsCmdCode) {
// single items are allowed
- case ANDROID_MP2TSEVENT_NONE:
case ANDROID_MP2TSEVENT_EOS:
+ if (dataLength > 0) {
+ SL_LOGE("Can't enqueue non-zero data with EOS");
+ return SL_RESULT_PRECONDITIONS_VIOLATED;
+ }
+ *pEOS = true;
+ break;
+ case ANDROID_MP2TSEVENT_NONE:
case ANDROID_MP2TSEVENT_DISCONTINUITY:
case ANDROID_MP2TSEVENT_DISCON_NEWPTS:
case ANDROID_MP2TSEVENT_FORMAT_CHANGE:
break;
// no combinations are allowed
default:
- SL_LOGE("Invalid combination of items; all ignored");
- pBuff->mItems.mTsCmdData.mTsCmdCode = ANDROID_MP2TSEVENT_NONE;
- break;
+ SL_LOGE("Invalid combination of items");
+ return SL_RESULT_PARAMETER_INVALID;
}
} break;
case kAndroidBufferTypeAacadts: {
// only one item supported, and thus no combination check needed
+ if (pBuff->mItems.mAdtsCmdData.mAdtsCmdCode == ANDROID_ADTSEVENT_EOS) {
+ if (dataLength > 0) {
+ SL_LOGE("Can't enqueue non-zero data with EOS");
+ return SL_RESULT_PRECONDITIONS_VIOLATED;
+ }
+ *pEOS = true;
+ }
} break;
case kAndroidBufferTypeInvalid:
default:
// not reachable as we checked this earlier
- return;
+ return SL_RESULT_INTERNAL_ERROR;
}
+ return SL_RESULT_SUCCESS;
}
@@ -241,40 +252,17 @@
// reset the queue state
thiz->mState.count = 0;
thiz->mState.index = 0;
- // reset the individual buffers
- for (XAuint16 i=0 ; i<(thiz->mNumBuffers + 1) ; i++) {
- thiz->mBufferArray[i].mDataBuffer = NULL;
- thiz->mBufferArray[i].mDataSize = 0;
- thiz->mBufferArray[i].mDataSizeConsumed = 0;
- thiz->mBufferArray[i].mBufferContext = NULL;
- thiz->mBufferArray[i].mBufferState = SL_ANDROIDBUFFERQUEUEEVENT_NONE;
- switch (thiz->mBufferType) {
- case kAndroidBufferTypeMpeg2Ts:
- thiz->mBufferArray[i].mItems.mTsCmdData.mTsCmdCode = ANDROID_MP2TSEVENT_NONE;
- thiz->mBufferArray[i].mItems.mTsCmdData.mPts = 0;
- break;
- case kAndroidBufferTypeAacadts:
- thiz->mBufferArray[i].mItems.mAdtsCmdData.mAdtsCmdCode = ANDROID_ADTSEVENT_NONE;
- break;
- default:
- result = SL_RESULT_CONTENT_UNSUPPORTED;
- }
- }
- if (SL_RESULT_SUCCESS == result) {
- // object-specific behavior for a clear
- switch (InterfaceToObjectID(thiz)) {
- case SL_OBJECTID_AUDIOPLAYER:
- result = SL_RESULT_SUCCESS;
- android_audioPlayer_androidBufferQueue_clear_l((CAudioPlayer*) thiz->mThis);
- break;
- case XA_OBJECTID_MEDIAPLAYER:
- result = SL_RESULT_SUCCESS;
- android_Player_androidBufferQueue_clear_l((CMediaPlayer*) thiz->mThis);
- break;
- default:
- result = SL_RESULT_PARAMETER_INVALID;
- }
+ // object-specific behavior for a clear
+ switch (InterfaceToObjectID(thiz)) {
+ case SL_OBJECTID_AUDIOPLAYER:
+ android_audioPlayer_androidBufferQueue_clear_l((CAudioPlayer*) thiz->mThis);
+ break;
+ case XA_OBJECTID_MEDIAPLAYER:
+ android_Player_androidBufferQueue_clear_l((CMediaPlayer*) thiz->mThis);
+ break;
+ default:
+ result = SL_RESULT_PARAMETER_INVALID;
}
interface_unlock_exclusive(thiz);
@@ -313,16 +301,24 @@
// buffer size check, can be done outside of lock because buffer type can't change
switch (thiz->mBufferType) {
case kAndroidBufferTypeMpeg2Ts:
- if (dataLength % MPEG2_TS_BLOCK_SIZE == 0) {
+ if (dataLength % MPEG2_TS_PACKET_SIZE == 0) {
+ // The downstream Stagefright MPEG-2 TS parser is sensitive to format errors,
+ // so do a quick sanity check beforehand on the first packet of the buffer.
+ // We don't check all the packets to avoid thrashing the data cache.
+ if ((dataLength > 0) && (*(SLuint8 *)pData != MPEG2_TS_PACKET_SYNC)) {
+ SL_LOGE("Error enqueueing MPEG-2 TS data: incorrect packet sync");
+ result = SL_RESULT_CONTENT_CORRUPTED;
+ SL_LEAVE_INTERFACE
+ }
break;
}
- SL_LOGE("Error enqueueing MPEG-2 TS data: size must be a multiple of %d (block size)",
- MPEG2_TS_BLOCK_SIZE);
+ SL_LOGE("Error enqueueing MPEG-2 TS data: size must be a multiple of %d (packet size)",
+ MPEG2_TS_PACKET_SIZE);
result = SL_RESULT_PARAMETER_INVALID;
SL_LEAVE_INTERFACE
break;
case kAndroidBufferTypeAacadts:
- // non-zero dataLength is permitted in case of EOS command only
+ // zero dataLength is permitted in case of EOS command only
if (dataLength > 0) {
result = android::AacBqToPcmCbRenderer::validateBufferStartEndOnFrameBoundaries(
pData, dataLength);
@@ -345,19 +341,24 @@
if ((newRear = oldRear + 1) == &thiz->mBufferArray[thiz->mNumBuffers + 1]) {
newRear = thiz->mBufferArray;
}
- if (newRear == thiz->mFront) {
+ if (thiz->mEOS) {
+ SL_LOGE("Can't enqueue after EOS");
+ result = SL_RESULT_PRECONDITIONS_VIOLATED;
+ } else if (newRear == thiz->mFront) {
result = SL_RESULT_BUFFER_INSUFFICIENT;
} else {
- oldRear->mDataBuffer = pData;
- oldRear->mDataSize = dataLength;
- oldRear->mDataSizeConsumed = 0;
- oldRear->mBufferContext = pBufferContext;
- oldRear->mBufferState = SL_ANDROIDBUFFERQUEUEEVENT_NONE;
- thiz->mRear = newRear;
- ++thiz->mState.count;
// set oldRear->mItems based on items
- setItems(pItems, itemsLength, thiz->mBufferType, oldRear);
- result = SL_RESULT_SUCCESS;
+ result = setItems(dataLength, pItems, itemsLength, thiz->mBufferType, oldRear,
+ &thiz->mEOS);
+ if (SL_RESULT_SUCCESS == result) {
+ oldRear->mDataBuffer = pData;
+ oldRear->mDataSize = dataLength;
+ oldRear->mDataSizeConsumed = 0;
+ oldRear->mBufferContext = pBufferContext;
+ //oldRear->mBufferState = TBD;
+ thiz->mRear = newRear;
+ ++thiz->mState.count;
+ }
}
// set enqueue attribute if state is PLAYING and the first buffer is enqueued
interface_unlock_exclusive_attributes(thiz, ((SL_RESULT_SUCCESS == result) &&
@@ -403,8 +404,7 @@
IAndroidBufferQueue *thiz = (IAndroidBufferQueue *) self;
interface_lock_exclusive(thiz);
// FIXME only supporting SL_ANDROIDBUFFERQUEUEEVENT_PROCESSED in this implementation
- if ((SL_ANDROIDBUFFERQUEUEEVENT_PROCESSED == eventFlags) ||
- (SL_ANDROIDBUFFERQUEUEEVENT_NONE == eventFlags)) {
+ if (!(~(SL_ANDROIDBUFFERQUEUEEVENT_PROCESSED /* | others TBD */ ) & eventFlags)) {
thiz->mCallbackEventsMask = eventFlags;
result = SL_RESULT_SUCCESS;
} else {
@@ -462,6 +462,7 @@
thiz->mBufferArray = NULL;
thiz->mFront = NULL;
thiz->mRear = NULL;
+ thiz->mEOS = false;
}
@@ -473,3 +474,98 @@
thiz->mBufferArray = NULL;
}
}
+
+
+#if 0
+// Dump the contents of an IAndroidBufferQueue to the log. This is for debugging only,
+// and is not a documented API. The associated object is locked throughout for atomicity,
+// but the log entries may be interspersed with unrelated logs.
+
+void IAndroidBufferQueue_log(IAndroidBufferQueue *thiz)
+{
+ interface_lock_shared(thiz);
+ SL_LOGI("IAndroidBufferQueue %p:", thiz);
+ SL_LOGI(" mState.count=%u mState.index=%u mCallback=%p mContext=%p",
+ thiz->mState.count, thiz->mState.index, thiz->mCallback, thiz->mContext);
+ const char *bufferTypeString;
+ switch (thiz->mBufferType) {
+ case kAndroidBufferTypeInvalid:
+ bufferTypeString = "kAndroidBufferTypeInvalid";
+ break;
+ case kAndroidBufferTypeMpeg2Ts:
+ bufferTypeString = "kAndroidBufferTypeMpeg2Ts";
+ break;
+ case kAndroidBufferTypeAacadts:
+ bufferTypeString = "kAndroidBufferTypeAacadts";
+ break;
+ default:
+ bufferTypeString = "unknown";
+ break;
+ }
+ SL_LOGI(" mCallbackEventsMask=0x%x, mBufferType=0x%x (%s), mEOS=%s",
+ thiz->mCallbackEventsMask,
+ thiz->mBufferType, bufferTypeString,
+ thiz->mEOS ? "true" : "false");
+ SL_LOGI(" mBufferArray=%p, mFront=%p (%u), mRear=%p (%u)",
+ thiz->mBufferArray,
+ thiz->mFront, thiz->mFront - thiz->mBufferArray,
+ thiz->mRear, thiz->mRear - thiz->mBufferArray);
+ SL_LOGI(" index mDataBuffer mDataSize mDataSizeConsumed mBufferContext mItems");
+ const AdvancedBufferHeader *hdr;
+ for (hdr = thiz->mFront; hdr != thiz->mRear; ) {
+ SLuint32 i = hdr - thiz->mBufferArray;
+ char itemString[32];
+ switch (thiz->mBufferType) {
+ case kAndroidBufferTypeMpeg2Ts:
+ switch (hdr->mItems.mTsCmdData.mTsCmdCode) {
+ case ANDROID_MP2TSEVENT_NONE:
+ strcpy(itemString, "NONE");
+ break;
+ case ANDROID_MP2TSEVENT_EOS:
+ strcpy(itemString, "EOS");
+ break;
+ case ANDROID_MP2TSEVENT_DISCONTINUITY:
+ strcpy(itemString, "DISCONTINUITY");
+ break;
+ case ANDROID_MP2TSEVENT_DISCON_NEWPTS:
+ snprintf(itemString, sizeof(itemString), "NEWPTS %llu",
+ hdr->mItems.mTsCmdData.mPts);
+ break;
+ case ANDROID_MP2TSEVENT_FORMAT_CHANGE:
+ strcpy(itemString, "FORMAT_CHANGE");
+ break;
+ default:
+ snprintf(itemString, sizeof(itemString), "0x%x", hdr->mItems.mTsCmdData.mTsCmdCode);
+ break;
+ }
+ break;
+ case kAndroidBufferTypeAacadts:
+ switch (hdr->mItems.mAdtsCmdData.mAdtsCmdCode) {
+ case ANDROID_ADTSEVENT_NONE:
+ strcpy(itemString, "NONE");
+ break;
+ case ANDROID_ADTSEVENT_EOS:
+ strcpy(itemString, "EOS");
+ break;
+ default:
+ snprintf(itemString, sizeof(itemString), "0x%x",
+ hdr->mItems.mAdtsCmdData.mAdtsCmdCode);
+ break;
+ }
+ break;
+ default:
+ strcpy(itemString, "");
+ break;
+ }
+ SL_LOGI(" %5u %11p %9u %17u %14p %s",
+ i, hdr->mDataBuffer, hdr->mDataSize, hdr->mDataSizeConsumed,
+ hdr->mBufferContext, itemString);
+ // mBufferState
+ if (++hdr == &thiz->mBufferArray[thiz->mNumBuffers + 1]) {
+ hdr = thiz->mBufferArray;
+ }
+ }
+ interface_unlock_shared(thiz);
+}
+
+#endif
diff --git a/src/itf/IEngine.c b/src/itf/IEngine.c
index 41de0a1..bf45a81 100644
--- a/src/itf/IEngine.c
+++ b/src/itf/IEngine.c
@@ -82,26 +82,6 @@
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.mAdtsCmdData.mAdtsCmdCode = ANDROID_ADTSEVENT_NONE;
- break;
- default:
- return SL_RESULT_CONTENT_UNSUPPORTED;
- }
- }
ap->mAndroidBufferQueue.mFront = ap->mAndroidBufferQueue.mBufferArray;
ap->mAndroidBufferQueue.mRear = ap->mAndroidBufferQueue.mBufferArray;
}
@@ -1229,25 +1209,6 @@
result = SL_RESULT_MEMORY_FAILURE;
break;
} else {
- for (XAuint16 i=0 ; i<(nbBuffers + 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 =
- XA_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 =
diff --git a/src/itfstruct.h b/src/itfstruct.h
index 8391c96..36d7565 100644
--- a/src/itfstruct.h
+++ b/src/itfstruct.h
@@ -672,6 +672,7 @@
AndroidBufferType_type mBufferType;
AdvancedBufferHeader *mBufferArray;
AdvancedBufferHeader *mFront, *mRear;
+ bool mEOS; // whether EOS has been enqueued; never reset
} IAndroidBufferQueue;
#endif
diff --git a/src/sles_allinclusive.h b/src/sles_allinclusive.h
index 513f774..0b33dcb 100644
--- a/src/sles_allinclusive.h
+++ b/src/sles_allinclusive.h
@@ -255,7 +255,8 @@
SLuint32 mDataSizeConsumed;
AdvancedBufferItems mItems;
const void *mBufferContext;
- SLuint32 mBufferState;
+ // mBufferState will be used for the other ABQ events we'll support in the future
+ // SLuint32 mBufferState;
} AdvancedBufferHeader;
#endif