Merge "Remove private ANativeWindow_fromSurfaceTexture"
diff --git a/src/android/AacBqToPcmCbRenderer.cpp b/src/android/AacBqToPcmCbRenderer.cpp
index 44aaafc..194662b 100644
--- a/src/android/AacBqToPcmCbRenderer.cpp
+++ b/src/android/AacBqToPcmCbRenderer.cpp
@@ -108,12 +108,12 @@
}
//--------------------------------------------------------------------------------------------------
-AacBqToPcmCbRenderer::AacBqToPcmCbRenderer(const AudioPlayback_Parameters* params) :
+AacBqToPcmCbRenderer::AacBqToPcmCbRenderer(const AudioPlayback_Parameters* params,
+ IAndroidBufferQueue *androidBufferQueue) :
AudioToCbRenderer(params),
- mBqSource(0)
+ mBqSource(new BufferQueueSource(androidBufferQueue))
{
SL_LOGD("AacBqToPcmCbRenderer::AacBqToPcmCbRenderer()");
-
}
@@ -124,20 +124,6 @@
//--------------------------------------------------
-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()");
@@ -157,23 +143,7 @@
mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_CHANNELMASK] = UNKNOWN_CHANNELMASK;
}
- 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;
- }
+ sp<MediaExtractor> extractor = new AacAdtsExtractor(mBqSource);
// only decoding a single track of data
const size_t kTrackToDecode = 0;
@@ -235,7 +205,7 @@
//---------------------------------
// The data source, and audio source (a decoder) are ready to be used
- mDataSource = dataSource;
+ mDataSource = mBqSource;
mAudioSource = source;
mAudioSourceStarted = true;
diff --git a/src/android/AudioPlayer_to_android.cpp b/src/android/AudioPlayer_to_android.cpp
index d94c4da..e79a161 100644
--- a/src/android/AudioPlayer_to_android.cpp
+++ b/src/android/AudioPlayer_to_android.cpp
@@ -713,7 +713,8 @@
ap->mAndroidObjState = ANDROID_READY;
if (PLAYER_SUCCESS == data1) {
- // Most of successful prepare completion is handled by a subclass.
+ // Most of successful prepare completion for ap->mAPlayer
+ // is handled by GenericPlayer and its subclasses.
} else {
// SfPlayer prepare() failed prefetching, there is no event in SLPrefetchStatus to
// indicate a prefetch error, so we signal it by sending simultaneously two events:
@@ -839,7 +840,7 @@
break;
case android::GenericPlayer::kEventErrorAfterPrepare: {
- SL_LOGI("kEventErrorAfterPrepare");
+ SL_LOGV("kEventErrorAfterPrepare");
// assume no callback
slPrefetchCallback callback = NULL;
@@ -847,12 +848,10 @@
object_lock_exclusive(&ap->mObject);
if (IsInterfaceInitialized(&ap->mObject, MPH_PREFETCHSTATUS)) {
- SL_LOGI("inited");
ap->mPrefetchStatus.mLevel = 0;
ap->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW;
if (!(~ap->mPrefetchStatus.mCallbackEventsMask &
(SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE))) {
- SL_LOGI("enabled");
callback = ap->mPrefetchStatus.mCallback;
callbackPContext = ap->mPrefetchStatus.mContext;
}
@@ -863,7 +862,6 @@
SL_LOGE("Error after prepare: %d", data1);
// callback with no lock held
- SL_LOGE("callback=%p context=%p", callback, callbackPContext);
if (NULL != callback) {
(*callback)(&ap->mPrefetchStatus.mItf, callbackPContext,
SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE);
@@ -1521,7 +1519,8 @@
//-----------------------------------
// AacBqToPcmCbRenderer
case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE: {
- android::AacBqToPcmCbRenderer* bqtobq = new android::AacBqToPcmCbRenderer(&app);
+ android::AacBqToPcmCbRenderer* bqtobq = new android::AacBqToPcmCbRenderer(&app,
+ &pAudioPlayer->mAndroidBufferQueue);
// configures the callback for the sink buffer queue
bqtobq->setDataPushListener(adecoder_writeToBufferQueue, pAudioPlayer);
pAudioPlayer->mAPlayer = bqtobq;
@@ -2146,30 +2145,6 @@
//-----------------------------------------------------------------------------
-SLresult android_audioPlayer_androidBufferQueue_registerCallback_l(CAudioPlayer *ap) {
- SLresult result = SL_RESULT_SUCCESS;
- assert(ap->mAPlayer != 0);
- // FIXME investigate why these two cases are not handled symmetrically any more
- switch (ap->mAndroidObjType) {
- case AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: {
- } 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;
-}
-
-//-----------------------------------------------------------------------------
void android_audioPlayer_androidBufferQueue_clear_l(CAudioPlayer *ap) {
switch (ap->mAndroidObjType) {
case AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE:
diff --git a/src/android/AudioPlayer_to_android.h b/src/android/AudioPlayer_to_android.h
index 96855e6..430b622 100644
--- a/src/android/AudioPlayer_to_android.h
+++ b/src/android/AudioPlayer_to_android.h
@@ -138,9 +138,6 @@
* Android Buffer Queue
****************************/
/* must be called with a lock on pAudioPlayer->mThis */
-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 */
extern void android_audioPlayer_androidBufferQueue_onRefilled_l(CAudioPlayer *pAudioPlayer);
diff --git a/src/android/BufferQueueSource.cpp b/src/android/BufferQueueSource.cpp
index 1ad2cad..9257f53 100644
--- a/src/android/BufferQueueSource.cpp
+++ b/src/android/BufferQueueSource.cpp
@@ -36,17 +36,11 @@
};
-BufferQueueSource::BufferQueueSource(const void* user, void *context, const void *caller) :
- mAndroidBufferQueueSource(NULL),
+BufferQueueSource::BufferQueueSource(IAndroidBufferQueue *androidBufferQueue) :
+ mAndroidBufferQueueSource(androidBufferQueue),
mStreamToBqOffset(0),
mEosReached(false)
{
- if (NULL != user) {
- mAndroidBufferQueueSource = &((CAudioPlayer*)user)->mAndroidBufferQueue;
- } else {
- SL_LOGE("Can't create BufferQueueSource with NULL user");
- }
-
}
diff --git a/src/android/BufferQueueSource.h b/src/android/BufferQueueSource.h
index c91b7b1..c362df2 100644
--- a/src/android/BufferQueueSource.h
+++ b/src/android/BufferQueueSource.h
@@ -33,7 +33,7 @@
// 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);
+ BufferQueueSource(IAndroidBufferQueue *androidBufferQueue);
virtual status_t initCheck() const;
@@ -45,7 +45,7 @@
private:
// the Android Buffer Queue from which data is consumed
- IAndroidBufferQueue* mAndroidBufferQueueSource;
+ IAndroidBufferQueue* const 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
diff --git a/src/android/MediaPlayer_to_android.cpp b/src/android/MediaPlayer_to_android.cpp
index 81653d7..692aa5c 100644
--- a/src/android/MediaPlayer_to_android.cpp
+++ b/src/android/MediaPlayer_to_android.cpp
@@ -52,36 +52,45 @@
switch(event) {
case android::GenericPlayer::kEventPrepared: {
-
- SL_LOGV("Received AVPlayer::kEventPrepared for CMediaPlayer %p", mp);
+ SL_LOGV("Received GenericPlayer::kEventPrepared for CMediaPlayer %p", mp);
// assume no callback
slPrefetchCallback callback = NULL;
- void* callbackPContext = NULL;
+ void* callbackPContext;
+ XAuint32 events;
object_lock_exclusive(&mp->mObject);
- // mark object as prepared; same state is used for successfully or unsuccessful prepare
+
+ // mark object as prepared; same state is used for successful or unsuccessful prepare
+ assert(mp->mAndroidObjState == ANDROID_PREPARING);
mp->mAndroidObjState = ANDROID_READY;
- // AVPlayer prepare() failed prefetching, there is no event in XAPrefetchStatus to
- // indicate a prefetch error, so we signal it by sending simulataneously two events:
- // - SL_PREFETCHEVENT_FILLLEVELCHANGE with a level of 0
- // - SL_PREFETCHEVENT_STATUSCHANGE with a status of SL_PREFETCHSTATUS_UNDERFLOW
- if (PLAYER_SUCCESS != data1 && IsInterfaceInitialized(&mp->mObject, MPH_XAPREFETCHSTATUS)) {
- mp->mPrefetchStatus.mLevel = 0;
- mp->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW;
- if (!(~mp->mPrefetchStatus.mCallbackEventsMask &
- (SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE))) {
- callback = mp->mPrefetchStatus.mCallback;
- callbackPContext = mp->mPrefetchStatus.mContext;
+ if (PLAYER_SUCCESS == data1) {
+ // Most of successful prepare completion for mp->mAVPlayer
+ // is handled by GenericPlayer and its subclasses.
+ } else {
+ // AVPlayer prepare() failed prefetching, there is no event in XAPrefetchStatus to
+ // indicate a prefetch error, so we signal it by sending simultaneously two events:
+ // - SL_PREFETCHEVENT_FILLLEVELCHANGE with a level of 0
+ // - SL_PREFETCHEVENT_STATUSCHANGE with a status of SL_PREFETCHSTATUS_UNDERFLOW
+ SL_LOGE(ERROR_PLAYER_PREFETCH_d, data1);
+ if (IsInterfaceInitialized(&mp->mObject, MPH_XAPREFETCHSTATUS)) {
+ mp->mPrefetchStatus.mLevel = 0;
+ mp->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW;
+ if (!(~mp->mPrefetchStatus.mCallbackEventsMask &
+ (SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE))) {
+ callback = mp->mPrefetchStatus.mCallback;
+ callbackPContext = mp->mPrefetchStatus.mContext;
+ events = SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE;
+ }
}
}
+
object_unlock_exclusive(&mp->mObject);
// callback with no lock held
if (NULL != callback) {
- (*callback)(&mp->mPrefetchStatus.mItf, callbackPContext,
- SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE);
+ (*callback)(&mp->mPrefetchStatus.mItf, callbackPContext, events);
}
break;
@@ -723,7 +732,8 @@
SL_LOGV("Displaying on ANativeWindow of type NATIVE_WINDOW_SURFACE");
android::sp<android::Surface> nativeSurface(
static_cast<android::Surface *>(nativeWindow));
- mp->mAVPlayer->setVideoSurface(nativeSurface);
+ mp->mAVPlayer->setVideoSurfaceTexture(
+ nativeSurface->getSurfaceTexture());
result = SL_RESULT_SUCCESS;
} break;
case NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT: { // SurfaceTextureClient
diff --git a/src/android/android_AudioSfDecoder.cpp b/src/android/android_AudioSfDecoder.cpp
index a436fac..a29f09f 100644
--- a/src/android/android_AudioSfDecoder.cpp
+++ b/src/android/android_AudioSfDecoder.cpp
@@ -19,6 +19,7 @@
#include "sles_allinclusive.h"
#include "android/android_AudioSfDecoder.h"
+#include <binder/IServiceManager.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -782,17 +783,25 @@
updateAudioSink();
}
-static const char* const kUnsupportedCodecs[] = { MEDIA_MIMETYPE_AUDIO_AMR_NB,
+static const char* const kPlaybackOnlyCodecs[] = { MEDIA_MIMETYPE_AUDIO_AMR_NB,
MEDIA_MIMETYPE_AUDIO_AMR_WB };
-#define NB_UNSUPPORTED_CODECS (sizeof(kUnsupportedCodecs)/sizeof(kUnsupportedCodecs[0]))
+#define NB_PLAYBACK_ONLY_CODECS (sizeof(kPlaybackOnlyCodecs)/sizeof(kPlaybackOnlyCodecs[0]))
bool AudioSfDecoder::isSupportedCodec(const char* mime) {
- for (unsigned int i = 0 ; i < NB_UNSUPPORTED_CODECS ; i++) {
- if (!strcasecmp(mime, kUnsupportedCodecs[i])) {
- return false;
+ bool codecRequiresPermission = false;
+ for (unsigned int i = 0 ; i < NB_PLAYBACK_ONLY_CODECS ; i++) {
+ if (!strcasecmp(mime, kPlaybackOnlyCodecs[i])) {
+ codecRequiresPermission = true;
+ break;
}
}
- return true;
+ if (codecRequiresPermission) {
+ // verify only the system can decode, for playback only
+ return checkCallingPermission(
+ String16("android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"));
+ } else {
+ return true;
+ }
}
} // namespace android
diff --git a/src/android/android_GenericMediaPlayer.cpp b/src/android/android_GenericMediaPlayer.cpp
index 68bc7f6..f3cebf5 100644
--- a/src/android/android_GenericMediaPlayer.cpp
+++ b/src/android/android_GenericMediaPlayer.cpp
@@ -23,6 +23,7 @@
#include <surfaceflinger/ISurfaceComposer.h>
#include <surfaceflinger/SurfaceComposerClient.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/mediaplayer.h> // media_event_type media_error_type media_info_type
// default delay in Us used when reposting an event when the player is not ready to accept
// the command yet. This is for instance used when seeking on a MediaPlayer that's still preparing
@@ -57,20 +58,53 @@
SL_LOGV("MediaPlayerNotificationClient::~MediaPlayerNotificationClient()");
}
-// Map a MEDIA_* enum to a string
-static const char *media_to_string(int msg)
+// Map a media_event_type enum (the msg of an IMediaPlayerClient::notify) to a string or NULL
+static const char *media_event_type_to_string(media_event_type msg)
{
switch (msg) {
-#define _(x) case MEDIA_##x: return "MEDIA_" #x;
- _(PREPARED)
- _(SET_VIDEO_SIZE)
- _(SEEK_COMPLETE)
- _(PLAYBACK_COMPLETE)
- _(BUFFERING_UPDATE)
- _(ERROR)
- _(NOP)
- _(TIMED_TEXT)
- _(INFO)
+#define _(code) case code: return #code;
+ _(MEDIA_NOP)
+ _(MEDIA_PREPARED)
+ _(MEDIA_PLAYBACK_COMPLETE)
+ _(MEDIA_BUFFERING_UPDATE)
+ _(MEDIA_SEEK_COMPLETE)
+ _(MEDIA_SET_VIDEO_SIZE)
+ _(MEDIA_TIMED_TEXT)
+ _(MEDIA_ERROR)
+ _(MEDIA_INFO)
+#undef _
+ default:
+ return NULL;
+ }
+}
+
+// Map a media_error_type enum (the ext1 of a MEDIA_ERROR event) to a string or NULL
+static const char *media_error_type_to_string(media_error_type err)
+{
+ switch (err) {
+#define _(code, msg) case code: return msg;
+ _(MEDIA_ERROR_UNKNOWN, "Unknown media error")
+ _(MEDIA_ERROR_SERVER_DIED, "Server died")
+ _(MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK, "Not valid for progressive playback")
+#undef _
+ default:
+ return NULL;
+ }
+}
+
+// Map a media_info_type enum (the ext1 of a MEDIA_INFO event) to a string or NULL
+static const char *media_info_type_to_string(media_info_type info)
+{
+ switch (info) {
+#define _(code, msg) case code: return msg;
+ _(MEDIA_INFO_UNKNOWN, "Unknown info")
+ _(MEDIA_INFO_VIDEO_TRACK_LAGGING, "Video track lagging")
+ _(MEDIA_INFO_BUFFERING_START, "Buffering start")
+ _(MEDIA_INFO_BUFFERING_END, "Buffering end")
+ _(MEDIA_INFO_NETWORK_BANDWIDTH, "Network bandwidth")
+ _(MEDIA_INFO_BAD_INTERLEAVING, "Bad interleaving")
+ _(MEDIA_INFO_NOT_SEEKABLE, "Not seekable")
+ _(MEDIA_INFO_METADATA_UPDATE, "Metadata update")
#undef _
default:
return NULL;
@@ -81,7 +115,7 @@
// IMediaPlayerClient implementation
void MediaPlayerNotificationClient::notify(int msg, int ext1, int ext2, const Parcel *obj) {
SL_LOGV("MediaPlayerNotificationClient::notify(msg=%s (%d), ext1=%d, ext2=%d)",
- media_to_string(msg), msg, ext1, ext2);
+ media_event_type_to_string((enum media_event_type) msg), msg, ext1, ext2);
sp<GenericMediaPlayer> genericMediaPlayer(mGenericMediaPlayer.promote());
if (genericMediaPlayer == NULL) {
@@ -89,7 +123,7 @@
return;
}
- switch (msg) {
+ switch ((media_event_type) msg) {
case MEDIA_PREPARED:
{
Mutex::Autolock _l(mLock);
@@ -109,9 +143,9 @@
// so it would normally be racy to access fields within genericMediaPlayer.
// But in this case mHasVideo is const, so it is safe to access.
// Or alternatively, we could notify unconditionally and let it decide whether to handle.
- if (genericMediaPlayer->mHasVideo) {
+ if (genericMediaPlayer->mHasVideo && (ext1 != 0 || ext2 != 0)) {
genericMediaPlayer->notify(PLAYEREVENT_VIDEO_SIZE_UPDATE,
- (int32_t)ext1, (int32_t)ext2, true /*async*/);
+ (int32_t)ext1 /*width*/, (int32_t)ext2 /*height*/, true /*async*/);
}
break;
@@ -124,6 +158,14 @@
break;
case MEDIA_BUFFERING_UPDATE:
+ // if we receive any out-of-range data, then clamp it to reduce further harm
+ if (ext1 < 0) {
+ SL_LOGE("MEDIA_BUFFERING_UPDATE %d%% < 0", ext1);
+ ext1 = 0;
+ } else if (ext1 > 100) {
+ SL_LOGE("MEDIA_BUFFERING_UPDATE %d%% > 100", ext1);
+ ext1 = 100;
+ }
// values received from Android framework for buffer fill level use percent,
// while SL/XA use permille, so does GenericPlayer
genericMediaPlayer->bufferingUpdate(ext1 * 10 /*fillLevelPerMille*/);
@@ -131,6 +173,8 @@
case MEDIA_ERROR:
{
+ SL_LOGV("MediaPlayerNotificationClient::notify(msg=MEDIA_ERROR, ext1=%s (%d), ext2=%d)",
+ media_error_type_to_string((media_error_type) ext1), ext1, ext2);
Mutex::Autolock _l(mLock);
if (PREPARE_IN_PROGRESS == mPlayerPrepared) {
mPlayerPrepared = PREPARE_COMPLETED_UNSUCCESSFULLY;
@@ -144,10 +188,31 @@
case MEDIA_NOP:
case MEDIA_TIMED_TEXT:
- case MEDIA_INFO:
break;
- default: { }
+ case MEDIA_INFO:
+ SL_LOGV("MediaPlayerNotificationClient::notify(msg=MEDIA_INFO, ext1=%s (%d), ext2=%d)",
+ media_info_type_to_string((media_info_type) ext1), ext1, ext2);
+ switch (ext1) {
+ case MEDIA_INFO_VIDEO_TRACK_LAGGING:
+ SL_LOGV("MEDIA_INFO_VIDEO_TRACK_LAGGING by %d ms", ext1);
+ break;
+ case MEDIA_INFO_NETWORK_BANDWIDTH:
+ SL_LOGV("MEDIA_INFO_NETWORK_BANDWIDTH %d kbps", ext2);
+ break;
+ case MEDIA_INFO_UNKNOWN:
+ case MEDIA_INFO_BUFFERING_START:
+ case MEDIA_INFO_BUFFERING_END:
+ case MEDIA_INFO_BAD_INTERLEAVING:
+ case MEDIA_INFO_NOT_SEEKABLE:
+ case MEDIA_INFO_METADATA_UPDATE:
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
}
}
@@ -177,10 +242,10 @@
GenericPlayer(params),
mHasVideo(hasVideo),
mSeekTimeMsec(0),
- mVideoSurface(0),
mVideoSurfaceTexture(0),
mPlayer(0),
- mPlayerClient(new MediaPlayerNotificationClient(this))
+ mPlayerClient(new MediaPlayerNotificationClient(this)),
+ mPlayerDeathNotifier(new MediaPlayerDeathNotifier(mPlayerClient))
{
SL_LOGD("GenericMediaPlayer::GenericMediaPlayer()");
@@ -199,7 +264,7 @@
// causes CHECK failure in Nuplayer, but commented out in the subclass preDestroy
// randomly causes a NPE in StagefrightPlayer, heap corruption, or app hang
//player->setDataSource(NULL);
- player->setVideoSurface(NULL);
+ player->setVideoSurfaceTexture(NULL);
player->disconnect();
// release all references to the IMediaPlayer
// FIXME illegal if not on looper
@@ -231,19 +296,6 @@
}
//--------------------------------------------------
-void GenericMediaPlayer::setVideoSurface(const sp<Surface> &surface) {
- SL_LOGV("GenericMediaPlayer::setVideoSurface()");
- // FIXME bug - race condition, should do in looper
- if (mVideoSurface.get() == surface.get()) {
- return;
- }
- if ((mStateFlags & kFlagPrepared) && (mPlayer != 0)) {
- mPlayer->setVideoSurface(surface);
- }
- mVideoSurface = surface;
- mVideoSurfaceTexture = NULL;
-}
-
void GenericMediaPlayer::setVideoSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture) {
SL_LOGV("GenericMediaPlayer::setVideoSurfaceTexture()");
// FIXME bug - race condition, should do in looper
@@ -254,7 +306,6 @@
mPlayer->setVideoSurfaceTexture(surfaceTexture);
}
mVideoSurfaceTexture = surfaceTexture;
- mVideoSurface = NULL;
}
@@ -267,9 +318,7 @@
// Attempt to prepare at most once, and only if there is a MediaPlayer
if (!(mStateFlags & (kFlagPrepared | kFlagPreparedUnsuccessfully)) && (mPlayer != 0)) {
if (mHasVideo) {
- if (mVideoSurface != 0) {
- mPlayer->setVideoSurface(mVideoSurface);
- } else if (mVideoSurfaceTexture != 0) {
+ if (mVideoSurfaceTexture != 0) {
mPlayer->setVideoSurfaceTexture(mVideoSurfaceTexture);
}
}
diff --git a/src/android/android_GenericMediaPlayer.h b/src/android/android_GenericMediaPlayer.h
index c3a9d00..3e8fe81 100644
--- a/src/android/android_GenericMediaPlayer.h
+++ b/src/android/android_GenericMediaPlayer.h
@@ -32,7 +32,6 @@
{
public:
MediaPlayerNotificationClient(GenericMediaPlayer* gmp);
- virtual ~MediaPlayerNotificationClient();
// IMediaPlayerClient implementation
virtual void notify(int msg, int ext1, int ext2, const Parcel *obj);
@@ -44,6 +43,9 @@
// completed successfully, or false if it completed unsuccessfully
bool blockUntilPlayerPrepared();
+protected:
+ virtual ~MediaPlayerNotificationClient();
+
private:
const wp<GenericMediaPlayer> mGenericMediaPlayer;
Mutex mLock; // protects mPlayerPrepared
@@ -57,6 +59,24 @@
};
+class MediaPlayerDeathNotifier : public IMediaDeathNotifier {
+public:
+ MediaPlayerDeathNotifier(const sp<MediaPlayerNotificationClient> playerClient) :
+ mPlayerClient(playerClient) {
+ }
+
+ void died() {
+ mPlayerClient->notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0, NULL);
+ }
+
+protected:
+ virtual ~MediaPlayerDeathNotifier() { }
+
+private:
+ const sp<MediaPlayerNotificationClient> mPlayerClient;
+};
+
+
//--------------------------------------------------------------------------------------------------
class GenericMediaPlayer : public GenericPlayer
{
@@ -70,7 +90,6 @@
// overridden from GenericPlayer
virtual void getPositionMsec(int* msec); // ANDROID_UNKNOWN_TIME if unknown
- virtual void setVideoSurface(const sp<Surface> &surface);
virtual void setVideoSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture);
protected:
@@ -91,8 +110,6 @@
const bool mHasVideo; // const allows MediaPlayerNotificationClient::notify to safely access
int32_t mSeekTimeMsec;
- // at most one of mVideoSurface and mVideoSurfaceTexture is non-NULL
- sp<Surface> mVideoSurface;
sp<ISurfaceTexture> mVideoSurfaceTexture;
// only safe to access from within Realize and looper
@@ -100,6 +117,9 @@
// Receives Android MediaPlayer events from mPlayer
const sp<MediaPlayerNotificationClient> mPlayerClient;
+ // Receives notifications about death of media.player service
+ const sp<MediaPlayerDeathNotifier> mPlayerDeathNotifier;
+
// Return a reference to the media player service, or LOGE and return NULL after retries fail
static const sp<IMediaPlayerService> getMediaPlayerService() {
return IMediaDeathNotifier::getMediaPlayerService();
diff --git a/src/android/android_GenericPlayer.h b/src/android/android_GenericPlayer.h
index b4104ad..d3206a1 100644
--- a/src/android/android_GenericPlayer.h
+++ b/src/android/android_GenericPlayer.h
@@ -78,7 +78,6 @@
virtual void getDurationMsec(int* msec); //msec != NULL, ANDROID_UNKNOWN_TIME if unknown
virtual void getPositionMsec(int* msec) = 0; //msec != NULL, ANDROID_UNKNOWN_TIME if unknown
- virtual void setVideoSurface(const sp<Surface> &surface) {}
virtual void setVideoSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture) {}
void setVolume(float leftVol, float rightVol);
diff --git a/src/android/android_StreamPlayer.cpp b/src/android/android_StreamPlayer.cpp
index a0ab4f8..7c400c8 100644
--- a/src/android/android_StreamPlayer.cpp
+++ b/src/android/android_StreamPlayer.cpp
@@ -45,8 +45,7 @@
}
StreamSourceAppProxy::~StreamSourceAppProxy() {
- // FIXME make this an SL_LOGV later; this just proves that the bug is fixed
- SL_LOGI("StreamSourceAppProxy::~StreamSourceAppProxy()");
+ SL_LOGV("StreamSourceAppProxy::~StreamSourceAppProxy()");
disconnect();
}
@@ -331,7 +330,7 @@
mPlayer->stop();
// causes CHECK failure in Nuplayer
//mPlayer->setDataSource(NULL);
- mPlayer->setVideoSurface(NULL);
+ mPlayer->setVideoSurfaceTexture(NULL);
mPlayer->disconnect();
mPlayer.clear();
{
@@ -340,7 +339,10 @@
mPreparedPlayer.clear();
}
}
- mStopForDestroyCompleted = true;
+ {
+ Mutex::Autolock _l(mStopForDestroyLock);
+ mStopForDestroyCompleted = true;
+ }
mStopForDestroyCondition.signal();
}
diff --git a/src/android/include/AacBqToPcmCbRenderer.h b/src/android/include/AacBqToPcmCbRenderer.h
index d144f1e..62cbd6e 100644
--- a/src/android/include/AacBqToPcmCbRenderer.h
+++ b/src/android/include/AacBqToPcmCbRenderer.h
@@ -33,11 +33,10 @@
{
public:
- AacBqToPcmCbRenderer(const AudioPlayback_Parameters* params);
+ AacBqToPcmCbRenderer(const AudioPlayback_Parameters* params,
+ IAndroidBufferQueue *androidBufferQueue);
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
@@ -51,9 +50,7 @@
private:
- // mutex used to protect mBqSource
- Mutex mBqSourceLock;
- sp<BufferQueueSource> mBqSource;
+ const sp<BufferQueueSource> mBqSource;
private:
DISALLOW_EVIL_CONSTRUCTORS(AacBqToPcmCbRenderer);
diff --git a/src/itf/IAndroidBufferQueue.c b/src/itf/IAndroidBufferQueue.c
index 278b7fd..76ab546 100644
--- a/src/itf/IAndroidBufferQueue.c
+++ b/src/itf/IAndroidBufferQueue.c
@@ -214,19 +214,7 @@
if (SL_PLAYSTATE_STOPPED == getAssociatedState(thiz)) {
thiz->mCallback = callback;
thiz->mContext = pContext;
-
- // FIXME investigate why these two cases are not handled symmetrically any more
- switch (InterfaceToObjectID(thiz)) {
- case SL_OBJECTID_AUDIOPLAYER:
- result = android_audioPlayer_androidBufferQueue_registerCallback_l(
- (CAudioPlayer*) thiz->mThis);
- break;
- case XA_OBJECTID_MEDIAPLAYER:
- result = SL_RESULT_SUCCESS;
- break;
- default:
- result = SL_RESULT_PARAMETER_INVALID;
- }
+ result = SL_RESULT_SUCCESS;
} else {
result = SL_RESULT_PRECONDITIONS_VIOLATED;
diff --git a/tests/examples/Android.mk b/tests/examples/Android.mk
index ac06ee7..f9e912e 100644
--- a/tests/examples/Android.mk
+++ b/tests/examples/Android.mk
@@ -293,7 +293,7 @@
LOCAL_CFLAGS += -UNDEBUG
-LOCAL_MODULE:= slesTestDecodeAac
+LOCAL_MODULE:= slesTest_decodeAac
include $(BUILD_EXECUTABLE)
diff --git a/tests/examples/slesTestDecodeAac.cpp b/tests/examples/slesTestDecodeAac.cpp
index 707ab37..1a51bb0 100644
--- a/tests/examples/slesTestDecodeAac.cpp
+++ b/tests/examples/slesTestDecodeAac.cpp
@@ -45,6 +45,7 @@
#define QUERY_METADATA
+#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -64,7 +65,7 @@
* on the AudioPlayer object for decoding, and
* SL_IID_METADATAEXTRACTION for retrieving the format of the decoded audio.
*/
-#define NUM_EXPLICIT_INTERFACES_FOR_PLAYER 3
+#define NUM_EXPLICIT_INTERFACES_FOR_PLAYER 4
/* Number of decoded samples produced by one AAC frame; defined by the standard */
#define SAMPLES_PER_AAC_FRAME 1024
@@ -115,7 +116,7 @@
size_t totalEncodeCompletions = 0; // number of Enqueue completions received
CentralTendencyStatistics frameStats;
size_t pauseFrame = 0; // pause after this many decoded frames, zero means don't pause
-SLboolean createRaw = SL_BOOLEAN_FALSE; // whether to create a .raw file containing PCM data
+SLboolean createRaw = SL_BOOLEAN_TRUE; // whether to create a .raw file containing PCM data
/* constant to identify a buffer context which is the end of the stream to decode */
static const int kEosBufferCntxt = 1980; // a magic value we can compare against
@@ -124,6 +125,23 @@
pthread_mutex_t eosLock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t eosCondition = PTHREAD_COND_INITIALIZER;
+// These are extensions to OpenMAX AL 1.0.1 values
+
+#define PREFETCHSTATUS_UNKNOWN ((SLuint32) 0)
+#define PREFETCHSTATUS_ERROR ((SLuint32) (-1))
+
+// Mutex and condition shared with main program to protect prefetch_status
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+SLuint32 prefetch_status = PREFETCHSTATUS_UNKNOWN;
+
+/* used to detect errors likely to have occured when the OpenSL ES framework fails to open
+ * a resource, for instance because a file URI is invalid, or an HTTP server doesn't respond.
+ */
+#define PREFETCHEVENT_ERROR_CANDIDATE \
+ (SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE)
+
//-----------------------------------------------------------------
/* Exits the application if an error is encountered */
#define ExitOnError(x) ExitOnErrorFunc(x,__LINE__)
@@ -137,6 +155,40 @@
}
//-----------------------------------------------------------------
+/* Callback for "prefetch" events, here used to detect audio resource opening errors */
+void PrefetchEventCallback(SLPrefetchStatusItf caller, void *pContext, SLuint32 event)
+{
+ // pContext is unused here, so we pass NULL
+ assert(pContext == NULL);
+ SLpermille level = 0;
+ SLresult result;
+ result = (*caller)->GetFillLevel(caller, &level);
+ ExitOnError(result);
+ SLuint32 status;
+ result = (*caller)->GetPrefetchStatus(caller, &status);
+ ExitOnError(result);
+ printf("prefetch level=%d status=0x%x event=%d\n", level, status, event);
+ SLuint32 new_prefetch_status;
+ if ((PREFETCHEVENT_ERROR_CANDIDATE == (event & PREFETCHEVENT_ERROR_CANDIDATE))
+ && (level == 0) && (status == SL_PREFETCHSTATUS_UNDERFLOW)) {
+ printf("PrefetchEventCallback: Error while prefetching data, exiting\n");
+ new_prefetch_status = PREFETCHSTATUS_ERROR;
+ } else if (event == SL_PREFETCHEVENT_STATUSCHANGE) {
+ new_prefetch_status = status;
+ } else {
+ return;
+ }
+ int ok;
+ ok = pthread_mutex_lock(&mutex);
+ assert(ok == 0);
+ prefetch_status = new_prefetch_status;
+ ok = pthread_cond_signal(&cond);
+ assert(ok == 0);
+ ok = pthread_mutex_unlock(&mutex);
+ assert(ok == 0);
+}
+
+//-----------------------------------------------------------------
/* Structure for passing information to callback function */
typedef struct CallbackCntxt_ {
#ifdef QUERY_METADATA
@@ -147,6 +199,11 @@
SLint8* pData; // Current address of local audio data storage
} CallbackCntxt;
+// used to notify when SL_PLAYEVENT_HEADATEND event is received
+static pthread_mutex_t head_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t head_cond = PTHREAD_COND_INITIALIZER;
+static SLboolean head_atend = SL_BOOLEAN_FALSE;
+
//-----------------------------------------------------------------
/* Callback for SLPlayItf through which we receive the SL_PLAYEVENT_HEADATEND event */
void PlayCallback(SLPlayItf caller, void *pContext, SLuint32 event) {
@@ -162,6 +219,10 @@
if (event & SL_PLAYEVENT_HEADATEND) {
printf("SL_PLAYEVENT_HEADATEND position=%u ms, all decoded data has been received\n",
position);
+ pthread_mutex_lock(&head_mutex);
+ head_atend = SL_BOOLEAN_TRUE;
+ pthread_cond_signal(&head_cond);
+ pthread_mutex_unlock(&head_mutex);
}
}
@@ -184,8 +245,7 @@
// for demonstration purposes:
// verify what type of information was enclosed in the processed buffer
if (NULL != pBufferContext) {
- const int processedCommand = *(int *)pBufferContext;
- if (kEosBufferCntxt == processedCommand) {
+ if (&kEosBufferCntxt == pBufferContext) {
fprintf(stdout, "EOS was processed\n");
}
}
@@ -193,7 +253,7 @@
++totalEncodeCompletions;
if (endOfEncodedStream) {
// we continue to receive acknowledgement after each buffer was processed
- if (pBufferContext == (void *) kEosBufferCntxt) {
+ if (pBufferContext == (void *) &kEosBufferCntxt) {
printf("Received EOS completion after EOS\n");
} else if (pBufferContext == NULL) {
printf("Received ADTS completion after EOS\n");
@@ -411,6 +471,8 @@
SLAndroidSimpleBufferQueueItf decBuffQueueItf;
/* to queue the AAC data to decode */
SLAndroidBufferQueueItf aacBuffQueueItf;
+ /* for prefetch status */
+ SLPrefetchStatusItf prefetchItf;
SLboolean required[NUM_EXPLICIT_INTERFACES_FOR_PLAYER];
SLInterfaceID iidArray[NUM_EXPLICIT_INTERFACES_FOR_PLAYER];
@@ -435,10 +497,13 @@
/* Request the AndroidBufferQueue interface */
required[1] = SL_BOOLEAN_TRUE;
iidArray[1] = SL_IID_ANDROIDBUFFERQUEUESOURCE;
+ /* Request the PrefetchStatus interface */
+ required[2] = SL_BOOLEAN_TRUE;
+ iidArray[2] = SL_IID_PREFETCHSTATUS;
#ifdef QUERY_METADATA
/* Request the MetadataExtraction interface */
- required[2] = SL_BOOLEAN_TRUE;
- iidArray[2] = SL_IID_METADATAEXTRACTION;
+ required[3] = SL_BOOLEAN_TRUE;
+ iidArray[3] = SL_IID_METADATAEXTRACTION;
#endif
/* Setup the data source for queueing AAC buffers of ADTS data */
@@ -529,6 +594,10 @@
res = (*player)->GetInterface(player, SL_IID_ANDROIDBUFFERQUEUESOURCE, (void*)&aacBuffQueueItf);
ExitOnError(res);
+ /* Get the prefetch status interface which was explicitly requested */
+ res = (*player)->GetInterface(player, SL_IID_PREFETCHSTATUS, (void*)&prefetchItf);
+ ExitOnError(res);
+
#ifdef QUERY_METADATA
/* Get the metadata extraction interface which was explicitly requested */
res = (*player)->GetInterface(player, SL_IID_METADATAEXTRACTION, (void*)&mdExtrItf);
@@ -561,6 +630,13 @@
}
printf("\n");
+ /* ------------------------------------------------------ */
+ /* Initialize the callback for prefetch errors, if we can't open the resource to decode */
+ res = (*prefetchItf)->RegisterCallback(prefetchItf, PrefetchEventCallback, NULL);
+ ExitOnError(res);
+ res = (*prefetchItf)->SetCallbackEventsMask(prefetchItf, PREFETCHEVENT_ERROR_CANDIDATE);
+ ExitOnError(res);
+
/* Initialize the callback for the Android buffer queue of the encoded data */
res = (*aacBuffQueueItf)->RegisterCallback(aacBuffQueueItf, AndroidBufferQueueCallback, NULL);
ExitOnError(res);
@@ -569,10 +645,14 @@
we don't want to starve the player initially */
printf("Enqueueing initial full buffers of encoded ADTS data");
for (i=0 ; i < NB_BUFFERS_IN_ADTS_QUEUE ; i++) {
- if (filelen < 7 || frame[0] != 0xFF || (frame[1] & 0xF0) != 0xF0)
+ if (filelen < 7 || frame[0] != 0xFF || (frame[1] & 0xF0) != 0xF0) {
+ printf("\ncorrupt ADTS frame encountered; offset %zu bytes\n",
+ frame - (unsigned char *) ptr);
+ // Note that prefetch will detect this error soon when it gets a premature EOF
break;
+ }
unsigned framelen = ((frame[3] & 3) << 11) | (frame[4] << 3) | (frame[5] >> 5);
- printf(" %d", i);
+ printf(" %d (%u bytes)", i, framelen);
res = (*aacBuffQueueItf)->Enqueue(aacBuffQueueItf, NULL /*pBufferContext*/,
frame, framelen, NULL, 0);
ExitOnError(res);
@@ -586,10 +666,12 @@
#ifdef QUERY_METADATA
/* ------------------------------------------------------ */
- /* Display the metadata obtained from the decoder */
+ /* Get and display the metadata key names for the decoder */
// This is for test / demonstration purposes only where we discover the key and value sizes
// of a PCM decoder. An application that would want to directly get access to those values
- // can make assumptions about the size of the keys and their matching values (all SLuint32)
+ // can make assumptions about the size of the keys and their matching values (all SLuint32),
+ // but it should not make assumptions about the key indices as these are subject to change.
+ // Note that we don't get the metadata values yet; that happens in the first decode callback.
SLuint32 itemCount;
res = (*mdExtrItf)->GetItemCount(mdExtrItf, &itemCount);
ExitOnError(res);
@@ -667,6 +749,24 @@
}
#endif
+ // set the player's state to paused, to start prefetching
+ printf("Setting play state to PAUSED\n");
+ res = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PAUSED);
+ ExitOnError(res);
+
+ // wait for prefetch status callback to indicate either sufficient data or error
+ printf("Awaiting prefetch complete\n");
+ pthread_mutex_lock(&mutex);
+ while (prefetch_status == PREFETCHSTATUS_UNKNOWN) {
+ pthread_cond_wait(&cond, &mutex);
+ }
+ pthread_mutex_unlock(&mutex);
+ if (prefetch_status == PREFETCHSTATUS_ERROR) {
+ fprintf(stderr, "Error during prefetch, exiting\n");
+ goto destroyRes;
+ }
+ printf("Prefetch is complete\n");
+
/* ------------------------------------------------------ */
/* Start decoding */
printf("Starting to decode\n");
@@ -674,6 +774,7 @@
ExitOnError(res);
/* Decode until the end of the stream is reached */
+ printf("Awaiting notification that all encoded buffers have been enqueued\n");
pthread_mutex_lock(&eosLock);
while (!eos) {
if (pauseFrame > 0) {
@@ -698,10 +799,15 @@
}
}
pthread_mutex_unlock(&eosLock);
+ printf("All encoded buffers have now been enqueued, but there's still more to do\n");
/* This just means done enqueueing; there may still more data in decode queue! */
- // FIXME here is where we should wait for HEADATEND
- usleep(100 * 1000);
+ pthread_mutex_lock(&head_mutex);
+ while (!head_atend) {
+ pthread_cond_wait(&head_cond, &head_mutex);
+ }
+ pthread_mutex_unlock(&head_mutex);
+ printf("Decode is now finished\n");
pthread_mutex_lock(&eosLock);
printf("Frame counters: encoded=%u decoded=%u\n", encodedFrames, decodedFrames);