}
void GenericMediaPlayer::preDestroy() {
- SL_LOGD("GenericMediaPlayer::preDestroy()");
- if (mPlayer != 0) {
- mPlayer->stop();
+ // FIXME can't access mPlayer from outside the looper (no mutex!) so using mPreparedPlayer
+ sp<IMediaPlayer> player;
+ getPreparedPlayer(player);
+ if (player != NULL) {
+ player->stop();
+ // causes CHECK failure in Nuplayer, but commented out in the subclass preDestroy
+ player->setDataSource(NULL);
+ player->setVideoSurface(NULL);
+ player->disconnect();
+ // release all references to the IMediaPlayer
+ // FIXME illegal if not on looper
+ //mPlayer.clear();
+ {
+ Mutex::Autolock _l(mPreparedPlayerLock);
+ mPreparedPlayer.clear();
+ }
}
GenericPlayer::preDestroy();
}
void GenericMediaPlayer::getPositionMsec(int* msec) {
SL_LOGD("GenericMediaPlayer::getPositionMsec()");
sp<IMediaPlayer> player;
- getPlayerPrepared(player);
+ getPreparedPlayer(player);
// To avoid deadlock, directly call the MediaPlayer object
if (player == 0 || player->getCurrentPosition(msec) != NO_ERROR) {
*msec = ANDROID_UNKNOWN_TIME;
assert(mStateFlags & kFlagPrepared);
// Mark this player as prepared successfully, so safe to directly call getCurrentPosition
{
- Mutex::Autolock _l(mPlayerPreparedLock);
- assert(mPlayerPrepared == 0);
- mPlayerPrepared = mPlayer;
+ Mutex::Autolock _l(mPreparedPlayerLock);
+ assert(mPreparedPlayer == 0);
+ mPreparedPlayer = mPlayer;
}
// retrieve channel count
assert(UNKNOWN_NUMCHANNELS == mChannelCount);
//--------------------------------------------------
// If player is prepared successfully, set output parameter to that reference, otherwise NULL
-void GenericMediaPlayer::getPlayerPrepared(sp<IMediaPlayer> &playerPrepared)
+void GenericMediaPlayer::getPreparedPlayer(sp<IMediaPlayer> &preparedPlayer)
{
- Mutex::Autolock _l(mPlayerPreparedLock);
- playerPrepared = mPlayerPrepared;
+ Mutex::Autolock _l(mPreparedPlayerLock);
+ preparedPlayer = mPreparedPlayer;
}
} // namespace android
#include <media/IStreamSource.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <binder/IPCThreadState.h>
//--------------------------------------------------------------------------------------------------
StreamSourceAppProxy::StreamSourceAppProxy(
IAndroidBufferQueue *androidBufferQueue,
const sp<CallbackProtector> &callbackProtector,
- const sp<StreamPlayer> &player) :
+ // sp<StreamPlayer> would cause StreamPlayer's destructor to run during it's own
+ // construction. If you pass in a sp<> to 'this' inside a constructor, then first the
+ // refcount is increased from 0 to 1, then decreased from 1 to 0, which causes the object's
+ // destructor to run from inside it's own constructor.
+ StreamPlayer * /* const sp<StreamPlayer> & */ player) :
+ mBuffersHasBeenSet(false),
mAndroidBufferQueue(androidBufferQueue),
mCallbackProtector(callbackProtector),
mPlayer(player)
}
StreamSourceAppProxy::~StreamSourceAppProxy() {
- SL_LOGD("StreamSourceAppProxy::~StreamSourceAppProxy()");
- mListener.clear();
- mBuffers.clear();
+ // FIXME make this an SL_LOGV later; this just proves that the bug is fixed
+ SL_LOGI("StreamSourceAppProxy::~StreamSourceAppProxy()");
+ disconnect();
}
const SLuint32 StreamSourceAppProxy::kItemProcessed[NB_BUFFEREVENT_ITEM_FIELDS] = {
//--------------------------------------------------
// IStreamSource implementation
void StreamSourceAppProxy::setListener(const sp<IStreamListener> &listener) {
+ assert(listener != NULL);
Mutex::Autolock _l(mLock);
+ assert(mListener == NULL);
mListener = listener;
}
void StreamSourceAppProxy::setBuffers(const Vector<sp<IMemory> > &buffers) {
+ Mutex::Autolock _l(mLock);
+ assert(!mBuffersHasBeenSet);
mBuffers = buffers;
+ mBuffersHasBeenSet = true;
}
void StreamSourceAppProxy::onBufferAvailable(size_t index) {
//SL_LOGD("StreamSourceAppProxy::onBufferAvailable(%d)", index);
- CHECK_LT(index, mBuffers.size());
- sp<IMemory> mem = mBuffers.itemAt(index);
- SLAint64 length = (SLAint64) mem->size();
-
{
Mutex::Autolock _l(mLock);
+ // assert not needed because if not set, size() will be zero and the CHECK_LT will also fail
+ // assert(mBuffersHasBeenSet);
+ CHECK_LT(index, mBuffers.size());
+#if 0 // enable if needed for debugging
+ sp<IMemory> mem = mBuffers.itemAt(index);
+ SLAint64 length = (SLAint64) mem->size();
+#endif
mAvailableBuffers.push_back(index);
+ //SL_LOGD("onBufferAvailable() now %d buffers available in queue", mAvailableBuffers.size());
}
- //SL_LOGD("onBufferAvailable() now %d buffers available in queue", mAvailableBuffers.size());
// a new shared mem buffer is available: let's try to fill immediately
pullFromBuffQueue();
}
}
+void StreamSourceAppProxy::disconnect() {
+ Mutex::Autolock _l(mLock);
+ mListener.clear();
+ // Force binder to push the decremented reference count for sp<IStreamListener>.
+ // mediaserver and client both have sp<> to the other. When you decrement an sp<>
+ // reference count, binder doesn't push that to the other process immediately.
+ IPCThreadState::self()->flushCommands();
+ mBuffers.clear();
+ mBuffersHasBeenSet = false;
+ mAvailableBuffers.clear();
+}
+
//--------------------------------------------------
// consumption from ABQ: pull from the ABQ, and push to shared memory (media server)
void StreamSourceAppProxy::pullFromBuffQueue() {
}
if (oldFront->mItems.mTsCmdData.mTsCmdCode & (ANDROID_MP2TSEVENT_DISCONTINUITY |
ANDROID_MP2TSEVENT_DISCON_NEWPTS | ANDROID_MP2TSEVENT_FORMAT_CHANGE)) {
- // FIXME see note at onSeek
- mPlayer->seek(ANDROID_UNKNOWN_TIME);
+ const sp<StreamPlayer> player(mPlayer.promote());
+ if (player != NULL) {
+ // FIXME see note at onSeek
+ player->seek(ANDROID_UNKNOWN_TIME);
+ }
}
oldFront->mItems.mTsCmdData.mTsCmdCode = ANDROID_MP2TSEVENT_NONE;
}
if (!mAvailableBuffers.empty()) {
// there is still room in the shared memory, recheck later if we can pull
// data from the buffer queue and write it to shared memory
- mPlayer->queueRefilled();
+ const sp<StreamPlayer> player(mPlayer.promote());
+ if (player != NULL) {
+ player->queueRefilled();
+ }
}
}
StreamPlayer::~StreamPlayer() {
SL_LOGD("StreamPlayer::~StreamPlayer()");
+ mAppProxy->disconnect();
}
mStopForDestroyCondition.wait(mStopForDestroyLock);
}
}
- // skipping past GenericMediaPlayer::preDestroy
- GenericPlayer::preDestroy();
+ // GenericMediaPlayer::preDestroy will repeat some of what we've done, but that's benign
+ GenericMediaPlayer::preDestroy();
}
void StreamPlayer::onStopForDestroy() {
if (mPlayer != 0) {
mPlayer->stop();
+ // causes CHECK failure in Nuplayer
+ //mPlayer->setDataSource(NULL);
+ mPlayer->setVideoSurface(NULL);
+ mPlayer->disconnect();
+ mPlayer.clear();
+ {
+ // FIXME ugh make this a method
+ Mutex::Autolock _l(mPreparedPlayerLock);
+ mPreparedPlayer.clear();
+ }
}
mStopForDestroyCompleted = true;
mStopForDestroyCondition.signal();
StreamSourceAppProxy(
IAndroidBufferQueue *androidBufferQueue,
const sp<CallbackProtector> &callbackProtector,
- const sp<StreamPlayer> &player);
+ StreamPlayer *player);
virtual ~StreamSourceAppProxy();
// store an item structure to indicate a processed buffer
static const SLuint32 kItemProcessed[NB_BUFFEREVENT_ITEM_FIELDS];
// IStreamSource implementation
- virtual void setListener(const sp<IStreamListener> &listener);
- virtual void setBuffers(const Vector<sp<IMemory> > &buffers);
+ virtual void setListener(const sp<IStreamListener> &listener); // mediaserver calls exactly once
+ virtual void setBuffers(const Vector<sp<IMemory> > &buffers); // mediaserver calls exactly once
virtual void onBufferAvailable(size_t index);
// Consumption from ABQ
void pullFromBuffQueue();
+private:
void receivedCmd_l(IStreamListener::Command cmd, const sp<AMessage> &msg = NULL);
void receivedBuffer_l(size_t buffIndex, size_t buffLength);
+public:
+ // Call at least once prior to releasing the last strong reference to this object. It causes
+ // the player to release all of its resources, similar to android.media.MediaPlayer disconnect.
+ void disconnect();
+
private:
- // for mListener and mAvailableBuffers
+ // protects mListener, mBuffers, mBuffersHasBeenSet, and mAvailableBuffers
Mutex mLock;
+
sp<IStreamListener> mListener;
// array of shared memory buffers
Vector<sp<IMemory> > mBuffers;
+ bool mBuffersHasBeenSet;
// list of available buffers in shared memory, identified by their index
List<size_t> mAvailableBuffers;
// the Android Buffer Queue from which data is consumed and written to shared memory
- IAndroidBufferQueue* mAndroidBufferQueue;
+ IAndroidBufferQueue* const mAndroidBufferQueue;
const sp<CallbackProtector> mCallbackProtector;
- const sp<StreamPlayer> mPlayer;
+ const wp<StreamPlayer> mPlayer;
DISALLOW_EVIL_CONSTRUCTORS(StreamSourceAppProxy);
};