OSDN Git Service

Gapless playback, step 1.
authorMarco Nelissen <marcone@google.com>
Wed, 29 Feb 2012 00:07:44 +0000 (16:07 -0800)
committerMarco Nelissen <marcone@google.com>
Tue, 13 Mar 2012 20:13:14 +0000 (13:13 -0700)
Currently able to play Ogg Vorbis, PCM WAV and other lossless files seamlessly
by reusing the initial AudioTrack for subsequent players.

Change-Id: Ie7cf6b9076bdf4f9211574456d192c02c04fecc7

include/media/IMediaPlayer.h
include/media/MediaPlayerInterface.h
include/media/mediaplayer.h
media/libmedia/IMediaPlayer.cpp
media/libmedia/mediaplayer.cpp
media/libmediaplayerservice/MediaPlayerService.cpp
media/libmediaplayerservice/MediaPlayerService.h
media/libstagefright/AudioPlayer.cpp

index 39d58ab..00facc5 100644 (file)
@@ -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.
index 8168dff..d4aa233 100644 (file)
@@ -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) {}
index 9cd5f9f..662dd13 100644 (file)
@@ -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();
index c47fa41..16ba484 100644 (file)
@@ -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:
index 4ff1862..eedb3ce 100644 (file)
@@ -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
index 1a85c9c..657cb3d 100644 (file)
@@ -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()
index 85cec22..d4e0eb1 100644 (file)
@@ -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
 
 // ----------------------------------------------------------------------------
 
index 2b3cb1a..f84e37f 100644 (file)
@@ -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;