summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarco Nelissen <marcone@google.com>2012-03-13 13:23:07 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2012-03-13 13:23:07 -0700
commit36a4d2cc7a44eb42d0d9ef5fe50b6747a7a34cab (patch)
treeeec0f805e2e83ed1d720519748527c03affafc29
parent5e7616cc0e5493241211333134555d74aec89982 (diff)
parent6b74d671a1321a6ecc4a40b6c87beedfecc1ec44 (diff)
downloadframeworks_av-36a4d2cc7a44eb42d0d9ef5fe50b6747a7a34cab.zip
frameworks_av-36a4d2cc7a44eb42d0d9ef5fe50b6747a7a34cab.tar.gz
frameworks_av-36a4d2cc7a44eb42d0d9ef5fe50b6747a7a34cab.tar.bz2
Merge "Gapless playback, step 1."
-rw-r--r--include/media/IMediaPlayer.h1
-rw-r--r--include/media/MediaPlayerInterface.h1
-rw-r--r--include/media/mediaplayer.h4
-rw-r--r--media/libmedia/IMediaPlayer.cpp15
-rw-r--r--media/libmedia/mediaplayer.cpp7
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp105
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.h51
-rw-r--r--media/libstagefright/AudioPlayer.cpp6
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;