Merge "Camera: Clear cached HIDL references"
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index c62833d..c7619af 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -111,7 +111,7 @@
LOCAL_SHARED_LIBRARIES := \
libstagefright liblog libutils libbinder libui libgui \
- libstagefright_foundation libmedia libcutils
+ libstagefright_foundation libmedia libcutils libmediaextractor
LOCAL_C_INCLUDES:= \
frameworks/av/media/libstagefright \
@@ -204,7 +204,7 @@
LOCAL_SHARED_LIBRARIES := \
libstagefright liblog libutils libbinder libstagefright_foundation \
- libcutils libc
+ libcutils libc libmediaextractor
LOCAL_C_INCLUDES:= \
frameworks/av/media/libstagefright \
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index b5c1ddf..07cec01 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -252,9 +252,9 @@
void DrmHal::closeOpenSessions() {
if (mPlugin != NULL) {
- auto openSessions = mOpenSessions;
- for (size_t i = 0; i < openSessions.size(); i++) {
- closeSession(openSessions[i]);
+ for (size_t i = 0; i < mOpenSessions.size(); i++) {
+ mPlugin->closeSession(toHidlVec(mOpenSessions[i]));
+ DrmSessionManager::Instance()->removeSession(mOpenSessions[i]);
}
}
mOpenSessions.clear();
@@ -472,7 +472,9 @@
for (size_t i = 0; i < mFactories.size(); i++) {
if (mFactories[i]->isCryptoSchemeSupported(uuid)) {
mPlugin = makeDrmPlugin(mFactories[i], uuid, appPackageName);
- mPluginV1_1 = drm::V1_1::IDrmPlugin::castFrom(mPlugin);
+ if (mPlugin != NULL) {
+ mPluginV1_1 = drm::V1_1::IDrmPlugin::castFrom(mPlugin);
+ }
}
}
@@ -504,6 +506,7 @@
}
}
mPlugin.clear();
+ mPluginV1_1.clear();
return OK;
}
diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp
index cc7fb72..26c8427 100644
--- a/drm/libmediadrm/PluginMetricsReporting.cpp
+++ b/drm/libmediadrm/PluginMetricsReporting.cpp
@@ -80,7 +80,6 @@
}
}
- analyticsItem.setFinalized(true);
if (!analyticsItem.selfrecord()) {
ALOGE("selfrecord() returned false. sessioId %" PRId64, sessionId);
}
diff --git a/media/extractors/aac/AACExtractor.cpp b/media/extractors/aac/AACExtractor.cpp
index 17d6aee..6f28374 100644
--- a/media/extractors/aac/AACExtractor.cpp
+++ b/media/extractors/aac/AACExtractor.cpp
@@ -21,7 +21,6 @@
#include "AACExtractor.h"
#include <media/DataSource.h>
#include <media/MediaSourceBase.h>
-#include <media/stagefright/foundation/avc_utils.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -29,6 +28,7 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MetaDataUtils.h>
#include <utils/String8.h>
namespace android {
diff --git a/media/extractors/aac/Android.bp b/media/extractors/aac/Android.bp
index 7937a29..92575f2 100644
--- a/media/extractors/aac/Android.bp
+++ b/media/extractors/aac/Android.bp
@@ -13,6 +13,10 @@
"libutils",
],
+ static_libs: [
+ "libstagefright_metadatautils",
+ ],
+
name: "libaacextractor",
relative_install_path: "extractors",
diff --git a/media/extractors/mkv/Android.bp b/media/extractors/mkv/Android.bp
index 0301ffa..c6cd753 100644
--- a/media/extractors/mkv/Android.bp
+++ b/media/extractors/mkv/Android.bp
@@ -18,6 +18,7 @@
static_libs: [
"libstagefright_flacdec",
+ "libstagefright_metadatautils",
"libwebm",
],
diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp
index 7baccb7..6df0012 100644
--- a/media/extractors/mkv/MatroskaExtractor.cpp
+++ b/media/extractors/mkv/MatroskaExtractor.cpp
@@ -28,12 +28,12 @@
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ByteUtils.h>
#include <media/stagefright/foundation/ColorUtils.h>
-#include <media/stagefright/foundation/avc_utils.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MetaDataUtils.h>
#include <utils/String8.h>
#include <inttypes.h>
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index e88a464..9f21db6 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -602,6 +602,14 @@
ALOGE("heif image %u has no meta!", imageIndex);
continue;
}
+ // Some heif files advertise image sequence brands (eg. 'hevc') in
+ // ftyp box, but don't have any valid tracks in them. Instead of
+ // reporting the entire file as malformed, we override the error
+ // to allow still images to be extracted.
+ if (err != OK) {
+ ALOGW("Extracting still images only");
+ err = OK;
+ }
ALOGV("adding HEIF image track %u", imageIndex);
Track *track = new Track;
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 3aebb8a..6b3a8f0 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -1316,6 +1316,14 @@
}
}
+// -------------------------------------------------------------------------
+
+status_t AudioRecord::getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones)
+{
+ AutoMutex lock(mLock);
+ return mAudioRecord->getActiveMicrophones(activeMicrophones).transactionError();
+}
+
// =========================================================================
void AudioRecord::DeathNotifier::binderDied(const wp<IBinder>& who __unused)
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index b6c98cc..3358e35 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -24,6 +24,7 @@
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
+#include <private/android_filesystem_config.h>
#include "IAudioFlinger.h"
@@ -894,6 +895,24 @@
break;
}
+ // make sure the following transactions come from system components
+ switch (code) {
+ case SET_MASTER_VOLUME:
+ case SET_MASTER_MUTE:
+ case SET_MODE:
+ case SET_MIC_MUTE:
+ case SET_LOW_RAM_DEVICE:
+ case SYSTEM_READY:
+ if (IPCThreadState::self()->getCallingUid() >= AID_APP_START) {
+ ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
+ __func__, code, IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+ return INVALID_OPERATION;
+ }
+ default:
+ break;
+ }
+
// Whitelist of relevant events to trigger log merging.
// Log merging should activate during audio activity of any kind. This are considered the
// most relevant events.
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index 6478975..a24a099 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -27,7 +27,7 @@
#include <media/AudioEffect.h>
#include <media/IAudioPolicyService.h>
-
+#include <private/android_filesystem_config.h>
#include <system/audio.h>
namespace android {
@@ -869,6 +869,27 @@
break;
}
+ // make sure the following transactions come from system components
+ switch (code) {
+ case SET_DEVICE_CONNECTION_STATE:
+ case HANDLE_DEVICE_CONFIG_CHANGE:
+ case SET_PHONE_STATE:
+ case SET_RINGER_MODE:
+ case SET_FORCE_USE:
+ case INIT_STREAM_VOLUME:
+ case SET_STREAM_VOLUME:
+ case REGISTER_POLICY_MIXES:
+ case SET_MASTER_MONO:
+ if (IPCThreadState::self()->getCallingUid() >= AID_APP_START) {
+ ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
+ __func__, code, IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+ return INVALID_OPERATION;
+ }
+ default:
+ break;
+ }
+
switch (code) {
case SET_DEVICE_CONNECTION_STATE: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
diff --git a/media/libaudioclient/aidl/android/media/IAudioRecord.aidl b/media/libaudioclient/aidl/android/media/IAudioRecord.aidl
index 7572671..01e0a71 100644
--- a/media/libaudioclient/aidl/android/media/IAudioRecord.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioRecord.aidl
@@ -16,6 +16,8 @@
package android.media;
+import android.media.MicrophoneInfo;
+
/* Native code must specify namespace media (media::IAudioRecord) when referring to this class */
interface IAudioRecord {
@@ -30,4 +32,8 @@
* will be processed, unless flush() is called.
*/
void stop();
+
+ /* Get a list of current active microphones.
+ */
+ void getActiveMicrophones(out MicrophoneInfo[] activeMicrophones);
}
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index caaefce..c07c397 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -23,8 +23,10 @@
#include <media/AudioTimestamp.h>
#include <media/MediaAnalyticsItem.h>
#include <media/Modulo.h>
+#include <media/MicrophoneInfo.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
+#include <vector>
#include "android/media/IAudioRecord.h"
@@ -527,6 +529,11 @@
/* Get the flags */
audio_input_flags_t getFlags() const { AutoMutex _l(mLock); return mFlags; }
+ /* Get active microphones. A empty vector of MicrophoneInfo will be passed as a parameter,
+ * the data will be filled when querying the hal.
+ */
+ status_t getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones);
+
/*
* Dumps the state of an audio record.
*/
@@ -703,7 +710,6 @@
// mAnalyticsItem alloc failure will be flagged in the constructor
// don't log empty records
if (mAnalyticsItem->count() > 0) {
- mAnalyticsItem->setFinalized(true);
mAnalyticsItem->selfrecord();
}
}
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 8fbe980..e5bb854 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -1198,7 +1198,6 @@
// mAnalyticsItem alloc failure will be flagged in the constructor
// don't log empty records
if (mAnalyticsItem->count() > 0) {
- mAnalyticsItem->setFinalized(true);
mAnalyticsItem->selfrecord();
}
}
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 28684da..1377005 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -318,9 +318,7 @@
srcs: [
"JAudioTrack.cpp",
- "MediaPlayer2Factory.cpp",
"MediaPlayer2Manager.cpp",
- "TestPlayerStub.cpp",
"mediaplayer2.cpp",
],
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 72f5f58..b2c91c4 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -64,6 +64,7 @@
SET_INPUT_DEVICE,
GET_ROUTED_DEVICE_ID,
ENABLE_AUDIO_DEVICE_CALLBACK,
+ GET_ACTIVE_MICROPHONES,
};
@@ -391,6 +392,21 @@
}
return reply.readInt32();
}
+
+ status_t getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones)
+ {
+ ALOGV("getActiveMicrophones");
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
+ status_t status = remote()->transact(GET_ACTIVE_MICROPHONES, data, &reply);
+ if (status != OK
+ || (status = (status_t)reply.readInt32()) != NO_ERROR) {
+ return status;
+ }
+ status = reply.readParcelableVector(activeMicrophones);
+ return status;
+ }
+
};
IMPLEMENT_META_INTERFACE(MediaRecorder, "android.media.IMediaRecorder");
@@ -631,6 +647,19 @@
reply->writeInt32(BAD_VALUE);
}
return NO_ERROR;
+ } break;
+ case GET_ACTIVE_MICROPHONES: {
+ ALOGV("GET_ACTIVE_MICROPHONES");
+ CHECK_INTERFACE(IMediaRecorder, data, reply);
+ std::vector<media::MicrophoneInfo> activeMicrophones;
+ status_t status = getActiveMicrophones(&activeMicrophones);
+ reply->writeInt32(status);
+ if (status != NO_ERROR) {
+ return NO_ERROR;
+ }
+ reply->writeParcelableVector(activeMicrophones);
+ return NO_ERROR;
+
}
default:
return BBinder::onTransact(code, data, reply, flags);
diff --git a/media/libmedia/MediaPlayer2Factory.cpp b/media/libmedia/MediaPlayer2Factory.cpp
deleted file mode 100644
index ac115f6..0000000
--- a/media/libmedia/MediaPlayer2Factory.cpp
+++ /dev/null
@@ -1,270 +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.
-*/
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaPlayer2Factory"
-#include <utils/Log.h>
-
-#include <cutils/properties.h>
-#include <media/DataSource.h>
-#include <media/MediaPlayer2Engine.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <utils/Errors.h>
-#include <utils/misc.h>
-
-#include "MediaPlayer2Factory.h"
-
-#include "TestPlayerStub.h"
-#include "nuplayer2/NuPlayer2Driver.h"
-
-namespace android {
-
-Mutex MediaPlayer2Factory::sLock;
-MediaPlayer2Factory::tFactoryMap *MediaPlayer2Factory::sFactoryMap;
-bool MediaPlayer2Factory::sInitComplete = false;
-
-// static
-bool MediaPlayer2Factory::ensureInit_l() {
- if (sFactoryMap == NULL) {
- sFactoryMap = new (std::nothrow) tFactoryMap();
- }
- return (sFactoryMap != NULL);
-}
-
-status_t MediaPlayer2Factory::registerFactory_l(IFactory* factory,
- player2_type type) {
- if (NULL == factory) {
- ALOGE("Failed to register MediaPlayer2Factory of type %d, factory is"
- " NULL.", type);
- return BAD_VALUE;
- }
-
- if (!ensureInit_l()) {
- return NO_INIT;
- }
-
- if (sFactoryMap->indexOfKey(type) >= 0) {
- ALOGE("Failed to register MediaPlayer2Factory of type %d, type is"
- " already registered.", type);
- return ALREADY_EXISTS;
- }
-
- if (sFactoryMap->add(type, factory) < 0) {
- ALOGE("Failed to register MediaPlayer2Factory of type %d, failed to add"
- " to map.", type);
- return UNKNOWN_ERROR;
- }
-
- return OK;
-}
-
-static player2_type getDefaultPlayerType() {
- return PLAYER2_NU_PLAYER2;
-}
-
-#define GET_PLAYER_TYPE_IMPL(a...) \
- Mutex::Autolock lock_(&sLock); \
- \
- player2_type ret = PLAYER2_STAGEFRIGHT_PLAYER; \
- float bestScore = 0.0; \
- \
- if (!ensureInit_l()) { \
- return ret; \
- } \
- \
- for (size_t i = 0; i < sFactoryMap->size(); ++i) { \
- \
- IFactory* v = sFactoryMap->valueAt(i); \
- float thisScore; \
- CHECK(v != NULL); \
- thisScore = v->scoreFactory(a, bestScore); \
- if (thisScore > bestScore) { \
- ret = sFactoryMap->keyAt(i); \
- bestScore = thisScore; \
- } \
- } \
- \
- if (0.0 == bestScore) { \
- ret = getDefaultPlayerType(); \
- } \
- \
- return ret;
-
-player2_type MediaPlayer2Factory::getPlayerType(const sp<MediaPlayer2Engine>& client,
- const char* url) {
- GET_PLAYER_TYPE_IMPL(client, url);
-}
-
-player2_type MediaPlayer2Factory::getPlayerType(const sp<MediaPlayer2Engine>& client,
- int fd,
- int64_t offset,
- int64_t length) {
- GET_PLAYER_TYPE_IMPL(client, fd, offset, length);
-}
-
-player2_type MediaPlayer2Factory::getPlayerType(const sp<MediaPlayer2Engine>& client,
- const sp<IStreamSource> &source) {
- GET_PLAYER_TYPE_IMPL(client, source);
-}
-
-player2_type MediaPlayer2Factory::getPlayerType(const sp<MediaPlayer2Engine>& client,
- const sp<DataSource> &source) {
- GET_PLAYER_TYPE_IMPL(client, source);
-}
-
-#undef GET_PLAYER_TYPE_IMPL
-
-sp<MediaPlayer2Base> MediaPlayer2Factory::createPlayer(
- player2_type playerType,
- const wp<MediaPlayer2Engine> &client,
- MediaPlayer2Base::NotifyCallback notifyFunc,
- pid_t pid) {
- sp<MediaPlayer2Base> p;
- IFactory* factory;
- status_t init_result;
- Mutex::Autolock lock_(&sLock);
-
- if (!ensureInit_l()) {
- return NULL;
- }
-
- if (sFactoryMap->indexOfKey(playerType) < 0) {
- ALOGE("Failed to create player object of type %d, no registered"
- " factory", playerType);
- return p;
- }
-
- factory = sFactoryMap->valueFor(playerType);
- CHECK(NULL != factory);
- p = factory->createPlayer(pid);
-
- if (p == NULL) {
- ALOGE("Failed to create player object of type %d, create failed",
- playerType);
- return p;
- }
-
- init_result = p->initCheck();
- if (init_result == NO_ERROR) {
- p->setNotifyCallback(client, notifyFunc);
- } else {
- ALOGE("Failed to create player object of type %d, initCheck failed"
- " (res = %d)", playerType, init_result);
- p.clear();
- }
-
- return p;
-}
-
-/*****************************************************************************
- * *
- * Built-In Factory Implementations *
- * *
- *****************************************************************************/
-
-class NuPlayer2Factory : public MediaPlayer2Factory::IFactory {
- public:
- virtual float scoreFactory(const sp<MediaPlayer2Engine>& /*client*/,
- const char* url,
- float curScore) {
- static const float kOurScore = 0.8;
-
- if (kOurScore <= curScore) {
- return 0.0;
- }
-
- if (!strncasecmp("http://", url, 7)
- || !strncasecmp("https://", url, 8)
- || !strncasecmp("file://", url, 7)) {
- size_t len = strlen(url);
- if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) {
- return kOurScore;
- }
-
- if (strstr(url,"m3u8")) {
- return kOurScore;
- }
-
- if ((len >= 4 && !strcasecmp(".sdp", &url[len - 4])) || strstr(url, ".sdp?")) {
- return kOurScore;
- }
- }
-
- if (!strncasecmp("rtsp://", url, 7)) {
- return kOurScore;
- }
-
- return 0.0;
- }
-
- virtual float scoreFactory(const sp<MediaPlayer2Engine>& /*client*/,
- const sp<IStreamSource>& /*source*/,
- float /*curScore*/) {
- return 1.0;
- }
-
- virtual float scoreFactory(const sp<MediaPlayer2Engine>& /*client*/,
- const sp<DataSource>& /*source*/,
- float /*curScore*/) {
- // Only NuPlayer2 supports setting a DataSource source directly.
- return 1.0;
- }
-
- virtual sp<MediaPlayer2Base> createPlayer(pid_t pid) {
- ALOGV(" create NuPlayer2");
- return new NuPlayer2Driver(pid);
- }
-};
-
-class TestPlayerFactory : public MediaPlayer2Factory::IFactory {
- public:
- virtual float scoreFactory(const sp<MediaPlayer2Engine>& /*client*/,
- const char* url,
- float /*curScore*/) {
- if (TestPlayerStub::canBeUsed(url)) {
- return 1.0;
- }
-
- return 0.0;
- }
-
- virtual sp<MediaPlayer2Base> createPlayer(pid_t /* pid */) {
- ALOGV("Create Test Player stub");
- return new TestPlayerStub();
- }
-};
-
-void MediaPlayer2Factory::registerBuiltinFactories() {
- Mutex::Autolock lock_(&sLock);
-
- if (sInitComplete) {
- return;
- }
-
- IFactory* factory = new NuPlayer2Factory();
- if (registerFactory_l(factory, PLAYER2_NU_PLAYER2) != OK) {
- delete factory;
- }
- factory = new TestPlayerFactory();
- if (registerFactory_l(factory, PLAYER2_TEST_PLAYER) != OK) {
- delete factory;
- }
-
- sInitComplete = true;
-}
-
-} // namespace android
diff --git a/media/libmedia/MediaPlayer2Factory.h b/media/libmedia/MediaPlayer2Factory.h
deleted file mode 100644
index 416d241..0000000
--- a/media/libmedia/MediaPlayer2Factory.h
+++ /dev/null
@@ -1,89 +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_MEDIAPLAYER2FACTORY_H
-#define ANDROID_MEDIAPLAYER2FACTORY_H
-
-#include <media/MediaPlayer2Interface.h>
-#include <media/stagefright/foundation/ABase.h>
-
-namespace android {
-
-class MediaPlayer2Factory {
- public:
- class IFactory {
- public:
- virtual ~IFactory() { }
-
- virtual float scoreFactory(const sp<MediaPlayer2Engine>& /*client*/,
- const char* /*url*/,
- float /*curScore*/) { return 0.0; }
-
- virtual float scoreFactory(const sp<MediaPlayer2Engine>& /*client*/,
- int /*fd*/,
- int64_t /*offset*/,
- int64_t /*length*/,
- float /*curScore*/) { return 0.0; }
-
- virtual float scoreFactory(const sp<MediaPlayer2Engine>& /*client*/,
- const sp<IStreamSource> &/*source*/,
- float /*curScore*/) { return 0.0; }
-
- virtual float scoreFactory(const sp<MediaPlayer2Engine>& /*client*/,
- const sp<DataSource> &/*source*/,
- float /*curScore*/) { return 0.0; }
-
- virtual sp<MediaPlayer2Base> createPlayer(pid_t pid) = 0;
- };
-
- static player2_type getPlayerType(const sp<MediaPlayer2Engine>& client,
- const char* url);
- static player2_type getPlayerType(const sp<MediaPlayer2Engine>& client,
- int fd,
- int64_t offset,
- int64_t length);
- static player2_type getPlayerType(const sp<MediaPlayer2Engine>& client,
- const sp<IStreamSource> &source);
- static player2_type getPlayerType(const sp<MediaPlayer2Engine>& client,
- const sp<DataSource> &source);
-
- static sp<MediaPlayer2Base> createPlayer(player2_type playerType,
- const wp<MediaPlayer2Engine> &client,
- MediaPlayer2Base::NotifyCallback notifyFunc,
- pid_t pid);
-
- static void registerBuiltinFactories();
-
- private:
- typedef KeyedVector<player2_type, IFactory*> tFactoryMap;
-
- MediaPlayer2Factory() { }
-
- static bool ensureInit_l();
-
- static status_t registerFactory_l(IFactory* factory,
- player2_type type);
-
- static Mutex sLock;
- static tFactoryMap *sFactoryMap;
- static bool sInitComplete;
-
- DISALLOW_EVIL_CONSTRUCTORS(MediaPlayer2Factory);
-};
-
-} // namespace android
-#endif // ANDROID_MEDIAPLAYER2FACTORY_H
diff --git a/media/libmedia/MediaPlayer2Manager.cpp b/media/libmedia/MediaPlayer2Manager.cpp
index c119750..aefb91c 100644
--- a/media/libmedia/MediaPlayer2Manager.cpp
+++ b/media/libmedia/MediaPlayer2Manager.cpp
@@ -68,8 +68,8 @@
#include <private/android_filesystem_config.h>
+#include <nuplayer2/NuPlayer2Driver.h>
#include "MediaPlayer2Manager.h"
-#include "MediaPlayer2Factory.h"
static const int kDumpLockRetries = 50;
static const int kDumpLockSleepUs = 20000;
@@ -269,8 +269,6 @@
mPid = IPCThreadState::self()->getCallingPid();
mUid = IPCThreadState::self()->getCallingUid();
mNextConnId = 1;
-
- MediaPlayer2Factory::registerBuiltinFactories();
}
MediaPlayer2Manager::~MediaPlayer2Manager() {
@@ -330,7 +328,7 @@
mPid, mConnId, mStatus, mLoop?"true": "false");
result.append(buffer);
- sp<MediaPlayer2Base> p;
+ sp<MediaPlayer2Interface> p;
sp<AudioOutput> audioOutput;
bool locked = false;
for (int i = 0; i < kDumpLockRetries; ++i) {
@@ -534,7 +532,7 @@
ALOGV("disconnect(%d) from pid %d", mConnId, mPid);
// grab local reference and clear main reference to prevent future
// access to object
- sp<MediaPlayer2Base> p;
+ sp<MediaPlayer2Interface> p;
{
Mutex::Autolock l(mLock);
p = mPlayer;
@@ -562,16 +560,17 @@
IPCThreadState::self()->flushCommands();
}
-sp<MediaPlayer2Base> MediaPlayer2Manager::Client::createPlayer(player2_type playerType)
-{
- // determine if we have the right player type
- sp<MediaPlayer2Base> p = getPlayer();
- if ((p != NULL) && (p->playerType() != playerType)) {
- ALOGV("delete player");
- p.clear();
- }
+sp<MediaPlayer2Interface> MediaPlayer2Manager::Client::createPlayer() {
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == NULL) {
- p = MediaPlayer2Factory::createPlayer(playerType, this, notify, mPid);
+ p = new NuPlayer2Driver(mPid);
+ status_t init_result = p->initCheck();
+ if (init_result == NO_ERROR) {
+ p->setNotifyCallback(this, notify);
+ } else {
+ ALOGE("Failed to create player, initCheck failed(res = %d)", init_result);
+ p.clear();
+ }
}
if (p != NULL) {
@@ -584,7 +583,7 @@
void MediaPlayer2Manager::Client::AudioDeviceUpdatedNotifier::onAudioDeviceUpdate(
audio_io_handle_t audioIo,
audio_port_handle_t deviceId) {
- sp<MediaPlayer2Base> listener = mListener.promote();
+ sp<MediaPlayer2Interface> listener = mListener.promote();
if (listener != NULL) {
listener->sendEvent(MEDIA2_AUDIO_ROUTING_CHANGED, audioIo, deviceId);
} else {
@@ -592,13 +591,8 @@
}
}
-sp<MediaPlayer2Base> MediaPlayer2Manager::Client::setDataSource_pre(
- player2_type playerType)
-{
- ALOGV("player type = %d", playerType);
-
- // create the right type of player
- sp<MediaPlayer2Base> p = createPlayer(playerType);
+sp<MediaPlayer2Interface> MediaPlayer2Manager::Client::setDataSource_pre() {
+ sp<MediaPlayer2Interface> p = createPlayer();
if (p == NULL) {
return p;
}
@@ -607,17 +601,15 @@
mAudioDeviceUpdatedListener = new AudioDeviceUpdatedNotifier(p);
- if (!p->hardwareOutput()) {
- mAudioOutput = new AudioOutput(mAudioSessionId, mUid,
- mPid, mAudioAttributes, mAudioDeviceUpdatedListener);
- static_cast<MediaPlayer2Interface*>(p.get())->setAudioSink(mAudioOutput);
- }
+ mAudioOutput = new AudioOutput(mAudioSessionId, mUid,
+ mPid, mAudioAttributes, mAudioDeviceUpdatedListener);
+ p->setAudioSink(mAudioOutput);
return p;
}
status_t MediaPlayer2Manager::Client::setDataSource_post(
- const sp<MediaPlayer2Base>& p,
+ const sp<MediaPlayer2Interface>& p,
status_t status)
{
ALOGV(" setDataSource");
@@ -663,8 +655,7 @@
mStatus = UNKNOWN_ERROR;
return mStatus;
} else {
- player2_type playerType = MediaPlayer2Factory::getPlayerType(this, url);
- sp<MediaPlayer2Base> p = setDataSource_pre(playerType);
+ sp<MediaPlayer2Interface> p = setDataSource_pre();
if (p == NULL) {
return NO_INIT;
}
@@ -701,11 +692,7 @@
ALOGV("calculated length = %lld", (long long)length);
}
- player2_type playerType = MediaPlayer2Factory::getPlayerType(this,
- fd,
- offset,
- length);
- sp<MediaPlayer2Base> p = setDataSource_pre(playerType);
+ sp<MediaPlayer2Interface> p = setDataSource_pre();
if (p == NULL) {
return NO_INIT;
}
@@ -716,9 +703,7 @@
status_t MediaPlayer2Manager::Client::setDataSource(
const sp<IStreamSource> &source) {
- // create the right type of player
- player2_type playerType = MediaPlayer2Factory::getPlayerType(this, source);
- sp<MediaPlayer2Base> p = setDataSource_pre(playerType);
+ sp<MediaPlayer2Interface> p = setDataSource_pre();
if (p == NULL) {
return NO_INIT;
}
@@ -729,8 +714,7 @@
status_t MediaPlayer2Manager::Client::setDataSource(
const sp<DataSource> &source) {
- player2_type playerType = MediaPlayer2Factory::getPlayerType(this, source);
- sp<MediaPlayer2Base> p = setDataSource_pre(playerType);
+ sp<MediaPlayer2Interface> p = setDataSource_pre();
if (p == NULL) {
return NO_INIT;
}
@@ -757,7 +741,7 @@
ALOGV("[%d] setVideoSurfaceTexture(%p)",
mConnId,
(nww == NULL ? NULL : nww->getANativeWindow()));
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
if (nww != NULL && nww->getANativeWindow() != NULL) {
@@ -810,7 +794,7 @@
status_t MediaPlayer2Manager::Client::invoke(const Parcel& request,
Parcel *reply)
{
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == NULL) return UNKNOWN_ERROR;
return p->invoke(request, reply);
}
@@ -834,7 +818,7 @@
status_t MediaPlayer2Manager::Client::getMetadata(
bool update_only, bool /*apply_filter*/, Parcel *reply)
{
- sp<MediaPlayer2Base> player = getPlayer();
+ sp<MediaPlayer2Interface> player = getPlayer();
if (player == 0) return UNKNOWN_ERROR;
status_t status;
@@ -879,7 +863,7 @@
{
ALOGV("[%d] setBufferingSettings{%s}",
mConnId, buffering.toString().string());
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
return p->setBufferingSettings(buffering);
}
@@ -887,7 +871,7 @@
status_t MediaPlayer2Manager::Client::getBufferingSettings(
BufferingSettings* buffering /* nonnull */)
{
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
// TODO: create mPlayer on demand.
if (p == 0) return UNKNOWN_ERROR;
status_t ret = p->getBufferingSettings(buffering);
@@ -903,7 +887,7 @@
status_t MediaPlayer2Manager::Client::prepareAsync()
{
ALOGV("[%d] prepareAsync", mConnId);
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
status_t ret = p->prepareAsync();
#if CALLBACK_ANTAGONIZER
@@ -916,7 +900,7 @@
status_t MediaPlayer2Manager::Client::start()
{
ALOGV("[%d] start", mConnId);
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
p->setLooping(mLoop);
return p->start();
@@ -925,7 +909,7 @@
status_t MediaPlayer2Manager::Client::stop()
{
ALOGV("[%d] stop", mConnId);
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
return p->stop();
}
@@ -933,7 +917,7 @@
status_t MediaPlayer2Manager::Client::pause()
{
ALOGV("[%d] pause", mConnId);
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
return p->pause();
}
@@ -941,7 +925,7 @@
status_t MediaPlayer2Manager::Client::isPlaying(bool* state)
{
*state = false;
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
*state = p->isPlaying();
ALOGV("[%d] isPlaying: %d", mConnId, *state);
@@ -952,14 +936,14 @@
{
ALOGV("[%d] setPlaybackSettings(%f, %f, %d, %d)",
mConnId, rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
return p->setPlaybackSettings(rate);
}
status_t MediaPlayer2Manager::Client::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */)
{
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
status_t ret = p->getPlaybackSettings(rate);
if (ret == NO_ERROR) {
@@ -976,7 +960,7 @@
{
ALOGV("[%d] setSyncSettings(%u, %u, %f, %f)",
mConnId, sync.mSource, sync.mAudioAdjustMode, sync.mTolerance, videoFpsHint);
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
return p->setSyncSettings(sync, videoFpsHint);
}
@@ -984,7 +968,7 @@
status_t MediaPlayer2Manager::Client::getSyncSettings(
AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */)
{
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
status_t ret = p->getSyncSettings(sync, videoFps);
if (ret == NO_ERROR) {
@@ -999,7 +983,7 @@
status_t MediaPlayer2Manager::Client::getCurrentPosition(int *msec)
{
ALOGV("getCurrentPosition");
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
status_t ret = p->getCurrentPosition(msec);
if (ret == NO_ERROR) {
@@ -1013,7 +997,7 @@
status_t MediaPlayer2Manager::Client::getDuration(int *msec)
{
ALOGV("getDuration");
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
status_t ret = p->getDuration(msec);
if (ret == NO_ERROR) {
@@ -1037,7 +1021,7 @@
if (c != NULL) {
if (mAudioOutput != NULL) {
mAudioOutput->setNextOutput(c->mAudioOutput);
- } else if ((mPlayer != NULL) && !mPlayer->hardwareOutput()) {
+ } else {
ALOGE("no current audio output");
}
@@ -1054,13 +1038,9 @@
const sp<VolumeShaper::Operation>& operation) {
// for hardware output, call player instead
ALOGV("Client::applyVolumeShaper(%p)", this);
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
{
Mutex::Autolock l(mLock);
- if (p != 0 && p->hardwareOutput()) {
- // TODO: investigate internal implementation
- return VolumeShaper::Status(INVALID_OPERATION);
- }
if (mAudioOutput.get() != nullptr) {
return mAudioOutput->applyVolumeShaper(configuration, operation);
}
@@ -1071,13 +1051,9 @@
sp<VolumeShaper::State> MediaPlayer2Manager::Client::getVolumeShaperState(int id) {
// for hardware output, call player instead
ALOGV("Client::getVolumeShaperState(%p)", this);
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
{
Mutex::Autolock l(mLock);
- if (p != 0 && p->hardwareOutput()) {
- // TODO: investigate internal implementation.
- return nullptr;
- }
if (mAudioOutput.get() != nullptr) {
return mAudioOutput->getVolumeShaperState(id);
}
@@ -1088,7 +1064,7 @@
status_t MediaPlayer2Manager::Client::seekTo(int msec, MediaPlayer2SeekMode mode)
{
ALOGV("[%d] seekTo(%d, %d)", mConnId, msec, mode);
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
return p->seekTo(msec, mode);
}
@@ -1097,7 +1073,7 @@
{
ALOGV("[%d] reset", mConnId);
mRetransmitEndpointValid = false;
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
return p->reset();
}
@@ -1105,7 +1081,7 @@
status_t MediaPlayer2Manager::Client::notifyAt(int64_t mediaTimeUs)
{
ALOGV("[%d] notifyAt(%lld)", mConnId, (long long)mediaTimeUs);
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
return p->notifyAt(mediaTimeUs);
}
@@ -1142,7 +1118,7 @@
{
ALOGV("[%d] setLooping(%d)", mConnId, loop);
mLoop = loop;
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p != 0) return p->setLooping(loop);
return NO_ERROR;
}
@@ -1152,17 +1128,10 @@
ALOGV("[%d] setVolume(%f, %f)", mConnId, leftVolume, rightVolume);
// for hardware output, call player instead
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
{
Mutex::Autolock l(mLock);
- if (p != 0 && p->hardwareOutput()) {
- MediaPlayerHWInterface* hwp =
- reinterpret_cast<MediaPlayerHWInterface*>(p.get());
- return hwp->setVolume(leftVolume, rightVolume);
- } else {
- if (mAudioOutput != 0) mAudioOutput->setVolume(leftVolume, rightVolume);
- return NO_ERROR;
- }
+ if (mAudioOutput != 0) mAudioOutput->setVolume(leftVolume, rightVolume);
}
return NO_ERROR;
@@ -1193,7 +1162,7 @@
return setAudioAttributes_l(request);
}
default:
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) { return UNKNOWN_ERROR; }
return p->setParameter(key, request);
}
@@ -1201,7 +1170,7 @@
status_t MediaPlayer2Manager::Client::getParameter(int key, Parcel *reply) {
ALOGV("[%d] getParameter(%d)", mConnId, key);
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
return p->getParameter(key, reply);
}
@@ -1218,7 +1187,7 @@
ALOGV("[%d] setRetransmitEndpoint = <none>", mConnId);
}
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
// Right now, the only valid time to set a retransmit endpoint is before
// player selection has been made (since the presence or absence of a
@@ -1244,7 +1213,7 @@
if (NULL == endpoint)
return BAD_VALUE;
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p != NULL)
return p->getRetransmitEndpoint(endpoint);
@@ -1347,7 +1316,7 @@
const Vector<uint8_t>& drmSessionId)
{
ALOGV("[%d] prepareDrm", mConnId);
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
status_t ret = p->prepareDrm(uuid, drmSessionId);
@@ -1359,7 +1328,7 @@
status_t MediaPlayer2Manager::Client::releaseDrm()
{
ALOGV("[%d] releaseDrm", mConnId);
- sp<MediaPlayer2Base> p = getPlayer();
+ sp<MediaPlayer2Interface> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
status_t ret = p->releaseDrm();
diff --git a/media/libmedia/MediaPlayer2Manager.h b/media/libmedia/MediaPlayer2Manager.h
index b42cbbb..20cc735 100644
--- a/media/libmedia/MediaPlayer2Manager.h
+++ b/media/libmedia/MediaPlayer2Manager.h
@@ -48,7 +48,7 @@
class Antagonizer {
public:
Antagonizer(
- MediaPlayer2Base::NotifyCallback cb,
+ MediaPlayer2Interface::NotifyCallback cb,
const wp<MediaPlayer2Engine> &client);
void start() { mActive = true; }
void stop() { mActive = false; }
@@ -62,14 +62,14 @@
bool mExit;
bool mActive;
wp<MediaPlayer2Engine> mClient;
- MediaPlayer2Base::NotifyCallback mCb;
+ MediaPlayer2Interface::NotifyCallback mCb;
};
#endif
class MediaPlayer2Manager {
class Client;
- class AudioOutput : public MediaPlayer2Base::AudioSink
+ class AudioOutput : public MediaPlayer2Interface::AudioSink
{
class CallbackData;
@@ -287,7 +287,7 @@
const sp<media::VolumeShaper::Operation>& operation) override;
virtual sp<media::VolumeShaper::State> getVolumeShaperState(int id) override;
- sp<MediaPlayer2Base> createPlayer(player2_type playerType);
+ sp<MediaPlayer2Interface> createPlayer();
virtual status_t setDataSource(
const sp<MediaHTTPService> &httpService,
@@ -300,8 +300,8 @@
virtual status_t setDataSource(const sp<DataSource> &source);
- sp<MediaPlayer2Base> setDataSource_pre(player2_type playerType);
- status_t setDataSource_post(const sp<MediaPlayer2Base>& p,
+ sp<MediaPlayer2Interface> setDataSource_pre();
+ status_t setDataSource_post(const sp<MediaPlayer2Interface>& p,
status_t status);
static void notify(const wp<MediaPlayer2Engine> &listener, int msg,
@@ -323,7 +323,7 @@
class AudioDeviceUpdatedNotifier: public AudioSystem::AudioDeviceCallback
{
public:
- AudioDeviceUpdatedNotifier(const sp<MediaPlayer2Base>& listener) {
+ AudioDeviceUpdatedNotifier(const sp<MediaPlayer2Interface>& listener) {
mListener = listener;
}
~AudioDeviceUpdatedNotifier() {}
@@ -332,7 +332,7 @@
audio_port_handle_t deviceId);
private:
- wp<MediaPlayer2Base> mListener;
+ wp<MediaPlayer2Interface> mListener;
};
friend class MediaPlayer2Manager;
@@ -346,7 +346,7 @@
void deletePlayer();
- sp<MediaPlayer2Base> getPlayer() const { Mutex::Autolock lock(mLock); return mPlayer; }
+ sp<MediaPlayer2Interface> getPlayer() const { Mutex::Autolock lock(mLock); return mPlayer; }
@@ -366,7 +366,7 @@
status_t setAudioAttributes_l(const Parcel &request);
mutable Mutex mLock;
- sp<MediaPlayer2Base> mPlayer;
+ sp<MediaPlayer2Interface> mPlayer;
sp<MediaPlayer2EngineClient> mClient;
sp<AudioOutput> mAudioOutput;
pid_t mPid;
diff --git a/media/libmedia/TestPlayerStub.cpp b/media/libmedia/TestPlayerStub.cpp
deleted file mode 100644
index 3548793..0000000
--- a/media/libmedia/TestPlayerStub.cpp
+++ /dev/null
@@ -1,198 +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.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "TestPlayerStub"
-#include "utils/Log.h"
-
-#include "TestPlayerStub.h"
-
-#include <dlfcn.h> // for dlopen/dlclose
-#include <stdlib.h>
-#include <string.h>
-#include <cutils/properties.h>
-#include <utils/Errors.h> // for status_t
-
-#include "media/MediaPlayer2Interface.h"
-
-
-namespace {
-using android::status_t;
-using android::MediaPlayer2Base;
-
-const char *kTestUrlScheme = "test:";
-const char *kUrlParam = "url=";
-
-const char *kBuildTypePropName = "ro.build.type";
-const char *kEngBuild = "eng";
-const char *kTestBuild = "test";
-
-// @return true if the current build is 'eng' or 'test'.
-bool isTestBuild()
-{
- char prop[PROPERTY_VALUE_MAX] = { '\0', };
-
- property_get(kBuildTypePropName, prop, "\0");
- return strcmp(prop, kEngBuild) == 0 || strcmp(prop, kTestBuild) == 0;
-}
-
-// @return true if the url scheme is 'test:'
-bool isTestUrl(const char *url)
-{
- return url && strncmp(url, kTestUrlScheme, strlen(kTestUrlScheme)) == 0;
-}
-
-} // anonymous namespace
-
-namespace android {
-
-TestPlayerStub::TestPlayerStub()
- :mUrl(NULL), mFilename(NULL), mContentUrl(NULL),
- mHandle(NULL), mNewPlayer(NULL), mDeletePlayer(NULL),
- mPlayer(NULL) { }
-
-TestPlayerStub::~TestPlayerStub()
-{
- resetInternal();
-}
-
-status_t TestPlayerStub::initCheck()
-{
- return isTestBuild() ? OK : INVALID_OPERATION;
-}
-
-// Parse mUrl to get:
-// * The library to be dlopened.
-// * The url to be passed to the real setDataSource impl.
-//
-// mUrl is expected to be in following format:
-//
-// test:<name of the .so>?url=<url for setDataSource>
-//
-// The value of the url parameter is treated as a string (no
-// unescaping of illegal charaters).
-status_t TestPlayerStub::parseUrl()
-{
- if (strlen(mUrl) < strlen(kTestUrlScheme)) {
- resetInternal();
- return BAD_VALUE;
- }
-
- char *i = mUrl + strlen(kTestUrlScheme);
-
- mFilename = i;
-
- while (*i != '\0' && *i != '?') {
- ++i;
- }
-
- if (*i == '\0' || strncmp(i + 1, kUrlParam, strlen(kUrlParam)) != 0) {
- resetInternal();
- return BAD_VALUE;
- }
- *i = '\0'; // replace '?' to nul-terminate mFilename
-
- mContentUrl = i + 1 + strlen(kUrlParam);
- return OK;
-}
-
-// Load the dynamic library.
-// Create the test player.
-// Call setDataSource on the test player with the url in param.
-status_t TestPlayerStub::setDataSource(
- const sp<MediaHTTPService> &httpService,
- const char *url,
- const KeyedVector<String8, String8> *headers) {
- if (!isTestUrl(url) || NULL != mHandle) {
- return INVALID_OPERATION;
- }
-
- mUrl = strdup(url);
-
- status_t status = parseUrl();
-
- if (OK != status) {
- resetInternal();
- return status;
- }
-
- ::dlerror(); // Clears any pending error.
-
- // Load the test player from the url. dlopen will fail if the lib
- // is not there. dls are under /system/lib
- // None of the entry points should be NULL.
- mHandle = ::dlopen(mFilename, RTLD_NOW | RTLD_GLOBAL);
- if (!mHandle) {
- ALOGE("dlopen failed: %s", ::dlerror());
- resetInternal();
- return UNKNOWN_ERROR;
- }
-
- // Load the 2 entry points to create and delete instances.
- const char *err;
- mNewPlayer = reinterpret_cast<NEW_PLAYER>(dlsym(mHandle,
- "newPlayer"));
- err = ::dlerror();
- if (err || mNewPlayer == NULL) {
- // if err is NULL the string <null> is inserted in the logs =>
- // mNewPlayer was NULL.
- ALOGE("dlsym for newPlayer failed %s", err);
- resetInternal();
- return UNKNOWN_ERROR;
- }
-
- mDeletePlayer = reinterpret_cast<DELETE_PLAYER>(dlsym(mHandle,
- "deletePlayer"));
- err = ::dlerror();
- if (err || mDeletePlayer == NULL) {
- ALOGE("dlsym for deletePlayer failed %s", err);
- resetInternal();
- return UNKNOWN_ERROR;
- }
-
- mPlayer = (*mNewPlayer)();
- return mPlayer->setDataSource(httpService, mContentUrl, headers);
-}
-
-// Internal cleanup.
-status_t TestPlayerStub::resetInternal()
-{
- if(mUrl) {
- free(mUrl);
- mUrl = NULL;
- }
- mFilename = NULL;
- mContentUrl = NULL;
-
- if (mPlayer) {
- ALOG_ASSERT(mDeletePlayer != NULL, "mDeletePlayer is null");
- (*mDeletePlayer)(mPlayer);
- mPlayer = NULL;
- }
-
- if (mHandle) {
- ::dlclose(mHandle);
- mHandle = NULL;
- }
- return OK;
-}
-
-/* static */ bool TestPlayerStub::canBeUsed(const char *url)
-{
- return isTestBuild() && isTestUrl(url);
-}
-
-} // namespace android
diff --git a/media/libmedia/TestPlayerStub.h b/media/libmedia/TestPlayerStub.h
deleted file mode 100644
index 27c8bf4..0000000
--- a/media/libmedia/TestPlayerStub.h
+++ /dev/null
@@ -1,133 +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_FRAMEWORKS_BASE_MEDIA_LIBMEDIA_TESTPLAYERSTUB_H__
-#define ANDROID_FRAMEWORKS_BASE_MEDIA_LIBMEDIA_TESTPLAYERSTUB_H__
-
-#include <media/MediaPlayer2Interface.h>
-#include <utils/Errors.h>
-
-namespace android {
-class MediaPlayer2Base; // in media/MediaPlayer2Interface.h
-
-// Wrapper around a test media player that gets dynamically loaded.
-//
-// The URL passed to setDataSource has this format:
-//
-// test:<name of the .so>?url=<url for the real setDataSource impl.>
-//
-// e.g:
-// test:invoke_test_media_player.so?url=http://youtube.com/
-// test:invoke_test_media_player.so?url=speedtest
-//
-// TestPlayerStub::setDataSource loads the library in the test url. 2
-// entry points with C linkage are expected. One to create the test
-// player and one to destroy it.
-//
-// extern "C" android::MediaPlayer2Base* newPlayer();
-// extern "C" android::status_t deletePlayer(android::MediaPlayer2Base *p);
-//
-// Once the test player has been loaded, its setDataSource
-// implementation is called with the value of the 'url' parameter.
-//
-// typical usage in a java test:
-// ============================
-//
-// MediaPlayer2 p = new MediaPlayer2();
-// p.setDataSource("test:invoke_mock_media_player.so?url=http://youtube.com");
-// p.prepare();
-// ...
-// p.release();
-
-class TestPlayerStub : public MediaPlayer2Interface {
- public:
- typedef MediaPlayer2Base* (*NEW_PLAYER)();
- typedef status_t (*DELETE_PLAYER)(MediaPlayer2Base *);
-
- TestPlayerStub();
- virtual ~TestPlayerStub();
-
- // Called right after the constructor. Check if the current build
- // allows test players.
- virtual status_t initCheck();
-
- // @param url Should be a test url. See class comment.
- virtual status_t setDataSource(
- const sp<MediaHTTPService> &httpService,
- const char* url,
- const KeyedVector<String8, String8> *headers);
-
- // Test player for a file descriptor source is not supported.
- virtual status_t setDataSource(int, int64_t, int64_t) {
- return INVALID_OPERATION;
- }
-
-
- // All the methods below wrap the mPlayer instance.
- virtual status_t setVideoSurfaceTexture(
- const android::sp<android::ANativeWindowWrapper>& st) {
- return mPlayer->setVideoSurfaceTexture(st);
- }
- virtual status_t prepare() {return mPlayer->prepare();}
- virtual status_t prepareAsync() {return mPlayer->prepareAsync();}
- virtual status_t start() {return mPlayer->start();}
- virtual status_t stop() {return mPlayer->stop();}
- virtual status_t pause() {return mPlayer->pause();}
- virtual bool isPlaying() {return mPlayer->isPlaying();}
- virtual status_t seekTo(
- int msec,
- MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC) {
- return mPlayer->seekTo(msec, mode);
- }
- virtual status_t getCurrentPosition(int *p) {
- return mPlayer->getCurrentPosition(p);
- }
- virtual status_t getDuration(int *d) {return mPlayer->getDuration(d);}
- virtual status_t reset() {return mPlayer->reset();}
- virtual status_t setLooping(int b) {return mPlayer->setLooping(b);}
- virtual player2_type playerType() {return mPlayer->playerType();}
- virtual status_t invoke(const android::Parcel& in, android::Parcel *out) {
- return mPlayer->invoke(in, out);
- }
- virtual status_t setParameter(int key, const Parcel &request) {
- return mPlayer->setParameter(key, request);
- }
- virtual status_t getParameter(int key, Parcel *reply) {
- return mPlayer->getParameter(key, reply);
- }
-
-
- // @return true if the current build is 'eng' or 'test' and the
- // url's scheme is 'test:'
- static bool canBeUsed(const char *url);
-
- private:
- // Release the player, dlclose the library.
- status_t resetInternal();
- status_t parseUrl();
-
- char *mUrl; // test:foo.so?url=http://bar
- char *mFilename; // foo.so
- char *mContentUrl; // http://bar
- void *mHandle; // returned by dlopen
- NEW_PLAYER mNewPlayer;
- DELETE_PLAYER mDeletePlayer;
- MediaPlayer2Base *mPlayer; // wrapped player
-};
-
-} // namespace android
-
-#endif // ANDROID_FRAMEWORKS_BASE_MEDIA_LIBMEDIA_TESTPLAYERSTUB_H__
diff --git a/media/libmedia/include/media/IMediaRecorder.h b/media/libmedia/include/media/IMediaRecorder.h
index 3cef329..379000e 100644
--- a/media/libmedia/include/media/IMediaRecorder.h
+++ b/media/libmedia/include/media/IMediaRecorder.h
@@ -19,7 +19,9 @@
#define ANDROID_IMEDIARECORDER_H
#include <binder/IInterface.h>
+#include <media/MicrophoneInfo.h>
#include <system/audio.h>
+#include <vector>
namespace android {
@@ -69,6 +71,9 @@
virtual status_t setInputDevice(audio_port_handle_t deviceId) = 0;
virtual status_t getRoutedDeviceId(audio_port_handle_t *deviceId) = 0;
virtual status_t enableAudioDeviceCallback(bool enabled) = 0;
+ virtual status_t getActiveMicrophones(
+ std::vector<media::MicrophoneInfo>* activeMicrophones) = 0;
+
};
// ----------------------------------------------------------------------------
diff --git a/media/libmedia/include/media/MediaPlayer2Interface.h b/media/libmedia/include/media/MediaPlayer2Interface.h
index 931a110..699618e 100644
--- a/media/libmedia/include/media/MediaPlayer2Interface.h
+++ b/media/libmedia/include/media/MediaPlayer2Interface.h
@@ -47,16 +47,6 @@
template<typename T> class SortedVector;
-enum player2_type {
- PLAYER2_STAGEFRIGHT_PLAYER = 3,
- PLAYER2_NU_PLAYER2 = 4,
- // Test players are available only in the 'test' and 'eng' builds.
- // The shared library with the test player is passed passed as an
- // argument to the 'test:' url in the setDataSource call.
- PLAYER2_TEST_PLAYER = 5,
-};
-
-
#define DEFAULT_AUDIOSINK_BUFFERCOUNT 4
#define DEFAULT_AUDIOSINK_BUFFERSIZE 1200
#define DEFAULT_AUDIOSINK_SAMPLERATE 44100
@@ -68,7 +58,7 @@
#define AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US 5000000
// abstract base class - use MediaPlayer2Interface
-class MediaPlayer2Base : public AHandler
+class MediaPlayer2Interface : public AHandler
{
public:
// callback mechanism for passing messages to MediaPlayer2 object
@@ -158,15 +148,16 @@
virtual status_t enableAudioDeviceCallback(bool enabled);
};
- MediaPlayer2Base() : mClient(0), mNotify(0) {}
- virtual ~MediaPlayer2Base() {}
+ MediaPlayer2Interface() : mClient(0), mNotify(0) { }
+ virtual ~MediaPlayer2Interface() { }
virtual status_t initCheck() = 0;
- virtual bool hardwareOutput() = 0;
virtual status_t setUID(uid_t /* uid */) {
return INVALID_OPERATION;
}
+ virtual void setAudioSink(const sp<AudioSink>& audioSink) { mAudioSink = audioSink; }
+
virtual status_t setDataSource(
const sp<MediaHTTPService> &httpService,
const char *url,
@@ -234,7 +225,6 @@
return INVALID_OPERATION;
}
virtual status_t setLooping(int loop) = 0;
- virtual player2_type playerType() = 0;
virtual status_t setParameter(int key, const Parcel &request) = 0;
virtual status_t getParameter(int key, Parcel *reply) = 0;
@@ -245,7 +235,7 @@
virtual status_t getRetransmitEndpoint(struct sockaddr_in* /* endpoint */) {
return INVALID_OPERATION;
}
- virtual status_t setNextPlayer(const sp<MediaPlayer2Base>& /* next */) {
+ virtual status_t setNextPlayer(const sp<MediaPlayer2Interface>& /* next */) {
return OK;
}
@@ -303,6 +293,9 @@
return INVALID_OPERATION;
}
+protected:
+ sp<AudioSink> mAudioSink;
+
private:
friend class MediaPlayer2Manager;
@@ -311,27 +304,6 @@
NotifyCallback mNotify;
};
-// Implement this class for media players that use the AudioFlinger software mixer
-class MediaPlayer2Interface : public MediaPlayer2Base
-{
-public:
- virtual ~MediaPlayer2Interface() { }
- virtual bool hardwareOutput() { return false; }
- virtual void setAudioSink(const sp<AudioSink>& audioSink) { mAudioSink = audioSink; }
-protected:
- sp<AudioSink> mAudioSink;
-};
-
-// Implement this class for media players that output audio directly to hardware
-class MediaPlayer2HWInterface : public MediaPlayer2Base
-{
-public:
- virtual ~MediaPlayer2HWInterface() {}
- virtual bool hardwareOutput() { return true; }
- virtual status_t setVolume(float leftVolume, float rightVolume) = 0;
- virtual status_t setAudioStreamType(audio_stream_type_t streamType) = 0;
-};
-
}; // namespace android
#endif // __cplusplus
diff --git a/media/libmedia/include/media/MediaRecorderBase.h b/media/libmedia/include/media/MediaRecorderBase.h
index 748153c..5340dde 100644
--- a/media/libmedia/include/media/MediaRecorderBase.h
+++ b/media/libmedia/include/media/MediaRecorderBase.h
@@ -19,10 +19,13 @@
#define MEDIA_RECORDER_BASE_H_
#include <media/AudioSystem.h>
+#include <media/MicrophoneInfo.h>
#include <media/mediarecorder.h>
#include <system/audio.h>
+#include <vector>
+
namespace android {
class ICameraRecordingProxy;
@@ -67,6 +70,9 @@
virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId) = 0;
virtual void setAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback) = 0;
virtual status_t enableAudioDeviceCallback(bool enabled) = 0;
+ virtual status_t getActiveMicrophones(
+ std::vector<media::MicrophoneInfo>* activeMicrophones) = 0;
+
protected:
diff --git a/media/libmedia/include/media/mediarecorder.h b/media/libmedia/include/media/mediarecorder.h
index 5f2a6fe..d8b0fe7 100644
--- a/media/libmedia/include/media/mediarecorder.h
+++ b/media/libmedia/include/media/mediarecorder.h
@@ -24,6 +24,7 @@
#include <utils/Errors.h>
#include <media/IMediaRecorderClient.h>
#include <media/IMediaDeathNotifier.h>
+#include <media/MicrophoneInfo.h>
namespace android {
@@ -258,6 +259,7 @@
status_t setInputDevice(audio_port_handle_t deviceId);
status_t getRoutedDeviceId(audio_port_handle_t *deviceId);
status_t enableAudioDeviceCallback(bool enabled);
+ status_t getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones);
private:
void doCleanUp();
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index aab845b..721a043 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -829,4 +829,15 @@
return mMediaRecorder->enableAudioDeviceCallback(enabled);
}
+status_t MediaRecorder::getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones)
+{
+ ALOGV("getActiveMicrophones");
+
+ if (mMediaRecorder == NULL) {
+ ALOGE("media recorder is not initialized yet");
+ return INVALID_OPERATION;
+ }
+ return mMediaRecorder->getActiveMicrophones(activeMicrophones);
+}
+
} // namespace android
diff --git a/media/libmedia/nuplayer2/GenericSource.cpp b/media/libmedia/nuplayer2/GenericSource.cpp
index 094af7e..6907216 100644
--- a/media/libmedia/nuplayer2/GenericSource.cpp
+++ b/media/libmedia/nuplayer2/GenericSource.cpp
@@ -1647,19 +1647,15 @@
return OK; // source without DRM info
}
- Parcel parcel;
- NuPlayer2Drm::retrieveDrmInfo(pssh, psshsize, &parcel);
- ALOGV("checkDrmInfo: MEDIA2_DRM_INFO PSSH size: %d Parcel size: %d objects#: %d",
- (int)psshsize, (int)parcel.dataSize(), (int)parcel.objectsCount());
+ sp<ABuffer> drmInfoBuffer = NuPlayer2Drm::retrieveDrmInfo(pssh, psshsize);
+ ALOGV("checkDrmInfo: MEDIA2_DRM_INFO PSSH size: %d drmInfoBuffer size: %d",
+ (int)psshsize, (int)drmInfoBuffer->size());
- if (parcel.dataSize() == 0) {
- ALOGE("checkDrmInfo: Unexpected parcel size: 0");
+ if (drmInfoBuffer->size() == 0) {
+ ALOGE("checkDrmInfo: Unexpected drmInfoBuffer size: 0");
return UNKNOWN_ERROR;
}
- // Can't pass parcel as a message to the player. Converting Parcel->ABuffer to pass it
- // to the Player's onSourceNotify then back to Parcel for calling driver's notifyListener.
- sp<ABuffer> drmInfoBuffer = ABuffer::CreateAsCopy(parcel.data(), parcel.dataSize());
notifyDrmInfo(drmInfoBuffer);
return OK;
diff --git a/media/libmedia/nuplayer2/NuPlayer2.cpp b/media/libmedia/nuplayer2/NuPlayer2.cpp
index 2745219..2c7f416 100644
--- a/media/libmedia/nuplayer2/NuPlayer2.cpp
+++ b/media/libmedia/nuplayer2/NuPlayer2.cpp
@@ -64,7 +64,7 @@
namespace android {
-static status_t sendMetaDataToHal(sp<MediaPlayer2Base::AudioSink>& sink,
+static status_t sendMetaDataToHal(sp<MediaPlayer2Interface::AudioSink>& sink,
const sp<MetaData>& meta) {
int32_t sampleRate = 0;
int32_t bitRate = 0;
@@ -417,7 +417,7 @@
msg->post();
}
-void NuPlayer2::setAudioSink(const sp<MediaPlayer2Base::AudioSink> &sink) {
+void NuPlayer2::setAudioSink(const sp<MediaPlayer2Interface::AudioSink> &sink) {
sp<AMessage> msg = new AMessage(kWhatSetAudioSink, this);
msg->setObject("sink", sink);
msg->post();
@@ -850,7 +850,7 @@
sp<RefBase> obj;
CHECK(msg->findObject("sink", &obj));
- mAudioSink = static_cast<MediaPlayer2Base::AudioSink *>(obj.get());
+ mAudioSink = static_cast<MediaPlayer2Interface::AudioSink *>(obj.get());
break;
}
diff --git a/media/libmedia/nuplayer2/NuPlayer2.h b/media/libmedia/nuplayer2/NuPlayer2.h
index 638b259..23c4fdf 100644
--- a/media/libmedia/nuplayer2/NuPlayer2.h
+++ b/media/libmedia/nuplayer2/NuPlayer2.h
@@ -61,7 +61,7 @@
void setVideoSurfaceTextureAsync(const sp<ANativeWindowWrapper> &nww);
- void setAudioSink(const sp<MediaPlayer2Base::AudioSink> &sink);
+ void setAudioSink(const sp<MediaPlayer2Interface::AudioSink> &sink);
status_t setPlaybackSettings(const AudioPlaybackRate &rate);
status_t getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */);
status_t setSyncSettings(const AVSyncSettings &sync, float videoFpsHint);
@@ -168,7 +168,7 @@
sp<Source> mSource;
uint32_t mSourceFlags;
sp<ANativeWindowWrapper> mNativeWindow;
- sp<MediaPlayer2Base::AudioSink> mAudioSink;
+ sp<MediaPlayer2Interface::AudioSink> mAudioSink;
sp<DecoderBase> mVideoDecoder;
bool mOffloadAudio;
sp<DecoderBase> mAudioDecoder;
diff --git a/media/libmedia/nuplayer2/NuPlayer2Driver.cpp b/media/libmedia/nuplayer2/NuPlayer2Driver.cpp
index ccfcc47..e48acea 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Driver.cpp
+++ b/media/libmedia/nuplayer2/NuPlayer2Driver.cpp
@@ -129,7 +129,6 @@
// set up an analytics record
mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer);
- mAnalyticsItem->generateSessionID();
mNuPlayer2Looper->start(
false, /* runOnCallingThread */
@@ -661,14 +660,12 @@
// So the canonical "empty" record has 3 elements in it.
if (mAnalyticsItem->count() > 3) {
- mAnalyticsItem->setFinalized(true);
mAnalyticsItem->selfrecord();
// re-init in case we prepare() and start() again.
delete mAnalyticsItem ;
mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer);
if (mAnalyticsItem) {
- mAnalyticsItem->generateSessionID();
mAnalyticsItem->setUid(mClientUid);
}
} else {
@@ -736,10 +733,6 @@
return OK;
}
-player2_type NuPlayer2Driver::playerType() {
- return PLAYER2_NU_PLAYER2;
-}
-
status_t NuPlayer2Driver::invoke(const Parcel &request, Parcel *reply) {
if (reply == NULL) {
ALOGE("reply is a NULL pointer");
diff --git a/media/libmedia/nuplayer2/NuPlayer2Driver.h b/media/libmedia/nuplayer2/NuPlayer2Driver.h
index d393f9d..7bbb367 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Driver.h
+++ b/media/libmedia/nuplayer2/NuPlayer2Driver.h
@@ -66,7 +66,6 @@
virtual status_t reset();
virtual status_t notifyAt(int64_t mediaTimeUs) override;
virtual status_t setLooping(int loop);
- virtual player2_type playerType();
virtual status_t invoke(const Parcel &request, Parcel *reply);
virtual void setAudioSink(const sp<AudioSink> &audioSink);
virtual status_t setParameter(int key, const Parcel &request);
diff --git a/media/libmedia/nuplayer2/NuPlayer2Drm.cpp b/media/libmedia/nuplayer2/NuPlayer2Drm.cpp
index 4751849..4853ae1 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Drm.cpp
+++ b/media/libmedia/nuplayer2/NuPlayer2Drm.cpp
@@ -21,7 +21,7 @@
#include <media/NdkWrapper.h>
#include <utils/Log.h>
-
+#include <sstream>
namespace android {
@@ -105,26 +105,70 @@
return supportedDRMs;
}
-// Parcel has only private copy constructor so passing it in rather than returning
-void NuPlayer2Drm::retrieveDrmInfo(const void *pssh, size_t psshsize, Parcel *parcel)
+sp<ABuffer> NuPlayer2Drm::retrieveDrmInfo(const void *pssh, uint32_t psshsize)
{
- // 1) PSSH bytes
- parcel->writeUint32(psshsize);
- parcel->writeByteArray(psshsize, (const uint8_t*)pssh);
+ std::ostringstream buf;
- ALOGV("retrieveDrmInfo: MEDIA2_DRM_INFO PSSH: size: %zu %s", psshsize,
+ // 1) PSSH bytes
+ buf.write(reinterpret_cast<const char *>(&psshsize), sizeof(psshsize));
+ buf.write(reinterpret_cast<const char *>(pssh), psshsize);
+
+ ALOGV("retrieveDrmInfo: MEDIA2_DRM_INFO PSSH: size: %u %s", psshsize,
DrmUUID::arrayToHex((uint8_t*)pssh, psshsize).string());
// 2) supportedDRMs
Vector<DrmUUID> supportedDRMs = getSupportedDrmSchemes(pssh, psshsize);
- parcel->writeUint32(supportedDRMs.size());
- for (size_t i = 0; i < supportedDRMs.size(); i++) {
+ uint32_t n = supportedDRMs.size();
+ buf.write(reinterpret_cast<char *>(&n), sizeof(n));
+ for (size_t i = 0; i < n; i++) {
DrmUUID uuid = supportedDRMs[i];
- parcel->writeByteArray(DrmUUID::UUID_SIZE, uuid.ptr());
+ buf.write(reinterpret_cast<const char *>(&n), sizeof(n));
+ buf.write(reinterpret_cast<const char *>(uuid.ptr()), DrmUUID::UUID_SIZE);
ALOGV("retrieveDrmInfo: MEDIA2_DRM_INFO supportedScheme[%zu] %s", i,
uuid.toHexString().string());
}
+
+ sp<ABuffer> drmInfoBuffer = ABuffer::CreateAsCopy(buf.str().c_str(), buf.tellp());
+ return drmInfoBuffer;
+}
+
+sp<ABuffer> NuPlayer2Drm::retrieveDrmInfo(PsshInfo *psshInfo)
+{
+
+ std::ostringstream pssh, drmInfo;
+
+ // 0) Generate PSSH bytes
+ for (size_t i = 0; i < psshInfo->numentries; i++) {
+ PsshEntry *entry = &psshInfo->entries[i];
+ uint32_t datalen = entry->datalen;
+ pssh.write(reinterpret_cast<const char *>(&entry->uuid), sizeof(entry->uuid));
+ pssh.write(reinterpret_cast<const char *>(&datalen), sizeof(datalen));
+ pssh.write(reinterpret_cast<const char *>(entry->data), datalen);
+ }
+
+ uint32_t psshSize = pssh.tellp();
+ const uint8_t* psshPtr = reinterpret_cast<const uint8_t*>(pssh.str().c_str());
+ const char *psshHex = DrmUUID::arrayToHex(psshPtr, psshSize).string();
+ ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO PSSH: size: %u %s", psshSize, psshHex);
+
+ // 1) Write PSSH bytes
+ drmInfo.write(reinterpret_cast<const char *>(&psshSize), sizeof(psshSize));
+ drmInfo.write(reinterpret_cast<const char *>(pssh.str().c_str()), psshSize);
+
+ // 2) Write supportedDRMs
+ uint32_t numentries = psshInfo->numentries;
+ drmInfo.write(reinterpret_cast<const char *>(&numentries), sizeof(numentries));
+ for (size_t i = 0; i < numentries; i++) {
+ PsshEntry *entry = &psshInfo->entries[i];
+ drmInfo.write(reinterpret_cast<const char *>(&entry->uuid), sizeof(entry->uuid));
+ ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO supportedScheme[%zu] %s", i,
+ DrmUUID::arrayToHex((const uint8_t*)&entry->uuid, sizeof(AMediaUUID)).string());
+ }
+
+ sp<ABuffer> drmInfoBuf = ABuffer::CreateAsCopy(drmInfo.str().c_str(), drmInfo.tellp());
+ drmInfoBuf->setRange(0, drmInfo.tellp());
+ return drmInfoBuf;
}
} // namespace android
diff --git a/media/libmedia/nuplayer2/NuPlayer2Drm.h b/media/libmedia/nuplayer2/NuPlayer2Drm.h
index f9c8711..99d2415 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Drm.h
+++ b/media/libmedia/nuplayer2/NuPlayer2Drm.h
@@ -17,8 +17,11 @@
#ifndef NUPLAYER2_DRM_H_
#define NUPLAYER2_DRM_H_
-#include <binder/Parcel.h>
+#include <media/NdkMediaExtractor.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
namespace android {
@@ -76,8 +79,8 @@
// static helpers - public
public:
- // Parcel has only private copy constructor so passing it in rather than returning
- static void retrieveDrmInfo(const void *pssh, size_t psshsize, Parcel *parcel);
+ static sp<ABuffer> retrieveDrmInfo(const void *pssh, uint32_t psshsize);
+ static sp<ABuffer> retrieveDrmInfo(PsshInfo *);
}; // NuPlayer2Drm
diff --git a/media/libmedia/nuplayer2/NuPlayer2Renderer.cpp b/media/libmedia/nuplayer2/NuPlayer2Renderer.cpp
index 1a9f246..a0bd900 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Renderer.cpp
+++ b/media/libmedia/nuplayer2/NuPlayer2Renderer.cpp
@@ -87,7 +87,7 @@
const int64_t NuPlayer2::Renderer::kMinPositionUpdateDelayUs = 100000ll;
NuPlayer2::Renderer::Renderer(
- const sp<MediaPlayer2Base::AudioSink> &sink,
+ const sp<MediaPlayer2Interface::AudioSink> &sink,
const sp<MediaClock> &mediaClock,
const sp<AMessage> ¬ify,
uint32_t flags)
@@ -805,28 +805,28 @@
// static
size_t NuPlayer2::Renderer::AudioSinkCallback(
- MediaPlayer2Base::AudioSink * /* audioSink */,
+ MediaPlayer2Interface::AudioSink * /* audioSink */,
void *buffer,
size_t size,
void *cookie,
- MediaPlayer2Base::AudioSink::cb_event_t event) {
+ MediaPlayer2Interface::AudioSink::cb_event_t event) {
NuPlayer2::Renderer *me = (NuPlayer2::Renderer *)cookie;
switch (event) {
- case MediaPlayer2Base::AudioSink::CB_EVENT_FILL_BUFFER:
+ case MediaPlayer2Interface::AudioSink::CB_EVENT_FILL_BUFFER:
{
return me->fillAudioBuffer(buffer, size);
break;
}
- case MediaPlayer2Base::AudioSink::CB_EVENT_STREAM_END:
+ case MediaPlayer2Interface::AudioSink::CB_EVENT_STREAM_END:
{
ALOGV("AudioSink::CB_EVENT_STREAM_END");
me->notifyEOSCallback();
break;
}
- case MediaPlayer2Base::AudioSink::CB_EVENT_TEAR_DOWN:
+ case MediaPlayer2Interface::AudioSink::CB_EVENT_TEAR_DOWN:
{
ALOGV("AudioSink::CB_EVENT_TEAR_DOWN");
me->notifyAudioTearDown(kDueToError);
diff --git a/media/libmedia/nuplayer2/NuPlayer2Renderer.h b/media/libmedia/nuplayer2/NuPlayer2Renderer.h
index 3007654..62cf0d8 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Renderer.h
+++ b/media/libmedia/nuplayer2/NuPlayer2Renderer.h
@@ -35,15 +35,15 @@
FLAG_REAL_TIME = 1,
FLAG_OFFLOAD_AUDIO = 2,
};
- Renderer(const sp<MediaPlayer2Base::AudioSink> &sink,
+ Renderer(const sp<MediaPlayer2Interface::AudioSink> &sink,
const sp<MediaClock> &mediaClock,
const sp<AMessage> ¬ify,
uint32_t flags = 0);
static size_t AudioSinkCallback(
- MediaPlayer2Base::AudioSink *audioSink,
+ MediaPlayer2Interface::AudioSink *audioSink,
void *data, size_t size, void *me,
- MediaPlayer2Base::AudioSink::cb_event_t event);
+ MediaPlayer2Interface::AudioSink::cb_event_t event);
void queueBuffer(
bool audio,
@@ -148,7 +148,7 @@
static const int64_t kMinPositionUpdateDelayUs;
- sp<MediaPlayer2Base::AudioSink> mAudioSink;
+ sp<MediaPlayer2Interface::AudioSink> mAudioSink;
bool mUseVirtualAudioSink;
sp<AMessage> mNotify;
Mutex mLock;
diff --git a/media/libmediaextractor/Android.bp b/media/libmediaextractor/Android.bp
index 984d4f1..4071fba 100644
--- a/media/libmediaextractor/Android.bp
+++ b/media/libmediaextractor/Android.bp
@@ -29,6 +29,7 @@
"MediaSourceBase.cpp",
"MediaSource.cpp",
"MediaExtractor.cpp",
+ "MetaData.cpp",
],
clang: true,
diff --git a/media/libstagefright/foundation/MetaData.cpp b/media/libmediaextractor/MetaData.cpp
similarity index 99%
rename from media/libstagefright/foundation/MetaData.cpp
rename to media/libmediaextractor/MetaData.cpp
index 2415c61..98cddbe 100644
--- a/media/libstagefright/foundation/MetaData.cpp
+++ b/media/libmediaextractor/MetaData.cpp
@@ -17,6 +17,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MetaData"
#include <inttypes.h>
+#include <binder/Parcel.h>
#include <utils/KeyedVector.h>
#include <utils/Log.h>
diff --git a/media/libmediaextractor/include/media/DataSource.h b/media/libmediaextractor/include/media/DataSource.h
index 9925a21..9f00e0a 100644
--- a/media/libmediaextractor/include/media/DataSource.h
+++ b/media/libmediaextractor/include/media/DataSource.h
@@ -59,20 +59,6 @@
bool getUInt32Var(off64_t offset, uint32_t *x, size_t size);
bool getUInt64Var(off64_t offset, uint64_t *x, size_t size);
- // Reads in "count" entries of type T into vector *x.
- // Returns true if "count" entries can be read.
- // If fewer than "count" entries can be read, return false. In this case,
- // the output vector *x will still have those entries that were read. Call
- // x->size() to obtain the number of entries read.
- // The optional parameter chunkSize specifies how many entries should be
- // read from the data source at one time into a temporary buffer. Increasing
- // chunkSize can improve the performance at the cost of extra memory usage.
- // The default value for chunkSize is set to read at least 4k bytes at a
- // time, depending on sizeof(T).
- template <typename T>
- bool getVector(off64_t offset, Vector<T>* x, size_t count,
- size_t chunkSize = (4095 / sizeof(T)) + 1);
-
// May return ERROR_UNSUPPORTED.
virtual status_t getSize(off64_t *size);
@@ -111,51 +97,6 @@
DataSource &operator=(const DataSource &);
};
-template <typename T>
-bool DataSource::getVector(off64_t offset, Vector<T>* x, size_t count,
- size_t chunkSize)
-{
- x->clear();
- if (chunkSize == 0) {
- return false;
- }
- if (count == 0) {
- return true;
- }
-
- T tmp[chunkSize];
- ssize_t numBytesRead;
- size_t numBytesPerChunk = chunkSize * sizeof(T);
- size_t i;
-
- for (i = 0; i + chunkSize < count; i += chunkSize) {
- // This loops is executed when more than chunkSize records need to be
- // read.
- numBytesRead = this->readAt(offset, (void*)&tmp, numBytesPerChunk);
- if (numBytesRead == -1) { // If readAt() returns -1, there is an error.
- return false;
- }
- if (static_cast<size_t>(numBytesRead) < numBytesPerChunk) {
- // This case is triggered when the stream ends before the whole
- // chunk is read.
- x->appendArray(tmp, (size_t)numBytesRead / sizeof(T));
- return false;
- }
- x->appendArray(tmp, chunkSize);
- offset += numBytesPerChunk;
- }
-
- // There are (count - i) more records to read.
- // Right now, (count - i) <= chunkSize.
- // We do the same thing as above, but with chunkSize replaced by count - i.
- numBytesRead = this->readAt(offset, (void*)&tmp, (count - i) * sizeof(T));
- if (numBytesRead == -1) {
- return false;
- }
- x->appendArray(tmp, (size_t)numBytesRead / sizeof(T));
- return x->size() == count;
-}
-
} // namespace android
#endif // DATA_SOURCE_H_
diff --git a/media/libmediaextractor/include/media/MediaExtractor.h b/media/libmediaextractor/include/media/MediaExtractor.h
index 15a5d8c..27581f3 100644
--- a/media/libmediaextractor/include/media/MediaExtractor.h
+++ b/media/libmediaextractor/include/media/MediaExtractor.h
@@ -22,6 +22,7 @@
#include <vector>
#include <utils/Errors.h>
+#include <utils/Log.h>
#include <utils/RefBase.h>
namespace android {
diff --git a/media/libmediaextractor/include/media/MediaSourceBase.h b/media/libmediaextractor/include/media/MediaSourceBase.h
index 77d4fc9..9db6099 100644
--- a/media/libmediaextractor/include/media/MediaSourceBase.h
+++ b/media/libmediaextractor/include/media/MediaSourceBase.h
@@ -24,6 +24,7 @@
#include <binder/MemoryDealer.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
+#include <utils/Log.h>
#include <utils/RefBase.h>
#include <utils/Vector.h>
diff --git a/media/libstagefright/include/media/stagefright/MetaData.h b/media/libmediaextractor/include/media/stagefright/MetaData.h
similarity index 99%
rename from media/libstagefright/include/media/stagefright/MetaData.h
rename to media/libmediaextractor/include/media/stagefright/MetaData.h
index 5959e86..e4a84b7 100644
--- a/media/libstagefright/include/media/stagefright/MetaData.h
+++ b/media/libmediaextractor/include/media/stagefright/MetaData.h
@@ -22,12 +22,13 @@
#include <stdint.h>
-#include <binder/Parcel.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
namespace android {
+class Parcel;
+
// The following keys map to int32_t data unless indicated otherwise.
enum {
kKeyMIMEType = 'mime', // cstring
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
index 2e7efad..dc2bec8 100644
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ b/media/libmediametrics/MediaAnalyticsItem.cpp
@@ -60,7 +60,7 @@
mPkgVersionCode(0),
mSessionID(MediaAnalyticsItem::SessionIDNone),
mTimestamp(0),
- mFinalized(0),
+ mFinalized(1),
mPropCount(0), mPropSize(0), mProps(NULL)
{
mKey = MediaAnalyticsItem::kKeyNone;
@@ -72,7 +72,7 @@
mPkgVersionCode(0),
mSessionID(MediaAnalyticsItem::SessionIDNone),
mTimestamp(0),
- mFinalized(0),
+ mFinalized(1),
mPropCount(0), mPropSize(0), mProps(NULL)
{
if (DEBUG_ALLOCATIONS) {
@@ -137,16 +137,6 @@
return dst;
}
-// so clients can send intermediate values to be overlaid later
-MediaAnalyticsItem &MediaAnalyticsItem::setFinalized(bool value) {
- mFinalized = value;
- return *this;
-}
-
-bool MediaAnalyticsItem::getFinalized() const {
- return mFinalized;
-}
-
MediaAnalyticsItem &MediaAnalyticsItem::setSessionID(MediaAnalyticsItem::SessionID_t id) {
mSessionID = id;
return *this;
@@ -636,7 +626,10 @@
mPkgName = data.readCString();
mPkgVersionCode = data.readInt64();
mSessionID = data.readInt64();
+ // We no longer pay attention to user setting of finalized, BUT it's
+ // still part of the wire packet -- so read & discard.
mFinalized = data.readInt32();
+ mFinalized = 1;
mTimestamp = data.readInt64();
int count = data.readInt32();
@@ -978,9 +971,6 @@
mSessionID = incoming->mSessionID;
}
- // we always take the more recent 'finalized' value
- setFinalized(incoming->getFinalized());
-
// for each attribute from 'incoming', resolve appropriately
int nattr = incoming->mPropCount;
for (int i = 0 ; i < nattr; i++ ) {
diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h
index 79ff093..263cde7 100644
--- a/media/libmediametrics/include/MediaAnalyticsItem.h
+++ b/media/libmediametrics/include/MediaAnalyticsItem.h
@@ -89,10 +89,6 @@
MediaAnalyticsItem(Key);
~MediaAnalyticsItem();
- // so clients can send intermediate values to be overlaid later
- MediaAnalyticsItem &setFinalized(bool);
- bool getFinalized() const;
-
// SessionID ties multiple submissions for same key together
// so that if video "height" and "width" are known at one point
// and "framerate" is only known later, they can be be brought
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index dcd393b..4206647 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -527,4 +527,14 @@
}
return NO_INIT;
}
+
+status_t MediaRecorderClient::getActiveMicrophones(
+ std::vector<media::MicrophoneInfo>* activeMicrophones) {
+ ALOGV("getActiveMicrophones");
+ Mutex::Autolock lock(mLock);
+ if (mRecorder != NULL) {
+ return mRecorder->getActiveMicrophones(activeMicrophones);
+ }
+ return NO_INIT;
+}
}; // namespace android
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index 538b461..d2e681f 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -107,6 +107,8 @@
virtual status_t setInputDevice(audio_port_handle_t deviceId);
virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
virtual status_t enableAudioDeviceCallback(bool enabled);
+ virtual status_t getActiveMicrophones(
+ std::vector<media::MicrophoneInfo>* activeMicrophones);
private:
friend class MediaPlayerService; // for accessing private constructor
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 77eaefe..bc8d8c8 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -125,7 +125,6 @@
if (mAnalyticsDirty && mAnalyticsItem != NULL) {
updateMetrics();
if (mAnalyticsItem->count() > 0) {
- mAnalyticsItem->setFinalized(true);
mAnalyticsItem->selfrecord();
}
delete mAnalyticsItem;
@@ -185,14 +184,12 @@
if (mAnalyticsDirty && mAnalyticsItem != NULL) {
updateMetrics();
if (mAnalyticsItem->count() > 0) {
- mAnalyticsItem->setFinalized(true);
mAnalyticsItem->selfrecord();
}
delete mAnalyticsItem;
mAnalyticsItem = NULL;
}
mAnalyticsItem = new MediaAnalyticsItem(kKeyRecorder);
- (void) mAnalyticsItem->generateSessionID();
mAnalyticsDirty = false;
}
@@ -2163,6 +2160,15 @@
return NO_ERROR;
}
+status_t StagefrightRecorder::getActiveMicrophones(
+ std::vector<media::MicrophoneInfo>* activeMicrophones) {
+ if (mAudioSourceNode != 0) {
+ return mAudioSourceNode->getActiveMicrophones(activeMicrophones);
+ }
+ return NO_INIT;
+}
+
+
status_t StagefrightRecorder::dump(
int fd, const Vector<String16>& args) const {
ALOGV("dump");
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index ec7e8ed..18c9256 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -76,6 +76,8 @@
virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
virtual void setAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
virtual status_t enableAudioDeviceCallback(bool enabled);
+ virtual status_t getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones);
+
private:
mutable Mutex mLock;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 8aa06fc..b3fd00a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -91,7 +91,6 @@
// set up an analytics record
mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer);
- mAnalyticsItem->generateSessionID();
mLooper->start(
false, /* runOnCallingThread */
@@ -617,14 +616,12 @@
// So the canonical "empty" record has 3 elements in it.
if (mAnalyticsItem->count() > 3) {
- mAnalyticsItem->setFinalized(true);
mAnalyticsItem->selfrecord();
// re-init in case we prepare() and start() again.
delete mAnalyticsItem ;
mAnalyticsItem = new MediaAnalyticsItem("nuplayer");
if (mAnalyticsItem) {
- mAnalyticsItem->generateSessionID();
mAnalyticsItem->setUid(mClientUid);
}
} else {
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 90f6ed7..c5abe9b 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -26,6 +26,28 @@
shared_libs: ["libmedia"],
}
+cc_library_static {
+ name: "libstagefright_metadatautils",
+
+ srcs: ["MetaDataUtils.cpp"],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
+
+ shared_libs: ["libmedia"],
+}
+
cc_library_shared {
name: "libstagefright",
@@ -78,8 +100,8 @@
"StagefrightMetadataRetriever.cpp",
"SurfaceMediaSource.cpp",
"SurfaceUtils.cpp",
- "ThrottledSource.cpp",
"Utils.cpp",
+ "ThrottledSource.cpp",
"VideoFrameScheduler.cpp",
],
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index b8da980..70ce38c 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -498,4 +498,12 @@
return NO_INIT;
}
+status_t AudioSource::getActiveMicrophones(
+ std::vector<media::MicrophoneInfo>* activeMicrophones) {
+ if (mRecord != 0) {
+ return mRecord->getActiveMicrophones(activeMicrophones);
+ }
+ return NO_INIT;
+}
+
} // namespace android
diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp
index 2f4a7ea..04f7593 100644
--- a/media/libstagefright/CCodecBufferChannel.cpp
+++ b/media/libstagefright/CCodecBufferChannel.cpp
@@ -312,6 +312,7 @@
mBufferArray[i].available = false;
*index = i;
*buffer = mBufferArray[i].clientBuffer;
+ (*buffer)->meta()->clear();
(*buffer)->setRange(0, (*buffer)->capacity());
return true;
}
@@ -487,6 +488,7 @@
*index = i;
*codecBuffer = mBufferArray[i].clientBuffer;
(*codecBuffer)->setFormat(mFormat);
+ (*codecBuffer)->meta()->clear();
mBufferArray[i].compBuffer = buffer;
mBufferArray[i].available = false;
return true;
@@ -512,6 +514,7 @@
*index = i;
*codecBuffer = mBufferArray[i].clientBuffer;
(*codecBuffer)->setFormat(mFormat);
+ (*codecBuffer)->meta()->clear();
mBufferArray[i].available = false;
return true;
}
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 559b108..56ac3ed 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -502,7 +502,6 @@
// set up our new record, get a sessionID, put it into the in-progress list
mAnalyticsItem = new MediaAnalyticsItem(kCodecKeyName);
if (mAnalyticsItem != NULL) {
- (void) mAnalyticsItem->generateSessionID();
// don't record it yet; only at the end, when we have decided that we have
// data worth writing (e.g. .count() > 0)
}
@@ -512,7 +511,6 @@
if (mAnalyticsItem != NULL) {
// don't log empty records
if (mAnalyticsItem->count() > 0) {
- mAnalyticsItem->setFinalized(true);
mAnalyticsItem->selfrecord();
}
delete mAnalyticsItem;
diff --git a/media/libstagefright/MetaDataUtils.cpp b/media/libstagefright/MetaDataUtils.cpp
new file mode 100644
index 0000000..fd51a2c
--- /dev/null
+++ b/media/libstagefright/MetaDataUtils.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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 "MetaDataUtils"
+
+#include <media/stagefright/foundation/avc_utils.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaDataUtils.h>
+
+namespace android {
+
+sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit) {
+ int32_t width;
+ int32_t height;
+ int32_t sarWidth;
+ int32_t sarHeight;
+ sp<ABuffer> csd = MakeAVCCodecSpecificData(accessUnit, &width, &height, &sarWidth, &sarHeight);
+ if (csd == nullptr) {
+ return nullptr;
+ }
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+
+ meta->setData(kKeyAVCC, kTypeAVCC, csd->data(), csd->size());
+ meta->setInt32(kKeyWidth, width);
+ meta->setInt32(kKeyHeight, height);
+ if (sarWidth > 0 && sarHeight > 0) {
+ meta->setInt32(kKeySARWidth, sarWidth);
+ meta->setInt32(kKeySARHeight, sarHeight);
+ }
+ return meta;
+}
+
+sp<MetaData> MakeAACCodecSpecificData(
+ unsigned profile, unsigned sampling_freq_index,
+ unsigned channel_configuration) {
+ int32_t sampleRate;
+ int32_t channelCount;
+ sp<ABuffer> csd = MakeAACCodecSpecificData(profile, sampling_freq_index,
+ channel_configuration, &sampleRate, &channelCount);
+ if (csd == nullptr) {
+ return nullptr;
+ }
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+
+ meta->setInt32(kKeySampleRate, sampleRate);
+ meta->setInt32(kKeyChannelCount, channelCount);
+
+ meta->setData(kKeyESDS, 0, csd->data(), csd->size());
+ return meta;
+}
+
+} // namespace android
diff --git a/media/libstagefright/RemoteMediaExtractor.cpp b/media/libstagefright/RemoteMediaExtractor.cpp
index 43d33e5..3ebee51 100644
--- a/media/libstagefright/RemoteMediaExtractor.cpp
+++ b/media/libstagefright/RemoteMediaExtractor.cpp
@@ -44,7 +44,6 @@
mAnalyticsItem = nullptr;
if (MEDIA_LOG) {
mAnalyticsItem = new MediaAnalyticsItem(kKeyExtractor);
- (void) mAnalyticsItem->generateSessionID();
// track the container format (mpeg, aac, wvm, etc)
size_t ntracks = extractor->countTracks();
@@ -73,7 +72,6 @@
if (MEDIA_LOG) {
if (mAnalyticsItem != nullptr) {
if (mAnalyticsItem->count() > 0) {
- mAnalyticsItem->setFinalized(true);
mAnalyticsItem->selfrecord();
}
}
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 53699ef..4a93051 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -667,7 +667,7 @@
if (!strncasecmp("image/", mime, 6)) {
int32_t gridWidth, gridHeight, gridRows, gridCols;
if (meta->findInt32(kKeyGridWidth, &gridWidth)
- && meta->findInt32(kKeyHeight, &gridHeight)
+ && meta->findInt32(kKeyGridHeight, &gridHeight)
&& meta->findInt32(kKeyGridRows, &gridRows)
&& meta->findInt32(kKeyGridCols, &gridCols)) {
msg->setInt32("grid-width", gridWidth);
diff --git a/media/libstagefright/codec2/vndk/C2Store.cpp b/media/libstagefright/codec2/vndk/C2Store.cpp
index 496cbe1..a49fd24 100644
--- a/media/libstagefright/codec2/vndk/C2Store.cpp
+++ b/media/libstagefright/codec2/vndk/C2Store.cpp
@@ -407,6 +407,7 @@
mComponents.emplace("c2.google.avc.encoder", "libstagefright_soft_c2avcenc.so");
mComponents.emplace("c2.google.aac.decoder", "libstagefright_soft_c2aacdec.so");
mComponents.emplace("c2.google.aac.encoder", "libstagefright_soft_c2aacenc.so");
+ mComponents.emplace("c2.google.mp3.decoder", "libstagefright_soft_c2mp3dec.so");
}
c2_status_t C2PlatformComponentStore::copyBuffer(
diff --git a/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.h b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.h
index 5b2a514..a113436 100644
--- a/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.h
+++ b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.h
@@ -17,6 +17,8 @@
#ifndef C2_SOFT_AVC_ENC_H__
#define C2_SOFT_AVC_ENC_H__
+#include <map>
+
#include <media/stagefright/foundation/ABase.h>
#include <utils/Vector.h>
diff --git a/media/libstagefright/codecs/mp3dec/Android.bp b/media/libstagefright/codecs/mp3dec/Android.bp
index e6eb32b..9e41d17 100644
--- a/media/libstagefright/codecs/mp3dec/Android.bp
+++ b/media/libstagefright/codecs/mp3dec/Android.bp
@@ -119,6 +119,50 @@
compile_multilib: "32",
}
+cc_library_shared {
+ name: "libstagefright_soft_c2mp3dec",
+// vendor_available: true,
+// vndk: {
+// enabled: true,
+// },
+
+ srcs: ["C2SoftMP3.cpp"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ local_include_dirs: [
+ "src",
+ "include",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ ],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
+
+ static_libs: [
+ "libstagefright_mp3dec"
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ "libstagefright_codec2",
+ "libstagefright_codec2_vndk",
+ "libstagefright_foundation",
+ "libstagefright_simple_c2component",
+ ],
+}
+
//###############################################################################
cc_test {
name: "libstagefright_mp3dec_test",
diff --git a/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp
new file mode 100644
index 0000000..c34a6f0
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 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 "C2SoftMP3"
+#include <utils/Log.h>
+
+#include "pvmp3decoder_api.h"
+
+#include "C2SoftMP3.h"
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <numeric>
+
+namespace android {
+
+C2SoftMP3::C2SoftMP3(const char *name, c2_node_id_t id)
+ : SimpleC2Component(
+ SimpleC2Interface::Builder(name, id)
+ .inputFormat(C2FormatCompressed)
+ .outputFormat(C2FormatAudio)
+ .build()),
+ mConfig(nullptr),
+ mDecoderBuf(nullptr) {
+}
+
+C2SoftMP3::~C2SoftMP3() {
+ onRelease();
+}
+
+c2_status_t C2SoftMP3::onInit() {
+ status_t err = initDecoder();
+ return err == OK ? C2_OK : C2_NO_MEMORY;
+}
+
+c2_status_t C2SoftMP3::onStop() {
+ // Make sure that the next buffer output does not still
+ // depend on fragments from the last one decoded.
+ pvmp3_InitDecoder(mConfig, mDecoderBuf);
+ mSignalledError = false;
+ mIsFirst = true;
+ mSignalledOutputEos = false;
+
+ return C2_OK;
+}
+
+void C2SoftMP3::onReset() {
+ (void)onStop();
+}
+
+void C2SoftMP3::onRelease() {
+ if (mDecoderBuf) {
+ free(mDecoderBuf);
+ mDecoderBuf = nullptr;
+ }
+
+ if (mConfig) {
+ delete mConfig;
+ mConfig = nullptr;
+ }
+}
+
+status_t C2SoftMP3::initDecoder() {
+ mConfig = new tPVMP3DecoderExternal{};
+ if (!mConfig) return NO_MEMORY;
+ mConfig->equalizerType = flat;
+ mConfig->crcEnabled = false;
+
+ size_t memRequirements = pvmp3_decoderMemRequirements();
+ mDecoderBuf = malloc(memRequirements);
+ if (!mDecoderBuf) return NO_MEMORY;
+
+ pvmp3_InitDecoder(mConfig, mDecoderBuf);
+
+ mNumChannels = 2;
+ mSamplingRate = 44100;
+ mIsFirst = true;
+ mSignalledError = false;
+ mSignalledOutputEos = false;
+
+ return OK;
+}
+
+/* The below code is borrowed from ./test/mp3reader.cpp */
+static bool parseMp3Header(uint32_t header, size_t *frame_size,
+ uint32_t *out_sampling_rate = nullptr,
+ uint32_t *out_channels = nullptr,
+ uint32_t *out_bitrate = nullptr,
+ uint32_t *out_num_samples = nullptr) {
+ *frame_size = 0;
+ if (out_sampling_rate) *out_sampling_rate = 0;
+ if (out_channels) *out_channels = 0;
+ if (out_bitrate) *out_bitrate = 0;
+ if (out_num_samples) *out_num_samples = 1152;
+
+ if ((header & 0xffe00000) != 0xffe00000) return false;
+
+ unsigned version = (header >> 19) & 3;
+ if (version == 0x01) return false;
+
+ unsigned layer = (header >> 17) & 3;
+ if (layer == 0x00) return false;
+
+ unsigned bitrate_index = (header >> 12) & 0x0f;
+ if (bitrate_index == 0 || bitrate_index == 0x0f) return false;
+
+ unsigned sampling_rate_index = (header >> 10) & 3;
+ if (sampling_rate_index == 3) return false;
+
+ static const int kSamplingRateV1[] = { 44100, 48000, 32000 };
+ int sampling_rate = kSamplingRateV1[sampling_rate_index];
+ if (version == 2 /* V2 */) {
+ sampling_rate /= 2;
+ } else if (version == 0 /* V2.5 */) {
+ sampling_rate /= 4;
+ }
+
+ unsigned padding = (header >> 9) & 1;
+
+ if (layer == 3) { // layer I
+ static const int kBitrateV1[] =
+ {
+ 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448
+ };
+ static const int kBitrateV2[] =
+ {
+ 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256
+ };
+
+ int bitrate = (version == 3 /* V1 */) ? kBitrateV1[bitrate_index - 1] :
+ kBitrateV2[bitrate_index - 1];
+
+ if (out_bitrate) {
+ *out_bitrate = bitrate;
+ }
+ *frame_size = (12000 * bitrate / sampling_rate + padding) * 4;
+ if (out_num_samples) {
+ *out_num_samples = 384;
+ }
+ } else { // layer II or III
+ static const int kBitrateV1L2[] =
+ {
+ 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384
+ };
+
+ static const int kBitrateV1L3[] =
+ {
+ 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320
+ };
+
+ static const int kBitrateV2[] =
+ {
+ 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160
+ };
+
+ int bitrate;
+ if (version == 3 /* V1 */) {
+ bitrate = (layer == 2 /* L2 */) ? kBitrateV1L2[bitrate_index - 1] :
+ kBitrateV1L3[bitrate_index - 1];
+
+ if (out_num_samples) {
+ *out_num_samples = 1152;
+ }
+ } else { // V2 (or 2.5)
+ bitrate = kBitrateV2[bitrate_index - 1];
+ if (out_num_samples) {
+ *out_num_samples = (layer == 1 /* L3 */) ? 576 : 1152;
+ }
+ }
+
+ if (out_bitrate) {
+ *out_bitrate = bitrate;
+ }
+
+ if (version == 3 /* V1 */) {
+ *frame_size = 144000 * bitrate / sampling_rate + padding;
+ } else { // V2 or V2.5
+ size_t tmp = (layer == 1 /* L3 */) ? 72000 : 144000;
+ *frame_size = tmp * bitrate / sampling_rate + padding;
+ }
+ }
+
+ if (out_sampling_rate) {
+ *out_sampling_rate = sampling_rate;
+ }
+
+ if (out_channels) {
+ int channel_mode = (header >> 6) & 3;
+
+ *out_channels = (channel_mode == 3) ? 1 : 2;
+ }
+
+ return true;
+}
+
+static uint32_t U32_AT(const uint8_t *ptr) {
+ return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
+}
+
+static status_t calculateOutSize(uint8 *header, size_t inSize,
+ std::vector<size_t> *decodedSizes) {
+ uint32_t channels;
+ uint32_t numSamples;
+ size_t frameSize;
+ size_t totalInSize = 0;
+
+ while (totalInSize + 4 < inSize) {
+ if (!parseMp3Header(U32_AT(header + totalInSize), &frameSize,
+ nullptr, &channels, nullptr, &numSamples)) {
+ ALOGE("Error in parse mp3 header during outSize estimation");
+ return UNKNOWN_ERROR;
+ }
+ totalInSize += frameSize;
+ decodedSizes->push_back(numSamples * channels * sizeof(int16_t));
+ }
+
+ if (decodedSizes->empty()) return UNKNOWN_ERROR;
+
+ return OK;
+}
+
+c2_status_t C2SoftMP3::onFlush_sm() {
+ return onStop();
+}
+
+c2_status_t C2SoftMP3::drain(
+ uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ (void) pool;
+ if (drainMode == NO_DRAIN) {
+ ALOGW("drain with NO_DRAIN: no-op");
+ return C2_OK;
+ }
+ if (drainMode == DRAIN_CHAIN) {
+ ALOGW("DRAIN_CHAIN not supported");
+ return C2_OMITTED;
+ }
+
+ return C2_OK;
+}
+
+// TODO: Can overall error checking be improved? As in the check for validity of
+// work, pool ptr, work->input.buffers.size() == 1, ...
+// TODO: Gapless playback: decoder has a delay of 529 samples. For the first
+// frame we intend to remove 529 samples worth of data. When this is
+// done it is going to effect the timestamps of future frames. This
+// timestamp correction is handled by the client or plugin? Soft omx mp3
+// plugin also has this problem
+// TODO: Blind removal of 529 samples from the output may not work. Because
+// mpeg layer 1 frame size is 384 samples per frame. This should introduce
+// negative values and can cause SEG faults. Soft omx mp3 plugin can have
+// this problem (CHECK!)
+void C2SoftMP3::process(
+ const std::unique_ptr<C2Work> &work,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ work->result = C2_OK;
+ work->workletsProcessed = 0u;
+ if (mSignalledError || mSignalledOutputEos) {
+ work->result = C2_BAD_VALUE;
+ return;
+ }
+
+ const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+ bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+ size_t inOffset = inBuffer.offset();
+ size_t inSize = inBuffer.size();
+ C2ReadView rView = inBuffer.map().get();
+ if (inSize && rView.error()) {
+ ALOGE("read view map failed %d", rView.error());
+ work->result = C2_CORRUPTED;
+ return;
+ }
+
+ if (inSize == 0) {
+ work->worklets.front()->output.flags = work->input.flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+ if (eos) {
+ mSignalledOutputEos = true;
+ ALOGV("signalled EOS");
+ }
+ return;
+ }
+ ALOGV("in buffer attr. size %zu timestamp %d frameindex %d", inSize,
+ (int)work->input.ordinal.timestamp.peeku(), (int)work->input.ordinal.frameIndex.peeku());
+
+ size_t calOutSize;
+ std::vector<size_t> decodedSizes;
+ if (OK != calculateOutSize(const_cast<uint8 *>(rView.data() + inOffset),
+ inSize, &decodedSizes)) {
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ calOutSize = std::accumulate(decodedSizes.begin(), decodedSizes.end(), 0);
+ std::shared_ptr<C2LinearBlock> block;
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ c2_status_t err = pool->fetchLinearBlock(calOutSize, usage, &block);
+ if (err != C2_OK) {
+ ALOGE("fetchLinearBlock for Output failed with status %d", err);
+ work->result = C2_NO_MEMORY;
+ return;
+ }
+ C2WriteView wView = block->map().get();
+ if (wView.error()) {
+ ALOGE("write view map failed %d", wView.error());
+ work->result = C2_CORRUPTED;
+ return;
+ }
+
+ int outSize = 0;
+ int outOffset = 0;
+ auto it = decodedSizes.begin();
+ while (inOffset < inSize) {
+ if (it == decodedSizes.end()) {
+ ALOGE("unexpected trailing bytes, ignoring them");
+ break;
+ }
+
+ mConfig->pInputBuffer = const_cast<uint8 *>(rView.data() + inOffset);
+ mConfig->inputBufferCurrentLength = (inSize - inOffset);
+ mConfig->inputBufferMaxLength = 0;
+ mConfig->inputBufferUsedLength = 0;
+ mConfig->outputFrameSize = (calOutSize - outSize);
+ mConfig->pOutputBuffer = reinterpret_cast<int16_t *> (wView.data() + outSize);
+
+ ERROR_CODE decoderErr;
+ if ((decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf))
+ != NO_DECODING_ERROR) {
+ ALOGE("mp3 decoder returned error %d", decoderErr);
+ if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR
+ && decoderErr != SIDE_INFO_ERROR) {
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+
+ // This is recoverable, just ignore the current frame and
+ // play silence instead.
+ ALOGV("ignoring error and sending silence");
+ if (mConfig->outputFrameSize == 0) {
+ mConfig->outputFrameSize = *it / sizeof(int16_t);
+ }
+ memset(mConfig->pOutputBuffer, 0, mConfig->outputFrameSize * sizeof(int16_t));
+ } else if (mConfig->samplingRate != mSamplingRate
+ || mConfig->num_channels != mNumChannels) {
+ mSamplingRate = mConfig->samplingRate;
+ mNumChannels = mConfig->num_channels;
+ }
+ if (*it != mConfig->outputFrameSize * sizeof(int16_t)) {
+ ALOGE("panic, parsed size does not match decoded size");
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ outSize += mConfig->outputFrameSize * sizeof(int16_t);
+ inOffset += mConfig->inputBufferUsedLength;
+ it++;
+ }
+ if (mIsFirst) {
+ mIsFirst = false;
+ // The decoder delay is 529 samples, so trim that many samples off
+ // the start of the first output buffer. This essentially makes this
+ // decoder have zero delay, which the rest of the pipeline assumes.
+ outOffset = kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t);
+ }
+ ALOGV("out buffer attr. offset %d size %d", outOffset, outSize);
+ decodedSizes.clear();
+ work->worklets.front()->output.flags = work->input.flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.buffers.push_back(createLinearBuffer(block, outOffset, outSize));
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+ if (eos) {
+ mSignalledOutputEos = true;
+ ALOGV("signalled EOS");
+ }
+}
+
+class C2SoftMp3DecFactory : public C2ComponentFactory {
+public:
+ virtual c2_status_t createComponent(
+ c2_node_id_t id, std::shared_ptr<C2Component>* const component,
+ std::function<void(::android::C2Component*)> deleter) override {
+ *component = std::shared_ptr<C2Component>(new C2SoftMP3("mp3", id), deleter);
+ return C2_OK;
+ }
+
+ virtual c2_status_t createInterface(
+ c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
+ std::function<void(::android::C2ComponentInterface*)> deleter) override {
+ *interface =
+ SimpleC2Interface::Builder("mp3", id, deleter)
+ .inputFormat(C2FormatCompressed)
+ .outputFormat(C2FormatAudio)
+ .build();
+ return C2_OK;
+ }
+
+ virtual ~C2SoftMp3DecFactory() override = default;
+};
+
+} // namespace android
+
+extern "C" ::android::C2ComponentFactory* CreateCodec2Factory() {
+ ALOGV("in %s", __func__);
+ return new ::android::C2SoftMp3DecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::android::C2ComponentFactory* factory) {
+ ALOGV("in %s", __func__);
+ delete factory;
+}
+
diff --git a/media/libstagefright/codecs/mp3dec/C2SoftMP3.h b/media/libstagefright/codecs/mp3dec/C2SoftMP3.h
new file mode 100644
index 0000000..ad974bd
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/C2SoftMP3.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 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 C2_SOFT_MP3_H_
+#define C2_SOFT_MP3_H_
+
+#include <SimpleC2Component.h>
+
+#include <media/stagefright/foundation/ABase.h>
+
+struct tPVMP3DecoderExternal;
+
+bool parseMp3Header(uint32_t header, size_t *frame_size,
+ uint32_t *out_sampling_rate = nullptr,
+ uint32_t *out_channels = nullptr,
+ uint32_t *out_bitrate = nullptr,
+ uint32_t *out_num_samples = nullptr);
+
+namespace android {
+
+struct C2SoftMP3 : public SimpleC2Component {
+ C2SoftMP3(const char *name, c2_node_id_t id);
+ virtual ~C2SoftMP3();
+
+ // From SimpleC2Component
+ c2_status_t onInit() override;
+ c2_status_t onStop() override;
+ void onReset() override;
+ void onRelease() override;
+ c2_status_t onFlush_sm() override;
+ void process(
+ const std::unique_ptr<C2Work> &work,
+ const std::shared_ptr<C2BlockPool> &pool) override;
+ c2_status_t drain(
+ uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool) override;
+
+private:
+ enum {
+ kPVMP3DecoderDelay = 529 // samples
+ };
+
+ tPVMP3DecoderExternal *mConfig;
+ void *mDecoderBuf;
+
+ int32_t mNumChannels;
+ int32_t mSamplingRate;
+ bool mIsFirst;
+ bool mSignalledError;
+ bool mSignalledOutputEos;
+
+ status_t initDecoder();
+
+ DISALLOW_EVIL_CONSTRUCTORS(C2SoftMP3);
+};
+
+} // namespace android
+
+#endif // C2_SOFT_MP3_H_
diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp
index 2258e2c..b343c16 100644
--- a/media/libstagefright/foundation/Android.bp
+++ b/media/libstagefright/foundation/Android.bp
@@ -64,7 +64,6 @@
"ColorUtils.cpp",
"MediaDefs.cpp",
"MediaKeys.cpp",
- "MetaData.cpp",
"ParsedMessage.cpp",
"avc_utils.cpp",
"base64.cpp",
diff --git a/media/libstagefright/foundation/avc_utils.cpp b/media/libstagefright/foundation/avc_utils.cpp
index bfaeb21..b58474c 100644
--- a/media/libstagefright/foundation/avc_utils.cpp
+++ b/media/libstagefright/foundation/avc_utils.cpp
@@ -383,7 +383,9 @@
}
}
-sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit) {
+sp<ABuffer> MakeAVCCodecSpecificData(
+ const sp<ABuffer> &accessUnit, int32_t *width, int32_t *height,
+ int32_t *sarWidth, int32_t *sarHeight) {
const uint8_t *data = accessUnit->data();
size_t size = accessUnit->size();
@@ -392,10 +394,8 @@
return NULL;
}
- int32_t width, height;
- int32_t sarWidth, sarHeight;
FindAVCDimensions(
- seqParamSet, &width, &height, &sarWidth, &sarHeight);
+ seqParamSet, width, height, sarWidth, sarHeight);
sp<ABuffer> picParamSet = FindNAL(data, size, 8);
CHECK(picParamSet != NULL);
@@ -434,38 +434,32 @@
hexdump(seqParamSet->data(), seqParamSet->size());
#endif
- sp<MetaData> meta = new MetaData;
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
- meta->setData(kKeyAVCC, kTypeAVCC, csd->data(), csd->size());
- meta->setInt32(kKeyWidth, width);
- meta->setInt32(kKeyHeight, height);
-
- if ((sarWidth > 0 && sarHeight > 0) && (sarWidth != 1 || sarHeight != 1)) {
- // We treat *:0 and 0:* (unspecified) as 1:1.
-
- meta->setInt32(kKeySARWidth, sarWidth);
- meta->setInt32(kKeySARHeight, sarHeight);
-
- ALOGI("found AVC codec config (%d x %d, %s-profile level %d.%d) "
- "SAR %d : %d",
- width,
- height,
- AVCProfileToString(profile),
- level / 10,
- level % 10,
- sarWidth,
- sarHeight);
- } else {
- ALOGI("found AVC codec config (%d x %d, %s-profile level %d.%d)",
- width,
- height,
- AVCProfileToString(profile),
- level / 10,
- level % 10);
+ if (sarWidth != nullptr && sarHeight != nullptr) {
+ if ((*sarWidth > 0 && *sarHeight > 0) && (*sarWidth != 1 || *sarHeight != 1)) {
+ ALOGI("found AVC codec config (%d x %d, %s-profile level %d.%d) "
+ "SAR %d : %d",
+ *width,
+ *height,
+ AVCProfileToString(profile),
+ level / 10,
+ level % 10,
+ *sarWidth,
+ *sarHeight);
+ } else {
+ // We treat *:0 and 0:* (unspecified) as 1:1.
+ *sarWidth = 0;
+ *sarHeight = 0;
+ ALOGI("found AVC codec config (%d x %d, %s-profile level %d.%d)",
+ *width,
+ *height,
+ AVCProfileToString(profile),
+ level / 10,
+ level % 10);
+ }
}
- return meta;
+ return csd;
}
bool IsIDR(const uint8_t *data, size_t size) {
@@ -543,19 +537,17 @@
return layerId;
}
-sp<MetaData> MakeAACCodecSpecificData(
+sp<ABuffer> MakeAACCodecSpecificData(
unsigned profile, unsigned sampling_freq_index,
- unsigned channel_configuration) {
- sp<MetaData> meta = new MetaData;
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
-
+ unsigned channel_configuration, int32_t *sampleRate,
+ int32_t *channelCount) {
CHECK_LE(sampling_freq_index, 11u);
static const int32_t kSamplingFreq[] = {
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
16000, 12000, 11025, 8000
};
- meta->setInt32(kKeySampleRate, kSamplingFreq[sampling_freq_index]);
- meta->setInt32(kKeyChannelCount, channel_configuration);
+ *sampleRate = kSamplingFreq[sampling_freq_index];
+ *channelCount = channel_configuration;
static const uint8_t kStaticESDS[] = {
0x03, 22,
@@ -585,9 +577,7 @@
csd->data()[sizeof(kStaticESDS) + 1] =
((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
- meta->setData(kKeyESDS, 0, csd->data(), csd->size());
-
- return meta;
+ return csd;
}
bool ExtractDimensionsFromVOLHeader(
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/avc_utils.h b/media/libstagefright/foundation/include/media/stagefright/foundation/avc_utils.h
index a939f12..2ca66fb 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/avc_utils.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/avc_utils.h
@@ -80,8 +80,9 @@
const uint8_t **nalStart, size_t *nalSize,
bool startCodeFollows = false);
-class MetaData;
-sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit);
+sp<ABuffer> MakeAVCCodecSpecificData(
+ const sp<ABuffer> &accessUnit, int32_t *width, int32_t *height,
+ int32_t *sarWidth = nullptr, int32_t *sarHeight = nullptr);
bool IsIDR(const uint8_t *data, size_t size);
bool IsAVCReferenceFrame(const sp<ABuffer> &accessUnit);
@@ -89,9 +90,10 @@
const char *AVCProfileToString(uint8_t profile);
-sp<MetaData> MakeAACCodecSpecificData(
+sp<ABuffer> MakeAACCodecSpecificData(
unsigned profile, unsigned sampling_freq_index,
- unsigned channel_configuration);
+ unsigned channel_configuration, int32_t *sampleRate,
+ int32_t *channelCount);
// Given an MPEG4 video VOL-header chunk (starting with 0x00 0x00 0x01 0x2?)
// parse it and fill in dimensions, returns true iff successful.
diff --git a/media/libstagefright/httplive/Android.bp b/media/libstagefright/httplive/Android.bp
index de560b6..f64e437 100644
--- a/media/libstagefright/httplive/Android.bp
+++ b/media/libstagefright/httplive/Android.bp
@@ -47,6 +47,7 @@
static_libs: [
"libstagefright_id3",
+ "libstagefright_metadatautils",
"libstagefright_mpeg2support",
],
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index b46d923..55fc680 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -35,6 +35,7 @@
#include <media/stagefright/foundation/avc_utils.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MetaDataUtils.h>
#include <media/stagefright/Utils.h>
#include <ctype.h>
diff --git a/media/libstagefright/include/FrameDecoder.h b/media/libstagefright/include/FrameDecoder.h
index 6f07838..c40324b 100644
--- a/media/libstagefright/include/FrameDecoder.h
+++ b/media/libstagefright/include/FrameDecoder.h
@@ -17,6 +17,9 @@
#ifndef FRAME_DECODER_H_
#define FRAME_DECODER_H_
+#include <memory>
+#include <vector>
+
#include <media/stagefright/foundation/AString.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/MediaSource.h>
diff --git a/media/libstagefright/include/media/stagefright/AudioSource.h b/media/libstagefright/include/media/stagefright/AudioSource.h
index 9414aab..f66b92d 100644
--- a/media/libstagefright/include/media/stagefright/AudioSource.h
+++ b/media/libstagefright/include/media/stagefright/AudioSource.h
@@ -21,11 +21,14 @@
#include <media/AudioRecord.h>
#include <media/AudioSystem.h>
#include <media/MediaSource.h>
+#include <media/MicrophoneInfo.h>
#include <media/stagefright/MediaBuffer.h>
#include <utils/List.h>
#include <system/audio.h>
+#include <vector>
+
namespace android {
class AudioRecord;
@@ -64,6 +67,9 @@
status_t addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
status_t removeAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
+ status_t getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones);
+
+
protected:
virtual ~AudioSource();
diff --git a/media/libstagefright/include/media/stagefright/MetaData.h b/media/libstagefright/include/media/stagefright/MetaData.h
new file mode 120000
index 0000000..160f8d3
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/MetaData.h
@@ -0,0 +1 @@
+../../../../libmediaextractor/include/media/stagefright/MetaData.h
\ No newline at end of file
diff --git a/media/libstagefright/include/media/stagefright/MetaDataUtils.h b/media/libstagefright/include/media/stagefright/MetaDataUtils.h
new file mode 100644
index 0000000..7c18bc2
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/MetaDataUtils.h
@@ -0,0 +1,32 @@
+/*
+ * 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 META_DATA_UTILS_H_
+
+#define META_DATA_UTILS_H_
+
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+struct ABuffer;
+sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit);
+sp<MetaData> MakeAACCodecSpecificData(unsigned profile, unsigned sampling_freq_index,
+ unsigned channel_configuration);
+
+} // namespace android
+
+#endif // META_DATA_UTILS_H_
diff --git a/media/libstagefright/mpeg2ts/Android.bp b/media/libstagefright/mpeg2ts/Android.bp
index 7654eb3..0b2a48f 100644
--- a/media/libstagefright/mpeg2ts/Android.bp
+++ b/media/libstagefright/mpeg2ts/Android.bp
@@ -37,4 +37,8 @@
"android.hardware.cas.native@1.0",
"android.hidl.memory@1.0",
],
+
+ whole_static_libs: [
+ "libstagefright_metadatautils",
+ ],
}
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index b621fd0..850face 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -29,6 +29,7 @@
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MetaDataUtils.h>
#include <media/cas/DescramblerAPI.h>
#include <media/hardware/CryptoAPI.h>
diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp
index ca691f7..4a36681 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -71,6 +71,7 @@
"libmedia_omx",
"libmedia_jni",
"libmediadrm",
+ "libmediaextractor",
"libstagefright",
"libstagefright_foundation",
"liblog",
diff --git a/packages/MediaComponents/res/layout/media_controller.xml b/packages/MediaComponents/res/layout/media_controller.xml
index 74a8306..dd56e7c 100644
--- a/packages/MediaComponents/res/layout/media_controller.xml
+++ b/packages/MediaComponents/res/layout/media_controller.xml
@@ -51,6 +51,7 @@
<view class="com.android.support.mediarouter.app.MediaRouteButton" android:id="@+id/cast"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
+ android:visibility="gone"
style="@style/TitleBarButton" />
</RelativeLayout>
diff --git a/packages/MediaComponents/res/values/attrs.xml b/packages/MediaComponents/res/values/attrs.xml
index 6175b11..e37285b 100644
--- a/packages/MediaComponents/res/values/attrs.xml
+++ b/packages/MediaComponents/res/values/attrs.xml
@@ -41,5 +41,4 @@
<attr name="mediaRouteControlPanelThemeOverlay" format="reference" />
<attr name="mediaRouteTheme" format="reference" />
- <attr name="enableControlView" format="boolean" />
</resources>
diff --git a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
index d21edae..3e6d98f 100644
--- a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
@@ -44,7 +44,7 @@
}
@Override
- public void getBrowserRoot_impl(Bundle rootHints) {
+ public void getLibraryRoot_impl(Bundle rootHints) {
final IMediaSession2 binder = getSessionBinder();
if (binder != null) {
try {
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 4144342..41dfd00 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -43,6 +43,8 @@
import android.support.annotation.GuardedBy;
import android.util.Log;
+import com.android.media.MediaSession2Impl.CommandButtonImpl;
+
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -225,7 +227,7 @@
@Override
public void play_impl() {
- sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_START);
+ sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_PLAY);
}
@Override
@@ -568,7 +570,8 @@
Log.w(TAG, "Don't fail silently here. Highly likely a bug");
return;
}
- controller.pushPlaybackStateChanges(PlaybackState2.fromBundle(state));
+ controller.pushPlaybackStateChanges(
+ PlaybackState2.fromBundle(controller.getContext(), state));
}
@Override
@@ -595,7 +598,8 @@
Log.w(TAG, "Don't fail silently here. Highly likely a bug");
return;
}
- controller.pushPlaylistParamsChanges(PlaylistParams.fromBundle(params));
+ controller.pushPlaylistParamsChanges(
+ PlaylistParams.fromBundle(controller.getContext(), params));
}
@Override
@@ -648,7 +652,7 @@
}
List<CommandButton> layout = new ArrayList<>();
for (int i = 0; i < commandButtonlist.size(); i++) {
- CommandButton button = CommandButton.fromBundle(
+ CommandButton button = CommandButtonImpl.fromBundle(
browser.getContext(), commandButtonlist.get(i));
if (button != null) {
layout.add(button);
diff --git a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
index 5639346..f51e246 100644
--- a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
@@ -90,7 +90,7 @@
bundle.putString(KEY_ID, mId);
bundle.putInt(KEY_FLAGS, mFlags);
if (mMetadata != null) {
- bundle.putBundle(KEY_METADATA, mMetadata.getBundle());
+ bundle.putBundle(KEY_METADATA, mMetadata.toBundle());
}
return bundle;
}
@@ -102,7 +102,7 @@
final String id = bundle.getString(KEY_ID);
final Bundle metadataBundle = bundle.getBundle(KEY_METADATA);
final MediaMetadata2 metadata = metadataBundle != null
- ? new MediaMetadata2(metadataBundle) : null;
+ ? MediaMetadata2.fromBundle(context, metadataBundle) : null;
final int flags = bundle.getInt(KEY_FLAGS);
return new MediaItem2Impl(context, id, metadata, flags).getInstance();
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
index 4b1b9de..54c8d41 100644
--- a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
@@ -19,7 +19,9 @@
import android.app.PendingIntent;
import android.content.Context;
import android.media.MediaLibraryService2;
+import android.media.MediaLibraryService2.LibraryRoot;
import android.media.MediaLibraryService2.MediaLibrarySession;
+import android.media.MediaLibraryService2.MediaLibrarySessionBuilder;
import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
import android.media.MediaPlayerInterface;
import android.media.MediaSession2;
@@ -30,6 +32,8 @@
import android.media.update.MediaLibraryService2Provider;
import android.os.Bundle;
+import com.android.media.MediaSession2Impl.BuilderBaseImpl;
+
import java.util.concurrent.Executor;
public class MediaLibraryService2Impl extends MediaSessionService2Impl implements
@@ -61,20 +65,28 @@
public static class MediaLibrarySessionImpl extends MediaSession2Impl
implements MediaLibrarySessionProvider {
- private final MediaLibrarySession mInstance;
private final MediaLibrarySessionCallback mCallback;
- public MediaLibrarySessionImpl(Context context, MediaLibrarySession instance,
+ public MediaLibrarySessionImpl(Context context,
MediaPlayerInterface player, String id, VolumeProvider volumeProvider,
int ratingType, PendingIntent sessionActivity, Executor callbackExecutor,
- MediaLibrarySessionCallback callback) {
- super(context, instance, player, id, volumeProvider, ratingType, sessionActivity,
+ MediaLibrarySessionCallback callback) {
+ super(context, player, id, volumeProvider, ratingType, sessionActivity,
callbackExecutor, callback);
- mInstance = instance;
mCallback = callback;
}
@Override
+ MediaLibrarySession createInstance() {
+ return new MediaLibrarySession(this);
+ }
+
+ @Override
+ MediaLibrarySession getInstance() {
+ return (MediaLibrarySession) super.getInstance();
+ }
+
+ @Override
public void notifyChildrenChanged_impl(ControllerInfo controller, String parentId,
Bundle options) {
// TODO(jaewan): Implements
@@ -85,4 +97,47 @@
// TODO(jaewan): Implements
}
}
-}
+
+ public static class BuilderImpl
+ extends BuilderBaseImpl<MediaLibrarySession, MediaLibrarySessionCallback> {
+ public BuilderImpl(Context context, MediaLibrarySessionBuilder instance,
+ MediaPlayerInterface player, Executor callbackExecutor,
+ MediaLibrarySessionCallback callback) {
+ super(context, player);
+ setSessionCallback_impl(callbackExecutor, callback);
+ }
+
+ @Override
+ public MediaLibrarySession build_impl() {
+ return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mVolumeProvider, mRatingType,
+ mSessionActivity, mCallbackExecutor, mCallback).getInstance();
+ }
+ }
+
+ public static final class LibraryRootImpl implements LibraryRootProvider {
+ private final LibraryRoot mInstance;
+ private final String mRootId;
+ private final Bundle mExtras;
+
+ public LibraryRootImpl(Context context, LibraryRoot instance, String rootId,
+ Bundle extras) {
+ if (rootId == null) {
+ throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
+ "Use null for BrowserRoot instead.");
+ }
+ mInstance = instance;
+ mRootId = rootId;
+ mExtras = extras;
+ }
+
+ @Override
+ public String getRootId_impl() {
+ return mRootId;
+ }
+
+ @Override
+ public Bundle getExtras_impl() {
+ return mExtras;
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java b/packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java
new file mode 100644
index 0000000..9088029
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java
@@ -0,0 +1,328 @@
+/*
+ * 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 static android.media.MediaMetadata2.*;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.media.MediaMetadata2;
+import android.media.MediaMetadata2.BitmapKey;
+import android.media.MediaMetadata2.Builder;
+import android.media.MediaMetadata2.LongKey;
+import android.media.MediaMetadata2.RatingKey;
+import android.media.MediaMetadata2.TextKey;
+import android.media.Rating2;
+import android.media.update.MediaMetadata2Provider;
+import android.os.Bundle;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import java.util.Set;
+
+public class MediaMetadata2Impl implements MediaMetadata2Provider {
+ private static final String TAG = "MediaMetadata2";
+
+ /**
+ * A {@link Bundle} extra.
+ * @hide
+ */
+ public static final String METADATA_KEY_EXTRA = "android.media.metadata.EXTRA";
+
+ static final int METADATA_TYPE_LONG = 0;
+ static final int METADATA_TYPE_TEXT = 1;
+ static final int METADATA_TYPE_BITMAP = 2;
+ static final int METADATA_TYPE_RATING = 3;
+ static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;
+
+ static {
+ METADATA_KEYS_TYPE = new ArrayMap<String, Integer>();
+ METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_TITLE, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_SUBTITLE, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON, METADATA_TYPE_BITMAP);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON_URI, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_ID, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_BT_FOLDER_TYPE, METADATA_TYPE_LONG);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_URI, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_ADVERTISEMENT, METADATA_TYPE_LONG);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DOWNLOAD_STATUS, METADATA_TYPE_LONG);
+ }
+
+ private static final @TextKey
+ String[] PREFERRED_DESCRIPTION_ORDER = {
+ METADATA_KEY_TITLE,
+ METADATA_KEY_ARTIST,
+ METADATA_KEY_ALBUM,
+ METADATA_KEY_ALBUM_ARTIST,
+ METADATA_KEY_WRITER,
+ METADATA_KEY_AUTHOR,
+ METADATA_KEY_COMPOSER
+ };
+
+ private static final @BitmapKey
+ String[] PREFERRED_BITMAP_ORDER = {
+ METADATA_KEY_DISPLAY_ICON,
+ METADATA_KEY_ART,
+ METADATA_KEY_ALBUM_ART
+ };
+
+ private static final @TextKey
+ String[] PREFERRED_URI_ORDER = {
+ METADATA_KEY_DISPLAY_ICON_URI,
+ METADATA_KEY_ART_URI,
+ METADATA_KEY_ALBUM_ART_URI
+ };
+
+ private final Context mContext;
+ private final MediaMetadata2 mInstance;
+ private final Bundle mBundle;
+
+ public MediaMetadata2Impl(Context context, Bundle bundle) {
+ mContext = context;
+ mInstance = new MediaMetadata2(this);
+ mBundle = bundle;
+ }
+
+ public MediaMetadata2 getInstance() {
+ return mInstance;
+ }
+
+ @Override
+ public boolean containsKey_impl(String key) {
+ return mBundle.containsKey(key);
+ }
+
+ @Override
+ public CharSequence getText_impl(@TextKey String key) {
+ return mBundle.getCharSequence(key);
+ }
+
+ @Override
+ public @Nullable String getMediaId_impl() {
+ return mInstance.getString(METADATA_KEY_MEDIA_ID);
+ }
+
+ @Override
+ public String getString_impl(@TextKey String key) {
+ CharSequence text = mBundle.getCharSequence(key);
+ if (text != null) {
+ return text.toString();
+ }
+ return null;
+ }
+
+ @Override
+ public long getLong_impl(@LongKey String key) {
+ return mBundle.getLong(key, 0);
+ }
+
+ @Override
+ public Rating2 getRating_impl(@RatingKey String key) {
+ // TODO(jaewan): Add backward compatibility
+ Rating2 rating = null;
+ try {
+ rating = Rating2.fromBundle(mContext, mBundle.getBundle(key));
+ } catch (Exception e) {
+ // ignore, value was not a rating
+ Log.w(TAG, "Failed to retrieve a key as Rating.", e);
+ }
+ return rating;
+ }
+
+ @Override
+ public Bitmap getBitmap_impl(@BitmapKey String key) {
+ Bitmap bmp = null;
+ try {
+ bmp = mBundle.getParcelable(key);
+ } catch (Exception e) {
+ // ignore, value was not a bitmap
+ Log.w(TAG, "Failed to retrieve a key as Bitmap.", e);
+ }
+ return bmp;
+ }
+
+ @Override
+ public Bundle getExtra_impl() {
+ try {
+ return mBundle.getBundle(METADATA_KEY_EXTRA);
+ } catch (Exception e) {
+ // ignore, value was not an bundle
+ Log.w(TAG, "Failed to retrieve an extra");
+ }
+ return null;
+ }
+
+ @Override
+ public int size_impl() {
+ return mBundle.size();
+ }
+
+ @Override
+ public Set<String> keySet_impl() {
+ return mBundle.keySet();
+ }
+
+ @Override
+ public Bundle toBundle_impl() {
+ return mBundle;
+ }
+
+ public static MediaMetadata2 fromBundle(Context context, Bundle bundle) {
+ return new MediaMetadata2Impl(context, bundle).getInstance();
+ }
+
+ public static final class BuilderImpl implements MediaMetadata2Provider.BuilderProvider {
+ private final Context mContext;
+ private final MediaMetadata2.Builder mInstance;
+ private final Bundle mBundle;
+
+ public BuilderImpl(Context context, MediaMetadata2.Builder instance) {
+ mContext = context;
+ mInstance = instance;
+ mBundle = new Bundle();
+ }
+
+ public BuilderImpl(Context context, MediaMetadata2.Builder instance, MediaMetadata2 source) {
+ if (source == null) {
+ throw new IllegalArgumentException("source shouldn't be null");
+ }
+ mContext = context;
+ mInstance = instance;
+ mBundle = new Bundle(source.toBundle());
+ }
+
+ public BuilderImpl(Context context, int maxBitmapSize) {
+ mContext = context;
+ mInstance = new MediaMetadata2.Builder(this);
+ mBundle = new Bundle();
+
+ for (String key : mBundle.keySet()) {
+ Object value = mBundle.get(key);
+ if (value instanceof Bitmap) {
+ Bitmap bmp = (Bitmap) value;
+ if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) {
+ mInstance.putBitmap(key, scaleBitmap(bmp, maxBitmapSize));
+ }
+ }
+ }
+ }
+
+ @Override
+ public Builder putText_impl(@TextKey String key, CharSequence value) {
+ if (METADATA_KEYS_TYPE.containsKey(key)) {
+ if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
+ throw new IllegalArgumentException("The " + key
+ + " key cannot be used to put a CharSequence");
+ }
+ }
+ mBundle.putCharSequence(key, value);
+ return mInstance;
+ }
+
+ @Override
+ public Builder putString_impl(@TextKey String key, String value) {
+ if (METADATA_KEYS_TYPE.containsKey(key)) {
+ if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
+ throw new IllegalArgumentException("The " + key
+ + " key cannot be used to put a String");
+ }
+ }
+ mBundle.putCharSequence(key, value);
+ return mInstance;
+ }
+
+ @Override
+ public Builder putLong_impl(@LongKey String key, long value) {
+ if (METADATA_KEYS_TYPE.containsKey(key)) {
+ if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_LONG) {
+ throw new IllegalArgumentException("The " + key
+ + " key cannot be used to put a long");
+ }
+ }
+ mBundle.putLong(key, value);
+ return mInstance;
+ }
+
+ @Override
+ public Builder putRating_impl(@RatingKey String key, Rating2 value) {
+ if (METADATA_KEYS_TYPE.containsKey(key)) {
+ if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_RATING) {
+ throw new IllegalArgumentException("The " + key
+ + " key cannot be used to put a Rating");
+ }
+ }
+ mBundle.putBundle(key, value.toBundle());
+ return mInstance;
+ }
+
+ @Override
+ public Builder putBitmap_impl(@BitmapKey String key, Bitmap value) {
+ if (METADATA_KEYS_TYPE.containsKey(key)) {
+ if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) {
+ throw new IllegalArgumentException("The " + key
+ + " key cannot be used to put a Bitmap");
+ }
+ }
+ mBundle.putParcelable(key, value);
+ return mInstance;
+ }
+
+ @Override
+ public Builder setExtra_impl(Bundle bundle) {
+ mBundle.putBundle(METADATA_KEY_EXTRA, bundle);
+ return mInstance;
+ }
+
+ @Override
+ public MediaMetadata2 build_impl() {
+ return new MediaMetadata2Impl(mContext, mBundle).getInstance();
+ }
+
+ private Bitmap scaleBitmap(Bitmap bmp, int maxSize) { float maxSizeF = maxSize;
+ float widthScale = maxSizeF / bmp.getWidth();
+ float heightScale = maxSizeF / bmp.getHeight();
+ float scale = Math.min(widthScale, heightScale);
+ int height = (int) (bmp.getHeight() * scale);
+ int width = (int) (bmp.getWidth() * scale);
+ return Bitmap.createScaledBitmap(bmp, width, height, true);
+ }
+ }
+}
+
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 53b35cc..f820cdc 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -16,6 +16,7 @@
package com.android.media;
+import static android.media.MediaSession2.COMMAND_CODE_CUSTOM;
import static android.media.SessionToken2.TYPE_LIBRARY_SERVICE;
import static android.media.SessionToken2.TYPE_SESSION;
import static android.media.SessionToken2.TYPE_SESSION_SERVICE;
@@ -31,6 +32,7 @@
import android.content.pm.ResolveInfo;
import android.media.MediaItem2;
import android.media.MediaLibraryService2;
+import android.media.MediaMetadata2;
import android.media.MediaPlayerInterface;
import android.media.MediaPlayerInterface.PlaybackListener;
import android.media.MediaSession2;
@@ -40,6 +42,8 @@
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.PlaylistParams;
+import android.media.MediaSession2.PlaylistParams.RepeatMode;
+import android.media.MediaSession2.PlaylistParams.ShuffleMode;
import android.media.MediaSession2.SessionCallback;
import android.media.MediaSessionService2;
import android.media.PlaybackState2;
@@ -47,6 +51,7 @@
import android.media.VolumeProvider;
import android.media.session.MediaSessionManager;
import android.media.update.MediaSession2Provider;
+import android.media.update.MediaSession2Provider.CommandButtonProvider;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.Process;
@@ -98,11 +103,11 @@
* @param ratingType
* @param sessionActivity
*/
- public MediaSession2Impl(Context context, MediaSession2 instance, MediaPlayerInterface player,
+ public MediaSession2Impl(Context context, MediaPlayerInterface player,
String id, VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity,
Executor callbackExecutor, SessionCallback callback) {
- mInstance = instance;
// TODO(jaewan): Keep other params.
+ mInstance = createInstance();
// Argument checks are done by builder already.
// Initialize finals first.
@@ -110,8 +115,6 @@
mId = id;
mCallback = callback;
mCallbackExecutor = callbackExecutor;
- // Only remember player. Actual settings will be done in the initialize().
- mPlayer = player;
mSessionStub = new MediaSession2Stub(this);
// Infer type from the id and package name.
@@ -130,6 +133,24 @@
mSessionToken = new SessionToken2Impl(context, Process.myUid(), TYPE_SESSION,
mContext.getPackageName(), null, id, mSessionStub).getInstance();
}
+
+ setPlayerLocked(player);
+
+ // Ask server for the sanity check, and starts
+ // Sanity check for making session ID unique 'per package' cannot be done in here.
+ // Server can only know if the package has another process and has another session with the
+ // same id. Note that 'ID is unique per package' is important for controller to distinguish
+ // a session in another package.
+ MediaSessionManager manager =
+ (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
+ if (!manager.onSessionCreated(mSessionToken)) {
+ throw new IllegalStateException("Session with the same id is already used by"
+ + " another process. Use MediaController2 instead.");
+ }
+ }
+
+ MediaSession2 createInstance() {
+ return new MediaSession2(this);
}
private static String getServiceName(Context context, String serviceAction, String id) {
@@ -157,24 +178,6 @@
return serviceName;
}
- @Override
- public void initialize() {
- synchronized (mLock) {
- setPlayerLocked(mPlayer);
- }
- // Ask server for the sanity check, and starts
- // Sanity check for making session ID unique 'per package' cannot be done in here.
- // Server can only know if the package has another process and has another session with the
- // same id. Note that 'ID is unique per package' is important for controller to distinguish
- // a session in another package.
- MediaSessionManager manager =
- (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
- if (!manager.onSessionCreated(mSessionToken)) {
- throw new IllegalStateException("Session with the same id is already used by"
- + " another process. Use MediaController2 instead.");
- }
- }
-
// TODO(jaewan): Add explicit release() and do not remove session object with the
// setPlayer(null). Token can be available when player is null, and
// controller can also attach to session.
@@ -549,7 +552,7 @@
throw new IllegalArgumentException("action shouldn't be null");
}
mInstance = instance;
- mCommandCode = MediaSession2.COMMAND_CODE_CUSTOM;
+ mCommandCode = COMMAND_CODE_CUSTOM;
mCustomCommand = action;
mExtra = extra;
}
@@ -582,7 +585,7 @@
*/
public static Command fromBundle_impl(Context context, Bundle command) {
int code = command.getInt(KEY_COMMAND_CODE);
- if (code != MediaSession2.COMMAND_CODE_CUSTOM) {
+ if (code != COMMAND_CODE_CUSTOM) {
return new Command(context, code);
} else {
String customCommand = command.getString(KEY_COMMAND_CUSTOM_COMMAND);
@@ -638,20 +641,10 @@
@Override
public void addAllPredefinedCommands_impl() {
- // TODO(jaewan): Is there any better way than this?
- mCommands.add(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_START));
- mCommands.add(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE));
- mCommands.add(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_STOP));
- mCommands.add(new Command(mContext,
- MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
- mCommands.add(new Command(mContext,
- MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
- mCommands.add(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE));
- mCommands.add(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_FAST_FORWARD));
- mCommands.add(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_REWIND));
- mCommands.add(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_SEEK_TO));
- mCommands.add(new Command(mContext,
- MediaSession2.COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM));
+ final int COMMAND_CODE_MAX = 22;
+ for (int i = 1; i <= COMMAND_CODE_MAX; i++) {
+ mCommands.add(new Command(mContext, i));
+ }
}
@Override
@@ -666,7 +659,7 @@
@Override
public boolean hasCommand_impl(int code) {
- if (code == MediaSession2.COMMAND_CODE_CUSTOM) {
+ if (code == COMMAND_CODE_CUSTOM) {
throw new IllegalArgumentException("Use hasCommand(Command) for custom command");
}
for (int i = 0; i < mCommands.size(); i++) {
@@ -814,4 +807,306 @@
return (ControllerInfoImpl) controller.getProvider();
}
}
+
+ public static class PlaylistParamsImpl implements PlaylistParamsProvider {
+ /**
+ * Keys used for converting a PlaylistParams object to a bundle object and vice versa.
+ */
+ private static final String KEY_REPEAT_MODE =
+ "android.media.session2.playlistparams2.repeat_mode";
+ private static final String KEY_SHUFFLE_MODE =
+ "android.media.session2.playlistparams2.shuffle_mode";
+ private static final String KEY_MEDIA_METADATA2_BUNDLE =
+ "android.media.session2.playlistparams2.metadata2_bundle";
+
+ private Context mContext;
+ private PlaylistParams mInstance;
+ private @RepeatMode int mRepeatMode;
+ private @ShuffleMode int mShuffleMode;
+ private MediaMetadata2 mPlaylistMetadata;
+
+ public PlaylistParamsImpl(Context context, PlaylistParams instance,
+ @RepeatMode int repeatMode, @ShuffleMode int shuffleMode,
+ MediaMetadata2 playlistMetadata) {
+ // TODO(jaewan): Sanity check
+ mContext = context;
+ mInstance = instance;
+ mRepeatMode = repeatMode;
+ mShuffleMode = shuffleMode;
+ mPlaylistMetadata = playlistMetadata;
+ }
+
+ public @RepeatMode int getRepeatMode_impl() {
+ return mRepeatMode;
+ }
+
+ public @ShuffleMode int getShuffleMode_impl() {
+ return mShuffleMode;
+ }
+
+ public MediaMetadata2 getPlaylistMetadata_impl() {
+ return mPlaylistMetadata;
+ }
+
+ @Override
+ public Bundle toBundle_impl() {
+ Bundle bundle = new Bundle();
+ bundle.putInt(KEY_REPEAT_MODE, mRepeatMode);
+ bundle.putInt(KEY_SHUFFLE_MODE, mShuffleMode);
+ if (mPlaylistMetadata != null) {
+ bundle.putBundle(KEY_MEDIA_METADATA2_BUNDLE, mPlaylistMetadata.toBundle());
+ }
+ return bundle;
+ }
+
+ public static PlaylistParams fromBundle(Context context, Bundle bundle) {
+ if (bundle == null) {
+ return null;
+ }
+ if (!bundle.containsKey(KEY_REPEAT_MODE) || !bundle.containsKey(KEY_SHUFFLE_MODE)) {
+ return null;
+ }
+
+ Bundle metadataBundle = bundle.getBundle(KEY_MEDIA_METADATA2_BUNDLE);
+ MediaMetadata2 metadata = metadataBundle == null
+ ? null : MediaMetadata2.fromBundle(context, metadataBundle);
+
+ return new PlaylistParams(context,
+ bundle.getInt(KEY_REPEAT_MODE),
+ bundle.getInt(KEY_SHUFFLE_MODE),
+ metadata);
+ }
+ }
+
+ public static class CommandButtonImpl implements CommandButtonProvider {
+ private static final String KEY_COMMAND
+ = "android.media.media_session2.command_button.command";
+ private static final String KEY_ICON_RES_ID
+ = "android.media.media_session2.command_button.icon_res_id";
+ private static final String KEY_DISPLAY_NAME
+ = "android.media.media_session2.command_button.display_name";
+ private static final String KEY_EXTRA
+ = "android.media.media_session2.command_button.extra";
+ private static final String KEY_ENABLED
+ = "android.media.media_session2.command_button.enabled";
+
+ private final CommandButton mInstance;
+ private Command mCommand;
+ private int mIconResId;
+ private String mDisplayName;
+ private Bundle mExtra;
+ private boolean mEnabled;
+
+ public CommandButtonImpl(Context context, @Nullable Command command, int iconResId,
+ @Nullable String displayName, Bundle extra, boolean enabled) {
+ mCommand = command;
+ mIconResId = iconResId;
+ mDisplayName = displayName;
+ mExtra = extra;
+ mEnabled = enabled;
+ mInstance = new CommandButton(this);
+ }
+
+ @Override
+ public @Nullable Command getCommand_impl() {
+ return mCommand;
+ }
+
+ @Override
+ public int getIconResId_impl() {
+ return mIconResId;
+ }
+
+ @Override
+ public @Nullable String getDisplayName_impl() {
+ return mDisplayName;
+ }
+
+ @Override
+ public @Nullable Bundle getExtra_impl() {
+ return mExtra;
+ }
+
+ @Override
+ public boolean isEnabled_impl() {
+ return mEnabled;
+ }
+
+ public @NonNull Bundle toBundle() {
+ Bundle bundle = new Bundle();
+ bundle.putBundle(KEY_COMMAND, mCommand.toBundle());
+ bundle.putInt(KEY_ICON_RES_ID, mIconResId);
+ bundle.putString(KEY_DISPLAY_NAME, mDisplayName);
+ bundle.putBundle(KEY_EXTRA, mExtra);
+ bundle.putBoolean(KEY_ENABLED, mEnabled);
+ return bundle;
+ }
+
+ public static @Nullable CommandButton fromBundle(Context context, Bundle bundle) {
+ if (bundle == null) {
+ return null;
+ }
+ CommandButton.Builder builder = new CommandButton.Builder(context);
+ builder.setCommand(Command.fromBundle(context, bundle.getBundle(KEY_COMMAND)));
+ builder.setIconResId(bundle.getInt(KEY_ICON_RES_ID, 0));
+ builder.setDisplayName(bundle.getString(KEY_DISPLAY_NAME));
+ builder.setExtra(bundle.getBundle(KEY_EXTRA));
+ builder.setEnabled(bundle.getBoolean(KEY_ENABLED));
+ try {
+ return builder.build();
+ } catch (IllegalStateException e) {
+ // Malformed or version mismatch. Return null for now.
+ return null;
+ }
+ }
+
+ /**
+ * Builder for {@link CommandButton}.
+ */
+ public static class BuilderImpl implements CommandButtonProvider.BuilderProvider {
+ private final Context mContext;
+ private final CommandButton.Builder mInstance;
+ private Command mCommand;
+ private int mIconResId;
+ private String mDisplayName;
+ private Bundle mExtra;
+ private boolean mEnabled;
+
+ public BuilderImpl(Context context, CommandButton.Builder instance) {
+ mContext = context;
+ mInstance = instance;
+ mEnabled = true;
+ }
+
+ @Override
+ public CommandButton.Builder setCommand_impl(Command command) {
+ mCommand = command;
+ return mInstance;
+ }
+
+ @Override
+ public CommandButton.Builder setIconResId_impl(int resId) {
+ mIconResId = resId;
+ return mInstance;
+ }
+
+ @Override
+ public CommandButton.Builder setDisplayName_impl(String displayName) {
+ mDisplayName = displayName;
+ return mInstance;
+ }
+
+ @Override
+ public CommandButton.Builder setEnabled_impl(boolean enabled) {
+ mEnabled = enabled;
+ return mInstance;
+ }
+
+ @Override
+ public CommandButton.Builder setExtra_impl(Bundle extra) {
+ mExtra = extra;
+ return mInstance;
+ }
+
+ @Override
+ public CommandButton build_impl() {
+ if (mEnabled && mCommand == null) {
+ throw new IllegalStateException("Enabled button needs Command"
+ + " for controller to invoke the command");
+ }
+ if (mCommand != null && mCommand.getCommandCode() == COMMAND_CODE_CUSTOM
+ && (mIconResId == 0 || TextUtils.isEmpty(mDisplayName))) {
+ throw new IllegalStateException("Custom commands needs icon and"
+ + " and name to display");
+ }
+ return new CommandButtonImpl(
+ mContext, mCommand, mIconResId, mDisplayName, mExtra, mEnabled).mInstance;
+ }
+ }
+ }
+
+ public static abstract class BuilderBaseImpl<T extends MediaSession2, C extends SessionCallback>
+ implements BuilderBaseProvider<T, C> {
+ final Context mContext;
+ final MediaPlayerInterface mPlayer;
+ String mId;
+ Executor mCallbackExecutor;
+ C mCallback;
+ VolumeProvider mVolumeProvider;
+ int mRatingType;
+ PendingIntent mSessionActivity;
+
+ /**
+ * Constructor.
+ *
+ * @param context a context
+ * @param player a player to handle incoming command from any controller.
+ * @throws IllegalArgumentException if any parameter is null, or the player is a
+ * {@link MediaSession2} or {@link MediaController2}.
+ */
+ // TODO(jaewan): Also need executor
+ public BuilderBaseImpl(Context context, MediaPlayerInterface player) {
+ if (context == null) {
+ throw new IllegalArgumentException("context shouldn't be null");
+ }
+ if (player == null) {
+ throw new IllegalArgumentException("player shouldn't be null");
+ }
+ mContext = context;
+ mPlayer = player;
+ // Ensure non-null
+ mId = "";
+ }
+
+ public void setVolumeProvider_impl(VolumeProvider volumeProvider) {
+ mVolumeProvider = volumeProvider;
+ }
+
+ public void setRatingType_impl(int type) {
+ mRatingType = type;
+ }
+
+ public void setSessionActivity_impl(PendingIntent pi) {
+ mSessionActivity = pi;
+ }
+
+ public void setId_impl(String id) {
+ if (id == null) {
+ throw new IllegalArgumentException("id shouldn't be null");
+ }
+ mId = id;
+ }
+
+ public void setSessionCallback_impl(Executor executor, C callback) {
+ if (executor == null) {
+ throw new IllegalArgumentException("executor shouldn't be null");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("callback shouldn't be null");
+ }
+ mCallbackExecutor = executor;
+ mCallback = callback;
+ }
+
+ public abstract T build_impl();
+ }
+
+ public static class BuilderImpl extends BuilderBaseImpl<MediaSession2, SessionCallback> {
+ public BuilderImpl(Context context, Builder instance, MediaPlayerInterface player) {
+ super(context, player);
+ }
+
+ @Override
+ public MediaSession2 build_impl() {
+ if (mCallbackExecutor == null) {
+ mCallbackExecutor = mContext.getMainExecutor();
+ }
+ if (mCallback == null) {
+ mCallback = new SessionCallback(mContext);
+ }
+
+ return new MediaSession2Impl(mContext, mPlayer, mId, mVolumeProvider, mRatingType,
+ 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 4bb5f47..1f71187 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -18,7 +18,7 @@
import android.content.Context;
import android.media.MediaItem2;
-import android.media.MediaLibraryService2.BrowserRoot;
+import android.media.MediaLibraryService2.LibraryRoot;
import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
import android.media.MediaSession2;
import android.media.MediaSession2.Command;
@@ -27,6 +27,7 @@
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.PlaylistParams;
import android.media.PlaybackState2;
+import android.media.update.MediaSession2Provider.CommandButtonProvider;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -36,6 +37,7 @@
import android.util.ArrayMap;
import android.util.Log;
+import com.android.media.MediaSession2Impl.CommandButtonImpl;
import com.android.media.MediaSession2Impl.ControllerInfoImpl;
import java.lang.ref.WeakReference;
@@ -192,7 +194,7 @@
}
switch (commandCode) {
- case MediaSession2.COMMAND_CODE_PLAYBACK_START:
+ case MediaSession2.COMMAND_CODE_PLAYBACK_PLAY:
session.getInstance().play();
break;
case MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE:
@@ -225,7 +227,7 @@
break;
case MediaSession2.COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS:
session.getInstance().setPlaylistParams(
- PlaylistParams.fromBundle(
+ PlaylistParams.fromBundle(session.getContext(),
args.getBundle(ARGUMENT_KEY_PLAYLIST_PARAMS)));
break;
default:
@@ -261,7 +263,7 @@
final MediaSession2Impl sessionImpl = getSession();
if (!(sessionImpl.getCallback() instanceof MediaLibrarySessionCallback)) {
if (DEBUG) {
- Log.d(TAG, "Session cannot hand getBrowserRoot()");
+ Log.d(TAG, "Session cannot hand getLibraryRoot()");
}
return;
}
@@ -280,7 +282,7 @@
final MediaLibrarySessionCallback libraryCallback =
(MediaLibrarySessionCallback) session.getCallback();
final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
- BrowserRoot root = libraryCallback.onGetRoot(controller, rootHints);
+ LibraryRoot root = libraryCallback.onGetRoot(controller, rootHints);
try {
controllerImpl.getControllerBinder().onGetRootResult(rootHints,
root == null ? null : root.getRootId(),
@@ -336,7 +338,7 @@
try {
List<Bundle> layoutBundles = new ArrayList<>();
for (int i = 0; i < layout.size(); i++) {
- Bundle bundle = layout.get(i).toBundle();
+ Bundle bundle = ((CommandButtonImpl) layout.get(i).getProvider()).toBundle();
if (bundle != null) {
layoutBundles.add(bundle);
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
index 7dce109..8773df4 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
@@ -18,7 +18,9 @@
import static android.content.Context.NOTIFICATION_SERVICE;
+import android.app.Notification;
import android.app.NotificationManager;
+import android.content.Context;
import android.content.Intent;
import android.media.MediaPlayerInterface.PlaybackListener;
import android.media.MediaSession2;
@@ -116,7 +118,8 @@
if (!mIsRunningForeground) {
mIsRunningForeground = true;
mInstance.startForegroundService(mStartSelfIntent);
- mInstance.startForeground(mediaNotification.id, mediaNotification.notification);
+ mInstance.startForeground(mediaNotification.getNotificationId(),
+ mediaNotification.getNotification());
return;
}
break;
@@ -128,7 +131,8 @@
}
break;
}
- mNotificationManager.notify(mediaNotification.id, mediaNotification.notification);
+ mNotificationManager.notify(mediaNotification.getNotificationId(),
+ mediaNotification.getNotification());
}
private class SessionServicePlaybackListener implements PlaybackListener {
@@ -142,4 +146,28 @@
updateNotification(state);
}
}
+
+ public static class MediaNotificationImpl implements MediaNotificationProvider {
+ private int mNotificationId;
+ private Notification mNotification;
+
+ public MediaNotificationImpl(Context context, MediaNotification instance,
+ int notificationId, Notification notification) {
+ if (notification == null) {
+ throw new IllegalArgumentException("notification shouldn't be null");
+ }
+ mNotificationId = notificationId;
+ mNotification = notification;
+ }
+
+ @Override
+ public int getNotificationId_impl() {
+ return mNotificationId;
+ }
+
+ @Override
+ public Notification getNotification_impl() {
+ return mNotification;
+ }
+ }
}
diff --git a/packages/MediaComponents/src/com/android/media/PlaybackInfoImpl.java b/packages/MediaComponents/src/com/android/media/PlaybackInfoImpl.java
new file mode 100644
index 0000000..0782cf1
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/PlaybackInfoImpl.java
@@ -0,0 +1,116 @@
+/*
+ * 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.content.Context;
+import android.media.AudioAttributes;
+import android.media.MediaController2.PlaybackInfo;
+import android.media.update.PlaybackInfoProvider;
+import android.os.Bundle;
+
+public final class PlaybackInfoImpl implements PlaybackInfoProvider {
+
+ private static final String KEY_PLAYBACK_TYPE =
+ "android.media.playbackinfo_impl.playback_type";
+ private static final String KEY_CONTROL_TYPE =
+ "android.media.playbackinfo_impl.control_type";
+ private static final String KEY_MAX_VOLUME =
+ "android.media.playbackinfo_impl.max_volume";
+ private static final String KEY_CURRENT_VOLUME =
+ "android.media.playbackinfo_impl.current_volume";
+ private static final String KEY_AUDIO_ATTRIBUTES =
+ "android.media.playbackinfo_impl.audio_attrs";
+
+ private final Context mContext;
+ private final PlaybackInfo mInstance;
+
+ private final int mPlaybackType;
+ private final int mControlType;
+ private final int mMaxVolume;
+ private final int mCurrentVolume;
+ private final AudioAttributes mAudioAttrs;
+
+ private PlaybackInfoImpl(Context context, int playbackType, AudioAttributes attrs,
+ int controlType, int max, int current) {
+ mContext = context;
+ mPlaybackType = playbackType;
+ mAudioAttrs = attrs;
+ mControlType = controlType;
+ mMaxVolume = max;
+ mCurrentVolume = current;
+ mInstance = new PlaybackInfo(this);
+ }
+
+ @Override
+ public int getPlaybackType_impl() {
+ return mPlaybackType;
+ }
+
+ @Override
+ public AudioAttributes getAudioAttributes_impl() {
+ return mAudioAttrs;
+ }
+
+ @Override
+ public int getControlType_impl() {
+ return mControlType;
+ }
+
+ @Override
+ public int getMaxVolume_impl() {
+ return mMaxVolume;
+ }
+
+ @Override
+ public int getCurrentVolume_impl() {
+ return mCurrentVolume;
+ }
+
+ public PlaybackInfo getInstance() {
+ return mInstance;
+ }
+
+ public Bundle toBundle() {
+ Bundle bundle = new Bundle();
+ bundle.putInt(KEY_PLAYBACK_TYPE, mPlaybackType);
+ bundle.putInt(KEY_CONTROL_TYPE, mControlType);
+ bundle.putInt(KEY_MAX_VOLUME, mMaxVolume);
+ bundle.putInt(KEY_CURRENT_VOLUME, mCurrentVolume);
+ bundle.putParcelable(KEY_AUDIO_ATTRIBUTES, mAudioAttrs);
+ return bundle;
+ }
+
+ public static PlaybackInfo createPlaybackInfo(Context context, int playbackType,
+ AudioAttributes attrs, int controlType, int max, int current) {
+ return new PlaybackInfoImpl(context, playbackType, attrs, controlType, max, current)
+ .getInstance();
+ }
+
+ public static PlaybackInfo fromBundle(Context context, Bundle bundle) {
+ if (bundle == null) {
+ return null;
+ }
+ final int volumeType = bundle.getInt(KEY_PLAYBACK_TYPE);
+ final int volumeControl = bundle.getInt(KEY_CONTROL_TYPE);
+ final int maxVolume = bundle.getInt(KEY_MAX_VOLUME);
+ final int currentVolume = bundle.getInt(KEY_CURRENT_VOLUME);
+ final AudioAttributes attrs = bundle.getParcelable(KEY_AUDIO_ATTRIBUTES);
+
+ return createPlaybackInfo(
+ context, volumeType, attrs, volumeControl, maxVolume, currentVolume);
+ }
+}
diff --git a/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java b/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java
new file mode 100644
index 0000000..5eb1129
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java
@@ -0,0 +1,146 @@
+/*
+ * 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.content.Context;
+import android.media.PlaybackState2;
+import android.media.update.PlaybackState2Provider;
+import android.os.Bundle;
+
+public final class PlaybackState2Impl implements PlaybackState2Provider {
+ /**
+ * Keys used for converting a PlaybackState2 to a bundle object and vice versa.
+ */
+ private static final String KEY_STATE = "android.media.playbackstate2.state";
+ private static final String KEY_POSITION = "android.media.playbackstate2.position";
+ 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";
+
+ private final Context mContext;
+ private final PlaybackState2 mInstance;
+ private final int mState;
+ private final long mPosition;
+ private final long mUpdateTime;
+ 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) {
+ mContext = context;
+ mInstance = instance;
+ mState = state;
+ mPosition = position;
+ mSpeed = speed;
+ mUpdateTime = updateTime;
+ mBufferedPosition = bufferedPosition;
+ mActiveItemId = activeItemId;
+ mErrorMessage = error;
+ }
+
+ @Override
+ public String toString_impl() {
+ StringBuilder bob = new StringBuilder("PlaybackState {");
+ bob.append("state=").append(mState);
+ bob.append(", position=").append(mPosition);
+ bob.append(", buffered position=").append(mBufferedPosition);
+ 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();
+ }
+
+ @Override
+ public int getState_impl() {
+ return mState;
+ }
+
+ @Override
+ public long getPosition_impl() {
+ return mPosition;
+ }
+
+ @Override
+ public long getBufferedPosition_impl() {
+ return mBufferedPosition;
+ }
+
+ @Override
+ public float getPlaybackSpeed_impl() {
+ return mSpeed;
+ }
+
+ @Override
+ public CharSequence getErrorMessage_impl() {
+ return mErrorMessage;
+ }
+
+ @Override
+ public long getLastPositionUpdateTime_impl() {
+ return mUpdateTime;
+ }
+
+ @Override
+ public long getCurrentPlaylistItemIndex_impl() {
+ return mActiveItemId;
+ }
+
+ @Override
+ public Bundle toBundle_impl() {
+ Bundle bundle = new Bundle();
+ bundle.putInt(KEY_STATE, mState);
+ bundle.putLong(KEY_POSITION, mPosition);
+ bundle.putLong(KEY_UPDATE_TIME, mUpdateTime);
+ 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;
+ }
+
+ public static PlaybackState2 fromBundle(Context context, Bundle bundle) {
+ if (bundle == null) {
+ return null;
+ }
+ if (!bundle.containsKey(KEY_STATE)
+ || !bundle.containsKey(KEY_POSITION)
+ || !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)) {
+ 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));
+ }
+}
\ No newline at end of file
diff --git a/packages/MediaComponents/src/com/android/media/Rating2Impl.java b/packages/MediaComponents/src/com/android/media/Rating2Impl.java
new file mode 100644
index 0000000..68e104a
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/Rating2Impl.java
@@ -0,0 +1,193 @@
+/*
+ * 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 static android.media.Rating2.*;
+
+import android.content.Context;
+import android.media.Rating2;
+import android.media.Rating2.Style;
+import android.media.update.Rating2Provider;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.Objects;
+
+public final class Rating2Impl implements Rating2Provider {
+ private static final String TAG = "Rating2";
+
+ private static final String KEY_STYLE = "android.media.rating2.style";
+ private static final String KEY_VALUE = "android.media.rating2.value";
+
+ private final static float RATING_NOT_RATED = -1.0f;
+
+ private final Rating2 mInstance;
+ private final int mRatingStyle;
+ private final float mRatingValue;
+
+ private Rating2Impl(Context context, @Style int ratingStyle, float rating) {
+ mRatingStyle = ratingStyle;
+ mRatingValue = rating;
+ mInstance = new Rating2(this);
+ }
+
+ @Override
+ public String toString_impl() {
+ return "Rating2:style=" + mRatingStyle + " rating="
+ + (mRatingValue < 0.0f ? "unrated" : String.valueOf(mRatingValue));
+ }
+
+ @Override
+ public boolean equals_impl(Object obj) {
+ if (!(obj instanceof Rating2)) {
+ return false;
+ }
+ Rating2Impl other = (Rating2Impl) ((Rating2) obj).getProvider();
+ return mRatingStyle == other.mRatingStyle
+ && mRatingValue == other.mRatingValue;
+ }
+
+ @Override
+ public int hashCode_impl() {
+ return Objects.hash(mRatingStyle, mRatingValue);
+ }
+
+ public Rating2 getInstance() {
+ return mInstance;
+ }
+
+ public static Rating2 fromBundle(Context context, Bundle bundle) {
+ if (bundle == null) {
+ return null;
+ }
+ return new Rating2Impl(context, bundle.getInt(KEY_STYLE), bundle.getFloat(KEY_VALUE))
+ .getInstance();
+ }
+
+ public Bundle toBundle_impl() {
+ Bundle bundle = new Bundle();
+ bundle.putInt(KEY_STYLE, mRatingStyle);
+ bundle.putFloat(KEY_VALUE, mRatingValue);
+ return bundle;
+ }
+
+ public static Rating2 newUnratedRating(Context context, @Style int ratingStyle) {
+ switch(ratingStyle) {
+ case RATING_HEART:
+ case RATING_THUMB_UP_DOWN:
+ case RATING_3_STARS:
+ case RATING_4_STARS:
+ case RATING_5_STARS:
+ case RATING_PERCENTAGE:
+ return new Rating2Impl(context, ratingStyle, RATING_NOT_RATED).getInstance();
+ default:
+ return null;
+ }
+ }
+
+ public static Rating2 newHeartRating(Context context, boolean hasHeart) {
+ return new Rating2Impl(context, RATING_HEART, hasHeart ? 1.0f : 0.0f).getInstance();
+ }
+
+ public static Rating2 newThumbRating(Context context, boolean thumbIsUp) {
+ return new Rating2Impl(context, RATING_THUMB_UP_DOWN, thumbIsUp ? 1.0f : 0.0f)
+ .getInstance();
+ }
+
+ public static Rating2 newStarRating(Context context, int starRatingStyle, float starRating) {
+ float maxRating = RATING_NOT_RATED;
+ switch(starRatingStyle) {
+ case RATING_3_STARS:
+ maxRating = 3.0f;
+ break;
+ case RATING_4_STARS:
+ maxRating = 4.0f;
+ break;
+ case RATING_5_STARS:
+ maxRating = 5.0f;
+ break;
+ default:
+ Log.e(TAG, "Invalid rating style (" + starRatingStyle + ") for a star rating");
+ return null;
+ }
+ if ((starRating < 0.0f) || (starRating > maxRating)) {
+ Log.e(TAG, "Trying to set out of range star-based rating");
+ return null;
+ }
+ return new Rating2Impl(context, starRatingStyle, starRating).getInstance();
+ }
+
+ public static Rating2 newPercentageRating(Context context, float percent) {
+ if ((percent < 0.0f) || (percent > 100.0f)) {
+ Log.e(TAG, "Invalid percentage-based rating value");
+ return null;
+ } else {
+ return new Rating2Impl(context, RATING_PERCENTAGE, percent).getInstance();
+ }
+ }
+
+ @Override
+ public boolean isRated_impl() {
+ return mRatingValue >= 0.0f;
+ }
+
+ @Override
+ public int getRatingStyle_impl() {
+ return mRatingStyle;
+ }
+
+ @Override
+ public boolean hasHeart_impl() {
+ if (mRatingStyle != RATING_HEART) {
+ return false;
+ } else {
+ return (mRatingValue == 1.0f);
+ }
+ }
+
+ @Override
+ public boolean isThumbUp_impl() {
+ if (mRatingStyle != RATING_THUMB_UP_DOWN) {
+ return false;
+ } else {
+ return (mRatingValue == 1.0f);
+ }
+ }
+
+ @Override
+ public float getStarRating_impl() {
+ switch (mRatingStyle) {
+ case RATING_3_STARS:
+ case RATING_4_STARS:
+ case RATING_5_STARS:
+ if (mInstance.isRated()) {
+ return mRatingValue;
+ }
+ default:
+ return -1.0f;
+ }
+ }
+
+ @Override
+ public float getPercentRating_impl() {
+ if ((mRatingStyle != RATING_PERCENTAGE) || !mInstance.isRated()) {
+ return -1.0f;
+ } else {
+ return mRatingValue;
+ }
+ }
+}
diff --git a/packages/MediaComponents/src/com/android/media/VolumeProvider2Impl.java b/packages/MediaComponents/src/com/android/media/VolumeProvider2Impl.java
new file mode 100644
index 0000000..5af3ddf
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/VolumeProvider2Impl.java
@@ -0,0 +1,77 @@
+/*
+ * 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.content.Context;
+import android.media.VolumeProvider2;
+import android.media.update.VolumeProvider2Provider;
+
+public class VolumeProvider2Impl implements VolumeProvider2Provider {
+
+ private final Context mContext;
+ private final VolumeProvider2 mInstance;
+ private final int mControlType;
+ private final int mMaxVolume;
+
+ private int mCurrentVolume;
+ private Callback mCallback;
+
+ public VolumeProvider2Impl(Context context, VolumeProvider2 instance,
+ @VolumeProvider2.ControlType int controlType, int maxVolume, int currentVolume) {
+ mContext = context;
+ mInstance = instance;
+ mControlType = controlType;
+ mMaxVolume = maxVolume;
+ mCurrentVolume = currentVolume;
+ }
+
+ @Override
+ public int getControlType_impl() {
+ return mControlType;
+ }
+
+ @Override
+ public int getMaxVolume_impl() {
+ return mMaxVolume;
+ }
+
+ @Override
+ public int getCurrentVolume_impl() {
+ return mCurrentVolume;
+ }
+
+ @Override
+ public void setCurrentVolume_impl(int currentVolume) {
+ mCurrentVolume = currentVolume;
+ if (mCallback != null) {
+ mCallback.onVolumeChanged(mInstance);
+ }
+ }
+
+ /**
+ * Sets a callback to receive volume changes.
+ */
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ /**
+ * Listens for changes to the volume.
+ */
+ public static abstract class Callback {
+ public abstract void onVolumeChanged(VolumeProvider2 volumeProvider);
+ }
+}
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index 8fbbe5f..661e252 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -16,7 +16,7 @@
package com.android.media.update;
-import android.app.PendingIntent;
+import android.app.Notification;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -27,31 +27,45 @@
import android.media.MediaController2.ControllerCallback;
import android.media.MediaItem2;
import android.media.MediaLibraryService2;
+import android.media.MediaLibraryService2.LibraryRoot;
import android.media.MediaLibraryService2.MediaLibrarySession;
+import android.media.MediaLibraryService2.MediaLibrarySessionBuilder;
import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
import android.media.MediaMetadata2;
import android.media.MediaPlayerInterface;
import android.media.MediaSession2;
import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandButton.Builder;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.PlaylistParams;
import android.media.MediaSession2.SessionCallback;
import android.media.MediaSessionService2;
+import android.media.MediaSessionService2.MediaNotification;
+import android.media.PlaybackState2;
+import android.media.Rating2;
import android.media.SessionPlayer2;
import android.media.SessionToken2;
-import android.media.VolumeProvider;
+import android.media.VolumeProvider2;
import android.media.update.MediaBrowser2Provider;
import android.media.update.MediaControlView2Provider;
import android.media.update.MediaController2Provider;
import android.media.update.MediaItem2Provider;
-import android.media.update.MediaLibraryService2Provider.MediaLibrarySessionProvider;
+import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
+import android.media.update.MediaMetadata2Provider;
import android.media.update.MediaSession2Provider;
+import android.media.update.MediaSession2Provider.BuilderBaseProvider;
+import android.media.update.MediaSession2Provider.CommandButtonProvider.BuilderProvider;
+import android.media.update.MediaSession2Provider.PlaylistParamsProvider;
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.VideoView2Provider;
import android.media.update.StaticProvider;
+import android.media.update.VideoView2Provider;
import android.media.update.ViewProvider;
+import android.media.update.VolumeProvider2Provider;
import android.os.Bundle;
import android.os.IInterface;
import android.support.annotation.Nullable;
@@ -59,16 +73,20 @@
import android.widget.MediaControlView2;
import android.widget.VideoView2;
-import com.android.media.IMediaSession2;
import com.android.media.IMediaSession2Callback;
import com.android.media.MediaBrowser2Impl;
import com.android.media.MediaController2Impl;
import com.android.media.MediaItem2Impl;
import com.android.media.MediaLibraryService2Impl;
-import com.android.media.MediaLibraryService2Impl.MediaLibrarySessionImpl;
+import com.android.media.MediaLibraryService2Impl.LibraryRootImpl;
+import com.android.media.MediaMetadata2Impl;
import com.android.media.MediaSession2Impl;
+import com.android.media.MediaSession2Impl.PlaylistParamsImpl;
import com.android.media.MediaSessionService2Impl;
+import com.android.media.PlaybackState2Impl;
+import com.android.media.Rating2Impl;
import com.android.media.SessionToken2Impl;
+import com.android.media.VolumeProvider2Impl;
import com.android.widget.MediaControlView2Impl;
import com.android.widget.VideoView2Impl;
@@ -95,15 +113,6 @@
}
@Override
- public MediaSession2Provider createMediaSession2(Context context, MediaSession2 instance,
- MediaPlayerInterface player, String id, VolumeProvider volumeProvider,
- int ratingType, PendingIntent sessionActivity, Executor callbackExecutor,
- SessionCallback callback) {
- return new MediaSession2Impl(context, instance, player, id, volumeProvider, ratingType,
- sessionActivity, callbackExecutor, callback);
- }
-
- @Override
public MediaSession2Provider.CommandProvider createMediaSession2Command(
Command instance, int commandCode, String action, Bundle extra) {
if (action == null && extra == null) {
@@ -130,7 +139,7 @@
}
@Override
- public MediaSession2Provider.ControllerInfoProvider createMediaSession2ControllerInfoProvider(
+ public MediaSession2Provider.ControllerInfoProvider createMediaSession2ControllerInfo(
Context context, ControllerInfo instance, int uid, int pid, String packageName,
IInterface callback) {
return new MediaSession2Impl.ControllerInfoImpl(context,
@@ -138,24 +147,61 @@
}
@Override
+ public PlaylistParamsProvider createMediaSession2PlaylistParams(Context context,
+ PlaylistParams playlistParams, int repeatMode, int shuffleMode,
+ MediaMetadata2 playlistMetadata) {
+ return new PlaylistParamsImpl(context, playlistParams, repeatMode, shuffleMode,
+ playlistMetadata);
+ }
+
+ @Override
+ public PlaylistParams fromBundle_PlaylistParams(Context context, Bundle bundle) {
+ return PlaylistParamsImpl.fromBundle(context, bundle);
+ }
+
+ @Override
+ public BuilderProvider createMediaSession2CommandButtonBuilder(Context context,
+ Builder instance) {
+ return new MediaSession2Impl.CommandButtonImpl.BuilderImpl(context, instance);
+ }
+
+ public BuilderBaseProvider<MediaSession2, SessionCallback> createMediaSession2Builder(
+ Context context, MediaSession2.Builder instance, MediaPlayerInterface player) {
+ return new MediaSession2Impl.BuilderImpl(context, instance, player);
+ }
+
+ @Override
public MediaSessionService2Provider createMediaSessionService2(
MediaSessionService2 instance) {
return new MediaSessionService2Impl(instance);
}
@Override
+ public MediaNotificationProvider createMediaSessionService2MediaNotification(Context context,
+ MediaNotification instance, int notificationId, Notification notification) {
+ return new MediaSessionService2Impl.MediaNotificationImpl(
+ context, instance, notificationId, notification);
+ }
+
+ @Override
public MediaSessionService2Provider createMediaLibraryService2(
MediaLibraryService2 instance) {
return new MediaLibraryService2Impl(instance);
}
@Override
- public MediaLibrarySessionProvider createMediaLibraryService2MediaLibrarySession(
- Context context, MediaLibrarySession instance, MediaPlayerInterface player,
- String id, VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity,
+ public BuilderBaseProvider<MediaLibrarySession, MediaLibrarySessionCallback>
+ createMediaLibraryService2Builder(
+ Context context, MediaLibrarySessionBuilder instance, MediaPlayerInterface player,
Executor callbackExecutor, MediaLibrarySessionCallback callback) {
- return new MediaLibrarySessionImpl(context, instance, player, id, volumeProvider,
- ratingType, sessionActivity, callbackExecutor, callback);
+ return new MediaLibraryService2Impl.BuilderImpl(context, instance, player, callbackExecutor,
+ callback);
+ }
+
+ @Override
+ public LibraryRootProvider createMediaLibraryService2LibraryRoot(Context context,
+ LibraryRoot instance, String rootId, Bundle extras) {
+ return new LibraryRootImpl(context, instance, rootId, extras);
}
@Override
@@ -189,7 +235,7 @@
}
@Override
- public MediaItem2Provider createMediaItem2Provider(Context context, MediaItem2 instance,
+ public MediaItem2Provider createMediaItem2(Context context, MediaItem2 instance,
String mediaId, DataSourceDesc dsd, MediaMetadata2 metadata, int flags) {
return new MediaItem2Impl(context, instance, mediaId, dsd, metadata, flags);
}
@@ -198,4 +244,70 @@
public MediaItem2 fromBundle_MediaItem2(Context context, Bundle bundle) {
return MediaItem2Impl.fromBundle(context, bundle);
}
+
+ @Override
+ public VolumeProvider2Provider createVolumeProvider2(Context context, VolumeProvider2 instance,
+ int controlType, int maxVolume, int currentVolume) {
+ return new VolumeProvider2Impl(context, instance, controlType, maxVolume, currentVolume);
+ }
+
+ @Override
+ public MediaMetadata2 fromBundle_MediaMetadata2(Context context, Bundle bundle) {
+ return MediaMetadata2Impl.fromBundle(context, bundle);
+ }
+
+ @Override
+ public MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
+ Context context, MediaMetadata2.Builder builder) {
+ return new MediaMetadata2Impl.BuilderImpl(context, builder);
+ }
+
+ @Override
+ public MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
+ Context context, MediaMetadata2.Builder builder, MediaMetadata2 source) {
+ return new MediaMetadata2Impl.BuilderImpl(context, builder, source);
+ }
+
+ @Override
+ public Rating2 fromBundle_Rating2(Context context, Bundle bundle) {
+ return Rating2Impl.fromBundle(context, bundle);
+ }
+
+ @Override
+ public Rating2 newUnratedRating_Rating2(Context context, int ratingStyle) {
+ return Rating2Impl.newUnratedRating(context, ratingStyle);
+ }
+
+ @Override
+ public Rating2 newHeartRating_Rating2(Context context, boolean hasHeart) {
+ return Rating2Impl.newHeartRating(context, hasHeart);
+ }
+
+ @Override
+ public Rating2 newThumbRating_Rating2(Context context, boolean thumbIsUp) {
+ return Rating2Impl.newThumbRating(context, thumbIsUp);
+ }
+
+ @Override
+ public Rating2 newStarRating_Rating2(Context context, int starRatingStyle, float starRating) {
+ return Rating2Impl.newStarRating(context, starRatingStyle, starRating);
+ }
+
+ @Override
+ public Rating2 newPercentageRating_Rating2(Context context, float percent) {
+ return Rating2Impl.newPercentageRating(context, percent);
+ }
+
+ @Override
+ public PlaybackState2Provider createPlaybackState2(Context context, PlaybackState2 instance,
+ int state, long position, long updateTime, float speed, long bufferedPosition,
+ long activeItemId, CharSequence error) {
+ return new PlaybackState2Impl(context, instance, state, position, updateTime, speed,
+ bufferedPosition, activeItemId, error);
+ }
+
+ @Override
+ public PlaybackState2 fromBundle_PlaybackState2(Context context, Bundle bundle) {
+ return PlaybackState2Impl.fromBundle(context, bundle);
+ }
}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java
index 65fc88c..7fdcfe4 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java
@@ -90,6 +90,7 @@
private final MediaRouterCallback mCallback;
private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY;
+ private int mRouteCallbackFlags;
private MediaRouteDialogFactory mDialogFactory = MediaRouteDialogFactory.getDefault();
private boolean mAttachedToWindow;
@@ -174,23 +175,38 @@
* Sets the media route selector for filtering the routes that the user can
* select using the media route chooser dialog.
*
- * @param selector The selector, must not be null.
+ * @param selector The selector.
*/
public void setRouteSelector(MediaRouteSelector selector) {
- if (selector == null) {
- throw new IllegalArgumentException("selector must not be null");
+ setRouteSelector(selector, 0);
+ }
+
+ /**
+ * Sets the media route selector for filtering the routes that the user can
+ * select using the media route chooser dialog.
+ *
+ * @param selector The selector.
+ * @param flags Flags to control the behavior of the callback. May be zero or a combination of
+ * {@link #MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and
+ * {@link #MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS}.
+ */
+ public void setRouteSelector(MediaRouteSelector selector, int flags) {
+ if (mSelector.equals(selector) && mRouteCallbackFlags == flags) {
+ return;
+ }
+ if (!mSelector.isEmpty()) {
+ mRouter.removeCallback(mCallback);
+ }
+ if (selector == null || selector.isEmpty()) {
+ mSelector = MediaRouteSelector.EMPTY;
+ return;
}
- if (!mSelector.equals(selector)) {
- if (mAttachedToWindow) {
- if (!mSelector.isEmpty()) {
- mRouter.removeCallback(mCallback);
- }
- if (!selector.isEmpty()) {
- mRouter.addCallback(selector, mCallback);
- }
- }
- mSelector = selector;
+ mSelector = selector;
+ mRouteCallbackFlags = flags;
+
+ if (mAttachedToWindow) {
+ mRouter.addCallback(selector, mCallback, flags);
refreshRoute();
}
}
@@ -411,7 +427,7 @@
mAttachedToWindow = true;
if (!mSelector.isEmpty()) {
- mRouter.addCallback(mSelector, mCallback);
+ mRouter.addCallback(mSelector, mCallback, mRouteCallbackFlags);
}
refreshRoute();
}
diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
index b4aaa79..138232e 100644
--- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
@@ -38,6 +38,9 @@
import com.android.media.update.ApiHelper;
import com.android.media.update.R;
+import com.android.support.mediarouter.app.MediaRouteButton;
+import com.android.support.mediarouter.media.MediaRouter;
+import com.android.support.mediarouter.media.MediaRouteSelector;
import java.util.Formatter;
import java.util.List;
@@ -107,6 +110,9 @@
private StringBuilder mFormatBuilder;
private Formatter mFormatter;
+ private MediaRouteButton mRouteButton;
+ private MediaRouteSelector mRouteSelector;
+
public MediaControlView2Impl(
MediaControlView2 instance, ViewProvider superProvider) {
mInstance = instance;
@@ -305,6 +311,17 @@
mSuperProvider.setEnabled_impl(enabled);
}
+ public void setRouteSelector(MediaRouteSelector selector) {
+ mRouteSelector = selector;
+ if (mRouteSelector != null && !mRouteSelector.isEmpty()) {
+ mRouteButton.setRouteSelector(selector, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
+ mRouteButton.setVisibility(View.VISIBLE);
+ } else {
+ mRouteButton.setRouteSelector(MediaRouteSelector.EMPTY);
+ mRouteButton.setVisibility(View.GONE);
+ }
+ }
+
///////////////////////////////////////////////////
// Protected or private methods
///////////////////////////////////////////////////
@@ -375,6 +392,8 @@
mPauseDescription = res.getText(R.string.lockscreen_pause_button_content_description);
mReplayDescription = res.getText(R.string.lockscreen_replay_button_content_description);
+ mRouteButton = v.findViewById(R.id.cast);
+
mPlayPauseButton = v.findViewById(R.id.pause);
if (mPlayPauseButton != null) {
mPlayPauseButton.requestFocus();
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index a8864fc..7218150 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -35,6 +35,7 @@
import android.media.TtmlRenderer;
import android.media.WebVttRenderer;
import android.media.session.MediaController;
+import android.media.session.MediaController.PlaybackInfo;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.media.update.VideoView2Provider;
@@ -103,7 +104,7 @@
private MediaController mMediaController;
private MediaSession.Callback mRouteSessionCallback = new RouteSessionCallback();
private MediaRouter mMediaRouter;
- private MediaRouteSelector mMediaRouteSelector;
+ private MediaRouteSelector mRouteSelector;
private Metadata mMetadata;
private String mTitle;
@@ -172,16 +173,38 @@
// TODO: Need a common namespace for attributes those are defined in updatable library.
boolean enableControlView = (attrs == null) || attrs.getAttributeBooleanValue(
- "http://schemas.android.com/apk/com.android.media.update",
+ "http://schemas.android.com/apk/res/android",
"enableControlView", true);
if (enableControlView) {
mMediaControlView = new MediaControlView2(mInstance.getContext());
}
+ boolean showSubtitle = (attrs == null) || attrs.getAttributeBooleanValue(
+ "http://schemas.android.com/apk/res/android",
+ "showSubtitle", true);
+ if (showSubtitle) {
+ Log.d(TAG, "showSubtitle attribute is true.");
+ // TODO: implement
+ }
+ int viewType = (attrs == null) ? VideoView2.VIEW_TYPE_SURFACEVIEW
+ : attrs.getAttributeIntValue(
+ "http://schemas.android.com/apk/res/android",
+ "viewType", 0);
+ if (viewType == 0) {
+ Log.d(TAG, "viewType attribute is surfaceView.");
+ // TODO: implement
+ } else if (viewType == 1) {
+ Log.d(TAG, "viewType attribute is textureView.");
+ // TODO: implement
+ }
}
@Override
public void setMediaControlView2_impl(MediaControlView2 mediaControlView) {
mMediaControlView = mediaControlView;
+ if (mRouteSelector != null) {
+ ((MediaControlView2Impl) mMediaControlView.getProvider())
+ .setRouteSelector(mRouteSelector);
+ }
if (mInstance.isAttachedToWindow()) {
attachMediaControlView();
@@ -202,30 +225,29 @@
}
@Override
- public void showSubtitle_impl() {
- // Retrieve all tracks that belong to the current video.
- MediaPlayer.TrackInfo[] trackInfos = mMediaPlayer.getTrackInfo();
+ public void showSubtitle_impl(boolean show) {
+ if (show) {
+ // Retrieve all tracks that belong to the current video.
+ MediaPlayer.TrackInfo[] trackInfos = mMediaPlayer.getTrackInfo();
- List<Integer> subtitleTrackIndices = new ArrayList<>();
- for (int i = 0; i < trackInfos.length; ++i) {
- int trackType = trackInfos[i].getTrackType();
- if (trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
- subtitleTrackIndices.add(i);
+ List<Integer> subtitleTrackIndices = new ArrayList<>();
+ for (int i = 0; i < trackInfos.length; ++i) {
+ int trackType = trackInfos[i].getTrackType();
+ if (trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
+ subtitleTrackIndices.add(i);
+ }
}
- }
- if (subtitleTrackIndices.size() > 0) {
- // Select first subtitle track
- mCCEnabled = true;
- mSelectedTrackIndex = subtitleTrackIndices.get(0);
- mMediaPlayer.selectTrack(mSelectedTrackIndex);
- }
- }
-
- @Override
- public void hideSubtitle_impl() {
- if (mCCEnabled) {
- mMediaPlayer.deselectTrack(mSelectedTrackIndex);
- mCCEnabled = false;
+ if (subtitleTrackIndices.size() > 0) {
+ // Select first subtitle track
+ mCCEnabled = true;
+ mSelectedTrackIndex = subtitleTrackIndices.get(0);
+ mMediaPlayer.selectTrack(mSelectedTrackIndex);
+ }
+ } else {
+ if (mCCEnabled) {
+ mMediaPlayer.deselectTrack(mSelectedTrackIndex);
+ mCCEnabled = false;
+ }
}
}
@@ -275,7 +297,11 @@
for (String category : routeCategories) {
builder.addControlCategory(category);
}
- mMediaRouteSelector = builder.build();
+ mRouteSelector = builder.build();
+ if (mMediaControlView != null) {
+ ((MediaControlView2Impl) mMediaControlView.getProvider())
+ .setRouteSelector(mRouteSelector);
+ }
mMediaRouter = MediaRouter.getInstance(mInstance.getContext());
mRouteSessionCallback = sessionPlayer;
if (mMediaSession != null) {
@@ -386,6 +412,7 @@
@Override
public void onDetachedFromWindow_impl() {
+ Log.e(TAG, ".... Debugging. onDetachedFromWindow_impl()");
mSuperProvider.onDetachedFromWindow_impl();
mMediaSession.release();
mMediaSession = null;
@@ -703,6 +730,14 @@
}
}
+ private boolean isRemotePlayback() {
+ if (mMediaController == null) {
+ return false;
+ }
+ PlaybackInfo playbackInfo = mMediaController.getPlaybackInfo();
+ return (playbackInfo != null) && (playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE);
+ }
+
MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =
new MediaPlayer.OnVideoSizeChangedListener() {
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
@@ -895,16 +930,15 @@
private class MediaSessionCallback extends MediaSession.Callback {
@Override
public void onCommand(String command, Bundle args, ResultReceiver receiver) {
- if (mMediaController.getPlaybackInfo().getPlaybackType()
- == MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
+ if (isRemotePlayback()) {
mRouteSessionCallback.onCommand(command, args, receiver);
} else {
switch (command) {
case MediaControlView2.COMMAND_SHOW_SUBTITLE:
- mInstance.showSubtitle();
+ mInstance.showSubtitle(true);
break;
case MediaControlView2.COMMAND_HIDE_SUBTITLE:
- mInstance.hideSubtitle();
+ mInstance.showSubtitle(false);
break;
case MediaControlView2.COMMAND_SET_FULLSCREEN:
if (mOnFullScreenRequestListener != null) {
@@ -924,8 +958,7 @@
@Override
public void onPlay() {
- if (mMediaController.getPlaybackInfo().getPlaybackType()
- == MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
+ if (isRemotePlayback()) {
mRouteSessionCallback.onPlay();
} else {
if (isInPlaybackState() && mCurrentView.hasAvailableSurface()) {
@@ -944,8 +977,7 @@
@Override
public void onPause() {
- if (mMediaController.getPlaybackInfo().getPlaybackType()
- == MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
+ if (isRemotePlayback()) {
mRouteSessionCallback.onPause();
} else {
if (isInPlaybackState()) {
@@ -965,8 +997,7 @@
@Override
public void onSeekTo(long pos) {
- if (mMediaController.getPlaybackInfo().getPlaybackType()
- == MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
+ if (isRemotePlayback()) {
mRouteSessionCallback.onSeekTo(pos);
} else {
if (isInPlaybackState()) {
@@ -981,8 +1012,7 @@
@Override
public void onStop() {
- if (mMediaController.getPlaybackInfo().getPlaybackType()
- == MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
+ if (isRemotePlayback()) {
mRouteSessionCallback.onStop();
} else {
resetPlayer();
diff --git a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
index 50e59b8..38475a8 100644
--- a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
@@ -66,7 +66,7 @@
}
@Test
- public void testGetBrowserRoot() throws InterruptedException {
+ public void testGetLibraryRoot() throws InterruptedException {
final Bundle param = new Bundle();
param.putString(TAG, TAG);
@@ -84,7 +84,7 @@
final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
MediaBrowser2 browser =
(MediaBrowser2) createController(token,true, callback);
- browser.getBrowserRoot(param);
+ browser.getLibraryRoot(param);
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
diff --git a/packages/MediaComponents/test/src/android/media/MediaController2Test.java b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
index 27dbaf8..e8eaa20 100644
--- a/packages/MediaComponents/test/src/android/media/MediaController2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -42,7 +42,8 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import static android.media.TestUtils.createPlaybackState;
+import static android.media.TestUtils.ensurePlaylistParamsModeEquals;
+
import static org.junit.Assert.*;
/**
@@ -200,7 +201,7 @@
@Test
public void testGetSetPlaylistParams() throws Exception {
- final PlaylistParams params = new PlaylistParams(
+ final PlaylistParams params = new PlaylistParams(mContext,
PlaylistParams.REPEAT_MODE_ALL,
PlaylistParams.SHUFFLE_MODE_ALL,
null /* PlaylistMetadata */);
@@ -209,7 +210,7 @@
final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
@Override
public void onPlaylistParamsChanged(PlaylistParams givenParams) {
- TestUtils.equals(params.toBundle(), givenParams.toBundle());
+ ensurePlaylistParamsModeEquals(params, givenParams);
latch.countDown();
}
};
@@ -218,8 +219,8 @@
controller.setPlaylistParams(params);
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- TestUtils.equals(params.toBundle(), mSession.getPlaylistParams().toBundle());
- TestUtils.equals(params.toBundle(), controller.getPlaylistParams().toBundle());
+ ensurePlaylistParamsModeEquals(params, mSession.getPlaylistParams());
+ ensurePlaylistParamsModeEquals(params, controller.getPlaylistParams());
}
@Test
diff --git a/packages/MediaComponents/test/src/android/media/MediaMetadata2Test.java b/packages/MediaComponents/test/src/android/media/MediaMetadata2Test.java
new file mode 100644
index 0000000..ea42651
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/MediaMetadata2Test.java
@@ -0,0 +1,63 @@
+/*
+ * 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 static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import android.content.Context;
+import android.media.MediaMetadata2.Builder;
+import android.os.Bundle;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MediaMetadata2Test {
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ @Test
+ public void testBuilder() {
+ final Bundle extra = new Bundle();
+ extra.putString("MediaMetadata2Test", "testBuilder");
+ final String title = "title";
+ final long discNumber = 10;
+ final Rating2 rating = Rating2.newThumbRating(mContext, true);
+
+ MediaMetadata2.Builder builder = new Builder(mContext);
+ builder.setExtra(extra);
+ builder.putString(MediaMetadata2.METADATA_KEY_DISPLAY_TITLE, title);
+ builder.putLong(MediaMetadata2.METADATA_KEY_DISC_NUMBER, discNumber);
+ builder.putRating(MediaMetadata2.METADATA_KEY_USER_RATING, rating);
+
+ MediaMetadata2 metadata = builder.build();
+ assertTrue(TestUtils.equals(extra, metadata.getExtra()));
+ assertEquals(title, metadata.getString(MediaMetadata2.METADATA_KEY_DISPLAY_TITLE));
+ assertEquals(discNumber, metadata.getLong(MediaMetadata2.METADATA_KEY_DISC_NUMBER));
+ assertEquals(rating, metadata.getRating(MediaMetadata2.METADATA_KEY_USER_RATING));
+ }
+}
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
index c5bcfff..ff8c2b7 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
@@ -19,7 +19,7 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
-import static android.media.TestUtils.createPlaybackState;
+import static android.media.TestUtils.ensurePlaylistParamsModeEquals;
import android.media.MediaPlayerInterface.PlaybackListener;
import android.media.MediaSession2.Builder;
@@ -170,7 +170,7 @@
@Test
public void testSetPlaylistParams() throws Exception {
- final PlaylistParams params = new PlaylistParams(
+ final PlaylistParams params = new PlaylistParams(mContext,
PlaylistParams.REPEAT_MODE_ALL,
PlaylistParams.SHUFFLE_MODE_ALL,
null /* PlaylistMetadata */);
@@ -179,7 +179,7 @@
final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
@Override
public void onPlaylistParamsChanged(PlaylistParams givenParams) {
- TestUtils.equals(params.toBundle(), givenParams.toBundle());
+ ensurePlaylistParamsModeEquals(params, givenParams);
latch.countDown();
}
};
@@ -187,8 +187,8 @@
final MediaController2 controller = createController(mSession.getToken(), true, callback);
mSession.setPlaylistParams(params);
assertTrue(mPlayer.mSetPlaylistParamsCalled);
- TestUtils.equals(params.toBundle(), mPlayer.mPlaylistParams.toBundle());
- TestUtils.equals(params.toBundle(), mSession.getPlaylistParams().toBundle());
+ ensurePlaylistParamsModeEquals(params, mPlayer.mPlaylistParams);
+ ensurePlaylistParamsModeEquals(params, mSession.getPlaylistParams());
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
@@ -390,7 +390,7 @@
assertEquals(aItem.getMediaId(), bItem.getMediaId());
assertEquals(aItem.getFlags(), bItem.getFlags());
- TestUtils.equals(aItem.getMetadata().getBundle(), bItem.getMetadata().getBundle());
+ TestUtils.equals(aItem.getMetadata().toBundle(), bItem.getMetadata().toBundle());
// Note: Here it does not check whether DataSourceDesc are equal,
// since there DataSourceDec is not comparable.
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
index 8e1c782..5d94b17 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
@@ -108,6 +108,17 @@
}
}
+ /**
+ * Creates a {@link android.media.session.PlaybackState} with the given state.
+ *
+ * @param state one of the PlaybackState.STATE_xxx.
+ * @return a PlaybackState
+ */
+ public PlaybackState2 createPlaybackState(int state) {
+ return new PlaybackState2(mContext, state, 0, 0, 1.0f,
+ 0, 0, null);
+ }
+
final MediaController2 createController(SessionToken2 token) throws InterruptedException {
return createController(token, true, null);
}
diff --git a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
index 96ae8b7..852f2fa 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
@@ -33,7 +33,6 @@
import java.util.List;
import java.util.concurrent.TimeUnit;
-import static android.media.TestUtils.createPlaybackState;
import static org.junit.Assert.*;
/**
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
index 6e1501a..ec69ff6 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
@@ -97,8 +97,8 @@
}
@Override
- public BrowserRoot onGetRoot(ControllerInfo controller, Bundle rootHints) {
- return new BrowserRoot(ROOT_ID, EXTRA);
+ public LibraryRoot onGetRoot(ControllerInfo controller, Bundle rootHints) {
+ return new LibraryRoot(MockMediaLibraryService2.this, ROOT_ID, EXTRA);
}
}
}
\ No newline at end of file
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
index d85875e..c8ed184 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
@@ -87,7 +87,7 @@
.setContentTitle(getPackageName())
.setContentText("Playback state: " + state.getState())
.setSmallIcon(android.R.drawable.sym_def_app_icon).build();
- return MediaNotification.create(DEFAULT_MEDIA_NOTIFICATION_ID, notification);
+ return new MediaNotification(this, DEFAULT_MEDIA_NOTIFICATION_ID, notification);
}
private class MySessionCallback extends SessionCallback {
diff --git a/packages/MediaComponents/test/src/android/media/TestUtils.java b/packages/MediaComponents/test/src/android/media/TestUtils.java
index 9a1fa10..12b24c0 100644
--- a/packages/MediaComponents/test/src/android/media/TestUtils.java
+++ b/packages/MediaComponents/test/src/android/media/TestUtils.java
@@ -17,6 +17,7 @@
package android.media;
import android.content.Context;
+import android.media.MediaSession2.PlaylistParams;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
import android.os.Bundle;
@@ -29,6 +30,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -40,17 +42,6 @@
private static final int WAIT_SERVICE_TIME_MS = 5000;
/**
- * Creates a {@link android.media.session.PlaybackState} with the given state.
- *
- * @param state one of the PlaybackState.STATE_xxx.
- * @return a PlaybackState
- */
- public static PlaybackState2 createPlaybackState(int state) {
- return new PlaybackState2(state, 0, 0, 1.0f,
- 0, 0, null);
- }
-
- /**
* Finds the session with id in this test package.
*
* @param context
@@ -100,6 +91,11 @@
return true;
}
+ public static void ensurePlaylistParamsModeEquals(PlaylistParams a, PlaylistParams b) {
+ assertEquals(a.getRepeatMode(), b.getRepeatMode());
+ assertEquals(a.getShuffleMode(), b.getShuffleMode());
+ }
+
/**
* Handler that always waits until the Runnable finishes.
*/
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 83caca7..7c38bcc 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -559,6 +559,8 @@
virtual binder::Status start(int /*AudioSystem::sync_event_t*/ event,
int /*audio_session_t*/ triggerSession);
virtual binder::Status stop();
+ virtual binder::Status getActiveMicrophones(
+ std::vector<media::MicrophoneInfo>* activeMicrophones);
private:
const sp<RecordThread::RecordTrack> mRecordTrack;
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 63a3d98..1733ef5 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -66,6 +66,8 @@
void setSilenced(bool silenced) { mSilenced = silenced; }
bool isSilenced() const { return mSilenced; }
+ status_t getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones);
+
private:
friend class AudioFlinger; // for mState
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 420e6e1..14d7e2e 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -7043,6 +7043,49 @@
#endif
}
+status_t AudioFlinger::RecordThread::getActiveMicrophones(
+ std::vector<media::MicrophoneInfo>* activeMicrophones)
+{
+ ALOGV("RecordThread::getActiveMicrophones");
+ AutoMutex _l(mLock);
+ // Fake data
+ struct audio_microphone_characteristic_t characteristic;
+ sprintf(characteristic.device_id, "builtin_mic");
+ characteristic.type = AUDIO_DEVICE_IN_BUILTIN_MIC;
+ sprintf(characteristic.address, "");
+ characteristic.location = AUDIO_MICROPHONE_LOCATION_MAINBODY;
+ characteristic.group = 0;
+ characteristic.index_in_the_group = 0;
+ characteristic.sensitivity = 1.0f;
+ characteristic.max_spl = 100.0f;
+ characteristic.min_spl = 0.0f;
+ characteristic.directionality = AUDIO_MICROPHONE_DIRECTIONALITY_OMNI;
+ characteristic.num_frequency_responses = 5;
+ for (size_t i = 0; i < characteristic.num_frequency_responses; i++) {
+ characteristic.frequency_responses[0][i] = 100.0f - i;
+ characteristic.frequency_responses[1][i] = 100.0f + i;
+ }
+ for (size_t i = 0; i < AUDIO_CHANNEL_COUNT_MAX; i++) {
+ characteristic.channel_mapping[i] = AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED;
+ }
+ audio_microphone_channel_mapping_t channel_mappings[] = {
+ AUDIO_MICROPHONE_CHANNEL_MAPPING_DIRECT,
+ AUDIO_MICROPHONE_CHANNEL_MAPPING_PROCESSED,
+ };
+ for (size_t i = 0; i < mChannelCount; i++) {
+ characteristic.channel_mapping[i] = channel_mappings[i % 2];
+ }
+ characteristic.geometric_location.x = 0.1f;
+ characteristic.geometric_location.y = 0.2f;
+ characteristic.geometric_location.z = 0.3f;
+ characteristic.orientation.x = 0.0f;
+ characteristic.orientation.y = 1.0f;
+ characteristic.orientation.z = 0.0f;
+ media::MicrophoneInfo microphoneInfo = media::MicrophoneInfo(characteristic);
+ activeMicrophones->push_back(microphoneInfo);
+ return NO_ERROR;
+}
+
// destroyTrack_l() must be called with ThreadBase::mLock held
void AudioFlinger::RecordThread::destroyTrack_l(const sp<RecordTrack>& track)
{
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index eb29497..53cb8ad 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1398,6 +1398,8 @@
// Sets the UID records silence
void setRecordSilenced(uid_t uid, bool silenced);
+ status_t getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones);
+
private:
// Enter standby if not already in standby, and set mStandby flag
void standbyIfNotAlreadyInStandby();
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index cdd8ca0..ce5c53b 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1580,6 +1580,13 @@
mRecordTrack->stop();
}
+binder::Status AudioFlinger::RecordHandle::getActiveMicrophones(
+ std::vector<media::MicrophoneInfo>* activeMicrophones) {
+ ALOGV("RecordHandle::getActiveMicrophones()");
+ return binder::Status::fromStatusT(
+ mRecordTrack->getActiveMicrophones(activeMicrophones));
+}
+
// ----------------------------------------------------------------------------
// RecordTrack constructor must be called with AudioFlinger::mLock and ThreadBase::mLock held
@@ -1792,6 +1799,18 @@
mServerProxy->setTimestamp(local);
}
+status_t AudioFlinger::RecordThread::RecordTrack::getActiveMicrophones(
+ std::vector<media::MicrophoneInfo>* activeMicrophones)
+{
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ RecordThread *recordThread = (RecordThread *)thread.get();
+ return recordThread->getActiveMicrophones(activeMicrophones);
+ } else {
+ return BAD_VALUE;
+ }
+}
+
AudioFlinger::RecordThread::PatchRecord::PatchRecord(RecordThread *recordThread,
uint32_t sampleRate,
audio_channel_mask_t channelMask,
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 7343601..40e0199 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1427,7 +1427,7 @@
// Explicit routing?
sp<DeviceDescriptor> deviceDesc;
if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) {
- deviceDesc = mAvailableOutputDevices.getDeviceFromId(*selectedDeviceId);
+ deviceDesc = mAvailableInputDevices.getDeviceFromId(*selectedDeviceId);
}
mInputRoutes.addRoute(session, SessionRoute::STREAM_TYPE_NA, inputSource, deviceDesc, uid);
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index e5aed9a..90a5a0f 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -273,7 +273,7 @@
void AudioPolicyService::NotificationClient::onDynamicPolicyMixStateUpdate(
const String8& regId, int32_t state)
{
- if (mAudioPolicyServiceClient != 0) {
+ if (mAudioPolicyServiceClient != 0 && mUid < AID_APP_START) {
mAudioPolicyServiceClient->onDynamicPolicyMixStateUpdate(regId, state);
}
}
@@ -283,7 +283,7 @@
const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig,
audio_patch_handle_t patchHandle)
{
- if (mAudioPolicyServiceClient != 0) {
+ if (mAudioPolicyServiceClient != 0 && mUid < AID_APP_START) {
mAudioPolicyServiceClient->onRecordingConfigurationUpdate(event, clientInfo,
clientConfig, deviceConfig, patchHandle);
}
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index a132e25..6d84a42 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -100,9 +100,6 @@
mDumpProtoDefault(MediaAnalyticsItem::PROTO_V1) {
ALOGD("MediaAnalyticsService created");
- // clear our queues
- mOpen = new List<MediaAnalyticsItem *>();
- mFinalized = new List<MediaAnalyticsItem *>();
mItemsSubmitted = 0;
mItemsFinalized = 0;
@@ -118,26 +115,13 @@
MediaAnalyticsService::~MediaAnalyticsService() {
ALOGD("MediaAnalyticsService destroyed");
- // clean out mOpen and mFinalized
- while (mOpen->size() > 0) {
- MediaAnalyticsItem * oitem = *(mOpen->begin());
- mOpen->erase(mOpen->begin());
+ while (mItems.size() > 0) {
+ MediaAnalyticsItem * oitem = *(mItems.begin());
+ mItems.erase(mItems.begin());
delete oitem;
mItemsDiscarded++;
mItemsDiscardedCount++;
}
- delete mOpen;
- mOpen = NULL;
-
- while (mFinalized->size() > 0) {
- MediaAnalyticsItem * oitem = *(mFinalized->begin());
- mFinalized->erase(mFinalized->begin());
- delete oitem;
- mItemsDiscarded++;
- mItemsDiscardedCount++;
- }
- delete mFinalized;
- mFinalized = NULL;
}
@@ -149,9 +133,14 @@
}
// caller surrenders ownership of 'item'
-MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(MediaAnalyticsItem *item, bool forcenew) {
+MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(MediaAnalyticsItem *item, bool forcenew)
+{
+ UNUSED(forcenew);
- MediaAnalyticsItem::SessionID_t id = MediaAnalyticsItem::SessionIDInvalid;
+ // fill in a sessionID if we do not yet have one
+ if (item->getSessionID() <= MediaAnalyticsItem::SessionIDNone) {
+ item->setSessionID(generateUniqueSessionID());
+ }
// we control these, generally not trusting user input
nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
@@ -164,9 +153,7 @@
int uid_given = item->getUid();
int pid_given = item->getPid();
- // although we do make exceptions for particular client uids
- // that we know we trust.
- //
+ // although we do make exceptions for some trusted client uids
bool isTrusted = false;
ALOGV("caller has uid=%d, embedded uid=%d", uid, uid_given);
@@ -192,7 +179,6 @@
break;
}
-
// Overwrite package name and version if the caller was untrusted.
if (!isTrusted) {
setPkgInfo(item, item->getUid(), true, true);
@@ -217,75 +203,23 @@
return MediaAnalyticsItem::SessionIDInvalid;
}
-
- // if we have a sesisonid in the new record, look to make
+ // XXX: if we have a sessionid in the new record, look to make
// sure it doesn't appear in the finalized list.
// XXX: this is for security / DOS prevention.
// may also require that we persist the unique sessionIDs
// across boots [instead of within a single boot]
-
- // match this new record up against records in the open
- // list...
- // if there's a match, merge them together
- // deal with moving the old / merged record into the finalized que
-
- bool finalizing = item->getFinalized();
-
- Mutex::Autolock _l(mLock);
-
- // if finalizing, we'll remove it
- MediaAnalyticsItem *oitem = findItem(mOpen, item, finalizing | forcenew);
- if (oitem != NULL) {
- if (forcenew) {
- // old one gets finalized, then we insert the new one
- // so we'll have 2 records at the end of this.
- // but don't finalize an empty record
- if (oitem->count() == 0) {
- // we're responsible for disposing of the dead record
- delete oitem;
- oitem = NULL;
- } else {
- oitem->setFinalized(true);
- saveItem(mFinalized, oitem, 0);
- }
- // new record could itself be marked finalized...
- id = item->getSessionID();
- if (finalizing) {
- saveItem(mFinalized, item, 0);
- mItemsFinalized++;
- } else {
- saveItem(mOpen, item, 1);
- }
- } else {
- // combine the records, send it to finalized if appropriate
- oitem->merge(item);
- id = oitem->getSessionID();
- if (finalizing) {
- saveItem(mFinalized, oitem, 0);
- mItemsFinalized++;
- }
-
- // we're responsible for disposing of the dead record
- delete item;
- item = NULL;
- }
- } else {
- // nothing to merge, save the new record
- id = item->getSessionID();
- if (finalizing) {
- if (item->count() == 0) {
- // drop empty records
- delete item;
- item = NULL;
- } else {
- saveItem(mFinalized, item, 0);
- mItemsFinalized++;
- }
- } else {
- saveItem(mOpen, item, 1);
- }
+ if (item->count() == 0) {
+ // drop empty records
+ delete item;
+ item = NULL;
+ return MediaAnalyticsItem::SessionIDInvalid;
}
+
+ // save the new record
+ MediaAnalyticsItem::SessionID_t id = item->getSessionID();
+ saveItem(item);
+ mItemsFinalized++;
return id;
}
@@ -378,6 +312,7 @@
}
Mutex::Autolock _l(mLock);
+ // mutex between insertion and dumping the contents
mDumpProto = chosenProto;
@@ -392,9 +327,9 @@
if (clear) {
// remove everything from the finalized queue
- while (mFinalized->size() > 0) {
- MediaAnalyticsItem * oitem = *(mFinalized->begin());
- mFinalized->erase(mFinalized->begin());
+ while (mItems.size() > 0) {
+ MediaAnalyticsItem * oitem = *(mItems.begin());
+ mItems.erase(mItems.begin());
delete oitem;
mItemsDiscarded++;
}
@@ -408,7 +343,8 @@
}
// dump headers
-void MediaAnalyticsService::dumpHeaders(String8 &result, nsecs_t ts_since) {
+void MediaAnalyticsService::dumpHeaders(String8 &result, nsecs_t ts_since)
+{
const size_t SIZE = 512;
char buffer[SIZE];
@@ -425,7 +361,7 @@
snprintf(buffer, SIZE,
"Since Boot: Submissions: %8" PRId64
- " Finalizations: %8" PRId64 "\n",
+ " Accepted: %8" PRId64 "\n",
mItemsSubmitted, mItemsFinalized);
result.append(buffer);
snprintf(buffer, SIZE,
@@ -433,19 +369,17 @@
" (by Count: %" PRId64 " by Expiration: %" PRId64 ")\n",
mItemsDiscarded, mItemsDiscardedCount, mItemsDiscardedExpire);
result.append(buffer);
- snprintf(buffer, SIZE,
- "Summary Sets Discarded: %" PRId64 "\n", mSetsDiscarded);
- result.append(buffer);
if (ts_since != 0) {
snprintf(buffer, SIZE,
- "Dumping Queue entries more recent than: %" PRId64 "\n",
+ "Emitting Queue entries more recent than: %" PRId64 "\n",
(int64_t) ts_since);
result.append(buffer);
}
}
// the recent, detailed queues
-void MediaAnalyticsService::dumpRecent(String8 &result, nsecs_t ts_since, const char * only) {
+void MediaAnalyticsService::dumpRecent(String8 &result, nsecs_t ts_since, const char * only)
+{
const size_t SIZE = 512;
char buffer[SIZE];
@@ -456,30 +390,27 @@
// show the recently recorded records
snprintf(buffer, sizeof(buffer), "\nFinalized Metrics (oldest first):\n");
result.append(buffer);
- result.append(this->dumpQueue(mFinalized, ts_since, only));
-
- snprintf(buffer, sizeof(buffer), "\nIn-Progress Metrics (newest first):\n");
- result.append(buffer);
- result.append(this->dumpQueue(mOpen, ts_since, only));
+ result.append(this->dumpQueue(ts_since, only));
// show who is connected and injecting records?
// talk about # records fed to the 'readers'
// talk about # records we discarded, perhaps "discarded w/o reading" too
}
+
// caller has locked mLock...
-String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList) {
- return dumpQueue(theList, (nsecs_t) 0, NULL);
+String8 MediaAnalyticsService::dumpQueue() {
+ return dumpQueue((nsecs_t) 0, NULL);
}
-String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList, nsecs_t ts_since, const char * only) {
+String8 MediaAnalyticsService::dumpQueue(nsecs_t ts_since, const char * only) {
String8 result;
int slot = 0;
- if (theList->empty()) {
+ if (mItems.empty()) {
result.append("empty\n");
} else {
- List<MediaAnalyticsItem *>::iterator it = theList->begin();
- for (; it != theList->end(); it++) {
+ List<MediaAnalyticsItem *>::iterator it = mItems.begin();
+ for (; it != mItems.end(); it++) {
nsecs_t when = (*it)->getTimestamp();
if (when < ts_since) {
continue;
@@ -500,35 +431,25 @@
//
// Our Cheap in-core, non-persistent records management.
-// XXX: rewrite this to manage persistence, etc.
// insert appropriately into queue
-// caller should hold mLock
-void MediaAnalyticsService::saveItem(List<MediaAnalyticsItem *> *l, MediaAnalyticsItem * item, int front) {
+void MediaAnalyticsService::saveItem(MediaAnalyticsItem * item)
+{
- if (front) {
- // for non-finalized stuff, since we expect to reference it again soon,
- // make it quicker to find (nearer the front of our list)
- l->push_front(item);
- } else {
- // for finalized records, which we want to dump 'in sequence order'
- l->push_back(item);
- }
+ Mutex::Autolock _l(mLock);
+ // mutex between insertion and dumping the contents
- // our reclaim process is for oldest-first queues
- if (front) {
- return;
- }
-
+ // we want to dump 'in FIFO order', so insert at the end
+ mItems.push_back(item);
// keep removing old records the front until we're in-bounds (count)
if (mMaxRecords > 0) {
- while (l->size() > (size_t) mMaxRecords) {
- MediaAnalyticsItem * oitem = *(l->begin());
+ while (mItems.size() > (size_t) mMaxRecords) {
+ MediaAnalyticsItem * oitem = *(mItems.begin());
if (oitem == item) {
break;
}
- l->erase(l->begin());
+ mItems.erase(mItems.begin());
delete oitem;
mItemsDiscarded++;
mItemsDiscardedCount++;
@@ -536,10 +457,11 @@
}
// keep removing old records the front until we're in-bounds (count)
+ // NB: expired entries aren't removed until the next insertion, which could be a while
if (mMaxRecordAgeNs > 0) {
nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
- while (l->size() > 0) {
- MediaAnalyticsItem * oitem = *(l->begin());
+ while (mItems.size() > 0) {
+ MediaAnalyticsItem * oitem = *(mItems.begin());
nsecs_t when = oitem->getTimestamp();
if (oitem == item) {
break;
@@ -549,7 +471,7 @@
// this (and the rest) are recent enough to keep
break;
}
- l->erase(l->begin());
+ mItems.erase(mItems.begin());
delete oitem;
mItemsDiscarded++;
mItemsDiscardedExpire++;
@@ -557,79 +479,6 @@
}
}
-// are they alike enough that nitem can be folded into oitem?
-static bool compatibleItems(MediaAnalyticsItem * oitem, MediaAnalyticsItem * nitem) {
-
- // general safety
- if (nitem->getUid() != oitem->getUid()) {
- return false;
- }
- if (nitem->getPid() != oitem->getPid()) {
- return false;
- }
-
- // key -- needs to match
- if (nitem->getKey() == oitem->getKey()) {
- // still in the game.
- } else {
- return false;
- }
-
- // session id -- empty field in new is allowed
- MediaAnalyticsItem::SessionID_t osession = oitem->getSessionID();
- MediaAnalyticsItem::SessionID_t nsession = nitem->getSessionID();
- if (nsession != osession) {
- // incoming '0' matches value in osession
- if (nsession != 0) {
- return false;
- }
- }
-
- return true;
-}
-
-// find the incomplete record that this will overlay
-// caller should hold mLock
-MediaAnalyticsItem *MediaAnalyticsService::findItem(List<MediaAnalyticsItem*> *theList, MediaAnalyticsItem *nitem, bool removeit) {
- if (nitem == NULL) {
- return NULL;
- }
-
- MediaAnalyticsItem *item = NULL;
-
- for (List<MediaAnalyticsItem *>::iterator it = theList->begin();
- it != theList->end(); it++) {
- MediaAnalyticsItem *tmp = (*it);
-
- if (!compatibleItems(tmp, nitem)) {
- continue;
- }
-
- // we match! this is the one I want.
- if (removeit) {
- theList->erase(it);
- }
- item = tmp;
- break;
- }
- return item;
-}
-
-
-// delete the indicated record
-// caller should hold mLock
-void MediaAnalyticsService::deleteItem(List<MediaAnalyticsItem *> *l, MediaAnalyticsItem *item) {
-
- for (List<MediaAnalyticsItem *>::iterator it = l->begin();
- it != l->end(); it++) {
- if ((*it)->getSessionID() != item->getSessionID())
- continue;
- delete *it;
- l->erase(it);
- break;
- }
-}
-
static std::string allowedKeys[] =
{
"audiorecord",
@@ -676,7 +525,9 @@
#define PKG_EXPIRATION_NS (30*60*1000000000ll) // 30 minutes, in nsecs
// give me the package name, perhaps going to find it
-void MediaAnalyticsService::setPkgInfo(MediaAnalyticsItem *item, uid_t uid, bool setName, bool setVersion) {
+// manages its own mutex operations internally
+void MediaAnalyticsService::setPkgInfo(MediaAnalyticsItem *item, uid_t uid, bool setName, bool setVersion)
+{
ALOGV("asking for packagename to go with uid=%d", uid);
if (!setName && !setVersion) {
@@ -686,22 +537,26 @@
nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
struct UidToPkgMap mapping;
- mapping.uid = (-1);
+ mapping.uid = (uid_t)(-1);
- ssize_t i = mPkgMappings.indexOfKey(uid);
- if (i >= 0) {
- mapping = mPkgMappings.valueAt(i);
- ALOGV("Expiration? uid %d expiration %" PRId64 " now %" PRId64,
- uid, mapping.expiration, now);
- if (mapping.expiration < now) {
- // purge our current entry and re-query
- ALOGV("entry for uid %d expired, now= %" PRId64 "", uid, now);
- mPkgMappings.removeItemsAt(i, 1);
- // could cheat and use a goto back to the top of the routine.
- // a good compiler should recognize the local tail recursion...
- return setPkgInfo(item, uid, setName, setVersion);
+ {
+ Mutex::Autolock _l(mLock_mappings);
+ int i = mPkgMappings.indexOfKey(uid);
+ if (i >= 0) {
+ mapping = mPkgMappings.valueAt(i);
+ ALOGV("Expiration? uid %d expiration %" PRId64 " now %" PRId64,
+ uid, mapping.expiration, now);
+ if (mapping.expiration <= now) {
+ // purge the stale entry and fall into re-fetching
+ ALOGV("entry for uid %d expired, now= %" PRId64 "", uid, now);
+ mPkgMappings.removeItemsAt(i);
+ mapping.uid = (uid_t)(-1);
+ }
}
- } else {
+ }
+
+ // if we did not find it
+ if (mapping.uid == (uid_t)(-1)) {
std::string pkg;
std::string installer = "";
int64_t versionCode = 0;
@@ -711,7 +566,7 @@
pkg = pw->pw_name;
}
- // find the proper value -- should we cache this binder??
+ // find the proper value
sp<IBinder> binder = NULL;
sp<IServiceManager> sm = defaultServiceManager();
diff --git a/services/mediaanalytics/MediaAnalyticsService.h b/services/mediaanalytics/MediaAnalyticsService.h
index 484339c..b3c902a 100644
--- a/services/mediaanalytics/MediaAnalyticsService.h
+++ b/services/mediaanalytics/MediaAnalyticsService.h
@@ -53,7 +53,6 @@
int64_t mItemsDiscarded;
int64_t mItemsDiscardedExpire;
int64_t mItemsDiscardedCount;
- int64_t mSetsDiscarded;
MediaAnalyticsItem::SessionID_t mLastSessionID;
// partitioned a bit so we don't over serialize
@@ -76,25 +75,15 @@
bool contentValid(MediaAnalyticsItem *item, bool isTrusted);
bool rateLimited(MediaAnalyticsItem *);
- // the ones that are still open
- // (newest at front) since we keep looking for them
- List<MediaAnalyticsItem *> *mOpen;
- // the ones we've finalized
// (oldest at front) so it prints nicely for dumpsys
- List<MediaAnalyticsItem *> *mFinalized;
- // searching within these queues: queue, key
- MediaAnalyticsItem *findItem(List<MediaAnalyticsItem *> *,
- MediaAnalyticsItem *, bool removeit);
-
- void saveItem(MediaAnalyticsItem);
- void saveItem(List<MediaAnalyticsItem *> *, MediaAnalyticsItem *, int);
- void deleteItem(List<MediaAnalyticsItem *> *, MediaAnalyticsItem *);
+ List<MediaAnalyticsItem *> mItems;
+ void saveItem(MediaAnalyticsItem *);
// support for generating output
int mDumpProto;
int mDumpProtoDefault;
- String8 dumpQueue(List<MediaAnalyticsItem*> *);
- String8 dumpQueue(List<MediaAnalyticsItem*> *, nsecs_t, const char *only);
+ String8 dumpQueue();
+ String8 dumpQueue(nsecs_t, const char *only);
void dumpHeaders(String8 &result, nsecs_t ts_since);
void dumpSummaries(String8 &result, nsecs_t ts_since, const char * only);