diff options
-rw-r--r-- | include/media/IMediaPlayer.h | 1 | ||||
-rw-r--r-- | include/media/MediaPlayerInterface.h | 1 | ||||
-rw-r--r-- | include/media/mediaplayer.h | 4 | ||||
-rw-r--r-- | media/libmedia/IMediaPlayer.cpp | 15 | ||||
-rw-r--r-- | media/libmedia/mediaplayer.cpp | 7 | ||||
-rw-r--r-- | media/libmediaplayerservice/MediaPlayerService.cpp | 105 | ||||
-rw-r--r-- | media/libmediaplayerservice/MediaPlayerService.h | 51 | ||||
-rw-r--r-- | media/libstagefright/AudioPlayer.cpp | 6 |
8 files changed, 180 insertions, 10 deletions
diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h index 39d58ab..00facc5 100644 --- a/include/media/IMediaPlayer.h +++ b/include/media/IMediaPlayer.h @@ -64,6 +64,7 @@ public: virtual status_t setParameter(int key, const Parcel& request) = 0; virtual status_t getParameter(int key, Parcel* reply) = 0; virtual status_t setRetransmitEndpoint(const struct sockaddr_in* endpoint) = 0; + virtual status_t setNextPlayer(const sp<IMediaPlayer>& next) = 0; // Invoke a generic method on the player by using opaque parcels // for the request and reply. diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h index 8168dff..d4aa233 100644 --- a/include/media/MediaPlayerInterface.h +++ b/include/media/MediaPlayerInterface.h @@ -107,6 +107,7 @@ public: virtual void close() = 0; virtual status_t setPlaybackRatePermille(int32_t rate) { return INVALID_OPERATION; } + virtual bool needsTrailingPadding() { return true; } }; MediaPlayerBase() : mCookie(0), mNotify(0) {} diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h index 9cd5f9f..662dd13 100644 --- a/include/media/mediaplayer.h +++ b/include/media/mediaplayer.h @@ -96,6 +96,9 @@ enum media_error_type { enum media_info_type { // 0xx MEDIA_INFO_UNKNOWN = 1, + // The player was started because it was used as the next player for another + // player, which just completed playback + MEDIA_INFO_STARTED_AS_NEXT = 2, // 7xx // The video is too complex for the decoder: it can't decode frames fast // enough. Possibly only the audio plays fine at this stage. @@ -207,6 +210,7 @@ public: status_t setParameter(int key, const Parcel& request); status_t getParameter(int key, Parcel* reply); status_t setRetransmitEndpoint(const char* addrString, uint16_t port); + status_t setNextMediaPlayer(const sp<MediaPlayer>& player); private: void clear_l(); diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp index f586b02..0bb237d 100644 --- a/media/libmedia/IMediaPlayer.cpp +++ b/media/libmedia/IMediaPlayer.cpp @@ -55,6 +55,7 @@ enum { SET_PARAMETER, GET_PARAMETER, SET_RETRANSMIT_ENDPOINT, + SET_NEXT_PLAYER, }; class BpMediaPlayer: public BpInterface<IMediaPlayer> @@ -307,7 +308,15 @@ public: if (OK != err) { return err; } + return reply.readInt32(); + } + status_t setNextPlayer(const sp<IMediaPlayer>& player) { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + sp<IBinder> b(player->asBinder()); + data.writeStrongBinder(b); + remote()->transact(SET_NEXT_PLAYER, data, &reply); return reply.readInt32(); } }; @@ -489,7 +498,11 @@ status_t BnMediaPlayer::onTransact( } else { reply->writeInt32(setRetransmitEndpoint(NULL)); } - + return NO_ERROR; + } break; + case SET_NEXT_PLAYER: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(setNextPlayer(interface_cast<IMediaPlayer>(data.readStrongBinder()))); return NO_ERROR; } break; default: diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 7ea9490..b52a37d 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -788,4 +788,11 @@ void MediaPlayer::died() } +status_t MediaPlayer::setNextMediaPlayer(const sp<MediaPlayer>& next) { + if (mPlayer == NULL) { + return NO_INIT; + } + return mPlayer->setNextPlayer(next == NULL ? NULL : next->mPlayer); +} + }; // namespace android diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index bbc53f3..1cc120c 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -1068,6 +1068,20 @@ status_t MediaPlayerService::Client::getDuration(int *msec) return ret; } +status_t MediaPlayerService::Client::setNextPlayer(const sp<IMediaPlayer>& player) { + ALOGV("setNextPlayer"); + Mutex::Autolock l(mLock); + sp<Client> c = static_cast<Client*>(player.get()); + mNextClient = c; + if (mAudioOutput != NULL && c != NULL) { + mAudioOutput->setNextOutput(c->mAudioOutput); + } else { + ALOGE("no current audio output"); + } + return OK; +} + + status_t MediaPlayerService::Client::seekTo(int msec) { ALOGV("[%d] seekTo(%d)", mConnId, msec); @@ -1189,6 +1203,15 @@ void MediaPlayerService::Client::notify( { Client* client = static_cast<Client*>(cookie); + { + Mutex::Autolock l(client->mLock); + if (msg == MEDIA_PLAYBACK_COMPLETE && client->mNextClient != NULL) { + client->mAudioOutput->switchToNextOutput(); + client->mNextClient->start(); + client->mNextClient->mClient->notify(MEDIA_INFO, MEDIA_INFO_STARTED_AS_NEXT, 0, obj); + } + } + if (MEDIA_INFO == msg && MEDIA_INFO_METADATA_UPDATE == ext1) { const media::Metadata::Type metadata_type = ext2; @@ -1376,9 +1399,11 @@ Exit: MediaPlayerService::AudioOutput::AudioOutput(int sessionId) : mCallback(NULL), mCallbackCookie(NULL), + mCallbackData(NULL), mSessionId(sessionId) { ALOGV("AudioOutput(%d)", sessionId); mTrack = 0; + mRecycledTrack = 0; mStreamType = AUDIO_STREAM_MUSIC; mLeftVolume = 1.0; mRightVolume = 1.0; @@ -1393,6 +1418,8 @@ MediaPlayerService::AudioOutput::AudioOutput(int sessionId) MediaPlayerService::AudioOutput::~AudioOutput() { close(); + delete mRecycledTrack; + delete mCallbackData; } void MediaPlayerService::AudioOutput::setMinBufferCount() @@ -1473,7 +1500,6 @@ status_t MediaPlayerService::AudioOutput::open( } ALOGV("open(%u, %d, 0x%x, %d, %d, %d)", sampleRate, channelCount, channelMask, format, bufferCount, mSessionId); - if (mTrack) close(); int afSampleRate; int afFrameCount; int frameCount; @@ -1494,9 +1520,48 @@ status_t MediaPlayerService::AudioOutput::open( return NO_INIT; } } + if (mRecycledTrack) { + // check if the existing track can be reused as-is, or if a new track needs to be created. + + bool reuse = true; + if ((mCallbackData == NULL && mCallback != NULL) || + (mCallbackData != NULL && mCallback == NULL)) { + // recycled track uses callbacks but the caller wants to use writes, or vice versa + ALOGV("can't chain callback and write"); + reuse = false; + } else if ((mRecycledTrack->getSampleRate() != sampleRate) || + (mRecycledTrack->channelCount() != channelCount) || + (mRecycledTrack->frameCount() != frameCount)) { + ALOGV("samplerate, channelcount or framecount differ"); + reuse = false; + } + if (reuse) { + ALOGV("chaining to next output"); + close(); + mTrack = mRecycledTrack; + mRecycledTrack = NULL; + if (mCallbackData != NULL) { + mCallbackData->setOutput(this); + } + return OK; + } + + // if we're not going to reuse the track, unblock and flush it + if (mCallbackData != NULL) { + mCallbackData->setOutput(NULL); + mCallbackData->endTrackSwitch(); + } + mRecycledTrack->flush(); + delete mRecycledTrack; + mRecycledTrack = NULL; + delete mCallbackData; + mCallbackData = NULL; + close(); + } AudioTrack *t; if (mCallback != NULL) { + mCallbackData = new CallbackData(this); t = new AudioTrack( mStreamType, sampleRate, @@ -1505,7 +1570,7 @@ status_t MediaPlayerService::AudioOutput::open( frameCount, AUDIO_POLICY_OUTPUT_FLAG_NONE, CallbackWrapper, - this, + mCallbackData, 0, mSessionId); } else { @@ -1546,6 +1611,9 @@ status_t MediaPlayerService::AudioOutput::open( void MediaPlayerService::AudioOutput::start() { ALOGV("start"); + if (mCallbackData != NULL) { + mCallbackData->endTrackSwitch(); + } if (mTrack) { mTrack->setVolume(mLeftVolume, mRightVolume); mTrack->setAuxEffectSendLevel(mSendLevel); @@ -1553,7 +1621,26 @@ void MediaPlayerService::AudioOutput::start() } } +void MediaPlayerService::AudioOutput::setNextOutput(const sp<AudioOutput>& nextOutput) { + mNextOutput = nextOutput; +} + +void MediaPlayerService::AudioOutput::switchToNextOutput() { + ALOGV("switchToNextOutput"); + if (mNextOutput != NULL) { + if (mCallbackData != NULL) { + mCallbackData->beginTrackSwitch(); + } + delete mNextOutput->mCallbackData; + mNextOutput->mCallbackData = mCallbackData; + mCallbackData = NULL; + mNextOutput->mRecycledTrack = mTrack; + mTrack = NULL; + mNextOutput->mSampleRateHz = mSampleRateHz; + mNextOutput->mMsecsPerFrame = mMsecsPerFrame; + } +} ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size) { @@ -1646,13 +1733,22 @@ void MediaPlayerService::AudioOutput::CallbackWrapper( return; } - AudioOutput *me = (AudioOutput *)cookie; + CallbackData *data = (CallbackData*)cookie; + data->lock(); + AudioOutput *me = data->getOutput(); AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info; + if (me == NULL) { + // no output set, likely because the track was scheduled to be reused + // by another player, but the format turned out to be incompatible. + data->unlock(); + buffer->size = 0; + return; + } size_t actualSize = (*me->mCallback)( me, buffer->raw, buffer->size, me->mCallbackCookie); - if (actualSize == 0 && buffer->size > 0) { + if (actualSize == 0 && buffer->size > 0 && me->mNextOutput == NULL) { // We've reached EOS but the audio track is not stopped yet, // keep playing silence. @@ -1661,6 +1757,7 @@ void MediaPlayerService::AudioOutput::CallbackWrapper( } buffer->size = actualSize; + data->unlock(); } int MediaPlayerService::AudioOutput::getSessionId() diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 85cec22..d4e0eb1 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -31,6 +31,7 @@ #include <media/IMediaPlayerService.h> #include <media/MediaPlayerInterface.h> #include <media/Metadata.h> +#include <media/stagefright/foundation/ABase.h> #include <system/audio.h> @@ -69,7 +70,9 @@ class MediaPlayerService : public BnMediaPlayerService class AudioOutput : public MediaPlayerBase::AudioSink { - public: + class CallbackData; + + public: AudioOutput(int sessionId); virtual ~AudioOutput(); @@ -104,14 +107,21 @@ class MediaPlayerService : public BnMediaPlayerService static bool isOnEmulator(); static int getMinBufferCount(); + void setNextOutput(const sp<AudioOutput>& nextOutput); + void switchToNextOutput(); + virtual bool needsTrailingPadding() { return mNextOutput == NULL; } + private: static void setMinBufferCount(); static void CallbackWrapper( int event, void *me, void *info); AudioTrack* mTrack; + AudioTrack* mRecycledTrack; + sp<AudioOutput> mNextOutput; AudioCallback mCallback; void * mCallbackCookie; + CallbackData * mCallbackData; audio_stream_type_t mStreamType; float mLeftVolume; float mRightVolume; @@ -124,7 +134,38 @@ class MediaPlayerService : public BnMediaPlayerService static bool mIsOnEmulator; static int mMinBufferCount; // 12 for emulator; otherwise 4 - }; + // CallbackData is what is passed to the AudioTrack as the "user" data. + // We need to be able to target this to a different Output on the fly, + // so we can't use the Output itself for this. + class CallbackData { + public: + CallbackData(AudioOutput *cookie) { + mData = cookie; + mSwitching = false; + } + AudioOutput * getOutput() { return mData;} + void setOutput(AudioOutput* newcookie) { mData = newcookie; } + // lock/unlock are used by the callback before accessing the payload of this object + void lock() { mLock.lock(); } + void unlock() { mLock.unlock(); } + // beginTrackSwitch/endTrackSwitch are used when this object is being handed over + // to the next sink. + void beginTrackSwitch() { mLock.lock(); mSwitching = true; } + void endTrackSwitch() { + if (mSwitching) { + mLock.unlock(); + } + mSwitching = false; + } + private: + AudioOutput * mData; + mutable Mutex mLock; + bool mSwitching; + DISALLOW_EVIL_CONSTRUCTORS(CallbackData); + }; + + }; // AudioOutput + class AudioCache : public MediaPlayerBase::AudioSink { @@ -184,7 +225,7 @@ class MediaPlayerService : public BnMediaPlayerService bool mCommandComplete; sp<Thread> mCallbackThread; - }; + }; // AudioCache public: static void instantiate(); @@ -278,6 +319,7 @@ private: virtual status_t setParameter(int key, const Parcel &request); virtual status_t getParameter(int key, Parcel *reply); virtual status_t setRetransmitEndpoint(const struct sockaddr_in* endpoint); + virtual status_t setNextPlayer(const sp<IMediaPlayer>& player); sp<MediaPlayerBase> createPlayer(player_type playerType); @@ -350,6 +392,7 @@ private: sp<IBinder> mConnectedWindowBinder; struct sockaddr_in mRetransmitEndpoint; bool mRetransmitEndpointValid; + sp<Client> mNextClient; // Metadata filters. media::Metadata::Filter mMetadataAllow; // protected by mLock @@ -364,7 +407,7 @@ private: #if CALLBACK_ANTAGONIZER Antagonizer* mAntagonizer; #endif - }; + }; // Client // ---------------------------------------------------------------------------- diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index 23c3c74..9db3c97 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -419,7 +419,11 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { timeToCompletionUs, timeToCompletionUs / 1E6); postEOS = true; - postEOSDelayUs = timeToCompletionUs + mLatencyUs; + if (mAudioSink->needsTrailingPadding()) { + postEOSDelayUs = timeToCompletionUs + mLatencyUs; + } else { + postEOSDelayUs = 0; + } } mReachedEOS = true; |