From 6b74d671a1321a6ecc4a40b6c87beedfecc1ec44 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 28 Feb 2012 16:07:44 -0800 Subject: Gapless playback, step 1. Currently able to play Ogg Vorbis, PCM WAV and other lossless files seamlessly by reusing the initial AudioTrack for subsequent players. Change-Id: Ie7cf6b9076bdf4f9211574456d192c02c04fecc7 --- media/libmedia/IMediaPlayer.cpp | 15 ++- media/libmedia/mediaplayer.cpp | 7 ++ media/libmediaplayerservice/MediaPlayerService.cpp | 105 ++++++++++++++++++++- media/libmediaplayerservice/MediaPlayerService.h | 51 +++++++++- media/libstagefright/AudioPlayer.cpp | 6 +- 5 files changed, 174 insertions(+), 10 deletions(-) (limited to 'media') diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp index c47fa41..16ba484 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 @@ -307,7 +308,15 @@ public: if (OK != err) { return err; } + return reply.readInt32(); + } + status_t setNextPlayer(const sp& player) { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + sp 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(data.readStrongBinder()))); return NO_ERROR; } break; default: diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 4ff1862..eedb3ce 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -788,4 +788,11 @@ void MediaPlayer::died() } +status_t MediaPlayer::setNextMediaPlayer(const sp& 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 1a85c9c..657cb3d 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& player) { + ALOGV("setNextPlayer"); + Mutex::Autolock l(mLock); + sp c = static_cast(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(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, 0 /* flags */, 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& 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 #include #include +#include #include @@ -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& 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 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 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& player); sp createPlayer(player_type playerType); @@ -350,6 +392,7 @@ private: sp mConnectedWindowBinder; struct sockaddr_in mRetransmitEndpoint; bool mRetransmitEndpointValid; + sp 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 2b3cb1a..f84e37f 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; -- cgit v1.1