Merge "CCodec: buffer handling refactoring"
diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h
index 67003c1..51cef8c 100644
--- a/camera/ndk/include/camera/NdkCameraCaptureSession.h
+++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h
@@ -33,6 +33,7 @@
* Do not #include files that aren't part of the NDK.
*/
#include <sys/cdefs.h>
+#include <stdbool.h>
#include <android/native_window.h>
#include "NdkCameraError.h"
diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h
index f425830..b715b12 100644
--- a/camera/ndk/include/camera/NdkCameraDevice.h
+++ b/camera/ndk/include/camera/NdkCameraDevice.h
@@ -153,6 +153,11 @@
} ACameraDevice_StateCallbacks;
/**
+ * For backward compatiblity.
+ */
+typedef ACameraDevice_StateCallbacks ACameraDevice_stateCallbacks;
+
+/**
* Close the connection and free this ACameraDevice synchronously. Access to the ACameraDevice
* after calling this method will cause a crash.
*
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 57c5745..068a52b 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -261,20 +261,39 @@
}
void DrmHal::closeOpenSessions() {
- if (mPlugin != NULL) {
- for (size_t i = 0; i < mOpenSessions.size(); i++) {
- mPlugin->closeSession(toHidlVec(mOpenSessions[i]));
- DrmSessionManager::Instance()->removeSession(mOpenSessions[i]);
- }
+ Mutex::Autolock autoLock(mLock);
+ auto openSessions = mOpenSessions;
+ for (size_t i = 0; i < openSessions.size(); i++) {
+ mLock.unlock();
+ closeSession(openSessions[i]);
+ mLock.lock();
}
mOpenSessions.clear();
}
DrmHal::~DrmHal() {
- closeOpenSessions();
DrmSessionManager::Instance()->removeDrm(mDrmSessionClient);
}
+void DrmHal::cleanup() {
+ closeOpenSessions();
+
+ Mutex::Autolock autoLock(mLock);
+ reportPluginMetrics();
+ reportFrameworkMetrics();
+
+ setListener(NULL);
+ mInitCheck = NO_INIT;
+
+ if (mPlugin != NULL) {
+ if (!mPlugin->setListener(NULL).isOk()) {
+ mInitCheck = DEAD_OBJECT;
+ }
+ }
+ mPlugin.clear();
+ mPluginV1_1.clear();
+}
+
Vector<sp<IDrmFactory>> DrmHal::makeDrmFactories() {
Vector<sp<IDrmFactory>> factories;
@@ -512,22 +531,7 @@
}
status_t DrmHal::destroyPlugin() {
- Mutex::Autolock autoLock(mLock);
- INIT_CHECK();
-
- closeOpenSessions();
- reportPluginMetrics();
- reportFrameworkMetrics();
- setListener(NULL);
- mInitCheck = NO_INIT;
-
- if (mPlugin != NULL) {
- if (!mPlugin->setListener(NULL).isOk()) {
- mInitCheck = DEAD_OBJECT;
- }
- }
- mPlugin.clear();
- mPluginV1_1.clear();
+ cleanup();
return OK;
}
@@ -633,7 +637,6 @@
status_t response = toStatusT(status);
mMetrics.SetSessionEnd(sessionId);
mMetrics.mCloseSessionCounter.Increment(response);
- reportPluginMetrics();
return response;
}
mMetrics.mCloseSessionCounter.Increment(DEAD_OBJECT);
@@ -1267,17 +1270,7 @@
void DrmHal::binderDied(const wp<IBinder> &the_late_who __unused)
{
- Mutex::Autolock autoLock(mLock);
- closeOpenSessions();
- setListener(NULL);
- mInitCheck = NO_INIT;
-
- if (mPlugin != NULL) {
- if (!mPlugin->setListener(NULL).isOk()) {
- mInitCheck = DEAD_OBJECT;
- }
- }
- mPlugin.clear();
+ cleanup();
}
void DrmHal::writeByteArray(Parcel &obj, hidl_vec<uint8_t> const &vec)
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index ff440bc..b4fa3c5 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -438,7 +438,11 @@
return 0;
}
- virtual uint32_t getUnderrunFrames() const {
+ virtual uint32_t getUnderrunFrames() const override {
+ return 0;
+ }
+
+ virtual uint32_t getUnderrunCount() const override {
return 0;
}
diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h
index 2e29316..cf7d90f 100644
--- a/media/libaudioclient/include/media/AudioMixer.h
+++ b/media/libaudioclient/include/media/AudioMixer.h
@@ -46,10 +46,6 @@
class AudioMixer
{
public:
- // This mixer has a hard-coded upper limit of active track inputs;
- // the value is arbitrary but should be less than TRACK0 to avoid confusion.
- static constexpr int32_t MAX_NUM_TRACKS = 256;
-
// Do not change these unless underlying code changes.
// This mixer has a hard-coded upper limit of 8 channels for output.
static constexpr uint32_t MAX_NUM_CHANNELS = FCC_8;
@@ -61,12 +57,6 @@
static const CONSTEXPR float UNITY_GAIN_FLOAT = 1.0f;
enum { // names
-
- // track names (MAX_NUM_TRACKS units)
- TRACK0 = 0x1000,
-
- // 0x2000 is unused
-
// setParameter targets
TRACK = 0x3000,
RESAMPLE = 0x3001,
@@ -105,23 +95,33 @@
// parameter 'value' is a pointer to the new playback rate.
};
- AudioMixer(size_t frameCount, uint32_t sampleRate, int32_t maxNumTracks = MAX_NUM_TRACKS)
- : mMaxNumTracks(maxNumTracks)
- , mSampleRate(sampleRate)
+ AudioMixer(size_t frameCount, uint32_t sampleRate)
+ : mSampleRate(sampleRate)
, mFrameCount(frameCount) {
pthread_once(&sOnceControl, &sInitRoutine);
}
- // For all APIs with "name": TRACK0 <= name < TRACK0 + MAX_NUM_TRACKS
+ // Create a new track in the mixer.
+ //
+ // \param name a unique user-provided integer associated with the track.
+ // If name already exists, the function will abort.
+ // \param channelMask output channel mask.
+ // \param format PCM format
+ // \param sessionId Session id for the track. Tracks with the same
+ // session id will be submixed together.
+ //
+ // \return OK on success.
+ // BAD_VALUE if the format does not satisfy isValidFormat()
+ // or the channelMask does not satisfy isValidChannelMask().
+ status_t create(
+ int name, audio_channel_mask_t channelMask, audio_format_t format, int sessionId);
- // Allocate a track name. Returns new track name if successful, -1 on failure.
- // The failure could be because of an invalid channelMask or format, or that
- // the track capacity of the mixer is exceeded.
- int getTrackName(audio_channel_mask_t channelMask,
- audio_format_t format, int sessionId);
+ bool exists(int name) const {
+ return mTracks.count(name) > 0;
+ }
- // Free an allocated track by name
- void deleteTrackName(int name);
+ // Free an allocated track by name.
+ void destroy(int name);
// Enable or disable an allocated track by name
void enable(int name);
@@ -149,6 +149,23 @@
mNBLogWriter = logWriter;
}
+ static inline bool isValidFormat(audio_format_t format) {
+ switch (format) {
+ case AUDIO_FORMAT_PCM_8_BIT:
+ case AUDIO_FORMAT_PCM_16_BIT:
+ case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+ case AUDIO_FORMAT_PCM_32_BIT:
+ case AUDIO_FORMAT_PCM_FLOAT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static inline bool isValidChannelMask(audio_channel_mask_t channelMask) {
+ return audio_channel_mask_is_valid(channelMask); // the RemixBufferProvider is flexible.
+ }
+
private:
/* For multi-format functions (calls template functions
@@ -361,23 +378,9 @@
static void convertMixerFormat(void *out, audio_format_t mixerOutFormat,
void *in, audio_format_t mixerInFormat, size_t sampleCount);
- static inline bool isValidPcmTrackFormat(audio_format_t format) {
- switch (format) {
- case AUDIO_FORMAT_PCM_8_BIT:
- case AUDIO_FORMAT_PCM_16_BIT:
- case AUDIO_FORMAT_PCM_24_BIT_PACKED:
- case AUDIO_FORMAT_PCM_32_BIT:
- case AUDIO_FORMAT_PCM_FLOAT:
- return true;
- default:
- return false;
- }
- }
-
static void sInitRoutine();
// initialization constants
- const int mMaxNumTracks;
const uint32_t mSampleRate;
const size_t mFrameCount;
@@ -390,12 +393,6 @@
std::unique_ptr<int32_t[]> mOutputTemp;
std::unique_ptr<int32_t[]> mResampleTemp;
- // fast lookup of previously deleted track names for reuse.
- // the AudioMixer tries to return the smallest unused name -
- // this is an arbitrary decision (actually any non-negative
- // integer that isn't in mTracks could be used).
- std::set<int /* name */> mUnusedNames; // set of unused track names (may be empty)
-
// track names grouped by main buffer, in no particular order of main buffer.
// however names for a particular main buffer are in order (by construction).
std::unordered_map<void * /* mainBuffer */, std::vector<int /* name */>> mGroups;
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index f1daeb4..2042913 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -90,34 +90,21 @@
return kUseFloat && kUseNewMixer ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
}
-int AudioMixer::getTrackName(
- audio_channel_mask_t channelMask, audio_format_t format, int sessionId)
+status_t AudioMixer::create(
+ int name, audio_channel_mask_t channelMask, audio_format_t format, int sessionId)
{
- if (!isValidPcmTrackFormat(format)) {
- ALOGE("AudioMixer::getTrackName invalid format (%#x)", format);
- return -1;
- }
- if (mTracks.size() >= (size_t)mMaxNumTracks) {
- ALOGE("%s: out of track names (max = %d)", __func__, mMaxNumTracks);
- return -1;
- }
+ LOG_ALWAYS_FATAL_IF(exists(name), "name %d already exists", name);
- // get a new name for the track.
- int name;
- if (mUnusedNames.size() != 0) {
- // reuse first name for deleted track.
- auto it = mUnusedNames.begin();
- name = *it;
- (void)mUnusedNames.erase(it);
- } else {
- // we're fully populated, so create a new name.
- name = mTracks.size();
+ if (!isValidChannelMask(channelMask)) {
+ ALOGE("%s invalid channelMask: %#x", __func__, channelMask);
+ return BAD_VALUE;
}
- ALOGV("add track (%d)", name);
+ if (!isValidFormat(format)) {
+ ALOGE("%s invalid format: %#x", __func__, format);
+ return BAD_VALUE;
+ }
auto t = std::make_shared<Track>();
- mTracks[name] = t;
-
{
// TODO: move initialization to the Track constructor.
// assume default parameters for the track, except where noted below
@@ -179,12 +166,14 @@
status_t status = t->prepareForDownmix();
if (status != OK) {
ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask);
- return -1;
+ return BAD_VALUE;
}
// prepareForDownmix() may change mDownmixRequiresFormat
ALOGVV("mMixerFormat:%#x mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat);
t->prepareForReformat();
- return TRACK0 + name;
+
+ mTracks[name] = t;
+ return OK;
}
}
@@ -193,7 +182,7 @@
// which will simplify this logic.
bool AudioMixer::setChannelMasks(int name,
audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask) {
- LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
const std::shared_ptr<Track> &track = mTracks[name];
if (trackChannelMask == track->channelMask
@@ -361,23 +350,20 @@
}
}
-void AudioMixer::deleteTrackName(int name)
+void AudioMixer::destroy(int name)
{
- name -= TRACK0;
- LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
ALOGV("deleteTrackName(%d)", name);
if (mTracks[name]->enabled) {
invalidate();
}
mTracks.erase(name); // deallocate track
- mUnusedNames.emplace(name); // recycle name
}
void AudioMixer::enable(int name)
{
- name -= TRACK0;
- LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
const std::shared_ptr<Track> &track = mTracks[name];
if (!track->enabled) {
@@ -389,8 +375,7 @@
void AudioMixer::disable(int name)
{
- name -= TRACK0;
- LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
const std::shared_ptr<Track> &track = mTracks[name];
if (track->enabled) {
@@ -528,8 +513,7 @@
void AudioMixer::setParameter(int name, int target, int param, void *value)
{
- name -= TRACK0;
- LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
const std::shared_ptr<Track> &track = mTracks[name];
int valueInt = static_cast<int>(reinterpret_cast<uintptr_t>(value));
@@ -808,7 +792,6 @@
size_t AudioMixer::getUnreleasedFrames(int name) const
{
- name -= TRACK0;
const auto it = mTracks.find(name);
if (it != mTracks.end()) {
return it->second->getUnreleasedFrames();
@@ -818,7 +801,7 @@
void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider)
{
- name -= TRACK0;
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
const std::shared_ptr<Track> &track = mTracks[name];
if (track->mInputBufferProvider == bufferProvider) {
diff --git a/media/libaudioprocessing/tests/test-mixer.cpp b/media/libaudioprocessing/tests/test-mixer.cpp
index b67810d..bc9d2a6 100644
--- a/media/libaudioprocessing/tests/test-mixer.cpp
+++ b/media/libaudioprocessing/tests/test-mixer.cpp
@@ -143,10 +143,6 @@
usage(progname);
return EXIT_FAILURE;
}
- if ((unsigned)argc > AudioMixer::MAX_NUM_TRACKS) {
- fprintf(stderr, "too many tracks: %d > %u", argc, AudioMixer::MAX_NUM_TRACKS);
- return EXIT_FAILURE;
- }
size_t outputFrames = 0;
@@ -246,9 +242,10 @@
for (size_t i = 0; i < providers.size(); ++i) {
//printf("track %d out of %d\n", i, providers.size());
uint32_t channelMask = audio_channel_out_mask_from_count(providers[i].getNumChannels());
- int32_t name = mixer->getTrackName(channelMask,
- formats[i], AUDIO_SESSION_OUTPUT_MIX);
- ALOG_ASSERT(name >= 0);
+ const int name = i;
+ const status_t status = mixer->create(
+ name, channelMask, formats[i], AUDIO_SESSION_OUTPUT_MIX);
+ LOG_ALWAYS_FATAL_IF(status != OK);
names[i] = name;
mixer->setBufferProvider(name, &providers[i]);
mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
@@ -315,8 +312,10 @@
writeFile(outputFilename, outputAddr,
outputSampleRate, outputChannels, outputFrames, useMixerFloat);
if (auxFilename) {
- // Aux buffer is always in q4_27 format for now.
- memcpy_to_i16_from_q4_27((int16_t*)auxAddr, (const int32_t*)auxAddr, outputFrames);
+ // Aux buffer is always in q4_27 format for O and earlier.
+ // memcpy_to_i16_from_q4_27((int16_t*)auxAddr, (const int32_t*)auxAddr, outputFrames);
+ // Aux buffer is always in float format for P.
+ memcpy_to_i16_from_float((int16_t*)auxAddr, (const float*)auxAddr, outputFrames);
writeFile(auxFilename, auxAddr, outputSampleRate, 1, outputFrames, false);
}
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index bf91ea9..c64b003 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -188,6 +188,7 @@
Vector<Vector<uint8_t>> mOpenSessions;
void closeOpenSessions();
+ void cleanup();
/**
* mInitCheck is:
diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp
index 17fa01c..1fa8789 100644
--- a/media/libmediaplayer2/Android.bp
+++ b/media/libmediaplayer2/Android.bp
@@ -9,7 +9,7 @@
srcs: [
"JAudioTrack.cpp",
- "MediaPlayer2Manager.cpp",
+ "MediaPlayer2AudioOutput.cpp",
"mediaplayer2.cpp",
],
diff --git a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp
new file mode 100644
index 0000000..a8e1d1f
--- /dev/null
+++ b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp
@@ -0,0 +1,727 @@
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaPlayer2AudioOutput"
+#include <mediaplayer2/MediaPlayer2AudioOutput.h>
+
+#include <cutils/properties.h> // for property_get
+#include <utils/Log.h>
+
+#include <media/AudioPolicyHelper.h>
+#include <media/AudioTrack.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace {
+
+const float kMaxRequiredSpeed = 8.0f; // for PCM tracks allow up to 8x speedup.
+
+} // anonymous namespace
+
+namespace android {
+
+// TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround
+/* static */ int MediaPlayer2AudioOutput::mMinBufferCount = 4;
+/* static */ bool MediaPlayer2AudioOutput::mIsOnEmulator = false;
+
+status_t MediaPlayer2AudioOutput::dump(int fd, const Vector<String16>& args) const {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ result.append(" MediaPlayer2AudioOutput\n");
+ snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n",
+ mStreamType, mLeftVolume, mRightVolume);
+ result.append(buffer);
+ snprintf(buffer, 255, " msec per frame(%f), latency (%d)\n",
+ mMsecsPerFrame, (mTrack != 0) ? mTrack->latency() : -1);
+ result.append(buffer);
+ snprintf(buffer, 255, " aux effect id(%d), send level (%f)\n",
+ mAuxEffectId, mSendLevel);
+ result.append(buffer);
+
+ ::write(fd, result.string(), result.size());
+ if (mTrack != 0) {
+ mTrack->dump(fd, args);
+ }
+ return NO_ERROR;
+}
+
+MediaPlayer2AudioOutput::MediaPlayer2AudioOutput(audio_session_t sessionId, uid_t uid, int pid,
+ const audio_attributes_t* attr, const sp<AudioSystem::AudioDeviceCallback>& deviceCallback)
+ : mCallback(NULL),
+ mCallbackCookie(NULL),
+ mCallbackData(NULL),
+ mStreamType(AUDIO_STREAM_MUSIC),
+ mLeftVolume(1.0),
+ mRightVolume(1.0),
+ mPlaybackRate(AUDIO_PLAYBACK_RATE_DEFAULT),
+ mSampleRateHz(0),
+ mMsecsPerFrame(0),
+ mFrameSize(0),
+ mSessionId(sessionId),
+ mUid(uid),
+ mPid(pid),
+ mSendLevel(0.0),
+ mAuxEffectId(0),
+ mFlags(AUDIO_OUTPUT_FLAG_NONE),
+ mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
+ mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
+ mDeviceCallbackEnabled(false),
+ mDeviceCallback(deviceCallback) {
+ ALOGV("MediaPlayer2AudioOutput(%d)", sessionId);
+ if (attr != NULL) {
+ mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
+ if (mAttributes != NULL) {
+ memcpy(mAttributes, attr, sizeof(audio_attributes_t));
+ mStreamType = audio_attributes_to_stream_type(attr);
+ }
+ } else {
+ mAttributes = NULL;
+ }
+
+ setMinBufferCount();
+}
+
+MediaPlayer2AudioOutput::~MediaPlayer2AudioOutput() {
+ close();
+ free(mAttributes);
+ delete mCallbackData;
+}
+
+//static
+void MediaPlayer2AudioOutput::setMinBufferCount() {
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("ro.kernel.qemu", value, 0)) {
+ mIsOnEmulator = true;
+ mMinBufferCount = 12; // to prevent systematic buffer underrun for emulator
+ }
+}
+
+// static
+bool MediaPlayer2AudioOutput::isOnEmulator() {
+ setMinBufferCount(); // benign race wrt other threads
+ return mIsOnEmulator;
+}
+
+// static
+int MediaPlayer2AudioOutput::getMinBufferCount() {
+ setMinBufferCount(); // benign race wrt other threads
+ return mMinBufferCount;
+}
+
+ssize_t MediaPlayer2AudioOutput::bufferSize() const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->frameCount() * mFrameSize;
+}
+
+ssize_t MediaPlayer2AudioOutput::frameCount() const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->frameCount();
+}
+
+ssize_t MediaPlayer2AudioOutput::channelCount() const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->channelCount();
+}
+
+ssize_t MediaPlayer2AudioOutput::frameSize() const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mFrameSize;
+}
+
+uint32_t MediaPlayer2AudioOutput::latency () const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return 0;
+ }
+ return mTrack->latency();
+}
+
+float MediaPlayer2AudioOutput::msecsPerFrame() const {
+ Mutex::Autolock lock(mLock);
+ return mMsecsPerFrame;
+}
+
+status_t MediaPlayer2AudioOutput::getPosition(uint32_t *position) const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->getPosition(position);
+}
+
+status_t MediaPlayer2AudioOutput::getTimestamp(AudioTimestamp &ts) const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->getTimestamp(ts);
+}
+
+// TODO: Remove unnecessary calls to getPlayedOutDurationUs()
+// as it acquires locks and may query the audio driver.
+//
+// Some calls could conceivably retrieve extrapolated data instead of
+// accessing getTimestamp() or getPosition() every time a data buffer with
+// a media time is received.
+//
+// Calculate duration of played samples if played at normal rate (i.e., 1.0).
+int64_t MediaPlayer2AudioOutput::getPlayedOutDurationUs(int64_t nowUs) const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0 || mSampleRateHz == 0) {
+ return 0;
+ }
+
+ uint32_t numFramesPlayed;
+ int64_t numFramesPlayedAtUs;
+ AudioTimestamp ts;
+
+ status_t res = mTrack->getTimestamp(ts);
+ if (res == OK) { // case 1: mixing audio tracks and offloaded tracks.
+ numFramesPlayed = ts.mPosition;
+ numFramesPlayedAtUs = ts.mTime.tv_sec * 1000000LL + ts.mTime.tv_nsec / 1000;
+ //ALOGD("getTimestamp: OK %d %lld", numFramesPlayed, (long long)numFramesPlayedAtUs);
+ } else if (res == WOULD_BLOCK) { // case 2: transitory state on start of a new track
+ numFramesPlayed = 0;
+ numFramesPlayedAtUs = nowUs;
+ //ALOGD("getTimestamp: WOULD_BLOCK %d %lld",
+ // numFramesPlayed, (long long)numFramesPlayedAtUs);
+ } else { // case 3: transitory at new track or audio fast tracks.
+ res = mTrack->getPosition(&numFramesPlayed);
+ CHECK_EQ(res, (status_t)OK);
+ numFramesPlayedAtUs = nowUs;
+ numFramesPlayedAtUs += 1000LL * mTrack->latency() / 2; /* XXX */
+ //ALOGD("getPosition: %u %lld", numFramesPlayed, (long long)numFramesPlayedAtUs);
+ }
+
+ // CHECK_EQ(numFramesPlayed & (1 << 31), 0); // can't be negative until 12.4 hrs, test
+ // TODO: remove the (int32_t) casting below as it may overflow at 12.4 hours.
+ int64_t durationUs = (int64_t)((int32_t)numFramesPlayed * 1000000LL / mSampleRateHz)
+ + nowUs - numFramesPlayedAtUs;
+ if (durationUs < 0) {
+ // Occurs when numFramesPlayed position is very small and the following:
+ // (1) In case 1, the time nowUs is computed before getTimestamp() is called and
+ // numFramesPlayedAtUs is greater than nowUs by time more than numFramesPlayed.
+ // (2) In case 3, using getPosition and adding mAudioSink->latency() to
+ // numFramesPlayedAtUs, by a time amount greater than numFramesPlayed.
+ //
+ // Both of these are transitory conditions.
+ ALOGV("getPlayedOutDurationUs: negative duration %lld set to zero", (long long)durationUs);
+ durationUs = 0;
+ }
+ ALOGV("getPlayedOutDurationUs(%lld) nowUs(%lld) frames(%u) framesAt(%lld)",
+ (long long)durationUs, (long long)nowUs,
+ numFramesPlayed, (long long)numFramesPlayedAtUs);
+ return durationUs;
+}
+
+status_t MediaPlayer2AudioOutput::getFramesWritten(uint32_t *frameswritten) const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ ExtendedTimestamp ets;
+ status_t status = mTrack->getTimestamp(&ets);
+ if (status == OK || status == WOULD_BLOCK) {
+ *frameswritten = (uint32_t)ets.mPosition[ExtendedTimestamp::LOCATION_CLIENT];
+ }
+ return status;
+}
+
+status_t MediaPlayer2AudioOutput::setParameters(const String8& keyValuePairs) {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->setParameters(keyValuePairs);
+}
+
+String8 MediaPlayer2AudioOutput::getParameters(const String8& keys) {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return String8::empty();
+ }
+ return mTrack->getParameters(keys);
+}
+
+void MediaPlayer2AudioOutput::setAudioAttributes(const audio_attributes_t * attributes) {
+ Mutex::Autolock lock(mLock);
+ if (attributes == NULL) {
+ free(mAttributes);
+ mAttributes = NULL;
+ } else {
+ if (mAttributes == NULL) {
+ mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
+ }
+ memcpy(mAttributes, attributes, sizeof(audio_attributes_t));
+ mStreamType = audio_attributes_to_stream_type(attributes);
+ }
+}
+
+void MediaPlayer2AudioOutput::setAudioStreamType(audio_stream_type_t streamType) {
+ Mutex::Autolock lock(mLock);
+ // do not allow direct stream type modification if attributes have been set
+ if (mAttributes == NULL) {
+ mStreamType = streamType;
+ }
+}
+
+void MediaPlayer2AudioOutput::close_l() {
+ mTrack.clear();
+}
+
+status_t MediaPlayer2AudioOutput::open(
+ uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
+ audio_format_t format, int bufferCount,
+ AudioCallback cb, void *cookie,
+ audio_output_flags_t flags,
+ const audio_offload_info_t *offloadInfo,
+ bool doNotReconnect,
+ uint32_t suggestedFrameCount) {
+ ALOGV("open(%u, %d, 0x%x, 0x%x, %d, %d 0x%x)", sampleRate, channelCount, channelMask,
+ format, bufferCount, mSessionId, flags);
+
+ // offloading is only supported in callback mode for now.
+ // offloadInfo must be present if offload flag is set
+ if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) &&
+ ((cb == NULL) || (offloadInfo == NULL))) {
+ return BAD_VALUE;
+ }
+
+ // compute frame count for the AudioTrack internal buffer
+ size_t frameCount;
+ if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
+ frameCount = 0; // AudioTrack will get frame count from AudioFlinger
+ } else {
+ // try to estimate the buffer processing fetch size from AudioFlinger.
+ // framesPerBuffer is approximate and generally correct, except when it's not :-).
+ uint32_t afSampleRate;
+ size_t afFrameCount;
+ if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) {
+ return NO_INIT;
+ }
+ if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) {
+ return NO_INIT;
+ }
+ const size_t framesPerBuffer =
+ (unsigned long long)sampleRate * afFrameCount / afSampleRate;
+
+ if (bufferCount == 0) {
+ // use suggestedFrameCount
+ bufferCount = (suggestedFrameCount + framesPerBuffer - 1) / framesPerBuffer;
+ }
+ // Check argument bufferCount against the mininum buffer count
+ if (bufferCount != 0 && bufferCount < mMinBufferCount) {
+ ALOGV("bufferCount (%d) increased to %d", bufferCount, mMinBufferCount);
+ bufferCount = mMinBufferCount;
+ }
+ // if frameCount is 0, then AudioTrack will get frame count from AudioFlinger
+ // which will be the minimum size permitted.
+ frameCount = bufferCount * framesPerBuffer;
+ }
+
+ if (channelMask == CHANNEL_MASK_USE_CHANNEL_ORDER) {
+ channelMask = audio_channel_out_mask_from_count(channelCount);
+ if (0 == channelMask) {
+ ALOGE("open() error, can\'t derive mask for %d audio channels", channelCount);
+ return NO_INIT;
+ }
+ }
+
+ Mutex::Autolock lock(mLock);
+ mCallback = cb;
+ mCallbackCookie = cookie;
+
+ sp<AudioTrack> t;
+ CallbackData *newcbd = NULL;
+
+ ALOGV("creating new AudioTrack");
+
+ if (mCallback != NULL) {
+ newcbd = new CallbackData(this);
+ t = new AudioTrack(
+ mStreamType,
+ sampleRate,
+ format,
+ channelMask,
+ frameCount,
+ flags,
+ CallbackWrapper,
+ newcbd,
+ 0, // notification frames
+ mSessionId,
+ AudioTrack::TRANSFER_CALLBACK,
+ offloadInfo,
+ mUid,
+ mPid,
+ mAttributes,
+ doNotReconnect,
+ 1.0f, // default value for maxRequiredSpeed
+ mSelectedDeviceId);
+ } else {
+ // TODO: Due to buffer memory concerns, we use a max target playback speed
+ // based on mPlaybackRate at the time of open (instead of kMaxRequiredSpeed),
+ // also clamping the target speed to 1.0 <= targetSpeed <= kMaxRequiredSpeed.
+ const float targetSpeed =
+ std::min(std::max(mPlaybackRate.mSpeed, 1.0f), kMaxRequiredSpeed);
+ ALOGW_IF(targetSpeed != mPlaybackRate.mSpeed,
+ "track target speed:%f clamped from playback speed:%f",
+ targetSpeed, mPlaybackRate.mSpeed);
+ t = new AudioTrack(
+ mStreamType,
+ sampleRate,
+ format,
+ channelMask,
+ frameCount,
+ flags,
+ NULL, // callback
+ NULL, // user data
+ 0, // notification frames
+ mSessionId,
+ AudioTrack::TRANSFER_DEFAULT,
+ NULL, // offload info
+ mUid,
+ mPid,
+ mAttributes,
+ doNotReconnect,
+ targetSpeed,
+ mSelectedDeviceId);
+ }
+
+ 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
+ return NO_INIT;
+ } else {
+ // successful AudioTrack initialization implies a legacy stream type was generated
+ // from the audio attributes
+ mStreamType = t->streamType();
+ }
+
+ CHECK((t != NULL) && ((mCallback == NULL) || (newcbd != NULL)));
+
+ mCallbackData = newcbd;
+ ALOGV("setVolume");
+ t->setVolume(mLeftVolume, mRightVolume);
+
+ mSampleRateHz = sampleRate;
+ mFlags = flags;
+ mMsecsPerFrame = 1E3f / (mPlaybackRate.mSpeed * sampleRate);
+ mFrameSize = t->frameSize();
+ mTrack = t;
+
+ return updateTrack_l();
+}
+
+status_t MediaPlayer2AudioOutput::updateTrack_l() {
+ if (mTrack == NULL) {
+ return NO_ERROR;
+ }
+
+ status_t res = NO_ERROR;
+ // Note some output devices may give us a direct track even though we don't specify it.
+ // Example: Line application b/17459982.
+ if ((mTrack->getFlags()
+ & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT)) == 0) {
+ res = mTrack->setPlaybackRate(mPlaybackRate);
+ if (res == NO_ERROR) {
+ mTrack->setAuxEffectSendLevel(mSendLevel);
+ res = mTrack->attachAuxEffect(mAuxEffectId);
+ }
+ }
+ mTrack->setOutputDevice(mSelectedDeviceId);
+ if (mDeviceCallbackEnabled) {
+ mTrack->addAudioDeviceCallback(mDeviceCallback.promote());
+ }
+ ALOGV("updateTrack_l() DONE status %d", res);
+ return res;
+}
+
+status_t MediaPlayer2AudioOutput::start() {
+ ALOGV("start");
+ Mutex::Autolock lock(mLock);
+ if (mCallbackData != NULL) {
+ mCallbackData->endTrackSwitch();
+ }
+ if (mTrack != 0) {
+ mTrack->setVolume(mLeftVolume, mRightVolume);
+ mTrack->setAuxEffectSendLevel(mSendLevel);
+ status_t status = mTrack->start();
+ return status;
+ }
+ return NO_INIT;
+}
+
+ssize_t MediaPlayer2AudioOutput::write(const void* buffer, size_t size, bool blocking) {
+ Mutex::Autolock lock(mLock);
+ LOG_ALWAYS_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
+
+ //ALOGV("write(%p, %u)", buffer, size);
+ if (mTrack != 0) {
+ return mTrack->write(buffer, size, blocking);
+ }
+ return NO_INIT;
+}
+
+void MediaPlayer2AudioOutput::stop() {
+ ALOGV("stop");
+ Mutex::Autolock lock(mLock);
+ if (mTrack != 0) {
+ mTrack->stop();
+ }
+}
+
+void MediaPlayer2AudioOutput::flush() {
+ ALOGV("flush");
+ Mutex::Autolock lock(mLock);
+ if (mTrack != 0) {
+ mTrack->flush();
+ }
+}
+
+void MediaPlayer2AudioOutput::pause() {
+ ALOGV("pause");
+ Mutex::Autolock lock(mLock);
+ if (mTrack != 0) {
+ mTrack->pause();
+ }
+}
+
+void MediaPlayer2AudioOutput::close() {
+ ALOGV("close");
+ sp<AudioTrack> track;
+ {
+ Mutex::Autolock lock(mLock);
+ track = mTrack;
+ close_l(); // clears mTrack
+ }
+ // destruction of the track occurs outside of mutex.
+}
+
+void MediaPlayer2AudioOutput::setVolume(float left, float right) {
+ ALOGV("setVolume(%f, %f)", left, right);
+ Mutex::Autolock lock(mLock);
+ mLeftVolume = left;
+ mRightVolume = right;
+ if (mTrack != 0) {
+ mTrack->setVolume(left, right);
+ }
+}
+
+status_t MediaPlayer2AudioOutput::setPlaybackRate(const AudioPlaybackRate &rate) {
+ ALOGV("setPlaybackRate(%f %f %d %d)",
+ rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ // remember rate so that we can set it when the track is opened
+ mPlaybackRate = rate;
+ return OK;
+ }
+ status_t res = mTrack->setPlaybackRate(rate);
+ if (res != NO_ERROR) {
+ return res;
+ }
+ // rate.mSpeed is always greater than 0 if setPlaybackRate succeeded
+ CHECK_GT(rate.mSpeed, 0.f);
+ mPlaybackRate = rate;
+ if (mSampleRateHz != 0) {
+ mMsecsPerFrame = 1E3f / (rate.mSpeed * mSampleRateHz);
+ }
+ return res;
+}
+
+status_t MediaPlayer2AudioOutput::getPlaybackRate(AudioPlaybackRate *rate) {
+ ALOGV("setPlaybackRate");
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ *rate = mTrack->getPlaybackRate();
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2AudioOutput::setAuxEffectSendLevel(float level) {
+ ALOGV("setAuxEffectSendLevel(%f)", level);
+ Mutex::Autolock lock(mLock);
+ mSendLevel = level;
+ if (mTrack != 0) {
+ return mTrack->setAuxEffectSendLevel(level);
+ }
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2AudioOutput::attachAuxEffect(int effectId) {
+ ALOGV("attachAuxEffect(%d)", effectId);
+ Mutex::Autolock lock(mLock);
+ mAuxEffectId = effectId;
+ if (mTrack != 0) {
+ return mTrack->attachAuxEffect(effectId);
+ }
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2AudioOutput::setOutputDevice(audio_port_handle_t deviceId) {
+ ALOGV("setOutputDevice(%d)", deviceId);
+ Mutex::Autolock lock(mLock);
+ mSelectedDeviceId = deviceId;
+ if (mTrack != 0) {
+ return mTrack->setOutputDevice(deviceId);
+ }
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2AudioOutput::getRoutedDeviceId(audio_port_handle_t* deviceId) {
+ ALOGV("getRoutedDeviceId");
+ Mutex::Autolock lock(mLock);
+ if (mTrack != 0) {
+ mRoutedDeviceId = mTrack->getRoutedDeviceId();
+ }
+ *deviceId = mRoutedDeviceId;
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2AudioOutput::enableAudioDeviceCallback(bool enabled) {
+ ALOGV("enableAudioDeviceCallback, %d", enabled);
+ Mutex::Autolock lock(mLock);
+ mDeviceCallbackEnabled = enabled;
+ if (mTrack != 0) {
+ status_t status;
+ if (enabled) {
+ status = mTrack->addAudioDeviceCallback(mDeviceCallback.promote());
+ } else {
+ status = mTrack->removeAudioDeviceCallback(mDeviceCallback.promote());
+ }
+ return status;
+ }
+ return NO_ERROR;
+}
+
+// static
+void MediaPlayer2AudioOutput::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();
+ MediaPlayer2AudioOutput *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;
+ }
+ return;
+ }
+
+ 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.
+ // NuPlayer2Renderer 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);
+ }
+
+ data->unlock();
+}
+
+audio_session_t MediaPlayer2AudioOutput::getSessionId() const
+{
+ Mutex::Autolock lock(mLock);
+ return mSessionId;
+}
+
+uint32_t MediaPlayer2AudioOutput::getSampleRate() const
+{
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return 0;
+ }
+ return mTrack->getSampleRate();
+}
+
+int64_t MediaPlayer2AudioOutput::getBufferDurationInUs() const
+{
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return 0;
+ }
+ int64_t duration;
+ if (mTrack->getBufferDurationInUs(&duration) != OK) {
+ return 0;
+ }
+ return duration;
+}
+
+} // namespace android
diff --git a/media/libmediaplayer2/MediaPlayer2Manager.cpp b/media/libmediaplayer2/MediaPlayer2Manager.cpp
deleted file mode 100644
index 39b102c..0000000
--- a/media/libmediaplayer2/MediaPlayer2Manager.cpp
+++ /dev/null
@@ -1,2119 +0,0 @@
-/*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-// Proxy for media player implementations
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaPlayer2Manager"
-#include <utils/Log.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <dirent.h>
-#include <unistd.h>
-
-#include <string.h>
-
-#include <cutils/atomic.h>
-#include <cutils/properties.h> // for property_get
-
-#include <utils/misc.h>
-
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/MemoryHeapBase.h>
-#include <binder/MemoryBase.h>
-#include <utils/Errors.h> // for status_t
-#include <utils/String8.h>
-#include <utils/SystemClock.h>
-#include <utils/Timers.h>
-#include <utils/Vector.h>
-
-#include <media/AudioPolicyHelper.h>
-#include <media/DataSourceDesc.h>
-#include <media/MediaHTTPService.h>
-#include <media/Metadata.h>
-#include <media/AudioTrack.h>
-#include <media/MemoryLeakTrackUtil.h>
-#include <media/NdkWrapper.h>
-
-#include <media/stagefright/InterfaceUtils.h>
-#include <media/stagefright/MediaCodecList.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/Utils.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/ALooperRoster.h>
-#include <media/stagefright/SurfaceUtils.h>
-#include <mediautils/BatteryNotifier.h>
-
-#include <mediaplayer2/MediaPlayer2EngineClient.h>
-#include <mediaplayer2/MediaPlayer2Interface.h>
-
-#include <memunreachable/memunreachable.h>
-#include <system/audio.h>
-#include <system/window.h>
-
-#include <private/android_filesystem_config.h>
-
-#include <nuplayer2/NuPlayer2Driver.h>
-#include "MediaPlayer2Manager.h"
-
-static const int kDumpLockRetries = 50;
-static const int kDumpLockSleepUs = 20000;
-
-namespace {
-using android::media::Metadata;
-using android::status_t;
-using android::OK;
-using android::BAD_VALUE;
-using android::NOT_ENOUGH_DATA;
-using android::Parcel;
-
-// Max number of entries in the filter.
-const int kMaxFilterSize = 64; // I pulled that out of thin air.
-
-const float kMaxRequiredSpeed = 8.0f; // for PCM tracks allow up to 8x speedup.
-
-// FIXME: Move all the metadata related function in the Metadata.cpp
-
-
-// Unmarshall a filter from a Parcel.
-// Filter format in a parcel:
-//
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | number of entries (n) |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | metadata type 1 |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | metadata type 2 |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// ....
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | metadata type n |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-//
-// @param p Parcel that should start with a filter.
-// @param[out] filter On exit contains the list of metadata type to be
-// filtered.
-// @param[out] status On exit contains the status code to be returned.
-// @return true if the parcel starts with a valid filter.
-bool unmarshallFilter(const Parcel& p,
- Metadata::Filter *filter,
- status_t *status)
-{
- int32_t val;
- if (p.readInt32(&val) != OK)
- {
- ALOGE("Failed to read filter's length");
- *status = NOT_ENOUGH_DATA;
- return false;
- }
-
- if( val > kMaxFilterSize || val < 0)
- {
- ALOGE("Invalid filter len %d", val);
- *status = BAD_VALUE;
- return false;
- }
-
- const size_t num = val;
-
- filter->clear();
- filter->setCapacity(num);
-
- size_t size = num * sizeof(Metadata::Type);
-
-
- if (p.dataAvail() < size)
- {
- ALOGE("Filter too short expected %zu but got %zu", size, p.dataAvail());
- *status = NOT_ENOUGH_DATA;
- return false;
- }
-
- const Metadata::Type *data =
- static_cast<const Metadata::Type*>(p.readInplace(size));
-
- if (NULL == data)
- {
- ALOGE("Filter had no data");
- *status = BAD_VALUE;
- return false;
- }
-
- // TODO: The stl impl of vector would be more efficient here
- // because it degenerates into a memcpy on pod types. Try to
- // replace later or use stl::set.
- for (size_t i = 0; i < num; ++i)
- {
- filter->add(*data);
- ++data;
- }
- *status = OK;
- return true;
-}
-
-// @param filter Of metadata type.
-// @param val To be searched.
-// @return true if a match was found.
-bool findMetadata(const Metadata::Filter& filter, const int32_t val)
-{
- // Deal with empty and ANY right away
- if (filter.isEmpty()) return false;
- if (filter[0] == Metadata::kAny) return true;
-
- return filter.indexOf(val) >= 0;
-}
-
-} // anonymous namespace
-
-
-namespace {
-using android::Parcel;
-using android::String16;
-
-// marshalling tag indicating flattened utf16 tags
-// keep in sync with frameworks/base/media/java/android/media/AudioAttributes.java
-const int32_t kAudioAttributesMarshallTagFlattenTags = 1;
-
-// Audio attributes format in a parcel:
-//
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | usage |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | content_type |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | source |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | flags |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | kAudioAttributesMarshallTagFlattenTags | // ignore tags if not found
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | flattened tags in UTF16 |
-// | ... |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-//
-// @param p Parcel that contains audio attributes.
-// @param[out] attributes On exit points to an initialized audio_attributes_t structure
-// @param[out] status On exit contains the status code to be returned.
-void unmarshallAudioAttributes(const Parcel& parcel, audio_attributes_t *attributes)
-{
- attributes->usage = (audio_usage_t) parcel.readInt32();
- attributes->content_type = (audio_content_type_t) parcel.readInt32();
- attributes->source = (audio_source_t) parcel.readInt32();
- attributes->flags = (audio_flags_mask_t) parcel.readInt32();
- const bool hasFlattenedTag = (parcel.readInt32() == kAudioAttributesMarshallTagFlattenTags);
- if (hasFlattenedTag) {
- // the tags are UTF16, convert to UTF8
- String16 tags = parcel.readString16();
- ssize_t realTagSize = utf16_to_utf8_length(tags.string(), tags.size());
- if (realTagSize <= 0) {
- strcpy(attributes->tags, "");
- } else {
- // copy the flattened string into the attributes as the destination for the conversion:
- // copying array size -1, array for tags was calloc'd, no need to NULL-terminate it
- size_t tagSize = realTagSize > AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 ?
- AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 : realTagSize;
- utf16_to_utf8(tags.string(), tagSize, attributes->tags,
- sizeof(attributes->tags) / sizeof(attributes->tags[0]));
- }
- } else {
- ALOGE("unmarshallAudioAttributes() received unflattened tags, ignoring tag values");
- strcpy(attributes->tags, "");
- }
-}
-} // anonymous namespace
-
-
-namespace android {
-
-extern ALooperRoster gLooperRoster;
-
-MediaPlayer2Manager gMediaPlayer2Manager;
-
-// TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround
-/* static */ int MediaPlayer2Manager::AudioOutput::mMinBufferCount = 4;
-/* static */ bool MediaPlayer2Manager::AudioOutput::mIsOnEmulator = false;
-
-// static
-MediaPlayer2Manager& MediaPlayer2Manager::get() {
- return gMediaPlayer2Manager;
-}
-
-MediaPlayer2Manager::MediaPlayer2Manager() {
- ALOGV("MediaPlayer2Manager created");
- // TODO: remove all unnecessary pid/uid handling.
- mPid = IPCThreadState::self()->getCallingPid();
- mUid = IPCThreadState::self()->getCallingUid();
- mNextConnId = 1;
-}
-
-MediaPlayer2Manager::~MediaPlayer2Manager() {
- ALOGV("MediaPlayer2Manager destroyed");
-}
-
-sp<MediaPlayer2Engine> MediaPlayer2Manager::create(
- const sp<MediaPlayer2EngineClient>& client,
- audio_session_t audioSessionId)
-{
- int32_t connId = android_atomic_inc(&mNextConnId);
-
- sp<Client> c = new Client(
- mPid, connId, client, audioSessionId, mUid);
-
- if (!c->init()) {
- return NULL;
- }
-
- ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, mPid, mUid);
-
- wp<Client> w = c;
- {
- Mutex::Autolock lock(mLock);
- mClients.add(w);
- }
- return c;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::dump(int fd, const Vector<String16>& args) const
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
-
- result.append(" AudioOutput\n");
- snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n",
- mStreamType, mLeftVolume, mRightVolume);
- result.append(buffer);
- snprintf(buffer, 255, " msec per frame(%f), latency (%d)\n",
- mMsecsPerFrame, (mTrack != 0) ? mTrack->latency() : -1);
- result.append(buffer);
- snprintf(buffer, 255, " aux effect id(%d), send level (%f)\n",
- mAuxEffectId, mSendLevel);
- result.append(buffer);
-
- ::write(fd, result.string(), result.size());
- if (mTrack != 0) {
- mTrack->dump(fd, args);
- }
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::dump(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- result.append(" Client\n");
- snprintf(buffer, 255, " pid(%d), connId(%d), status(%d), looping(%s)\n",
- mPid, mConnId, mStatus, mLoop?"true": "false");
- result.append(buffer);
-
- sp<MediaPlayer2Interface> p;
- sp<AudioOutput> audioOutput;
- bool locked = false;
- for (int i = 0; i < kDumpLockRetries; ++i) {
- if (mLock.tryLock() == NO_ERROR) {
- locked = true;
- break;
- }
- usleep(kDumpLockSleepUs);
- }
-
- if (locked) {
- p = mPlayer;
- audioOutput = mAudioOutput;
- mLock.unlock();
- } else {
- result.append(" lock is taken, no dump from player and audio output\n");
- }
- write(fd, result.string(), result.size());
-
- if (p != NULL) {
- p->dump(fd, args);
- }
- if (audioOutput != 0) {
- audioOutput->dump(fd, args);
- }
- write(fd, "\n", 1);
- return NO_ERROR;
-}
-
-/**
- * The only arguments this understands right now are -c, -von and -voff,
- * which are parsed by ALooperRoster::dump()
- */
-status_t MediaPlayer2Manager::dump(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- SortedVector< sp<Client> > clients; //to serialise the mutex unlock & client destruction.
-
- if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
- snprintf(buffer, SIZE, "Permission Denial: "
- "can't dump MediaPlayer2Manager from pid=%d, uid=%d\n",
- mPid, mUid);
- result.append(buffer);
- } else {
- Mutex::Autolock lock(mLock);
- for (int i = 0, n = mClients.size(); i < n; ++i) {
- sp<Client> c = mClients[i].promote();
- if (c != 0) c->dump(fd, args);
- clients.add(c);
- }
-
- result.append(" Files opened and/or mapped:\n");
- snprintf(buffer, SIZE, "/proc/%d/maps", getpid());
- FILE *f = fopen(buffer, "r");
- if (f) {
- while (!feof(f)) {
- fgets(buffer, SIZE, f);
- if (strstr(buffer, " /storage/") ||
- strstr(buffer, " /system/sounds/") ||
- strstr(buffer, " /data/") ||
- strstr(buffer, " /system/media/")) {
- result.append(" ");
- result.append(buffer);
- }
- }
- fclose(f);
- } else {
- result.append("couldn't open ");
- result.append(buffer);
- result.append("\n");
- }
-
- snprintf(buffer, SIZE, "/proc/%d/fd", getpid());
- DIR *d = opendir(buffer);
- if (d) {
- struct dirent *ent;
- while((ent = readdir(d)) != NULL) {
- if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) {
- snprintf(buffer, SIZE, "/proc/%d/fd/%s", getpid(), ent->d_name);
- struct stat s;
- if (lstat(buffer, &s) == 0) {
- if ((s.st_mode & S_IFMT) == S_IFLNK) {
- char linkto[256];
- int len = readlink(buffer, linkto, sizeof(linkto));
- if(len > 0) {
- if(len > 255) {
- linkto[252] = '.';
- linkto[253] = '.';
- linkto[254] = '.';
- linkto[255] = 0;
- } else {
- linkto[len] = 0;
- }
- if (strstr(linkto, "/storage/") == linkto ||
- strstr(linkto, "/system/sounds/") == linkto ||
- strstr(linkto, "/data/") == linkto ||
- strstr(linkto, "/system/media/") == linkto) {
- result.append(" ");
- result.append(buffer);
- result.append(" -> ");
- result.append(linkto);
- result.append("\n");
- }
- }
- } else {
- result.append(" unexpected type for ");
- result.append(buffer);
- result.append("\n");
- }
- }
- }
- }
- closedir(d);
- } else {
- result.append("couldn't open ");
- result.append(buffer);
- result.append("\n");
- }
-
- gLooperRoster.dump(fd, args);
-
- bool dumpMem = false;
- bool unreachableMemory = false;
- for (size_t i = 0; i < args.size(); i++) {
- if (args[i] == String16("-m")) {
- dumpMem = true;
- } else if (args[i] == String16("--unreachable")) {
- unreachableMemory = true;
- }
- }
- if (dumpMem) {
- result.append("\nDumping memory:\n");
- std::string s = dumpMemoryAddresses(100 /* limit */);
- result.append(s.c_str(), s.size());
- }
- if (unreachableMemory) {
- result.append("\nDumping unreachable memory:\n");
- // TODO - should limit be an argument parameter?
- // TODO: enable GetUnreachableMemoryString if it's part of stable API
- //std::string s = GetUnreachableMemoryString(true /* contents */, 10000 /* limit */);
- //result.append(s.c_str(), s.size());
- }
- }
- write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-void MediaPlayer2Manager::removeClient(const wp<Client>& client)
-{
- Mutex::Autolock lock(mLock);
- mClients.remove(client);
-}
-
-bool MediaPlayer2Manager::hasClient(wp<Client> client)
-{
- Mutex::Autolock lock(mLock);
- return mClients.indexOf(client) != NAME_NOT_FOUND;
-}
-
-MediaPlayer2Manager::Client::Client(
- pid_t pid,
- int32_t connId,
- const sp<MediaPlayer2EngineClient>& client,
- audio_session_t audioSessionId,
- uid_t uid)
-{
- ALOGV("Client(%d) constructor", connId);
- mPid = pid;
- mConnId = connId;
- mClient = client;
- mLoop = false;
- mStatus = NO_INIT;
- mAudioSessionId = audioSessionId;
- mUid = uid;
- mAudioAttributes = NULL;
-
-#if CALLBACK_ANTAGONIZER
- ALOGD("create Antagonizer");
- mAntagonizer = new Antagonizer(notify, this);
-#endif
-}
-
-bool MediaPlayer2Manager::Client::init() {
- sp<MediaPlayer2Interface> p = new NuPlayer2Driver(mPid, mUid);
- status_t init_result = p->initCheck();
- if (init_result != NO_ERROR) {
- ALOGE("Failed to create player object, initCheck failed(%d)", init_result);
- return false;
- }
-
- p->setNotifyCallback(this, notify);
- mAudioDeviceUpdatedListener = new AudioDeviceUpdatedNotifier(p);
- mAudioOutput = new AudioOutput(mAudioSessionId, mUid,
- mPid, mAudioAttributes, mAudioDeviceUpdatedListener);
- p->setAudioSink(mAudioOutput);
-
- mPlayer = p;
- return true;
-}
-
-MediaPlayer2Manager::Client::~Client()
-{
- ALOGV("Client(%d) destructor pid = %d", mConnId, mPid);
- mAudioOutput.clear();
- wp<Client> client(this);
- disconnect();
- gMediaPlayer2Manager.removeClient(client);
- if (mAudioAttributes != NULL) {
- free(mAudioAttributes);
- }
- mAudioDeviceUpdatedListener.clear();
-}
-
-void MediaPlayer2Manager::Client::disconnect()
-{
- ALOGV("disconnect(%d) from pid %d", mConnId, mPid);
- // grab local reference and clear main reference to prevent future
- // access to object
- sp<MediaPlayer2Interface> p;
- {
- Mutex::Autolock l(mLock);
- p = mPlayer;
- mClient.clear();
- mPlayer.clear();
- }
-
- // clear the notification to prevent callbacks to dead client
- // and reset the player. We assume the player will serialize
- // access to itself if necessary.
- if (p != 0) {
- p->setNotifyCallback(0, 0);
-#if CALLBACK_ANTAGONIZER
- ALOGD("kill Antagonizer");
- mAntagonizer->kill();
-#endif
- p->reset();
- }
-
- {
- Mutex::Autolock l(mLock);
- disconnectNativeWindow_l();
- }
-
- IPCThreadState::self()->flushCommands();
-}
-
-void MediaPlayer2Manager::Client::AudioDeviceUpdatedNotifier::onAudioDeviceUpdate(
- audio_io_handle_t audioIo,
- audio_port_handle_t deviceId) {
- sp<MediaPlayer2Interface> listener = mListener.promote();
- if (listener != NULL) {
- listener->sendEvent(0, MEDIA2_AUDIO_ROUTING_CHANGED, audioIo, deviceId);
- } else {
- ALOGW("listener for process %d death is gone", MEDIA2_AUDIO_ROUTING_CHANGED);
- }
-}
-
-status_t MediaPlayer2Manager::Client::setDataSource(
- const sp<DataSourceDesc> &dsd) {
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == NULL) {
- return NO_INIT;
- }
-
- if (dsd == NULL) {
- return BAD_VALUE;
- }
-
- status_t status = p->setDataSource(dsd);
- if (status != OK) {
- ALOGE("setDataSource error: %d", status);
- return status;
- }
-
- return status;
-}
-
-status_t MediaPlayer2Manager::Client::prepareNextDataSource(
- const sp<DataSourceDesc> &dsd) {
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == NULL) {
- return NO_INIT;
- }
-
- if (dsd == NULL) {
- return BAD_VALUE;
- }
-
- status_t status = p->prepareNextDataSource(dsd);
- if (status != OK) {
- ALOGE("prepareNextDataSource error: %d", status);
- }
-
- return status;
-}
-
-status_t MediaPlayer2Manager::Client::playNextDataSource(int64_t srcId) {
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == NULL) {
- return NO_INIT;
- }
-
- status_t status = p->playNextDataSource(srcId);
- if (status != OK) {
- ALOGE("playNextDataSource error: %d", status);
- }
-
- return status;
-}
-
-void MediaPlayer2Manager::Client::disconnectNativeWindow_l() {
- if (mConnectedWindow != NULL && mConnectedWindow->getANativeWindow() != NULL) {
- status_t err = native_window_api_disconnect(
- mConnectedWindow->getANativeWindow(), NATIVE_WINDOW_API_MEDIA);
-
- if (err != OK) {
- ALOGW("nativeWindowDisconnect returned an error: %s (%d)",
- strerror(-err), err);
- }
- }
- mConnectedWindow.clear();
-}
-
-status_t MediaPlayer2Manager::Client::setVideoSurfaceTexture(
- const sp<ANativeWindowWrapper>& nww)
-{
- ALOGV("[%d] setVideoSurfaceTexture(%p)",
- mConnId,
- (nww == NULL ? NULL : nww->getANativeWindow()));
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
-
- if (nww != NULL && nww->getANativeWindow() != NULL) {
- if (mConnectedWindow != NULL
- && mConnectedWindow->getANativeWindow() == nww->getANativeWindow()) {
- return OK;
- }
- status_t err = native_window_api_connect(
- nww->getANativeWindow(), NATIVE_WINDOW_API_MEDIA);
-
- if (err != OK) {
- ALOGE("setVideoSurfaceTexture failed: %d", err);
- // Note that we must do the reset before disconnecting from the ANW.
- // Otherwise queue/dequeue calls could be made on the disconnected
- // ANW, which may result in errors.
- reset();
-
- Mutex::Autolock lock(mLock);
- disconnectNativeWindow_l();
-
- return err;
- }
- }
-
- // Note that we must set the player's new GraphicBufferProducer before
- // disconnecting the old one. Otherwise queue/dequeue calls could be made
- // on the disconnected ANW, which may result in errors.
- status_t err = p->setVideoSurfaceTexture(nww);
-
- mLock.lock();
- disconnectNativeWindow_l();
-
- if (err == OK) {
- mConnectedWindow = nww;
- mLock.unlock();
- } else if (nww != NULL) {
- mLock.unlock();
- status_t err = native_window_api_disconnect(
- nww->getANativeWindow(), NATIVE_WINDOW_API_MEDIA);
-
- if (err != OK) {
- ALOGW("nativeWindowDisconnect returned an error: %s (%d)",
- strerror(-err), err);
- }
- }
-
- return err;
-}
-
-status_t MediaPlayer2Manager::Client::invoke(const Parcel& request,
- Parcel *reply)
-{
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == NULL) return UNKNOWN_ERROR;
- return p->invoke(request, reply);
-}
-
-// This call doesn't need to access the native player.
-status_t MediaPlayer2Manager::Client::setMetadataFilter(const Parcel& filter)
-{
- status_t status;
- media::Metadata::Filter allow, drop;
-
- if (unmarshallFilter(filter, &allow, &status) &&
- unmarshallFilter(filter, &drop, &status)) {
- Mutex::Autolock lock(mLock);
-
- mMetadataAllow = allow;
- mMetadataDrop = drop;
- }
- return status;
-}
-
-status_t MediaPlayer2Manager::Client::getMetadata(
- bool update_only, bool /*apply_filter*/, Parcel *reply)
-{
- sp<MediaPlayer2Interface> player = getPlayer();
- if (player == 0) return UNKNOWN_ERROR;
-
- status_t status;
- // Placeholder for the return code, updated by the caller.
- reply->writeInt32(-1);
-
- media::Metadata::Filter ids;
-
- // We don't block notifications while we fetch the data. We clear
- // mMetadataUpdated first so we don't lose notifications happening
- // during the rest of this call.
- {
- Mutex::Autolock lock(mLock);
- if (update_only) {
- ids = mMetadataUpdated;
- }
- mMetadataUpdated.clear();
- }
-
- media::Metadata metadata(reply);
-
- metadata.appendHeader();
- status = player->getMetadata(ids, reply);
-
- if (status != OK) {
- metadata.resetParcel();
- ALOGE("getMetadata failed %d", status);
- return status;
- }
-
- // FIXME: ement filtering on the result. Not critical since
- // filtering takes place on the update notifications already. This
- // would be when all the metadata are fetch and a filter is set.
-
- // Everything is fine, update the metadata length.
- metadata.updateLength();
- return OK;
-}
-
-status_t MediaPlayer2Manager::Client::setBufferingSettings(
- const BufferingSettings& buffering)
-{
- ALOGV("[%d] setBufferingSettings{%s}",
- mConnId, buffering.toString().string());
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->setBufferingSettings(buffering);
-}
-
-status_t MediaPlayer2Manager::Client::getBufferingSettings(
- BufferingSettings* buffering /* nonnull */)
-{
- sp<MediaPlayer2Interface> p = getPlayer();
- // TODO: create mPlayer on demand.
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->getBufferingSettings(buffering);
- if (ret == NO_ERROR) {
- ALOGV("[%d] getBufferingSettings{%s}",
- mConnId, buffering->toString().string());
- } else {
- ALOGE("[%d] getBufferingSettings returned %d", mConnId, ret);
- }
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::prepareAsync()
-{
- ALOGV("[%d] prepareAsync", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->prepareAsync();
-#if CALLBACK_ANTAGONIZER
- ALOGD("start Antagonizer");
- if (ret == NO_ERROR) mAntagonizer->start();
-#endif
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::start()
-{
- ALOGV("[%d] start", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- p->setLooping(mLoop);
- return p->start();
-}
-
-status_t MediaPlayer2Manager::Client::stop()
-{
- ALOGV("[%d] stop", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->stop();
-}
-
-status_t MediaPlayer2Manager::Client::pause()
-{
- ALOGV("[%d] pause", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->pause();
-}
-
-status_t MediaPlayer2Manager::Client::isPlaying(bool* state)
-{
- *state = false;
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- *state = p->isPlaying();
- ALOGV("[%d] isPlaying: %d", mConnId, *state);
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setPlaybackSettings(const AudioPlaybackRate& rate)
-{
- ALOGV("[%d] setPlaybackSettings(%f, %f, %d, %d)",
- mConnId, rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->setPlaybackSettings(rate);
-}
-
-status_t MediaPlayer2Manager::Client::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */)
-{
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->getPlaybackSettings(rate);
- if (ret == NO_ERROR) {
- ALOGV("[%d] getPlaybackSettings(%f, %f, %d, %d)",
- mConnId, rate->mSpeed, rate->mPitch, rate->mFallbackMode, rate->mStretchMode);
- } else {
- ALOGV("[%d] getPlaybackSettings returned %d", mConnId, ret);
- }
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::setSyncSettings(
- const AVSyncSettings& sync, float videoFpsHint)
-{
- ALOGV("[%d] setSyncSettings(%u, %u, %f, %f)",
- mConnId, sync.mSource, sync.mAudioAdjustMode, sync.mTolerance, videoFpsHint);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->setSyncSettings(sync, videoFpsHint);
-}
-
-status_t MediaPlayer2Manager::Client::getSyncSettings(
- AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */)
-{
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->getSyncSettings(sync, videoFps);
- if (ret == NO_ERROR) {
- ALOGV("[%d] getSyncSettings(%u, %u, %f, %f)",
- mConnId, sync->mSource, sync->mAudioAdjustMode, sync->mTolerance, *videoFps);
- } else {
- ALOGV("[%d] getSyncSettings returned %d", mConnId, ret);
- }
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::getCurrentPosition(int *msec)
-{
- ALOGV("getCurrentPosition");
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->getCurrentPosition(msec);
- if (ret == NO_ERROR) {
- ALOGV("[%d] getCurrentPosition = %d", mConnId, *msec);
- } else {
- ALOGE("getCurrentPosition returned %d", ret);
- }
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::getDuration(int *msec)
-{
- ALOGV("getDuration");
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->getDuration(msec);
- if (ret == NO_ERROR) {
- ALOGV("[%d] getDuration = %d", mConnId, *msec);
- } else {
- ALOGE("getDuration returned %d", ret);
- }
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::setNextPlayer(const sp<MediaPlayer2Engine>& player) {
- ALOGV("setNextPlayer");
- Mutex::Autolock l(mLock);
- sp<Client> c = static_cast<Client*>(player.get());
- if (c != NULL && !gMediaPlayer2Manager.hasClient(c)) {
- return BAD_VALUE;
- }
-
- mNextClient = c;
-
- if (c != NULL) {
- if (mAudioOutput != NULL) {
- mAudioOutput->setNextOutput(c->mAudioOutput);
- } else {
- ALOGE("no current audio output");
- }
-
- if ((mPlayer != NULL) && (mNextClient->getPlayer() != NULL)) {
- mPlayer->setNextPlayer(mNextClient->getPlayer());
- }
- }
-
- return OK;
-}
-
-status_t MediaPlayer2Manager::Client::seekTo(int msec, MediaPlayer2SeekMode mode)
-{
- ALOGV("[%d] seekTo(%d, %d)", mConnId, msec, mode);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->seekTo(msec, mode);
-}
-
-status_t MediaPlayer2Manager::Client::reset()
-{
- ALOGV("[%d] reset", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->reset();
-}
-
-status_t MediaPlayer2Manager::Client::notifyAt(int64_t mediaTimeUs)
-{
- ALOGV("[%d] notifyAt(%lld)", mConnId, (long long)mediaTimeUs);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->notifyAt(mediaTimeUs);
-}
-
-status_t MediaPlayer2Manager::Client::setAudioStreamType(audio_stream_type_t type)
-{
- ALOGV("[%d] setAudioStreamType(%d)", mConnId, type);
- // TODO: for hardware output, call player instead
- Mutex::Autolock l(mLock);
- if (mAudioOutput != 0) mAudioOutput->setAudioStreamType(type);
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setAudioAttributes_l(const Parcel &parcel)
-{
- if (mAudioAttributes != NULL) { free(mAudioAttributes); }
- mAudioAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
- if (mAudioAttributes == NULL) {
- return NO_MEMORY;
- }
- unmarshallAudioAttributes(parcel, mAudioAttributes);
-
- ALOGV("setAudioAttributes_l() usage=%d content=%d flags=0x%x tags=%s",
- mAudioAttributes->usage, mAudioAttributes->content_type, mAudioAttributes->flags,
- mAudioAttributes->tags);
-
- if (mAudioOutput != 0) {
- mAudioOutput->setAudioAttributes(mAudioAttributes);
- }
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setLooping(int loop)
-{
- ALOGV("[%d] setLooping(%d)", mConnId, loop);
- mLoop = loop;
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p != 0) return p->setLooping(loop);
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setVolume(float leftVolume, float rightVolume)
-{
- ALOGV("[%d] setVolume(%f, %f)", mConnId, leftVolume, rightVolume);
-
- // for hardware output, call player instead
- sp<MediaPlayer2Interface> p = getPlayer();
- {
- Mutex::Autolock l(mLock);
- if (mAudioOutput != 0) mAudioOutput->setVolume(leftVolume, rightVolume);
- }
-
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setAuxEffectSendLevel(float level)
-{
- ALOGV("[%d] setAuxEffectSendLevel(%f)", mConnId, level);
- Mutex::Autolock l(mLock);
- if (mAudioOutput != 0) return mAudioOutput->setAuxEffectSendLevel(level);
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::attachAuxEffect(int effectId)
-{
- ALOGV("[%d] attachAuxEffect(%d)", mConnId, effectId);
- Mutex::Autolock l(mLock);
- if (mAudioOutput != 0) return mAudioOutput->attachAuxEffect(effectId);
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setParameter(int key, const Parcel &request) {
- ALOGV("[%d] setParameter(%d)", mConnId, key);
- switch (key) {
- case MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES:
- {
- Mutex::Autolock l(mLock);
- return setAudioAttributes_l(request);
- }
- default:
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) { return UNKNOWN_ERROR; }
- return p->setParameter(key, request);
- }
-}
-
-status_t MediaPlayer2Manager::Client::getParameter(int key, Parcel *reply) {
- ALOGV("[%d] getParameter(%d)", mConnId, key);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->getParameter(key, reply);
-}
-
-void MediaPlayer2Manager::Client::notify(
- const wp<MediaPlayer2Engine> &listener, int64_t srcId,
- int msg, int ext1, int ext2, const Parcel *obj)
-{
- sp<MediaPlayer2Engine> spListener = listener.promote();
- if (spListener == NULL) {
- return;
- }
- Client* client = static_cast<Client*>(spListener.get());
-
- sp<MediaPlayer2EngineClient> c;
- sp<Client> nextClient;
- status_t errStartNext = NO_ERROR;
- {
- Mutex::Autolock l(client->mLock);
- c = client->mClient;
- if (msg == MEDIA2_PLAYBACK_COMPLETE && client->mNextClient != NULL) {
- nextClient = client->mNextClient;
-
- if (client->mAudioOutput != NULL)
- client->mAudioOutput->switchToNextOutput();
-
- errStartNext = nextClient->start();
- }
- }
-
- if (nextClient != NULL) {
- sp<MediaPlayer2EngineClient> nc;
- {
- Mutex::Autolock l(nextClient->mLock);
- nc = nextClient->mClient;
- }
- if (nc != NULL) {
- if (errStartNext == NO_ERROR) {
- nc->notify(srcId, MEDIA2_INFO, MEDIA2_INFO_STARTED_AS_NEXT, 0, obj);
- } else {
- nc->notify(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN , 0, obj);
- ALOGE("gapless:start playback for next track failed, err(%d)", errStartNext);
- }
- }
- }
-
- if (MEDIA2_INFO == msg &&
- MEDIA2_INFO_METADATA_UPDATE == ext1) {
- const media::Metadata::Type metadata_type = ext2;
-
- if(client->shouldDropMetadata(metadata_type)) {
- return;
- }
-
- // Update the list of metadata that have changed. getMetadata
- // also access mMetadataUpdated and clears it.
- client->addNewMetadataUpdate(metadata_type);
- }
-
- if (c != NULL) {
- ALOGV("[%d] notify (%p, %lld, %d, %d, %d)", client->mConnId, spListener.get(),
- (long long)srcId, msg, ext1, ext2);
- c->notify(srcId, msg, ext1, ext2, obj);
- }
-}
-
-
-bool MediaPlayer2Manager::Client::shouldDropMetadata(media::Metadata::Type code) const
-{
- Mutex::Autolock lock(mLock);
-
- if (findMetadata(mMetadataDrop, code)) {
- return true;
- }
-
- if (mMetadataAllow.isEmpty() || findMetadata(mMetadataAllow, code)) {
- return false;
- } else {
- return true;
- }
-}
-
-
-void MediaPlayer2Manager::Client::addNewMetadataUpdate(media::Metadata::Type metadata_type) {
- Mutex::Autolock lock(mLock);
- if (mMetadataUpdated.indexOf(metadata_type) < 0) {
- mMetadataUpdated.add(metadata_type);
- }
-}
-
-// Modular DRM
-status_t MediaPlayer2Manager::Client::prepareDrm(const uint8_t uuid[16],
- const Vector<uint8_t>& drmSessionId)
-{
- ALOGV("[%d] prepareDrm", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
-
- status_t ret = p->prepareDrm(uuid, drmSessionId);
- ALOGV("prepareDrm ret: %d", ret);
-
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::releaseDrm()
-{
- ALOGV("[%d] releaseDrm", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
-
- status_t ret = p->releaseDrm();
- ALOGV("releaseDrm ret: %d", ret);
-
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::setOutputDevice(audio_port_handle_t deviceId)
-{
- ALOGV("[%d] setOutputDevice", mConnId);
- {
- Mutex::Autolock l(mLock);
- if (mAudioOutput.get() != nullptr) {
- return mAudioOutput->setOutputDevice(deviceId);
- }
- }
- return NO_INIT;
-}
-
-status_t MediaPlayer2Manager::Client::getRoutedDeviceId(audio_port_handle_t* deviceId)
-{
- ALOGV("[%d] getRoutedDeviceId", mConnId);
- {
- Mutex::Autolock l(mLock);
- if (mAudioOutput.get() != nullptr) {
- return mAudioOutput->getRoutedDeviceId(deviceId);
- }
- }
- return NO_INIT;
-}
-
-status_t MediaPlayer2Manager::Client::enableAudioDeviceCallback(bool enabled)
-{
- ALOGV("[%d] enableAudioDeviceCallback, %d", mConnId, enabled);
- {
- Mutex::Autolock l(mLock);
- if (mAudioOutput.get() != nullptr) {
- return mAudioOutput->enableAudioDeviceCallback(enabled);
- }
- }
- return NO_INIT;
-}
-
-#if CALLBACK_ANTAGONIZER
-const int Antagonizer::interval = 10000; // 10 msecs
-
-Antagonizer::Antagonizer(
- MediaPlayer2Manager::NotifyCallback cb,
- const wp<MediaPlayer2Engine> &client) :
- mExit(false), mActive(false), mClient(client), mCb(cb)
-{
- createThread(callbackThread, this);
-}
-
-void Antagonizer::kill()
-{
- Mutex::Autolock _l(mLock);
- mActive = false;
- mExit = true;
- mCondition.wait(mLock);
-}
-
-int Antagonizer::callbackThread(void* user)
-{
- ALOGD("Antagonizer started");
- Antagonizer* p = reinterpret_cast<Antagonizer*>(user);
- while (!p->mExit) {
- if (p->mActive) {
- ALOGV("send event");
- p->mCb(p->mClient, 0, 0, 0);
- }
- usleep(interval);
- }
- Mutex::Autolock _l(p->mLock);
- p->mCondition.signal();
- ALOGD("Antagonizer stopped");
- return 0;
-}
-#endif
-
-#undef LOG_TAG
-#define LOG_TAG "AudioSink"
-MediaPlayer2Manager::AudioOutput::AudioOutput(audio_session_t sessionId, uid_t uid, int pid,
- const audio_attributes_t* attr, const sp<AudioSystem::AudioDeviceCallback>& deviceCallback)
- : mCallback(NULL),
- mCallbackCookie(NULL),
- mCallbackData(NULL),
- mStreamType(AUDIO_STREAM_MUSIC),
- mLeftVolume(1.0),
- mRightVolume(1.0),
- mPlaybackRate(AUDIO_PLAYBACK_RATE_DEFAULT),
- mSampleRateHz(0),
- mMsecsPerFrame(0),
- mFrameSize(0),
- mSessionId(sessionId),
- mUid(uid),
- mPid(pid),
- mSendLevel(0.0),
- mAuxEffectId(0),
- mFlags(AUDIO_OUTPUT_FLAG_NONE),
- mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
- mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
- mDeviceCallbackEnabled(false),
- mDeviceCallback(deviceCallback)
-{
- ALOGV("AudioOutput(%d)", sessionId);
- if (attr != NULL) {
- mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
- if (mAttributes != NULL) {
- memcpy(mAttributes, attr, sizeof(audio_attributes_t));
- mStreamType = audio_attributes_to_stream_type(attr);
- }
- } else {
- mAttributes = NULL;
- }
-
- setMinBufferCount();
-}
-
-MediaPlayer2Manager::AudioOutput::~AudioOutput()
-{
- close();
- free(mAttributes);
- delete mCallbackData;
-}
-
-//static
-void MediaPlayer2Manager::AudioOutput::setMinBufferCount()
-{
- char value[PROPERTY_VALUE_MAX];
- if (property_get("ro.kernel.qemu", value, 0)) {
- mIsOnEmulator = true;
- mMinBufferCount = 12; // to prevent systematic buffer underrun for emulator
- }
-}
-
-// static
-bool MediaPlayer2Manager::AudioOutput::isOnEmulator()
-{
- setMinBufferCount(); // benign race wrt other threads
- return mIsOnEmulator;
-}
-
-// static
-int MediaPlayer2Manager::AudioOutput::getMinBufferCount()
-{
- setMinBufferCount(); // benign race wrt other threads
- return mMinBufferCount;
-}
-
-ssize_t MediaPlayer2Manager::AudioOutput::bufferSize() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->frameCount() * mFrameSize;
-}
-
-ssize_t MediaPlayer2Manager::AudioOutput::frameCount() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->frameCount();
-}
-
-ssize_t MediaPlayer2Manager::AudioOutput::channelCount() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->channelCount();
-}
-
-ssize_t MediaPlayer2Manager::AudioOutput::frameSize() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mFrameSize;
-}
-
-uint32_t MediaPlayer2Manager::AudioOutput::latency () const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return 0;
- return mTrack->latency();
-}
-
-float MediaPlayer2Manager::AudioOutput::msecsPerFrame() const
-{
- Mutex::Autolock lock(mLock);
- return mMsecsPerFrame;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::getPosition(uint32_t *position) const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->getPosition(position);
-}
-
-status_t MediaPlayer2Manager::AudioOutput::getTimestamp(AudioTimestamp &ts) const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->getTimestamp(ts);
-}
-
-// TODO: Remove unnecessary calls to getPlayedOutDurationUs()
-// as it acquires locks and may query the audio driver.
-//
-// Some calls could conceivably retrieve extrapolated data instead of
-// accessing getTimestamp() or getPosition() every time a data buffer with
-// a media time is received.
-//
-// Calculate duration of played samples if played at normal rate (i.e., 1.0).
-int64_t MediaPlayer2Manager::AudioOutput::getPlayedOutDurationUs(int64_t nowUs) const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0 || mSampleRateHz == 0) {
- return 0;
- }
-
- uint32_t numFramesPlayed;
- int64_t numFramesPlayedAtUs;
- AudioTimestamp ts;
-
- status_t res = mTrack->getTimestamp(ts);
- if (res == OK) { // case 1: mixing audio tracks and offloaded tracks.
- numFramesPlayed = ts.mPosition;
- numFramesPlayedAtUs = ts.mTime.tv_sec * 1000000LL + ts.mTime.tv_nsec / 1000;
- //ALOGD("getTimestamp: OK %d %lld", numFramesPlayed, (long long)numFramesPlayedAtUs);
- } else if (res == WOULD_BLOCK) { // case 2: transitory state on start of a new track
- numFramesPlayed = 0;
- numFramesPlayedAtUs = nowUs;
- //ALOGD("getTimestamp: WOULD_BLOCK %d %lld",
- // numFramesPlayed, (long long)numFramesPlayedAtUs);
- } else { // case 3: transitory at new track or audio fast tracks.
- res = mTrack->getPosition(&numFramesPlayed);
- CHECK_EQ(res, (status_t)OK);
- numFramesPlayedAtUs = nowUs;
- numFramesPlayedAtUs += 1000LL * mTrack->latency() / 2; /* XXX */
- //ALOGD("getPosition: %u %lld", numFramesPlayed, (long long)numFramesPlayedAtUs);
- }
-
- // CHECK_EQ(numFramesPlayed & (1 << 31), 0); // can't be negative until 12.4 hrs, test
- // TODO: remove the (int32_t) casting below as it may overflow at 12.4 hours.
- int64_t durationUs = (int64_t)((int32_t)numFramesPlayed * 1000000LL / mSampleRateHz)
- + nowUs - numFramesPlayedAtUs;
- if (durationUs < 0) {
- // Occurs when numFramesPlayed position is very small and the following:
- // (1) In case 1, the time nowUs is computed before getTimestamp() is called and
- // numFramesPlayedAtUs is greater than nowUs by time more than numFramesPlayed.
- // (2) In case 3, using getPosition and adding mAudioSink->latency() to
- // numFramesPlayedAtUs, by a time amount greater than numFramesPlayed.
- //
- // Both of these are transitory conditions.
- ALOGV("getPlayedOutDurationUs: negative duration %lld set to zero", (long long)durationUs);
- durationUs = 0;
- }
- ALOGV("getPlayedOutDurationUs(%lld) nowUs(%lld) frames(%u) framesAt(%lld)",
- (long long)durationUs, (long long)nowUs,
- numFramesPlayed, (long long)numFramesPlayedAtUs);
- return durationUs;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::getFramesWritten(uint32_t *frameswritten) const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- ExtendedTimestamp ets;
- status_t status = mTrack->getTimestamp(&ets);
- if (status == OK || status == WOULD_BLOCK) {
- *frameswritten = (uint32_t)ets.mPosition[ExtendedTimestamp::LOCATION_CLIENT];
- }
- return status;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::setParameters(const String8& keyValuePairs)
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->setParameters(keyValuePairs);
-}
-
-String8 MediaPlayer2Manager::AudioOutput::getParameters(const String8& keys)
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return String8::empty();
- return mTrack->getParameters(keys);
-}
-
-void MediaPlayer2Manager::AudioOutput::setAudioAttributes(const audio_attributes_t * attributes) {
- Mutex::Autolock lock(mLock);
- if (attributes == NULL) {
- free(mAttributes);
- mAttributes = NULL;
- } else {
- if (mAttributes == NULL) {
- mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
- }
- memcpy(mAttributes, attributes, sizeof(audio_attributes_t));
- mStreamType = audio_attributes_to_stream_type(attributes);
- }
-}
-
-void MediaPlayer2Manager::AudioOutput::setAudioStreamType(audio_stream_type_t streamType)
-{
- Mutex::Autolock lock(mLock);
- // do not allow direct stream type modification if attributes have been set
- if (mAttributes == NULL) {
- mStreamType = streamType;
- }
-}
-
-void MediaPlayer2Manager::AudioOutput::deleteRecycledTrack_l()
-{
- ALOGV("deleteRecycledTrack_l");
- if (mRecycledTrack != 0) {
-
- if (mCallbackData != NULL) {
- mCallbackData->setOutput(NULL);
- mCallbackData->endTrackSwitch();
- }
-
- if ((mRecycledTrack->getFlags() & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) {
- int32_t msec = 0;
- if (!mRecycledTrack->stopped()) { // check if active
- (void)mRecycledTrack->pendingDuration(&msec);
- }
- mRecycledTrack->stop(); // ensure full data drain
- ALOGD("deleting recycled track, waiting for data drain (%d msec)", msec);
- if (msec > 0) {
- static const int32_t WAIT_LIMIT_MS = 3000;
- if (msec > WAIT_LIMIT_MS) {
- msec = WAIT_LIMIT_MS;
- }
- usleep(msec * 1000LL);
- }
- }
- // An offloaded track isn't flushed because the STREAM_END is reported
- // slightly prematurely to allow time for the gapless track switch
- // but this means that if we decide not to recycle the track there
- // could be a small amount of residual data still playing. We leave
- // AudioFlinger to drain the track.
-
- mRecycledTrack.clear();
- close_l();
- delete mCallbackData;
- mCallbackData = NULL;
- }
-}
-
-void MediaPlayer2Manager::AudioOutput::close_l()
-{
- mTrack.clear();
-}
-
-status_t MediaPlayer2Manager::AudioOutput::open(
- uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
- audio_format_t format, int bufferCount,
- AudioCallback cb, void *cookie,
- audio_output_flags_t flags,
- const audio_offload_info_t *offloadInfo,
- bool doNotReconnect,
- uint32_t suggestedFrameCount)
-{
- ALOGV("open(%u, %d, 0x%x, 0x%x, %d, %d 0x%x)", sampleRate, channelCount, channelMask,
- format, bufferCount, mSessionId, flags);
-
- // offloading is only supported in callback mode for now.
- // offloadInfo must be present if offload flag is set
- if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) &&
- ((cb == NULL) || (offloadInfo == NULL))) {
- return BAD_VALUE;
- }
-
- // compute frame count for the AudioTrack internal buffer
- size_t frameCount;
- if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
- frameCount = 0; // AudioTrack will get frame count from AudioFlinger
- } else {
- // try to estimate the buffer processing fetch size from AudioFlinger.
- // framesPerBuffer is approximate and generally correct, except when it's not :-).
- uint32_t afSampleRate;
- size_t afFrameCount;
- if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) {
- return NO_INIT;
- }
- if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) {
- return NO_INIT;
- }
- const size_t framesPerBuffer =
- (unsigned long long)sampleRate * afFrameCount / afSampleRate;
-
- if (bufferCount == 0) {
- // use suggestedFrameCount
- bufferCount = (suggestedFrameCount + framesPerBuffer - 1) / framesPerBuffer;
- }
- // Check argument bufferCount against the mininum buffer count
- if (bufferCount != 0 && bufferCount < mMinBufferCount) {
- ALOGV("bufferCount (%d) increased to %d", bufferCount, mMinBufferCount);
- bufferCount = mMinBufferCount;
- }
- // if frameCount is 0, then AudioTrack will get frame count from AudioFlinger
- // which will be the minimum size permitted.
- frameCount = bufferCount * framesPerBuffer;
- }
-
- if (channelMask == CHANNEL_MASK_USE_CHANNEL_ORDER) {
- channelMask = audio_channel_out_mask_from_count(channelCount);
- if (0 == channelMask) {
- ALOGE("open() error, can\'t derive mask for %d audio channels", channelCount);
- return NO_INIT;
- }
- }
-
- Mutex::Autolock lock(mLock);
- mCallback = cb;
- mCallbackCookie = cookie;
-
- // Check whether we can recycle the track
- bool reuse = false;
- bool bothOffloaded = false;
-
- if (mRecycledTrack != 0) {
- // check whether we are switching between two offloaded tracks
- bothOffloaded = (flags & mRecycledTrack->getFlags()
- & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0;
-
- // check if the existing track can be reused as-is, or if a new track needs to be created.
- 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() != (uint32_t)channelCount) ) {
- ALOGV("samplerate, channelcount differ: %u/%u Hz, %u/%d ch",
- mRecycledTrack->getSampleRate(), sampleRate,
- mRecycledTrack->channelCount(), channelCount);
- reuse = false;
- } else if (flags != mFlags) {
- ALOGV("output flags differ %08x/%08x", flags, mFlags);
- reuse = false;
- } else if (mRecycledTrack->format() != format) {
- reuse = false;
- }
- } else {
- ALOGV("no track available to recycle");
- }
-
- ALOGV_IF(bothOffloaded, "both tracks offloaded");
-
- // If we can't recycle and both tracks are offloaded
- // we must close the previous output before opening a new one
- if (bothOffloaded && !reuse) {
- ALOGV("both offloaded and not recycling");
- deleteRecycledTrack_l();
- }
-
- sp<AudioTrack> t;
- CallbackData *newcbd = NULL;
-
- // 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
- // are switching where one is offloaded and one isn't then we create
- // the new track in advance so that we can read additional stream info
-
- if (!(reuse && bothOffloaded)) {
- ALOGV("creating new AudioTrack");
-
- if (mCallback != NULL) {
- newcbd = new CallbackData(this);
- t = new AudioTrack(
- mStreamType,
- sampleRate,
- format,
- channelMask,
- frameCount,
- flags,
- CallbackWrapper,
- newcbd,
- 0, // notification frames
- mSessionId,
- AudioTrack::TRANSFER_CALLBACK,
- offloadInfo,
- mUid,
- mPid,
- mAttributes,
- doNotReconnect,
- 1.0f, // default value for maxRequiredSpeed
- mSelectedDeviceId);
- } else {
- // TODO: Due to buffer memory concerns, we use a max target playback speed
- // based on mPlaybackRate at the time of open (instead of kMaxRequiredSpeed),
- // also clamping the target speed to 1.0 <= targetSpeed <= kMaxRequiredSpeed.
- const float targetSpeed =
- std::min(std::max(mPlaybackRate.mSpeed, 1.0f), kMaxRequiredSpeed);
- ALOGW_IF(targetSpeed != mPlaybackRate.mSpeed,
- "track target speed:%f clamped from playback speed:%f",
- targetSpeed, mPlaybackRate.mSpeed);
- t = new AudioTrack(
- mStreamType,
- sampleRate,
- format,
- channelMask,
- frameCount,
- flags,
- NULL, // callback
- NULL, // user data
- 0, // notification frames
- mSessionId,
- AudioTrack::TRANSFER_DEFAULT,
- NULL, // offload info
- mUid,
- mPid,
- mAttributes,
- doNotReconnect,
- targetSpeed,
- mSelectedDeviceId);
- }
-
- 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
- return NO_INIT;
- } else {
- // successful AudioTrack initialization implies a legacy stream type was generated
- // from the audio attributes
- mStreamType = t->streamType();
- }
- }
-
- if (reuse) {
- CHECK(mRecycledTrack != NULL);
-
- if (!bothOffloaded) {
- if (mRecycledTrack->frameCount() != t->frameCount()) {
- ALOGV("framecount differs: %zu/%zu frames",
- mRecycledTrack->frameCount(), t->frameCount());
- reuse = false;
- }
- }
-
- if (reuse) {
- ALOGV("chaining to next output and recycling track");
- close_l();
- mTrack = mRecycledTrack;
- mRecycledTrack.clear();
- if (mCallbackData != NULL) {
- mCallbackData->setOutput(this);
- }
- delete newcbd;
- return updateTrack();
- }
- }
-
- // we're not going to reuse the track, unblock and flush it
- // this was done earlier if both tracks are offloaded
- if (!bothOffloaded) {
- deleteRecycledTrack_l();
- }
-
- CHECK((t != NULL) && ((mCallback == NULL) || (newcbd != NULL)));
-
- mCallbackData = newcbd;
- ALOGV("setVolume");
- t->setVolume(mLeftVolume, mRightVolume);
-
- mSampleRateHz = sampleRate;
- mFlags = flags;
- mMsecsPerFrame = 1E3f / (mPlaybackRate.mSpeed * sampleRate);
- mFrameSize = t->frameSize();
- mTrack = t;
-
- return updateTrack();
-}
-
-status_t MediaPlayer2Manager::AudioOutput::updateTrack() {
- if (mTrack == NULL) {
- return NO_ERROR;
- }
-
- status_t res = NO_ERROR;
- // Note some output devices may give us a direct track even though we don't specify it.
- // Example: Line application b/17459982.
- if ((mTrack->getFlags()
- & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT)) == 0) {
- res = mTrack->setPlaybackRate(mPlaybackRate);
- if (res == NO_ERROR) {
- mTrack->setAuxEffectSendLevel(mSendLevel);
- res = mTrack->attachAuxEffect(mAuxEffectId);
- }
- }
- mTrack->setOutputDevice(mSelectedDeviceId);
- if (mDeviceCallbackEnabled) {
- mTrack->addAudioDeviceCallback(mDeviceCallback.promote());
- }
- ALOGV("updateTrack() DONE status %d", res);
- return res;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::start()
-{
- ALOGV("start");
- Mutex::Autolock lock(mLock);
- if (mCallbackData != NULL) {
- mCallbackData->endTrackSwitch();
- }
- if (mTrack != 0) {
- mTrack->setVolume(mLeftVolume, mRightVolume);
- mTrack->setAuxEffectSendLevel(mSendLevel);
- status_t status = mTrack->start();
- return status;
- }
- return NO_INIT;
-}
-
-void MediaPlayer2Manager::AudioOutput::setNextOutput(const sp<AudioOutput>& nextOutput) {
- Mutex::Autolock lock(mLock);
- mNextOutput = nextOutput;
-}
-
-void MediaPlayer2Manager::AudioOutput::switchToNextOutput() {
- ALOGV("switchToNextOutput");
-
- // Try to acquire the callback lock before moving track (without incurring deadlock).
- const unsigned kMaxSwitchTries = 100;
- Mutex::Autolock lock(mLock);
- for (unsigned tries = 0;;) {
- if (mTrack == 0) {
- return;
- }
- if (mNextOutput != NULL && mNextOutput != this) {
- if (mCallbackData != NULL) {
- // two alternative approaches
-#if 1
- CallbackData *callbackData = mCallbackData;
- mLock.unlock();
- // proper acquisition sequence
- callbackData->lock();
- mLock.lock();
- // Caution: it is unlikely that someone deleted our callback or changed our target
- if (callbackData != mCallbackData || mNextOutput == NULL || mNextOutput == this) {
- // fatal if we are starved out.
- LOG_ALWAYS_FATAL_IF(++tries > kMaxSwitchTries,
- "switchToNextOutput() cannot obtain correct lock sequence");
- callbackData->unlock();
- continue;
- }
- callbackData->mSwitching = true; // begin track switch
- callbackData->setOutput(NULL);
-#else
- // tryBeginTrackSwitch() returns false if the callback has the lock.
- if (!mCallbackData->tryBeginTrackSwitch()) {
- // fatal if we are starved out.
- LOG_ALWAYS_FATAL_IF(++tries > kMaxSwitchTries,
- "switchToNextOutput() cannot obtain callback lock");
- mLock.unlock();
- usleep(5 * 1000 /* usec */); // allow callback to use AudioOutput
- mLock.lock();
- continue;
- }
-#endif
- }
-
- Mutex::Autolock nextLock(mNextOutput->mLock);
-
- // If the next output track is not NULL, then it has been
- // opened already for playback.
- // This is possible even without the next player being started,
- // for example, the next player could be prepared and seeked.
- //
- // Presuming it isn't advisable to force the track over.
- if (mNextOutput->mTrack == NULL) {
- ALOGD("Recycling track for gapless playback");
- delete mNextOutput->mCallbackData;
- mNextOutput->mCallbackData = mCallbackData;
- mNextOutput->mRecycledTrack = mTrack;
- mNextOutput->mSampleRateHz = mSampleRateHz;
- mNextOutput->mMsecsPerFrame = mMsecsPerFrame;
- mNextOutput->mFlags = mFlags;
- mNextOutput->mFrameSize = mFrameSize;
- close_l();
- mCallbackData = NULL; // destruction handled by mNextOutput
- } else {
- ALOGW("Ignoring gapless playback because next player has already started");
- // remove track in case resource needed for future players.
- if (mCallbackData != NULL) {
- mCallbackData->endTrackSwitch(); // release lock for callbacks before close.
- }
- close_l();
- }
- }
- break;
- }
-}
-
-ssize_t MediaPlayer2Manager::AudioOutput::write(const void* buffer, size_t size, bool blocking)
-{
- Mutex::Autolock lock(mLock);
- LOG_ALWAYS_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
-
- //ALOGV("write(%p, %u)", buffer, size);
- if (mTrack != 0) {
- return mTrack->write(buffer, size, blocking);
- }
- return NO_INIT;
-}
-
-void MediaPlayer2Manager::AudioOutput::stop()
-{
- ALOGV("stop");
- Mutex::Autolock lock(mLock);
- if (mTrack != 0) mTrack->stop();
-}
-
-void MediaPlayer2Manager::AudioOutput::flush()
-{
- ALOGV("flush");
- Mutex::Autolock lock(mLock);
- if (mTrack != 0) mTrack->flush();
-}
-
-void MediaPlayer2Manager::AudioOutput::pause()
-{
- ALOGV("pause");
- Mutex::Autolock lock(mLock);
- if (mTrack != 0) mTrack->pause();
-}
-
-void MediaPlayer2Manager::AudioOutput::close()
-{
- ALOGV("close");
- sp<AudioTrack> track;
- {
- Mutex::Autolock lock(mLock);
- track = mTrack;
- close_l(); // clears mTrack
- }
- // destruction of the track occurs outside of mutex.
-}
-
-void MediaPlayer2Manager::AudioOutput::setVolume(float left, float right)
-{
- ALOGV("setVolume(%f, %f)", left, right);
- Mutex::Autolock lock(mLock);
- mLeftVolume = left;
- mRightVolume = right;
- if (mTrack != 0) {
- mTrack->setVolume(left, right);
- }
-}
-
-status_t MediaPlayer2Manager::AudioOutput::setPlaybackRate(const AudioPlaybackRate &rate)
-{
- ALOGV("setPlaybackRate(%f %f %d %d)",
- rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
- // remember rate so that we can set it when the track is opened
- mPlaybackRate = rate;
- return OK;
- }
- status_t res = mTrack->setPlaybackRate(rate);
- if (res != NO_ERROR) {
- return res;
- }
- // rate.mSpeed is always greater than 0 if setPlaybackRate succeeded
- CHECK_GT(rate.mSpeed, 0.f);
- mPlaybackRate = rate;
- if (mSampleRateHz != 0) {
- mMsecsPerFrame = 1E3f / (rate.mSpeed * mSampleRateHz);
- }
- return res;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::getPlaybackRate(AudioPlaybackRate *rate)
-{
- ALOGV("setPlaybackRate");
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
- return NO_INIT;
- }
- *rate = mTrack->getPlaybackRate();
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::setAuxEffectSendLevel(float level)
-{
- ALOGV("setAuxEffectSendLevel(%f)", level);
- Mutex::Autolock lock(mLock);
- mSendLevel = level;
- if (mTrack != 0) {
- return mTrack->setAuxEffectSendLevel(level);
- }
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::attachAuxEffect(int effectId)
-{
- ALOGV("attachAuxEffect(%d)", effectId);
- Mutex::Autolock lock(mLock);
- mAuxEffectId = effectId;
- if (mTrack != 0) {
- return mTrack->attachAuxEffect(effectId);
- }
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::setOutputDevice(audio_port_handle_t deviceId)
-{
- ALOGV("setOutputDevice(%d)", deviceId);
- Mutex::Autolock lock(mLock);
- mSelectedDeviceId = deviceId;
- if (mTrack != 0) {
- return mTrack->setOutputDevice(deviceId);
- }
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::getRoutedDeviceId(audio_port_handle_t* deviceId)
-{
- ALOGV("getRoutedDeviceId");
- Mutex::Autolock lock(mLock);
- if (mTrack != 0) {
- mRoutedDeviceId = mTrack->getRoutedDeviceId();
- }
- *deviceId = mRoutedDeviceId;
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::enableAudioDeviceCallback(bool enabled)
-{
- ALOGV("enableAudioDeviceCallback, %d", enabled);
- Mutex::Autolock lock(mLock);
- mDeviceCallbackEnabled = enabled;
- if (mTrack != 0) {
- status_t status;
- if (enabled) {
- status = mTrack->addAudioDeviceCallback(mDeviceCallback.promote());
- } else {
- status = mTrack->removeAudioDeviceCallback(mDeviceCallback.promote());
- }
- return status;
- }
- return NO_ERROR;
-}
-
-// static
-void MediaPlayer2Manager::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;
- }
- return;
- }
-
- 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.
- // NuPlayer2Renderer 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);
- }
-
- data->unlock();
-}
-
-audio_session_t MediaPlayer2Manager::AudioOutput::getSessionId() const
-{
- Mutex::Autolock lock(mLock);
- return mSessionId;
-}
-
-uint32_t MediaPlayer2Manager::AudioOutput::getSampleRate() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return 0;
- return mTrack->getSampleRate();
-}
-
-int64_t MediaPlayer2Manager::AudioOutput::getBufferDurationInUs() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
- return 0;
- }
- int64_t duration;
- if (mTrack->getBufferDurationInUs(&duration) != OK) {
- return 0;
- }
- return duration;
-}
-
-} // namespace android
diff --git a/media/libmediaplayer2/MediaPlayer2Manager.h b/media/libmediaplayer2/MediaPlayer2Manager.h
deleted file mode 100644
index 52bb9c6..0000000
--- a/media/libmediaplayer2/MediaPlayer2Manager.h
+++ /dev/null
@@ -1,387 +0,0 @@
-/*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#ifndef ANDROID_MEDIAPLAYER2MANAGER_H
-#define ANDROID_MEDIAPLAYER2MANAGER_H
-
-#include <arpa/inet.h>
-
-#include <utils/threads.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
-
-#include <media/Metadata.h>
-#include <media/stagefright/foundation/ABase.h>
-#include <mediaplayer2/MediaPlayer2Engine.h>
-#include <mediaplayer2/MediaPlayer2Interface.h>
-
-#include <system/audio.h>
-
-namespace android {
-
-struct ANativeWindowWrapper;
-struct AudioPlaybackRate;
-class AudioTrack;
-struct AVSyncSettings;
-class DataSource;
-struct MediaHTTPService;
-class MediaPlayer2EngineClient;
-
-#define CALLBACK_ANTAGONIZER 0
-#if CALLBACK_ANTAGONIZER
-class Antagonizer {
-public:
- Antagonizer(
- MediaPlayer2Interface::NotifyCallback cb,
- const wp<MediaPlayer2Engine> &client);
- void start() { mActive = true; }
- void stop() { mActive = false; }
- void kill();
-private:
- static const int interval;
- Antagonizer();
- static int callbackThread(void *cookie);
- Mutex mLock;
- Condition mCondition;
- bool mExit;
- bool mActive;
- wp<MediaPlayer2Engine> mClient;
- MediaPlayer2Interface::NotifyCallback mCb;
-};
-#endif
-
-class MediaPlayer2Manager {
- class Client;
-
- class AudioOutput : public MediaPlayer2Interface::AudioSink
- {
- class CallbackData;
-
- public:
- AudioOutput(
- audio_session_t sessionId,
- uid_t uid,
- int pid,
- const audio_attributes_t * attr,
- const sp<AudioSystem::AudioDeviceCallback>& deviceCallback);
- virtual ~AudioOutput();
-
- virtual bool ready() const { return mTrack != 0; }
- virtual ssize_t bufferSize() const;
- virtual ssize_t frameCount() const;
- virtual ssize_t channelCount() const;
- virtual ssize_t frameSize() const;
- virtual uint32_t latency() const;
- virtual float msecsPerFrame() const;
- virtual status_t getPosition(uint32_t *position) const;
- virtual status_t getTimestamp(AudioTimestamp &ts) const;
- virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const;
- virtual status_t getFramesWritten(uint32_t *frameswritten) const;
- virtual audio_session_t getSessionId() const;
- virtual uint32_t getSampleRate() const;
- virtual int64_t getBufferDurationInUs() const;
-
- virtual status_t open(
- uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
- audio_format_t format, int bufferCount,
- AudioCallback cb, void *cookie,
- audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
- const audio_offload_info_t *offloadInfo = NULL,
- bool doNotReconnect = false,
- uint32_t suggestedFrameCount = 0);
-
- virtual status_t start();
- virtual ssize_t write(const void* buffer, size_t size, bool blocking = true);
- virtual void stop();
- virtual void flush();
- virtual void pause();
- virtual void close();
- void setAudioStreamType(audio_stream_type_t streamType);
- virtual audio_stream_type_t getAudioStreamType() const { return mStreamType; }
- void setAudioAttributes(const audio_attributes_t * attributes);
-
- void setVolume(float left, float right);
- virtual status_t setPlaybackRate(const AudioPlaybackRate& rate);
- virtual status_t getPlaybackRate(AudioPlaybackRate* rate /* nonnull */);
-
- status_t setAuxEffectSendLevel(float level);
- status_t attachAuxEffect(int effectId);
- virtual status_t dump(int fd, const Vector<String16>& args) const;
-
- static bool isOnEmulator();
- static int getMinBufferCount();
- void setNextOutput(const sp<AudioOutput>& nextOutput);
- void switchToNextOutput();
- virtual bool needsTrailingPadding() { return mNextOutput == NULL; }
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys);
-
- // AudioRouting
- virtual status_t setOutputDevice(audio_port_handle_t deviceId);
- virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
- virtual status_t enableAudioDeviceCallback(bool enabled);
-
- private:
- static void setMinBufferCount();
- static void CallbackWrapper(
- int event, void *me, void *info);
- void deleteRecycledTrack_l();
- void close_l();
- status_t updateTrack();
-
- sp<AudioTrack> mTrack;
- sp<AudioTrack> mRecycledTrack;
- sp<AudioOutput> mNextOutput;
- AudioCallback mCallback;
- void * mCallbackCookie;
- CallbackData * mCallbackData;
- audio_stream_type_t mStreamType;
- audio_attributes_t * mAttributes;
- float mLeftVolume;
- float mRightVolume;
- AudioPlaybackRate mPlaybackRate;
- uint32_t mSampleRateHz; // sample rate of the content, as set in open()
- float mMsecsPerFrame;
- size_t mFrameSize;
- audio_session_t mSessionId;
- uid_t mUid;
- int mPid;
- float mSendLevel;
- int mAuxEffectId;
- audio_output_flags_t mFlags;
- audio_port_handle_t mSelectedDeviceId;
- audio_port_handle_t mRoutedDeviceId;
- bool mDeviceCallbackEnabled;
- wp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
- mutable Mutex mLock;
-
- // static variables below not protected by mutex
- 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 {
- friend AudioOutput;
- public:
- explicit CallbackData(AudioOutput *cookie) {
- mData = cookie;
- mSwitching = false;
- }
- AudioOutput * getOutput() const { return mData; }
- void setOutput(AudioOutput* newcookie) { mData = 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(); }
-
- // tryBeginTrackSwitch/endTrackSwitch are used when the CallbackData is handed over
- // to the next sink.
-
- // tryBeginTrackSwitch() returns true only if it obtains the lock.
- bool tryBeginTrackSwitch() {
- LOG_ALWAYS_FATAL_IF(mSwitching, "tryBeginTrackSwitch() already called");
- if (mLock.tryLock() != OK) {
- return false;
- }
- mSwitching = true;
- return true;
- }
- void endTrackSwitch() {
- if (mSwitching) {
- mLock.unlock();
- }
- mSwitching = false;
- }
- private:
- AudioOutput * mData;
- mutable Mutex mLock; // a recursive mutex might make this unnecessary.
- bool mSwitching;
- DISALLOW_EVIL_CONSTRUCTORS(CallbackData);
- };
-
- }; // AudioOutput
-
-
-public:
- MediaPlayer2Manager();
- virtual ~MediaPlayer2Manager();
-
- static MediaPlayer2Manager& get();
-
- // MediaPlayer2Manager interface
- virtual sp<MediaPlayer2Engine> create(const sp<MediaPlayer2EngineClient>& client,
- audio_session_t audioSessionId);
-
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- void removeClient(const wp<Client>& client);
- bool hasClient(wp<Client> client);
-
-private:
- class Client : public MediaPlayer2Engine {
- // MediaPlayer2Engine interface
- virtual void disconnect();
- virtual status_t setVideoSurfaceTexture(
- const sp<ANativeWindowWrapper>& nww) override;
- virtual status_t setBufferingSettings(const BufferingSettings& buffering) override;
- virtual status_t getBufferingSettings(
- BufferingSettings* buffering /* nonnull */) override;
- virtual status_t prepareAsync();
- virtual status_t start();
- virtual status_t stop();
- virtual status_t pause();
- virtual status_t isPlaying(bool* state);
- virtual status_t setPlaybackSettings(const AudioPlaybackRate& rate);
- virtual status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */);
- virtual status_t setSyncSettings(const AVSyncSettings& rate, float videoFpsHint);
- virtual status_t getSyncSettings(AVSyncSettings* rate /* nonnull */,
- float* videoFps /* nonnull */);
- virtual status_t seekTo(
- int msec,
- MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC);
- virtual status_t getCurrentPosition(int* msec);
- virtual status_t getDuration(int* msec);
- virtual status_t reset();
- virtual status_t notifyAt(int64_t mediaTimeUs);
- virtual status_t setAudioStreamType(audio_stream_type_t type);
- virtual status_t setLooping(int loop);
- virtual status_t setVolume(float leftVolume, float rightVolume);
- virtual status_t invoke(const Parcel& request, Parcel *reply);
- virtual status_t setMetadataFilter(const Parcel& filter);
- virtual status_t getMetadata(bool update_only,
- bool apply_filter,
- Parcel *reply);
- virtual status_t setAuxEffectSendLevel(float level);
- virtual status_t attachAuxEffect(int effectId);
- virtual status_t setParameter(int key, const Parcel &request);
- virtual status_t getParameter(int key, Parcel *reply);
- virtual status_t setNextPlayer(const sp<MediaPlayer2Engine>& player);
-
- virtual status_t setDataSource(const sp<DataSourceDesc> &dsd) override;
- virtual status_t prepareNextDataSource(const sp<DataSourceDesc> &dsd) override;
- virtual status_t playNextDataSource(int64_t srcId) override;
-
- static void notify(const wp<MediaPlayer2Engine> &listener, int64_t srcId,
- int msg, int ext1, int ext2, const Parcel *obj);
-
- pid_t pid() const { return mPid; }
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- audio_session_t getAudioSessionId() { return mAudioSessionId; }
- // Modular DRM
- virtual status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId);
- virtual status_t releaseDrm();
- // AudioRouting
- virtual status_t setOutputDevice(audio_port_handle_t deviceId);
- virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
- virtual status_t enableAudioDeviceCallback(bool enabled);
-
- private:
- class AudioDeviceUpdatedNotifier: public AudioSystem::AudioDeviceCallback
- {
- public:
- AudioDeviceUpdatedNotifier(const sp<MediaPlayer2Interface>& listener) {
- mListener = listener;
- }
- ~AudioDeviceUpdatedNotifier() {}
-
- virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
- audio_port_handle_t deviceId);
-
- private:
- wp<MediaPlayer2Interface> mListener;
- };
-
- friend class MediaPlayer2Manager;
- Client(pid_t pid,
- int32_t connId,
- const sp<MediaPlayer2EngineClient>& client,
- audio_session_t audioSessionId,
- uid_t uid);
- Client();
- virtual ~Client();
- bool init();
-
- void deletePlayer();
-
- sp<MediaPlayer2Interface> getPlayer() const { Mutex::Autolock lock(mLock); return mPlayer; }
-
-
-
- // @param type Of the metadata to be tested.
- // @return true if the metadata should be dropped according to
- // the filters.
- bool shouldDropMetadata(media::Metadata::Type type) const;
-
- // Add a new element to the set of metadata updated. Noop if
- // the element exists already.
- // @param type Of the metadata to be recorded.
- void addNewMetadataUpdate(media::Metadata::Type type);
-
- // Disconnect from the currently connected ANativeWindow.
- void disconnectNativeWindow_l();
-
- status_t setAudioAttributes_l(const Parcel &request);
-
- mutable Mutex mLock;
- sp<MediaPlayer2Interface> mPlayer;
- sp<MediaPlayer2EngineClient> mClient;
- sp<AudioOutput> mAudioOutput;
- pid_t mPid;
- status_t mStatus;
- bool mLoop;
- int32_t mConnId;
- audio_session_t mAudioSessionId;
- audio_attributes_t * mAudioAttributes;
- uid_t mUid;
- sp<ANativeWindowWrapper> mConnectedWindow;
- sp<Client> mNextClient;
-
- // Metadata filters.
- media::Metadata::Filter mMetadataAllow; // protected by mLock
- media::Metadata::Filter mMetadataDrop; // protected by mLock
-
- // Metadata updated. For each MEDIA_INFO_METADATA_UPDATE
- // notification we try to update mMetadataUpdated which is a
- // set: no duplicate.
- // getMetadata clears this set.
- media::Metadata::Filter mMetadataUpdated; // protected by mLock
-
- sp<AudioDeviceUpdatedNotifier> mAudioDeviceUpdatedListener;
-#if CALLBACK_ANTAGONIZER
- Antagonizer* mAntagonizer;
-#endif
- }; // Client
-
-// ----------------------------------------------------------------------------
-
- pid_t mPid;
- uid_t mUid;
-
- mutable Mutex mLock;
- SortedVector< wp<Client> > mClients;
- int32_t mNextConnId;
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_MEDIAPLAYER2MANAGER_H
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h
new file mode 100644
index 0000000..ad4937a
--- /dev/null
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h
@@ -0,0 +1,192 @@
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_MEDIAPLAYER2AUDIOOUTPUT_H
+#define ANDROID_MEDIAPLAYER2AUDIOOUTPUT_H
+
+#include <mediaplayer2/MediaPlayer2Interface.h>
+
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class AudioTrack;
+
+class MediaPlayer2AudioOutput : public MediaPlayer2Interface::AudioSink
+{
+ class CallbackData;
+
+public:
+ MediaPlayer2AudioOutput(audio_session_t sessionId,
+ uid_t uid,
+ int pid,
+ const audio_attributes_t * attr,
+ const sp<AudioSystem::AudioDeviceCallback>& deviceCallback);
+ virtual ~MediaPlayer2AudioOutput();
+
+ virtual bool ready() const {
+ return mTrack != 0;
+ }
+ virtual ssize_t bufferSize() const;
+ virtual ssize_t frameCount() const;
+ virtual ssize_t channelCount() const;
+ virtual ssize_t frameSize() const;
+ virtual uint32_t latency() const;
+ virtual float msecsPerFrame() const;
+ virtual status_t getPosition(uint32_t *position) const;
+ virtual status_t getTimestamp(AudioTimestamp &ts) const;
+ virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const;
+ virtual status_t getFramesWritten(uint32_t *frameswritten) const;
+ virtual audio_session_t getSessionId() const;
+ virtual uint32_t getSampleRate() const;
+ virtual int64_t getBufferDurationInUs() const;
+
+ virtual status_t open(
+ uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
+ audio_format_t format, int bufferCount,
+ AudioCallback cb, void *cookie,
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+ const audio_offload_info_t *offloadInfo = NULL,
+ bool doNotReconnect = false,
+ uint32_t suggestedFrameCount = 0);
+
+ virtual status_t start();
+ virtual ssize_t write(const void* buffer, size_t size, bool blocking = true);
+ virtual void stop();
+ virtual void flush();
+ virtual void pause();
+ virtual void close();
+ void setAudioStreamType(audio_stream_type_t streamType);
+ virtual audio_stream_type_t getAudioStreamType() const {
+ return mStreamType;
+ }
+ void setAudioAttributes(const audio_attributes_t * attributes);
+
+ void setVolume(float left, float right);
+ virtual status_t setPlaybackRate(const AudioPlaybackRate& rate);
+ virtual status_t getPlaybackRate(AudioPlaybackRate* rate /* nonnull */);
+
+ status_t setAuxEffectSendLevel(float level);
+ status_t attachAuxEffect(int effectId);
+ virtual status_t dump(int fd, const Vector<String16>& args) const;
+
+ static bool isOnEmulator();
+ static int getMinBufferCount();
+ void setNextOutput(const sp<MediaPlayer2AudioOutput>& nextOutput);
+ virtual bool needsTrailingPadding() {
+ return true;
+ // TODO: return correct value.
+ //return mNextOutput == NULL;
+ }
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+
+ // AudioRouting
+ virtual status_t setOutputDevice(audio_port_handle_t deviceId);
+ virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
+ virtual status_t enableAudioDeviceCallback(bool enabled);
+
+private:
+ static void setMinBufferCount();
+ static void CallbackWrapper(int event, void *me, void *info);
+ void deleteRecycledTrack_l();
+ void close_l();
+ status_t updateTrack_l();
+
+ sp<AudioTrack> mTrack;
+ AudioCallback mCallback;
+ void * mCallbackCookie;
+ CallbackData * mCallbackData;
+ audio_stream_type_t mStreamType;
+ audio_attributes_t * mAttributes;
+ float mLeftVolume;
+ float mRightVolume;
+ AudioPlaybackRate mPlaybackRate;
+ uint32_t mSampleRateHz; // sample rate of the content, as set in open()
+ float mMsecsPerFrame;
+ size_t mFrameSize;
+ audio_session_t mSessionId;
+ uid_t mUid;
+ int mPid;
+ float mSendLevel;
+ int mAuxEffectId;
+ audio_output_flags_t mFlags;
+ audio_port_handle_t mSelectedDeviceId;
+ audio_port_handle_t mRoutedDeviceId;
+ bool mDeviceCallbackEnabled;
+ wp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
+ mutable Mutex mLock;
+
+ // static variables below not protected by mutex
+ 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 {
+ friend MediaPlayer2AudioOutput;
+ public:
+ explicit CallbackData(MediaPlayer2AudioOutput *cookie) {
+ mData = cookie;
+ mSwitching = false;
+ }
+ MediaPlayer2AudioOutput *getOutput() const {
+ return mData;
+ }
+ void setOutput(MediaPlayer2AudioOutput* newcookie) {
+ mData = 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();
+ }
+
+ // tryBeginTrackSwitch/endTrackSwitch are used when the CallbackData is handed over
+ // to the next sink.
+
+ // tryBeginTrackSwitch() returns true only if it obtains the lock.
+ bool tryBeginTrackSwitch() {
+ LOG_ALWAYS_FATAL_IF(mSwitching, "tryBeginTrackSwitch() already called");
+ if (mLock.tryLock() != OK) {
+ return false;
+ }
+ mSwitching = true;
+ return true;
+ }
+ void endTrackSwitch() {
+ if (mSwitching) {
+ mLock.unlock();
+ }
+ mSwitching = false;
+ }
+
+ private:
+ MediaPlayer2AudioOutput *mData;
+ mutable Mutex mLock; // a recursive mutex might make this unnecessary.
+ bool mSwitching;
+ DISALLOW_EVIL_CONSTRUCTORS(CallbackData);
+ };
+};
+
+}; // namespace android
+
+#endif // ANDROID_MEDIAPLAYER2AUDIOOUTPUT_H
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Engine.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Engine.h
deleted file mode 100644
index 749d83c..0000000
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Engine.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_MEDIAPLAYER2ENGINE_H
-#define ANDROID_MEDIAPLAYER2ENGINE_H
-
-#include <utils/RefBase.h>
-#include <binder/Parcel.h>
-#include <utils/KeyedVector.h>
-#include <system/audio.h>
-
-#include <media/MediaSource.h>
-
-// Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
-// global, and not in android::
-struct sockaddr_in;
-
-namespace android {
-
-struct ANativeWindowWrapper;
-struct AVSyncSettings;
-struct AudioPlaybackRate;
-struct BufferingSettings;
-class DataSource;
-struct DataSourceDesc;
-struct IStreamSource;
-struct MediaHTTPService;
-class Parcel;
-
-typedef MediaSource::ReadOptions::SeekMode MediaPlayer2SeekMode;
-
-class MediaPlayer2Engine: public RefBase
-{
-public:
- virtual void disconnect() = 0;
-
- virtual status_t setDataSource(const sp<DataSourceDesc>& source) = 0;
- virtual status_t prepareNextDataSource(const sp<DataSourceDesc>& source) = 0;
- virtual status_t playNextDataSource(int64_t srcId) = 0;
- virtual status_t setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww) = 0;
- virtual status_t getBufferingSettings(
- BufferingSettings* buffering /* nonnull */) = 0;
- virtual status_t setBufferingSettings(const BufferingSettings& buffering) = 0;
- virtual status_t prepareAsync() = 0;
- virtual status_t start() = 0;
- virtual status_t stop() = 0;
- virtual status_t pause() = 0;
- virtual status_t isPlaying(bool* state) = 0;
- virtual status_t setPlaybackSettings(const AudioPlaybackRate& rate) = 0;
- virtual status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) = 0;
- virtual status_t setSyncSettings(const AVSyncSettings& sync, float videoFpsHint) = 0;
- virtual status_t getSyncSettings(AVSyncSettings* sync /* nonnull */,
- float* videoFps /* nonnull */) = 0;
- virtual status_t seekTo(
- int msec,
- MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC) = 0;
- virtual status_t getCurrentPosition(int* msec) = 0;
- virtual status_t getDuration(int* msec) = 0;
- virtual status_t notifyAt(int64_t mediaTimeUs) = 0;
- virtual status_t reset() = 0;
- virtual status_t setAudioStreamType(audio_stream_type_t type) = 0;
- virtual status_t setLooping(int loop) = 0;
- virtual status_t setVolume(float leftVolume, float rightVolume) = 0;
- virtual status_t setAuxEffectSendLevel(float level) = 0;
- virtual status_t attachAuxEffect(int effectId) = 0;
- virtual status_t setParameter(int key, const Parcel& request) = 0;
- virtual status_t getParameter(int key, Parcel* reply) = 0;
- virtual status_t setNextPlayer(const sp<MediaPlayer2Engine>& next) = 0;
-
- // Modular DRM
- virtual status_t prepareDrm(const uint8_t uuid[16],
- const Vector<uint8_t>& drmSessionId) = 0;
- virtual status_t releaseDrm() = 0;
-
- // Invoke a generic method on the player by using opaque parcels
- // for the request and reply.
- // @param request Parcel that must start with the media player
- // interface token.
- // @param[out] reply Parcel to hold the reply data. Cannot be null.
- // @return OK if the invocation was made successfully.
- virtual status_t invoke(const Parcel& request, Parcel *reply) = 0;
-
- // Set a new metadata filter.
- // @param filter A set of allow and drop rules serialized in a Parcel.
- // @return OK if the invocation was made successfully.
- virtual status_t setMetadataFilter(const Parcel& filter) = 0;
-
- // Retrieve a set of metadata.
- // @param update_only Include only the metadata that have changed
- // since the last invocation of getMetadata.
- // The set is built using the unfiltered
- // notifications the native player sent to the
- // MediaPlayer2Manager during that period of
- // time. If false, all the metadatas are considered.
- // @param apply_filter If true, once the metadata set has been built based
- // on the value update_only, the current filter is
- // applied.
- // @param[out] metadata On exit contains a set (possibly empty) of metadata.
- // Valid only if the call returned OK.
- // @return OK if the invocation was made successfully.
- virtual status_t getMetadata(bool update_only,
- bool apply_filter,
- Parcel *metadata) = 0;
-
- // AudioRouting
- virtual status_t setOutputDevice(audio_port_handle_t deviceId) = 0;
- virtual status_t getRoutedDeviceId(audio_port_handle_t *deviceId) = 0;
- virtual status_t enableAudioDeviceCallback(bool enabled) = 0;
-};
-
-}; // namespace android
-
-#endif // ANDROID_MEDIAPLAYER2ENGINE_H
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2EngineClient.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2EngineClient.h
deleted file mode 100644
index 0b066aa..0000000
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2EngineClient.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_MEDIAPLAYER2ENGINECLIENT_H
-#define ANDROID_MEDIAPLAYER2ENGINECLIENT_H
-
-#include <utils/RefBase.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-class MediaPlayer2EngineClient: public RefBase
-{
-public:
- virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
-};
-
-}; // namespace android
-
-#endif // ANDROID_MEDIAPLAYER2ENGINECLIENT_H
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
index c0c490b..96aea7f 100644
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
@@ -21,18 +21,17 @@
#include <sys/types.h>
#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
#include <utils/String8.h>
#include <utils/RefBase.h>
+#include <media/AVSyncSettings.h>
#include <media/AudioResamplerPublic.h>
#include <media/AudioSystem.h>
#include <media/AudioTimestamp.h>
-#include <media/AVSyncSettings.h>
#include <media/BufferingSettings.h>
#include <media/Metadata.h>
#include <media/stagefright/foundation/AHandler.h>
-#include <mediaplayer2/mediaplayer2.h>
+#include <mediaplayer2/MediaPlayer2Types.h>
// Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
// global, and not in android::
@@ -40,14 +39,10 @@
namespace android {
-class DataSource;
struct DataSourceDesc;
-struct MediaHTTPService;
class Parcel;
struct ANativeWindowWrapper;
-template<typename T> class SortedVector;
-
#define DEFAULT_AUDIOSINK_BUFFERCOUNT 4
#define DEFAULT_AUDIOSINK_BUFFERSIZE 1200
#define DEFAULT_AUDIOSINK_SAMPLERATE 44100
@@ -58,14 +53,14 @@
// duration below which we do not allow deep audio buffering
#define AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US 5000000
-// abstract base class - use MediaPlayer2Interface
-class MediaPlayer2Interface : public AHandler
+class MediaPlayer2InterfaceListener: public RefBase
{
public:
- // callback mechanism for passing messages to MediaPlayer2 object
- typedef void (*NotifyCallback)(const wp<MediaPlayer2Engine> &listener,
- int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj);
+ virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
+};
+class MediaPlayer2Interface : public AHandler {
+public:
// AudioSink: abstraction layer for audio output
class AudioSink : public RefBase {
public:
@@ -79,29 +74,28 @@
// Callback returns the number of bytes actually written to the buffer.
typedef size_t (*AudioCallback)(
- AudioSink *audioSink, void *buffer, size_t size, void *cookie,
- cb_event_t event);
+ AudioSink *audioSink, void *buffer, size_t size, void *cookie, cb_event_t event);
- virtual ~AudioSink() {}
- virtual bool ready() const = 0; // audio output is open and ready
- virtual ssize_t bufferSize() const = 0;
- virtual ssize_t frameCount() const = 0;
- virtual ssize_t channelCount() const = 0;
- virtual ssize_t frameSize() const = 0;
- virtual uint32_t latency() const = 0;
- virtual float msecsPerFrame() const = 0;
- virtual status_t getPosition(uint32_t *position) const = 0;
- virtual status_t getTimestamp(AudioTimestamp &ts) const = 0;
- virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const = 0;
- virtual status_t getFramesWritten(uint32_t *frameswritten) const = 0;
+ virtual ~AudioSink() {}
+ virtual bool ready() const = 0; // audio output is open and ready
+ virtual ssize_t bufferSize() const = 0;
+ virtual ssize_t frameCount() const = 0;
+ virtual ssize_t channelCount() const = 0;
+ virtual ssize_t frameSize() const = 0;
+ virtual uint32_t latency() const = 0;
+ virtual float msecsPerFrame() const = 0;
+ virtual status_t getPosition(uint32_t *position) const = 0;
+ virtual status_t getTimestamp(AudioTimestamp &ts) const = 0;
+ virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const = 0;
+ virtual status_t getFramesWritten(uint32_t *frameswritten) const = 0;
virtual audio_session_t getSessionId() const = 0;
virtual audio_stream_type_t getAudioStreamType() const = 0;
- virtual uint32_t getSampleRate() const = 0;
- virtual int64_t getBufferDurationInUs() const = 0;
+ virtual uint32_t getSampleRate() const = 0;
+ virtual int64_t getBufferDurationInUs() const = 0;
// If no callback is specified, use the "write" API below to submit
// audio data.
- virtual status_t open(
+ virtual status_t open(
uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
audio_format_t format=AUDIO_FORMAT_PCM_16_BIT,
int bufferCount=DEFAULT_AUDIOSINK_BUFFERCOUNT,
@@ -112,7 +106,7 @@
bool doNotReconnect = false,
uint32_t suggestedFrameCount = 0) = 0;
- virtual status_t start() = 0;
+ virtual status_t start() = 0;
/* Input parameter |size| is in byte units stored in |buffer|.
* Data is copied over and actual number of bytes written (>= 0)
@@ -124,19 +118,25 @@
* buffer, unless an error occurs or the copy operation is
* prematurely stopped.
*/
- virtual ssize_t write(const void* buffer, size_t size, bool blocking = true) = 0;
+ virtual ssize_t write(const void* buffer, size_t size, bool blocking = true) = 0;
- virtual void stop() = 0;
- virtual void flush() = 0;
- virtual void pause() = 0;
- virtual void close() = 0;
+ virtual void stop() = 0;
+ virtual void flush() = 0;
+ virtual void pause() = 0;
+ virtual void close() = 0;
- virtual status_t setPlaybackRate(const AudioPlaybackRate& rate) = 0;
- virtual status_t getPlaybackRate(AudioPlaybackRate* rate /* nonnull */) = 0;
- virtual bool needsTrailingPadding() { return true; }
+ virtual status_t setPlaybackRate(const AudioPlaybackRate& rate) = 0;
+ virtual status_t getPlaybackRate(AudioPlaybackRate* rate /* nonnull */) = 0;
+ virtual bool needsTrailingPadding() {
+ return true;
+ }
- virtual status_t setParameters(const String8& /* keyValuePairs */) { return NO_ERROR; }
- virtual String8 getParameters(const String8& /* keys */) { return String8::empty(); }
+ virtual status_t setParameters(const String8& /* keyValuePairs */) {
+ return NO_ERROR;
+ }
+ virtual String8 getParameters(const String8& /* keys */) {
+ return String8::empty();
+ }
// AudioRouting
virtual status_t setOutputDevice(audio_port_handle_t deviceId);
@@ -144,47 +144,48 @@
virtual status_t enableAudioDeviceCallback(bool enabled);
};
- MediaPlayer2Interface() : mClient(0), mNotify(0) { }
- virtual ~MediaPlayer2Interface() { }
- virtual status_t initCheck() = 0;
+ MediaPlayer2Interface() : mListener(NULL) { }
+ virtual ~MediaPlayer2Interface() { }
+ virtual status_t initCheck() = 0;
- virtual void setAudioSink(const sp<AudioSink>& audioSink) { mAudioSink = audioSink; }
+ virtual void setAudioSink(const sp<AudioSink>& audioSink) {
+ mAudioSink = audioSink;
+ }
- virtual status_t setDataSource(const sp<DataSourceDesc> &dsd) = 0;
+ virtual status_t setDataSource(const sp<DataSourceDesc> &dsd) = 0;
- virtual status_t prepareNextDataSource(const sp<DataSourceDesc> &dsd) = 0;
+ virtual status_t prepareNextDataSource(const sp<DataSourceDesc> &dsd) = 0;
- virtual status_t playNextDataSource(int64_t srcId) = 0;
+ virtual status_t playNextDataSource(int64_t srcId) = 0;
// pass the buffered native window to the media player service
- virtual status_t setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww) = 0;
+ virtual status_t setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww) = 0;
- virtual status_t getBufferingSettings(
- BufferingSettings* buffering /* nonnull */) {
+ virtual status_t getBufferingSettings(BufferingSettings* buffering /* nonnull */) {
*buffering = BufferingSettings();
return OK;
}
- virtual status_t setBufferingSettings(const BufferingSettings& /* buffering */) {
+ virtual status_t setBufferingSettings(const BufferingSettings& /* buffering */) {
return OK;
}
- virtual status_t prepareAsync() = 0;
- virtual status_t start() = 0;
- virtual status_t stop() = 0;
- virtual status_t pause() = 0;
- virtual bool isPlaying() = 0;
- virtual status_t setPlaybackSettings(const AudioPlaybackRate& rate) {
+ virtual status_t prepareAsync() = 0;
+ virtual status_t start() = 0;
+ virtual status_t stop() = 0;
+ virtual status_t pause() = 0;
+ virtual bool isPlaying() = 0;
+ virtual status_t setPlaybackSettings(const AudioPlaybackRate& rate) {
// by default, players only support setting rate to the default
if (!isAudioPlaybackRateEqual(rate, AUDIO_PLAYBACK_RATE_DEFAULT)) {
return BAD_VALUE;
}
return OK;
}
- virtual status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) {
+ virtual status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) {
*rate = AUDIO_PLAYBACK_RATE_DEFAULT;
return OK;
}
- virtual status_t setSyncSettings(const AVSyncSettings& sync, float /* videoFps */) {
+ virtual status_t setSyncSettings(const AVSyncSettings& sync, float /* videoFps */) {
// By default, players only support setting sync source to default; all other sync
// settings are ignored. There is no requirement for getters to return set values.
if (sync.mSource != AVSYNC_SOURCE_DEFAULT) {
@@ -192,27 +193,23 @@
}
return OK;
}
- virtual status_t getSyncSettings(
- AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */) {
+ virtual status_t getSyncSettings(
+ AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */) {
*sync = AVSyncSettings();
*videoFps = -1.f;
return OK;
}
- virtual status_t seekTo(
+ virtual status_t seekTo(
int msec, MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC) = 0;
- virtual status_t getCurrentPosition(int *msec) = 0;
- virtual status_t getDuration(int *msec) = 0;
- virtual status_t reset() = 0;
- virtual status_t notifyAt(int64_t /* mediaTimeUs */) {
+ virtual status_t getCurrentPosition(int *msec) = 0;
+ virtual status_t getDuration(int *msec) = 0;
+ virtual status_t reset() = 0;
+ virtual status_t notifyAt(int64_t /* mediaTimeUs */) {
return INVALID_OPERATION;
}
- virtual status_t setLooping(int loop) = 0;
- virtual status_t setParameter(int key, const Parcel &request) = 0;
- virtual status_t getParameter(int key, Parcel *reply) = 0;
-
- virtual status_t setNextPlayer(const sp<MediaPlayer2Interface>& /* next */) {
- return OK;
- }
+ virtual status_t setLooping(int loop) = 0;
+ virtual status_t setParameter(int key, const Parcel &request) = 0;
+ virtual status_t getParameter(int key, Parcel *reply) = 0;
// Invoke a generic method on the player by using opaque parcels
// for the request and reply.
@@ -221,7 +218,7 @@
// data sent by the java layer.
// @param[out] reply Parcel to hold the reply data. Cannot be null.
// @return OK if the call was successful.
- virtual status_t invoke(const Parcel& request, Parcel *reply) = 0;
+ virtual status_t invoke(const Parcel& request, Parcel *reply) = 0;
// The Client in the MetadataPlayerService calls this method on
// the native player to retrieve all or a subset of metadata.
@@ -230,28 +227,26 @@
// the known metadata should be returned.
// @param[inout] records Parcel where the player appends its metadata.
// @return OK if the call was successful.
- virtual status_t getMetadata(const media::Metadata::Filter& /* ids */,
- Parcel* /* records */) {
+ virtual status_t getMetadata(const media::Metadata::Filter& /* ids */,
+ Parcel* /* records */) {
return INVALID_OPERATION;
};
- void setNotifyCallback(
- const wp<MediaPlayer2Engine> &client, NotifyCallback notifyFunc) {
- Mutex::Autolock autoLock(mNotifyLock);
- mClient = client; mNotify = notifyFunc;
+ void setListener(const sp<MediaPlayer2InterfaceListener> &listener) {
+ Mutex::Autolock autoLock(mListenerLock);
+ mListener = listener;
}
- void sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0,
- const Parcel *obj=NULL) {
- NotifyCallback notifyCB;
- wp<MediaPlayer2Engine> client;
+ void sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0, const Parcel *obj=NULL) {
+ sp<MediaPlayer2InterfaceListener> listener;
{
- Mutex::Autolock autoLock(mNotifyLock);
- notifyCB = mNotify;
- client = mClient;
+ Mutex::Autolock autoLock(mListenerLock);
+ listener = mListener;
}
- if (notifyCB) notifyCB(client, srcId, msg, ext1, ext2, obj);
+ if (listener) {
+ listener->notify(srcId, msg, ext1, ext2, obj);
+ }
}
virtual status_t dump(int /* fd */, const Vector<String16>& /* args */) const {
@@ -261,7 +256,8 @@
virtual void onMessageReceived(const sp<AMessage> & /* msg */) override { }
// Modular DRM
- virtual status_t prepareDrm(const uint8_t /* uuid */[16], const Vector<uint8_t>& /* drmSessionId */) {
+ virtual status_t prepareDrm(const uint8_t /* uuid */[16],
+ const Vector<uint8_t>& /* drmSessionId */) {
return INVALID_OPERATION;
}
virtual status_t releaseDrm() {
@@ -269,14 +265,11 @@
}
protected:
- sp<AudioSink> mAudioSink;
+ sp<AudioSink> mAudioSink;
private:
- friend class MediaPlayer2Manager;
-
- Mutex mNotifyLock;
- wp<MediaPlayer2Engine> mClient;
- NotifyCallback mNotify;
+ Mutex mListenerLock;
+ sp<MediaPlayer2InterfaceListener> mListener;
};
}; // namespace android
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h
new file mode 100644
index 0000000..260c7ed
--- /dev/null
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIAPLAYER2_TYPES_H
+#define ANDROID_MEDIAPLAYER2_TYPES_H
+
+#include <media/mediaplayer_common.h>
+
+#include <media/MediaSource.h>
+
+namespace android {
+
+typedef MediaSource::ReadOptions::SeekMode MediaPlayer2SeekMode;
+
+enum media2_event_type {
+ MEDIA2_NOP = 0, // interface test message
+ MEDIA2_PREPARED = 1,
+ MEDIA2_PLAYBACK_COMPLETE = 2,
+ MEDIA2_BUFFERING_UPDATE = 3,
+ MEDIA2_SEEK_COMPLETE = 4,
+ MEDIA2_SET_VIDEO_SIZE = 5,
+ MEDIA2_STARTED = 6,
+ MEDIA2_PAUSED = 7,
+ MEDIA2_STOPPED = 8,
+ MEDIA2_SKIPPED = 9,
+ MEDIA2_NOTIFY_TIME = 98,
+ MEDIA2_TIMED_TEXT = 99,
+ MEDIA2_ERROR = 100,
+ MEDIA2_INFO = 200,
+ MEDIA2_SUBTITLE_DATA = 201,
+ MEDIA2_META_DATA = 202,
+ MEDIA2_DRM_INFO = 210,
+ MEDIA2_AUDIO_ROUTING_CHANGED = 10000,
+};
+
+// Generic error codes for the media player framework. Errors are fatal, the
+// playback must abort.
+//
+// Errors are communicated back to the client using the
+// MediaPlayer2Listener::notify method defined below.
+// In this situation, 'notify' is invoked with the following:
+// 'msg' is set to MEDIA_ERROR.
+// 'ext1' should be a value from the enum media2_error_type.
+// 'ext2' contains an implementation dependant error code to provide
+// more details. Should default to 0 when not used.
+//
+// The codes are distributed as follow:
+// 0xx: Reserved
+// 1xx: Android Player errors. Something went wrong inside the MediaPlayer2.
+// 2xx: Media errors (e.g Codec not supported). There is a problem with the
+// media itself.
+// 3xx: Runtime errors. Some extraordinary condition arose making the playback
+// impossible.
+//
+enum media2_error_type {
+ // 0xx
+ MEDIA2_ERROR_UNKNOWN = 1,
+ // 1xx
+ // MEDIA2_ERROR_SERVER_DIED = 100,
+ // 2xx
+ MEDIA2_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200,
+ // 3xx
+ MEDIA2_ERROR_FAILED_TO_SET_DATA_SOURCE = 300,
+};
+
+
+// Info and warning codes for the media player framework. These are non fatal,
+// the playback is going on but there might be some user visible issues.
+//
+// Info and warning messages are communicated back to the client using the
+// MediaPlayer2Listener::notify method defined below. In this situation,
+// 'notify' is invoked with the following:
+// 'msg' is set to MEDIA_INFO.
+// 'ext1' should be a value from the enum media2_info_type.
+// 'ext2' contains an implementation dependant info code to provide
+// more details. Should default to 0 when not used.
+//
+// The codes are distributed as follow:
+// 0xx: Reserved
+// 7xx: Android Player info/warning (e.g player lagging behind.)
+// 8xx: Media info/warning (e.g media badly interleaved.)
+//
+enum media2_info_type {
+ // 0xx
+ MEDIA2_INFO_UNKNOWN = 1,
+ // The player was started because it was used as the next player for another
+ // player, which just completed playback
+ MEDIA2_INFO_STARTED_AS_NEXT = 2,
+ // The player just pushed the very first video frame for rendering
+ MEDIA2_INFO_VIDEO_RENDERING_START = 3,
+ // The player just pushed the very first audio frame for rendering
+ MEDIA2_INFO_AUDIO_RENDERING_START = 4,
+ // The player just completed the playback of this data source
+ MEDIA2_INFO_PLAYBACK_COMPLETE = 5,
+ // The player just completed the playback of the full play list
+ MEDIA2_INFO_PLAYLIST_END = 6,
+
+ //1xx
+ // The player just prepared a data source.
+ MEDIA2_INFO_PREPARED = 100,
+ // The player just completed a call play().
+ MEDIA2_INFO_COMPLETE_CALL_PLAY = 101,
+ // The player just completed a call pause().
+ MEDIA2_INFO_COMPLETE_CALL_PAUSE = 102,
+ // The player just completed a call seekTo.
+ MEDIA2_INFO_COMPLETE_CALL_SEEK = 103,
+
+ // 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.
+ MEDIA2_INFO_VIDEO_TRACK_LAGGING = 700,
+ // MediaPlayer2 is temporarily pausing playback internally in order to
+ // buffer more data.
+ MEDIA2_INFO_BUFFERING_START = 701,
+ // MediaPlayer2 is resuming playback after filling buffers.
+ MEDIA2_INFO_BUFFERING_END = 702,
+ // Bandwidth in recent past
+ MEDIA2_INFO_NETWORK_BANDWIDTH = 703,
+
+ // 8xx
+ // Bad interleaving means that a media has been improperly interleaved or not
+ // interleaved at all, e.g has all the video samples first then all the audio
+ // ones. Video is playing but a lot of disk seek may be happening.
+ MEDIA2_INFO_BAD_INTERLEAVING = 800,
+ // The media is not seekable (e.g live stream).
+ MEDIA2_INFO_NOT_SEEKABLE = 801,
+ // New media metadata is available.
+ MEDIA2_INFO_METADATA_UPDATE = 802,
+ // Audio can not be played.
+ MEDIA2_INFO_PLAY_AUDIO_ERROR = 804,
+ // Video can not be played.
+ MEDIA2_INFO_PLAY_VIDEO_ERROR = 805,
+
+ //9xx
+ MEDIA2_INFO_TIMED_TEXT_ERROR = 900,
+};
+
+enum media_player2_states {
+ MEDIA_PLAYER2_STATE_ERROR = 0,
+ MEDIA_PLAYER2_IDLE = 1 << 0,
+ MEDIA_PLAYER2_INITIALIZED = 1 << 1,
+ MEDIA_PLAYER2_PREPARING = 1 << 2,
+ MEDIA_PLAYER2_PREPARED = 1 << 3,
+ MEDIA_PLAYER2_STARTED = 1 << 4,
+ MEDIA_PLAYER2_PAUSED = 1 << 5,
+ MEDIA_PLAYER2_STOPPED = 1 << 6,
+ MEDIA_PLAYER2_PLAYBACK_COMPLETE = 1 << 7
+};
+
+// Keep KEY_PARAMETER_* in sync with MediaPlayer2.java.
+// The same enum space is used for both set and get, in case there are future keys that
+// can be both set and get. But as of now, all parameters are either set only or get only.
+enum media2_parameter_keys {
+ // Streaming/buffering parameters
+ MEDIA2_KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS = 1100, // set only
+
+ // Return a Parcel containing a single int, which is the channel count of the
+ // audio track, or zero for error (e.g. no audio track) or unknown.
+ MEDIA2_KEY_PARAMETER_AUDIO_CHANNEL_COUNT = 1200, // get only
+
+ // Playback rate expressed in permille (1000 is normal speed), saved as int32_t, with negative
+ // values used for rewinding or reverse playback.
+ MEDIA2_KEY_PARAMETER_PLAYBACK_RATE_PERMILLE = 1300, // set only
+
+ // Set a Parcel containing the value of a parcelled Java AudioAttribute instance
+ MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400 // set only
+};
+
+// Keep INVOKE_ID_* in sync with MediaPlayer2.java.
+enum media_player2_invoke_ids {
+ MEDIA_PLAYER2_INVOKE_ID_GET_TRACK_INFO = 1,
+ MEDIA_PLAYER2_INVOKE_ID_ADD_EXTERNAL_SOURCE = 2,
+ MEDIA_PLAYER2_INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3,
+ MEDIA_PLAYER2_INVOKE_ID_SELECT_TRACK = 4,
+ MEDIA_PLAYER2_INVOKE_ID_UNSELECT_TRACK = 5,
+ MEDIA_PLAYER2_INVOKE_ID_SET_VIDEO_SCALING_MODE = 6,
+ MEDIA_PLAYER2_INVOKE_ID_GET_SELECTED_TRACK = 7
+};
+
+}; // namespace android
+
+#endif // ANDROID_MEDIAPLAYER2_TYPES_H
diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
index 0afef1e..11e4f42 100644
--- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
+++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
@@ -17,196 +17,27 @@
#ifndef ANDROID_MEDIAPLAYER2_H
#define ANDROID_MEDIAPLAYER2_H
-#include <media/mediaplayer_common.h>
-
-#include <arpa/inet.h>
-
+#include <media/AVSyncSettings.h>
#include <media/AudioResamplerPublic.h>
#include <media/BufferingSettings.h>
-#include <mediaplayer2/MediaPlayer2EngineClient.h>
-#include <mediaplayer2/MediaPlayer2Engine.h>
+#include <media/Metadata.h>
+#include <media/mediaplayer_common.h>
+#include <mediaplayer2/MediaPlayer2Interface.h>
+#include <mediaplayer2/MediaPlayer2Types.h>
-#include <utils/Condition.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/ThreadDefs.h>
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+#include <system/audio-base.h>
namespace android {
-struct AVSyncSettings;
struct ANativeWindowWrapper;
-class DataSource;
struct DataSourceDesc;
-struct MediaHTTPService;
+class MediaPlayer2AudioOutput;
-enum media2_event_type {
- MEDIA2_NOP = 0, // interface test message
- MEDIA2_PREPARED = 1,
- MEDIA2_PLAYBACK_COMPLETE = 2,
- MEDIA2_BUFFERING_UPDATE = 3,
- MEDIA2_SEEK_COMPLETE = 4,
- MEDIA2_SET_VIDEO_SIZE = 5,
- MEDIA2_STARTED = 6,
- MEDIA2_PAUSED = 7,
- MEDIA2_STOPPED = 8,
- MEDIA2_SKIPPED = 9,
- MEDIA2_NOTIFY_TIME = 98,
- MEDIA2_TIMED_TEXT = 99,
- MEDIA2_ERROR = 100,
- MEDIA2_INFO = 200,
- MEDIA2_SUBTITLE_DATA = 201,
- MEDIA2_META_DATA = 202,
- MEDIA2_DRM_INFO = 210,
- MEDIA2_AUDIO_ROUTING_CHANGED = 10000,
-};
-
-// Generic error codes for the media player framework. Errors are fatal, the
-// playback must abort.
-//
-// Errors are communicated back to the client using the
-// MediaPlayer2Listener::notify method defined below.
-// In this situation, 'notify' is invoked with the following:
-// 'msg' is set to MEDIA_ERROR.
-// 'ext1' should be a value from the enum media2_error_type.
-// 'ext2' contains an implementation dependant error code to provide
-// more details. Should default to 0 when not used.
-//
-// The codes are distributed as follow:
-// 0xx: Reserved
-// 1xx: Android Player errors. Something went wrong inside the MediaPlayer2.
-// 2xx: Media errors (e.g Codec not supported). There is a problem with the
-// media itself.
-// 3xx: Runtime errors. Some extraordinary condition arose making the playback
-// impossible.
-//
-enum media2_error_type {
- // 0xx
- MEDIA2_ERROR_UNKNOWN = 1,
- // 1xx
- // MEDIA2_ERROR_SERVER_DIED = 100,
- // 2xx
- MEDIA2_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200,
- // 3xx
- MEDIA2_ERROR_FAILED_TO_SET_DATA_SOURCE = 300,
-};
-
-
-// Info and warning codes for the media player framework. These are non fatal,
-// the playback is going on but there might be some user visible issues.
-//
-// Info and warning messages are communicated back to the client using the
-// MediaPlayer2Listener::notify method defined below. In this situation,
-// 'notify' is invoked with the following:
-// 'msg' is set to MEDIA_INFO.
-// 'ext1' should be a value from the enum media2_info_type.
-// 'ext2' contains an implementation dependant info code to provide
-// more details. Should default to 0 when not used.
-//
-// The codes are distributed as follow:
-// 0xx: Reserved
-// 7xx: Android Player info/warning (e.g player lagging behind.)
-// 8xx: Media info/warning (e.g media badly interleaved.)
-//
-enum media2_info_type {
- // 0xx
- MEDIA2_INFO_UNKNOWN = 1,
- // The player was started because it was used as the next player for another
- // player, which just completed playback
- MEDIA2_INFO_STARTED_AS_NEXT = 2,
- // The player just pushed the very first video frame for rendering
- MEDIA2_INFO_VIDEO_RENDERING_START = 3,
- // The player just pushed the very first audio frame for rendering
- MEDIA2_INFO_AUDIO_RENDERING_START = 4,
- // The player just completed the playback of this data source
- MEDIA2_INFO_PLAYBACK_COMPLETE = 5,
- // The player just completed the playback of the full play list
- MEDIA2_INFO_PLAYLIST_END = 6,
-
- //1xx
- // The player just prepared a data source.
- MEDIA2_INFO_PREPARED = 100,
- // The player just completed a call play().
- MEDIA2_INFO_COMPLETE_CALL_PLAY = 101,
- // The player just completed a call pause().
- MEDIA2_INFO_COMPLETE_CALL_PAUSE = 102,
- // The player just completed a call seekTo.
- MEDIA2_INFO_COMPLETE_CALL_SEEK = 103,
-
- // 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.
- MEDIA2_INFO_VIDEO_TRACK_LAGGING = 700,
- // MediaPlayer2 is temporarily pausing playback internally in order to
- // buffer more data.
- MEDIA2_INFO_BUFFERING_START = 701,
- // MediaPlayer2 is resuming playback after filling buffers.
- MEDIA2_INFO_BUFFERING_END = 702,
- // Bandwidth in recent past
- MEDIA2_INFO_NETWORK_BANDWIDTH = 703,
-
- // 8xx
- // Bad interleaving means that a media has been improperly interleaved or not
- // interleaved at all, e.g has all the video samples first then all the audio
- // ones. Video is playing but a lot of disk seek may be happening.
- MEDIA2_INFO_BAD_INTERLEAVING = 800,
- // The media is not seekable (e.g live stream).
- MEDIA2_INFO_NOT_SEEKABLE = 801,
- // New media metadata is available.
- MEDIA2_INFO_METADATA_UPDATE = 802,
- // Audio can not be played.
- MEDIA2_INFO_PLAY_AUDIO_ERROR = 804,
- // Video can not be played.
- MEDIA2_INFO_PLAY_VIDEO_ERROR = 805,
-
- //9xx
- MEDIA2_INFO_TIMED_TEXT_ERROR = 900,
-};
-
-
-
-enum media_player2_states {
- MEDIA_PLAYER2_STATE_ERROR = 0,
- MEDIA_PLAYER2_IDLE = 1 << 0,
- MEDIA_PLAYER2_INITIALIZED = 1 << 1,
- MEDIA_PLAYER2_PREPARING = 1 << 2,
- MEDIA_PLAYER2_PREPARED = 1 << 3,
- MEDIA_PLAYER2_STARTED = 1 << 4,
- MEDIA_PLAYER2_PAUSED = 1 << 5,
- MEDIA_PLAYER2_STOPPED = 1 << 6,
- MEDIA_PLAYER2_PLAYBACK_COMPLETE = 1 << 7
-};
-
-// Keep KEY_PARAMETER_* in sync with MediaPlayer2.java.
-// The same enum space is used for both set and get, in case there are future keys that
-// can be both set and get. But as of now, all parameters are either set only or get only.
-enum media2_parameter_keys {
- // Streaming/buffering parameters
- MEDIA2_KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS = 1100, // set only
-
- // Return a Parcel containing a single int, which is the channel count of the
- // audio track, or zero for error (e.g. no audio track) or unknown.
- MEDIA2_KEY_PARAMETER_AUDIO_CHANNEL_COUNT = 1200, // get only
-
- // Playback rate expressed in permille (1000 is normal speed), saved as int32_t, with negative
- // values used for rewinding or reverse playback.
- MEDIA2_KEY_PARAMETER_PLAYBACK_RATE_PERMILLE = 1300, // set only
-
- // Set a Parcel containing the value of a parcelled Java AudioAttribute instance
- MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400 // set only
-};
-
-// Keep INVOKE_ID_* in sync with MediaPlayer2.java.
-enum media_player2_invoke_ids {
- MEDIA_PLAYER2_INVOKE_ID_GET_TRACK_INFO = 1,
- MEDIA_PLAYER2_INVOKE_ID_ADD_EXTERNAL_SOURCE = 2,
- MEDIA_PLAYER2_INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3,
- MEDIA_PLAYER2_INVOKE_ID_SELECT_TRACK = 4,
- MEDIA_PLAYER2_INVOKE_ID_UNSELECT_TRACK = 5,
- MEDIA_PLAYER2_INVOKE_ID_SET_VIDEO_SCALING_MODE = 6,
- MEDIA_PLAYER2_INVOKE_ID_GET_SELECTED_TRACK = 7
-};
-
-// ----------------------------------------------------------------------------
// ref-counted object for callbacks
class MediaPlayer2Listener: virtual public RefBase
{
@@ -214,11 +45,14 @@
virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
};
-class MediaPlayer2 : public MediaPlayer2EngineClient
+class MediaPlayer2 : public MediaPlayer2InterfaceListener
{
public:
- MediaPlayer2();
~MediaPlayer2();
+
+ static sp<MediaPlayer2> Create();
+ static status_t DumpAll(int fd, const Vector<String16>& args);
+
void disconnect();
status_t getSrcId(int64_t *srcId);
@@ -275,22 +109,43 @@
audio_port_handle_t getRoutedDeviceId();
status_t enableAudioDeviceCallback(bool enabled);
-private:
- void clear_l();
- status_t seekTo_l(int msec, MediaPlayer2SeekMode mode);
- status_t prepareAsync_l();
- status_t getDuration_l(int *msec);
- status_t attachNewPlayer(const sp<MediaPlayer2Engine>& player, long srcId);
- status_t reset_l();
- status_t checkStateForKeySet_l(int key);
+ status_t dump(int fd, const Vector<String16>& args);
- sp<MediaPlayer2Engine> mPlayer;
+private:
+ MediaPlayer2();
+ bool init();
+
+ // @param type Of the metadata to be tested.
+ // @return true if the metadata should be dropped according to
+ // the filters.
+ bool shouldDropMetadata(media::Metadata::Type type) const;
+
+ // Add a new element to the set of metadata updated. Noop if
+ // the element exists already.
+ // @param type Of the metadata to be recorded.
+ void addNewMetadataUpdate(media::Metadata::Type type);
+
+ // Disconnect from the currently connected ANativeWindow.
+ void disconnectNativeWindow_l();
+
+ status_t setAudioAttributes_l(const Parcel &request);
+
+ void clear_l();
+ status_t seekTo_l(int msec, MediaPlayer2SeekMode mode);
+ status_t prepareAsync_l();
+ status_t getDuration_l(int *msec);
+ status_t reset_l();
+ status_t checkStateForKeySet_l(int key);
+
+ pid_t mPid;
+ uid_t mUid;
+ sp<MediaPlayer2Interface> mPlayer;
+ sp<MediaPlayer2AudioOutput> mAudioOutput;
int64_t mSrcId;
thread_id_t mLockThreadId;
- Mutex mLock;
+ mutable Mutex mLock;
Mutex mNotifyLock;
sp<MediaPlayer2Listener> mListener;
- void* mCookie;
media_player2_states mCurrentState;
int mCurrentPosition;
MediaPlayer2SeekMode mCurrentSeekMode;
@@ -304,7 +159,20 @@
int mVideoWidth;
int mVideoHeight;
audio_session_t mAudioSessionId;
+ audio_attributes_t * mAudioAttributes;
float mSendLevel;
+
+ sp<ANativeWindowWrapper> mConnectedWindow;
+
+ // Metadata filters.
+ media::Metadata::Filter mMetadataAllow; // protected by mLock
+ media::Metadata::Filter mMetadataDrop; // protected by mLock
+
+ // Metadata updated. For each MEDIA_INFO_METADATA_UPDATE
+ // notification we try to update mMetadataUpdated which is a
+ // set: no duplicate.
+ // getMetadata clears this set.
+ media::Metadata::Filter mMetadataUpdated; // protected by mLock
};
}; // namespace android
diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp
index 75d06b8..b401ee8 100644
--- a/media/libmediaplayer2/mediaplayer2.cpp
+++ b/media/libmediaplayer2/mediaplayer2.cpp
@@ -18,44 +18,391 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaPlayer2Native"
-#include <fcntl.h>
-#include <inttypes.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <utils/Log.h>
-
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
-#include <media/AudioResamplerPublic.h>
#include <media/AudioSystem.h>
-#include <media/AVSyncSettings.h>
-#include <media/DataSource.h>
#include <media/DataSourceDesc.h>
#include <media/MediaAnalyticsItem.h>
+#include <media/MemoryLeakTrackUtil.h>
+#include <media/Metadata.h>
#include <media/NdkWrapper.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooperRoster.h>
+#include <mediaplayer2/MediaPlayer2AudioOutput.h>
#include <mediaplayer2/mediaplayer2.h>
-#include <binder/MemoryBase.h>
-
-#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+#include <utils/SortedVector.h>
#include <utils/String8.h>
#include <system/audio.h>
#include <system/window.h>
-#include "MediaPlayer2Manager.h"
+#include <nuplayer2/NuPlayer2Driver.h>
+
+#include <dirent.h>
+#include <sys/stat.h>
namespace android {
-MediaPlayer2::MediaPlayer2()
-{
+extern ALooperRoster gLooperRoster;
+
+namespace {
+
+const int kDumpLockRetries = 50;
+const int kDumpLockSleepUs = 20000;
+
+// Max number of entries in the filter.
+const int kMaxFilterSize = 64; // I pulled that out of thin air.
+
+// FIXME: Move all the metadata related function in the Metadata.cpp
+
+// Unmarshall a filter from a Parcel.
+// Filter format in a parcel:
+//
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | number of entries (n) |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | metadata type 1 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | metadata type 2 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// ....
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | metadata type n |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// @param p Parcel that should start with a filter.
+// @param[out] filter On exit contains the list of metadata type to be
+// filtered.
+// @param[out] status On exit contains the status code to be returned.
+// @return true if the parcel starts with a valid filter.
+bool unmarshallFilter(const Parcel& p,
+ media::Metadata::Filter *filter,
+ status_t *status) {
+ int32_t val;
+ if (p.readInt32(&val) != OK) {
+ ALOGE("Failed to read filter's length");
+ *status = NOT_ENOUGH_DATA;
+ return false;
+ }
+
+ if (val > kMaxFilterSize || val < 0) {
+ ALOGE("Invalid filter len %d", val);
+ *status = BAD_VALUE;
+ return false;
+ }
+
+ const size_t num = val;
+
+ filter->clear();
+ filter->setCapacity(num);
+
+ size_t size = num * sizeof(media::Metadata::Type);
+
+
+ if (p.dataAvail() < size) {
+ ALOGE("Filter too short expected %zu but got %zu", size, p.dataAvail());
+ *status = NOT_ENOUGH_DATA;
+ return false;
+ }
+
+ const media::Metadata::Type *data =
+ static_cast<const media::Metadata::Type*>(p.readInplace(size));
+
+ if (NULL == data) {
+ ALOGE("Filter had no data");
+ *status = BAD_VALUE;
+ return false;
+ }
+
+ // TODO: The stl impl of vector would be more efficient here
+ // because it degenerates into a memcpy on pod types. Try to
+ // replace later or use stl::set.
+ for (size_t i = 0; i < num; ++i) {
+ filter->add(*data);
+ ++data;
+ }
+ *status = OK;
+ return true;
+}
+
+// @param filter Of metadata type.
+// @param val To be searched.
+// @return true if a match was found.
+bool findMetadata(const media::Metadata::Filter& filter, const int32_t val) {
+ // Deal with empty and ANY right away
+ if (filter.isEmpty()) {
+ return false;
+ }
+ if (filter[0] == media::Metadata::kAny) {
+ return true;
+ }
+
+ return filter.indexOf(val) >= 0;
+}
+
+// marshalling tag indicating flattened utf16 tags
+// keep in sync with frameworks/base/media/java/android/media/AudioAttributes.java
+const int32_t kAudioAttributesMarshallTagFlattenTags = 1;
+
+// Audio attributes format in a parcel:
+//
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | usage |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | content_type |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | source |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | flags |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | kAudioAttributesMarshallTagFlattenTags | // ignore tags if not found
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | flattened tags in UTF16 |
+// | ... |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// @param p Parcel that contains audio attributes.
+// @param[out] attributes On exit points to an initialized audio_attributes_t structure
+// @param[out] status On exit contains the status code to be returned.
+void unmarshallAudioAttributes(const Parcel& parcel, audio_attributes_t *attributes) {
+ attributes->usage = (audio_usage_t) parcel.readInt32();
+ attributes->content_type = (audio_content_type_t) parcel.readInt32();
+ attributes->source = (audio_source_t) parcel.readInt32();
+ attributes->flags = (audio_flags_mask_t) parcel.readInt32();
+ const bool hasFlattenedTag = (parcel.readInt32() == kAudioAttributesMarshallTagFlattenTags);
+ if (hasFlattenedTag) {
+ // the tags are UTF16, convert to UTF8
+ String16 tags = parcel.readString16();
+ ssize_t realTagSize = utf16_to_utf8_length(tags.string(), tags.size());
+ if (realTagSize <= 0) {
+ strcpy(attributes->tags, "");
+ } else {
+ // copy the flattened string into the attributes as the destination for the conversion:
+ // copying array size -1, array for tags was calloc'd, no need to NULL-terminate it
+ size_t tagSize = realTagSize > AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 ?
+ AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 : realTagSize;
+ utf16_to_utf8(tags.string(), tagSize, attributes->tags,
+ sizeof(attributes->tags) / sizeof(attributes->tags[0]));
+ }
+ } else {
+ ALOGE("unmarshallAudioAttributes() received unflattened tags, ignoring tag values");
+ strcpy(attributes->tags, "");
+ }
+}
+
+class AudioDeviceUpdatedNotifier: public AudioSystem::AudioDeviceCallback {
+public:
+ AudioDeviceUpdatedNotifier(const sp<MediaPlayer2Interface>& listener)
+ : mListener(listener) { }
+
+ ~AudioDeviceUpdatedNotifier() { }
+
+ virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
+ audio_port_handle_t deviceId) override {
+ sp<MediaPlayer2Interface> listener = mListener.promote();
+ if (listener != NULL) {
+ listener->sendEvent(0, MEDIA2_AUDIO_ROUTING_CHANGED, audioIo, deviceId);
+ } else {
+ ALOGW("listener for process %d death is gone", MEDIA2_AUDIO_ROUTING_CHANGED);
+ }
+ }
+
+private:
+ wp<MediaPlayer2Interface> mListener;
+};
+
+class proxyListener : public MediaPlayer2InterfaceListener {
+public:
+ proxyListener(const wp<MediaPlayer2> &player)
+ : mPlayer(player) { }
+
+ ~proxyListener() { };
+
+ virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) override {
+ sp<MediaPlayer2> player = mPlayer.promote();
+ if (player != NULL) {
+ player->notify(srcId, msg, ext1, ext2, obj);
+ }
+ }
+
+private:
+ wp<MediaPlayer2> mPlayer;
+};
+
+Mutex sRecordLock;
+SortedVector<wp<MediaPlayer2> > *sPlayers;
+
+void ensureInit_l() {
+ if (sPlayers == NULL) {
+ sPlayers = new SortedVector<wp<MediaPlayer2> >();
+ }
+}
+
+void addPlayer(const wp<MediaPlayer2>& player) {
+ Mutex::Autolock lock(sRecordLock);
+ ensureInit_l();
+ sPlayers->add(player);
+}
+
+void removePlayer(const wp<MediaPlayer2>& player) {
+ Mutex::Autolock lock(sRecordLock);
+ ensureInit_l();
+ sPlayers->remove(player);
+}
+
+/**
+ * The only arguments this understands right now are -c, -von and -voff,
+ * which are parsed by ALooperRoster::dump()
+ */
+status_t dumpPlayers(int fd, const Vector<String16>& args) {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ SortedVector< sp<MediaPlayer2> > players; //to serialise the mutex unlock & client destruction.
+
+ if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ snprintf(buffer, SIZE, "Permission Denial: can't dump MediaPlayer2\n");
+ result.append(buffer);
+ } else {
+ {
+ Mutex::Autolock lock(sRecordLock);
+ ensureInit_l();
+ for (int i = 0, n = sPlayers->size(); i < n; ++i) {
+ sp<MediaPlayer2> p = (*sPlayers)[i].promote();
+ if (p != 0) {
+ p->dump(fd, args);
+ }
+ players.add(p);
+ }
+ }
+
+ result.append(" Files opened and/or mapped:\n");
+ snprintf(buffer, SIZE, "/proc/%d/maps", getpid());
+ FILE *f = fopen(buffer, "r");
+ if (f) {
+ while (!feof(f)) {
+ fgets(buffer, SIZE, f);
+ if (strstr(buffer, " /storage/") ||
+ strstr(buffer, " /system/sounds/") ||
+ strstr(buffer, " /data/") ||
+ strstr(buffer, " /system/media/")) {
+ result.append(" ");
+ result.append(buffer);
+ }
+ }
+ fclose(f);
+ } else {
+ result.append("couldn't open ");
+ result.append(buffer);
+ result.append("\n");
+ }
+
+ snprintf(buffer, SIZE, "/proc/%d/fd", getpid());
+ DIR *d = opendir(buffer);
+ if (d) {
+ struct dirent *ent;
+ while((ent = readdir(d)) != NULL) {
+ if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) {
+ snprintf(buffer, SIZE, "/proc/%d/fd/%s", getpid(), ent->d_name);
+ struct stat s;
+ if (lstat(buffer, &s) == 0) {
+ if ((s.st_mode & S_IFMT) == S_IFLNK) {
+ char linkto[256];
+ int len = readlink(buffer, linkto, sizeof(linkto));
+ if(len > 0) {
+ if(len > 255) {
+ linkto[252] = '.';
+ linkto[253] = '.';
+ linkto[254] = '.';
+ linkto[255] = 0;
+ } else {
+ linkto[len] = 0;
+ }
+ if (strstr(linkto, "/storage/") == linkto ||
+ strstr(linkto, "/system/sounds/") == linkto ||
+ strstr(linkto, "/data/") == linkto ||
+ strstr(linkto, "/system/media/") == linkto) {
+ result.append(" ");
+ result.append(buffer);
+ result.append(" -> ");
+ result.append(linkto);
+ result.append("\n");
+ }
+ }
+ } else {
+ result.append(" unexpected type for ");
+ result.append(buffer);
+ result.append("\n");
+ }
+ }
+ }
+ }
+ closedir(d);
+ } else {
+ result.append("couldn't open ");
+ result.append(buffer);
+ result.append("\n");
+ }
+
+ gLooperRoster.dump(fd, args);
+
+ bool dumpMem = false;
+ bool unreachableMemory = false;
+ for (size_t i = 0; i < args.size(); i++) {
+ if (args[i] == String16("-m")) {
+ dumpMem = true;
+ } else if (args[i] == String16("--unreachable")) {
+ unreachableMemory = true;
+ }
+ }
+ if (dumpMem) {
+ result.append("\nDumping memory:\n");
+ std::string s = dumpMemoryAddresses(100 /* limit */);
+ result.append(s.c_str(), s.size());
+ }
+ if (unreachableMemory) {
+ result.append("\nDumping unreachable memory:\n");
+ // TODO - should limit be an argument parameter?
+ // TODO: enable GetUnreachableMemoryString if it's part of stable API
+ //std::string s = GetUnreachableMemoryString(true /* contents */, 10000 /* limit */);
+ //result.append(s.c_str(), s.size());
+ }
+ }
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+} // anonymous namespace
+
+//static
+sp<MediaPlayer2> MediaPlayer2::Create() {
+ sp<MediaPlayer2> player = new MediaPlayer2();
+
+ if (!player->init()) {
+ return NULL;
+ }
+
+ ALOGV("Create new player(%p)", player.get());
+
+ addPlayer(player);
+ return player;
+}
+
+// static
+status_t MediaPlayer2::DumpAll(int fd, const Vector<String16>& args) {
+ return dumpPlayers(fd, args);
+}
+
+MediaPlayer2::MediaPlayer2() {
ALOGV("constructor");
mSrcId = 0;
+ mLockThreadId = 0;
mListener = NULL;
- mCookie = NULL;
mStreamType = AUDIO_STREAM_MUSIC;
mAudioAttributesParcel = NULL;
mCurrentPosition = -1;
@@ -66,14 +413,18 @@
mLoop = false;
mLeftVolume = mRightVolume = 1.0;
mVideoWidth = mVideoHeight = 0;
- mLockThreadId = 0;
mAudioSessionId = (audio_session_t) AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);
mSendLevel = 0;
+
+ // TODO: get pid and uid from JAVA
+ mPid = IPCThreadState::self()->getCallingPid();
+ mUid = IPCThreadState::self()->getCallingUid();
+
+ mAudioAttributes = NULL;
}
-MediaPlayer2::~MediaPlayer2()
-{
+MediaPlayer2::~MediaPlayer2() {
ALOGV("destructor");
if (mAudioAttributesParcel != NULL) {
delete mAudioAttributesParcel;
@@ -81,13 +432,21 @@
}
AudioSystem::releaseAudioSessionId(mAudioSessionId, -1);
disconnect();
- IPCThreadState::self()->flushCommands();
+ removePlayer(this);
+ if (mAudioAttributes != NULL) {
+ free(mAudioAttributes);
+ }
}
-void MediaPlayer2::disconnect()
-{
+bool MediaPlayer2::init() {
+ // TODO: after merge with NuPlayer2Driver, MediaPlayer2 will have its own
+ // looper for notification.
+ return true;
+}
+
+void MediaPlayer2::disconnect() {
ALOGV("disconnect");
- sp<MediaPlayer2Engine> p;
+ sp<MediaPlayer2Interface> p;
{
Mutex::Autolock _l(mLock);
p = mPlayer;
@@ -95,13 +454,17 @@
}
if (p != 0) {
- p->disconnect();
+ p->setListener(NULL);
+ p->reset();
+ }
+
+ {
+ Mutex::Autolock _l(mLock);
+ disconnectNativeWindow_l();
}
}
-// always call with lock held
-void MediaPlayer2::clear_l()
-{
+void MediaPlayer2::clear_l() {
mCurrentPosition = -1;
mCurrentSeekMode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC;
mSeekPosition = -1;
@@ -109,8 +472,7 @@
mVideoWidth = mVideoHeight = 0;
}
-status_t MediaPlayer2::setListener(const sp<MediaPlayer2Listener>& listener)
-{
+status_t MediaPlayer2::setListener(const sp<MediaPlayer2Listener>& listener) {
ALOGV("setListener");
Mutex::Autolock _l(mLock);
mListener = listener;
@@ -127,51 +489,54 @@
return OK;
}
-status_t MediaPlayer2::attachNewPlayer(const sp<MediaPlayer2Engine>& player, long srcId)
-{
- status_t err = UNKNOWN_ERROR;
- sp<MediaPlayer2Engine> p;
- { // scope for the lock
- Mutex::Autolock _l(mLock);
-
- if ( !( (mCurrentState & MEDIA_PLAYER2_IDLE) ||
- (mCurrentState == MEDIA_PLAYER2_STATE_ERROR ) ) ) {
- ALOGE("attachNewPlayer called in state %d", mCurrentState);
- return INVALID_OPERATION;
- }
-
- clear_l();
- p = mPlayer;
- mPlayer = player;
- mSrcId = srcId;
- if (player != 0) {
- mCurrentState = MEDIA_PLAYER2_INITIALIZED;
- err = NO_ERROR;
- } else {
- ALOGE("Unable to create media player");
- }
- }
-
- if (p != 0) {
- p->disconnect();
- }
-
- return err;
-}
-
-status_t MediaPlayer2::setDataSource(const sp<DataSourceDesc> &dsd)
-{
+status_t MediaPlayer2::setDataSource(const sp<DataSourceDesc> &dsd) {
if (dsd == NULL) {
return BAD_VALUE;
}
- ALOGV("setDataSource type(%d)", dsd->mType);
- status_t err = UNKNOWN_ERROR;
- sp<MediaPlayer2Engine> player(MediaPlayer2Manager::get().create(this, mAudioSessionId));
- if (NO_ERROR != player->setDataSource(dsd)) {
- player.clear();
+ ALOGV("setDataSource type(%d), srcId(%lld)", dsd->mType, (long long)dsd->mId);
+
+ sp<MediaPlayer2Interface> oldPlayer;
+
+ Mutex::Autolock _l(mLock);
+ {
+ if (!((mCurrentState & MEDIA_PLAYER2_IDLE)
+ || mCurrentState == MEDIA_PLAYER2_STATE_ERROR)) {
+ ALOGE("setDataSource called in wrong state %d", mCurrentState);
+ return INVALID_OPERATION;
+ }
+
+ sp<MediaPlayer2Interface> player = new NuPlayer2Driver(mPid, mUid);
+ status_t err = player->initCheck();
+ if (err != NO_ERROR) {
+ ALOGE("Failed to create player object, initCheck failed(%d)", err);
+ return err;
+ }
+
+ clear_l();
+
+ player->setListener(new proxyListener(this));
+ mAudioOutput = new MediaPlayer2AudioOutput(mAudioSessionId, mUid,
+ mPid, mAudioAttributes, new AudioDeviceUpdatedNotifier(player));
+ player->setAudioSink(mAudioOutput);
+
+ err = player->setDataSource(dsd);
+ if (err != OK) {
+ ALOGE("setDataSource error: %d", err);
+ return err;
+ }
+
+ sp<MediaPlayer2Interface> oldPlayer = mPlayer;
+ mPlayer = player;
+ mSrcId = dsd->mId;
+ mCurrentState = MEDIA_PLAYER2_INITIALIZED;
}
- err = attachNewPlayer(player, dsd->mId);
- return err;
+
+ if (oldPlayer != NULL) {
+ oldPlayer->setListener(NULL);
+ oldPlayer->reset();
+ }
+
+ return OK;
}
status_t MediaPlayer2::prepareNextDataSource(const sp<DataSourceDesc> &dsd) {
@@ -181,81 +546,179 @@
ALOGV("prepareNextDataSource type(%d), srcId(%lld)", dsd->mType, (long long)dsd->mId);
Mutex::Autolock _l(mLock);
- if (mPlayer != NULL) {
- return mPlayer->prepareNextDataSource(dsd);
+ if (mPlayer == NULL) {
+ ALOGE("prepareNextDataSource failed: state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
+ return INVALID_OPERATION;
}
- ALOGE("prepareNextDataSource failed: state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
- return INVALID_OPERATION;
+ return mPlayer->prepareNextDataSource(dsd);
}
status_t MediaPlayer2::playNextDataSource(int64_t srcId) {
ALOGV("playNextDataSource srcId(%lld)", (long long)srcId);
Mutex::Autolock _l(mLock);
- if (mPlayer != NULL) {
- mSrcId = srcId;
- return mPlayer->playNextDataSource(srcId);
+ if (mPlayer == NULL) {
+ ALOGE("playNextDataSource failed: state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
+ return INVALID_OPERATION;
}
- ALOGE("playNextDataSource failed: state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
- return INVALID_OPERATION;
+ mSrcId = srcId;
+ return mPlayer->playNextDataSource(srcId);
}
-status_t MediaPlayer2::invoke(const Parcel& request, Parcel *reply)
-{
+status_t MediaPlayer2::invoke(const Parcel& request, Parcel *reply) {
Mutex::Autolock _l(mLock);
const bool hasBeenInitialized =
(mCurrentState != MEDIA_PLAYER2_STATE_ERROR) &&
((mCurrentState & MEDIA_PLAYER2_IDLE) != MEDIA_PLAYER2_IDLE);
- if ((mPlayer != NULL) && hasBeenInitialized) {
- ALOGV("invoke %zu", request.dataSize());
- return mPlayer->invoke(request, reply);
+ if ((mPlayer == NULL) || !hasBeenInitialized) {
+ ALOGE("invoke failed: wrong state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
+ return INVALID_OPERATION;
}
- ALOGE("invoke failed: wrong state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
- return INVALID_OPERATION;
+ ALOGV("invoke %zu", request.dataSize());
+ return mPlayer->invoke(request, reply);
}
-status_t MediaPlayer2::setMetadataFilter(const Parcel& filter)
-{
+// This call doesn't need to access the native player.
+status_t MediaPlayer2::setMetadataFilter(const Parcel& filter) {
ALOGD("setMetadataFilter");
- Mutex::Autolock lock(mLock);
- if (mPlayer == NULL) {
- return NO_INIT;
+
+ status_t status;
+ media::Metadata::Filter allow, drop;
+
+ if (unmarshallFilter(filter, &allow, &status) &&
+ unmarshallFilter(filter, &drop, &status)) {
+ Mutex::Autolock lock(mLock);
+
+ mMetadataAllow = allow;
+ mMetadataDrop = drop;
}
- return mPlayer->setMetadataFilter(filter);
+ return status;
}
-status_t MediaPlayer2::getMetadata(bool update_only, bool apply_filter, Parcel *metadata)
-{
+status_t MediaPlayer2::getMetadata(bool update_only, bool /* apply_filter */, Parcel *reply) {
ALOGD("getMetadata");
+ sp<MediaPlayer2Interface> player;
+ media::Metadata::Filter ids;
Mutex::Autolock lock(mLock);
- if (mPlayer == NULL) {
+ {
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ player = mPlayer;
+ // Placeholder for the return code, updated by the caller.
+ reply->writeInt32(-1);
+
+ // We don't block notifications while we fetch the data. We clear
+ // mMetadataUpdated first so we don't lose notifications happening
+ // during the rest of this call.
+ if (update_only) {
+ ids = mMetadataUpdated;
+ }
+ mMetadataUpdated.clear();
+ }
+
+ media::Metadata metadata(reply);
+
+ metadata.appendHeader();
+ status_t status = player->getMetadata(ids, reply);
+
+ if (status != OK) {
+ metadata.resetParcel();
+ ALOGE("getMetadata failed %d", status);
+ return status;
+ }
+
+ // FIXME: ement filtering on the result. Not critical since
+ // filtering takes place on the update notifications already. This
+ // would be when all the metadata are fetch and a filter is set.
+
+ // Everything is fine, update the metadata length.
+ metadata.updateLength();
+ return OK;
+}
+
+void MediaPlayer2::disconnectNativeWindow_l() {
+ if (mConnectedWindow != NULL && mConnectedWindow->getANativeWindow() != NULL) {
+ status_t err = native_window_api_disconnect(
+ mConnectedWindow->getANativeWindow(), NATIVE_WINDOW_API_MEDIA);
+
+ if (err != OK) {
+ ALOGW("nativeWindowDisconnect returned an error: %s (%d)",
+ strerror(-err), err);
+ }
+ }
+ mConnectedWindow.clear();
+}
+
+status_t MediaPlayer2::setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww) {
+ ANativeWindow *anw = (nww == NULL ? NULL : nww->getANativeWindow());
+ ALOGV("setVideoSurfaceTexture(%p)", anw);
+ Mutex::Autolock _l(mLock);
+ if (mPlayer == 0) {
return NO_INIT;
}
- return mPlayer->getMetadata(update_only, apply_filter, metadata);
+
+ if (anw != NULL) {
+ if (mConnectedWindow != NULL
+ && mConnectedWindow->getANativeWindow() == anw) {
+ return OK;
+ }
+ status_t err = native_window_api_connect(anw, NATIVE_WINDOW_API_MEDIA);
+
+ if (err != OK) {
+ ALOGE("setVideoSurfaceTexture failed: %d", err);
+ // Note that we must do the reset before disconnecting from the ANW.
+ // Otherwise queue/dequeue calls could be made on the disconnected
+ // ANW, which may result in errors.
+ mPlayer->reset();
+ disconnectNativeWindow_l();
+ return err;
+ }
+ }
+
+ // Note that we must set the player's new GraphicBufferProducer before
+ // disconnecting the old one. Otherwise queue/dequeue calls could be made
+ // on the disconnected ANW, which may result in errors.
+ status_t err = mPlayer->setVideoSurfaceTexture(nww);
+
+ disconnectNativeWindow_l();
+
+ if (err == OK) {
+ mConnectedWindow = nww;
+ mLock.unlock();
+ } else if (anw != NULL) {
+ mLock.unlock();
+ status_t err = native_window_api_disconnect(anw, NATIVE_WINDOW_API_MEDIA);
+
+ if (err != OK) {
+ ALOGW("nativeWindowDisconnect returned an error: %s (%d)",
+ strerror(-err), err);
+ }
+ }
+
+ return err;
}
-status_t MediaPlayer2::setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww)
-{
- ALOGV("setVideoSurfaceTexture");
- Mutex::Autolock _l(mLock);
- if (mPlayer == 0) return NO_INIT;
- return mPlayer->setVideoSurfaceTexture(nww);
-}
-
-status_t MediaPlayer2::getBufferingSettings(BufferingSettings* buffering /* nonnull */)
-{
+status_t MediaPlayer2::getBufferingSettings(BufferingSettings* buffering /* nonnull */) {
ALOGV("getBufferingSettings");
Mutex::Autolock _l(mLock);
if (mPlayer == 0) {
return NO_INIT;
}
- return mPlayer->getBufferingSettings(buffering);
+
+ status_t ret = mPlayer->getBufferingSettings(buffering);
+ if (ret == NO_ERROR) {
+ ALOGV("getBufferingSettings{%s}", buffering->toString().string());
+ } else {
+ ALOGE("getBufferingSettings returned %d", ret);
+ }
+ return ret;
}
-status_t MediaPlayer2::setBufferingSettings(const BufferingSettings& buffering)
-{
- ALOGV("setBufferingSettings");
+status_t MediaPlayer2::setBufferingSettings(const BufferingSettings& buffering) {
+ ALOGV("setBufferingSettings{%s}", buffering.toString().string());
Mutex::Autolock _l(mLock);
if (mPlayer == 0) {
@@ -264,14 +727,37 @@
return mPlayer->setBufferingSettings(buffering);
}
-// must call with lock held
-status_t MediaPlayer2::prepareAsync_l()
-{
- if ( (mPlayer != 0) && ( mCurrentState & (MEDIA_PLAYER2_INITIALIZED | MEDIA_PLAYER2_STOPPED) ) ) {
+status_t MediaPlayer2::setAudioAttributes_l(const Parcel &parcel) {
+ if (mAudioAttributes != NULL) {
+ free(mAudioAttributes);
+ }
+ mAudioAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
+ if (mAudioAttributes == NULL) {
+ return NO_MEMORY;
+ }
+ unmarshallAudioAttributes(parcel, mAudioAttributes);
+
+ ALOGV("setAudioAttributes_l() usage=%d content=%d flags=0x%x tags=%s",
+ mAudioAttributes->usage, mAudioAttributes->content_type, mAudioAttributes->flags,
+ mAudioAttributes->tags);
+
+ if (mAudioOutput != 0) {
+ mAudioOutput->setAudioAttributes(mAudioAttributes);
+ }
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2::prepareAsync() {
+ ALOGV("prepareAsync");
+ Mutex::Autolock _l(mLock);
+ if ((mPlayer != 0) && (mCurrentState & (MEDIA_PLAYER2_INITIALIZED | MEDIA_PLAYER2_STOPPED))) {
if (mAudioAttributesParcel != NULL) {
- mPlayer->setParameter(MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES, *mAudioAttributesParcel);
- } else {
- mPlayer->setAudioStreamType(mStreamType);
+ status_t err = setAudioAttributes_l(*mAudioAttributesParcel);
+ if (err != OK) {
+ return err;
+ }
+ } else if (mAudioOutput != 0) {
+ mAudioOutput->setAudioStreamType(mStreamType);
}
mCurrentState = MEDIA_PLAYER2_PREPARING;
return mPlayer->prepareAsync();
@@ -280,15 +766,7 @@
return INVALID_OPERATION;
}
-status_t MediaPlayer2::prepareAsync()
-{
- ALOGV("prepareAsync");
- Mutex::Autolock _l(mLock);
- return prepareAsync_l();
-}
-
-status_t MediaPlayer2::start()
-{
+status_t MediaPlayer2::start() {
ALOGV("start");
status_t ret = NO_ERROR;
@@ -301,8 +779,14 @@
} else if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER2_PREPARED |
MEDIA_PLAYER2_PLAYBACK_COMPLETE | MEDIA_PLAYER2_PAUSED ) ) ) {
mPlayer->setLooping(mLoop);
- mPlayer->setVolume(mLeftVolume, mRightVolume);
- mPlayer->setAuxEffectSendLevel(mSendLevel);
+
+ if (mAudioOutput != 0) {
+ mAudioOutput->setVolume(mLeftVolume, mRightVolume);
+ }
+
+ if (mAudioOutput != 0) {
+ mAudioOutput->setAuxEffectSendLevel(mSendLevel);
+ }
mCurrentState = MEDIA_PLAYER2_STARTED;
ret = mPlayer->start();
if (ret != NO_ERROR) {
@@ -322,8 +806,7 @@
return ret;
}
-status_t MediaPlayer2::stop()
-{
+status_t MediaPlayer2::stop() {
ALOGV("stop");
Mutex::Autolock _l(mLock);
if (mCurrentState & MEDIA_PLAYER2_STOPPED) return NO_ERROR;
@@ -341,8 +824,7 @@
return INVALID_OPERATION;
}
-status_t MediaPlayer2::pause()
-{
+status_t MediaPlayer2::pause() {
ALOGV("pause");
Mutex::Autolock _l(mLock);
if (mCurrentState & (MEDIA_PLAYER2_PAUSED|MEDIA_PLAYER2_PLAYBACK_COMPLETE))
@@ -360,12 +842,10 @@
return INVALID_OPERATION;
}
-bool MediaPlayer2::isPlaying()
-{
+bool MediaPlayer2::isPlaying() {
Mutex::Autolock _l(mLock);
if (mPlayer != 0) {
- bool temp = false;
- mPlayer->isPlaying(&temp);
+ bool temp = mPlayer->isPlaying();
ALOGV("isPlaying: %d", temp);
if ((mCurrentState & MEDIA_PLAYER2_STARTED) && ! temp) {
ALOGE("internal/external state mismatch corrected");
@@ -380,13 +860,12 @@
return false;
}
-status_t MediaPlayer2::setPlaybackSettings(const AudioPlaybackRate& rate)
-{
+status_t MediaPlayer2::setPlaybackSettings(const AudioPlaybackRate& rate) {
ALOGV("setPlaybackSettings: %f %f %d %d",
rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
// Negative speed and pitch does not make sense. Further validation will
// be done by the respective mediaplayers.
- if (rate.mSpeed < 0.f || rate.mPitch < 0.f) {
+ if (rate.mSpeed <= 0.f || rate.mPitch < 0.f) {
return BAD_VALUE;
}
Mutex::Autolock _l(mLock);
@@ -394,36 +873,27 @@
return INVALID_OPERATION;
}
- if (rate.mSpeed != 0.f && !(mCurrentState & MEDIA_PLAYER2_STARTED)
- && (mCurrentState & (MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_PAUSED
- | MEDIA_PLAYER2_PLAYBACK_COMPLETE))) {
- mPlayer->setLooping(mLoop);
- mPlayer->setVolume(mLeftVolume, mRightVolume);
- mPlayer->setAuxEffectSendLevel(mSendLevel);
- }
-
status_t err = mPlayer->setPlaybackSettings(rate);
- if (err == OK) {
- if (rate.mSpeed == 0.f && mCurrentState == MEDIA_PLAYER2_STARTED) {
- mCurrentState = MEDIA_PLAYER2_PAUSED;
- } else if (rate.mSpeed != 0.f
- && (mCurrentState & (MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_PAUSED
- | MEDIA_PLAYER2_PLAYBACK_COMPLETE))) {
- mCurrentState = MEDIA_PLAYER2_STARTED;
- }
- }
return err;
}
-status_t MediaPlayer2::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */)
-{
+status_t MediaPlayer2::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) {
Mutex::Autolock _l(mLock);
- if (mPlayer == 0) return INVALID_OPERATION;
+ if (mPlayer == 0) {
+ return INVALID_OPERATION;
+ }
return mPlayer->getPlaybackSettings(rate);
+ status_t ret = mPlayer->getPlaybackSettings(rate);
+ if (ret == NO_ERROR) {
+ ALOGV("getPlaybackSettings(%f, %f, %d, %d)",
+ rate->mSpeed, rate->mPitch, rate->mFallbackMode, rate->mStretchMode);
+ } else {
+ ALOGV("getPlaybackSettings returned %d", ret);
+ }
+ return ret;
}
-status_t MediaPlayer2::setSyncSettings(const AVSyncSettings& sync, float videoFpsHint)
-{
+status_t MediaPlayer2::setSyncSettings(const AVSyncSettings& sync, float videoFpsHint) {
ALOGV("setSyncSettings: %u %u %f %f",
sync.mSource, sync.mAudioAdjustMode, sync.mTolerance, videoFpsHint);
Mutex::Autolock _l(mLock);
@@ -432,83 +902,92 @@
}
status_t MediaPlayer2::getSyncSettings(
- AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */)
-{
+ AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */) {
Mutex::Autolock _l(mLock);
if (mPlayer == 0) return INVALID_OPERATION;
- return mPlayer->getSyncSettings(sync, videoFps);
+ status_t ret = mPlayer->getSyncSettings(sync, videoFps);
+ if (ret == NO_ERROR) {
+ ALOGV("getSyncSettings(%u, %u, %f, %f)",
+ sync->mSource, sync->mAudioAdjustMode, sync->mTolerance, *videoFps);
+ } else {
+ ALOGV("getSyncSettings returned %d", ret);
+ }
+ return ret;
+
}
-status_t MediaPlayer2::getVideoWidth(int *w)
-{
+status_t MediaPlayer2::getVideoWidth(int *w) {
ALOGV("getVideoWidth");
Mutex::Autolock _l(mLock);
- if (mPlayer == 0) return INVALID_OPERATION;
+ if (mPlayer == 0) {
+ return INVALID_OPERATION;
+ }
*w = mVideoWidth;
return NO_ERROR;
}
-status_t MediaPlayer2::getVideoHeight(int *h)
-{
+status_t MediaPlayer2::getVideoHeight(int *h) {
ALOGV("getVideoHeight");
Mutex::Autolock _l(mLock);
- if (mPlayer == 0) return INVALID_OPERATION;
+ if (mPlayer == 0) {
+ return INVALID_OPERATION;
+ }
*h = mVideoHeight;
return NO_ERROR;
}
-status_t MediaPlayer2::getCurrentPosition(int *msec)
-{
+status_t MediaPlayer2::getCurrentPosition(int *msec) {
ALOGV("getCurrentPosition");
Mutex::Autolock _l(mLock);
- if (mPlayer != 0) {
- if (mCurrentPosition >= 0) {
- ALOGV("Using cached seek position: %d", mCurrentPosition);
- *msec = mCurrentPosition;
- return NO_ERROR;
- }
- return mPlayer->getCurrentPosition(msec);
+ if (mPlayer == 0) {
+ return INVALID_OPERATION;
}
- return INVALID_OPERATION;
+ if (mCurrentPosition >= 0) {
+ ALOGV("Using cached seek position: %d", mCurrentPosition);
+ *msec = mCurrentPosition;
+ return NO_ERROR;
+ }
+ status_t ret = mPlayer->getCurrentPosition(msec);
+ if (ret == NO_ERROR) {
+ ALOGV("getCurrentPosition = %d", *msec);
+ } else {
+ ALOGE("getCurrentPosition returned %d", ret);
+ }
+ return ret;
}
-status_t MediaPlayer2::getDuration_l(int *msec)
-{
+status_t MediaPlayer2::getDuration(int *msec) {
+ Mutex::Autolock _l(mLock);
ALOGV("getDuration_l");
bool isValidState = (mCurrentState & (MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_STARTED |
MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_STOPPED | MEDIA_PLAYER2_PLAYBACK_COMPLETE));
- if (mPlayer != 0 && isValidState) {
- int durationMs;
- status_t ret = mPlayer->getDuration(&durationMs);
-
- if (ret != OK) {
- // Do not enter error state just because no duration was available.
- durationMs = -1;
- ret = OK;
- }
-
- if (msec) {
- *msec = durationMs;
- }
- return ret;
+ if (mPlayer == 0 || !isValidState) {
+ ALOGE("Attempt to call getDuration in wrong state: mPlayer=%p, mCurrentState=%u",
+ mPlayer.get(), mCurrentState);
+ return INVALID_OPERATION;
}
- ALOGE("Attempt to call getDuration in wrong state: mPlayer=%p, mCurrentState=%u",
- mPlayer.get(), mCurrentState);
- return INVALID_OPERATION;
+ int durationMs;
+ status_t ret = mPlayer->getDuration(&durationMs);
+
+ if (ret == NO_ERROR) {
+ ALOGV("getDuration = %d", durationMs);
+ } else {
+ ALOGE("getDuration returned %d", ret);
+ // Do not enter error state just because no duration was available.
+ durationMs = -1;
+ }
+
+ if (msec) {
+ *msec = durationMs;
+ }
+ return OK;
}
-status_t MediaPlayer2::getDuration(int *msec)
-{
- Mutex::Autolock _l(mLock);
- return getDuration_l(msec);
-}
-
-status_t MediaPlayer2::seekTo_l(int msec, MediaPlayer2SeekMode mode)
-{
+status_t MediaPlayer2::seekTo_l(int msec, MediaPlayer2SeekMode mode) {
ALOGV("seekTo (%d, %d)", msec, mode);
if ((mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER2_STARTED | MEDIA_PLAYER2_PREPARED |
MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_PLAYBACK_COMPLETE) ) ) {
- if ( msec < 0 ) {
+ if (msec < 0) {
ALOGW("Attempt to seek to invalid position: %d", msec);
msec = 0;
}
@@ -537,8 +1016,7 @@
mSeekPosition = msec;
mSeekMode = mode;
return mPlayer->seekTo(msec, mode);
- }
- else {
+ } else {
ALOGV("Seek in progress - queue up seekTo[%d, %d]", msec, mode);
return NO_ERROR;
}
@@ -548,8 +1026,7 @@
return INVALID_OPERATION;
}
-status_t MediaPlayer2::seekTo(int msec, MediaPlayer2SeekMode mode)
-{
+status_t MediaPlayer2::seekTo(int msec, MediaPlayer2SeekMode mode) {
mLockThreadId = getThreadId();
Mutex::Autolock _l(mLock);
status_t result = seekTo_l(msec, mode);
@@ -558,26 +1035,27 @@
return result;
}
-status_t MediaPlayer2::notifyAt(int64_t mediaTimeUs)
-{
+status_t MediaPlayer2::notifyAt(int64_t mediaTimeUs) {
Mutex::Autolock _l(mLock);
if (mPlayer != 0) {
- return mPlayer->notifyAt(mediaTimeUs);
+ return INVALID_OPERATION;
}
- return INVALID_OPERATION;
+
+ return mPlayer->notifyAt(mediaTimeUs);
}
-status_t MediaPlayer2::reset_l()
-{
+status_t MediaPlayer2::reset_l() {
mLoop = false;
- if (mCurrentState == MEDIA_PLAYER2_IDLE) return NO_ERROR;
+ if (mCurrentState == MEDIA_PLAYER2_IDLE) {
+ return NO_ERROR;
+ }
if (mPlayer != 0) {
status_t ret = mPlayer->reset();
if (ret != NO_ERROR) {
ALOGE("reset() failed with return code (%d)", ret);
mCurrentState = MEDIA_PLAYER2_STATE_ERROR;
} else {
- mPlayer->disconnect();
+ mPlayer->setListener(NULL);
mCurrentState = MEDIA_PLAYER2_IDLE;
}
// setDataSource has to be called again to create a
@@ -589,8 +1067,7 @@
return NO_ERROR;
}
-status_t MediaPlayer2::reset()
-{
+status_t MediaPlayer2::reset() {
ALOGV("reset");
mLockThreadId = getThreadId();
Mutex::Autolock _l(mLock);
@@ -600,8 +1077,7 @@
return result;
}
-status_t MediaPlayer2::setAudioStreamType(audio_stream_type_t type)
-{
+status_t MediaPlayer2::setAudioStreamType(audio_stream_type_t type) {
ALOGV("MediaPlayer2::setAudioStreamType");
Mutex::Autolock _l(mLock);
if (mStreamType == type) return NO_ERROR;
@@ -616,16 +1092,14 @@
return OK;
}
-status_t MediaPlayer2::getAudioStreamType(audio_stream_type_t *type)
-{
+status_t MediaPlayer2::getAudioStreamType(audio_stream_type_t *type) {
ALOGV("getAudioStreamType");
Mutex::Autolock _l(mLock);
*type = mStreamType;
return OK;
}
-status_t MediaPlayer2::setLooping(int loop)
-{
+status_t MediaPlayer2::setLooping(int loop) {
ALOGV("MediaPlayer2::setLooping");
Mutex::Autolock _l(mLock);
mLoop = (loop != 0);
@@ -645,20 +1119,18 @@
return false;
}
-status_t MediaPlayer2::setVolume(float leftVolume, float rightVolume)
-{
+status_t MediaPlayer2::setVolume(float leftVolume, float rightVolume) {
ALOGV("MediaPlayer2::setVolume(%f, %f)", leftVolume, rightVolume);
Mutex::Autolock _l(mLock);
mLeftVolume = leftVolume;
mRightVolume = rightVolume;
- if (mPlayer != 0) {
- return mPlayer->setVolume(leftVolume, rightVolume);
+ if (mAudioOutput != 0) {
+ mAudioOutput->setVolume(leftVolume, rightVolume);
}
return OK;
}
-status_t MediaPlayer2::setAudioSessionId(audio_session_t sessionId)
-{
+status_t MediaPlayer2::setAudioSessionId(audio_session_t sessionId) {
ALOGV("MediaPlayer2::setAudioSessionId(%d)", sessionId);
Mutex::Autolock _l(mLock);
if (!(mCurrentState & MEDIA_PLAYER2_IDLE)) {
@@ -676,40 +1148,36 @@
return NO_ERROR;
}
-audio_session_t MediaPlayer2::getAudioSessionId()
-{
+audio_session_t MediaPlayer2::getAudioSessionId() {
Mutex::Autolock _l(mLock);
return mAudioSessionId;
}
-status_t MediaPlayer2::setAuxEffectSendLevel(float level)
-{
+status_t MediaPlayer2::setAuxEffectSendLevel(float level) {
ALOGV("MediaPlayer2::setAuxEffectSendLevel(%f)", level);
Mutex::Autolock _l(mLock);
mSendLevel = level;
- if (mPlayer != 0) {
- return mPlayer->setAuxEffectSendLevel(level);
+ if (mAudioOutput != 0) {
+ return mAudioOutput->setAuxEffectSendLevel(level);
}
return OK;
}
-status_t MediaPlayer2::attachAuxEffect(int effectId)
-{
+status_t MediaPlayer2::attachAuxEffect(int effectId) {
ALOGV("MediaPlayer2::attachAuxEffect(%d)", effectId);
Mutex::Autolock _l(mLock);
- if (mPlayer == 0 ||
+ if (mAudioOutput == 0 ||
(mCurrentState & MEDIA_PLAYER2_IDLE) ||
(mCurrentState == MEDIA_PLAYER2_STATE_ERROR )) {
ALOGE("attachAuxEffect called in state %d, mPlayer(%p)", mCurrentState, mPlayer.get());
return INVALID_OPERATION;
}
- return mPlayer->attachAuxEffect(effectId);
+ return mAudioOutput->attachAuxEffect(effectId);
}
// always call with lock held
-status_t MediaPlayer2::checkStateForKeySet_l(int key)
-{
+status_t MediaPlayer2::checkStateForKeySet_l(int key) {
switch(key) {
case MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES:
if (mCurrentState & ( MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_STARTED |
@@ -726,8 +1194,7 @@
return OK;
}
-status_t MediaPlayer2::setParameter(int key, const Parcel& request)
-{
+status_t MediaPlayer2::setParameter(int key, const Parcel& request) {
ALOGV("MediaPlayer2::setParameter(%d)", key);
status_t status = INVALID_OPERATION;
Mutex::Autolock _l(mLock);
@@ -737,10 +1204,15 @@
switch (key) {
case MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES:
// save the marshalled audio attributes
- if (mAudioAttributesParcel != NULL) { delete mAudioAttributesParcel; };
+ if (mAudioAttributesParcel != NULL) {
+ delete mAudioAttributesParcel;
+ }
mAudioAttributesParcel = new Parcel();
mAudioAttributesParcel->appendFrom(&request, 0, request.dataSize());
- status = OK;
+ status = setAudioAttributes_l(request);
+ if (status != OK) {
+ return status;
+ }
break;
default:
ALOGV_IF(mPlayer == NULL, "setParameter: no active player");
@@ -753,25 +1225,59 @@
return status;
}
-status_t MediaPlayer2::getParameter(int key, Parcel *reply)
-{
+status_t MediaPlayer2::getParameter(int key, Parcel *reply) {
ALOGV("MediaPlayer2::getParameter(%d)", key);
Mutex::Autolock _l(mLock);
- if (mPlayer != NULL) {
- status_t status = mPlayer->getParameter(key, reply);
- if (status != OK) {
- ALOGD("getParameter returns %d", status);
- }
- return status;
+ if (mPlayer == NULL) {
+ ALOGV("getParameter: no active player");
+ return INVALID_OPERATION;
}
- ALOGV("getParameter: no active player");
- return INVALID_OPERATION;
+
+ status_t status = mPlayer->getParameter(key, reply);
+ if (status != OK) {
+ ALOGD("getParameter returns %d", status);
+ }
+ return status;
}
-void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj)
-{
+bool MediaPlayer2::shouldDropMetadata(media::Metadata::Type code) const {
+ Mutex::Autolock lock(mLock);
+
+ if (findMetadata(mMetadataDrop, code)) {
+ return true;
+ }
+
+ if (mMetadataAllow.isEmpty() || findMetadata(mMetadataAllow, code)) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+
+void MediaPlayer2::addNewMetadataUpdate(media::Metadata::Type metadata_type) {
+ Mutex::Autolock lock(mLock);
+ if (mMetadataUpdated.indexOf(metadata_type) < 0) {
+ mMetadataUpdated.add(metadata_type);
+ }
+}
+
+void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) {
ALOGV("message received srcId=%lld, msg=%d, ext1=%d, ext2=%d",
(long long)srcId, msg, ext1, ext2);
+
+ if (MEDIA2_INFO == msg && MEDIA2_INFO_METADATA_UPDATE == ext1) {
+ const media::Metadata::Type metadata_type = ext2;
+
+ if(shouldDropMetadata(metadata_type)) {
+ return;
+ }
+
+ // Update the list of metadata that have changed. getMetadata
+ // also access mMetadataUpdated and clears it.
+ addNewMetadataUpdate(metadata_type);
+ }
+
bool send = true;
bool locked = false;
@@ -881,24 +1387,12 @@
}
}
-status_t MediaPlayer2::setNextMediaPlayer(const sp<MediaPlayer2>& next) {
- Mutex::Autolock _l(mLock);
- if (mPlayer == NULL) {
- return NO_INIT;
- }
-
- if (next != NULL && !(next->mCurrentState &
- (MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_PLAYBACK_COMPLETE))) {
- ALOGE("next player is not prepared");
- return INVALID_OPERATION;
- }
-
- return mPlayer->setNextPlayer(next == NULL ? NULL : next->mPlayer);
+status_t MediaPlayer2::setNextMediaPlayer(const sp<MediaPlayer2>& /* next */) {
+ return INVALID_OPERATION;
}
// Modular DRM
-status_t MediaPlayer2::prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId)
-{
+status_t MediaPlayer2::prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId) {
// TODO change to ALOGV
ALOGD("prepareDrm: uuid: %p drmSessionId: %p(%zu)", uuid,
drmSessionId.array(), drmSessionId.size());
@@ -931,8 +1425,7 @@
return status;
}
-status_t MediaPlayer2::releaseDrm()
-{
+status_t MediaPlayer2::releaseDrm() {
Mutex::Autolock _l(mLock);
if (mPlayer == NULL) {
return NO_INIT;
@@ -960,39 +1453,74 @@
return status;
}
-status_t MediaPlayer2::setOutputDevice(audio_port_handle_t deviceId)
-{
+status_t MediaPlayer2::setOutputDevice(audio_port_handle_t deviceId) {
Mutex::Autolock _l(mLock);
- if (mPlayer == NULL) {
- ALOGV("setOutputDevice: player not init");
+ if (mAudioOutput == NULL) {
+ ALOGV("setOutputDevice: audio sink not init");
return NO_INIT;
}
- return mPlayer->setOutputDevice(deviceId);
+ return mAudioOutput->setOutputDevice(deviceId);
}
-audio_port_handle_t MediaPlayer2::getRoutedDeviceId()
-{
+audio_port_handle_t MediaPlayer2::getRoutedDeviceId() {
Mutex::Autolock _l(mLock);
- if (mPlayer == NULL) {
- ALOGV("getRoutedDeviceId: player not init");
+ if (mAudioOutput == NULL) {
+ ALOGV("getRoutedDeviceId: audio sink not init");
return AUDIO_PORT_HANDLE_NONE;
}
audio_port_handle_t deviceId;
- status_t status = mPlayer->getRoutedDeviceId(&deviceId);
+ status_t status = mAudioOutput->getRoutedDeviceId(&deviceId);
if (status != NO_ERROR) {
return AUDIO_PORT_HANDLE_NONE;
}
return deviceId;
}
-status_t MediaPlayer2::enableAudioDeviceCallback(bool enabled)
-{
+status_t MediaPlayer2::enableAudioDeviceCallback(bool enabled) {
Mutex::Autolock _l(mLock);
- if (mPlayer == NULL) {
+ if (mAudioOutput == NULL) {
ALOGV("addAudioDeviceCallback: player not init");
return NO_INIT;
}
- return mPlayer->enableAudioDeviceCallback(enabled);
+ return mAudioOutput->enableAudioDeviceCallback(enabled);
+}
+
+status_t MediaPlayer2::dump(int fd, const Vector<String16>& args) {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ result.append(" MediaPlayer2\n");
+ snprintf(buffer, 255, " pid(%d), looping(%s)\n", mPid, mLoop?"true": "false");
+ result.append(buffer);
+
+ sp<MediaPlayer2Interface> player;
+ sp<MediaPlayer2AudioOutput> audioOutput;
+ bool locked = false;
+ for (int i = 0; i < kDumpLockRetries; ++i) {
+ if (mLock.tryLock() == NO_ERROR) {
+ locked = true;
+ break;
+ }
+ usleep(kDumpLockSleepUs);
+ }
+
+ if (locked) {
+ player = mPlayer;
+ audioOutput = mAudioOutput;
+ mLock.unlock();
+ } else {
+ result.append(" lock is taken, no dump from player and audio output\n");
+ }
+ write(fd, result.string(), result.size());
+
+ if (player != NULL) {
+ player->dump(fd, args);
+ }
+ if (audioOutput != 0) {
+ audioOutput->dump(fd, args);
+ }
+ write(fd, "\n", 1);
+ return NO_ERROR;
}
} // namespace android
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 541093a..6da6c13 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -7289,12 +7289,16 @@
}
}
- float rate;
- if (params->findFloat("operating-rate", &rate) && rate > 0) {
- status_t err = setOperatingRate(rate, mIsVideo);
+ int32_t rateInt = -1;
+ float rateFloat = -1;
+ if (!params->findFloat("operating-rate", &rateFloat)) {
+ params->findInt32("operating-rate", &rateInt);
+ rateFloat = (float) rateInt; // 16MHz (FLINTMAX) is OK for upper bound.
+ }
+ if (rateFloat > 0) {
+ status_t err = setOperatingRate(rateFloat, mIsVideo);
if (err != OK) {
- ALOGE("Failed to set parameter 'operating-rate' (err %d)", err);
- return err;
+ ALOGI("Failed to set parameter 'operating-rate' (err %d)", err);
}
}
@@ -7319,10 +7323,8 @@
}
}
- status_t err = configureTemporalLayers(params, false /* inConfigure */, mOutputFormat);
- if (err != OK) {
- err = OK; // ignore failure
- }
+ // Ignore errors as failure is expected for codecs that aren't video encoders.
+ (void)configureTemporalLayers(params, false /* inConfigure */, mOutputFormat);
return setVendorParameters(params);
}
diff --git a/media/libstagefright/codec2/1.0/InputSurfaceConnection.cpp b/media/libstagefright/codec2/1.0/InputSurfaceConnection.cpp
index 32d6404..08b5d65 100644
--- a/media/libstagefright/codec2/1.0/InputSurfaceConnection.cpp
+++ b/media/libstagefright/codec2/1.0/InputSurfaceConnection.cpp
@@ -22,7 +22,6 @@
#include <C2BlockInternal.h>
#include <C2PlatformSupport.h>
-#include <gui/Surface.h>
#include <media/stagefright/codec2/1.0/InputSurfaceConnection.h>
#include <system/window.h>
diff --git a/media/libstagefright/codec2/C2.cpp b/media/libstagefright/codec2/C2.cpp
index a51b073..359d4e5 100644
--- a/media/libstagefright/codec2/C2.cpp
+++ b/media/libstagefright/codec2/C2.cpp
@@ -22,8 +22,6 @@
#include <C2ParamDef.h>
#include <C2Work.h>
-namespace android {
-
/**
* There is nothing here yet. This library is built to see what symbols and methods get
* defined as part of the API include files.
@@ -32,5 +30,4 @@
* Codec2 clients.
*/
-} // namespace android
diff --git a/media/libstagefright/codec2/include/C2.h b/media/libstagefright/codec2/include/C2.h
index 00e3924..508f9ae 100644
--- a/media/libstagefright/codec2/include/C2.h
+++ b/media/libstagefright/codec2/include/C2.h
@@ -17,40 +17,18 @@
#ifndef C2_H_
#define C2_H_
-#include <string>
-#include <vector>
-#include <list>
-
-#ifdef __ANDROID__
-
-#include <utils/Errors.h> // for status_t
-#include <utils/Timers.h> // for nsecs_t
-
-namespace android {
-
-#else
-
#include <errno.h>
-typedef int64_t nsecs_t;
-enum {
- GRALLOC_USAGE_SW_READ_OFTEN,
- GRALLOC_USAGE_RENDERSCRIPT,
- GRALLOC_USAGE_HW_TEXTURE,
- GRALLOC_USAGE_HW_COMPOSER,
- GRALLOC_USAGE_HW_VIDEO_ENCODER,
- GRALLOC_USAGE_PROTECTED,
- GRALLOC_USAGE_SW_WRITE_OFTEN,
- GRALLOC_USAGE_HW_RENDER,
-};
+#include <string>
-#endif
+/** nanoseconds with arbitrary origin. */
+typedef int64_t c2_nsecs_t;
/** \mainpage Codec2
*
- * Codec2 is a frame-based data processing API used by android.
+ * Codec2 is a generic frame-based data processing API.
*
- * The framework accesses components via the \ref API.
+ * The media subsystem accesses components via the \ref API.
*/
/** \ingroup API
@@ -109,53 +87,35 @@
* c2_status_t: status codes used.
*/
enum c2_status_t : int32_t {
-
/*
- * Use android status constants if available. Otherwise, define the android status constants as
- * additional enum values using POSIX errno constants.
+ * Use POSIX errno constants.
*/
-#ifndef __ANDROID__
- ALREADY_EXISTS = -EEXIST,
- BAD_VALUE = -EINVAL,
- BAD_INDEX = -EOVERFLOW,
- FAILED_TRANSACTION = -ENOTSUP,
- INVALID_OPERATION = -ENOSYS,
- NAME_NOT_FOUND = -ENOENT,
- NO_MEMORY = -ENOMEM,
- NO_INIT = -ENODEV,
- OK = 0,
- PERMISSION_DENIED = -EPERM,
- TIMED_OUT = -ETIMEDOUT,
- UNKNOWN_ERROR = -EFAULT,
- UNKNOWN_TRANSACTION = -EBADMSG,
- WOULD_BLOCK = -EWOULDBLOCK,
-#endif
-
- C2_OK = OK, ///< operation completed successfully
+ C2_OK = 0, ///< operation completed successfully
// bad input
- C2_BAD_VALUE = BAD_VALUE, ///< argument has invalid value (user error)
- C2_BAD_INDEX = BAD_INDEX, ///< argument uses invalid index (user error)
- C2_CANNOT_DO = FAILED_TRANSACTION, ///< argument/index is valid but not possible
+ C2_BAD_VALUE = EINVAL, ///< argument has invalid value (user error)
+ C2_BAD_INDEX = ENXIO, ///< argument uses invalid index (user error)
+ C2_CANNOT_DO = ENOTSUP, ///< argument/index is valid but not possible
// bad sequencing of events
- C2_DUPLICATE = ALREADY_EXISTS, ///< object already exists
- C2_NOT_FOUND = NAME_NOT_FOUND, ///< object not found
- C2_BAD_STATE = INVALID_OPERATION, ///< operation is not permitted in the current state
- C2_BLOCKING = WOULD_BLOCK, ///< operation would block but blocking is not permitted
+ C2_DUPLICATE = EEXIST, ///< object already exists
+ C2_NOT_FOUND = ENOENT, ///< object not found
+ C2_BAD_STATE = EPERM, ///< operation is not permitted in the current state
+ C2_BLOCKING = EWOULDBLOCK, ///< operation would block but blocking is not permitted
+ C2_CANCELED = EINTR, ///< operation interrupted/canceled
// bad environment
- C2_NO_MEMORY = NO_MEMORY, ///< not enough memory to complete operation
- C2_REFUSED = PERMISSION_DENIED, ///< missing permission to complete operation
+ C2_NO_MEMORY = ENOMEM, ///< not enough memory to complete operation
+ C2_REFUSED = EACCES, ///< missing permission to complete operation
- C2_TIMED_OUT = TIMED_OUT, ///< operation did not complete within timeout
+ C2_TIMED_OUT = ETIMEDOUT, ///< operation did not complete within timeout
// bad versioning
- C2_OMITTED = UNKNOWN_TRANSACTION, ///< operation is not implemented/supported (optional only)
+ C2_OMITTED = ENOSYS, ///< operation is not implemented/supported (optional only)
// unknown fatal
- C2_CORRUPTED = UNKNOWN_ERROR, ///< some unexpected error prevented the operation
- C2_NO_INIT = NO_INIT, ///< status has not been initialized
+ C2_CORRUPTED = EFAULT, ///< some unexpected error prevented the operation
+ C2_NO_INIT = ENODEV, ///< status has not been initialized
};
/**
@@ -185,11 +145,12 @@
type args& operator=(const type args&) = delete; \
type(const type args&) = delete; \
-#define C2_PURE __attribute__((pure))
-#define C2_CONST __attribute__((const))
-#define C2_HIDE __attribute__((visibility("hidden")))
-#define C2_INTERNAL __attribute__((internal_linkage))
#define C2_ALLOW_OVERFLOW __attribute__((no_sanitize("integer")))
+#define C2_CONST __attribute__((const))
+#define C2_HIDE __attribute__((visibility("hidden")))
+#define C2_INTERNAL __attribute__((internal_linkage))
+#define C2_PACK __attribute__((packed))
+#define C2_PURE __attribute__((pure))
#define DEFINE_OTHER_COMPARISON_OPERATORS(type) \
inline bool operator!=(const type &other) const { return !(*this == other); } \
@@ -548,32 +509,28 @@
/// @}
-#ifdef __ANDROID__
-} // namespace android
-#endif
-
#include <functional>
template<typename T>
-struct std::less<::android::c2_cntr_t<T>> {
- constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
+struct std::less<::c2_cntr_t<T>> {
+ constexpr bool operator()(const ::c2_cntr_t<T> &lh, const ::c2_cntr_t<T> &rh) const {
return lh.peeku() < rh.peeku();
}
};
template<typename T>
-struct std::less_equal<::android::c2_cntr_t<T>> {
- constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
+struct std::less_equal<::c2_cntr_t<T>> {
+ constexpr bool operator()(const ::c2_cntr_t<T> &lh, const ::c2_cntr_t<T> &rh) const {
return lh.peeku() <= rh.peeku();
}
};
template<typename T>
-struct std::greater<::android::c2_cntr_t<T>> {
- constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
+struct std::greater<::c2_cntr_t<T>> {
+ constexpr bool operator()(const ::c2_cntr_t<T> &lh, const ::c2_cntr_t<T> &rh) const {
return lh.peeku() > rh.peeku();
}
};
template<typename T>
-struct std::greater_equal<::android::c2_cntr_t<T>> {
- constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
+struct std::greater_equal<::c2_cntr_t<T>> {
+ constexpr bool operator()(const ::c2_cntr_t<T> &lh, const ::c2_cntr_t<T> &rh) const {
return lh.peeku() >= rh.peeku();
}
};
diff --git a/media/libstagefright/codec2/include/C2Buffer.h b/media/libstagefright/codec2/include/C2Buffer.h
index 034075f..e49f82b 100644
--- a/media/libstagefright/codec2/include/C2Buffer.h
+++ b/media/libstagefright/codec2/include/C2Buffer.h
@@ -18,27 +18,20 @@
#define C2BUFFER_H_
#include <C2.h>
+#include <C2BufferBase.h>
#include <C2Param.h> // for C2Info
-#include <list>
#include <memory>
+#include <vector>
#ifdef __ANDROID__
-
-// #include <system/window.h>
-#include <cutils/native_handle.h>
-#include <hardware/gralloc.h> // TODO: remove
-
-typedef native_handle_t C2Handle;
-
+#include <android-C2Buffer.h>
#else
typedef void* C2Handle;
#endif
-namespace android {
-
/// \defgroup buffer Buffers
/// @{
@@ -89,7 +82,7 @@
* \retval C2_REFUSED no permission to wait for the fence (unexpected - system)
* \retval C2_CORRUPTED some unknown error prevented waiting for the fence (unexpected)
*/
- c2_status_t wait(nsecs_t timeoutNs);
+ c2_status_t wait(c2_nsecs_t timeoutNs);
/**
* Used to check if this fence is valid (if there is a chance for it to be signaled.)
@@ -550,41 +543,9 @@
ALLOCATIONS
**************************************************************************************************/
-/// \defgroup allocator Allocation and memory placement
+/// \ingroup allocator Allocation and memory placement
/// @{
-/**
- * Buffer/memory usage bits. These are used by the allocators to select optimal memory type/pool and
- * buffer layout.
- *
- * \note This struct has public fields without getters/setters. All methods are inline.
- */
-struct C2MemoryUsage {
-// public:
- // TODO: match these to gralloc1.h
- enum Consumer : uint64_t {
- // \todo do we need to distinguish often from rarely?
- CPU_READ = GRALLOC_USAGE_SW_READ_OFTEN,
- RENDERSCRIPT_READ = GRALLOC_USAGE_RENDERSCRIPT,
- HW_TEXTURE_READ = GRALLOC_USAGE_HW_TEXTURE,
- HW_COMPOSER_READ = GRALLOC_USAGE_HW_COMPOSER,
- HW_CODEC_READ = GRALLOC_USAGE_HW_VIDEO_ENCODER,
- READ_PROTECTED = GRALLOC_USAGE_PROTECTED,
- };
-
- enum Producer : uint64_t {
- CPU_WRITE = GRALLOC_USAGE_SW_WRITE_OFTEN,
- RENDERSCRIPT_WRITE = GRALLOC_USAGE_RENDERSCRIPT,
- HW_TEXTURE_WRITE = GRALLOC_USAGE_HW_RENDER,
- HW_COMPOSER_WRITE = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER,
- HW_CODEC_WRITE = GRALLOC_USAGE_HW_VIDEO_ENCODER,
- WRITE_PROTECTED = GRALLOC_USAGE_PROTECTED,
- };
-
- uint64_t consumer; // e.g. input
- uint64_t producer; // e.g. output
-};
-
class C2LinearAllocation;
class C2GraphicAllocation;
@@ -2301,6 +2262,10 @@
/// @}
-} // namespace android
+// expose some objects in android namespace
+namespace android {
+ /// \deprecated
+ typedef ::C2Fence C2Fence;
+}
#endif // C2BUFFER_H_
diff --git a/media/libstagefright/codec2/include/C2BufferBase.h b/media/libstagefright/codec2/include/C2BufferBase.h
new file mode 100644
index 0000000..68411f2
--- /dev/null
+++ b/media/libstagefright/codec2/include/C2BufferBase.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef C2BUFFER_BASE_H_
+#define C2BUFFER_BASE_H_
+
+/// \defgroup allocator Allocation and memory placement
+/// @{
+
+/**
+ * Buffer/memory usage bits. These shall be used by the allocators to select optimal memory type/
+ * pool and buffer layout. Usage bits are conceptually separated into read and write usage, while
+ * the buffer use life-cycle is separated into producers (writers) and consumers (readers).
+ * These two concepts are related but not equivalent: consumers may only read buffers and only
+ * producers may write to buffers; note, however, that buffer producers may also want or need to
+ * read the buffers.
+ *
+ * Read and write buffer usage bits shall be or-ed to arrive at the full buffer usage. Admittedly,
+ * this does not account for the amount of reading and writing (e.g. a buffer may have one or more
+ * readers); however, the proper information necessary to properly weigh the various usages would be
+ * the amount of data read/written for each usage type. This would result in an integer array of
+ * size 64 (or the number of distinct usages) for memory usage, and likely such detailed information
+ * would not always be available.
+ *
+ * That platform-agnostic Codec 2.0 API only defines the bare minimum usages. Platforms shall define
+ * usage bits that are appropriate for the platform.
+ */
+struct C2MemoryUsage {
+// public:
+ /**
+ * Buffer read usage.
+ */
+ enum Read : uint64_t {
+ /** Buffer is read by the CPU. */
+ CPU_READ = 1 << 0,
+ /**
+ * Buffer shall only be read by trusted hardware. The definition of trusted hardware is
+ * platform specific, but this flag is reserved to prevent mapping this block into CPU
+ * readable memory resulting in bus fault. This flag can be used when buffer access must be
+ * protected.
+ */
+ READ_PROTECTED = 1 << 1,
+ };
+
+ /**
+ * Buffer write usage.
+ */
+ enum Write : uint64_t {
+ /** Buffer is writted to by the CPU. */
+ CPU_WRITE = 1 << 2,
+ /**
+ * Buffer shall only be written to by trusted hardware. The definition of trusted hardware
+ * is platform specific, but this flag is reserved to prevent mapping this block into CPU
+ * writable memory resulting in bus fault. This flag can be used when buffer integrity must
+ * be protected.
+ */
+ WRITE_PROTECTED = 1 << 3,
+ };
+
+ enum : uint64_t {
+ /**
+ * Buffer usage bits reserved for the platform. We don't separately reserve read and
+ * write usages as platforms may have asymmetric distribution between them.
+ */
+ PLATFORM_MASK = ~(CPU_READ | CPU_WRITE | READ_PROTECTED | WRITE_PROTECTED),
+ };
+
+ /** Create a usage from separate consumer and producer usage mask. \deprecated */
+ inline C2MemoryUsage(uint64_t consumer, uint64_t producer)
+ : expected(consumer | producer) { }
+
+ inline explicit C2MemoryUsage(uint64_t expected_)
+ : expected(expected_) { }
+
+ uint64_t expected; // expected buffer usage
+};
+
+/// @}
+
+#endif // C2BUFFER_BASE_H_
+
diff --git a/media/libstagefright/codec2/include/C2Component.h b/media/libstagefright/codec2/include/C2Component.h
index 721966b..64bd1cb 100644
--- a/media/libstagefright/codec2/include/C2Component.h
+++ b/media/libstagefright/codec2/include/C2Component.h
@@ -29,13 +29,9 @@
#include <C2Param.h>
#include <C2Work.h>
-namespace android {
-
/// \defgroup components Components
/// @{
-class C2Component;
-
struct C2FieldSupportedValuesQuery {
enum type_t : uint32_t {
POSSIBLE, ///< query all possible values regardless of other settings
@@ -947,6 +943,11 @@
/// @}
-} // namespace android
+namespace android {
+ /// \deprecated
+ typedef ::C2Component C2Component;
+ /// \deprecated
+ typedef ::C2ComponentInterface C2ComponentInterface;
+}
#endif // C2COMPONENT_H_
diff --git a/media/libstagefright/codec2/include/C2Config.h b/media/libstagefright/codec2/include/C2Config.h
index 2a2b9de..3f149bb 100644
--- a/media/libstagefright/codec2/include/C2Config.h
+++ b/media/libstagefright/codec2/include/C2Config.h
@@ -19,8 +19,6 @@
#include <C2ParamDef.h>
-namespace android {
-
/// \defgroup config Component configuration
/// @{
@@ -262,6 +260,4 @@
/// @}
-} // namespace android
-
#endif
diff --git a/media/libstagefright/codec2/include/C2Param.h b/media/libstagefright/codec2/include/C2Param.h
index 4d9f707..181697d 100644
--- a/media/libstagefright/codec2/include/C2Param.h
+++ b/media/libstagefright/codec2/include/C2Param.h
@@ -23,13 +23,10 @@
#include <stdint.h>
#include <algorithm>
-#include <list>
#include <string>
#include <type_traits>
-
-#define C2_PACK __attribute__((packed))
-
-namespace android {
+#include <utility>
+#include <vector>
/// \addtogroup Parameters
/// @{
@@ -89,7 +86,6 @@
*/
/// \ingroup internal
-struct _C2ParamManipulator;
/**
* Parameter base class.
@@ -733,17 +729,6 @@
};
/**
- * Structure uniquely specifying a field, an array element of a field, or a
- * parameter in a configuration
- */
-struct C2ParamOrField : public C2ParamField {
-//public:
- template<typename S>
- inline C2ParamOrField(S* param)
- : C2ParamField(param->index(), 0u, param->size()) {}
-};
-
-/**
* A shared (union) representation of numeric values
*/
class C2Value {
@@ -1376,6 +1361,4 @@
/// @}
-} // namespace android
-
#endif // C2PARAM_H_
diff --git a/media/libstagefright/codec2/include/C2ParamDef.h b/media/libstagefright/codec2/include/C2ParamDef.h
index 3691e01..f0b6223 100644
--- a/media/libstagefright/codec2/include/C2ParamDef.h
+++ b/media/libstagefright/codec2/include/C2ParamDef.h
@@ -24,8 +24,6 @@
#include <C2Param.h>
-namespace android {
-
/// \addtogroup Parameters
/// @{
@@ -905,6 +903,4 @@
/// @}
-} // namespace android
-
#endif // C2PARAM_DEF_H_
diff --git a/media/libstagefright/codec2/include/C2Work.h b/media/libstagefright/codec2/include/C2Work.h
index b6c5814..a2f02e5 100644
--- a/media/libstagefright/codec2/include/C2Work.h
+++ b/media/libstagefright/codec2/include/C2Work.h
@@ -28,8 +28,6 @@
#include <list>
#include <vector>
-namespace android {
-
/// \defgroup work Work and data processing
/// @{
@@ -75,9 +73,8 @@
// WORK
// ================================================================================================
-// c2_node_id_t-s
+/** Unique ID for a processing node. */
typedef uint32_t c2_node_id_t;
-typedef c2_node_id_t c2_node_id_t;
enum {
kParamIndexWorkOrdinal,
@@ -211,6 +208,4 @@
/// @}
-} // namespace android
-
#endif // C2WORK_H_
diff --git a/media/libstagefright/codec2/include/android-C2Buffer.h b/media/libstagefright/codec2/include/android-C2Buffer.h
new file mode 100644
index 0000000..c71f2cf
--- /dev/null
+++ b/media/libstagefright/codec2/include/android-C2Buffer.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_C2BUFFER_H_
+#define ANDROID_C2BUFFER_H_
+
+#include <cutils/native_handle.h>
+#include <hardware/gralloc.h>
+
+/* Use android native handle for C2Handle */
+typedef ::native_handle_t C2Handle;
+
+namespace android {
+
+/**
+ * Android platform buffer/memory usage bits.
+ */
+struct C2AndroidMemoryUsage : public C2MemoryUsage {
+// public:
+ /**
+ * Reuse gralloc flags where possible, as Codec 2.0 API only uses bits 0 and 1.
+ */
+ enum Consumer : uint64_t {
+ RENDERSCRIPT_READ = GRALLOC_USAGE_RENDERSCRIPT,
+ HW_TEXTURE_READ = GRALLOC_USAGE_HW_TEXTURE,
+ HW_COMPOSER_READ = GRALLOC_USAGE_HW_COMPOSER,
+ HW_CODEC_READ = GRALLOC_USAGE_HW_VIDEO_ENCODER,
+ READ_PROTECTED = GRALLOC_USAGE_PROTECTED,
+ };
+
+ enum Producer : uint64_t {
+ RENDERSCRIPT_WRITE = GRALLOC_USAGE_RENDERSCRIPT,
+ HW_TEXTURE_WRITE = GRALLOC_USAGE_HW_RENDER,
+ HW_COMPOSER_WRITE = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER,
+ HW_CODEC_WRITE = GRALLOC_USAGE_HW_VIDEO_ENCODER,
+ WRITE_PROTECTED = GRALLOC_USAGE_PROTECTED,
+ };
+
+ /**
+ * Convert from gralloc usage.
+ */
+ static C2MemoryUsage FromGrallocUsage(uint64_t usage);
+
+ /**
+ * Convert to gralloc usage.
+ */
+ uint64_t asGrallocUsage() const;
+};
+
+} // namespace android
+
+#endif // ANDROID_C2BUFFER_H_
diff --git a/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h b/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
index fc19acd..b24a416 100644
--- a/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
+++ b/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
@@ -25,9 +25,6 @@
#include <media/stagefright/codec2/1.0/InputSurfaceConnection.h>
namespace android {
-
-class C2Allocator;
-
namespace hardware {
namespace media {
namespace c2 {
diff --git a/media/libstagefright/codec2/tests/C2Param_test.cpp b/media/libstagefright/codec2/tests/C2Param_test.cpp
index 1a29add..168b889 100644
--- a/media/libstagefright/codec2/tests/C2Param_test.cpp
+++ b/media/libstagefright/codec2/tests/C2Param_test.cpp
@@ -23,8 +23,6 @@
#include <util/C2ParamUtils.h>
#include <C2ParamDef.h>
-namespace android {
-
void PrintTo(const _C2FieldId &id, ::std::ostream* os) {
*os << "@" << id._mOffset << "+" << id._mSize;
}
@@ -2397,15 +2395,11 @@
// ***********************
-}
-
#include <util/C2ParamUtils.h>
#include <C2Config.h>
#include <C2Component.h>
#include <unordered_map>
-namespace android {
-
C2ENUM(
MetadataType, int32_t,
kInvalid = -1,
@@ -2935,4 +2929,3 @@
EXPECT_EQ(15.25f, fp);
}
-} // namespace android
diff --git a/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp b/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
index b287ca8..b255eec 100644
--- a/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
@@ -29,6 +29,36 @@
namespace android {
+namespace {
+ enum : uint64_t {
+ /**
+ * Usage mask that is passed through from gralloc to Codec 2.0 usage.
+ */
+ PASSTHROUGH_USAGE_MASK =
+ ~(GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK | GRALLOC_USAGE_PROTECTED)
+ };
+
+ // verify that passthrough mask is within the platform mask
+ static_assert((~C2MemoryUsage::PLATFORM_MASK & PASSTHROUGH_USAGE_MASK) == 0, "");
+}
+
+C2MemoryUsage C2AndroidMemoryUsage::FromGrallocUsage(uint64_t usage) {
+ // gralloc does not support WRITE_PROTECTED
+ return C2MemoryUsage(
+ ((usage & GRALLOC_USAGE_SW_READ_MASK) ? C2MemoryUsage::CPU_READ : 0) |
+ ((usage & GRALLOC_USAGE_SW_WRITE_MASK) ? C2MemoryUsage::CPU_WRITE : 0) |
+ ((usage & GRALLOC_USAGE_PROTECTED) ? C2MemoryUsage::READ_PROTECTED : 0) |
+ (usage & PASSTHROUGH_USAGE_MASK));
+}
+
+uint64_t C2AndroidMemoryUsage::asGrallocUsage() const {
+ // gralloc does not support WRITE_PROTECTED
+ return (((expected & C2MemoryUsage::CPU_READ) ? GRALLOC_USAGE_SW_READ_MASK : 0) |
+ ((expected & C2MemoryUsage::CPU_WRITE) ? GRALLOC_USAGE_SW_WRITE_MASK : 0) |
+ ((expected & C2MemoryUsage::READ_PROTECTED) ? GRALLOC_USAGE_PROTECTED : 0) |
+ (expected & PASSTHROUGH_USAGE_MASK));
+}
+
using ::android::hardware::graphics::allocator::V2_0::IAllocator;
using ::android::hardware::graphics::common::V1_0::BufferUsage;
using ::android::hardware::graphics::common::V1_0::PixelFormat;
diff --git a/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp b/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
index 4328a8d..18dad46 100644
--- a/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
+++ b/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
@@ -232,10 +232,10 @@
int prot = PROT_NONE;
int flags = MAP_PRIVATE;
- if (usage.consumer & C2MemoryUsage::CPU_READ) {
+ if (usage.expected & C2MemoryUsage::CPU_READ) {
prot |= PROT_READ;
}
- if (usage.producer & C2MemoryUsage::CPU_WRITE) {
+ if (usage.expected & C2MemoryUsage::CPU_WRITE) {
prot |= PROT_WRITE;
flags = MAP_SHARED;
}
diff --git a/media/libstagefright/codec2/vndk/C2Buffer.cpp b/media/libstagefright/codec2/vndk/C2Buffer.cpp
index 511ffe0..47fdca1 100644
--- a/media/libstagefright/codec2/vndk/C2Buffer.cpp
+++ b/media/libstagefright/codec2/vndk/C2Buffer.cpp
@@ -18,14 +18,13 @@
#define LOG_TAG "C2Buffer"
#include <utils/Log.h>
+#include <list>
#include <map>
#include <mutex>
#include <C2BufferPriv.h>
#include <C2BlockInternal.h>
-namespace android {
-
namespace {
// This anonymous namespace contains the helper classes that allow our implementation to create
@@ -34,63 +33,63 @@
// Inherit from the parent, share with the friend.
class ReadViewBuddy : public C2ReadView {
using C2ReadView::C2ReadView;
- friend class ::android::C2ConstLinearBlock;
+ friend class ::C2ConstLinearBlock;
};
class WriteViewBuddy : public C2WriteView {
using C2WriteView::C2WriteView;
- friend class ::android::C2LinearBlock;
+ friend class ::C2LinearBlock;
};
class ConstLinearBlockBuddy : public C2ConstLinearBlock {
using C2ConstLinearBlock::C2ConstLinearBlock;
- friend class ::android::C2LinearBlock;
+ friend class ::C2LinearBlock;
};
class LinearBlockBuddy : public C2LinearBlock {
using C2LinearBlock::C2LinearBlock;
- friend class ::android::C2BasicLinearBlockPool;
+ friend class ::C2BasicLinearBlockPool;
};
class AcquirableReadViewBuddy : public C2Acquirable<C2ReadView> {
using C2Acquirable::C2Acquirable;
- friend class ::android::C2ConstLinearBlock;
+ friend class ::C2ConstLinearBlock;
};
class AcquirableWriteViewBuddy : public C2Acquirable<C2WriteView> {
using C2Acquirable::C2Acquirable;
- friend class ::android::C2LinearBlock;
+ friend class ::C2LinearBlock;
};
class GraphicViewBuddy : public C2GraphicView {
using C2GraphicView::C2GraphicView;
- friend class ::android::C2ConstGraphicBlock;
- friend class ::android::C2GraphicBlock;
+ friend class ::C2ConstGraphicBlock;
+ friend class ::C2GraphicBlock;
};
class AcquirableConstGraphicViewBuddy : public C2Acquirable<const C2GraphicView> {
using C2Acquirable::C2Acquirable;
- friend class ::android::C2ConstGraphicBlock;
+ friend class ::C2ConstGraphicBlock;
};
class AcquirableGraphicViewBuddy : public C2Acquirable<C2GraphicView> {
using C2Acquirable::C2Acquirable;
- friend class ::android::C2GraphicBlock;
+ friend class ::C2GraphicBlock;
};
class ConstGraphicBlockBuddy : public C2ConstGraphicBlock {
using C2ConstGraphicBlock::C2ConstGraphicBlock;
- friend class ::android::C2GraphicBlock;
+ friend class ::C2GraphicBlock;
};
class GraphicBlockBuddy : public C2GraphicBlock {
using C2GraphicBlock::C2GraphicBlock;
- friend class ::android::C2BasicGraphicBlockPool;
+ friend class ::C2BasicGraphicBlockPool;
};
class BufferDataBuddy : public C2BufferData {
using C2BufferData::C2BufferData;
- friend class ::android::C2Buffer;
+ friend class ::C2Buffer;
};
} // namespace
@@ -803,4 +802,3 @@
return std::shared_ptr<C2Buffer>(new C2Buffer({ block }));
}
-} // namespace android
diff --git a/media/libstagefright/codec2/vndk/C2Store.cpp b/media/libstagefright/codec2/vndk/C2Store.cpp
index 05b46c3..c4ed2f4 100644
--- a/media/libstagefright/codec2/vndk/C2Store.cpp
+++ b/media/libstagefright/codec2/vndk/C2Store.cpp
@@ -136,13 +136,13 @@
switch (id) {
case C2BlockPool::BASIC_LINEAR:
res = allocatorStore->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &allocator);
- if (res == OK) {
+ if (res == C2_OK) {
*pool = std::make_shared<C2BasicLinearBlockPool>(allocator);
}
break;
case C2BlockPool::BASIC_GRAPHIC:
res = allocatorStore->fetchAllocator(C2AllocatorStore::DEFAULT_GRAPHIC, &allocator);
- if (res == OK) {
+ if (res == C2_OK) {
*pool = std::make_shared<C2BasicGraphicBlockPool>(allocator);
}
break;
@@ -345,7 +345,7 @@
c2_status_t C2PlatformComponentStore::ComponentModule::createInterface(
c2_node_id_t id, std::shared_ptr<C2ComponentInterface> *interface,
- std::function<void(::android::C2ComponentInterface*)> deleter) {
+ std::function<void(::C2ComponentInterface*)> deleter) {
interface->reset();
if (mInit != C2_OK) {
return mInit;
@@ -362,7 +362,7 @@
c2_status_t C2PlatformComponentStore::ComponentModule::createComponent(
c2_node_id_t id, std::shared_ptr<C2Component> *component,
- std::function<void(::android::C2Component*)> deleter) {
+ std::function<void(::C2Component*)> deleter) {
component->reset();
if (mInit != C2_OK) {
return mInit;
diff --git a/media/libstagefright/codec2/vndk/include/C2BufferPriv.h b/media/libstagefright/codec2/vndk/include/C2BufferPriv.h
index 875a8c2..977cf7b 100644
--- a/media/libstagefright/codec2/vndk/include/C2BufferPriv.h
+++ b/media/libstagefright/codec2/vndk/include/C2BufferPriv.h
@@ -21,8 +21,6 @@
#include <C2Buffer.h>
-namespace android {
-
class C2BasicLinearBlockPool : public C2BlockPool {
public:
explicit C2BasicLinearBlockPool(const std::shared_ptr<C2Allocator> &allocator);
@@ -73,6 +71,4 @@
const std::shared_ptr<C2Allocator> mAllocator;
};
-} // namespace android
-
#endif // STAGEFRIGHT_CODEC2_BUFFER_PRIV_H_
diff --git a/media/libstagefright/codec2/vndk/include/C2ComponentFactory.h b/media/libstagefright/codec2/vndk/include/C2ComponentFactory.h
index cfea104..7168498 100644
--- a/media/libstagefright/codec2/vndk/include/C2ComponentFactory.h
+++ b/media/libstagefright/codec2/vndk/include/C2ComponentFactory.h
@@ -22,8 +22,6 @@
#include <functional>
#include <memory>
-namespace android {
-
/**
* Component factory object that enables to create a component and/or interface from a dynamically
* linked library. This is needed because the component/interfaces are managed objects, but we
@@ -36,8 +34,8 @@
*/
class C2ComponentFactory {
public:
- typedef std::function<void(::android::C2Component*)> ComponentDeleter;
- typedef std::function<void(::android::C2ComponentInterface*)> InterfaceDeleter;
+ typedef std::function<void(::C2Component*)> ComponentDeleter;
+ typedef std::function<void(::C2ComponentInterface*)> InterfaceDeleter;
/**
* Creates a component.
@@ -81,9 +79,12 @@
virtual ~C2ComponentFactory() = default;
- typedef ::android::C2ComponentFactory* (*CreateCodec2FactoryFunc)(void);
- typedef void (*DestroyCodec2FactoryFunc)(::android::C2ComponentFactory*);
+ typedef ::C2ComponentFactory* (*CreateCodec2FactoryFunc)(void);
+ typedef void (*DestroyCodec2FactoryFunc)(::C2ComponentFactory*);
};
-} // namespace android
+
+namespace android {
+ typedef ::C2ComponentFactory C2ComponentFactory;
+}
#endif // STAGEFRIGHT_CODEC2_COMPONENT_FACTORY_H_
diff --git a/media/libstagefright/codec2/vndk/include/C2ErrnoUtils.h b/media/libstagefright/codec2/vndk/include/C2ErrnoUtils.h
index 41132b9..5b995f6 100644
--- a/media/libstagefright/codec2/vndk/include/C2ErrnoUtils.h
+++ b/media/libstagefright/codec2/vndk/include/C2ErrnoUtils.h
@@ -20,8 +20,6 @@
#include <errno.h>
#include <C2.h>
-namespace android {
-
// standard ERRNO mappings
template<int N> constexpr c2_status_t _c2_errno2status_impl();
template<> constexpr c2_status_t _c2_errno2status_impl<0>() { return C2_OK; }
@@ -52,7 +50,5 @@
return _c2_map_errno_impl<N...>::map(result);
}
-} // namespace android
-
#endif // STAGEFRIGHT_CODEC2_ERRNO_UTILS_H_
diff --git a/media/libstagefright/codec2/vndk/include/C2PlatformSupport.h b/media/libstagefright/codec2/vndk/include/C2PlatformSupport.h
index 76b02ed..afa51ee 100644
--- a/media/libstagefright/codec2/vndk/include/C2PlatformSupport.h
+++ b/media/libstagefright/codec2/vndk/include/C2PlatformSupport.h
@@ -86,6 +86,7 @@
* \retval nullptr if the platform component store could not be obtained
*/
std::shared_ptr<C2ComponentStore> GetCodec2PlatformComponentStore();
+
} // namespace android
#endif // STAGEFRIGHT_CODEC2_PLATFORM_SUPPORT_H_
diff --git a/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h b/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h
index 3168248..1accc2c 100644
--- a/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h
+++ b/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h
@@ -21,13 +21,14 @@
#include <util/_C2MacroUtils.h>
#include <iostream>
+#include <list>
+#include <utility>
+#include <vector>
/** \file
* Utilities for parameter handling to be used by Codec2 implementations.
*/
-namespace android {
-
/// \cond INTERNAL
/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */
@@ -313,7 +314,5 @@
/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */
-} // namespace android
-
#endif // C2UTILS_PARAM_UTILS_H_
diff --git a/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h b/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h
index 9c68369..25003cb 100644
--- a/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h
+++ b/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h
@@ -19,8 +19,6 @@
#include <C2Buffer.h>
-namespace android {
-
struct _C2BlockPoolData;
/**
@@ -64,7 +62,5 @@
const C2Rect &allottedCrop = C2Rect(~0u, ~0u));
};
-}
-
#endif // ANDROID_STAGEFRIGHT_C2BLOCK_INTERNAL_H_
diff --git a/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h b/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h
index 5bf3009..c805830 100644
--- a/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h
+++ b/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h
@@ -19,8 +19,6 @@
#include <C2Param.h>
-namespace android {
-
struct C2_HIDE _C2ParamInspector {
inline static uint32_t getIndex(const C2ParamField &pf) {
return pf._mIndex;
@@ -44,7 +42,6 @@
}
};
-}
#endif // ANDROID_STAGEFRIGHT_C2PARAM_INTERNAL_H_
diff --git a/media/libstagefright/codecs/cmds/codec2.cpp b/media/libstagefright/codecs/cmds/codec2.cpp
index 8022b84..95b0c61 100644
--- a/media/libstagefright/codecs/cmds/codec2.cpp
+++ b/media/libstagefright/codecs/cmds/codec2.cpp
@@ -59,11 +59,6 @@
#include <C2PlatformSupport.h>
#include <C2Work.h>
-extern "C" ::android::C2ComponentFactory *CreateCodec2Factory();
-extern "C" void DestroyCodec2Factory(::android::C2ComponentFactory *);
-
-#include "../avcdec/C2SoftAvcDec.h"
-
using namespace android;
using namespace std::chrono_literals;
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
index 9538c3d..63c0697 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
@@ -37,7 +37,7 @@
// not to expose other methods to the controller whose connection wasn't accepted.
// But this would be enough for now because it's the same as existing
// MediaBrowser and MediaBrowserService.
- void connect(String callingPackage, IMediaSession2Callback callback);
+ void connect(IMediaSession2Callback caller, String callingPackage);
void release(IMediaSession2Callback caller);
void setVolumeTo(IMediaSession2Callback caller, int value, int flags);
@@ -52,21 +52,24 @@
void sendCustomCommand(IMediaSession2Callback caller, in Bundle command, in Bundle args,
in ResultReceiver receiver);
- void prepareFromUri(IMediaSession2Callback caller, in Uri uri, in Bundle extra);
- void prepareFromSearch(IMediaSession2Callback caller, String query, in Bundle extra);
- void prepareFromMediaId(IMediaSession2Callback caller, String mediaId, in Bundle extra);
- void playFromUri(IMediaSession2Callback caller, in Uri uri, in Bundle extra);
- void playFromSearch(IMediaSession2Callback caller, String query, in Bundle extra);
- void playFromMediaId(IMediaSession2Callback caller, String mediaId, in Bundle extra);
+ void prepareFromUri(IMediaSession2Callback caller, in Uri uri, in Bundle extras);
+ void prepareFromSearch(IMediaSession2Callback caller, String query, in Bundle extras);
+ void prepareFromMediaId(IMediaSession2Callback caller, String mediaId, in Bundle extras);
+ void playFromUri(IMediaSession2Callback caller, in Uri uri, in Bundle extras);
+ void playFromSearch(IMediaSession2Callback caller, String query, in Bundle extras);
+ void playFromMediaId(IMediaSession2Callback caller, String mediaId, in Bundle extras);
+ void setRating(IMediaSession2Callback caller, String mediaId, in Bundle rating);
- //////////////////////////////////////////////////////////////////////////////////////////////
- // Get library service specific
//////////////////////////////////////////////////////////////////////////////////////////////
- void getBrowserRoot(IMediaSession2Callback callback, in Bundle rootHints);
- void getItem(IMediaSession2Callback callback, String mediaId);
- void getChildren(IMediaSession2Callback callback, String parentId, int page, int pageSize,
+ // library service specific
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ void getBrowserRoot(IMediaSession2Callback caller, in Bundle rootHints);
+ void getItem(IMediaSession2Callback caller, String mediaId);
+ void getChildren(IMediaSession2Callback caller, String parentId, int page, int pageSize,
in Bundle extras);
- void search(IMediaSession2Callback callback, String query, in Bundle extras);
- void getSearchResult(IMediaSession2Callback callback, String query, int page, int pageSize,
+ void search(IMediaSession2Callback caller, String query, in Bundle extras);
+ void getSearchResult(IMediaSession2Callback caller, String query, int page, int pageSize,
in Bundle extras);
+ void subscribe(IMediaSession2Callback caller, String parentId, in Bundle extras);
+ void unsubscribe(IMediaSession2Callback caller, String parentId);
}
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
index b3aa59c..9a0be7a 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
@@ -36,7 +36,7 @@
// TODO(jaewan): Handle when the playlist becomes too huge.
void onConnected(IMediaSession2 sessionBinder, in Bundle commandGroup, in Bundle playbackState,
- in Bundle playbackInfo, in Bundle params, in List<Bundle> playlist, int ratingType,
+ in Bundle playbackInfo, in Bundle params, in List<Bundle> playlist,
in PendingIntent sessionActivity);
void onDisconnected();
@@ -49,8 +49,10 @@
//////////////////////////////////////////////////////////////////////////////////////////////
void onGetRootResult(in Bundle rootHints, String rootMediaId, in Bundle rootExtra);
void onItemLoaded(String mediaId, in Bundle result);
- void onChildrenLoaded(String parentId, int page, int pageSize, in Bundle extras,
- in List<Bundle> result);
- void onSearchResultLoaded(String query, int page, int pageSize, in Bundle extras,
- in List<Bundle> result);
+ void onChildrenChanged(String rootMediaId, int childCount, in Bundle extras);
+ void onChildrenLoaded(String parentId, int page, int pageSize, in List<Bundle> result,
+ in Bundle extras);
+ void onSearchResultChanged(String query, int itemCount, in Bundle extras);
+ void onSearchResultLoaded(String query, int page, int pageSize, in List<Bundle> result,
+ in Bundle extras);
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
index 76da42b..c095187 100644
--- a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
@@ -20,7 +20,6 @@
import android.media.MediaBrowser2;
import android.media.MediaBrowser2.BrowserCallback;
import android.media.MediaItem2;
-import android.media.MediaSession2.CommandButton;
import android.media.SessionToken2;
import android.media.update.MediaBrowser2Provider;
import android.os.Bundle;
@@ -64,12 +63,36 @@
@Override
public void subscribe_impl(String parentId, Bundle extras) {
- // TODO(jaewan): Implement
+ final IMediaSession2 binder = getSessionBinder();
+ if (binder != null) {
+ try {
+ binder.subscribe(getControllerStub(), parentId, extras);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle disconnect.
+ if (DEBUG) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ }
+ } else {
+ Log.w(TAG, "Session isn't active", new IllegalStateException());
+ }
}
@Override
- public void unsubscribe_impl(String parentId, Bundle extras) {
- // TODO(jaewan): Implement
+ public void unsubscribe_impl(String parentId) {
+ final IMediaSession2 binder = getSessionBinder();
+ if (binder != null) {
+ try {
+ binder.unsubscribe(getControllerStub(), parentId);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle disconnect.
+ if (DEBUG) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ }
+ } else {
+ Log.w(TAG, "Session isn't active", new IllegalStateException());
+ }
}
@Override
@@ -170,17 +193,29 @@
});
}
- public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle extras,
- List<MediaItem2> result) {
+ public void onChildrenLoaded(String parentId, int page, int pageSize, List<MediaItem2> result,
+ Bundle extras) {
getCallbackExecutor().execute(() -> {
- mCallback.onChildrenLoaded(parentId, page, pageSize, extras, result);
+ mCallback.onChildrenLoaded(parentId, page, pageSize, result, extras);
});
}
- public void onSearchResultLoaded(String query, int page, int pageSize, Bundle extras,
- List<MediaItem2> result) {
+ public void onSearchResultChanged(String query, int itemCount, Bundle extras) {
getCallbackExecutor().execute(() -> {
- mCallback.onSearchResultLoaded(query, page, pageSize, extras, result);
+ mCallback.onSearchResultChanged(query, itemCount, extras);
+ });
+ }
+
+ public void onSearchResultLoaded(String query, int page, int pageSize, List<MediaItem2> result,
+ Bundle extras) {
+ getCallbackExecutor().execute(() -> {
+ mCallback.onSearchResultLoaded(query, page, pageSize, result, extras);
+ });
+ }
+
+ public void onChildrenChanged(final String parentId, int childCount, final Bundle extras) {
+ getCallbackExecutor().execute(() -> {
+ mCallback.onChildrenChanged(parentId, childCount, extras);
});
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 5af4240..77db355 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -75,8 +75,6 @@
@GuardedBy("mLock")
private PlaybackInfo mPlaybackInfo;
@GuardedBy("mLock")
- private int mRatingType;
- @GuardedBy("mLock")
private PendingIntent mSessionActivity;
@GuardedBy("mLock")
private CommandGroup mCommandGroup;
@@ -164,7 +162,7 @@
private void connectToSession(IMediaSession2 sessionBinder) {
try {
- sessionBinder.connect(mContext.getPackageName(), mSessionCallbackStub);
+ sessionBinder.connect(mSessionCallbackStub, mContext.getPackageName());
} catch (RemoteException e) {
Log.w(TAG, "Failed to call connection request. Framework will retry"
+ " automatically");
@@ -286,11 +284,6 @@
}
@Override
- public int getRatingType_impl() {
- return mRatingType;
- }
-
- @Override
public void setVolumeTo_impl(int value, int flags) {
// TODO(hdmoon): sanity check
final IMediaSession2 binder = mSessionBinder;
@@ -403,9 +396,26 @@
// TODO(jaewan): Handle.
}
}
+
@Override
- public void setRating_impl(Rating2 rating) {
- // TODO(jaewan): Implement
+ public void setRating_impl(String mediaId, Rating2 rating) {
+ if (mediaId == null) {
+ throw new IllegalArgumentException("mediaId shouldn't be null");
+ }
+ if (rating == null) {
+ throw new IllegalArgumentException("rating shouldn't be null");
+ }
+
+ final IMediaSession2 binder = mSessionBinder;
+ if (binder != null) {
+ try {
+ binder.setRating(mSessionCallbackStub, mediaId, rating.toBundle());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ } else {
+ // TODO(jaewan): Handle.
+ }
}
@Override
@@ -563,7 +573,7 @@
// Should be used without a lock to prevent potential deadlock.
void onConnectedNotLocked(IMediaSession2 sessionBinder,
final CommandGroup commandGroup, final PlaybackState2 state, final PlaybackInfo info,
- final PlaylistParams params, final List<MediaItem2> playlist, final int ratingType,
+ final PlaylistParams params, final List<MediaItem2> playlist,
final PendingIntent sessionActivity) {
if (DEBUG) {
Log.d(TAG, "onConnectedNotLocked sessionBinder=" + sessionBinder
@@ -591,7 +601,6 @@
mPlaybackInfo = info;
mPlaylistParams = params;
mPlaylist = playlist;
- mRatingType = ratingType;
mSessionActivity = sessionActivity;
mSessionBinder = sessionBinder;
try {
diff --git a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
index 52db74e..b9d2fa4 100644
--- a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
@@ -26,12 +26,12 @@
import android.media.MediaPlayerInterface;
import android.media.MediaSession2;
import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.SessionCallback;
import android.media.MediaSessionService2;
import android.media.SessionToken2;
import android.media.VolumeProvider2;
import android.media.update.MediaLibraryService2Provider;
import android.os.Bundle;
+import android.text.TextUtils;
import com.android.media.MediaSession2Impl.BuilderBaseImpl;
@@ -68,10 +68,9 @@
implements MediaLibrarySessionProvider {
public MediaLibrarySessionImpl(Context context,
MediaPlayerInterface player, String id, VolumeProvider2 volumeProvider,
- int ratingType, PendingIntent sessionActivity, Executor callbackExecutor,
+ PendingIntent sessionActivity, Executor callbackExecutor,
MediaLibrarySessionCallback callback) {
- super(context, player, id, volumeProvider, ratingType, sessionActivity,
- callbackExecutor, callback);
+ super(context, player, id, volumeProvider, sessionActivity, callbackExecutor, callback);
// Don't put any extra initialization here. Here's the reason.
// System service will recognize this session inside of the super constructor and would
// connect to this session assuming that initialization is finished. However, if any
@@ -96,13 +95,36 @@
@Override
public void notifyChildrenChanged_impl(ControllerInfo controller, String parentId,
- Bundle options) {
- // TODO(jaewan): Implements
+ int childCount, Bundle extras) {
+ if (controller == null) {
+ throw new IllegalArgumentException("controller shouldn't be null");
+ }
+ if (parentId == null) {
+ throw new IllegalArgumentException("parentId shouldn't be null");
+ }
+ getSessionStub().notifyChildrenChangedNotLocked(controller, parentId, childCount,
+ extras);
}
@Override
- public void notifyChildrenChanged_impl(String parentId, Bundle options) {
- // TODO(jaewan): Implements
+ public void notifyChildrenChanged_impl(String parentId, int childCount, Bundle extras) {
+ if (parentId == null) {
+ throw new IllegalArgumentException("parentId shouldn't be null");
+ }
+ getSessionStub().notifyChildrenChangedNotLocked(parentId, childCount, extras);
+ }
+
+ @Override
+ public void notifySearchResultChanged_impl(ControllerInfo controller, String query,
+ int itemCount, Bundle extras) {
+ ensureCallingThread();
+ if (controller == null) {
+ throw new IllegalArgumentException("controller shouldn't be null");
+ }
+ if (TextUtils.isEmpty(query)) {
+ throw new IllegalArgumentException("query shouldn't be empty");
+ }
+ getSessionStub().notifySearchResultChanged(controller, query, itemCount, extras);
}
}
@@ -117,7 +139,7 @@
@Override
public MediaLibrarySession build_impl() {
- return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mVolumeProvider, mRatingType,
+ return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mVolumeProvider,
mSessionActivity, mCallbackExecutor, mCallback).getInstance();
}
}
@@ -148,4 +170,4 @@
return mExtras;
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
index 852029a..e174d91 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
@@ -125,7 +125,7 @@
@Override
public void onConnected(IMediaSession2 sessionBinder, Bundle commandGroup,
Bundle playbackState, Bundle playbackInfo, Bundle playlistParams, List<Bundle>
- playlist, int ratingType, PendingIntent sessionActivity) {
+ playlist, PendingIntent sessionActivity) {
final MediaController2Impl controller = mController.get();
if (controller == null) {
if (DEBUG) {
@@ -146,7 +146,7 @@
PlaybackState2.fromBundle(context, playbackState),
PlaybackInfoImpl.fromBundle(context, playbackInfo),
PlaylistParams.fromBundle(context, playlistParams),
- list, ratingType, sessionActivity);
+ list, sessionActivity);
}
@Override
@@ -244,8 +244,8 @@
}
@Override
- public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle extras,
- List<Bundle> itemBundleList) throws RuntimeException {
+ public void onChildrenLoaded(String parentId, int page, int pageSize,
+ List<Bundle> itemBundleList, Bundle extras) throws RuntimeException {
final MediaBrowser2Impl browser;
try {
browser = getBrowser();
@@ -265,12 +265,29 @@
result.add(MediaItem2.fromBundle(browser.getContext(), bundle));
}
}
- browser.onChildrenLoaded(parentId, page, pageSize, extras, result);
+ browser.onChildrenLoaded(parentId, page, pageSize, result, extras);
}
@Override
- public void onSearchResultLoaded(String query, int page, int pageSize, Bundle extras,
- List<Bundle> itemBundleList) throws RuntimeException {
+ public void onSearchResultChanged(String query, int itemCount, Bundle extras)
+ throws RuntimeException {
+ final MediaBrowser2Impl browser;
+ try {
+ browser = getBrowser();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ if (browser == null) {
+ // TODO(jaewan): Revisit here. Could be a bug
+ return;
+ }
+ browser.onSearchResultChanged(query, itemCount, extras);
+ }
+
+ @Override
+ public void onSearchResultLoaded(String query, int page, int pageSize,
+ List<Bundle> itemBundleList, Bundle extras) throws RuntimeException {
final MediaBrowser2Impl browser;
try {
browser = getBrowser();
@@ -290,6 +307,22 @@
result.add(MediaItem2.fromBundle(browser.getContext(), bundle));
}
}
- browser.onSearchResultLoaded(query, page, pageSize, extras, result);
+ browser.onSearchResultLoaded(query, page, pageSize, result, extras);
+ }
+
+ @Override
+ public void onChildrenChanged(String parentId, int childCount, Bundle extras) {
+ final MediaBrowser2Impl browser;
+ try {
+ browser = getBrowser();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ if (browser == null) {
+ // TODO(jaewan): Revisit here. Could be a bug
+ return;
+ }
+ browser.onChildrenChanged(parentId, childCount, extras);
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 4a9a729..a32ea0a 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -38,7 +38,7 @@
import android.media.MediaLibraryService2;
import android.media.MediaMetadata2;
import android.media.MediaPlayerInterface;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
import android.media.MediaSession2;
import android.media.MediaSession2.Builder;
import android.media.MediaSession2.Command;
@@ -62,6 +62,7 @@
import android.os.ResultReceiver;
import android.support.annotation.GuardedBy;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -84,8 +85,7 @@
private final MediaSession2Stub mSessionStub;
private final SessionToken2 mSessionToken;
private final AudioManager mAudioManager;
- private final List<PlaybackListenerHolder> mListeners = new ArrayList<>();
- private final int mRatingType;
+ private final ArrayMap<EventCallback, Executor> mCallbacks = new ArrayMap<>();
private final PendingIntent mSessionActivity;
// mPlayer is set to null when the session is closed, and we shouldn't throw an exception
@@ -111,7 +111,7 @@
@GuardedBy("mLock")
private PlaybackInfo mPlaybackInfo;
@GuardedBy("mLock")
- private MyPlaybackListener mListener;
+ private MyEventCallback mEventCallback;
/**
* Can be only called by the {@link Builder#build()}.
@@ -119,13 +119,13 @@
* @param context
* @param player
* @param id
- * @param callback
* @param volumeProvider
- * @param ratingType
* @param sessionActivity
+ * @param callbackExecutor
+ * @param callback
*/
public MediaSession2Impl(Context context, MediaPlayerInterface player, String id,
- VolumeProvider2 volumeProvider, int ratingType, PendingIntent sessionActivity,
+ VolumeProvider2 volumeProvider, PendingIntent sessionActivity,
Executor callbackExecutor, SessionCallback callback) {
// TODO(jaewan): Keep other params.
mInstance = createInstance();
@@ -136,7 +136,6 @@
mId = id;
mCallback = callback;
mCallbackExecutor = callbackExecutor;
- mRatingType = ratingType;
mSessionActivity = sessionActivity;
mSessionStub = new MediaSession2Stub(this);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -225,19 +224,20 @@
}
private void setPlayer(MediaPlayerInterface player, VolumeProvider2 volumeProvider) {
- PlaybackInfo info = createPlaybackInfo(volumeProvider, player.getAudioAttributes());
+ final PlaybackInfo info = createPlaybackInfo(volumeProvider, player.getAudioAttributes());
synchronized (mLock) {
- if (mPlayer != null && mListener != null) {
+ if (mPlayer != null && mEventCallback != null) {
// This might not work for a poorly implemented player.
- mPlayer.removePlaybackListener(mListener);
+ mPlayer.unregisterEventCallback(mEventCallback);
}
mPlayer = player;
- mListener = new MyPlaybackListener(this, player);
- player.addPlaybackListener(mCallbackExecutor, mListener);
+ mEventCallback = new MyEventCallback(this, player);
+ player.registerEventCallback(mCallbackExecutor, mEventCallback);
mVolumeProvider = volumeProvider;
mPlaybackInfo = info;
}
mSessionStub.notifyPlaybackInfoChanged(info);
+ notifyPlaybackStateChangedNotLocked(mInstance.getPlaybackState());
}
private PlaybackInfo createPlaybackInfo(VolumeProvider2 volumeProvider, AudioAttributes attrs) {
@@ -293,7 +293,7 @@
synchronized (mLock) {
if (mPlayer != null) {
// close can be called multiple times
- mPlayer.removePlaybackListener(mListener);
+ mPlayer.unregisterEventCallback(mEventCallback);
mPlayer = null;
}
}
@@ -522,32 +522,31 @@
}
@Override
- public void addPlaybackListener_impl(Executor executor, PlaybackListener listener) {
+ public void registerPlayerEventCallback_impl(Executor executor, EventCallback callback) {
if (executor == null) {
throw new IllegalArgumentException("executor shouldn't be null");
}
- if (listener == null) {
- throw new IllegalArgumentException("listener shouldn't be null");
+ if (callback == null) {
+ throw new IllegalArgumentException("callback shouldn't be null");
}
ensureCallingThread();
- if (PlaybackListenerHolder.contains(mListeners, listener)) {
- Log.w(TAG, "listener is already added. Ignoring.");
+ if (mCallbacks.get(callback) != null) {
+ Log.w(TAG, "callback is already added. Ignoring.");
return;
}
- mListeners.add(new PlaybackListenerHolder(executor, listener));
- executor.execute(() -> listener.onPlaybackChanged(getInstance().getPlaybackState()));
+ mCallbacks.put(callback, executor);
+ // TODO(jaewan): Double check if we need this.
+ final PlaybackState2 state = getInstance().getPlaybackState();
+ executor.execute(() -> callback.onPlaybackStateChanged(state));
}
@Override
- public void removePlaybackListener_impl(PlaybackListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException("listener shouldn't be null");
+ public void unregisterPlayerEventCallback_impl(EventCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback shouldn't be null");
}
ensureCallingThread();
- int idx = PlaybackListenerHolder.indexOf(mListeners, listener);
- if (idx >= 0) {
- mListeners.remove(idx);
- }
+ mCallbacks.remove(callback);
}
@Override
@@ -580,7 +579,7 @@
// 1. Allow calls from random threads for all methods.
// 2. Allow calls from random threads for all methods, except for the
// {@link #setPlayer()}.
- private void ensureCallingThread() {
+ void ensureCallingThread() {
// TODO(jaewan): Uncomment or remove
/*
if (mHandler.getLooper() != Looper.myLooper()) {
@@ -588,19 +587,35 @@
}*/
}
- private void notifyPlaybackStateChangedNotLocked(PlaybackState2 state) {
- List<PlaybackListenerHolder> listeners = new ArrayList<>();
+ private void notifyPlaybackStateChangedNotLocked(final PlaybackState2 state) {
+ ArrayMap<EventCallback, Executor> callbacks = new ArrayMap<>();
synchronized (mLock) {
- listeners.addAll(mListeners);
+ callbacks.putAll(mCallbacks);
}
- // Notify to listeners added directly to this session
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).postPlaybackChange(state);
+ // Notify to callbacks added directly to this session
+ for (int i = 0; i < callbacks.size(); i++) {
+ final EventCallback callback = callbacks.keyAt(i);
+ final Executor executor = callbacks.valueAt(i);
+ executor.execute(() -> callback.onPlaybackStateChanged(state));
}
// Notify to controllers as well.
mSessionStub.notifyPlaybackStateChangedNotLocked(state);
}
+ private void notifyErrorNotLocked(String mediaId, int what, int extra) {
+ ArrayMap<EventCallback, Executor> callbacks = new ArrayMap<>();
+ synchronized (mLock) {
+ callbacks.putAll(mCallbacks);
+ }
+ // Notify to callbacks added directly to this session
+ for (int i = 0; i < callbacks.size(); i++) {
+ final EventCallback callback = callbacks.keyAt(i);
+ final Executor executor = callbacks.valueAt(i);
+ executor.execute(() -> callback.onError(mediaId, what, extra));
+ }
+ // TODO(jaewan): Notify to controllers as well.
+ }
+
Context getContext() {
return mContext;
}
@@ -621,6 +636,10 @@
return mCallback;
}
+ MediaSession2Stub getSessionStub() {
+ return mSessionStub;
+ }
+
VolumeProvider2 getVolumeProvider() {
return mVolumeProvider;
}
@@ -631,33 +650,47 @@
}
}
- int getRatingType() {
- return mRatingType;
- }
-
PendingIntent getSessionActivity() {
return mSessionActivity;
}
- private static class MyPlaybackListener implements MediaPlayerInterface.PlaybackListener {
+ private static class MyEventCallback implements EventCallback {
private final WeakReference<MediaSession2Impl> mSession;
private final MediaPlayerInterface mPlayer;
- private MyPlaybackListener(MediaSession2Impl session, MediaPlayerInterface player) {
+ private MyEventCallback(MediaSession2Impl session, MediaPlayerInterface player) {
mSession = new WeakReference<>(session);
mPlayer = player;
}
@Override
- public void onPlaybackChanged(PlaybackState2 state) {
+ public void onPlaybackStateChanged(PlaybackState2 state) {
MediaSession2Impl session = mSession.get();
if (mPlayer != session.mInstance.getPlayer()) {
Log.w(TAG, "Unexpected playback state change notifications. Ignoring.",
new IllegalStateException());
return;
}
+ if (DEBUG) {
+ Log.d(TAG, "onPlaybackStateChanged from player, state=" + state);
+ }
session.notifyPlaybackStateChangedNotLocked(state);
}
+
+ @Override
+ public void onError(String mediaId, int what, int extra) {
+ MediaSession2Impl session = mSession.get();
+ if (mPlayer != session.mInstance.getPlayer()) {
+ Log.w(TAG, "Unexpected playback state change notifications. Ignoring.",
+ new IllegalStateException());
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "onError from player, mediaId=" + mediaId + ", what=" + what
+ + ", extra=" + extra);
+ }
+ session.notifyErrorNotLocked(mediaId, what, extra);
+ }
}
public static final class CommandImpl implements CommandProvider {
@@ -1167,7 +1200,6 @@
Executor mCallbackExecutor;
C mCallback;
VolumeProvider2 mVolumeProvider;
- int mRatingType;
PendingIntent mSessionActivity;
/**
@@ -1196,10 +1228,6 @@
mVolumeProvider = volumeProvider;
}
- public void setRatingType_impl(int type) {
- mRatingType = type;
- }
-
public void setSessionActivity_impl(PendingIntent pi) {
mSessionActivity = pi;
}
@@ -1239,7 +1267,7 @@
mCallback = new SessionCallback(mContext);
}
- return new MediaSession2Impl(mContext, mPlayer, mId, mVolumeProvider, mRatingType,
+ return new MediaSession2Impl(mContext, mPlayer, mId, mVolumeProvider,
mSessionActivity, mCallbackExecutor, mCallback).getInstance();
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index 759d580..914f85e 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -28,6 +28,7 @@
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.PlaylistParams;
import android.media.PlaybackState2;
+import android.media.Rating2;
import android.media.VolumeProvider2;
import android.net.Uri;
import android.os.Binder;
@@ -47,6 +48,8 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
public class MediaSession2Stub extends IMediaSession2.Stub {
@@ -63,6 +66,8 @@
@GuardedBy("mLock")
private final ArrayMap<IBinder, ControllerInfo> mControllers = new ArrayMap<>();
+ @GuardedBy("mLock")
+ private final ArrayMap<ControllerInfo, Set<String>> mSubscriptions = new ArrayMap<>();
public MediaSession2Stub(MediaSession2Impl session) {
mSession = new WeakReference<>(session);
@@ -76,11 +81,11 @@
mControllers.clear();
}
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
((ControllerInfoImpl) list.get(i).getProvider()).getControllerBinder();
try {
// Should be used without a lock hold to prevent potential deadlock.
- callbackBinder.onDisconnected();
+ controllerBinder.onDisconnected();
} catch (RemoteException e) {
// Controller is gone. Should be fine because we're destroying.
}
@@ -117,12 +122,12 @@
//////////////////////////////////////////////////////////////////////////////////////////////
@Override
- public void connect(String callingPackage, final IMediaSession2Callback callback)
+ public void connect(final IMediaSession2Callback caller, String callingPackage)
throws RuntimeException {
final MediaSession2Impl sessionImpl = getSession();
final Context context = sessionImpl.getContext();
final ControllerInfo request = new ControllerInfo(context,
- Binder.getCallingUid(), Binder.getCallingPid(), callingPackage, callback);
+ Binder.getCallingUid(), Binder.getCallingPid(), callingPackage, caller);
sessionImpl.getCallbackExecutor().execute(() -> {
final MediaSession2Impl session = mSession.get();
if (session == null) {
@@ -134,7 +139,7 @@
// media keys to.
boolean accept = allowedCommands != null || request.isTrusted();
ControllerInfoImpl impl = ControllerInfoImpl.from(request);
- if (accept && allowedCommands != null) {
+ if (accept) {
if (DEBUG) {
Log.d(TAG, "Accepting connection, request=" + request
+ " allowedCommands=" + allowedCommands);
@@ -155,11 +160,10 @@
// TODO(jaewan): Should we protect getting playback state?
final PlaybackState2 state = session.getInstance().getPlaybackState();
final Bundle playbackStateBundle = (state != null) ? state.toBundle() : null;
- final Bundle playbackInfoBundle =
- ((MediaController2Impl.PlaybackInfoImpl) session.getPlaybackInfo().getProvider()).toBundle();
+ final Bundle playbackInfoBundle = ((MediaController2Impl.PlaybackInfoImpl)
+ session.getPlaybackInfo().getProvider()).toBundle();
final PlaylistParams params = session.getInstance().getPlaylistParams();
final Bundle paramsBundle = (params != null) ? params.toBundle() : null;
- final int ratingType = session.getRatingType();
final PendingIntent sessionActivity = session.getSessionActivity();
final List<MediaItem2> playlist = session.getInstance().getPlaylist();
final List<Bundle> playlistBundle = new ArrayList<>();
@@ -182,9 +186,9 @@
return;
}
try {
- callback.onConnected(MediaSession2Stub.this,
+ caller.onConnected(MediaSession2Stub.this,
allowedCommands.toBundle(), playbackStateBundle, playbackInfoBundle,
- paramsBundle, playlistBundle, ratingType, sessionActivity);
+ paramsBundle, playlistBundle, sessionActivity);
} catch (RemoteException e) {
// Controller may be died prematurely.
// TODO(jaewan): Handle here.
@@ -194,7 +198,7 @@
Log.d(TAG, "Rejecting connection, request=" + request);
}
try {
- callback.onDisconnected();
+ caller.onDisconnected();
} catch (RemoteException e) {
// Controller may be died prematurely.
// Not an issue because we'll ignore it anyway.
@@ -210,6 +214,7 @@
if (DEBUG) {
Log.d(TAG, "releasing " + controllerInfo);
}
+ mSubscriptions.remove(controllerInfo);
}
}
@@ -389,7 +394,7 @@
@Override
public void prepareFromUri(final IMediaSession2Callback caller, final Uri uri,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -403,13 +408,13 @@
if (session == null) {
return;
}
- session.getCallback().onPrepareFromUri(controller, uri, extra);
+ session.getCallback().onPrepareFromUri(controller, uri, extras);
});
}
@Override
public void prepareFromSearch(final IMediaSession2Callback caller, final String query,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -423,13 +428,13 @@
if (session == null) {
return;
}
- session.getCallback().onPrepareFromSearch(controller, query, extra);
+ session.getCallback().onPrepareFromSearch(controller, query, extras);
});
}
@Override
public void prepareFromMediaId(final IMediaSession2Callback caller, final String mediaId,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -443,13 +448,13 @@
if (session == null) {
return;
}
- session.getCallback().onPrepareFromMediaId(controller, mediaId, extra);
+ session.getCallback().onPrepareFromMediaId(controller, mediaId, extras);
});
}
@Override
public void playFromUri(final IMediaSession2Callback caller, final Uri uri,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -463,13 +468,13 @@
if (session == null) {
return;
}
- session.getCallback().onPlayFromUri(controller, uri, extra);
+ session.getCallback().onPlayFromUri(controller, uri, extras);
});
}
@Override
public void playFromSearch(final IMediaSession2Callback caller, final String query,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -483,13 +488,13 @@
if (session == null) {
return;
}
- session.getCallback().onPlayFromSearch(controller, query, extra);
+ session.getCallback().onPlayFromSearch(controller, query, extras);
});
}
@Override
public void playFromMediaId(final IMediaSession2Callback caller, final String mediaId,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -503,7 +508,28 @@
if (session == null) {
return;
}
- session.getCallback().onPlayFromMediaId(controller, mediaId, extra);
+ session.getCallback().onPlayFromMediaId(controller, mediaId, extras);
+ });
+ }
+
+ @Override
+ public void setRating(final IMediaSession2Callback caller, final String mediaId,
+ final Bundle ratingBundle) {
+ final MediaSession2Impl sessionImpl = getSession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
+ }
+ return;
+ }
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaSession2Impl session = mSession.get();
+ if (session == null) {
+ return;
+ }
+ Rating2 rating = Rating2Impl.fromBundle(session.getContext(), ratingBundle);
+ session.getCallback().onSetRating(controller, mediaId, rating);
});
}
@@ -621,7 +647,7 @@
try {
controllerImpl.getControllerBinder().onChildrenLoaded(
- parentId, page, pageSize, extras, bundleList);
+ parentId, page, pageSize, bundleList, extras);
} catch (RemoteException e) {
// Controller may be died prematurely.
// TODO(jaewan): Handle this.
@@ -704,7 +730,7 @@
try {
controllerImpl.getControllerBinder().onSearchResultLoaded(
- query, page, pageSize, extras, bundleList);
+ query, page, pageSize, bundleList, extras);
} catch (RemoteException e) {
// Controller may be died prematurely.
// TODO(jaewan): Handle this.
@@ -712,6 +738,56 @@
});
}
+ @Override
+ public void subscribe(final IMediaSession2Callback caller, final String parentId,
+ final Bundle option) {
+ final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "subscribe() from a browser that hasn't connected. Ignore");
+ }
+ return;
+ }
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaLibrarySessionImpl session = getLibrarySession();
+ if (session == null) {
+ return;
+ }
+ session.getCallback().onSubscribed(controller, parentId, option);
+ synchronized (mLock) {
+ Set<String> subscription = mSubscriptions.get(controller);
+ if (subscription == null) {
+ subscription = new HashSet<>();
+ mSubscriptions.put(controller, subscription);
+ }
+ subscription.add(parentId);
+ }
+ });
+ }
+
+ @Override
+ public void unsubscribe(final IMediaSession2Callback caller, final String parentId) {
+ final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "unsubscribe() from a browser that hasn't connected. Ignore");
+ }
+ return;
+ }
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaLibrarySessionImpl session = getLibrarySession();
+ if (session == null) {
+ return;
+ }
+ session.getCallback().onUnsubscribed(controller, parentId);
+ synchronized (mLock) {
+ mSubscriptions.remove(controller);
+ }
+ });
+ }
+
//////////////////////////////////////////////////////////////////////////////////////////////
// APIs for MediaSession2Impl
//////////////////////////////////////////////////////////////////////////////////////////////
@@ -731,11 +807,11 @@
public void notifyPlaybackStateChangedNotLocked(PlaybackState2 state) {
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(list.get(i)).getControllerBinder();
try {
final Bundle bundle = state != null ? state.toBundle() : null;
- callbackBinder.onPlaybackStateChanged(bundle);
+ controllerBinder.onPlaybackStateChanged(bundle);
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -746,7 +822,7 @@
public void notifyCustomLayoutNotLocked(ControllerInfo controller, List<CommandButton> layout) {
// TODO(jaewan): It's OK to be called while it's connecting, but not OK if the connection
// is rejected. Handle the case.
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(controller).getControllerBinder();
try {
List<Bundle> layoutBundles = new ArrayList<>();
@@ -756,7 +832,7 @@
layoutBundles.add(bundle);
}
}
- callbackBinder.onCustomLayoutChanged(layoutBundles);
+ controllerBinder.onCustomLayoutChanged(layoutBundles);
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -778,10 +854,10 @@
}
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(list.get(i)).getControllerBinder();
try {
- callbackBinder.onPlaylistChanged(bundleList);
+ controllerBinder.onPlaylistChanged(bundleList);
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -792,10 +868,10 @@
public void notifyPlaylistParamsChanged(MediaSession2.PlaylistParams params) {
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(list.get(i)).getControllerBinder();
try {
- callbackBinder.onPlaylistParamsChanged(params.toBundle());
+ controllerBinder.onPlaylistParamsChanged(params.toBundle());
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -806,11 +882,11 @@
public void notifyPlaybackInfoChanged(MediaController2.PlaybackInfo playbackInfo) {
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(list.get(i)).getControllerBinder();
try {
- callbackBinder.onPlaybackInfoChanged(
- ((MediaController2Impl.PlaybackInfoImpl) playbackInfo.getProvider()).toBundle());
+ controllerBinder.onPlaybackInfoChanged(((MediaController2Impl.PlaybackInfoImpl)
+ playbackInfo.getProvider()).toBundle());
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -827,9 +903,9 @@
if (command == null) {
throw new IllegalArgumentException("command shouldn't be null");
}
- final IMediaSession2Callback callbackBinder =
+ final IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(controller).getControllerBinder();
- if (getController(callbackBinder) == null) {
+ if (getController(controllerBinder) == null) {
throw new IllegalArgumentException("Controller is gone");
}
sendCustomCommandInternal(controller, command, args, receiver);
@@ -847,14 +923,64 @@
private void sendCustomCommandInternal(ControllerInfo controller, Command command, Bundle args,
ResultReceiver receiver) {
- final IMediaSession2Callback callbackBinder =
+ final IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(controller).getControllerBinder();
try {
Bundle commandBundle = command.toBundle();
- callbackBinder.sendCustomCommand(commandBundle, args, receiver);
+ controllerBinder.sendCustomCommand(commandBundle, args, receiver);
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
}
}
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ // APIs for MediaLibrarySessionImpl
+ //////////////////////////////////////////////////////////////////////////////////////////////
+
+ public void notifySearchResultChanged(ControllerInfo controller, String query, int itemCount,
+ Bundle extras) {
+ final IMediaSession2Callback callbackBinder =
+ ControllerInfoImpl.from(controller).getControllerBinder();
+ try {
+ callbackBinder.onSearchResultChanged(query, itemCount, extras);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Controller is gone", e);
+ // TODO(jaewan): What to do when the controller is gone?
+ }
+ }
+
+ public void notifyChildrenChangedNotLocked(ControllerInfo controller, String parentId,
+ int childCount, Bundle extras) {
+ // TODO(jaewan): Handle when controller is disconnected and no longer valid.
+ // Note: Commands may be sent while onConnected() is running. Should we also
+ // consider it as error?
+ notifyChildrenChangedInternalNotLocked(controller, parentId, childCount, extras);
+ }
+
+ public void notifyChildrenChangedNotLocked(String parentId, int childCount, Bundle extras) {
+ final List<ControllerInfo> controllers = getControllers();
+ for (int i = 0; i < controllers.size(); i++) {
+ notifyChildrenChangedInternalNotLocked(controllers.get(i), parentId, childCount,
+ extras);
+ }
+ }
+
+ public void notifyChildrenChangedInternalNotLocked(final ControllerInfo controller,
+ String parentId, int childCount, Bundle extras) {
+ // Ensure subscription
+ synchronized (mLock) {
+ Set<String> subscriptions = mSubscriptions.get(controller);
+ if (subscriptions == null || !subscriptions.contains(parentId)) {
+ return;
+ }
+ }
+ final IMediaSession2Callback callbackBinder =
+ ControllerInfoImpl.from(controller).getControllerBinder();
+ try {
+ callbackBinder.onChildrenChanged(parentId, childCount, extras);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle controller removed?
+ }
+ }
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
index 8773df4..aa5ac84 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
@@ -22,7 +22,7 @@
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
import android.media.MediaSession2;
import android.media.MediaSessionService2;
import android.media.MediaSessionService2.MediaNotification;
@@ -42,7 +42,7 @@
private static final boolean DEBUG = true; // TODO(jaewan): Change this.
private final MediaSessionService2 mInstance;
- private final PlaybackListener mListener = new SessionServicePlaybackListener();
+ private final EventCallback mCallback = new SessionServiceEventCallback();
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -94,7 +94,7 @@
+ ", but got " + mSession);
}
// TODO(jaewan): Uncomment here.
- // mSession.addPlaybackListener(mListener, mSession.getExecutor());
+ // mSession.registerPlayerEventCallback(mCallback, mSession.getExecutor());
}
@TokenType int getSessionType() {
@@ -135,9 +135,9 @@
mediaNotification.getNotification());
}
- private class SessionServicePlaybackListener implements PlaybackListener {
+ private class SessionServiceEventCallback implements EventCallback {
@Override
- public void onPlaybackChanged(PlaybackState2 state) {
+ public void onPlaybackStateChanged(PlaybackState2 state) {
if (state == null) {
Log.w(TAG, "Ignoring null playback state");
return;
diff --git a/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java b/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java
deleted file mode 100644
index 4241f85..0000000
--- a/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.media;
-
-import android.media.MediaPlayerInterface.PlaybackListener;
-import android.media.PlaybackState2;
-import android.os.Handler;
-import android.support.annotation.NonNull;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Holds {@link PlaybackListener} with the {@link Handler}.
- */
-public class PlaybackListenerHolder {
- public final Executor executor;
- public final PlaybackListener listener;
-
- public PlaybackListenerHolder(Executor executor, @NonNull PlaybackListener listener) {
- this.executor = executor;
- this.listener = listener;
- }
-
- public void postPlaybackChange(final PlaybackState2 state) {
- executor.execute(() -> listener.onPlaybackChanged(state));
- }
-
- /**
- * Returns {@code true} if the given list contains a {@link PlaybackListenerHolder} that holds
- * the given listener.
- *
- * @param list list to check
- * @param listener listener to check
- * @return {@code true} if the given list contains listener. {@code false} otherwise.
- */
- public static <Holder extends PlaybackListenerHolder> boolean contains(
- @NonNull List<Holder> list, PlaybackListener listener) {
- return indexOf(list, listener) >= 0;
- }
-
- /**
- * Returns the index of the {@link PlaybackListenerHolder} that contains the given listener.
- *
- * @param list list to check
- * @param listener listener to check
- * @return {@code index} of item if the given list contains listener. {@code -1} otherwise.
- */
- public static <Holder extends PlaybackListenerHolder> int indexOf(
- @NonNull List<Holder> list, PlaybackListener listener) {
- for (int i = 0; i < list.size(); i++) {
- if (list.get(i).listener == listener) {
- return i;
- }
- }
- return -1;
- }
-}
diff --git a/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java b/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java
index 5eb1129..ee8d6d7 100644
--- a/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java
@@ -30,7 +30,6 @@
private static final String KEY_BUFFERED_POSITION =
"android.media.playbackstate2.buffered_position";
private static final String KEY_SPEED = "android.media.playbackstate2.speed";
- private static final String KEY_ERROR_MESSAGE = "android.media.playbackstate2.error_message";
private static final String KEY_UPDATE_TIME = "android.media.playbackstate2.update_time";
private static final String KEY_ACTIVE_ITEM_ID = "android.media.playbackstate2.active_item_id";
@@ -42,11 +41,9 @@
private final float mSpeed;
private final long mBufferedPosition;
private final long mActiveItemId;
- private final CharSequence mErrorMessage;
public PlaybackState2Impl(Context context, PlaybackState2 instance, int state, long position,
- long updateTime, float speed, long bufferedPosition, long activeItemId,
- CharSequence error) {
+ long updateTime, float speed, long bufferedPosition, long activeItemId) {
mContext = context;
mInstance = instance;
mState = state;
@@ -55,7 +52,6 @@
mUpdateTime = updateTime;
mBufferedPosition = bufferedPosition;
mActiveItemId = activeItemId;
- mErrorMessage = error;
}
@Override
@@ -67,7 +63,6 @@
bob.append(", speed=").append(mSpeed);
bob.append(", updated=").append(mUpdateTime);
bob.append(", active item id=").append(mActiveItemId);
- bob.append(", error=").append(mErrorMessage);
bob.append("}");
return bob.toString();
}
@@ -93,11 +88,6 @@
}
@Override
- public CharSequence getErrorMessage_impl() {
- return mErrorMessage;
- }
-
- @Override
public long getLastPositionUpdateTime_impl() {
return mUpdateTime;
}
@@ -116,7 +106,6 @@
bundle.putFloat(KEY_SPEED, mSpeed);
bundle.putLong(KEY_BUFFERED_POSITION, mBufferedPosition);
bundle.putLong(KEY_ACTIVE_ITEM_ID, mActiveItemId);
- bundle.putCharSequence(KEY_ERROR_MESSAGE, mErrorMessage);
return bundle;
}
@@ -129,18 +118,15 @@
|| !bundle.containsKey(KEY_UPDATE_TIME)
|| !bundle.containsKey(KEY_SPEED)
|| !bundle.containsKey(KEY_BUFFERED_POSITION)
- || !bundle.containsKey(KEY_ACTIVE_ITEM_ID)
- || !bundle.containsKey(KEY_ERROR_MESSAGE)) {
+ || !bundle.containsKey(KEY_ACTIVE_ITEM_ID)) {
return null;
}
-
return new PlaybackState2(context,
bundle.getInt(KEY_STATE),
bundle.getLong(KEY_POSITION),
bundle.getLong(KEY_UPDATE_TIME),
bundle.getFloat(KEY_SPEED),
bundle.getLong(KEY_BUFFERED_POSITION),
- bundle.getLong(KEY_ACTIVE_ITEM_ID),
- bundle.getCharSequence(KEY_ERROR_MESSAGE));
+ bundle.getLong(KEY_ACTIVE_ITEM_ID));
}
}
\ No newline at end of file
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index 46812e7..994824d 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -44,7 +44,6 @@
import android.media.MediaSessionService2.MediaNotification;
import android.media.PlaybackState2;
import android.media.Rating2;
-import android.media.SessionPlayer2;
import android.media.SessionToken2;
import android.media.VolumeProvider2;
import android.media.update.MediaBrowser2Provider;
@@ -60,7 +59,6 @@
import android.media.update.MediaSessionService2Provider;
import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
import android.media.update.PlaybackState2Provider;
-import android.media.update.SessionPlayer2Provider;
import android.media.update.SessionToken2Provider;
import android.media.update.StaticProvider;
import android.media.update.VideoView2Provider;
@@ -230,12 +228,6 @@
}
@Override
- public SessionPlayer2Provider createSessionPlayer2(Context context, SessionPlayer2 instance) {
- // TODO(jaewan): Implement this
- return null;
- }
-
- @Override
public MediaItem2Provider createMediaItem2(Context context, MediaItem2 instance,
String mediaId, DataSourceDesc dsd, MediaMetadata2 metadata, int flags) {
return new MediaItem2Impl(context, instance, mediaId, dsd, metadata, flags);
@@ -302,9 +294,9 @@
@Override
public PlaybackState2Provider createPlaybackState2(Context context, PlaybackState2 instance,
int state, long position, long updateTime, float speed, long bufferedPosition,
- long activeItemId, CharSequence error) {
+ long activeItemId) {
return new PlaybackState2Impl(context, instance, state, position, updateTime, speed,
- bufferedPosition, activeItemId, error);
+ bufferedPosition, activeItemId);
}
@Override
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index ea7e714..a8ce18b 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -17,9 +17,7 @@
package com.android.widget;
import android.annotation.NonNull;
-import android.app.AlertDialog;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.res.Resources;
import android.media.AudioAttributes;
import android.media.AudioFocusRequest;
@@ -48,6 +46,7 @@
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
@@ -92,13 +91,9 @@
private AudioAttributes mAudioAttributes;
private int mAudioFocusType = AudioManager.AUDIOFOCUS_GAIN; // legacy focus gain
- private VideoView2.OnCustomActionListener mOnCustomActionListener;
- private VideoView2.OnPreparedListener mOnPreparedListener;
- private VideoView2.OnCompletionListener mOnCompletionListener;
- private VideoView2.OnErrorListener mOnErrorListener;
- private VideoView2.OnInfoListener mOnInfoListener;
- private VideoView2.OnViewTypeChangedListener mOnViewTypeChangedListener;
- private VideoView2.OnFullScreenRequestListener mOnFullScreenRequestListener;
+ private Pair<Executor, VideoView2.OnCustomActionListener> mCustomActionListenerRecord;
+ private VideoView2.OnViewTypeChangedListener mViewTypeChangedListener;
+ private VideoView2.OnFullScreenRequestListener mFullScreenRequestListener;
private VideoViewInterface mCurrentView;
private VideoTextureView mTextureView;
@@ -351,13 +346,12 @@
return mCurrentView.getViewType();
}
- // TODO: Handle executor properly for all the set listener methods.
@Override
public void setCustomActions_impl(
List<PlaybackState.CustomAction> actionList,
Executor executor, VideoView2.OnCustomActionListener listener) {
mCustomActionList = actionList;
- mOnCustomActionListener = listener;
+ mCustomActionListenerRecord = new Pair<>(executor, listener);
// Create a new playback builder in order to clear existing the custom actions.
mStateBuilder = null;
@@ -365,35 +359,13 @@
}
@Override
- public void setOnPreparedListener_impl(Executor executor, VideoView2.OnPreparedListener l) {
- mOnPreparedListener = l;
+ public void setOnViewTypeChangedListener_impl(VideoView2.OnViewTypeChangedListener l) {
+ mViewTypeChangedListener = l;
}
@Override
- public void setOnCompletionListener_impl(Executor executor, VideoView2.OnCompletionListener l) {
- mOnCompletionListener = l;
- }
-
- @Override
- public void setOnErrorListener_impl(Executor executor, VideoView2.OnErrorListener l) {
- mOnErrorListener = l;
- }
-
- @Override
- public void setOnInfoListener_impl(Executor executor, VideoView2.OnInfoListener l) {
- mOnInfoListener = l;
- }
-
- @Override
- public void setOnViewTypeChangedListener_impl(Executor executor,
- VideoView2.OnViewTypeChangedListener l) {
- mOnViewTypeChangedListener = l;
- }
-
- @Override
- public void setFullScreenRequestListener_impl(Executor executor,
- VideoView2.OnFullScreenRequestListener l) {
- mOnFullScreenRequestListener = l;
+ public void setFullScreenRequestListener_impl(VideoView2.OnFullScreenRequestListener l) {
+ mFullScreenRequestListener = l;
}
@Override
@@ -492,8 +464,8 @@
Log.d(TAG, "onSurfaceTakeOverDone(). Now current view is: " + view);
}
mCurrentView = view;
- if (mOnViewTypeChangedListener != null) {
- mOnViewTypeChangedListener.onViewTypeChanged(mInstance, view.getViewType());
+ if (mViewTypeChangedListener != null) {
+ mViewTypeChangedListener.onViewTypeChanged(mInstance, view.getViewType());
}
if (needToStart()) {
mMediaController.getTransportControls().play();
@@ -845,9 +817,6 @@
updatePlaybackState();
extractSubtitleTracks();
- if (mOnPreparedListener != null) {
- mOnPreparedListener.onPrepared(mInstance);
- }
if (mMediaControlView != null) {
mMediaControlView.setEnabled(true);
}
@@ -908,9 +877,6 @@
mTargetState = STATE_PLAYBACK_COMPLETED;
updatePlaybackState();
- if (mOnCompletionListener != null) {
- mOnCompletionListener.onCompletion(mInstance);
- }
if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
mAudioManager.abandonAudioFocus(null);
}
@@ -920,10 +886,6 @@
private MediaPlayer.OnInfoListener mInfoListener =
new MediaPlayer.OnInfoListener() {
public boolean onInfo(MediaPlayer mp, int what, int extra) {
- if (mOnInfoListener != null) {
- mOnInfoListener.onInfo(mInstance, what, extra);
- }
-
if (what == MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
extractSubtitleTracks();
}
@@ -944,47 +906,6 @@
if (mMediaControlView != null) {
mMediaControlView.setVisibility(View.GONE);
}
-
- /* If an error handler has been supplied, use it and finish. */
- if (mOnErrorListener != null) {
- if (mOnErrorListener.onError(mInstance, frameworkErr, implErr)) {
- return true;
- }
- }
-
- /* Otherwise, pop up an error dialog so the user knows that
- * something bad has happened. Only try and pop up the dialog
- * if we're attached to a window. When we're going away and no
- * longer have a window, don't bother showing the user an error.
- */
- if (mInstance.getWindowToken() != null) {
- int messageId;
-
- if (frameworkErr
- == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
- messageId = R.string.VideoView2_error_text_invalid_progressive_playback;
- } else {
- messageId = R.string.VideoView2_error_text_unknown;
- }
-
- Resources res = ApiHelper.getLibResources();
- new AlertDialog.Builder(mInstance.getContext())
- .setMessage(res.getString(messageId))
- .setPositiveButton(res.getString(R.string.VideoView2_error_button),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog,
- int whichButton) {
- /* If we get here, there is no onError listener, so
- * at least inform them that the video is over.
- */
- if (mOnCompletionListener != null) {
- mOnCompletionListener.onCompletion(mInstance);
- }
- }
- })
- .setCancelable(false)
- .show();
- }
return true;
}
};
@@ -1020,8 +941,8 @@
mInstance.setSubtitleEnabled(false);
break;
case MediaControlView2.COMMAND_SET_FULLSCREEN:
- if (mOnFullScreenRequestListener != null) {
- mOnFullScreenRequestListener.onFullScreenRequest(
+ if (mFullScreenRequestListener != null) {
+ mFullScreenRequestListener.onFullScreenRequest(
mInstance,
args.getBoolean(MediaControlView2Impl.ARGUMENT_KEY_FULLSCREEN));
}
@@ -1033,7 +954,8 @@
@Override
public void onCustomAction(String action, Bundle extras) {
- mOnCustomActionListener.onCustomAction(action, extras);
+ mCustomActionListenerRecord.first.execute(() ->
+ mCustomActionListenerRecord.second.onCustomAction(action, extras));
showController();
}
diff --git a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
index b60fde3..7e93232 100644
--- a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
@@ -21,16 +21,21 @@
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.fail;
import android.annotation.Nullable;
import android.content.Context;
import android.media.MediaBrowser2.BrowserCallback;
+import android.media.MediaLibraryService2.MediaLibrarySession;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.PlaylistParams;
+import android.media.TestServiceRegistry.SessionCallbackProxy;
import android.os.Bundle;
import android.os.ResultReceiver;
+import android.os.Process;
import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
import android.support.test.filters.SmallTest;
@@ -68,8 +73,12 @@
// Browser specific callbacks
default void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {}
default void onItemLoaded(String mediaId, MediaItem2 result) {}
- default void onChildrenLoaded(String parentId, int page, int pageSize, Bundle options,
- List<MediaItem2> result) {}
+ default void onChildrenChanged(String parentId, int childCount, Bundle extras) {}
+ default void onChildrenLoaded(String parentId, int page, int pageSize,
+ List<MediaItem2> result, Bundle extras) {}
+ default void onSearchResultChanged(String query, int itemCount, Bundle extras) {}
+ default void onSearchResultLoaded(String query, int page, int pageSize,
+ List<MediaItem2> result, Bundle extras) {}
}
@Test
@@ -83,7 +92,7 @@
public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {
assertTrue(TestUtils.equals(param, rootHints));
assertEquals(MockMediaLibraryService2.ROOT_ID, rootMediaId);
- assertTrue(TestUtils.equals(MockMediaLibraryService2.EXTRA, rootExtra));
+ assertTrue(TestUtils.equals(MockMediaLibraryService2.EXTRAS, rootExtra));
latch.countDown();
}
};
@@ -141,23 +150,22 @@
final String parentId = MockMediaLibraryService2.PARENT_ID;
final int page = 4;
final int pageSize = 10;
- final Bundle options = new Bundle();
- options.putString(TAG, TAG);
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
final CountDownLatch latch = new CountDownLatch(1);
final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
@Override
public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
- Bundle optionsOut, List<MediaItem2> result) {
+ List<MediaItem2> result, Bundle extrasOut) {
assertEquals(parentId, parentIdOut);
assertEquals(page, pageOut);
assertEquals(pageSize, pageSizeOut);
- assertTrue(TestUtils.equals(options, optionsOut));
+ assertTrue(TestUtils.equals(extras, extrasOut));
assertNotNull(result);
int fromIndex = (page - 1) * pageSize;
- int toIndex = Math.min(page * pageSize,
- MockMediaLibraryService2.GET_CHILDREN_RESULT.size());
+ int toIndex = Math.min(page * pageSize, MockMediaLibraryService2.CHILDREN_COUNT);
// Compare the given results with originals.
for (int originalIndex = fromIndex; originalIndex < toIndex; originalIndex++) {
@@ -173,7 +181,7 @@
final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
- browser.getChildren(parentId, page, pageSize, options);
+ browser.getChildren(parentId, page, pageSize, extras);
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
@@ -185,7 +193,7 @@
final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
@Override
public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
- Bundle optionsOut, List<MediaItem2> result) {
+ List<MediaItem2> result, Bundle extrasOut) {
assertNotNull(result);
assertEquals(0, result.size());
latch.countDown();
@@ -206,7 +214,7 @@
final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
@Override
public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
- Bundle optionsOut, List<MediaItem2> result) {
+ List<MediaItem2> result, Bundle extrasOut) {
assertNull(result);
latch.countDown();
}
@@ -218,6 +226,224 @@
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
+ @Test
+ public void testSearch() throws InterruptedException {
+ final String query = MockMediaLibraryService2.SEARCH_QUERY;
+ final int page = 4;
+ final int pageSize = 10;
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
+
+ final CountDownLatch latchForSearch = new CountDownLatch(1);
+ final CountDownLatch latchForGetSearchResult = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+ @Override
+ public void onSearchResultChanged(String queryOut, int itemCount, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertEquals(MockMediaLibraryService2.SEARCH_RESULT_COUNT, itemCount);
+ latchForSearch.countDown();
+ }
+
+ @Override
+ public void onSearchResultLoaded(String queryOut, int pageOut, int pageSizeOut,
+ List<MediaItem2> result, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertEquals(page, pageOut);
+ assertEquals(pageSize, pageSizeOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertNotNull(result);
+
+ int fromIndex = (page - 1) * pageSize;
+ int toIndex = Math.min(
+ page * pageSize, MockMediaLibraryService2.SEARCH_RESULT_COUNT);
+
+ // Compare the given results with originals.
+ for (int originalIndex = fromIndex; originalIndex < toIndex; originalIndex++) {
+ int relativeIndex = originalIndex - fromIndex;
+ assertEquals(
+ MockMediaLibraryService2.SEARCH_RESULT.get(originalIndex).getMediaId(),
+ result.get(relativeIndex).getMediaId());
+ }
+ latchForGetSearchResult.countDown();
+ }
+ };
+
+ // Request the search.
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.search(query, extras);
+ assertTrue(latchForSearch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+
+ // Get the search result.
+ browser.getSearchResult(query, page, pageSize, extras);
+ assertTrue(latchForGetSearchResult.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSearchTakesTime() throws InterruptedException {
+ final String query = MockMediaLibraryService2.SEARCH_QUERY_TAKES_TIME;
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+ @Override
+ public void onSearchResultChanged(String queryOut, int itemCount, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertEquals(MockMediaLibraryService2.SEARCH_RESULT_COUNT, itemCount);
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.search(query, extras);
+ assertTrue(latch.await(
+ MockMediaLibraryService2.SEARCH_TIME_IN_MS + WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSearchEmptyResult() throws InterruptedException {
+ final String query = MockMediaLibraryService2.SEARCH_QUERY_EMPTY_RESULT;
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+ @Override
+ public void onSearchResultChanged(String queryOut, int itemCount, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertEquals(0, itemCount);
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.search(query, extras);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSubscribe() throws InterruptedException {
+ final String testParentId = "testSubscribeId";
+ final Bundle testExtras = new Bundle();
+ testExtras.putString(testParentId, testParentId);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallbackProxy callbackProxy = new SessionCallbackProxy(mContext) {
+ @Override
+ public void onSubscribed(ControllerInfo info, String parentId, Bundle extras) {
+ if (Process.myUid() == info.getUid()) {
+ assertEquals(testParentId, parentId);
+ assertTrue(TestUtils.equals(testExtras, extras));
+ latch.countDown();
+ }
+ }
+ };
+ TestServiceRegistry.getInstance().setSessionCallbackProxy(callbackProxy);
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token);
+ browser.subscribe(testParentId, testExtras);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testUnsubscribe() throws InterruptedException {
+ final String testParentId = "testUnsubscribeId";
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallbackProxy callbackProxy = new SessionCallbackProxy(mContext) {
+ @Override
+ public void onUnsubscribed(ControllerInfo info, String parentId) {
+ if (Process.myUid() == info.getUid()) {
+ assertEquals(testParentId, parentId);
+ latch.countDown();
+ }
+ }
+ };
+ TestServiceRegistry.getInstance().setSessionCallbackProxy(callbackProxy);
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token);
+ browser.unsubscribe(testParentId);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testBrowserCallback_notifyChildrenChanged() throws InterruptedException {
+ // TODO(jaewan): Add test for the notifyChildrenChanged itself.
+ final String testParentId1 = "testBrowserCallback_notifyChildrenChanged_unexpectedParent";
+ final String testParentId2 = "testBrowserCallback_notifyChildrenChanged";
+ final int testChildrenCount = 101;
+ final Bundle testExtras = new Bundle();
+ testExtras.putString(testParentId1, testParentId1);
+
+ final CountDownLatch latch = new CountDownLatch(3);
+ final SessionCallbackProxy sessionCallbackProxy = new SessionCallbackProxy(mContext) {
+ @Override
+ public CommandGroup onConnect(ControllerInfo controller) {
+ final MockMediaLibraryService2 service = (MockMediaLibraryService2)
+ TestServiceRegistry.getInstance().getServiceInstance();
+ final MediaLibrarySession session = (MediaLibrarySession) service.getSession();
+ // Shouldn't trigger onChildrenChanged() for the browser, because it hasn't
+ // subscribed.
+ session.notifyChildrenChanged(testParentId1, testChildrenCount, null);
+ session.notifyChildrenChanged(controller, testParentId1, testChildrenCount, null);
+ return super.onConnect(controller);
+ }
+
+ @Override
+ public void onSubscribed(ControllerInfo info, String parentId, Bundle extras) {
+ if (Process.myUid() == info.getUid()) {
+ final MediaLibrarySession session = (MediaLibrarySession) mSession;
+ session.notifyChildrenChanged(testParentId2, testChildrenCount, null);
+ session.notifyChildrenChanged(info, testParentId2, testChildrenCount,
+ testExtras);
+ }
+ }
+ };
+ final TestBrowserCallbackInterface controllerCallbackProxy =
+ new TestBrowserCallbackInterface() {
+ @Override
+ public void onChildrenChanged(String parentId, int childCount,
+ Bundle extras) {
+ switch ((int) latch.getCount()) {
+ case 3:
+ assertEquals(testParentId2, parentId);
+ assertEquals(testChildrenCount, childCount);
+ assertNull(extras);
+ latch.countDown();
+ break;
+ case 2:
+ assertEquals(testParentId2, parentId);
+ assertEquals(testChildrenCount, childCount);
+ assertTrue(TestUtils.equals(testExtras, extras));
+ latch.countDown();
+ break;
+ default:
+ // Unexpected call.
+ fail();
+ }
+ }
+ };
+ TestServiceRegistry.getInstance().setSessionCallbackProxy(sessionCallbackProxy);
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ final MediaBrowser2 browser = (MediaBrowser2) createController(
+ token, true, controllerCallbackProxy);
+ final MockMediaLibraryService2 service =
+ (MockMediaLibraryService2) TestServiceRegistry.getInstance().getServiceInstance();
+ if (mSession != null) {
+ mSession.close();
+ }
+ mSession = service.getSession();
+ assertTrue(mSession instanceof MediaLibrarySession);
+ browser.subscribe(testParentId2, null);
+ // This ensures that onChildrenChanged() is only called for the expected reasons.
+ assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
public static class TestBrowserCallback extends BrowserCallback
implements WaitForConnectionInterface {
private final TestControllerCallbackInterface mCallbackProxy;
@@ -287,12 +513,40 @@
}
@Override
- public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle options,
- List<MediaItem2> result) {
- super.onChildrenLoaded(parentId, page, pageSize, options, result);
+ public void onChildrenLoaded(String parentId, int page, int pageSize,
+ List<MediaItem2> result, Bundle extras) {
+ super.onChildrenLoaded(parentId, page, pageSize, result, extras);
if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
((TestBrowserCallbackInterface) mCallbackProxy)
- .onChildrenLoaded(parentId, page, pageSize, options, result);
+ .onChildrenLoaded(parentId, page, pageSize, result, extras);
+ }
+ }
+
+ @Override
+ public void onSearchResultChanged(String query, int itemCount, Bundle extras) {
+ super.onSearchResultChanged(query, itemCount, extras);
+ if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+ ((TestBrowserCallbackInterface) mCallbackProxy)
+ .onSearchResultChanged(query, itemCount, extras);
+ }
+ }
+
+ @Override
+ public void onSearchResultLoaded(String query, int page, int pageSize,
+ List<MediaItem2> result, Bundle extras) {
+ super.onSearchResultLoaded(query, page, pageSize, result, extras);
+ if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+ ((TestBrowserCallbackInterface) mCallbackProxy)
+ .onSearchResultLoaded(query, page, pageSize, result, extras);
+ }
+ }
+
+ @Override
+ public void onChildrenChanged(String parentId, int childCount, Bundle extras) {
+ super.onChildrenChanged(parentId, childCount, extras);
+ if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+ ((TestBrowserCallbackInterface) mCallbackProxy)
+ .onChildrenChanged(parentId, childCount, extras);
}
}
@@ -329,4 +583,4 @@
return mCallback;
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/MediaComponents/test/src/android/media/MediaController2Test.java b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
index 7bf0fd2..e162f1d 100644
--- a/packages/MediaComponents/test/src/android/media/MediaController2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -19,7 +19,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
@@ -36,11 +36,9 @@
import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.text.TextUtils;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -62,7 +60,6 @@
@FlakyTest
public class MediaController2Test extends MediaSession2TestBase {
private static final String TAG = "MediaController2Test";
- private static final int DEFAULT_RATING_TYPE = Rating2.RATING_5_STARS;
PendingIntent mIntent;
MediaSession2 mSession;
@@ -80,7 +77,6 @@
mPlayer = new MockPlayer(1);
mSession = new MediaSession2.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, new SessionCallback(mContext))
- .setRatingType(DEFAULT_RATING_TYPE)
.setSessionActivity(mIntent)
.setId(TAG).build();
mController = createController(mSession.getToken());
@@ -212,11 +208,6 @@
}
@Test
- public void testGetRatingType() throws InterruptedException {
- assertEquals(DEFAULT_RATING_TYPE, mController.getRatingType());
- }
-
- @Test
public void testGetSessionActivity() throws InterruptedException {
PendingIntent sessionActivity = mController.getSessionActivity();
assertEquals(mContext.getPackageName(), sessionActivity.getCreatorPackage());
@@ -391,7 +382,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, query);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -415,7 +406,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, uri);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -439,7 +430,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, id);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -460,11 +451,12 @@
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallback callback = new SessionCallback(mContext) {
@Override
- public void onPrepareFromSearch(ControllerInfo controller, String query, Bundle extras) {
+ public void onPrepareFromSearch(ControllerInfo controller, String query,
+ Bundle extras) {
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, query);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -488,7 +480,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, uri);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -512,7 +504,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, id);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -525,6 +517,34 @@
}
@Test
+ public void testSetRating() throws InterruptedException {
+ final int ratingType = Rating2.RATING_5_STARS;
+ final float ratingValue = 3.5f;
+ final Rating2 rating = Rating2.newStarRating(mContext, ratingType, ratingValue);
+ final String mediaId = "media_id";
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallback callback = new SessionCallback(mContext) {
+ @Override
+ public void onSetRating(ControllerInfo controller, String mediaIdOut,
+ Rating2 ratingOut) {
+ assertEquals(mContext.getPackageName(), controller.getPackageName());
+ assertEquals(mediaId, mediaIdOut);
+ assertEquals(rating, ratingOut);
+ latch.countDown();
+ }
+ };
+
+ try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
+ .setSessionCallback(sHandlerExecutor, callback)
+ .setId("testSetRating").build()) {
+ MediaController2 controller = createController(session.getToken());
+ controller.setRating(mediaId, rating);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
public void testIsConnected() throws InterruptedException {
assertTrue(mController.isConnected());
sHandler.postAndSync(()->{
@@ -601,7 +621,6 @@
}
}
- @Ignore
@Test
public void testGetServiceToken() {
SessionToken2 token = TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID);
@@ -612,20 +631,19 @@
}
private void connectToService(SessionToken2 token) throws InterruptedException {
+ if (mSession != null) {
+ mSession.close();
+ }
mController = createController(token);
mSession = TestServiceRegistry.getInstance().getServiceInstance().getSession();
mPlayer = (MockPlayer) mSession.getPlayer();
}
- // TODO(jaewan): Reenable when session manager detects app installs
- @Ignore
@Test
public void testConnectToService_sessionService() throws InterruptedException {
testConnectToService(MockMediaSessionService2.ID);
}
- // TODO(jaewan): Reenable when session manager detects app installs
- @Ignore
@Test
public void testConnectToService_libraryService() throws InterruptedException {
testConnectToService(MockMediaLibraryService2.ID);
@@ -639,13 +657,13 @@
if (Process.myUid() == controller.getUid()) {
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertFalse(controller.isTrusted());
- latch.countDown();;
+ latch.countDown();
}
return super.onConnect(controller);
}
};
TestServiceRegistry.getInstance().setSessionCallbackProxy(proxy);
- mController = createController(TestUtils.getServiceToken(mContext, id));
+ connectToService(TestUtils.getServiceToken(mContext, id));
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// Test command from controller to session service
@@ -657,7 +675,7 @@
// TODO(jaewan): Add equivalent tests again
/*
final CountDownLatch latch = new CountDownLatch(1);
- mController.addPlaybackListener((state) -> {
+ mController.registerPlayerEventCallback((state) -> {
assertNotNull(state);
assertEquals(PlaybackState.STATE_REWINDING, state.getState());
latch.countDown();
@@ -668,15 +686,11 @@
*/
}
- // TODO(jaewan): Re-enable after b/72792686 is fixed
- @Ignore
@Test
public void testControllerAfterSessionIsGone_session() throws InterruptedException {
testControllerAfterSessionIsGone(mSession.getToken().getId());
}
- // TODO(jaewan): Re-enable after b/72792686 is fixed
- @Ignore
@Test
public void testControllerAfterSessionIsGone_sessionService() throws InterruptedException {
connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
@@ -708,15 +722,11 @@
testControllerAfterSessionIsGone(id);
}
- // TODO(jaewan): Reenable when session manager detects app installs
- @Ignore
@Test
public void testClose_sessionService() throws InterruptedException {
testCloseFromService(MockMediaSessionService2.ID);
}
- // TODO(jaewan): Reenable when session manager detects app installs
- @Ignore
@Test
public void testClose_libraryService() throws InterruptedException {
testCloseFromService(MockMediaLibraryService2.ID);
@@ -751,7 +761,7 @@
waitForDisconnect(mController, true);
testNoInteraction();
- // Test with the newly created session.
+ // Ensure that the controller cannot use newly create session with the same ID.
sHandler.postAndSync(() -> {
// Recreated session has different session stub, so previously created controller
// shouldn't be available.
@@ -764,16 +774,19 @@
private void testNoInteraction() throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
- final PlaybackListener playbackListener = (state) -> {
- fail("Controller shouldn't be notified about change in session after the close.");
- latch.countDown();
+ final EventCallback callback = new EventCallback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState2 state) {
+ fail("Controller shouldn't be notified about change in session after the close.");
+ latch.countDown();
+ }
};
// TODO(jaewan): Add equivalent tests again
/*
- mController.addPlaybackListener(playbackListener, sHandler);
+ mController.registerPlayerEventCallback(playbackListener, sHandler);
mPlayer.notifyPlaybackState(TestUtils.createPlaybackState(PlaybackState.STATE_BUFFERING));
assertFalse(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- mController.removePlaybackListener(playbackListener);
+ mController.unregisterPlayerEventCallback(playbackListener);
*/
}
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
index f5ac6aa..9de4ce7 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
@@ -28,9 +28,8 @@
import static org.junit.Assert.fail;
import android.content.Context;
-import android.media.AudioManager;
import android.media.MediaController2.PlaybackInfo;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
import android.media.MediaSession2.Builder;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
@@ -49,7 +48,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -253,51 +251,67 @@
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
- // TODO(jaewan): Re-enable test..
- @Ignore
@Test
- public void testPlaybackStateChangedListener() throws InterruptedException {
- final CountDownLatch latch = new CountDownLatch(2);
+ public void testRegisterEventCallback() throws InterruptedException {
+ final int testWhat = 1001;
final MockPlayer player = new MockPlayer(0);
- final PlaybackListener listener = (state) -> {
- assertEquals(sHandler.getLooper(), Looper.myLooper());
- assertNotNull(state);
- switch ((int) latch.getCount()) {
- case 2:
- assertEquals(PlaybackState2.STATE_PLAYING, state.getState());
- break;
- case 1:
- assertEquals(PlaybackState2.STATE_PAUSED, state.getState());
- break;
- case 0:
- fail();
+ final CountDownLatch playbackLatch = new CountDownLatch(3);
+ final CountDownLatch errorLatch = new CountDownLatch(1);
+ final EventCallback callback = new EventCallback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState2 state) {
+ assertEquals(sHandler.getLooper(), Looper.myLooper());
+ switch ((int) playbackLatch.getCount()) {
+ case 3:
+ assertNull(state);
+ break;
+ case 2:
+ assertNotNull(state);
+ assertEquals(PlaybackState2.STATE_PLAYING, state.getState());
+ break;
+ case 1:
+ assertNotNull(state);
+ assertEquals(PlaybackState2.STATE_PAUSED, state.getState());
+ break;
+ case 0:
+ fail();
+ }
+ playbackLatch.countDown();
}
- latch.countDown();
+
+ @Override
+ public void onError(String mediaId, int what, int extra) {
+ assertEquals(testWhat, what);
+ errorLatch.countDown();
+ }
};
player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PLAYING));
- sHandler.postAndSync(() -> {
- mSession.addPlaybackListener(sHandlerExecutor, listener);
- // When the player is set, listeners will be notified about the player's current state.
- mSession.setPlayer(player);
- });
+ // EventCallback will be notified with the mPlayer's playback state (null)
+ mSession.registerPlayerEventCallback(sHandlerExecutor, callback);
+ // When the player is set, EventCallback will be notified about the new player's state.
+ mSession.setPlayer(player);
+ // When the player is set, EventCallback will be notified about the new player's state.
player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PAUSED));
- assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertTrue(playbackLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ player.notifyError(testWhat);
+ assertTrue(errorLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
@Test
public void testBadPlayer() throws InterruptedException {
// TODO(jaewan): Add equivalent tests again
- final CountDownLatch latch = new CountDownLatch(3); // expected call + 1
+ final CountDownLatch latch = new CountDownLatch(4); // expected call + 1
final BadPlayer player = new BadPlayer(0);
- sHandler.postAndSync(() -> {
- mSession.addPlaybackListener(sHandlerExecutor, (state) -> {
+ mSession.registerPlayerEventCallback(sHandlerExecutor, new EventCallback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState2 state) {
// This will be called for every setPlayer() calls, but no more.
assertNull(state);
latch.countDown();
- });
- mSession.setPlayer(player);
- mSession.setPlayer(mPlayer);
+ }
});
+ mSession.setPlayer(player);
+ mSession.setPlayer(mPlayer);
player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PAUSED));
assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
@@ -308,7 +322,7 @@
}
@Override
- public void removePlaybackListener(@NonNull PlaybackListener listener) {
+ public void unregisterEventCallback(@NonNull EventCallback listener) {
// No-op. This bad player will keep push notification to the listener that is previously
// registered by session.setPlayer().
}
@@ -461,7 +475,8 @@
}
@Override
- public boolean onCommandRequest(ControllerInfo controllerInfo, MediaSession2.Command command) {
+ public boolean onCommandRequest(ControllerInfo controllerInfo,
+ MediaSession2.Command command) {
assertEquals(mContext.getPackageName(), controllerInfo.getPackageName());
assertEquals(Process.myUid(), controllerInfo.getUid());
assertFalse(controllerInfo.isTrusted());
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
index f5abfff..7106561 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
@@ -118,7 +118,7 @@
*/
public PlaybackState2 createPlaybackState(int state) {
return new PlaybackState2(mContext, state, 0, 0, 1.0f,
- 0, 0, null);
+ 0, 0);
}
final MediaController2 createController(SessionToken2 token) throws InterruptedException {
diff --git a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
index 852f2fa..d61baab 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
@@ -26,7 +26,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,9 +39,6 @@
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
-@Ignore
-// TODO(jaewan): Reenable test when the media session service detects newly installed sesison
-// service app.
public class MediaSessionManager_MediaSession2 extends MediaSession2TestBase {
private static final String TAG = "MediaSessionManager_MediaSession2";
@@ -101,7 +97,7 @@
}
/**
- * Test if server recognizes session even if session refuses the connection from server.
+ * Test if server recognizes a session even if the session refuses the connection from server.
*
* @throws InterruptedException
*/
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
index 5fabebc..e1cce25 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
@@ -26,7 +26,6 @@
import android.media.TestServiceRegistry.SessionCallbackProxy;
import android.media.TestUtils.SyncHandler;
import android.os.Bundle;
-import android.os.Process;
import android.util.Log;
import java.io.FileDescriptor;
@@ -34,6 +33,8 @@
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
import javax.annotation.concurrent.GuardedBy;
@@ -45,23 +46,32 @@
public static final String ID = "TestLibrary";
public static final String ROOT_ID = "rootId";
- public static final Bundle EXTRA = new Bundle();
+ public static final Bundle EXTRAS = new Bundle();
public static final String MEDIA_ID_GET_ITEM = "media_id_get_item";
public static final String PARENT_ID = "parent_id";
public static final String PARENT_ID_NO_CHILDREN = "parent_id_no_children";
public static final String PARENT_ID_ERROR = "parent_id_error";
- public static final List<MediaItem2> GET_CHILDREN_RESULT = new ArrayList<>();
- private static final int CHILDREN_COUNT = 100;
+ public static final List<MediaItem2> GET_CHILDREN_RESULT = new ArrayList<>();
+ public static final int CHILDREN_COUNT = 100;
+
+ public static final String SEARCH_QUERY = "search_query";
+ public static final String SEARCH_QUERY_TAKES_TIME = "search_query_takes_time";
+ public static final int SEARCH_TIME_IN_MS = 5000;
+ public static final String SEARCH_QUERY_EMPTY_RESULT = "search_query_empty_result";
+
+ public static final List<MediaItem2> SEARCH_RESULT = new ArrayList<>();
+ public static final int SEARCH_RESULT_COUNT = 50;
+
private static final DataSourceDesc DATA_SOURCE_DESC =
new DataSourceDesc.Builder().setDataSource(new FileDescriptor()).build();
private static final String TAG = "MockMediaLibrarySvc2";
static {
- EXTRA.putString(ROOT_ID, ROOT_ID);
+ EXTRAS.putString(ROOT_ID, ROOT_ID);
}
@GuardedBy("MockMediaLibraryService2.class")
private static SessionToken2 sToken;
@@ -71,9 +81,15 @@
public MockMediaLibraryService2() {
super();
GET_CHILDREN_RESULT.clear();
- String mediaIdPrefix = "media_id_";
+ String getChildrenMediaIdPrefix = "get_children_media_id_";
for (int i = 0; i < CHILDREN_COUNT; i++) {
- GET_CHILDREN_RESULT.add(createMediaItem(mediaIdPrefix + i));
+ GET_CHILDREN_RESULT.add(createMediaItem(getChildrenMediaIdPrefix + i));
+ }
+
+ SEARCH_RESULT.clear();
+ String getSearchResultMediaIdPrefix = "get_search_result_media_id_";
+ for (int i = 0; i < SEARCH_RESULT_COUNT; i++) {
+ SEARCH_RESULT.add(createMediaItem(getSearchResultMediaIdPrefix + i));
}
}
@@ -133,7 +149,7 @@
@Override
public LibraryRoot onGetRoot(ControllerInfo controller, Bundle rootHints) {
- return new LibraryRoot(MockMediaLibraryService2.this, ROOT_ID, EXTRA);
+ return new LibraryRoot(MockMediaLibraryService2.this, ROOT_ID, EXTRAS);
}
@Override
@@ -147,7 +163,7 @@
@Override
public List<MediaItem2> onLoadChildren(ControllerInfo controller, String parentId, int page,
- int pageSize, Bundle options) {
+ int pageSize, Bundle extras) {
if (PARENT_ID.equals(parentId)) {
return getPaginatedResult(GET_CHILDREN_RESULT, page, pageSize);
} else if (PARENT_ID_ERROR.equals(parentId)) {
@@ -156,6 +172,47 @@
// Includes the case of PARENT_ID_NO_CHILDREN.
return new ArrayList<>();
}
+
+ @Override
+ public void onSearch(ControllerInfo controllerInfo, String query, Bundle extras) {
+ if (SEARCH_QUERY.equals(query)) {
+ mSession.notifySearchResultChanged(controllerInfo, query, SEARCH_RESULT_COUNT,
+ extras);
+ } else if (SEARCH_QUERY_TAKES_TIME.equals(query)) {
+ // Searching takes some time. Notify after 5 seconds.
+ Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
+ @Override
+ public void run() {
+ mSession.notifySearchResultChanged(
+ controllerInfo, query, SEARCH_RESULT_COUNT, extras);
+ }
+ }, SEARCH_TIME_IN_MS, TimeUnit.MILLISECONDS);
+ } else if (SEARCH_QUERY_EMPTY_RESULT.equals(query)) {
+ mSession.notifySearchResultChanged(controllerInfo, query, 0, extras);
+ } else {
+ // TODO: For the error case, how should we notify the browser?
+ }
+ }
+
+ @Override
+ public List<MediaItem2> onLoadSearchResult(ControllerInfo controllerInfo,
+ String query, int page, int pageSize, Bundle extras) {
+ if (SEARCH_QUERY.equals(query)) {
+ return getPaginatedResult(SEARCH_RESULT, page, pageSize);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void onSubscribed(ControllerInfo controller, String parentId, Bundle extras) {
+ mCallbackProxy.onSubscribed(controller, parentId, extras);
+ }
+
+ @Override
+ public void onUnsubscribed(ControllerInfo controller, String parentId) {
+ mCallbackProxy.onUnsubscribed(controller, parentId);
+ }
}
private List<MediaItem2> getPaginatedResult(List<MediaItem2> items, int page, int pageSize) {
@@ -191,4 +248,4 @@
.build(),
0 /* Flags */);
}
-}
\ No newline at end of file
+}
diff --git a/packages/MediaComponents/test/src/android/media/MockPlayer.java b/packages/MediaComponents/test/src/android/media/MockPlayer.java
index 1faf0f4..ae31ce6 100644
--- a/packages/MediaComponents/test/src/android/media/MockPlayer.java
+++ b/packages/MediaComponents/test/src/android/media/MockPlayer.java
@@ -19,6 +19,7 @@
import android.media.MediaSession2.PlaylistParams;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.util.ArrayMap;
import java.util.ArrayList;
import java.util.List;
@@ -46,7 +47,7 @@
public boolean mSetPlaylistCalled;
public boolean mSetPlaylistParamsCalled;
- public List<PlaybackListenerHolder> mListeners = new ArrayList<>();
+ public ArrayMap<EventCallback, Executor> mCallbacks = new ArrayMap<>();
public List<MediaItem2> mPlaylist;
public PlaylistParams mPlaylistParams;
@@ -146,23 +147,30 @@
}
@Override
- public void addPlaybackListener(@NonNull Executor executor,
- @NonNull PlaybackListener listener) {
- mListeners.add(new PlaybackListenerHolder(executor, listener));
+ public void registerEventCallback(@NonNull Executor executor,
+ @NonNull EventCallback callback) {
+ mCallbacks.put(callback, executor);
}
@Override
- public void removePlaybackListener(@NonNull PlaybackListener listener) {
- int index = PlaybackListenerHolder.indexOf(mListeners, listener);
- if (index >= 0) {
- mListeners.remove(index);
- }
+ public void unregisterEventCallback(@NonNull EventCallback callback) {
+ mCallbacks.remove(callback);
}
public void notifyPlaybackState(final PlaybackState2 state) {
mLastPlaybackState = state;
- for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).postPlaybackChange(state);
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ final EventCallback callback = mCallbacks.keyAt(i);
+ final Executor executor = mCallbacks.valueAt(i);
+ executor.execute(() -> callback.onPlaybackStateChanged(state));
+ }
+ }
+
+ public void notifyError(int what) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ final EventCallback callback = mCallbacks.keyAt(i);
+ final Executor executor = mCallbacks.valueAt(i);
+ executor.execute(() -> callback.onError(null, what, 0));
}
}
diff --git a/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java b/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java
deleted file mode 100644
index 0f1644c..0000000
--- a/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.media.MediaPlayerInterface.PlaybackListener;
-import android.os.Handler;
-import android.support.annotation.NonNull;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Holds {@link PlaybackListener} with the {@link Handler}.
- */
-public class PlaybackListenerHolder {
- public final Executor executor;
- public final PlaybackListener listener;
-
- public PlaybackListenerHolder(Executor executor, @NonNull PlaybackListener listener) {
- this.executor = executor;
- this.listener = listener;
- }
-
- public void postPlaybackChange(final PlaybackState2 state) {
- executor.execute(() -> listener.onPlaybackChanged(state));
- }
-
- /**
- * Returns {@code true} if the given list contains a {@link PlaybackListenerHolder} that holds
- * the given listener.
- *
- * @param list list to check
- * @param listener listener to check
- * @return {@code true} if the given list contains listener. {@code false} otherwise.
- */
- public static <Holder extends PlaybackListenerHolder> boolean contains(
- @NonNull List<Holder> list, PlaybackListener listener) {
- return indexOf(list, listener) >= 0;
- }
-
- /**
- * Returns the index of the {@link PlaybackListenerHolder} that contains the given listener.
- *
- * @param list list to check
- * @param listener listener to check
- * @return {@code index} of item if the given list contains listener. {@code -1} otherwise.
- */
- public static <Holder extends PlaybackListenerHolder> int indexOf(
- @NonNull List<Holder> list, PlaybackListener listener) {
- for (int i = 0; i < list.size(); i++) {
- if (list.get(i).listener == listener) {
- return i;
- }
- }
- return -1;
- }
-}
diff --git a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
index 3800c28..a18ad8e 100644
--- a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
+++ b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
@@ -22,6 +22,7 @@
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
import android.media.TestUtils.SyncHandler;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Process;
import android.support.annotation.GuardedBy;
@@ -73,6 +74,9 @@
* Called when enclosing service is destroyed.
*/
public void onServiceDestroyed() { }
+
+ public void onSubscribed(ControllerInfo info, String parentId, Bundle extra) { }
+ public void onUnsubscribed(ControllerInfo info, String parentId) { }
}
@GuardedBy("TestServiceRegistry.class")
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index a3ce1f6..9a30f71 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1099,26 +1099,17 @@
if (status != NO_ERROR) {
return status;
}
+ if (output == AUDIO_IO_HANDLE_NONE) {
+ return BAD_VALUE;
+ }
ALOG_ASSERT(stream != AUDIO_STREAM_PATCH, "attempt to change AUDIO_STREAM_PATCH volume");
AutoMutex lock(mLock);
- Vector<VolumeInterface *> volumeInterfaces;
- if (output != AUDIO_IO_HANDLE_NONE) {
- VolumeInterface *volumeInterface = getVolumeInterface_l(output);
- if (volumeInterface == NULL) {
- return BAD_VALUE;
- }
- volumeInterfaces.add(volumeInterface);
+ VolumeInterface *volumeInterface = getVolumeInterface_l(output);
+ if (volumeInterface == NULL) {
+ return BAD_VALUE;
}
-
- mStreamTypes[stream].volume = value;
-
- if (volumeInterfaces.size() == 0) {
- volumeInterfaces = getAllVolumeInterfaces_l();
- }
- for (size_t i = 0; i < volumeInterfaces.size(); i++) {
- volumeInterfaces[i]->setStreamVolume(stream, value);
- }
+ volumeInterface->setStreamVolume(stream, value);
return NO_ERROR;
}
@@ -1157,21 +1148,17 @@
if (status != NO_ERROR) {
return 0.0f;
}
-
- AutoMutex lock(mLock);
- float volume;
- if (output != AUDIO_IO_HANDLE_NONE) {
- VolumeInterface *volumeInterface = getVolumeInterface_l(output);
- if (volumeInterface != NULL) {
- volume = volumeInterface->streamVolume(stream);
- } else {
- volume = 0.0f;
- }
- } else {
- volume = streamVolume_l(stream);
+ if (output == AUDIO_IO_HANDLE_NONE) {
+ return 0.0f;
}
- return volume;
+ AutoMutex lock(mLock);
+ VolumeInterface *volumeInterface = getVolumeInterface_l(output);
+ if (volumeInterface == NULL) {
+ return 0.0f;
+ }
+
+ return volumeInterface->streamVolume(stream);
}
bool AudioFlinger::streamMute(audio_stream_type_t stream) const
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 7c38bcc..ebd1b18 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -616,9 +616,6 @@
// no range check, AudioFlinger::mLock held
bool streamMute_l(audio_stream_type_t stream) const
{ return mStreamTypes[stream].mute; }
- // no range check, doesn't check per-thread stream volume, AudioFlinger::mLock held
- float streamVolume_l(audio_stream_type_t stream) const
- { return mStreamTypes[stream].volume; }
void ioConfigChanged(audio_io_config_event event,
const sp<AudioIoDescriptor>& ioDesc,
pid_t pid = 0);
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 979290f..0ce5350 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -589,6 +589,13 @@
status = cmdStatus;
}
+ // Ignore error if non-offloadable effect is created on an offload thread.
+ // Will be switched to non-offload thread when the effect is enabled.
+ if (status != NO_ERROR && thread->type() == ThreadBase::OFFLOAD && !isOffloaded()) {
+ ALOGV("Ignore error %d on non-offloadable effect on offload thread", status);
+ status = NO_ERROR;
+ }
+
#ifdef MULTICHANNEL_EFFECT_CHAIN
if (status != NO_ERROR &&
(mConfig.inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index ace586c..ef466a2 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -79,7 +79,6 @@
unsigned i;
for (i = 0; i < FastMixerState::sMaxFastTracks; ++i) {
- mFastTrackNames[i] = -1;
mGenerations[i] = 0;
}
#ifdef FAST_THREAD_STATISTICS
@@ -190,7 +189,7 @@
// FIXME new may block for unbounded time at internal mutex of the heap
// implementation; it would be better to have normal mixer allocate for us
// to avoid blocking here and to prevent possible priority inversion
- mMixer = new AudioMixer(frameCount, mSampleRate, FastMixerState::sMaxFastTracks);
+ mMixer = new AudioMixer(frameCount, mSampleRate);
// FIXME See the other FIXME at FastMixer::setNBLogWriter()
const size_t mixerFrameSize = mSinkChannelCount
* audio_bytes_per_sample(mMixerBufferFormat);
@@ -235,7 +234,6 @@
dumpState->mTrackMask = currentTrackMask;
if (current->mFastTracksGen != mFastTracksGen) {
ALOG_ASSERT(mMixerBuffer != NULL);
- int name;
// process removed tracks first to avoid running out of track names
unsigned removedTracks = previousTrackMask & ~currentTrackMask;
@@ -245,9 +243,7 @@
const FastTrack* fastTrack = ¤t->mFastTracks[i];
ALOG_ASSERT(fastTrack->mBufferProvider == NULL);
if (mMixer != NULL) {
- name = mFastTrackNames[i];
- ALOG_ASSERT(name >= 0);
- mMixer->deleteTrackName(name);
+ mMixer->destroy(i);
}
#if !LOG_NDEBUG
mFastTrackNames[i] = -1;
@@ -265,10 +261,16 @@
AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
ALOG_ASSERT(bufferProvider != NULL && mFastTrackNames[i] == -1);
if (mMixer != NULL) {
- name = mMixer->getTrackName(fastTrack->mChannelMask,
+ const int name = i; // for clarity, choose name as fast track index.
+ status_t status = mMixer->create(
+ name,
+ fastTrack->mChannelMask,
fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
- ALOG_ASSERT(name >= 0);
- mFastTrackNames[i] = name;
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+ "%s: cannot create track name"
+ " %d, mask %#x, format %#x, sessionId %d in AudioMixer",
+ __func__, name,
+ fastTrack->mChannelMask, fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
mMixer->setBufferProvider(name, bufferProvider);
mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
(void *)mMixerBuffer);
@@ -300,8 +302,7 @@
AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
ALOG_ASSERT(bufferProvider != NULL);
if (mMixer != NULL) {
- name = mFastTrackNames[i];
- ALOG_ASSERT(name >= 0);
+ const int name = i;
mMixer->setBufferProvider(name, bufferProvider);
if (fastTrack->mVolumeProvider == NULL) {
float f = AudioMixer::UNITY_GAIN_FLOAT;
@@ -378,8 +379,7 @@
perTrackTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] = trackFramesWritten;
fastTrack->mBufferProvider->onTimestamp(perTrackTimestamp);
- int name = mFastTrackNames[i];
- ALOG_ASSERT(name >= 0);
+ const int name = i;
if (fastTrack->mVolumeProvider != NULL) {
gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR();
float vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index 930fa8d..235d23f 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -57,8 +57,6 @@
static const FastMixerState sInitial;
FastMixerState mPreIdle; // copy of state before we went into idle
- int mFastTrackNames[FastMixerState::kMaxFastTracks];
- // handles used by mixer to identify tracks
int mGenerations[FastMixerState::kMaxFastTracks];
// last observed mFastTracks[i].mGeneration
NBAIO_Sink* mOutputSink;
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 27c6d35..e5cb8a2 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -497,9 +497,6 @@
patch->mPatchRecord->buffer(),
patch->mPatchRecord->bufferSize(),
AUDIO_OUTPUT_FLAG_NONE);
- if (patch->mPatchTrack == 0) {
- return NO_MEMORY;
- }
status = patch->mPatchTrack->initCheck();
if (status != NO_ERROR) {
return status;
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index e97bb06..6454be5 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -51,6 +51,11 @@
void flush();
void destroy();
int name() const { return mName; }
+ void setName(int name) {
+ LOG_ALWAYS_FATAL_IF(mName >= 0 && name >= 0,
+ "%s both old name %d and new name %d are valid", __func__, mName, name);
+ mName = name;
+ }
virtual uint32_t sampleRate() const;
@@ -146,10 +151,7 @@
bool mResetDone;
const audio_stream_type_t mStreamType;
- int mName; // track name on the normal mixer,
- // allocated statically at track creation time,
- // and is even allocated (though unused) for fast tracks
- // FIXME don't allocate track name for fast tracks
+ int mName;
effect_buffer_t *mMainBuffer;
int32_t *mAuxBuffer;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index d6021b3..3134323 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1661,6 +1661,7 @@
mSuspendedFrames(0),
mActiveTracks(&this->mLocalLog),
// mStreamTypes[] initialized in constructor body
+ mTracks(type == MIXER),
mOutput(output),
mLastWriteTime(-1), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false),
mMixerStatus(MIXER_IDLE),
@@ -1702,11 +1703,14 @@
readOutputParameters_l();
// ++ operator does not compile
- for (audio_stream_type_t stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_CNT;
+ for (audio_stream_type_t stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_FOR_POLICY_CNT;
stream = (audio_stream_type_t) (stream + 1)) {
- mStreamTypes[stream].volume = mAudioFlinger->streamVolume_l(stream);
+ mStreamTypes[stream].volume = 0.0f;
mStreamTypes[stream].mute = mAudioFlinger->streamMute_l(stream);
}
+ // Audio patch volume is always max
+ mStreamTypes[AUDIO_STREAM_PATCH].volume = 1.0f;
+ mStreamTypes[AUDIO_STREAM_PATCH].mute = false;
}
AudioFlinger::PlaybackThread::~PlaybackThread()
@@ -2157,6 +2161,53 @@
return track;
}
+template<typename T>
+ssize_t AudioFlinger::PlaybackThread::Tracks<T>::add(const sp<T> &track)
+{
+ const ssize_t index = mTracks.add(track);
+ if (index >= 0) {
+ // set name for track when adding.
+ int name;
+ if (mUnusedTrackNames.empty()) {
+ name = mTracks.size() - 1; // new name {0 ... size-1}.
+ } else {
+ // reuse smallest name for deleted track.
+ auto it = mUnusedTrackNames.begin();
+ name = *it;
+ (void)mUnusedTrackNames.erase(it);
+ }
+ track->setName(name);
+ } else {
+ LOG_ALWAYS_FATAL("cannot add track");
+ }
+ return index;
+}
+
+template<typename T>
+ssize_t AudioFlinger::PlaybackThread::Tracks<T>::remove(const sp<T> &track)
+{
+ const int name = track->name();
+ const ssize_t index = mTracks.remove(track);
+ if (index >= 0) {
+ // invalidate name when removing from mTracks.
+ LOG_ALWAYS_FATAL_IF(name < 0, "invalid name %d for track on mTracks", name);
+
+ if (mSaveDeletedTrackNames) {
+ // We can't directly access mAudioMixer since the caller may be outside of threadLoop.
+ // Instead, we add to mDeletedTrackNames which is solely used for mAudioMixer update,
+ // to be handled when MixerThread::prepareTracks_l() next changes mAudioMixer.
+ mDeletedTrackNames.emplace(name);
+ }
+
+ mUnusedTrackNames.emplace(name);
+ track->setName(T::TRACK_NAME_PENDING);
+ } else {
+ LOG_ALWAYS_FATAL_IF(name >= 0,
+ "valid name %d for track not in mTracks (returned %zd)", name, index);
+ }
+ return index;
+}
+
uint32_t AudioFlinger::PlaybackThread::correctLatency_l(uint32_t latency) const
{
return latency;
@@ -2313,9 +2364,6 @@
mLocalLog.log("removeTrack_l (%p) %s", track.get(), result.string());
mTracks.remove(track);
- deleteTrackName_l(track->name());
- // redundant as track is about to be destroyed, for dumpsys only
- track->mName = -1;
if (track->isFastTrack()) {
int index = track->mFastIndex;
ALOG_ASSERT(0 < index && index < (int)FastMixerState::sMaxFastTracks);
@@ -4111,6 +4159,14 @@
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
Vector< sp<Track> > *tracksToRemove)
{
+ // clean up deleted track names in AudioMixer before allocating new tracks
+ (void)mTracks.processDeletedTrackNames([this](int name) {
+ // for each name, destroy it in the AudioMixer
+ if (mAudioMixer->exists(name)) {
+ mAudioMixer->destroy(name);
+ }
+ });
+ mTracks.clearDeletedTrackNames();
mixer_state mixerStatus = MIXER_IDLE;
// find out which tracks need to be processed
@@ -4332,6 +4388,24 @@
// The first time a track is added we wait
// for all its buffers to be filled before processing it
int name = track->name();
+
+ // if an active track doesn't exist in the AudioMixer, create it.
+ if (!mAudioMixer->exists(name)) {
+ status_t status = mAudioMixer->create(
+ name,
+ track->mChannelMask,
+ track->mFormat,
+ track->mSessionId);
+ if (status != OK) {
+ ALOGW("%s: cannot create track name"
+ " %d, mask %#x, format %#x, sessionId %d in AudioMixer",
+ __func__, name, track->mChannelMask, track->mFormat, track->mSessionId);
+ tracksToRemove->add(track);
+ track->invalidate(); // consider it dead.
+ continue;
+ }
+ }
+
// make sure that we have enough frames to mix one full buffer.
// enforce this condition only once to enable draining the buffer in case the client
// app does not call stop() and relies on underrun to stop:
@@ -4357,20 +4431,9 @@
size_t framesReady = track->framesReady();
if (ATRACE_ENABLED()) {
// I wish we had formatted trace names
- char traceName[16];
- strcpy(traceName, "nRdy");
- int name = track->name();
- if (AudioMixer::TRACK0 <= name &&
- name < (int) (AudioMixer::TRACK0 + AudioMixer::MAX_NUM_TRACKS)) {
- name -= AudioMixer::TRACK0;
- traceName[4] = (name / 10) + '0';
- traceName[5] = (name % 10) + '0';
- } else {
- traceName[4] = '?';
- traceName[5] = '?';
- }
- traceName[6] = '\0';
- ATRACE_INT(traceName, framesReady);
+ std::string traceName("nRdy");
+ traceName += std::to_string(track->name());
+ ATRACE_INT(traceName.c_str(), framesReady);
}
if ((framesReady >= minFrames) && track->isReady() &&
!track->isPaused() && !track->isTerminated())
@@ -4736,7 +4799,7 @@
}
// trackCountForUid_l() must be called with ThreadBase::mLock held
-uint32_t AudioFlinger::PlaybackThread::trackCountForUid_l(uid_t uid)
+uint32_t AudioFlinger::PlaybackThread::trackCountForUid_l(uid_t uid) const
{
uint32_t trackCount = 0;
for (size_t i = 0; i < mTracks.size() ; i++) {
@@ -4747,21 +4810,24 @@
return trackCount;
}
-// getTrackName_l() must be called with ThreadBase::mLock held
-int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask,
- audio_format_t format, audio_session_t sessionId, uid_t uid)
+// isTrackAllowed_l() must be called with ThreadBase::mLock held
+bool AudioFlinger::MixerThread::isTrackAllowed_l(
+ audio_channel_mask_t channelMask, audio_format_t format,
+ audio_session_t sessionId, uid_t uid) const
{
- if (trackCountForUid_l(uid) > (PlaybackThread::kMaxTracksPerUid - 1)) {
- return -1;
+ if (!PlaybackThread::isTrackAllowed_l(channelMask, format, sessionId, uid)) {
+ return false;
}
- return mAudioMixer->getTrackName(channelMask, format, sessionId);
-}
-
-// deleteTrackName_l() must be called with ThreadBase::mLock held
-void AudioFlinger::MixerThread::deleteTrackName_l(int name)
-{
- ALOGV("remove track (%d) and delete from mixer", name);
- mAudioMixer->deleteTrackName(name);
+ // Check validity as we don't call AudioMixer::create() here.
+ if (!AudioMixer::isValidFormat(format)) {
+ ALOGW("%s: invalid format: %#x", __func__, format);
+ return false;
+ }
+ if (!AudioMixer::isValidChannelMask(channelMask)) {
+ ALOGW("%s: invalid channelMask: %#x", __func__, channelMask);
+ return false;
+ }
+ return true;
}
// checkForNewParameter_l() must be called with ThreadBase::mLock held
@@ -4854,13 +4920,18 @@
readOutputParameters_l();
delete mAudioMixer;
mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);
- for (size_t i = 0; i < mTracks.size() ; i++) {
- int name = getTrackName_l(mTracks[i]->mChannelMask,
- mTracks[i]->mFormat, mTracks[i]->mSessionId, mTracks[i]->uid());
- if (name < 0) {
- break;
- }
- mTracks[i]->mName = name;
+ for (const auto &track : mTracks) {
+ const int name = track->name();
+ status_t status = mAudioMixer->create(
+ name,
+ track->mChannelMask,
+ track->mFormat,
+ track->mSessionId);
+ ALOGW_IF(status != NO_ERROR,
+ "%s: cannot create track name"
+ " %d, mask %#x, format %#x, sessionId %d in AudioMixer",
+ __func__,
+ name, track->mChannelMask, track->mFormat, track->mSessionId);
}
sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
}
@@ -5309,21 +5380,6 @@
return !mStandby && !(trackPaused || (mHwPaused && !trackStopped));
}
-// getTrackName_l() must be called with ThreadBase::mLock held
-int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask __unused,
- audio_format_t format __unused, audio_session_t sessionId __unused, uid_t uid)
-{
- if (trackCountForUid_l(uid) > (PlaybackThread::kMaxTracksPerUid - 1)) {
- return -1;
- }
- return 0;
-}
-
-// deleteTrackName_l() must be called with ThreadBase::mLock held
-void AudioFlinger::DirectOutputThread::deleteTrackName_l(int name __unused)
-{
-}
-
// checkForNewParameter_l() must be called with ThreadBase::mLock held
bool AudioFlinger::DirectOutputThread::checkForNewParameter_l(const String8& keyValuePair,
status_t& status)
@@ -5941,6 +5997,31 @@
}
}
+void AudioFlinger::DuplicatingThread::dumpInternals(int fd, const Vector<String16>& args __unused)
+{
+ MixerThread::dumpInternals(fd, args);
+
+ std::stringstream ss;
+ const size_t numTracks = mOutputTracks.size();
+ ss << " " << numTracks << " OutputTracks";
+ if (numTracks > 0) {
+ ss << ":";
+ for (const auto &track : mOutputTracks) {
+ const sp<ThreadBase> thread = track->thread().promote();
+ ss << " (" << track->name() << " : ";
+ if (thread.get() != nullptr) {
+ ss << thread.get() << ", " << thread->id();
+ } else {
+ ss << "null";
+ }
+ ss << ")";
+ }
+ }
+ ss << "\n";
+ std::string result = ss.str();
+ write(fd, result.c_str(), result.size());
+}
+
void AudioFlinger::DuplicatingThread::saveOutputTracks()
{
outputTracks = mOutputTracks;
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 53cb8ad..ae14ac1 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -624,6 +624,7 @@
static const int8_t kMaxTrackStartupRetriesOffload = 100;
static const int8_t kMaxTrackStopRetriesOffload = 2;
static constexpr uint32_t kMaxTracksPerUid = 40;
+ static constexpr size_t kMaxTracks = 256;
// Maximum delay (in nanoseconds) for upcoming buffers in suspend mode, otherwise
// if delay is greater, the estimated time for timeLoopNextNs is reset.
@@ -778,6 +779,16 @@
virtual bool isOutput() const override { return true; }
+ // returns true if the track is allowed to be added to the thread.
+ virtual bool isTrackAllowed_l(
+ audio_channel_mask_t channelMask __unused,
+ audio_format_t format __unused,
+ audio_session_t sessionId __unused,
+ uid_t uid) const {
+ return trackCountForUid_l(uid) < PlaybackThread::kMaxTracksPerUid
+ && mTracks.size() < PlaybackThread::kMaxTracks;
+ }
+
protected:
// updated by readOutputParameters_l()
size_t mNormalFrameCount; // normal mixer and effects
@@ -866,12 +877,6 @@
protected:
ActiveTracks<Track> mActiveTracks;
- // Allocate a track name for a given channel mask.
- // Returns name >= 0 if successful, -1 on failure.
- virtual int getTrackName_l(audio_channel_mask_t channelMask, audio_format_t format,
- audio_session_t sessionId, uid_t uid) = 0;
- virtual void deleteTrackName_l(int name) = 0;
-
// Time to sleep between cycles when:
virtual uint32_t activeSleepTimeUs() const; // mixer state MIXER_TRACKS_ENABLED
virtual uint32_t idleSleepTimeUs() const = 0; // mixer state MIXER_IDLE
@@ -899,7 +904,7 @@
&& mHwSupportsPause
&& (mOutput->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC); }
- uint32_t trackCountForUid_l(uid_t uid);
+ uint32_t trackCountForUid_l(uid_t uid) const;
private:
@@ -916,7 +921,64 @@
virtual void dumpInternals(int fd, const Vector<String16>& args);
void dumpTracks(int fd, const Vector<String16>& args);
- SortedVector< sp<Track> > mTracks;
+ // The Tracks class manages names for all tracks
+ // added and removed from the Thread.
+ template <typename T>
+ class Tracks {
+ public:
+ Tracks(bool saveDeletedTrackNames) :
+ mSaveDeletedTrackNames(saveDeletedTrackNames) { }
+
+ // SortedVector methods
+ ssize_t add(const sp<T> &track);
+ ssize_t remove(const sp<T> &track);
+ size_t size() const {
+ return mTracks.size();
+ }
+ bool isEmpty() const {
+ return mTracks.isEmpty();
+ }
+ ssize_t indexOf(const sp<T> &item) {
+ return mTracks.indexOf(item);
+ }
+ sp<T> operator[](size_t index) const {
+ return mTracks[index];
+ }
+ typename SortedVector<sp<T>>::iterator begin() {
+ return mTracks.begin();
+ }
+ typename SortedVector<sp<T>>::iterator end() {
+ return mTracks.end();
+ }
+
+ size_t processDeletedTrackNames(std::function<void(int)> f) {
+ const size_t size = mDeletedTrackNames.size();
+ if (size > 0) {
+ for (const int name : mDeletedTrackNames) {
+ f(name);
+ }
+ }
+ return size;
+ }
+
+ void clearDeletedTrackNames() { mDeletedTrackNames.clear(); }
+
+ private:
+ // Track names pending deletion for MIXER type threads
+ const bool mSaveDeletedTrackNames; // true to enable tracking
+ std::set<int> mDeletedTrackNames;
+
+ // Fast lookup of previously deleted track names for reuse.
+ // This is an arbitrary decision (actually any non-negative
+ // integer that isn't in mTracks[*]->names() could be used) - we attempt
+ // to use the smallest possible available name.
+ std::set<int> mUnusedTrackNames;
+
+ SortedVector<sp<T>> mTracks; // wrapped SortedVector.
+ };
+
+ Tracks<Track> mTracks;
+
stream_type_t mStreamTypes[AUDIO_STREAM_CNT];
AudioStreamOut *mOutput;
@@ -1023,11 +1085,11 @@
status_t& status);
virtual void dumpInternals(int fd, const Vector<String16>& args);
+ virtual bool isTrackAllowed_l(
+ audio_channel_mask_t channelMask, audio_format_t format,
+ audio_session_t sessionId, uid_t uid) const override;
protected:
virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
- virtual int getTrackName_l(audio_channel_mask_t channelMask, audio_format_t format,
- audio_session_t sessionId, uid_t uid);
- virtual void deleteTrackName_l(int name);
virtual uint32_t idleSleepTimeUs() const;
virtual uint32_t suspendSleepTimeUs() const;
virtual void cacheParameters_l();
@@ -1105,9 +1167,6 @@
virtual void flushHw_l();
protected:
- virtual int getTrackName_l(audio_channel_mask_t channelMask, audio_format_t format,
- audio_session_t sessionId, uid_t uid);
- virtual void deleteTrackName_l(int name);
virtual uint32_t activeSleepTimeUs() const;
virtual uint32_t idleSleepTimeUs() const;
virtual uint32_t suspendSleepTimeUs() const;
@@ -1211,6 +1270,8 @@
virtual ~DuplicatingThread();
// Thread virtuals
+ virtual void dumpInternals(int fd, const Vector<String16>& args) override;
+
void addOutputTrack(MixerThread* thread);
void removeOutputTrack(MixerThread* thread);
uint32_t waitTimeMs() const { return mWaitTimeMs; }
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index a3ea756..a7e966f 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -54,6 +54,11 @@
TYPE_PATCH,
};
+ enum {
+ TRACK_NAME_PENDING = -1,
+ TRACK_NAME_FAILURE = -2,
+ };
+
TrackBase(ThreadBase *thread,
const sp<Client>& client,
uint32_t sampleRate,
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 67f27d0..9b93939 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -394,7 +394,7 @@
// mRetryCount initialized later when needed
mSharedBuffer(sharedBuffer),
mStreamType(streamType),
- mName(-1), // see note below
+ mName(TRACK_NAME_FAILURE), // set to TRACK_NAME_PENDING on constructor success.
mMainBuffer(thread->sinkBuffer()),
mAuxBuffer(NULL),
mAuxEffectId(0), mHasVolumeController(false),
@@ -427,9 +427,8 @@
}
mServerProxy = mAudioTrackServerProxy;
- mName = thread->getTrackName_l(channelMask, format, sessionId, uid);
- if (mName < 0) {
- ALOGE("no more track names available");
+ if (!thread->isTrackAllowed_l(channelMask, format, sessionId, uid)) {
+ ALOGE("no more tracks available");
return;
}
// only allocate a fast track index if we were able to allocate a normal track name
@@ -448,6 +447,7 @@
mFastIndex = i;
thread->mFastTrackAvailMask &= ~(1 << i);
}
+ mName = TRACK_NAME_PENDING;
}
AudioFlinger::PlaybackThread::Track::~Track()
@@ -466,7 +466,7 @@
status_t AudioFlinger::PlaybackThread::Track::initCheck() const
{
status_t status = TrackBase::initCheck();
- if (status == NO_ERROR && mName < 0) {
+ if (status == NO_ERROR && mName == TRACK_NAME_FAILURE) {
status = NO_MEMORY;
}
return status;
@@ -527,10 +527,12 @@
if (isFastTrack()) {
result.appendFormat("F%c %3d", trackType, mFastIndex);
- } else if (mName >= AudioMixer::TRACK0) {
- result.appendFormat("%c %4d", trackType, mName - AudioMixer::TRACK0);
+ } else if (mName == TRACK_NAME_PENDING) {
+ result.appendFormat("%c pend", trackType);
+ } else if (mName == TRACK_NAME_FAILURE) {
+ result.appendFormat("%c fail", trackType);
} else {
- result.appendFormat("%c none", trackType);
+ result.appendFormat("%c %4d", trackType, mName);
}
char nowInUnderrun;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index e8416d4..57d9371 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -3791,9 +3791,11 @@
// ---
-void AudioPolicyManager::addOutput(audio_io_handle_t output, const sp<SwAudioOutputDescriptor>& outputDesc)
+void AudioPolicyManager::addOutput(audio_io_handle_t output,
+ const sp<SwAudioOutputDescriptor>& outputDesc)
{
mOutputs.add(output, outputDesc);
+ applyStreamVolumes(outputDesc, AUDIO_DEVICE_NONE, 0 /* delayMs */, true /* force */);
updateMono(output); // update mono status when adding to output list
selectOutputForMusicEffects();
nextAudioPortGeneration();
@@ -3805,7 +3807,8 @@
selectOutputForMusicEffects();
}
-void AudioPolicyManager::addInput(audio_io_handle_t input, const sp<AudioInputDescriptor>& inputDesc)
+void AudioPolicyManager::addInput(audio_io_handle_t input,
+ const sp<AudioInputDescriptor>& inputDesc)
{
mInputs.add(input, inputDesc);
nextAudioPortGeneration();
@@ -3955,9 +3958,6 @@
// outputs used by dynamic policy mixes
audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE;
- // set initial stream volume for device
- applyStreamVolumes(desc, device, 0, true);
-
//TODO: configure audio effect output stage here
// open a duplicating output thread for the new output and the primary output
@@ -3968,7 +3968,6 @@
if (status == NO_ERROR) {
// add duplicated output descriptor
addOutput(duplicatedOutput, dupOutputDesc);
- applyStreamVolumes(dupOutputDesc, device, 0, true);
} else {
ALOGW("checkOutputsForDevice() could not open dup output for %d and %d",
mPrimaryOutput->mIoHandle, output);
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 89ca6bb..a1857e8 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -1733,8 +1733,6 @@
}
bool CameraService::evictClientIdByRemote(const wp<IBinder>& remote) {
- const int callingPid = getCallingPid();
- const int servicePid = getpid();
bool ret = false;
{
// Acquire mServiceLock and prevent other clients from connecting
@@ -1750,8 +1748,7 @@
mActiveClientManager.remove(i);
continue;
}
- if (remote == clientSp->getRemote() && (callingPid == servicePid ||
- callingPid == clientSp->getClientPid())) {
+ if (remote == clientSp->getRemote()) {
mActiveClientManager.remove(i);
evicted.push_back(clientSp);
@@ -2770,7 +2767,7 @@
* While tempting to promote the wp<IBinder> into a sp, it's actually not supported by the
* binder driver
*/
-
+ // PID here is approximate and can be wrong.
logClientDied(getCallingPid(), String8("Binder died unexpectedly"));
// check torch client