Implement AudioTrackCallback in MediaPlayerService
Modify MediaPlayerService to implement AudioTrackCallback rather
than passing a static function callback.
Test: YT music offline playback successful. Data callbacks logged.
Test: MediaPlayerTest
Change-Id: I8864125025011f04b7114f3c5ffec560c11ac5bd
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index d63a002..7e180a2 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -146,6 +146,7 @@
"spatializer-aidl-cpp",
"framework-permission-aidl-cpp",
"libbinder",
+ "libmediametrics",
],
include_dirs: [
diff --git a/media/libmediaplayerservice/Android.bp b/media/libmediaplayerservice/Android.bp
index c416a51..e70e3b3 100644
--- a/media/libmediaplayerservice/Android.bp
+++ b/media/libmediaplayerservice/Android.bp
@@ -70,6 +70,7 @@
header_libs: [
"media_plugin_headers",
+ "libmediautils_headers",
],
static_libs: [
@@ -85,6 +86,10 @@
"framework-permission-aidl-cpp",
],
+ export_header_lib_headers: [
+ "libmediautils_headers",
+ ],
+
include_dirs: [
"frameworks/av/media/libstagefright/rtsp",
"frameworks/av/media/libstagefright/webm",
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index f85887e..53e3d52 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1831,7 +1831,6 @@
{
close();
free(mAttributes);
- delete mCallbackData;
}
//static
@@ -2052,8 +2051,7 @@
mRecycledTrack.clear();
close_l();
- delete mCallbackData;
- mCallbackData = NULL;
+ mCallbackData.clear();
}
}
@@ -2174,7 +2172,7 @@
}
sp<AudioTrack> t;
- CallbackData *newcbd = NULL;
+ sp<CallbackData> newcbd;
// We don't attempt to create a new track if we are recycling an
// offloaded track. But, if we are recycling a non-offloaded or we
@@ -2184,8 +2182,8 @@
if (!(reuse && bothOffloaded)) {
ALOGV("creating new AudioTrack");
- if (mCallback != NULL) {
- newcbd = new CallbackData(this);
+ if (mCallback != nullptr) {
+ newcbd = sp<CallbackData>::make(wp<AudioOutput>::fromExisting(this));
t = new AudioTrack(
mStreamType,
sampleRate,
@@ -2193,7 +2191,6 @@
channelMask,
frameCount,
flags,
- CallbackWrapper,
newcbd,
0, // notification frames
mSessionId,
@@ -2237,8 +2234,7 @@
t->setCallerName("media");
if ((t == 0) || (t->initCheck() != NO_ERROR)) {
ALOGE("Unable to create audio track");
- delete newcbd;
- // t goes out of scope, so reference count drops to zero
+ // t, newcbd goes out of scope, so reference count drops to zero
return NO_INIT;
} else {
// successful AudioTrack initialization implies a legacy stream type was generated
@@ -2272,7 +2268,6 @@
if (mCallbackData != NULL) {
mCallbackData->setOutput(this);
}
- delete newcbd;
return updateTrack();
}
}
@@ -2378,7 +2373,7 @@
if (mCallbackData != NULL) {
// two alternative approaches
#if 1
- CallbackData *callbackData = mCallbackData;
+ sp<CallbackData> callbackData = mCallbackData;
mLock.unlock();
// proper acquisition sequence
callbackData->lock();
@@ -2415,9 +2410,8 @@
// for example, the next player could be prepared and seeked.
//
// Presuming it isn't advisable to force the track over.
- if (mNextOutput->mTrack == NULL) {
+ if (mNextOutput->mTrack == nullptr) {
ALOGD("Recycling track for gapless playback");
- delete mNextOutput->mCallbackData;
mNextOutput->mCallbackData = mCallbackData;
mNextOutput->mRecycledTrack = mTrack;
mNextOutput->mSampleRateHz = mSampleRateHz;
@@ -2425,11 +2419,11 @@
mNextOutput->mFlags = mFlags;
mNextOutput->mFrameSize = mFrameSize;
close_l();
- mCallbackData = NULL; // destruction handled by mNextOutput
+ mCallbackData.clear();
} else {
ALOGW("Ignoring gapless playback because next player has already started");
// remove track in case resource needed for future players.
- if (mCallbackData != NULL) {
+ if (mCallbackData != nullptr) {
mCallbackData->endTrackSwitch(); // release lock for callbacks before close.
}
close_l();
@@ -2656,76 +2650,71 @@
}
}
-// static
-void MediaPlayerService::AudioOutput::CallbackWrapper(
- int event, void *cookie, void *info) {
- //ALOGV("callbackwrapper");
- CallbackData *data = (CallbackData*)cookie;
- // lock to ensure we aren't caught in the middle of a track switch.
- 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();
- if (buffer != NULL) {
- buffer->size = 0;
- }
+size_t MediaPlayerService::AudioOutput::CallbackData::onMoreData(const AudioTrack::Buffer& buffer) {
+ ALOGD("data callback");
+ lock();
+ sp<AudioOutput> me = getOutput();
+ if (me == nullptr) {
+ unlock();
+ return 0;
+ }
+ size_t actualSize = (*me->mCallback)(
+ me.get(), buffer.raw, buffer.size, me->mCallbackCookie,
+ CB_EVENT_FILL_BUFFER);
+
+ // Log when no data is returned from the callback.
+ // (1) We may have no data (especially with network streaming sources).
+ // (2) We may have reached the EOS and the audio track is not stopped yet.
+ // Note that AwesomePlayer/AudioPlayer will only return zero size when it reaches the EOS.
+ // NuPlayerRenderer will return zero when it doesn't have data (it doesn't block to fill).
+ //
+ // This is a benign busy-wait, with the next data request generated 10 ms or more later;
+ // nevertheless for power reasons, we don't want to see too many of these.
+
+ ALOGV_IF(actualSize == 0 && buffer->size > 0, "callbackwrapper: empty buffer returned");
+ unlock();
+ return actualSize;
+}
+
+void MediaPlayerService::AudioOutput::CallbackData::onStreamEnd() {
+ lock();
+ sp<AudioOutput> me = getOutput();
+ if (me == nullptr) {
+ unlock();
return;
}
+ ALOGV("callbackwrapper: deliver EVENT_STREAM_END");
+ (*me->mCallback)(me.get(), NULL /* buffer */, 0 /* size */,
+ me->mCallbackCookie, CB_EVENT_STREAM_END);
+ unlock();
+}
- switch(event) {
- case AudioTrack::EVENT_MORE_DATA: {
- size_t actualSize = (*me->mCallback)(
- me, buffer->raw, buffer->size, me->mCallbackCookie,
- CB_EVENT_FILL_BUFFER);
- // Log when no data is returned from the callback.
- // (1) We may have no data (especially with network streaming sources).
- // (2) We may have reached the EOS and the audio track is not stopped yet.
- // Note that AwesomePlayer/AudioPlayer will only return zero size when it reaches the EOS.
- // NuPlayerRenderer will return zero when it doesn't have data (it doesn't block to fill).
- //
- // This is a benign busy-wait, with the next data request generated 10 ms or more later;
- // nevertheless for power reasons, we don't want to see too many of these.
-
- ALOGV_IF(actualSize == 0 && buffer->size > 0, "callbackwrapper: empty buffer returned");
-
- buffer->size = actualSize;
- } break;
-
- case AudioTrack::EVENT_STREAM_END:
- // currently only occurs for offloaded callbacks
- ALOGV("callbackwrapper: deliver EVENT_STREAM_END");
- (*me->mCallback)(me, NULL /* buffer */, 0 /* size */,
- me->mCallbackCookie, CB_EVENT_STREAM_END);
- break;
-
- case AudioTrack::EVENT_NEW_IAUDIOTRACK :
- ALOGV("callbackwrapper: deliver EVENT_TEAR_DOWN");
- (*me->mCallback)(me, NULL /* buffer */, 0 /* size */,
- me->mCallbackCookie, CB_EVENT_TEAR_DOWN);
- break;
-
- case AudioTrack::EVENT_UNDERRUN:
- // This occurs when there is no data available, typically
- // when there is a failure to supply data to the AudioTrack. It can also
- // occur in non-offloaded mode when the audio device comes out of standby.
- //
- // If an AudioTrack underruns it outputs silence. Since this happens suddenly
- // it may sound like an audible pop or glitch.
- //
- // The underrun event is sent once per track underrun; the condition is reset
- // when more data is sent to the AudioTrack.
- ALOGD("callbackwrapper: EVENT_UNDERRUN (discarded)");
- break;
-
- default:
- ALOGE("received unknown event type: %d inside CallbackWrapper !", event);
+void MediaPlayerService::AudioOutput::CallbackData::onNewIAudioTrack() {
+ lock();
+ sp<AudioOutput> me = getOutput();
+ if (me == nullptr) {
+ unlock();
+ return;
}
+ ALOGV("callbackwrapper: deliver EVENT_TEAR_DOWN");
+ (*me->mCallback)(me.get(), NULL /* buffer */, 0 /* size */,
+ me->mCallbackCookie, CB_EVENT_TEAR_DOWN);
+ unlock();
+}
- data->unlock();
+void MediaPlayerService::AudioOutput::CallbackData::onUnderrun() {
+ // This occurs when there is no data available, typically
+ // when there is a failure to supply data to the AudioTrack. It can also
+ // occur in non-offloaded mode when the audio device comes out of standby.
+ //
+ // If an AudioTrack underruns it outputs silence. Since this happens suddenly
+ // it may sound like an audible pop or glitch.
+ //
+ // The underrun event is sent once per track underrun; the condition is reset
+ // when more data is sent to the AudioTrack.
+ ALOGD("callbackwrapper: EVENT_UNDERRUN (discarded)");
+
}
audio_session_t MediaPlayerService::AudioOutput::getSessionId() const
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 98091be..86be3fe 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -30,9 +30,11 @@
#include <media/AidlConversion.h>
#include <media/AudioResamplerPublic.h>
#include <media/AudioSystem.h>
+#include <media/AudioTrack.h>
#include <media/MediaPlayerInterface.h>
#include <media/Metadata.h>
#include <media/stagefright/foundation/ABase.h>
+#include <mediautils/Synchronization.h>
#include <android/content/AttributionSourceState.h>
#include <system/audio.h>
@@ -41,7 +43,6 @@
using content::AttributionSourceState;
-class AudioTrack;
struct AVSyncSettings;
class DeathNotifier;
class IDataSource;
@@ -161,7 +162,7 @@
sp<AudioOutput> mNextOutput;
AudioCallback mCallback;
void * mCallbackCookie;
- CallbackData * mCallbackData;
+ sp<CallbackData> mCallbackData;
audio_stream_type_t mStreamType;
audio_attributes_t * mAttributes;
float mLeftVolume;
@@ -189,15 +190,15 @@
// 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 {
+ class CallbackData : public AudioTrack::IAudioTrackCallback {
friend AudioOutput;
public:
- explicit CallbackData(AudioOutput *cookie) {
+ explicit CallbackData(const wp<AudioOutput>& cookie) {
mData = cookie;
mSwitching = false;
}
- AudioOutput * getOutput() const { return mData; }
- void setOutput(AudioOutput* newcookie) { mData = newcookie; }
+ sp<AudioOutput> getOutput() const { return mData.load().promote(); }
+ void setOutput(const wp<AudioOutput>& newcookie) { mData.store(newcookie); }
// lock/unlock are used by the callback before accessing the payload of this object
void lock() const { mLock.lock(); }
void unlock() const { mLock.unlock(); }
@@ -220,8 +221,13 @@
}
mSwitching = false;
}
+ protected:
+ size_t onMoreData(const AudioTrack::Buffer& buffer) override;
+ void onUnderrun() override;
+ void onStreamEnd() override;
+ void onNewIAudioTrack() override;
private:
- AudioOutput * mData;
+ mediautils::atomic_wp<AudioOutput> mData;
mutable Mutex mLock; // a recursive mutex might make this unnecessary.
bool mSwitching;
DISALLOW_EVIL_CONSTRUCTORS(CallbackData);