diff options
author | Marco Nelissen <marcone@google.com> | 2012-02-28 16:07:44 -0800 |
---|---|---|
committer | Marco Nelissen <marcone@google.com> | 2012-03-13 13:13:14 -0700 |
commit | 84b832054552e00257bb04997143ca33d6d100be (patch) | |
tree | cf04f15f15da2d9ddbd2112b2b89542f6270b4ce /media/libmediaplayerservice | |
parent | f19d5f0271a9e0cdf3a79c6ccab5aa151b0b3239 (diff) | |
download | frameworks_base-84b832054552e00257bb04997143ca33d6d100be.zip frameworks_base-84b832054552e00257bb04997143ca33d6d100be.tar.gz frameworks_base-84b832054552e00257bb04997143ca33d6d100be.tar.bz2 |
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
Diffstat (limited to 'media/libmediaplayerservice')
-rw-r--r-- | media/libmediaplayerservice/MediaPlayerService.cpp | 105 | ||||
-rw-r--r-- | media/libmediaplayerservice/MediaPlayerService.h | 51 |
2 files changed, 148 insertions, 8 deletions
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<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, 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<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 // ---------------------------------------------------------------------------- |