Merge "libcameraservice: Stop using IGBC::BufferItem"
diff --git a/include/media/stagefright/foundation/ABase.h b/include/media/stagefright/foundation/ABase.h
index 72e3d87..ef1e010 100644
--- a/include/media/stagefright/foundation/ABase.h
+++ b/include/media/stagefright/foundation/ABase.h
@@ -18,7 +18,9 @@
#define A_BASE_H_
+#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
+#endif
#define DISALLOW_EVIL_CONSTRUCTORS(name) \
name(const name &); \
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 3a399af..f113e21 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -442,6 +442,9 @@
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
+ SortedVector< sp<Client> > clients; //to serialise the mutex unlock & client destruction.
+ SortedVector< sp<MediaRecorderClient> > mediaRecorderClients;
+
if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
snprintf(buffer, SIZE, "Permission Denial: "
"can't dump MediaPlayerService from pid=%d, uid=%d\n",
@@ -453,6 +456,7 @@
for (int i = 0, n = mClients.size(); i < n; ++i) {
sp<Client> c = mClients[i].promote();
if (c != 0) c->dump(fd, args);
+ clients.add(c);
}
if (mMediaRecorderClients.size() == 0) {
result.append(" No media recorder client\n\n");
@@ -465,6 +469,7 @@
write(fd, result.string(), result.size());
result = "\n";
c->dump(fd, args);
+ mediaRecorderClients.add(c);
}
}
}
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 70ae85e..a040343 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -65,12 +65,12 @@
mUID(uid),
mFd(-1),
mDrmManagerClient(NULL),
- mMetaDataSize(-1ll),
mBitrate(-1ll),
mPollBufferingGeneration(0),
mPendingReadBufferTypes(0),
mBuffering(false),
- mPrepareBuffering(false) {
+ mPrepareBuffering(false),
+ mPrevBufferPercentage(-1) {
resetDataSource();
DataSource::RegisterDefaultSniffers();
}
@@ -147,14 +147,11 @@
return UNKNOWN_ERROR;
}
} else if (mIsStreaming) {
- if (mSniffedMIME.empty()) {
- if (!mDataSource->sniff(&mimeType, &confidence, &dummy)) {
- return UNKNOWN_ERROR;
- }
- mSniffedMIME = mimeType.string();
+ if (!mDataSource->sniff(&mimeType, &confidence, &dummy)) {
+ return UNKNOWN_ERROR;
}
isWidevineStreaming = !strcasecmp(
- mSniffedMIME.c_str(), MEDIA_MIMETYPE_CONTAINER_WVM);
+ mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM);
}
if (isWidevineStreaming) {
@@ -169,7 +166,7 @@
extractor = mWVMExtractor;
} else {
extractor = MediaExtractor::Create(mDataSource,
- mSniffedMIME.empty() ? NULL: mSniffedMIME.c_str());
+ mimeType.isEmpty() ? NULL : mimeType.string());
}
if (extractor == NULL) {
@@ -347,6 +344,7 @@
if (!mUri.empty()) {
const char* uri = mUri.c_str();
+ String8 contentType;
mIsWidevine = !strncasecmp(uri, "widevine://", 11);
if (!strncasecmp("http://", uri, 7)
@@ -361,7 +359,7 @@
}
mDataSource = DataSource::CreateFromURI(
- mHTTPService, uri, &mUriHeaders, &mContentType,
+ mHTTPService, uri, &mUriHeaders, &contentType,
static_cast<HTTPBase *>(mHttpSource.get()));
} else {
mIsWidevine = false;
@@ -389,20 +387,8 @@
mIsStreaming = (mIsWidevine || mCachedSource != NULL);
}
- // check initial caching status
- status_t err = prefillCacheIfNecessary();
- if (err != OK) {
- if (err == -EAGAIN) {
- (new AMessage(kWhatPrepareAsync, this))->post(200000);
- } else {
- ALOGE("Failed to prefill data cache!");
- notifyPreparedAndCleanup(UNKNOWN_ERROR);
- }
- return;
- }
-
- // init extrator from data source
- err = initFromDataSource();
+ // init extractor from data source
+ status_t err = initFromDataSource();
if (err != OK) {
ALOGE("Failed to init from data source!");
@@ -441,9 +427,6 @@
void NuPlayer::GenericSource::notifyPreparedAndCleanup(status_t err) {
if (err != OK) {
- mMetaDataSize = -1ll;
- mContentType = "";
- mSniffedMIME = "";
mDataSource.clear();
mCachedSource.clear();
mHttpSource.clear();
@@ -453,76 +436,6 @@
notifyPrepared(err);
}
-status_t NuPlayer::GenericSource::prefillCacheIfNecessary() {
- CHECK(mDataSource != NULL);
-
- if (mCachedSource == NULL) {
- // no prefill if the data source is not cached
- return OK;
- }
-
- // We're not doing this for streams that appear to be audio-only
- // streams to ensure that even low bandwidth streams start
- // playing back fairly instantly.
- if (!strncasecmp(mContentType.string(), "audio/", 6)) {
- return OK;
- }
-
- // We're going to prefill the cache before trying to instantiate
- // the extractor below, as the latter is an operation that otherwise
- // could block on the datasource for a significant amount of time.
- // During that time we'd be unable to abort the preparation phase
- // without this prefill.
-
- // Initially make sure we have at least 192 KB for the sniff
- // to complete without blocking.
- static const size_t kMinBytesForSniffing = 192 * 1024;
- static const size_t kDefaultMetaSize = 200000;
-
- status_t finalStatus;
-
- size_t cachedDataRemaining =
- mCachedSource->approxDataRemaining(&finalStatus);
-
- if (finalStatus != OK || (mMetaDataSize >= 0
- && (off64_t)cachedDataRemaining >= mMetaDataSize)) {
- ALOGV("stop caching, status %d, "
- "metaDataSize %lld, cachedDataRemaining %zu",
- finalStatus, mMetaDataSize, cachedDataRemaining);
- return OK;
- }
-
- ALOGV("now cached %zu bytes of data", cachedDataRemaining);
-
- if (mMetaDataSize < 0
- && cachedDataRemaining >= kMinBytesForSniffing) {
- String8 tmp;
- float confidence;
- sp<AMessage> meta;
- if (!mCachedSource->sniff(&tmp, &confidence, &meta)) {
- return UNKNOWN_ERROR;
- }
-
- // We successfully identified the file's extractor to
- // be, remember this mime type so we don't have to
- // sniff it again when we call MediaExtractor::Create()
- mSniffedMIME = tmp.string();
-
- if (meta == NULL
- || !meta->findInt64("meta-data-size",
- reinterpret_cast<int64_t*>(&mMetaDataSize))) {
- mMetaDataSize = kDefaultMetaSize;
- }
-
- if (mMetaDataSize < 0ll) {
- ALOGE("invalid metaDataSize = %lld bytes", mMetaDataSize);
- return UNKNOWN_ERROR;
- }
- }
-
- return -EAGAIN;
-}
-
void NuPlayer::GenericSource::start() {
ALOGI("start");
@@ -599,6 +512,7 @@
void NuPlayer::GenericSource::cancelPollBuffering() {
mBuffering = false;
++mPollBufferingGeneration;
+ mPrevBufferPercentage = -1;
}
void NuPlayer::GenericSource::restartPollBuffering() {
@@ -608,7 +522,19 @@
}
}
-void NuPlayer::GenericSource::notifyBufferingUpdate(int percentage) {
+void NuPlayer::GenericSource::notifyBufferingUpdate(int32_t percentage) {
+ // Buffering percent could go backward as it's estimated from remaining
+ // data and last access time. This could cause the buffering position
+ // drawn on media control to jitter slightly. Remember previously reported
+ // percentage and don't allow it to go backward.
+ if (percentage < mPrevBufferPercentage) {
+ percentage = mPrevBufferPercentage;
+ } else if (percentage > 100) {
+ percentage = 100;
+ }
+
+ mPrevBufferPercentage = percentage;
+
ALOGV("notifyBufferingUpdate: buffering %d%%", percentage);
sp<AMessage> msg = dupNotify();
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 385d73a..5fc41ec 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -139,14 +139,13 @@
sp<DecryptHandle> mDecryptHandle;
bool mStarted;
bool mStopRead;
- String8 mContentType;
- AString mSniffedMIME;
- off64_t mMetaDataSize;
int64_t mBitrate;
int32_t mPollBufferingGeneration;
uint32_t mPendingReadBufferTypes;
bool mBuffering;
bool mPrepareBuffering;
+ int32_t mPrevBufferPercentage;
+
mutable Mutex mReadBufferLock;
sp<ALooper> mLooper;
@@ -158,8 +157,6 @@
int64_t getLastReadPosition();
void setDrmPlaybackStatusIfNeeded(int playbackStatus, int64_t position);
- status_t prefillCacheIfNecessary();
-
void notifyPreparedAndCleanup(status_t err);
void onGetFormatMeta(sp<AMessage> msg) const;
@@ -200,7 +197,7 @@
void cancelPollBuffering();
void restartPollBuffering();
void onPollBuffering();
- void notifyBufferingUpdate(int percentage);
+ void notifyBufferingUpdate(int32_t percentage);
void startBufferingIfNecessary();
void stopBufferingIfNecessary();
void sendCacheStats();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 8e59b75..04ac699 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -56,6 +56,7 @@
mIsVideoAVC(false),
mIsSecure(false),
mFormatChangePending(false),
+ mTimeChangePending(false),
mPaused(true),
mResumePending(false),
mComponentName("decoder") {
@@ -121,6 +122,7 @@
CHECK(mCodec == NULL);
mFormatChangePending = false;
+ mTimeChangePending = false;
++mBufferGeneration;
@@ -235,7 +237,7 @@
}
}
-void NuPlayer::Decoder::onFlush(bool notifyComplete) {
+void NuPlayer::Decoder::doFlush(bool notifyComplete) {
if (mCCDecoder != NULL) {
mCCDecoder->flush();
}
@@ -259,13 +261,22 @@
// we attempt to release the buffers even if flush fails.
}
releaseAndResetMediaBuffers();
+}
- if (notifyComplete) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatFlushCompleted);
- notify->post();
- mPaused = true;
+void NuPlayer::Decoder::onFlush() {
+ doFlush(true);
+
+ if (isDiscontinuityPending()) {
+ // This could happen if the client starts seeking/shutdown
+ // after we queued an EOS for discontinuities.
+ // We can consider discontinuity handled.
+ finishHandleDiscontinuity(false /* flushOnTimeChange */);
}
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatFlushCompleted);
+ notify->post();
+ mPaused = true;
}
void NuPlayer::Decoder::onShutdown(bool notifyComplete) {
@@ -309,16 +320,17 @@
}
void NuPlayer::Decoder::doRequestBuffers() {
- if (mFormatChangePending) {
+ if (isDiscontinuityPending()) {
return;
}
status_t err = OK;
- while (!mDequeuedInputBuffers.empty()) {
+ while (err == OK && !mDequeuedInputBuffers.empty()) {
size_t bufferIx = *mDequeuedInputBuffers.begin();
sp<AMessage> msg = new AMessage();
msg->setSize("buffer-ix", bufferIx);
err = fetchInputData(msg);
- if (err != OK) {
+ if (err != OK && err != ERROR_END_OF_STREAM) {
+ // if EOS, need to queue EOS buffer
break;
}
mDequeuedInputBuffers.erase(mDequeuedInputBuffers.begin());
@@ -336,7 +348,7 @@
}
bool NuPlayer::Decoder::handleAnInputBuffer() {
- if (mFormatChangePending) {
+ if (isDiscontinuityPending()) {
return false;
}
size_t bufferIx = -1;
@@ -391,9 +403,6 @@
}
bool NuPlayer::Decoder::handleAnOutputBuffer() {
- if (mFormatChangePending) {
- return false;
- }
size_t bufferIx = -1;
size_t offset;
size_t size;
@@ -474,17 +483,20 @@
buffer->setRange(offset, size);
buffer->meta()->clear();
buffer->meta()->setInt64("timeUs", timeUs);
- if (flags & MediaCodec::BUFFER_FLAG_EOS) {
- buffer->meta()->setInt32("eos", true);
- notifyResumeCompleteIfNecessary();
- }
+
+ bool eos = flags & MediaCodec::BUFFER_FLAG_EOS;
// we do not expect CODECCONFIG or SYNCFRAME for decoder
sp<AMessage> reply = new AMessage(kWhatRenderBuffer, this);
reply->setSize("buffer-ix", bufferIx);
reply->setInt32("generation", mBufferGeneration);
- if (mSkipRenderingUntilMediaTimeUs >= 0) {
+ if (eos) {
+ ALOGI("[%s] saw output EOS", mIsAudio ? "audio" : "video");
+
+ buffer->meta()->setInt32("eos", true);
+ reply->setInt32("eos", true);
+ } else if (mSkipRenderingUntilMediaTimeUs >= 0) {
if (timeUs < mSkipRenderingUntilMediaTimeUs) {
ALOGV("[%s] dropping buffer at time %lld as requested.",
mComponentName.c_str(), (long long)timeUs);
@@ -502,7 +514,7 @@
if (mRenderer != NULL) {
// send the buffer to renderer.
mRenderer->queueBuffer(mIsAudio, buffer, reply);
- if (flags & MediaCodec::BUFFER_FLAG_EOS) {
+ if (eos && !isDiscontinuityPending()) {
mRenderer->queueEOS(mIsAudio, ERROR_END_OF_STREAM);
}
}
@@ -533,9 +545,6 @@
}
void NuPlayer::Decoder::requestCodecNotification() {
- if (mFormatChangePending) {
- return;
- }
if (mCodec != NULL) {
sp<AMessage> reply = new AMessage(kWhatCodecNotify, this);
reply->setInt32("generation", mBufferGeneration);
@@ -582,39 +591,31 @@
formatChange = !seamlessFormatChange;
}
- if (formatChange || timeChange) {
- sp<AMessage> msg = mNotify->dup();
- msg->setInt32("what", kWhatInputDiscontinuity);
- msg->setInt32("formatChange", formatChange);
- msg->post();
- }
-
+ // For format or time change, return EOS to queue EOS input,
+ // then wait for EOS on output.
if (formatChange /* not seamless */) {
- // must change decoder
- // return EOS and wait to be killed
mFormatChangePending = true;
- return ERROR_END_OF_STREAM;
+ err = ERROR_END_OF_STREAM;
} else if (timeChange) {
- // need to flush
- // TODO: Ideally we shouldn't need a flush upon time
- // discontinuity, flushing will cause loss of frames.
- // We probably should queue a time change marker to the
- // output queue, and handles it in renderer instead.
rememberCodecSpecificData(newFormat);
- onFlush(false /* notifyComplete */);
- err = OK;
+ mTimeChangePending = true;
+ err = ERROR_END_OF_STREAM;
} else if (seamlessFormatChange) {
// reuse existing decoder and don't flush
rememberCodecSpecificData(newFormat);
- err = OK;
+ continue;
} else {
// This stream is unaffected by the discontinuity
return -EWOULDBLOCK;
}
}
+ // reply should only be returned without a buffer set
+ // when there is an error (including EOS)
+ CHECK(err != OK);
+
reply->setInt32("err", err);
- return OK;
+ return ERROR_END_OF_STREAM;
}
if (!mIsAudio) {
@@ -636,7 +637,7 @@
#if 0
int64_t mediaTimeUs;
CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs));
- ALOGV("feeding %s input buffer at media time %.2f secs",
+ ALOGV("[%s] feeding input buffer at media time %" PRId64,
mIsAudio ? "audio" : "video",
mediaTimeUs / 1E6);
#endif
@@ -696,10 +697,7 @@
int32_t streamErr = ERROR_END_OF_STREAM;
CHECK(msg->findInt32("err", &streamErr) || !hasBuffer);
- if (streamErr == OK) {
- /* buffers are returned to hold on to */
- return true;
- }
+ CHECK(streamErr != OK);
// attempt to queue EOS
status_t err = mCodec->queueInputBuffer(
@@ -781,6 +779,7 @@
status_t err;
int32_t render;
size_t bufferIx;
+ int32_t eos;
CHECK(msg->findSize("buffer-ix", &bufferIx));
if (!mIsAudio) {
@@ -805,6 +804,42 @@
mComponentName.c_str(), err);
handleError(err);
}
+ if (msg->findInt32("eos", &eos) && eos
+ && isDiscontinuityPending()) {
+ finishHandleDiscontinuity(true /* flushOnTimeChange */);
+ }
+}
+
+bool NuPlayer::Decoder::isDiscontinuityPending() const {
+ return mFormatChangePending || mTimeChangePending;
+}
+
+void NuPlayer::Decoder::finishHandleDiscontinuity(bool flushOnTimeChange) {
+ ALOGV("finishHandleDiscontinuity: format %d, time %d, flush %d",
+ mFormatChangePending, mTimeChangePending, flushOnTimeChange);
+
+ // If we have format change, pause and wait to be killed;
+ // If we have time change only, flush and restart fetching.
+
+ if (mFormatChangePending) {
+ mPaused = true;
+ } else if (mTimeChangePending) {
+ if (flushOnTimeChange) {
+ doFlush(false /*notifyComplete*/);
+ }
+
+ // restart fetching input
+ scheduleRequestBuffers();
+ }
+
+ // Notify NuPlayer to either shutdown decoder, or rescan sources
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("what", kWhatInputDiscontinuity);
+ msg->setInt32("formatChange", mFormatChangePending);
+ msg->post();
+
+ mFormatChangePending = false;
+ mTimeChangePending = false;
}
bool NuPlayer::Decoder::supportsSeamlessAudioFormatChange(
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 1bfa94f..4aab2c6 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -43,7 +43,7 @@
virtual void onSetRenderer(const sp<Renderer> &renderer);
virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers);
virtual void onResume(bool notifyComplete);
- virtual void onFlush(bool notifyComplete);
+ virtual void onFlush();
virtual void onShutdown(bool notifyComplete);
virtual void doRequestBuffers();
@@ -81,6 +81,7 @@
bool mIsVideoAVC;
bool mIsSecure;
bool mFormatChangePending;
+ bool mTimeChangePending;
bool mPaused;
bool mResumePending;
@@ -93,6 +94,7 @@
void requestCodecNotification();
bool isStaleReply(const sp<AMessage> &msg);
+ void doFlush(bool notifyComplete);
status_t fetchInputData(sp<AMessage> &reply);
bool onInputBufferFetched(const sp<AMessage> &msg);
void onRenderBuffer(const sp<AMessage> &msg);
@@ -100,6 +102,8 @@
bool supportsSeamlessFormatChange(const sp<AMessage> &to) const;
bool supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const;
void rememberCodecSpecificData(const sp<AMessage> &format);
+ bool isDiscontinuityPending() const;
+ void finishHandleDiscontinuity(bool flushOnTimeChange);
void notifyResumeCompleteIfNecessary();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
index a726239..4636f0a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
@@ -157,7 +157,7 @@
case kWhatFlush:
{
- onFlush(true);
+ onFlush();
break;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
index 6732ff4..97e9269 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
@@ -65,7 +65,7 @@
virtual void onSetRenderer(const sp<Renderer> &renderer) = 0;
virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers) = 0;
virtual void onResume(bool notifyComplete) = 0;
- virtual void onFlush(bool notifyComplete) = 0;
+ virtual void onFlush() = 0;
virtual void onShutdown(bool notifyComplete) = 0;
void onRequestInputBuffers();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
index 479eba1..29b4c26 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
@@ -247,7 +247,7 @@
}
if (timeChange) {
- onFlush(false /* notifyComplete */);
+ doFlush(false /* notifyComplete */);
err = OK;
} else if (formatChange) {
// do seamless format change
@@ -364,7 +364,7 @@
}
}
-void NuPlayer::DecoderPassThrough::onFlush(bool notifyComplete) {
+void NuPlayer::DecoderPassThrough::doFlush(bool notifyComplete) {
++mBufferGeneration;
mSkipRenderingUntilMediaTimeUs = -1;
mPendingAudioAccessUnit.clear();
@@ -376,18 +376,21 @@
mRenderer->signalTimeDiscontinuity();
}
- if (notifyComplete) {
- mPaused = true;
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatFlushCompleted);
- notify->post();
- }
-
mPendingBuffersToDrain = 0;
mCachedBytes = 0;
mReachedEOS = false;
}
+void NuPlayer::DecoderPassThrough::onFlush() {
+ doFlush(true /* notifyComplete */);
+
+ mPaused = true;
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatFlushCompleted);
+ notify->post();
+
+}
+
void NuPlayer::DecoderPassThrough::onShutdown(bool notifyComplete) {
++mBufferGeneration;
mSkipRenderingUntilMediaTimeUs = -1;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
index a6e1faf..173cfbd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
@@ -43,7 +43,7 @@
virtual void onSetRenderer(const sp<Renderer> &renderer);
virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers);
virtual void onResume(bool notifyComplete);
- virtual void onFlush(bool notifyComplete);
+ virtual void onFlush();
virtual void onShutdown(bool notifyComplete);
virtual void doRequestBuffers();
@@ -77,6 +77,7 @@
status_t dequeueAccessUnit(sp<ABuffer> *accessUnit);
sp<ABuffer> aggregateBuffer(const sp<ABuffer> &accessUnit);
status_t fetchInputData(sp<AMessage> &reply);
+ void doFlush(bool notifyComplete);
void onInputBufferFetched(const sp<AMessage> &msg);
void onBufferConsumed(int32_t size);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 452c158..4bccfa8 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -715,6 +715,7 @@
int64_t nowUs = ALooper::GetNowUs();
int64_t nowMediaUs = mediaTimeUs - getPendingAudioPlayoutDurationUs(nowUs);
mMediaClock->updateAnchor(nowMediaUs, nowUs, mediaTimeUs);
+ mAnchorNumFramesWritten = mNumFramesWritten;
mAnchorTimeMediaUs = mediaTimeUs;
}
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index 530383b..e8abf48 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -26,6 +26,7 @@
#include <media/hardware/MetadataBufferType.h>
#include <ui/GraphicBuffer.h>
+#include <gui/BufferItem.h>
#include <gui/ISurfaceComposer.h>
#include <gui/IGraphicBufferAlloc.h>
#include <OMX_Component.h>
@@ -290,7 +291,7 @@
// TODO: mCurrentSlot can be made a bufferstate since there
// can be more than one "current" slots.
- BufferQueue::BufferItem item;
+ BufferItem item;
// If the recording has started and the queue is empty, then just
// wait here till the frames come in from the client side
while (mStarted) {
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
index 5ec3438..8ef2dca 100644
--- a/media/libstagefright/avc_utils.cpp
+++ b/media/libstagefright/avc_utils.cpp
@@ -26,6 +26,7 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
+#include <utils/misc.h>
namespace android {
@@ -186,17 +187,31 @@
if (aspect_ratio_idc == 255 /* extendedSAR */) {
sar_width = br.getBits(16);
sar_height = br.getBits(16);
- } else if (aspect_ratio_idc > 0 && aspect_ratio_idc < 14) {
- static const int32_t kFixedSARWidth[] = {
- 1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160
+ } else {
+ static const struct { unsigned width, height; } kFixedSARs[] = {
+ { 0, 0 }, // Invalid
+ { 1, 1 },
+ { 12, 11 },
+ { 10, 11 },
+ { 16, 11 },
+ { 40, 33 },
+ { 24, 11 },
+ { 20, 11 },
+ { 32, 11 },
+ { 80, 33 },
+ { 18, 11 },
+ { 15, 11 },
+ { 64, 33 },
+ { 160, 99 },
+ { 4, 3 },
+ { 3, 2 },
+ { 2, 1 },
};
- static const int32_t kFixedSARHeight[] = {
- 1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99
- };
-
- sar_width = kFixedSARWidth[aspect_ratio_idc - 1];
- sar_height = kFixedSARHeight[aspect_ratio_idc - 1];
+ if (aspect_ratio_idc > 0 && aspect_ratio_idc < NELEM(kFixedSARs)) {
+ sar_width = kFixedSARs[aspect_ratio_idc].width;
+ sar_height = kFixedSARs[aspect_ratio_idc].height;
+ }
}
}
diff --git a/media/libstagefright/filters/GraphicBufferListener.cpp b/media/libstagefright/filters/GraphicBufferListener.cpp
index fa38192..66374ba 100644
--- a/media/libstagefright/filters/GraphicBufferListener.cpp
+++ b/media/libstagefright/filters/GraphicBufferListener.cpp
@@ -21,6 +21,8 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaErrors.h>
+#include <gui/BufferItem.h>
+
#include "GraphicBufferListener.h"
namespace android {
@@ -83,8 +85,8 @@
// nothing to do
}
-BufferQueue::BufferItem GraphicBufferListener::getBufferItem() {
- BufferQueue::BufferItem item;
+BufferItem GraphicBufferListener::getBufferItem() {
+ BufferItem item;
{
Mutex::Autolock autoLock(mMutex);
@@ -124,8 +126,7 @@
return item;
}
-sp<GraphicBuffer> GraphicBufferListener::getBuffer(
- BufferQueue::BufferItem item) {
+sp<GraphicBuffer> GraphicBufferListener::getBuffer(BufferItem item) {
sp<GraphicBuffer> buf;
if (item.mBuf < 0 || item.mBuf >= BufferQueue::NUM_BUFFER_SLOTS) {
ALOGE("getBuffer() received invalid BufferItem: mBuf==%d", item.mBuf);
@@ -138,8 +139,7 @@
return buf;
}
-status_t GraphicBufferListener::releaseBuffer(
- BufferQueue::BufferItem item) {
+status_t GraphicBufferListener::releaseBuffer(BufferItem item) {
if (item.mBuf < 0 || item.mBuf >= BufferQueue::NUM_BUFFER_SLOTS) {
ALOGE("getBuffer() received invalid BufferItem: mBuf==%d", item.mBuf);
return ERROR_OUT_OF_RANGE;
diff --git a/media/libstagefright/filters/GraphicBufferListener.h b/media/libstagefright/filters/GraphicBufferListener.h
index b3e0ee3..586bf65 100644
--- a/media/libstagefright/filters/GraphicBufferListener.h
+++ b/media/libstagefright/filters/GraphicBufferListener.h
@@ -41,9 +41,9 @@
return mProducer;
}
- BufferQueue::BufferItem getBufferItem();
- sp<GraphicBuffer> getBuffer(BufferQueue::BufferItem item);
- status_t releaseBuffer(BufferQueue::BufferItem item);
+ BufferItem getBufferItem();
+ sp<GraphicBuffer> getBuffer(BufferItem item);
+ status_t releaseBuffer(BufferItem item);
enum {
kWhatFrameAvailable = 'frav',
diff --git a/media/libstagefright/filters/MediaFilter.cpp b/media/libstagefright/filters/MediaFilter.cpp
index d2f662d..0a09575 100644
--- a/media/libstagefright/filters/MediaFilter.cpp
+++ b/media/libstagefright/filters/MediaFilter.cpp
@@ -31,6 +31,8 @@
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaFilter.h>
+#include <gui/BufferItem.h>
+
#include "ColorConvert.h"
#include "GraphicBufferListener.h"
#include "IntrinsicBlurFilter.h"
@@ -749,7 +751,7 @@
}
void MediaFilter::onInputFrameAvailable() {
- BufferQueue::BufferItem item = mGraphicBufferListener->getBufferItem();
+ BufferItem item = mGraphicBufferListener->getBufferItem();
sp<GraphicBuffer> buf = mGraphicBufferListener->getBuffer(item);
// get pointer to graphic buffer
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index ea4aab6..a8f60a8 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -49,8 +49,13 @@
namespace android {
+// static
// Number of recently-read bytes to use for bandwidth estimation
const size_t LiveSession::kBandwidthHistoryBytes = 200 * 1024;
+// High water mark to start up switch or report prepared)
+const int64_t LiveSession::kHighWaterMark = 8000000ll;
+const int64_t LiveSession::kMidWaterMark = 5000000ll;
+const int64_t LiveSession::kLowWaterMark = 3000000ll;
LiveSession::LiveSession(
const sp<AMessage> ¬ify, uint32_t flags,
@@ -75,14 +80,14 @@
mSeekReplyID(0),
mFirstTimeUsValid(false),
mFirstTimeUs(0),
- mLastSeekTimeUs(0) {
+ mLastSeekTimeUs(0),
+ mPollBufferingGeneration(0) {
mStreams[kAudioIndex] = StreamItem("audio");
mStreams[kVideoIndex] = StreamItem("video");
mStreams[kSubtitleIndex] = StreamItem("subtitles");
for (size_t i = 0; i < kMaxStreams; ++i) {
- mDiscontinuities.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
mPacketSources.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
mPacketSources2.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
mBuffering[i] = false;
@@ -97,6 +102,9 @@
}
LiveSession::~LiveSession() {
+ if (mFetcherLooper != NULL) {
+ mFetcherLooper->stop();
+ }
}
sp<ABuffer> LiveSession::createFormatChangeBuffer(bool swap) {
@@ -125,24 +133,7 @@
return -EWOULDBLOCK;
}
- status_t finalResult;
- sp<AnotherPacketSource> discontinuityQueue = mDiscontinuities.valueFor(stream);
- if (discontinuityQueue->hasBufferAvailable(&finalResult)) {
- discontinuityQueue->dequeueAccessUnit(accessUnit);
- // seeking, track switching
- sp<AMessage> extra;
- int64_t timeUs;
- if ((*accessUnit)->meta()->findMessage("extra", &extra)
- && extra != NULL
- && extra->findInt64("timeUs", &timeUs)) {
- // seeking only
- mLastSeekTimeUs = timeUs;
- mDiscontinuityOffsetTimesUs.clear();
- mDiscontinuityAbsStartTimesUs.clear();
- }
- return INFO_DISCONTINUITY;
- }
-
+ status_t finalResult = OK;
sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(stream);
ssize_t idx = typeToIndex(stream);
@@ -172,7 +163,7 @@
if (mBuffering[idx]) {
if (mSwitchInProgress
|| packetSource->isFinished(0)
- || packetSource->getEstimatedDurationUs() > targetDurationUs) {
+ || packetSource->hasBufferAvailable(&finalResult)) {
mBuffering[idx] = false;
}
}
@@ -429,11 +420,16 @@
if (what == PlaylistFetcher::kWhatStopped) {
AString uri;
CHECK(msg->findString("uri", &uri));
- if (mFetcherInfos.removeItem(uri) < 0) {
+ ssize_t index = mFetcherInfos.indexOfKey(uri);
+ if (index < 0) {
// ignore duplicated kWhatStopped messages.
break;
}
+ mFetcherLooper->unregisterHandler(
+ mFetcherInfos[index].mFetcher->id());
+ mFetcherInfos.removeItemsAt(index);
+
if (mSwitchInProgress) {
tryToFinishBandwidthSwitch();
}
@@ -443,14 +439,6 @@
CHECK_GT(mContinuationCounter, 0);
if (--mContinuationCounter == 0) {
mContinuation->post();
-
- if (mSeekReplyID != 0) {
- CHECK(mSeekReply != NULL);
- mSeekReply->setInt32("err", OK);
- mSeekReply->postReply(mSeekReplyID);
- mSeekReplyID = 0;
- mSeekReply.clear();
- }
}
}
break;
@@ -464,8 +452,11 @@
int64_t durationUs;
CHECK(msg->findInt64("durationUs", &durationUs));
- FetcherInfo *info = &mFetcherInfos.editValueFor(uri);
- info->mDurationUs = durationUs;
+ ssize_t index = mFetcherInfos.indexOfKey(uri);
+ if (index >= 0) {
+ FetcherInfo *info = &mFetcherInfos.editValueFor(uri);
+ info->mDurationUs = durationUs;
+ }
break;
}
@@ -513,34 +504,6 @@
break;
}
- case PlaylistFetcher::kWhatTemporarilyDoneFetching:
- {
- AString uri;
- CHECK(msg->findString("uri", &uri));
-
- if (mFetcherInfos.indexOfKey(uri) < 0) {
- ALOGE("couldn't find uri");
- break;
- }
- FetcherInfo *info = &mFetcherInfos.editValueFor(uri);
- info->mIsPrepared = true;
-
- if (mInPreparationPhase) {
- bool allFetchersPrepared = true;
- for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
- if (!mFetcherInfos.valueAt(i).mIsPrepared) {
- allFetchersPrepared = false;
- break;
- }
- }
-
- if (allFetchersPrepared) {
- postPrepared(OK);
- }
- }
- break;
- }
-
case PlaylistFetcher::kWhatStartedAt:
{
int32_t switchGeneration;
@@ -569,19 +532,6 @@
break;
}
- case kWhatCheckBandwidth:
- {
- int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
-
- if (generation != mCheckBandwidthGeneration) {
- break;
- }
-
- onCheckBandwidth(msg);
- break;
- }
-
case kWhatChangeConfiguration:
{
onChangeConfiguration(msg);
@@ -612,15 +562,13 @@
break;
}
- case kWhatCheckSwitchDown:
+ case kWhatPollBuffering:
{
- onCheckSwitchDown();
- break;
- }
-
- case kWhatSwitchDown:
- {
- onSwitchDown();
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+ if (generation == mPollBufferingGeneration) {
+ onPollBuffering();
+ }
break;
}
@@ -691,6 +639,14 @@
return;
}
+ // create looper for fetchers
+ if (mFetcherLooper == NULL) {
+ mFetcherLooper = new ALooper();
+
+ mFetcherLooper->setName("Fetcher");
+ mFetcherLooper->start(false, false);
+ }
+
// We trust the content provider to make a reasonable choice of preferred
// initial bandwidth by listing it first in the variant playlist.
// At startup we really don't have a good estimate on the available
@@ -739,19 +695,20 @@
mPlaylist->pickRandomMediaItems();
changeConfiguration(
0ll /* timeUs */, initialBandwidthIndex, false /* pickTrack */);
+
+ schedulePollBuffering();
}
void LiveSession::finishDisconnect() {
// No reconfiguration is currently pending, make sure none will trigger
// during disconnection either.
- cancelCheckBandwidthEvent();
// Protect mPacketSources from a swapPacketSource race condition through disconnect.
// (finishDisconnect, onFinishDisconnect2)
cancelBandwidthSwitch();
- // cancel switch down monitor
- mSwitchDownMonitor.clear();
+ // cancel buffer polling
+ cancelPollBuffering();
for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
mFetcherInfos.valueAt(i).mFetcher->stopAsync();
@@ -799,7 +756,7 @@
info.mDurationUs = -1ll;
info.mIsPrepared = false;
info.mToBeRemoved = false;
- looper()->registerHandler(info.mFetcher);
+ mFetcherLooper->registerHandler(info.mFetcher);
mFetcherInfos.add(uri, info);
@@ -1201,19 +1158,6 @@
}
}
-bool LiveSession::canSwitchUp() {
- // Allow upwards bandwidth switch when a stream has buffered at least 10 seconds.
- status_t err = OK;
- for (size_t i = 0; i < mPacketSources.size(); ++i) {
- sp<AnotherPacketSource> source = mPacketSources.valueAt(i);
- int64_t dur = source->getBufferedDurationUs(&err);
- if (err == OK && dur > 10000000) {
- return true;
- }
- }
- return false;
-}
-
void LiveSession::changeConfiguration(
int64_t timeUs, size_t bandwidthIndex, bool pickTrack) {
// Protect mPacketSources from a swapPacketSource race condition through reconfiguration.
@@ -1296,14 +1240,6 @@
if (mContinuationCounter == 0) {
msg->post();
-
- if (mSeekReplyID != 0) {
- CHECK(mSeekReply != NULL);
- mSeekReply->setInt32("err", OK);
- mSeekReply->postReply(mSeekReplyID);
- mSeekReplyID = 0;
- mSeekReply.clear();
- }
}
}
@@ -1323,6 +1259,30 @@
// All fetchers are either suspended or have been removed now.
+ // If we're seeking, clear all packet sources before we report
+ // seek complete, to prevent decoder from pulling stale data.
+ int64_t timeUs;
+ CHECK(msg->findInt64("timeUs", &timeUs));
+
+ if (timeUs >= 0) {
+ mLastSeekTimeUs = timeUs;
+
+ for (size_t i = 0; i < mPacketSources.size(); i++) {
+ mPacketSources.editValueAt(i)->clear();
+ }
+
+ mDiscontinuityOffsetTimesUs.clear();
+ mDiscontinuityAbsStartTimesUs.clear();
+
+ if (mSeekReplyID != 0) {
+ CHECK(mSeekReply != NULL);
+ mSeekReply->setInt32("err", OK);
+ mSeekReply->postReply(mSeekReplyID);
+ mSeekReplyID = 0;
+ mSeekReply.clear();
+ }
+ }
+
uint32_t streamMask, resumeMask;
CHECK(msg->findInt32("streamMask", (int32_t *)&streamMask));
CHECK(msg->findInt32("resumeMask", (int32_t *)&resumeMask));
@@ -1428,19 +1388,8 @@
for (size_t j = 0; j < kMaxStreams; ++j) {
if ((resumeMask & indexToType(j)) && uri == mStreams[j].mUri) {
sources[j] = mPacketSources.valueFor(indexToType(j));
-
- if (j != kSubtitleIndex) {
- ALOGV("queueing dummy discontinuity for stream type %d", indexToType(j));
- sp<AnotherPacketSource> discontinuityQueue;
- discontinuityQueue = mDiscontinuities.valueFor(indexToType(j));
- discontinuityQueue->queueDiscontinuity(
- ATSParser::DISCONTINUITY_NONE,
- NULL,
- true);
- }
}
}
-
FetcherInfo &info = mFetcherInfos.editValueAt(i);
if (sources[kAudioIndex] != NULL || sources[kVideoIndex] != NULL
|| sources[kSubtitleIndex] != NULL) {
@@ -1486,15 +1435,7 @@
sources[j] = mPacketSources.valueFor(indexToType(j));
if (timeUs >= 0) {
- sources[j]->clear();
startTimeUs = timeUs;
-
- sp<AnotherPacketSource> discontinuityQueue;
- sp<AMessage> extra = new AMessage;
- extra->setInt64("timeUs", timeUs);
- discontinuityQueue = mDiscontinuities.valueFor(indexToType(j));
- discontinuityQueue->queueDiscontinuity(
- ATSParser::DISCONTINUITY_TIME, extra, true);
} else {
int32_t type;
sp<AMessage> meta;
@@ -1532,9 +1473,10 @@
if (j == kSubtitleIndex) {
break;
}
- sp<AnotherPacketSource> discontinuityQueue;
- discontinuityQueue = mDiscontinuities.valueFor(indexToType(j));
- discontinuityQueue->queueDiscontinuity(
+
+ ALOGV("stream[%d]: queue format change", j);
+
+ sources[j]->queueDiscontinuity(
ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true);
} else {
// adapting, queue discontinuities after resume
@@ -1564,9 +1506,6 @@
// All fetchers have now been started, the configuration change
// has completed.
- cancelCheckBandwidthEvent();
- scheduleCheckBandwidthEvent();
-
ALOGV("XXX configuration change completed.");
mReconfigurationInProgress = false;
if (switching) {
@@ -1623,47 +1562,35 @@
tryToFinishBandwidthSwitch();
}
-void LiveSession::onCheckSwitchDown() {
- if (mSwitchDownMonitor == NULL) {
- return;
- }
+void LiveSession::schedulePollBuffering() {
+ sp<AMessage> msg = new AMessage(kWhatPollBuffering, this);
+ msg->setInt32("generation", mPollBufferingGeneration);
+ msg->post(1000000ll);
+}
- if (mSwitchInProgress || mReconfigurationInProgress) {
- ALOGV("Switch/Reconfig in progress, defer switch down");
- mSwitchDownMonitor->post(1000000ll);
- return;
- }
+void LiveSession::cancelPollBuffering() {
+ ++mPollBufferingGeneration;
+}
- for (size_t i = 0; i < kMaxStreams; ++i) {
- int32_t targetDuration;
- sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(indexToType(i));
- sp<AMessage> meta = packetSource->getLatestDequeuedMeta();
+void LiveSession::onPollBuffering() {
+ ALOGV("onPollBuffering: mSwitchInProgress %d, mReconfigurationInProgress %d, "
+ "mInPreparationPhase %d, mStreamMask 0x%x",
+ mSwitchInProgress, mReconfigurationInProgress,
+ mInPreparationPhase, mStreamMask);
- if (meta != NULL && meta->findInt32("targetDuration", &targetDuration) ) {
- int64_t bufferedDurationUs = packetSource->getEstimatedDurationUs();
- int64_t targetDurationUs = targetDuration * 1000000ll;
+ bool low, mid, high;
+ if (checkBuffering(low, mid, high)) {
+ if (mInPreparationPhase && mid) {
+ postPrepared(OK);
+ }
- if (bufferedDurationUs < targetDurationUs / 3) {
- (new AMessage(kWhatSwitchDown, this))->post();
- break;
- }
+ // don't switch before we report prepared
+ if (!mInPreparationPhase && (low || high)) {
+ switchBandwidthIfNeeded(high);
}
}
- mSwitchDownMonitor->post(1000000ll);
-}
-
-void LiveSession::onSwitchDown() {
- if (mReconfigurationInProgress || mSwitchInProgress || mCurBandwidthIndex == 0) {
- return;
- }
-
- ssize_t bandwidthIndex = getBandwidthIndex();
- if (bandwidthIndex < mCurBandwidthIndex) {
- changeConfiguration(-1, bandwidthIndex, false);
- return;
- }
-
+ schedulePollBuffering();
}
// Mark switch done when:
@@ -1688,16 +1615,6 @@
}
}
-void LiveSession::scheduleCheckBandwidthEvent() {
- sp<AMessage> msg = new AMessage(kWhatCheckBandwidth, this);
- msg->setInt32("generation", mCheckBandwidthGeneration);
- msg->post(10000000ll);
-}
-
-void LiveSession::cancelCheckBandwidthEvent() {
- ++mCheckBandwidthGeneration;
-}
-
void LiveSession::cancelBandwidthSwitch() {
Mutex::Autolock lock(mSwapMutex);
mSwitchGeneration++;
@@ -1727,33 +1644,69 @@
}
}
-bool LiveSession::canSwitchBandwidthTo(size_t bandwidthIndex) {
- if (mReconfigurationInProgress || mSwitchInProgress) {
+bool LiveSession::checkBuffering(bool &low, bool &mid, bool &high) {
+ low = mid = high = false;
+
+ if (mSwitchInProgress || mReconfigurationInProgress) {
+ ALOGV("Switch/Reconfig in progress, defer buffer polling");
return false;
}
- if (mCurBandwidthIndex < 0) {
+ // TODO: Fine tune low/high mark.
+ // We also need to pause playback if buffering is too low.
+ // Currently during underflow, we depend on decoder to starve
+ // to pause, but A/V could have different buffering left,
+ // they're not paused together.
+ // TODO: Report buffering level to NuPlayer for BUFFERING_UPDATE
+
+ // Switch down if any of the fetchers are below low mark;
+ // Switch up if all of the fetchers are over high mark.
+ size_t activeCount, lowCount, midCount, highCount;
+ activeCount = lowCount = midCount = highCount = 0;
+ for (size_t i = 0; i < mPacketSources.size(); ++i) {
+ // we don't check subtitles for buffering level
+ if (!(mStreamMask & mPacketSources.keyAt(i)
+ & (STREAMTYPE_AUDIO | STREAMTYPE_VIDEO))) {
+ continue;
+ }
+ // ignore streams that never had any packet queued.
+ // (it's possible that the variant only has audio or video)
+ sp<AMessage> meta = mPacketSources[i]->getLatestEnqueuedMeta();
+ if (meta == NULL) {
+ continue;
+ }
+
+ ++activeCount;
+ int64_t bufferedDurationUs =
+ mPacketSources[i]->getEstimatedDurationUs();
+ ALOGV("source[%d]: buffered %lld us", i, bufferedDurationUs);
+ if (bufferedDurationUs < kLowWaterMark) {
+ ++lowCount;
+ break;
+ } else if (bufferedDurationUs > kHighWaterMark) {
+ ++midCount;
+ ++highCount;
+ } else if (bufferedDurationUs > kMidWaterMark) {
+ ++midCount;
+ }
+ }
+
+ if (activeCount > 0) {
+ high = (highCount == activeCount);
+ mid = (midCount == activeCount);
+ low = (lowCount > 0);
return true;
}
- if (bandwidthIndex == (size_t)mCurBandwidthIndex) {
- return false;
- } else if (bandwidthIndex > (size_t)mCurBandwidthIndex) {
- return canSwitchUp();
- } else {
- return true;
- }
+ return false;
}
-void LiveSession::onCheckBandwidth(const sp<AMessage> &msg) {
- size_t bandwidthIndex = getBandwidthIndex();
- if (canSwitchBandwidthTo(bandwidthIndex)) {
- changeConfiguration(-1ll /* timeUs */, bandwidthIndex);
- } else {
- // Come back and check again 10 seconds later in case there is nothing to do now.
- // If we DO change configuration, once that completes it'll schedule a new
- // check bandwidth event with an incremented mCheckBandwidthGeneration.
- msg->post(10000000ll);
+void LiveSession::switchBandwidthIfNeeded(bool canSwitchUp) {
+ ssize_t bandwidthIndex = getBandwidthIndex();
+
+ if ((canSwitchUp && bandwidthIndex > mCurBandwidthIndex)
+ || (!canSwitchUp && bandwidthIndex < mCurBandwidthIndex)) {
+ changeConfiguration(-1, bandwidthIndex, false);
}
}
@@ -1771,10 +1724,8 @@
notify->post();
mInPreparationPhase = false;
-
- mSwitchDownMonitor = new AMessage(kWhatCheckSwitchDown, this);
- mSwitchDownMonitor->post();
}
+
} // namespace android
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index 0d7c5e0..3b0a9a4 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -40,10 +40,6 @@
// Don't log any URLs.
kFlagIncognito = 1,
};
- LiveSession(
- const sp<AMessage> ¬ify,
- uint32_t flags,
- const sp<IMediaHTTPService> &httpService);
enum StreamIndex {
kAudioIndex = 0,
@@ -57,6 +53,12 @@
STREAMTYPE_VIDEO = 1 << kVideoIndex,
STREAMTYPE_SUBTITLES = 1 << kSubtitleIndex,
};
+
+ LiveSession(
+ const sp<AMessage> ¬ify,
+ uint32_t flags,
+ const sp<IMediaHTTPService> &httpService);
+
status_t dequeueAccessUnit(StreamType stream, sp<ABuffer> *accessUnit);
status_t getStreamFormat(StreamType stream, sp<AMessage> *format);
@@ -110,11 +112,13 @@
kWhatChangeConfiguration3 = 'chC3',
kWhatFinishDisconnect2 = 'fin2',
kWhatSwapped = 'swap',
- kWhatCheckSwitchDown = 'ckSD',
- kWhatSwitchDown = 'sDwn',
+ kWhatPollBuffering = 'poll',
};
static const size_t kBandwidthHistoryBytes;
+ static const int64_t kHighWaterMark;
+ static const int64_t kMidWaterMark;
+ static const int64_t kLowWaterMark;
struct BandwidthItem {
size_t mPlaylistIndex;
@@ -169,6 +173,7 @@
sp<M3UParser> mPlaylist;
+ sp<ALooper> mFetcherLooper;
KeyedVector<AString, FetcherInfo> mFetcherInfos;
uint32_t mStreamMask;
@@ -181,7 +186,6 @@
// we use this to track reconfiguration progress.
uint32_t mSwapMask;
- KeyedVector<StreamType, sp<AnotherPacketSource> > mDiscontinuities;
KeyedVector<StreamType, sp<AnotherPacketSource> > mPacketSources;
// A second set of packet sources that buffer content for the variant we're switching to.
KeyedVector<StreamType, sp<AnotherPacketSource> > mPacketSources2;
@@ -210,10 +214,11 @@
bool mFirstTimeUsValid;
int64_t mFirstTimeUs;
int64_t mLastSeekTimeUs;
- sp<AMessage> mSwitchDownMonitor;
KeyedVector<size_t, int64_t> mDiscontinuityAbsStartTimesUs;
KeyedVector<size_t, int64_t> mDiscontinuityOffsetTimesUs;
+ int32_t mPollBufferingGeneration;
+
sp<PlaylistFetcher> addFetcher(const char *uri);
void onConnect(const sp<AMessage> &msg);
@@ -257,27 +262,24 @@
void onChangeConfiguration2(const sp<AMessage> &msg);
void onChangeConfiguration3(const sp<AMessage> &msg);
void onSwapped(const sp<AMessage> &msg);
- void onCheckSwitchDown();
- void onSwitchDown();
void tryToFinishBandwidthSwitch();
- void scheduleCheckBandwidthEvent();
- void cancelCheckBandwidthEvent();
-
// cancelBandwidthSwitch is atomic wrt swapPacketSource; call it to prevent packet sources
// from being swapped out on stale discontinuities while manipulating
// mPacketSources/mPacketSources2.
void cancelBandwidthSwitch();
- bool canSwitchBandwidthTo(size_t bandwidthIndex);
- void onCheckBandwidth(const sp<AMessage> &msg);
+ void schedulePollBuffering();
+ void cancelPollBuffering();
+ void onPollBuffering();
+ bool checkBuffering(bool &low, bool &mid, bool &high);
+ void switchBandwidthIfNeeded(bool canSwitchUp);
void finishDisconnect();
void postPrepared(status_t err);
void swapPacketSource(StreamType stream);
- bool canSwitchUp();
DISALLOW_EVIL_CONSTRUCTORS(LiveSession);
};
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 4ccbf76..3710686 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -49,6 +49,7 @@
// static
const int64_t PlaylistFetcher::kMinBufferedDurationUs = 10000000ll;
const int64_t PlaylistFetcher::kMaxMonitorDelayUs = 3000000ll;
+const int64_t PlaylistFetcher::kFetcherResumeThreshold = 100000ll;
// LCM of 188 (size of a TS packet) & 1k works well
const int32_t PlaylistFetcher::kDownloadBlockSize = 47 * 1024;
const int32_t PlaylistFetcher::kNumSkipFrames = 5;
@@ -535,12 +536,19 @@
sp<AMessage> params;
CHECK(msg->findMessage("params", ¶ms));
- bool stop = false;
+ size_t stopCount = 0;
for (size_t i = 0; i < mPacketSources.size(); i++) {
sp<AnotherPacketSource> packetSource = mPacketSources.valueAt(i);
const char *stopKey;
int streamType = mPacketSources.keyAt(i);
+
+ if (streamType == LiveSession::STREAMTYPE_SUBTITLES) {
+ // the subtitle track can always be stopped
+ ++stopCount;
+ continue;
+ }
+
switch (streamType) {
case LiveSession::STREAMTYPE_VIDEO:
stopKey = "timeUsVideo";
@@ -550,15 +558,11 @@
stopKey = "timeUsAudio";
break;
- case LiveSession::STREAMTYPE_SUBTITLES:
- stopKey = "timeUsSubtitle";
- break;
-
default:
TRESPASS();
}
- // Don't resume if we would stop within a resume threshold.
+ // check if this stream has too little data left to be resumed
int32_t discontinuitySeq;
int64_t latestTimeUs = 0, stopTimeUs = 0;
sp<AMessage> latestMeta = packetSource->getLatestEnqueuedMeta();
@@ -567,12 +571,13 @@
&& discontinuitySeq == mDiscontinuitySeq
&& latestMeta->findInt64("timeUs", &latestTimeUs)
&& params->findInt64(stopKey, &stopTimeUs)
- && stopTimeUs - latestTimeUs < resumeThreshold(latestMeta)) {
- stop = true;
+ && stopTimeUs - latestTimeUs < kFetcherResumeThreshold) {
+ ++stopCount;
}
}
- if (stop) {
+ // Don't resume if all streams are within a resume threshold
+ if (stopCount == mPacketSources.size()) {
for (size_t i = 0; i < mPacketSources.size(); i++) {
mPacketSources.valueAt(i)->queueAccessUnit(mSession->createFormatChangeBuffer());
}
@@ -581,7 +586,7 @@
}
mStopParams = params;
- postMonitorQueue();
+ onDownloadNext();
return OK;
}
@@ -660,9 +665,6 @@
ALOGV("prepared, buffered=%" PRId64 " > %" PRId64 "",
bufferedDurationUs, targetDurationUs);
- sp<AMessage> msg = mNotify->dup();
- msg->setInt32("what", kWhatTemporarilyDoneFetching);
- msg->post();
}
if (finalResult == OK && downloadMore) {
@@ -676,11 +678,6 @@
msg->post(1000l);
} else {
// Nothing to do yet, try again in a second.
-
- sp<AMessage> msg = mNotify->dup();
- msg->setInt32("what", kWhatTemporarilyDoneFetching);
- msg->post();
-
int64_t delayUs = mPrepared ? kMaxMonitorDelayUs : targetDurationUs / 2;
ALOGV("pausing for %" PRId64 ", buffered=%" PRId64 " > %" PRId64 "",
delayUs, bufferedDurationUs, durationToBufferUs);
@@ -1687,33 +1684,4 @@
msg->post();
}
-int64_t PlaylistFetcher::resumeThreshold(const sp<AMessage> &msg) {
- int64_t durationUs;
- if (msg->findInt64("durationUs", &durationUs) && durationUs > 0) {
- return kNumSkipFrames * durationUs;
- }
-
- sp<RefBase> obj;
- msg->findObject("format", &obj);
- MetaData *format = static_cast<MetaData *>(obj.get());
-
- const char *mime;
- CHECK(format->findCString(kKeyMIMEType, &mime));
- bool audio = !strncasecmp(mime, "audio/", 6);
- if (audio) {
- // Assumes 1000 samples per frame.
- int32_t sampleRate;
- CHECK(format->findInt32(kKeySampleRate, &sampleRate));
- return kNumSkipFrames /* frames */ * 1000 /* samples */
- * (1000000 / sampleRate) /* sample duration (us) */;
- } else {
- int32_t frameRate;
- if (format->findInt32(kKeyFrameRate, &frameRate) && frameRate > 0) {
- return kNumSkipFrames * (1000000 / frameRate);
- }
- }
-
- return 500000ll;
-}
-
} // namespace android
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index 4e15f85..2f11949 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -36,6 +36,7 @@
struct PlaylistFetcher : public AHandler {
static const int64_t kMinBufferedDurationUs;
static const int32_t kDownloadBlockSize;
+ static const int64_t kFetcherResumeThreshold;
enum {
kWhatStarted,
@@ -43,7 +44,6 @@
kWhatStopped,
kWhatError,
kWhatDurationUpdate,
- kWhatTemporarilyDoneFetching,
kWhatPrepared,
kWhatPreparationFailed,
kWhatStartedAt,
@@ -212,10 +212,6 @@
void updateDuration();
- // Before resuming a fetcher in onResume, check the remaining duration is longer than that
- // returned by resumeThreshold.
- int64_t resumeThreshold(const sp<AMessage> &msg);
-
DISALLOW_EVIL_CONSTRUCTORS(PlaylistFetcher);
};
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 934e2e5..6786506 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -35,6 +35,7 @@
#include <media/stagefright/Utils.h>
#include <media/IStreamSource.h>
#include <utils/KeyedVector.h>
+#include <utils/Vector.h>
#include <inttypes.h>
@@ -86,6 +87,11 @@
}
private:
+ struct StreamInfo {
+ unsigned mType;
+ unsigned mPID;
+ };
+
ATSParser *mParser;
unsigned mProgramNumber;
unsigned mProgramMapPID;
@@ -96,6 +102,7 @@
status_t parseProgramMap(ABitReader *br);
int64_t recoverPTS(uint64_t PTS_33bit);
+ bool switchPIDs(const Vector<StreamInfo> &infos);
DISALLOW_EVIL_CONSTRUCTORS(Program);
};
@@ -185,7 +192,7 @@
mProgramMapPID(programMapPID),
mFirstPTSValid(false),
mFirstPTS(0),
- mLastRecoveredPTS(0) {
+ mLastRecoveredPTS(-1ll) {
ALOGV("new program number %u", programNumber);
}
@@ -240,10 +247,71 @@
}
}
-struct StreamInfo {
- unsigned mType;
- unsigned mPID;
-};
+bool ATSParser::Program::switchPIDs(const Vector<StreamInfo> &infos) {
+ bool success = false;
+
+ if (mStreams.size() == infos.size()) {
+ // build type->PIDs map for old and new mapping
+ size_t i;
+ KeyedVector<int32_t, Vector<int32_t> > oldType2PIDs, newType2PIDs;
+ for (i = 0; i < mStreams.size(); ++i) {
+ ssize_t index = oldType2PIDs.indexOfKey(mStreams[i]->type());
+ if (index < 0) {
+ oldType2PIDs.add(mStreams[i]->type(), Vector<int32_t>());
+ }
+ oldType2PIDs.editValueFor(mStreams[i]->type()).push_back(mStreams[i]->pid());
+ }
+ for (i = 0; i < infos.size(); ++i) {
+ ssize_t index = newType2PIDs.indexOfKey(infos[i].mType);
+ if (index < 0) {
+ newType2PIDs.add(infos[i].mType, Vector<int32_t>());
+ }
+ newType2PIDs.editValueFor(infos[i].mType).push_back(infos[i].mPID);
+ }
+
+ // we can recover if the number of streams for each type hasn't changed
+ if (oldType2PIDs.size() == newType2PIDs.size()) {
+ success = true;
+ for (i = 0; i < oldType2PIDs.size(); ++i) {
+ // KeyedVector is sorted, we just compare key and size of each index
+ if (oldType2PIDs.keyAt(i) != newType2PIDs.keyAt(i)
+ || oldType2PIDs[i].size() != newType2PIDs[i].size()) {
+ success = false;
+ break;
+ }
+ }
+ }
+
+ if (success) {
+ // save current streams to temp
+ KeyedVector<int32_t, sp<Stream> > temp;
+ for (i = 0; i < mStreams.size(); ++i) {
+ temp.add(mStreams.keyAt(i), mStreams.editValueAt(i));
+ }
+
+ mStreams.clear();
+ for (i = 0; i < temp.size(); ++i) {
+ // The two checks below shouldn't happen,
+ // we already checked above the stream count matches
+ ssize_t index = newType2PIDs.indexOfKey(temp[i]->type());
+ CHECK(index >= 0);
+ Vector<int32_t> &newPIDs = newType2PIDs.editValueAt(index);
+ CHECK(newPIDs.size() > 0);
+
+ // get the next PID for temp[i]->type() in the new PID map
+ Vector<int32_t>::iterator it = newPIDs.begin();
+
+ // change the PID of the stream, and add it back
+ temp.editValueAt(i)->setPID(*it);
+ mStreams.add(temp[i]->pid(), temp.editValueAt(i));
+
+ // removed the used PID
+ newPIDs.erase(it);
+ }
+ }
+ }
+ return success;
+}
status_t ATSParser::Program::parseProgramMap(ABitReader *br) {
unsigned table_id = br->getBits(8);
@@ -372,39 +440,8 @@
}
#endif
- // The only case we can recover from is if we have two streams
- // and they switched PIDs.
-
- bool success = false;
-
- if (mStreams.size() == 2 && infos.size() == 2) {
- const StreamInfo &info1 = infos.itemAt(0);
- const StreamInfo &info2 = infos.itemAt(1);
-
- sp<Stream> s1 = mStreams.editValueAt(0);
- sp<Stream> s2 = mStreams.editValueAt(1);
-
- bool caseA =
- info1.mPID == s1->pid() && info1.mType == s2->type()
- && info2.mPID == s2->pid() && info2.mType == s1->type();
-
- bool caseB =
- info1.mPID == s2->pid() && info1.mType == s1->type()
- && info2.mPID == s1->pid() && info2.mType == s2->type();
-
- if (caseA || caseB) {
- unsigned pid1 = s1->pid();
- unsigned pid2 = s2->pid();
- s1->setPID(pid2);
- s2->setPID(pid1);
-
- mStreams.clear();
- mStreams.add(s1->pid(), s1);
- mStreams.add(s2->pid(), s2);
-
- success = true;
- }
- }
+ // we can recover if number of streams for each type remain the same
+ bool success = switchPIDs(infos);
if (!success) {
ALOGI("Stream PIDs changed and we cannot recover.");
@@ -433,14 +470,25 @@
// reasonable amount of time. To handle the wrap-around, use fancy math
// to get an extended PTS that is within [-0xffffffff, 0xffffffff]
// of the latest recovered PTS.
- mLastRecoveredPTS = static_cast<int64_t>(
- ((mLastRecoveredPTS - PTS_33bit + 0x100000000ll)
- & 0xfffffffe00000000ull) | PTS_33bit);
+ if (mLastRecoveredPTS < 0ll) {
+ // Use the original 33bit number for 1st frame, the reason is that
+ // if 1st frame wraps to negative that's far away from 0, we could
+ // never start. Only start wrapping around from 2nd frame.
+ mLastRecoveredPTS = static_cast<int64_t>(PTS_33bit);
+ } else {
+ mLastRecoveredPTS = static_cast<int64_t>(
+ ((mLastRecoveredPTS - PTS_33bit + 0x100000000ll)
+ & 0xfffffffe00000000ull) | PTS_33bit);
+ // We start from 0, but recovered PTS could be slightly below 0.
+ // Clamp it to 0 as rest of the pipeline doesn't take negative pts.
+ // (eg. video is read first and starts at 0, but audio starts at 0xfffffff0)
+ if (mLastRecoveredPTS < 0ll) {
+ ALOGI("Clamping negative recovered PTS (%" PRId64 ") to 0", mLastRecoveredPTS);
+ mLastRecoveredPTS = 0ll;
+ }
+ }
- // We start from 0, but recovered PTS could be slightly below 0.
- // Clamp it to 0 as rest of the pipeline doesn't take negative pts.
- // (eg. video is read first and starts at 0, but audio starts at 0xfffffff0)
- return mLastRecoveredPTS < 0ll ? 0ll : mLastRecoveredPTS;
+ return mLastRecoveredPTS;
}
sp<MediaSource> ATSParser::Program::getSource(SourceType type) {
@@ -1118,7 +1166,8 @@
if (payload_unit_start_indicator) {
if (!section->isEmpty()) {
- return ERROR_UNSUPPORTED;
+ ALOGW("parsePID encounters payload_unit_start_indicator when section is not empty");
+ section->clear();
}
unsigned skip = br->getBits(8);
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index d81da3f..477cfc6 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -28,6 +28,7 @@
#include <media/hardware/MetadataBufferType.h>
#include <ui/GraphicBuffer.h>
+#include <gui/BufferItem.h>
#include <inttypes.h>
#include "FrameDropper.h"
@@ -360,7 +361,7 @@
mSuspended = true;
while (mNumFramesAvailable > 0) {
- BufferQueue::BufferItem item;
+ BufferItem item;
status_t err = mConsumer->acquireBuffer(&item, 0);
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
@@ -410,7 +411,7 @@
ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%zu",
mNumFramesAvailable);
- BufferQueue::BufferItem item;
+ BufferItem item;
status_t err = mConsumer->acquireBuffer(&item, 0);
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
// shouldn't happen
@@ -503,7 +504,7 @@
return false;
}
- BufferQueue::BufferItem item;
+ BufferItem item;
item.mBuf = mLatestBufferId;
item.mFrameNumber = mLatestBufferFrameNum;
item.mTimestamp = mRepeatLastFrameTimestamp;
@@ -534,7 +535,7 @@
}
void GraphicBufferSource::setLatestBuffer_l(
- const BufferQueue::BufferItem &item, bool dropped) {
+ const BufferItem &item, bool dropped) {
ALOGV("setLatestBuffer_l");
if (mLatestBufferId >= 0) {
@@ -590,7 +591,7 @@
return OK;
}
-int64_t GraphicBufferSource::getTimestamp(const BufferQueue::BufferItem &item) {
+int64_t GraphicBufferSource::getTimestamp(const BufferItem &item) {
int64_t timeUs = item.mTimestamp / 1000;
if (mTimePerCaptureUs > 0ll) {
@@ -651,7 +652,7 @@
}
status_t GraphicBufferSource::submitBuffer_l(
- const BufferQueue::BufferItem &item, int cbi) {
+ const BufferItem &item, int cbi) {
ALOGV("submitBuffer_l cbi=%d", cbi);
int64_t timeUs = getTimestamp(item);
@@ -777,7 +778,7 @@
ALOGV("onFrameAvailable: suspended, ignoring frame");
}
- BufferQueue::BufferItem item;
+ BufferItem item;
status_t err = mConsumer->acquireBuffer(&item, 0);
if (err == OK) {
// If this is the first time we're seeing this buffer, add it to our
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
index ce3881e..1067472 100644
--- a/media/libstagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -192,15 +192,15 @@
// Marks the mCodecBuffers entry as in-use, copies the GraphicBuffer
// reference into the codec buffer, and submits the data to the codec.
- status_t submitBuffer_l(const BufferQueue::BufferItem &item, int cbi);
+ status_t submitBuffer_l(const BufferItem &item, int cbi);
// Submits an empty buffer, with the EOS flag set. Returns without
// doing anything if we don't have a codec buffer available.
void submitEndOfInputStream_l();
- void setLatestBuffer_l(const BufferQueue::BufferItem &item, bool dropped);
+ void setLatestBuffer_l(const BufferItem &item, bool dropped);
bool repeatLatestBuffer_l();
- int64_t getTimestamp(const BufferQueue::BufferItem &item);
+ int64_t getTimestamp(const BufferItem &item);
// Lock, covers all member variables.
mutable Mutex mMutex;
diff --git a/services/audiopolicy/managerdefault/ConfigParsingUtils.h b/services/audiopolicy/managerdefault/ConfigParsingUtils.h
index 7969661..b2d9763 100644
--- a/services/audiopolicy/managerdefault/ConfigParsingUtils.h
+++ b/services/audiopolicy/managerdefault/ConfigParsingUtils.h
@@ -26,7 +26,9 @@
};
#define STRING_TO_ENUM(string) { #string, string }
+#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
const StringToEnum sDeviceNameToEnumTable[] = {
STRING_TO_ENUM(AUDIO_DEVICE_OUT_EARPIECE),