Merge changes from topic "media-drm-fix-list-metrics"
* changes:
Fix Metrics with PersistableBundle support
Fix DrmHal implementation to return all metrics
diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h
index c0eb5c1..f425830 100644
--- a/camera/ndk/include/camera/NdkCameraDevice.h
+++ b/camera/ndk/include/camera/NdkCameraDevice.h
@@ -252,35 +252,6 @@
*/
TEMPLATE_MANUAL = 6,
- /**
- * A template for selecting camera parameters that match TEMPLATE_PREVIEW as closely as
- * possible while improving the camera output for motion tracking use cases.
- *
- * <p>This template is best used by applications that are frequently switching between motion
- * tracking use cases and regular still capture use cases, to minimize the IQ changes
- * when swapping use cases.</p>
- *
- * <p>This template is guaranteed to be supported on camera devices that support the
- * {@link ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING} capability.</p>
- *
- * @see ACameraDevice_createCaptureRequest
- */
- TEMPLATE_MOTION_TRACKING_PREVIEW = 7,
-
- /**
- * A template for selecting camera parameters that maximize the quality of camera output for
- * motion tracking use cases.
- *
- * <p>This template is best used by applications dedicated to motion tracking applications,
- * which aren't concerned about fast switches between motion tracking and other use cases.</p>
- *
- * <p>This template is guaranteed to be supported on camera devices that support the
- * {@link ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING} capability.</p>
- *
- * @see ACameraDevice_createCaptureRequest
- */
- TEMPLATE_MOTION_TRACKING_BEST = 8,
-
} ACameraDevice_request_template;
/**
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 588e96a..d35a52b 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -2453,9 +2453,6 @@
*
* <p>Different calibration methods and use cases can produce better or worse results
* depending on the selected coordinate origin.</p>
- * <p>For devices designed to support the MOTION_TRACKING capability, the GYROSCOPE origin
- * makes device calibration and later usage by applications combining camera and gyroscope
- * information together simpler.</p>
*/
ACAMERA_LENS_POSE_REFERENCE = // byte (acamera_metadata_enum_android_lens_pose_reference_t)
ACAMERA_LENS_START + 12,
@@ -4565,7 +4562,7 @@
ACAMERA_STATISTICS_LENS_SHADING_MAP_MODE = // byte (acamera_metadata_enum_android_statistics_lens_shading_map_mode_t)
ACAMERA_STATISTICS_START + 16,
/**
- * <p>Whether the camera device outputs the OIS data in output
+ * <p>A control for selecting whether OIS position information is included in output
* result metadata.</p>
*
* <p>Type: byte (acamera_metadata_enum_android_statistics_ois_data_mode_t)</p>
@@ -4578,7 +4575,7 @@
*
* <p>When set to ON,
* ACAMERA_STATISTICS_OIS_TIMESTAMPS, android.statistics.oisShiftPixelX,
- * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p>
+ * and android.statistics.oisShiftPixelY provide OIS data in the output result metadata.</p>
*
* @see ACAMERA_STATISTICS_OIS_TIMESTAMPS
*/
@@ -6601,7 +6598,7 @@
/**
* <p>The value of ACAMERA_LENS_POSE_TRANSLATION is relative to the optical center of
* the largest camera device facing the same direction as this camera.</p>
- * <p>This default value for API levels before Android P.</p>
+ * <p>This is the default value for API levels before Android P.</p>
*
* @see ACAMERA_LENS_POSE_TRANSLATION
*/
@@ -6610,7 +6607,6 @@
/**
* <p>The value of ACAMERA_LENS_POSE_TRANSLATION is relative to the position of the
* primary gyroscope of this Android device.</p>
- * <p>This is the value reported by all devices that support the MOTION_TRACKING capability.</p>
*
* @see ACAMERA_LENS_POSE_TRANSLATION
*/
@@ -6974,46 +6970,12 @@
ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8,
/**
- * <p>The device supports controls and metadata required for accurate motion tracking for
- * use cases such as augmented reality, electronic image stabilization, and so on.</p>
- * <p>This means this camera device has accurate optical calibration and timestamps relative
- * to the inertial sensors.</p>
- * <p>This capability requires the camera device to support the following:</p>
- * <ul>
- * <li>Capture request templates <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice.html#TEMPLATE_MOTION_TRACKING_PREVIEW">CameraDevice#TEMPLATE_MOTION_TRACKING_PREVIEW</a> and <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice.html#TEMPLATE_MOTION_TRACKING_BEST">CameraDevice#TEMPLATE_MOTION_TRACKING_BEST</a> are defined.</li>
- * <li>The stream configurations listed in <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice.html#createCaptureSession">CameraDevice#createCaptureSession</a> for MOTION_TRACKING are
- * supported, either at 30 or 60fps maximum frame rate.</li>
- * <li>The following camera characteristics and capture result metadata are provided:<ul>
- * <li>ACAMERA_LENS_INTRINSIC_CALIBRATION</li>
- * <li>ACAMERA_LENS_RADIAL_DISTORTION</li>
- * <li>ACAMERA_LENS_POSE_ROTATION</li>
- * <li>ACAMERA_LENS_POSE_TRANSLATION</li>
- * <li>ACAMERA_LENS_POSE_REFERENCE with value GYROSCOPE</li>
- * </ul>
- * </li>
- * <li>The ACAMERA_SENSOR_INFO_TIMESTAMP_SOURCE field has value <code>REALTIME</code>. When compared to
- * timestamps from the device's gyroscopes, the clock difference for events occuring at
- * the same actual time instant will be less than 1 ms.</li>
- * <li>The value of the ACAMERA_SENSOR_ROLLING_SHUTTER_SKEW field is accurate to within 1 ms.</li>
- * <li>The value of ACAMERA_SENSOR_EXPOSURE_TIME is guaranteed to be available in the
- * capture result.</li>
- * <li>The ACAMERA_CONTROL_CAPTURE_INTENT control supports MOTION_TRACKING to limit maximum
- * exposure to 20 milliseconds.</li>
- * <li>The stream configurations required for MOTION_TRACKING (listed at <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice.html#createCaptureSession">CameraDevice#createCaptureSession</a>) can operate at least at
- * 30fps; optionally, they can operate at 60fps, and '[60, 60]' is listed in
- * ACAMERA_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES.</li>
- * </ul>
+ * <p>The camera device supports the MOTION_TRACKING value for
+ * ACAMERA_CONTROL_CAPTURE_INTENT, which limits maximum exposure time to 20 ms.</p>
+ * <p>This limits the motion blur of capture images, resulting in better image tracking
+ * results for use cases such as image stabilization or augmented reality.</p>
*
- * @see ACAMERA_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES
* @see ACAMERA_CONTROL_CAPTURE_INTENT
- * @see ACAMERA_LENS_INTRINSIC_CALIBRATION
- * @see ACAMERA_LENS_POSE_REFERENCE
- * @see ACAMERA_LENS_POSE_ROTATION
- * @see ACAMERA_LENS_POSE_TRANSLATION
- * @see ACAMERA_LENS_RADIAL_DISTORTION
- * @see ACAMERA_SENSOR_EXPOSURE_TIME
- * @see ACAMERA_SENSOR_INFO_TIMESTAMP_SOURCE
- * @see ACAMERA_SENSOR_ROLLING_SHUTTER_SKEW
*/
ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10,
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 46bd8f0..9d2daab 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -50,6 +50,7 @@
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaMuxer.h>
+#include <media/stagefright/PersistentSurface.h>
#include <media/ICrypto.h>
#include <media/MediaCodecBuffer.h>
@@ -70,6 +71,7 @@
static bool gVerbose = false; // chatty on stdout
static bool gRotate = false; // rotate 90 degrees
static bool gMonotonicTime = false; // use system monotonic time for timestamps
+static bool gPersistentSurface = false; // use persistent surface
static enum {
FORMAT_MP4, FORMAT_H264, FORMAT_FRAMES, FORMAT_RAW_FRAMES
} gOutputFormat = FORMAT_MP4; // data format for output
@@ -199,10 +201,18 @@
ALOGV("Creating encoder input surface");
sp<IGraphicBufferProducer> bufferProducer;
- err = codec->createInputSurface(&bufferProducer);
+ if (gPersistentSurface) {
+ sp<PersistentSurface> surface = MediaCodec::CreatePersistentInputSurface();
+ bufferProducer = surface->getBufferProducer();
+ err = codec->setInputSurface(surface);
+ } else {
+ err = codec->createInputSurface(&bufferProducer);
+ }
if (err != NO_ERROR) {
fprintf(stderr,
- "ERROR: unable to create encoder input surface (err=%d)\n", err);
+ "ERROR: unable to %s encoder input surface (err=%d)\n",
+ gPersistentSurface ? "set" : "create",
+ err);
codec->release();
return err;
}
@@ -920,6 +930,7 @@
{ "output-format", required_argument, NULL, 'o' },
{ "codec-name", required_argument, NULL, 'N' },
{ "monotonic-time", no_argument, NULL, 'm' },
+ { "persistent-surface", no_argument, NULL, 'p' },
{ NULL, 0, NULL, 0 }
};
@@ -1005,6 +1016,9 @@
case 'm':
gMonotonicTime = true;
break;
+ case 'p':
+ gPersistentSurface = true;
+ break;
default:
if (ic != '?') {
fprintf(stderr, "getopt_long returned unexpected value 0x%x\n", ic);
diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp
index 2114d40..4377154 100644
--- a/drm/libmediadrm/CryptoHal.cpp
+++ b/drm/libmediadrm/CryptoHal.cpp
@@ -118,15 +118,24 @@
auto manager = ::IServiceManager::getService();
if (manager != NULL) {
- manager->listByInterface(ICryptoFactory::descriptor,
+ manager->listByInterface(drm::V1_0::ICryptoFactory::descriptor,
[&factories](const hidl_vec<hidl_string> ®istered) {
for (const auto &instance : registered) {
- auto factory = ICryptoFactory::getService(instance);
+ auto factory = drm::V1_0::ICryptoFactory::getService(instance);
if (factory != NULL) {
+ ALOGD("found drm@1.0 ICryptoFactory %s", instance.c_str());
factories.push_back(factory);
- ALOGI("makeCryptoFactories: factory instance %s is %s",
- instance.c_str(),
- factory->isRemote() ? "Remote" : "Not Remote");
+ }
+ }
+ }
+ );
+ manager->listByInterface(drm::V1_1::ICryptoFactory::descriptor,
+ [&factories](const hidl_vec<hidl_string> ®istered) {
+ for (const auto &instance : registered) {
+ auto factory = drm::V1_1::ICryptoFactory::getService(instance);
+ if (factory != NULL) {
+ ALOGD("found drm@1.1 ICryptoFactory %s", instance.c_str());
+ factories.push_back(factory);
}
}
}
@@ -137,7 +146,7 @@
// must be in passthrough mode, load the default passthrough service
auto passthrough = ICryptoFactory::getService();
if (passthrough != NULL) {
- ALOGI("makeCryptoFactories: using default crypto instance");
+ ALOGI("makeCryptoFactories: using default passthrough crypto instance");
factories.push_back(passthrough);
} else {
ALOGE("Failed to find any crypto factories");
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index c88c836..6b782f0 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -21,8 +21,6 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
-#include <android/hardware/drm/1.0/IDrmFactory.h>
-#include <android/hardware/drm/1.0/IDrmPlugin.h>
#include <android/hardware/drm/1.0/types.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <hidl/ServiceManagement.h>
@@ -272,15 +270,24 @@
auto manager = hardware::defaultServiceManager();
if (manager != NULL) {
- manager->listByInterface(IDrmFactory::descriptor,
+ manager->listByInterface(drm::V1_0::IDrmFactory::descriptor,
[&factories](const hidl_vec<hidl_string> ®istered) {
for (const auto &instance : registered) {
- auto factory = IDrmFactory::getService(instance);
+ auto factory = drm::V1_0::IDrmFactory::getService(instance);
if (factory != NULL) {
+ ALOGD("found drm@1.0 IDrmFactory %s", instance.c_str());
factories.push_back(factory);
- ALOGI("makeDrmFactories: factory instance %s is %s",
- instance.c_str(),
- factory->isRemote() ? "Remote" : "Not Remote");
+ }
+ }
+ }
+ );
+ manager->listByInterface(drm::V1_1::IDrmFactory::descriptor,
+ [&factories](const hidl_vec<hidl_string> ®istered) {
+ for (const auto &instance : registered) {
+ auto factory = drm::V1_1::IDrmFactory::getService(instance);
+ if (factory != NULL) {
+ ALOGD("found drm@1.1 IDrmFactory %s", instance.c_str());
+ factories.push_back(factory);
}
}
}
@@ -291,7 +298,7 @@
// must be in passthrough mode, load the default passthrough service
auto passthrough = IDrmFactory::getService();
if (passthrough != NULL) {
- ALOGI("makeDrmFactories: using default drm instance");
+ ALOGI("makeDrmFactories: using default passthrough drm instance");
factories.push_back(passthrough);
} else {
ALOGE("Failed to find any drm factories");
@@ -511,24 +518,62 @@
return OK;
}
-status_t DrmHal::openSession(Vector<uint8_t> &sessionId) {
+status_t DrmHal::openSession(DrmPlugin::SecurityLevel level,
+ Vector<uint8_t> &sessionId) {
Mutex::Autolock autoLock(mLock);
INIT_CHECK();
- status_t err = UNKNOWN_ERROR;
+ SecurityLevel hSecurityLevel;
+ bool setSecurityLevel = true;
+ switch(level) {
+ case DrmPlugin::kSecurityLevelSwSecureCrypto:
+ hSecurityLevel = SecurityLevel::SW_SECURE_CRYPTO;
+ break;
+ case DrmPlugin::kSecurityLevelSwSecureDecode:
+ hSecurityLevel = SecurityLevel::SW_SECURE_DECODE;
+ break;
+ case DrmPlugin::kSecurityLevelHwSecureCrypto:
+ hSecurityLevel = SecurityLevel::HW_SECURE_CRYPTO;
+ break;
+ case DrmPlugin::kSecurityLevelHwSecureDecode:
+ hSecurityLevel = SecurityLevel::HW_SECURE_DECODE;
+ break;
+ case DrmPlugin::kSecurityLevelHwSecureAll:
+ hSecurityLevel = SecurityLevel::HW_SECURE_ALL;
+ break;
+ case DrmPlugin::kSecurityLevelMax:
+ setSecurityLevel = false;
+ break;
+ default:
+ return ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ status_t err = UNKNOWN_ERROR;
bool retry = true;
do {
hidl_vec<uint8_t> hSessionId;
- Return<void> hResult = mPlugin->openSession(
- [&](Status status, const hidl_vec<uint8_t>& id) {
- if (status == Status::OK) {
- sessionId = toVector(id);
+ Return<void> hResult;
+ if (mPluginV1_1 == NULL || !setSecurityLevel) {
+ hResult = mPlugin->openSession(
+ [&](Status status,const hidl_vec<uint8_t>& id) {
+ if (status == Status::OK) {
+ sessionId = toVector(id);
+ }
+ err = toStatusT(status);
}
- err = toStatusT(status);
- }
- );
+ );
+ } else {
+ hResult = mPluginV1_1->openSession_1_1(hSecurityLevel,
+ [&](Status status, const hidl_vec<uint8_t>& id) {
+ if (status == Status::OK) {
+ sessionId = toVector(id);
+ }
+ err = toStatusT(status);
+ }
+ );
+ }
if (!hResult.isOk()) {
err = DEAD_OBJECT;
@@ -982,42 +1027,6 @@
return hResult.isOk() ? err : DEAD_OBJECT;
}
-status_t DrmHal::setSecurityLevel(Vector<uint8_t> const &sessionId,
- const DrmPlugin::SecurityLevel& level) {
- Mutex::Autolock autoLock(mLock);
- INIT_CHECK();
-
- if (mPluginV1_1 == NULL) {
- return ERROR_DRM_CANNOT_HANDLE;
- }
-
- SecurityLevel hSecurityLevel;
-
- switch(level) {
- case DrmPlugin::kSecurityLevelSwSecureCrypto:
- hSecurityLevel = SecurityLevel::SW_SECURE_CRYPTO;
- break;
- case DrmPlugin::kSecurityLevelSwSecureDecode:
- hSecurityLevel = SecurityLevel::SW_SECURE_DECODE;
- break;
- case DrmPlugin::kSecurityLevelHwSecureCrypto:
- hSecurityLevel = SecurityLevel::HW_SECURE_CRYPTO;
- break;
- case DrmPlugin::kSecurityLevelHwSecureDecode:
- hSecurityLevel = SecurityLevel::HW_SECURE_DECODE;
- break;
- case DrmPlugin::kSecurityLevelHwSecureAll:
- hSecurityLevel = SecurityLevel::HW_SECURE_ALL;
- break;
- default:
- return ERROR_DRM_CANNOT_HANDLE;
- }
-
- Status status = mPluginV1_1->setSecurityLevel(toHidlVec(sessionId),
- hSecurityLevel);
- return toStatusT(status);
-}
-
status_t DrmHal::getPropertyString(String8 const &name, String8 &value ) const {
Mutex::Autolock autoLock(mLock);
return getPropertyStringInternal(name, value);
diff --git a/drm/libmediadrm/IDrm.cpp b/drm/libmediadrm/IDrm.cpp
index 7da52ea..22e4e6c 100644
--- a/drm/libmediadrm/IDrm.cpp
+++ b/drm/libmediadrm/IDrm.cpp
@@ -60,7 +60,6 @@
GET_HDCP_LEVELS,
GET_NUMBER_OF_SESSIONS,
GET_SECURITY_LEVEL,
- SET_SECURITY_LEVEL,
REMOVE_SECURE_STOP,
GET_SECURE_STOP_IDS
};
@@ -121,9 +120,11 @@
return reply.readInt32();
}
- virtual status_t openSession(Vector<uint8_t> &sessionId) {
+ virtual status_t openSession(DrmPlugin::SecurityLevel securityLevel,
+ Vector<uint8_t> &sessionId) {
Parcel data, reply;
data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+ data.writeInt32(securityLevel);
status_t status = remote()->transact(OPEN_SESSION, data, &reply);
if (status != OK) {
@@ -448,23 +449,6 @@
return reply.readInt32();
}
- virtual status_t setSecurityLevel(Vector<uint8_t> const &sessionId,
- const DrmPlugin::SecurityLevel& level) {
- Parcel data, reply;
-
- data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- writeVector(data, sessionId);
- data.writeInt32(static_cast<uint32_t>(level));
-
- status_t status = remote()->transact(SET_SECURITY_LEVEL, data, &reply);
- if (status != OK) {
- return status;
- }
-
- return reply.readInt32();
- }
-
virtual status_t getPropertyByteArray(String8 const &name, Vector<uint8_t> &value) const {
Parcel data, reply;
data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
@@ -759,8 +743,10 @@
case OPEN_SESSION:
{
CHECK_INTERFACE(IDrm, data, reply);
+ DrmPlugin::SecurityLevel level =
+ static_cast<DrmPlugin::SecurityLevel>(data.readInt32());
Vector<uint8_t> sessionId;
- status_t result = openSession(sessionId);
+ status_t result = openSession(level, sessionId);
writeVector(reply, sessionId);
reply->writeInt32(result);
return OK;
@@ -994,18 +980,6 @@
return OK;
}
- case SET_SECURITY_LEVEL:
- {
- CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId;
- readVector(data, sessionId);
- DrmPlugin::SecurityLevel level =
- static_cast<DrmPlugin::SecurityLevel>(data.readInt32());
- status_t result = setSecurityLevel(sessionId, level);
- reply->writeInt32(result);
- return OK;
- }
-
case GET_PROPERTY_STRING:
{
CHECK_INTERFACE(IDrm, data, reply);
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index ec75896..6b0201a 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -73,12 +73,24 @@
mByteArrayProperties[kMetricsKey] = valueVector;
}
+
Return<void> DrmPlugin::openSession(openSession_cb _hidl_cb) {
sp<Session> session = mSessionLibrary->createSession();
std::vector<uint8_t> sessionId = session->sessionId();
- setSecurityLevel(sessionId, SecurityLevel::SW_SECURE_CRYPTO);
- _hidl_cb(Status::OK, toHidlVec(sessionId));
+ Status status = setSecurityLevel(sessionId, SecurityLevel::SW_SECURE_CRYPTO);
+ _hidl_cb(status, toHidlVec(sessionId));
+ mOpenSessionOkCount++;
+ return Void();
+}
+
+Return<void> DrmPlugin::openSession_1_1(SecurityLevel securityLevel,
+ openSession_1_1_cb _hidl_cb) {
+ sp<Session> session = mSessionLibrary->createSession();
+ std::vector<uint8_t> sessionId = session->sessionId();
+
+ Status status = setSecurityLevel(sessionId, securityLevel);
+ _hidl_cb(status, toHidlVec(sessionId));
mOpenSessionOkCount++;
return Void();
}
@@ -98,6 +110,38 @@
return Status::ERROR_DRM_SESSION_NOT_OPENED;
}
+Status DrmPlugin::getKeyRequestCommon(const hidl_vec<uint8_t>& scope,
+ const hidl_vec<uint8_t>& initData,
+ const hidl_string& mimeType,
+ KeyType keyType,
+ const hidl_vec<KeyValue>& optionalParameters,
+ std::vector<uint8_t> *request,
+ KeyRequestType *keyRequestType,
+ std::string *defaultUrl) {
+ UNUSED(optionalParameters);
+
+ *defaultUrl = "";
+ *keyRequestType = KeyRequestType::UNKNOWN;
+ *request = std::vector<uint8_t>();
+
+ if (scope.size() == 0) {
+ return Status::BAD_VALUE;
+ }
+
+ if (keyType != KeyType::STREAMING) {
+ return Status::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ sp<Session> session = mSessionLibrary->findSession(toVector(scope));
+ if (!session.get()) {
+ return Status::ERROR_DRM_SESSION_NOT_OPENED;
+ }
+
+ Status status = session->getKeyRequest(initData, mimeType, request);
+ *keyRequestType = KeyRequestType::INITIAL;
+ return status;
+}
+
Return<void> DrmPlugin::getKeyRequest(
const hidl_vec<uint8_t>& scope,
const hidl_vec<uint8_t>& initData,
@@ -107,29 +151,16 @@
getKeyRequest_cb _hidl_cb) {
UNUSED(optionalParameters);
- if (scope.size() == 0) {
- // Returns empty keyRequest, unknown keyType and empty defaultUrl
- _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>(),
- KeyRequestType::UNKNOWN, "");
- return Void();
- }
-
- if (keyType != KeyType::STREAMING) {
- _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>(),
- KeyRequestType::UNKNOWN, "");
- return Void();
- }
-
- sp<Session> session = mSessionLibrary->findSession(toVector(scope));
- if (!session.get()) {
- _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, hidl_vec<uint8_t>(),
- KeyRequestType::UNKNOWN, "");
- return Void();
- }
-
+ KeyRequestType keyRequestType = KeyRequestType::UNKNOWN;
+ std::string defaultUrl("");
std::vector<uint8_t> request;
- Status status = session->getKeyRequest(initData, mimeType, &request);
- _hidl_cb(status, toHidlVec(request), KeyRequestType::INITIAL, "");
+ Status status = getKeyRequestCommon(
+ scope, initData, mimeType, keyType, optionalParameters,
+ &request, &keyRequestType, &defaultUrl);
+
+ _hidl_cb(status, toHidlVec(request),
+ static_cast<drm::V1_0::KeyRequestType>(keyRequestType),
+ hidl_string(defaultUrl));
return Void();
}
@@ -140,23 +171,16 @@
KeyType keyType,
const hidl_vec<KeyValue>& optionalParameters,
getKeyRequest_1_1_cb _hidl_cb) {
- hidl_string defaultUrl;
- hidl_vec<uint8_t> request;
- ::android::hardware::drm::V1_1::KeyRequestType requestType =
- static_cast<::android::hardware::drm::V1_1::KeyRequestType>(KeyRequestType::UNKNOWN);
- Status status = Status::OK;
+ UNUSED(optionalParameters);
- defaultUrl.clear();
- getKeyRequest(scope, initData, mimeType, keyType, optionalParameters,
- [&](Status statusCode, const hidl_vec<uint8_t>& hResult,
- KeyRequestType hKeyRequestType,
- const hidl_string& hDefaultUrl) {
- defaultUrl = hDefaultUrl;
- request = hResult;
- requestType = static_cast<::android::hardware::drm::V1_1::KeyRequestType>(hKeyRequestType);
- status = statusCode;
- });
- _hidl_cb(status, request, requestType, defaultUrl);
+ KeyRequestType keyRequestType = KeyRequestType::UNKNOWN;
+ std::string defaultUrl("");
+ std::vector<uint8_t> request;
+ Status status = getKeyRequestCommon(
+ scope, initData, mimeType, keyType, optionalParameters,
+ &request, &keyRequestType, &defaultUrl);
+
+ _hidl_cb(status, toHidlVec(request), keyRequestType, hidl_string(defaultUrl));
return Void();
}
@@ -344,8 +368,8 @@
return Status::BAD_VALUE;
}
- if (level > SecurityLevel::HW_SECURE_ALL) {
- ALOGE("Cannot set invalid security level");
+ if (level > SecurityLevel::SW_SECURE_CRYPTO) {
+ ALOGE("Cannot set security level > max");
return Status::BAD_VALUE;
}
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
index 64c8262..19baf0b 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
@@ -31,7 +31,6 @@
using ::android::hardware::drm::V1_0::EventType;
using ::android::hardware::drm::V1_0::IDrmPluginListener;
-using ::android::hardware::drm::V1_0::KeyRequestType;
using ::android::hardware::drm::V1_0::KeyStatus;
using ::android::hardware::drm::V1_0::KeyType;
using ::android::hardware::drm::V1_0::KeyValue;
@@ -39,6 +38,8 @@
using ::android::hardware::drm::V1_0::SecureStopId;
using ::android::hardware::drm::V1_0::Status;
using ::android::hardware::drm::V1_1::DrmMetricGroup;
+using ::android::hardware::drm::V1_1::IDrmPlugin;
+using ::android::hardware::drm::V1_1::KeyRequestType;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
@@ -46,13 +47,14 @@
using ::android::hardware::Void;
using ::android::sp;
-
struct DrmPlugin : public IDrmPlugin {
explicit DrmPlugin(SessionLibrary* sessionLibrary);
virtual ~DrmPlugin() {}
Return<void> openSession(openSession_cb _hidl_cb) override;
+ Return<void> openSession_1_1(SecurityLevel securityLevel,
+ openSession_cb _hidl_cb) override;
Return<Status> closeSession(const hidl_vec<uint8_t>& sessionId) override;
@@ -162,9 +164,6 @@
Return<void> getSecurityLevel(const hidl_vec<uint8_t>& sessionId,
getSecurityLevel_cb _hidl_cb) override;
- Return<Status> setSecurityLevel(const hidl_vec<uint8_t>& sessionId,
- SecurityLevel level) override;
-
Return<void> getMetrics(getMetrics_cb _hidl_cb) override;
Return<void> getPropertyString(
@@ -333,6 +332,18 @@
void initProperties();
void setPlayPolicy();
+ Return<Status> setSecurityLevel(const hidl_vec<uint8_t>& sessionId,
+ SecurityLevel level);
+
+ Status getKeyRequestCommon(const hidl_vec<uint8_t>& scope,
+ const hidl_vec<uint8_t>& initData,
+ const hidl_string& mimeType,
+ KeyType keyType,
+ const hidl_vec<KeyValue>& optionalParameters,
+ std::vector<uint8_t> *request,
+ KeyRequestType *getKeyRequestType,
+ std::string *defaultUrl);
+
std::vector<KeyValue> mPlayPolicy;
std::map<std::string, std::string> mStringProperties;
std::map<std::string, std::vector<uint8_t> > mByteArrayProperties;
diff --git a/media/extractors/aac/AACExtractor.cpp b/media/extractors/aac/AACExtractor.cpp
index 1614ca4..9df0aaa 100644
--- a/media/extractors/aac/AACExtractor.cpp
+++ b/media/extractors/aac/AACExtractor.cpp
@@ -333,13 +333,20 @@
static MediaExtractor* CreateExtractor(
DataSourceBase *source,
- const sp<AMessage>& meta) {
- return new AACExtractor(source, meta);
+ void *meta) {
+ sp<AMessage> metaData = static_cast<AMessage *>(meta);
+ return new AACExtractor(source, metaData);
+}
+
+static void FreeMeta(void *meta) {
+ if (meta != nullptr) {
+ static_cast<AMessage *>(meta)->decStrong(nullptr);
+ }
}
static MediaExtractor::CreatorFunc Sniff(
- DataSourceBase *source, String8 *mimeType, float *confidence,
- sp<AMessage> *meta) {
+ DataSourceBase *source, float *confidence, void **meta,
+ MediaExtractor::FreeMetaFunc *freeMeta) {
off64_t pos = 0;
for (;;) {
@@ -377,11 +384,14 @@
// ADTS syncword
if ((header[0] == 0xff) && ((header[1] & 0xf6) == 0xf0)) {
- *mimeType = MEDIA_MIMETYPE_AUDIO_AAC_ADTS;
*confidence = 0.2;
- *meta = new AMessage;
- (*meta)->setInt64("offset", pos);
+ AMessage *msg = new AMessage;
+ msg->setInt64("offset", pos);
+ *meta = msg;
+ *freeMeta = &FreeMeta;
+ // ref count will be decreased in FreeMeta.
+ msg->incStrong(nullptr);
return CreateExtractor;
}
diff --git a/media/extractors/amr/AMRExtractor.cpp b/media/extractors/amr/AMRExtractor.cpp
index 547e3f5..eec18b5 100644
--- a/media/extractors/amr/AMRExtractor.cpp
+++ b/media/extractors/amr/AMRExtractor.cpp
@@ -122,7 +122,7 @@
mOffsetTableLength(0) {
String8 mimeType;
float confidence;
- if (!SniffAMR(mDataSource, &mimeType, &confidence, NULL)) {
+ if (!SniffAMR(mDataSource, &mimeType, &confidence)) {
return;
}
@@ -339,8 +339,7 @@
////////////////////////////////////////////////////////////////////////////////
bool SniffAMR(
- DataSourceBase *source, String8 *mimeType, float *confidence,
- sp<AMessage> *) {
+ DataSourceBase *source, String8 *mimeType, float *confidence) {
char header[9];
if (source->readAt(0, header, sizeof(header)) != sizeof(header)) {
@@ -348,12 +347,16 @@
}
if (!memcmp(header, "#!AMR\n", 6)) {
- *mimeType = MEDIA_MIMETYPE_AUDIO_AMR_NB;
+ if (mimeType != nullptr) {
+ *mimeType = MEDIA_MIMETYPE_AUDIO_AMR_NB;
+ }
*confidence = 0.5;
return true;
} else if (!memcmp(header, "#!AMR-WB\n", 9)) {
- *mimeType = MEDIA_MIMETYPE_AUDIO_AMR_WB;
+ if (mimeType != nullptr) {
+ *mimeType = MEDIA_MIMETYPE_AUDIO_AMR_WB;
+ }
*confidence = 0.5;
return true;
@@ -373,13 +376,13 @@
"AMR Extractor",
[](
DataSourceBase *source,
- String8 *mimeType,
float *confidence,
- sp<AMessage> *meta __unused) -> MediaExtractor::CreatorFunc {
- if (SniffAMR(source, mimeType, confidence, meta)) {
+ void **,
+ MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc {
+ if (SniffAMR(source, nullptr, confidence)) {
return [](
DataSourceBase *source,
- const sp<AMessage>& meta __unused) -> MediaExtractor* {
+ void *) -> MediaExtractor* {
return new AMRExtractor(source);};
}
return NULL;
diff --git a/media/extractors/amr/AMRExtractor.h b/media/extractors/amr/AMRExtractor.h
index d6d49f2..b8b44ea 100644
--- a/media/extractors/amr/AMRExtractor.h
+++ b/media/extractors/amr/AMRExtractor.h
@@ -55,8 +55,7 @@
};
bool SniffAMR(
- DataSourceBase *source, String8 *mimeType, float *confidence,
- sp<AMessage> *);
+ DataSourceBase *source, String8 *mimeType, float *confidence);
} // namespace android
diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp
index 8dbb5a1..2ce20db 100644
--- a/media/extractors/flac/FLACExtractor.cpp
+++ b/media/extractors/flac/FLACExtractor.cpp
@@ -968,9 +968,7 @@
// Sniffer
-bool SniffFLAC(
- DataSourceBase *source, String8 *mimeType, float *confidence,
- sp<AMessage> *)
+bool SniffFLAC(DataSourceBase *source, float *confidence)
{
// first 4 is the signature word
// second 4 is the sizeof STREAMINFO
@@ -983,7 +981,6 @@
return false;
}
- *mimeType = MEDIA_MIMETYPE_AUDIO_FLAC;
*confidence = 0.5;
return true;
@@ -1001,13 +998,13 @@
"FLAC Extractor",
[](
DataSourceBase *source,
- String8 *mimeType,
float *confidence,
- sp<AMessage> *meta __unused) -> MediaExtractor::CreatorFunc {
- if (SniffFLAC(source, mimeType, confidence, meta)) {
+ void **,
+ MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc {
+ if (SniffFLAC(source, confidence)) {
return [](
DataSourceBase *source,
- const sp<AMessage>& meta __unused) -> MediaExtractor* {
+ void *) -> MediaExtractor* {
return new FLACExtractor(source);};
}
return NULL;
diff --git a/media/extractors/flac/FLACExtractor.h b/media/extractors/flac/FLACExtractor.h
index ef07212..f41d878 100644
--- a/media/extractors/flac/FLACExtractor.h
+++ b/media/extractors/flac/FLACExtractor.h
@@ -56,8 +56,7 @@
};
-bool SniffFLAC(DataSourceBase *source, String8 *mimeType,
- float *confidence, sp<AMessage> *);
+bool SniffFLAC(DataSourceBase *source, float *confidence);
} // namespace android
diff --git a/media/extractors/midi/MidiExtractor.cpp b/media/extractors/midi/MidiExtractor.cpp
index 711c6a5..1e38194 100644
--- a/media/extractors/midi/MidiExtractor.cpp
+++ b/media/extractors/midi/MidiExtractor.cpp
@@ -307,13 +307,10 @@
// Sniffer
-bool SniffMidi(
- DataSourceBase *source, String8 *mimeType, float *confidence,
- sp<AMessage> *)
+bool SniffMidi(DataSourceBase *source, float *confidence)
{
sp<MidiEngine> p = new MidiEngine(source, NULL, NULL);
if (p->initCheck() == OK) {
- *mimeType = MEDIA_MIMETYPE_AUDIO_MIDI;
*confidence = 0.8;
ALOGV("SniffMidi: yes");
return true;
@@ -334,13 +331,13 @@
"MIDI Extractor",
[](
DataSourceBase *source,
- String8 *mimeType,
float *confidence,
- sp<AMessage> *meta __unused) -> MediaExtractor::CreatorFunc {
- if (SniffMidi(source, mimeType, confidence, meta)) {
+ void **,
+ MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc {
+ if (SniffMidi(source, confidence)) {
return [](
DataSourceBase *source,
- const sp<AMessage>& meta __unused) -> MediaExtractor* {
+ void *) -> MediaExtractor* {
return new MidiExtractor(source);};
}
return NULL;
diff --git a/media/extractors/midi/MidiExtractor.h b/media/extractors/midi/MidiExtractor.h
index 91efd06..173a814 100644
--- a/media/extractors/midi/MidiExtractor.h
+++ b/media/extractors/midi/MidiExtractor.h
@@ -87,8 +87,7 @@
};
-bool SniffMidi(DataSourceBase *source, String8 *mimeType,
- float *confidence, sp<AMessage> *);
+bool SniffMidi(DataSourceBase *source, float *confidence);
} // namespace android
diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp
index f61f7c7..7396113 100644
--- a/media/extractors/mkv/MatroskaExtractor.cpp
+++ b/media/extractors/mkv/MatroskaExtractor.cpp
@@ -1548,8 +1548,7 @@
}
bool SniffMatroska(
- DataSourceBase *source, String8 *mimeType, float *confidence,
- sp<AMessage> *) {
+ DataSourceBase *source, float *confidence) {
DataSourceBaseReader reader(source);
mkvparser::EBMLHeader ebmlHeader;
long long pos;
@@ -1557,7 +1556,6 @@
return false;
}
- mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MATROSKA);
*confidence = 0.6;
return true;
@@ -1575,13 +1573,13 @@
"Matroska Extractor",
[](
DataSourceBase *source,
- String8 *mimeType,
float *confidence,
- sp<AMessage> *meta __unused) -> MediaExtractor::CreatorFunc {
- if (SniffMatroska(source, mimeType, confidence, meta)) {
+ void **,
+ MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc {
+ if (SniffMatroska(source, confidence)) {
return [](
DataSourceBase *source,
- const sp<AMessage>& meta __unused) -> MediaExtractor* {
+ void *) -> MediaExtractor* {
return new MatroskaExtractor(source);};
}
return NULL;
diff --git a/media/extractors/mp3/MP3Extractor.cpp b/media/extractors/mp3/MP3Extractor.cpp
index 25d4deb..e55084e 100644
--- a/media/extractors/mp3/MP3Extractor.cpp
+++ b/media/extractors/mp3/MP3Extractor.cpp
@@ -668,13 +668,20 @@
static MediaExtractor* CreateExtractor(
DataSourceBase *source,
- const sp<AMessage>& meta) {
- return new MP3Extractor(source, meta);
+ void *meta) {
+ sp<AMessage> metaData = static_cast<AMessage *>(meta);
+ return new MP3Extractor(source, metaData);
+}
+
+static void FreeMeta(void *meta) {
+ if (meta != nullptr) {
+ static_cast<AMessage *>(meta)->decStrong(nullptr);
+ }
}
static MediaExtractor::CreatorFunc Sniff(
- DataSourceBase *source, String8 *mimeType,
- float *confidence, sp<AMessage> *meta) {
+ DataSourceBase *source, float *confidence, void **meta,
+ MediaExtractor::FreeMetaFunc *freeMeta) {
off64_t pos = 0;
off64_t post_id3_pos;
uint32_t header;
@@ -691,12 +698,15 @@
return NULL;
}
- *meta = new AMessage;
- (*meta)->setInt64("offset", pos);
- (*meta)->setInt32("header", header);
- (*meta)->setInt64("post-id3-offset", post_id3_pos);
+ AMessage *msg = new AMessage;
+ msg->setInt64("offset", pos);
+ msg->setInt32("header", header);
+ msg->setInt64("post-id3-offset", post_id3_pos);
+ *meta = msg;
+ *freeMeta = &FreeMeta;
+ // ref count will be decreased in FreeMeta.
+ msg->incStrong(nullptr);
- *mimeType = MEDIA_MIMETYPE_AUDIO_MPEG;
*confidence = 0.2f;
return CreateExtractor;
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 30dda13..3d7e45c 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -5382,8 +5382,7 @@
return NULL;
}
-static bool LegacySniffMPEG4(
- DataSourceBase *source, String8 *mimeType, float *confidence) {
+static bool LegacySniffMPEG4(DataSourceBase *source, float *confidence) {
uint8_t header[8];
ssize_t n = source->readAt(4, header, sizeof(header));
@@ -5399,7 +5398,6 @@
|| !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8)
|| !memcmp(header, "ftypmif1", 8) || !memcmp(header, "ftypheic", 8)
|| !memcmp(header, "ftypmsf1", 8) || !memcmp(header, "ftyphevc", 8)) {
- *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
*confidence = 0.4;
return true;
@@ -5449,9 +5447,7 @@
// Also try to identify where this file's metadata ends
// (end of the 'moov' atom) and report it to the caller as part of
// the metadata.
-static bool BetterSniffMPEG4(
- DataSourceBase *source, String8 *mimeType, float *confidence,
- sp<AMessage> *meta) {
+static bool BetterSniffMPEG4(DataSourceBase *source, float *confidence) {
// We scan up to 128 bytes to identify this file as an MP4.
static const off64_t kMaxScanOffset = 128ll;
@@ -5553,35 +5549,23 @@
return false;
}
- *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
*confidence = 0.4f;
- if (moovAtomEndOffset >= 0) {
- *meta = new AMessage;
- (*meta)->setInt64("meta-data-size", moovAtomEndOffset);
-
- ALOGV("found metadata size: %lld", (long long)moovAtomEndOffset);
- }
-
return true;
}
-static MediaExtractor* CreateExtractor(
- DataSourceBase *source,
- const sp<AMessage>& meta __unused) {
+static MediaExtractor* CreateExtractor(DataSourceBase *source, void *) {
return new MPEG4Extractor(source);
}
static MediaExtractor::CreatorFunc Sniff(
- DataSourceBase *source,
- String8 *mimeType,
- float *confidence,
- sp<AMessage> *meta) {
- if (BetterSniffMPEG4(source, mimeType, confidence, meta)) {
+ DataSourceBase *source, float *confidence, void **,
+ MediaExtractor::FreeMetaFunc *) {
+ if (BetterSniffMPEG4(source, confidence)) {
return CreateExtractor;
}
- if (LegacySniffMPEG4(source, mimeType, confidence)) {
+ if (LegacySniffMPEG4(source, confidence)) {
ALOGW("Identified supported mpeg4 through LegacySniffMPEG4.");
return CreateExtractor;
}
diff --git a/media/extractors/mpeg2/ExtractorBundle.cpp b/media/extractors/mpeg2/ExtractorBundle.cpp
index 443d685..8a0fa03 100644
--- a/media/extractors/mpeg2/ExtractorBundle.cpp
+++ b/media/extractors/mpeg2/ExtractorBundle.cpp
@@ -35,18 +35,18 @@
"MPEG2-PS/TS Extractor",
[](
DataSourceBase *source,
- String8 *mimeType,
float *confidence,
- sp<AMessage> *meta __unused) -> MediaExtractor::CreatorFunc {
- if (SniffMPEG2TS(source, mimeType, confidence, meta)) {
+ void **,
+ MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc {
+ if (SniffMPEG2TS(source, confidence)) {
return [](
DataSourceBase *source,
- const sp<AMessage>& meta __unused) -> MediaExtractor* {
+ void *) -> MediaExtractor* {
return new MPEG2TSExtractor(source);};
- } else if (SniffMPEG2PS(source, mimeType, confidence, meta)) {
+ } else if (SniffMPEG2PS(source, confidence)) {
return [](
DataSourceBase *source,
- const sp<AMessage>& meta __unused) -> MediaExtractor* {
+ void *) -> MediaExtractor* {
return new MPEG2PSExtractor(source);};
}
return NULL;
diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.cpp b/media/extractors/mpeg2/MPEG2PSExtractor.cpp
index 697e44f..b4d0ee5 100644
--- a/media/extractors/mpeg2/MPEG2PSExtractor.cpp
+++ b/media/extractors/mpeg2/MPEG2PSExtractor.cpp
@@ -751,8 +751,7 @@
////////////////////////////////////////////////////////////////////////////////
bool SniffMPEG2PS(
- DataSourceBase *source, String8 *mimeType, float *confidence,
- sp<AMessage> *) {
+ DataSourceBase *source, float *confidence) {
uint8_t header[5];
if (source->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) {
return false;
@@ -764,8 +763,6 @@
*confidence = 0.25f; // Slightly larger than .mp3 extractor's confidence
- mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MPEG2PS);
-
return true;
}
diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.h b/media/extractors/mpeg2/MPEG2PSExtractor.h
index adf719a..2541f4d 100644
--- a/media/extractors/mpeg2/MPEG2PSExtractor.h
+++ b/media/extractors/mpeg2/MPEG2PSExtractor.h
@@ -71,9 +71,7 @@
DISALLOW_EVIL_CONSTRUCTORS(MPEG2PSExtractor);
};
-bool SniffMPEG2PS(
- DataSourceBase *source, String8 *mimeType, float *confidence,
- sp<AMessage> *);
+bool SniffMPEG2PS(DataSourceBase *source, float *confidence);
} // namespace android
diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.cpp b/media/extractors/mpeg2/MPEG2TSExtractor.cpp
index a8a366b..3183064 100644
--- a/media/extractors/mpeg2/MPEG2TSExtractor.cpp
+++ b/media/extractors/mpeg2/MPEG2TSExtractor.cpp
@@ -645,9 +645,7 @@
////////////////////////////////////////////////////////////////////////////////
-bool SniffMPEG2TS(
- DataSourceBase *source, String8 *mimeType, float *confidence,
- sp<AMessage> *) {
+bool SniffMPEG2TS(DataSourceBase *source, float *confidence) {
for (int i = 0; i < 5; ++i) {
char header;
if (source->readAt(kTSPacketSize * i, &header, 1) != 1
@@ -657,7 +655,6 @@
}
*confidence = 0.1f;
- mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
return true;
}
diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.h b/media/extractors/mpeg2/MPEG2TSExtractor.h
index fc15501..df07fac 100644
--- a/media/extractors/mpeg2/MPEG2TSExtractor.h
+++ b/media/extractors/mpeg2/MPEG2TSExtractor.h
@@ -99,9 +99,7 @@
DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSExtractor);
};
-bool SniffMPEG2TS(
- DataSourceBase *source, String8 *mimeType, float *confidence,
- sp<AMessage> *);
+bool SniffMPEG2TS(DataSourceBase *source, float *confidence);
} // namespace android
diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp
index 1d04bed..ab51e5e 100644
--- a/media/extractors/ogg/OggExtractor.cpp
+++ b/media/extractors/ogg/OggExtractor.cpp
@@ -1371,21 +1371,20 @@
static MediaExtractor* CreateExtractor(
DataSourceBase *source,
- const sp<AMessage>& meta __unused) {
+ void *) {
return new OggExtractor(source);
}
static MediaExtractor::CreatorFunc Sniff(
DataSourceBase *source,
- String8 *mimeType,
float *confidence,
- sp<AMessage> *) {
+ void **,
+ MediaExtractor::FreeMetaFunc *) {
char tmp[4];
if (source->readAt(0, tmp, 4) < 4 || memcmp(tmp, "OggS", 4)) {
return NULL;
}
- mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_OGG);
*confidence = 0.2f;
return CreateExtractor;
diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp
index 105a37f..2c991a7 100644
--- a/media/extractors/wav/WAVExtractor.cpp
+++ b/media/extractors/wav/WAVExtractor.cpp
@@ -546,15 +546,15 @@
static MediaExtractor* CreateExtractor(
DataSourceBase *source,
- const sp<AMessage>& meta __unused) {
+ void *) {
return new WAVExtractor(source);
}
static MediaExtractor::CreatorFunc Sniff(
DataSourceBase *source,
- String8 *mimeType,
float *confidence,
- sp<AMessage> *) {
+ void **,
+ MediaExtractor::FreeMetaFunc *) {
char header[12];
if (source->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) {
return NULL;
@@ -571,7 +571,6 @@
return NULL;
}
- *mimeType = MEDIA_MIMETYPE_CONTAINER_WAV;
*confidence = 0.3f;
return CreateExtractor;
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 9dfb514..1c4a80e 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -904,6 +904,7 @@
audio_session_t session,
pid_t pid,
uid_t uid,
+ const String16& opPackageName,
const audio_config_base_t *config,
audio_input_flags_t flags,
audio_port_handle_t *selectedDeviceId,
@@ -912,35 +913,29 @@
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return NO_INIT;
return aps->getInputForAttr(
- attr, input, session, pid, uid,
+ attr, input, session, pid, uid, opPackageName,
config, flags, selectedDeviceId, portId);
}
-status_t AudioSystem::startInput(audio_io_handle_t input,
- audio_session_t session,
- audio_devices_t device,
- uid_t uid,
- bool *silenced)
+status_t AudioSystem::startInput(audio_port_handle_t portId, bool *silenced)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->startInput(input, session, device, uid, silenced);
+ return aps->startInput(portId, silenced);
}
-status_t AudioSystem::stopInput(audio_io_handle_t input,
- audio_session_t session)
+status_t AudioSystem::stopInput(audio_port_handle_t portId)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->stopInput(input, session);
+ return aps->stopInput(portId);
}
-void AudioSystem::releaseInput(audio_io_handle_t input,
- audio_session_t session)
+void AudioSystem::releaseInput(audio_port_handle_t portId)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return;
- aps->releaseInput(input, session);
+ aps->releaseInput(portId);
}
status_t AudioSystem::initStreamVolume(audio_stream_type_t stream,
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index a24a099..8f5ff30 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -285,6 +285,7 @@
audio_session_t session,
pid_t pid,
uid_t uid,
+ const String16& opPackageName,
const audio_config_base_t *config,
audio_input_flags_t flags,
audio_port_handle_t *selectedDeviceId,
@@ -313,6 +314,7 @@
data.writeInt32(session);
data.writeInt32(pid);
data.writeInt32(uid);
+ data.writeString16(opPackageName);
data.write(config, sizeof(audio_config_base_t));
data.writeInt32(flags);
data.writeInt32(*selectedDeviceId);
@@ -331,18 +333,12 @@
return NO_ERROR;
}
- virtual status_t startInput(audio_io_handle_t input,
- audio_session_t session,
- audio_devices_t device,
- uid_t uid,
+ virtual status_t startInput(audio_port_handle_t portId,
bool *silenced)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(input);
- data.writeInt32(session);
- data.writeInt32(device);
- data.writeInt32(uid);
+ data.writeInt32(portId);
data.writeInt32(*silenced ? 1 : 0);
remote()->transact(START_INPUT, data, &reply);
status_t status = static_cast <status_t> (reply.readInt32());
@@ -350,24 +346,20 @@
return status;
}
- virtual status_t stopInput(audio_io_handle_t input,
- audio_session_t session)
+ virtual status_t stopInput(audio_port_handle_t portId)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(input);
- data.writeInt32(session);
+ data.writeInt32(portId);
remote()->transact(STOP_INPUT, data, &reply);
return static_cast <status_t> (reply.readInt32());
}
- virtual void releaseInput(audio_io_handle_t input,
- audio_session_t session)
+ virtual void releaseInput(audio_port_handle_t portId)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(input);
- data.writeInt32(session);
+ data.writeInt32(portId);
remote()->transact(RELEASE_INPUT, data, &reply);
}
@@ -1055,6 +1047,7 @@
audio_session_t session = (audio_session_t)data.readInt32();
pid_t pid = (pid_t)data.readInt32();
uid_t uid = (uid_t)data.readInt32();
+ const String16 opPackageName = data.readString16();
audio_config_base_t config;
memset(&config, 0, sizeof(audio_config_base_t));
data.read(&config, sizeof(audio_config_base_t));
@@ -1062,7 +1055,7 @@
audio_port_handle_t selectedDeviceId = (audio_port_handle_t) data.readInt32();
audio_port_handle_t portId = (audio_port_handle_t)data.readInt32();
status_t status = getInputForAttr(&attr, &input, session, pid, uid,
- &config,
+ opPackageName, &config,
flags, &selectedDeviceId, &portId);
reply->writeInt32(status);
if (status == NO_ERROR) {
@@ -1075,12 +1068,9 @@
case START_INPUT: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_io_handle_t input = static_cast <audio_io_handle_t>(data.readInt32());
- audio_session_t session = static_cast <audio_session_t>(data.readInt32());
- audio_devices_t device = static_cast <audio_devices_t>(data.readInt32());
- uid_t uid = static_cast <uid_t>(data.readInt32());
+ audio_port_handle_t portId = static_cast <audio_port_handle_t>(data.readInt32());
bool silenced = data.readInt32() == 1;
- status_t status = startInput(input, session, device, uid, &silenced);
+ status_t status = startInput(portId, &silenced);
reply->writeInt32(static_cast <uint32_t>(status));
reply->writeInt32(silenced ? 1 : 0);
return NO_ERROR;
@@ -1088,17 +1078,15 @@
case STOP_INPUT: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_io_handle_t input = static_cast <audio_io_handle_t>(data.readInt32());
- audio_session_t session = static_cast <audio_session_t>(data.readInt32());
- reply->writeInt32(static_cast <uint32_t>(stopInput(input, session)));
+ audio_port_handle_t portId = static_cast <audio_port_handle_t>(data.readInt32());
+ reply->writeInt32(static_cast <uint32_t>(stopInput(portId)));
return NO_ERROR;
} break;
case RELEASE_INPUT: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_io_handle_t input = static_cast <audio_io_handle_t>(data.readInt32());
- audio_session_t session = static_cast <audio_session_t>(data.readInt32());
- releaseInput(input, session);
+ audio_port_handle_t portId = static_cast <audio_port_handle_t>(data.readInt32());
+ releaseInput(portId);
return NO_ERROR;
} break;
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 3c8e7bc..22b700d 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -241,20 +241,16 @@
audio_session_t session,
pid_t pid,
uid_t uid,
+ const String16& opPackageName,
const audio_config_base_t *config,
audio_input_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId);
- static status_t startInput(audio_io_handle_t input,
- audio_session_t session,
- audio_devices_t device,
- uid_t uid,
+ static status_t startInput(audio_port_handle_t portId,
bool *silenced);
- static status_t stopInput(audio_io_handle_t input,
- audio_session_t session);
- static void releaseInput(audio_io_handle_t input,
- audio_session_t session);
+ static status_t stopInput(audio_port_handle_t portId);
+ static void releaseInput(audio_port_handle_t portId);
static status_t initStreamVolume(audio_stream_type_t stream,
int indexMin,
int indexMax);
diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h
index 5338927..949d593 100644
--- a/media/libaudioclient/include/media/IAudioPolicyService.h
+++ b/media/libaudioclient/include/media/IAudioPolicyService.h
@@ -80,19 +80,15 @@
audio_session_t session,
pid_t pid,
uid_t uid,
+ const String16& opPackageName,
const audio_config_base_t *config,
audio_input_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId) = 0;
- virtual status_t startInput(audio_io_handle_t input,
- audio_session_t session,
- audio_devices_t device,
- uid_t uid,
+ virtual status_t startInput(audio_port_handle_t portId,
bool *silenced) = 0;
- virtual status_t stopInput(audio_io_handle_t input,
- audio_session_t session) = 0;
- virtual void releaseInput(audio_io_handle_t input,
- audio_session_t session) = 0;
+ virtual status_t stopInput(audio_port_handle_t portId) = 0;
+ virtual void releaseInput(audio_port_handle_t portId) = 0;
virtual status_t initStreamVolume(audio_stream_type_t stream,
int indexMin,
int indexMax) = 0;
diff --git a/media/libmedia/MidiIoWrapper.cpp b/media/libmedia/MidiIoWrapper.cpp
index 0896e75..5ca3b48 100644
--- a/media/libmedia/MidiIoWrapper.cpp
+++ b/media/libmedia/MidiIoWrapper.cpp
@@ -38,6 +38,7 @@
mFd = open(path, O_RDONLY | O_LARGEFILE);
mBase = 0;
mLength = lseek(mFd, 0, SEEK_END);
+ mDataSource = nullptr;
}
MidiIoWrapper::MidiIoWrapper(int fd, off64_t offset, int64_t size) {
@@ -45,6 +46,7 @@
mFd = fd < 0 ? -1 : dup(fd);
mBase = offset;
mLength = size;
+ mDataSource = nullptr;
}
MidiIoWrapper::MidiIoWrapper(DataSourceBase *source) {
diff --git a/media/libmedia/NdkWrapper.cpp b/media/libmedia/NdkWrapper.cpp
index 942393d..936e92f 100644
--- a/media/libmedia/NdkWrapper.cpp
+++ b/media/libmedia/NdkWrapper.cpp
@@ -1057,6 +1057,13 @@
return translateErrorCode(AMediaExtractor_setDataSource(mAMediaExtractor, location));
}
+status_t AMediaExtractorWrapper::setDataSource(AMediaDataSource *source) {
+ if (mAMediaExtractor == NULL) {
+ return DEAD_OBJECT;
+ }
+ return translateErrorCode(AMediaExtractor_setDataSourceCustom(mAMediaExtractor, source));
+}
+
size_t AMediaExtractorWrapper::getTrackCount() {
if (mAMediaExtractor == NULL) {
return 0;
@@ -1064,6 +1071,13 @@
return AMediaExtractor_getTrackCount(mAMediaExtractor);
}
+sp<AMediaFormatWrapper> AMediaExtractorWrapper::getFormat() {
+ if (mAMediaExtractor == NULL) {
+ return NULL;
+ }
+ return new AMediaFormatWrapper(AMediaExtractor_getFileFormat(mAMediaExtractor));
+}
+
sp<AMediaFormatWrapper> AMediaExtractorWrapper::getTrackFormat(size_t idx) {
if (mAMediaExtractor == NULL) {
return NULL;
@@ -1085,6 +1099,26 @@
return translateErrorCode(AMediaExtractor_unselectTrack(mAMediaExtractor, idx));
}
+status_t AMediaExtractorWrapper::selectSingleTrack(size_t idx) {
+ if (mAMediaExtractor == NULL) {
+ return DEAD_OBJECT;
+ }
+ for (size_t i = 0; i < AMediaExtractor_getTrackCount(mAMediaExtractor); ++i) {
+ if (i == idx) {
+ media_status_t err = AMediaExtractor_selectTrack(mAMediaExtractor, i);
+ if (err != AMEDIA_OK) {
+ return translateErrorCode(err);
+ }
+ } else {
+ media_status_t err = AMediaExtractor_unselectTrack(mAMediaExtractor, i);
+ if (err != AMEDIA_OK) {
+ return translateErrorCode(err);
+ }
+ }
+ }
+ return OK;
+}
+
ssize_t AMediaExtractorWrapper::readSampleData(const sp<ABuffer> &buffer) {
if (mAMediaExtractor == NULL) {
return -1;
@@ -1092,6 +1126,13 @@
return AMediaExtractor_readSampleData(mAMediaExtractor, buffer->data(), buffer->capacity());
}
+ssize_t AMediaExtractorWrapper::getSampleSize() {
+ if (mAMediaExtractor == NULL) {
+ return 0;
+ }
+ return AMediaExtractor_getSampleSize(mAMediaExtractor);
+}
+
uint32_t AMediaExtractorWrapper::getSampleFlags() {
if (mAMediaExtractor == NULL) {
return 0;
@@ -1113,6 +1154,13 @@
return AMediaExtractor_getSampleTime(mAMediaExtractor);
}
+int64_t AMediaExtractorWrapper::getCachedDuration() {
+ if (mAMediaExtractor == NULL) {
+ return -1;
+ }
+ return AMediaExtractor_getCachedDuration(mAMediaExtractor);
+}
+
bool AMediaExtractorWrapper::advance() {
if (mAMediaExtractor == NULL) {
return false;
@@ -1120,11 +1168,27 @@
return AMediaExtractor_advance(mAMediaExtractor);
}
-status_t AMediaExtractorWrapper::seekTo(int64_t seekPosUs, SeekMode mode) {
+status_t AMediaExtractorWrapper::seekTo(int64_t seekPosUs, MediaSource::ReadOptions::SeekMode mode) {
if (mAMediaExtractor == NULL) {
return DEAD_OBJECT;
}
- return AMediaExtractor_seekTo(mAMediaExtractor, seekPosUs, mode);
+
+ SeekMode aMode;
+ switch (mode) {
+ case MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC: {
+ aMode = AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC;
+ break;
+ }
+ case MediaSource::ReadOptions::SEEK_NEXT_SYNC: {
+ aMode = AMEDIAEXTRACTOR_SEEK_NEXT_SYNC;
+ break;
+ }
+ default: {
+ aMode = AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC;
+ break;
+ }
+ }
+ return AMediaExtractor_seekTo(mAMediaExtractor, seekPosUs, aMode);
}
PsshInfo* AMediaExtractorWrapper::getPsshInfo() {
@@ -1141,4 +1205,43 @@
return new AMediaCodecCryptoInfoWrapper(AMediaExtractor_getSampleCryptoInfo(mAMediaExtractor));
}
+ssize_t AMediaDataSourceWrapper::AMediaDataSourceWrapper_getSize(void *userdata) {
+ DataSource *source = static_cast<DataSource *>(userdata);
+ off64_t size = -1;
+ source->getSize(&size);
+ return size;
+}
+
+ssize_t AMediaDataSourceWrapper::AMediaDataSourceWrapper_readAt(void *userdata, off64_t offset, void * buf, size_t size) {
+ DataSource *source = static_cast<DataSource *>(userdata);
+ return source->readAt(offset, buf, size);
+}
+
+void AMediaDataSourceWrapper::AMediaDataSourceWrapper_close(void *userdata) {
+ DataSource *source = static_cast<DataSource *>(userdata);
+ source->close();
+}
+
+AMediaDataSourceWrapper::AMediaDataSourceWrapper(const sp<DataSource> &dataSource)
+ : mDataSource(dataSource),
+ mAMediaDataSource(AMediaDataSource_new()) {
+ ALOGV("setDataSource (source: %p)", dataSource.get());
+ AMediaDataSource_setUserdata(mAMediaDataSource, dataSource.get());
+ AMediaDataSource_setReadAt(mAMediaDataSource, AMediaDataSourceWrapper_readAt);
+ AMediaDataSource_setGetSize(mAMediaDataSource, AMediaDataSourceWrapper_getSize);
+ AMediaDataSource_setClose(mAMediaDataSource, AMediaDataSourceWrapper_close);
+}
+
+AMediaDataSourceWrapper::~AMediaDataSourceWrapper() {
+ if (mAMediaDataSource == NULL) {
+ return;
+ }
+ AMediaDataSource_delete(mAMediaDataSource);
+ mAMediaDataSource = NULL;
+}
+
+AMediaDataSource* AMediaDataSourceWrapper::getAMediaDataSource() {
+ return mAMediaDataSource;
+}
+
} // namespace android
diff --git a/media/libmedia/include/media/CryptoHal.h b/media/libmedia/include/media/CryptoHal.h
index a5d8b43..ed16f44 100644
--- a/media/libmedia/include/media/CryptoHal.h
+++ b/media/libmedia/include/media/CryptoHal.h
@@ -20,14 +20,16 @@
#include <android/hardware/drm/1.0/ICryptoFactory.h>
#include <android/hardware/drm/1.0/ICryptoPlugin.h>
+#include <android/hardware/drm/1.1/ICryptoFactory.h>
#include <media/ICrypto.h>
#include <utils/KeyedVector.h>
#include <utils/threads.h>
-using ::android::hardware::drm::V1_0::ICryptoFactory;
-using ::android::hardware::drm::V1_0::ICryptoPlugin;
-using ::android::hardware::drm::V1_0::SharedBuffer;
+namespace drm = ::android::hardware::drm;
+using drm::V1_0::ICryptoFactory;
+using drm::V1_0::ICryptoPlugin;
+using drm::V1_0::SharedBuffer;
class IMemoryHeap;
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index 7a04458..67659b2 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -18,10 +18,11 @@
#define DRM_HAL_H_
-#include <android/hardware/drm/1.0/IDrmPlugin.h>
-#include <android/hardware/drm/1.1/IDrmPlugin.h>
-#include <android/hardware/drm/1.0/IDrmPluginListener.h>
#include <android/hardware/drm/1.0/IDrmFactory.h>
+#include <android/hardware/drm/1.0/IDrmPlugin.h>
+#include <android/hardware/drm/1.0/IDrmPluginListener.h>
+#include <android/hardware/drm/1.1/IDrmFactory.h>
+#include <android/hardware/drm/1.1/IDrmPlugin.h>
#include <media/DrmMetrics.h>
#include <media/IDrm.h>
@@ -63,7 +64,8 @@
virtual status_t destroyPlugin();
- virtual status_t openSession(Vector<uint8_t> &sessionId);
+ virtual status_t openSession(DrmPlugin::SecurityLevel level,
+ Vector<uint8_t> &sessionId);
virtual status_t closeSession(Vector<uint8_t> const &sessionId);
@@ -110,8 +112,6 @@
uint32_t *maxSessions) const;
virtual status_t getSecurityLevel(Vector<uint8_t> const &sessionId,
DrmPlugin::SecurityLevel *level) const;
- virtual status_t setSecurityLevel(Vector<uint8_t> const &sessionId,
- const DrmPlugin::SecurityLevel& level);
virtual status_t getPropertyString(String8 const &name, String8 &value ) const;
virtual status_t getPropertyByteArray(String8 const &name,
diff --git a/media/libmedia/include/media/IDrm.h b/media/libmedia/include/media/IDrm.h
index c113240..a19b06b 100644
--- a/media/libmedia/include/media/IDrm.h
+++ b/media/libmedia/include/media/IDrm.h
@@ -41,7 +41,8 @@
virtual status_t destroyPlugin() = 0;
- virtual status_t openSession(Vector<uint8_t> &sessionId) = 0;
+ virtual status_t openSession(DrmPlugin::SecurityLevel securityLevel,
+ Vector<uint8_t> &sessionId) = 0;
virtual status_t closeSession(Vector<uint8_t> const &sessionId) = 0;
@@ -89,8 +90,6 @@
uint32_t *maxSessions) const = 0;
virtual status_t getSecurityLevel(Vector<uint8_t> const &sessionId,
DrmPlugin::SecurityLevel *level) const = 0;
- virtual status_t setSecurityLevel(Vector<uint8_t> const &sessionId,
- const DrmPlugin::SecurityLevel& level) = 0;
virtual status_t getPropertyString(String8 const &name, String8 &value) const = 0;
virtual status_t getPropertyByteArray(String8 const &name,
diff --git a/media/libmedia/include/media/NdkWrapper.h b/media/libmedia/include/media/NdkWrapper.h
index 00e0fd4..49d728d 100644
--- a/media/libmedia/include/media/NdkWrapper.h
+++ b/media/libmedia/include/media/NdkWrapper.h
@@ -18,6 +18,9 @@
#define NDK_WRAPPER_H_
+#include <media/DataSource.h>
+#include <media/MediaSource.h>
+#include <media/NdkMediaDataSource.h>
#include <media/NdkMediaError.h>
#include <media/NdkMediaExtractor.h>
#include <media/hardware/CryptoAPI.h>
@@ -286,25 +289,35 @@
status_t setDataSource(const char *location);
+ status_t setDataSource(AMediaDataSource *);
+
size_t getTrackCount();
+ sp<AMediaFormatWrapper> getFormat();
+
sp<AMediaFormatWrapper> getTrackFormat(size_t idx);
status_t selectTrack(size_t idx);
status_t unselectTrack(size_t idx);
+ status_t selectSingleTrack(size_t idx);
+
ssize_t readSampleData(const sp<ABuffer> &buffer);
+ ssize_t getSampleSize();
+
uint32_t getSampleFlags();
int getSampleTrackIndex();
int64_t getSampleTime();
+ int64_t getCachedDuration();
+
bool advance();
- status_t seekTo(int64_t seekPosUs, SeekMode mode);
+ status_t seekTo(int64_t seekPosUs, MediaSource::ReadOptions::SeekMode mode);
// the returned PsshInfo is still owned by this wrapper.
PsshInfo* getPsshInfo();
@@ -320,6 +333,31 @@
DISALLOW_EVIL_CONSTRUCTORS(AMediaExtractorWrapper);
};
+struct AMediaDataSourceWrapper : public RefBase {
+
+ static status_t translate_error(media_status_t err);
+
+ static ssize_t AMediaDataSourceWrapper_getSize(void *userdata);
+
+ static ssize_t AMediaDataSourceWrapper_readAt(void *userdata, off64_t offset, void * buf, size_t size);
+
+ static void AMediaDataSourceWrapper_close(void *userdata);
+
+ AMediaDataSourceWrapper(const sp<DataSource> &dataSource);
+
+ AMediaDataSource *getAMediaDataSource();
+
+protected:
+ virtual ~AMediaDataSourceWrapper();
+
+private:
+ sp<DataSource> mDataSource;
+
+ AMediaDataSource *mAMediaDataSource;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AMediaDataSourceWrapper);
+};
+
} // namespace android
#endif // NDK_WRAPPER_H_
diff --git a/media/libmedia/include/media/OMXBuffer.h b/media/libmedia/include/media/OMXBuffer.h
index 3e84858..9c9f5e7 100644
--- a/media/libmedia/include/media/OMXBuffer.h
+++ b/media/libmedia/include/media/OMXBuffer.h
@@ -91,6 +91,7 @@
private:
friend struct OMXNodeInstance;
+ friend struct C2OMXNode;
// This is needed temporarily for OMX HIDL transition.
friend inline bool (::android::hardware::media::omx::V1_0::implementation::
diff --git a/media/libmediaextractor/MediaBufferGroup.cpp b/media/libmediaextractor/MediaBufferGroup.cpp
index cb62d92..22f01a5 100644
--- a/media/libmediaextractor/MediaBufferGroup.cpp
+++ b/media/libmediaextractor/MediaBufferGroup.cpp
@@ -17,9 +17,13 @@
#define LOG_TAG "MediaBufferGroup"
#include <utils/Log.h>
+#include <list>
+
+#include <binder/MemoryDealer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
+#include <utils/threads.h>
namespace android {
@@ -32,17 +36,26 @@
static const size_t kSharedMemoryThreshold = MIN(
(size_t)MediaBuffer::kSharedMemThreshold, (size_t)(4 * 1024));
-MediaBufferGroup::MediaBufferGroup(size_t growthLimit) :
- mGrowthLimit(growthLimit) {
+struct MediaBufferGroup::InternalData {
+ Mutex mLock;
+ Condition mCondition;
+ size_t mGrowthLimit; // Do not automatically grow group larger than this.
+ std::list<MediaBuffer *> mBuffers;
+};
+
+MediaBufferGroup::MediaBufferGroup(size_t growthLimit)
+ : mInternal(new InternalData()) {
+ mInternal->mGrowthLimit = growthLimit;
}
MediaBufferGroup::MediaBufferGroup(size_t buffers, size_t buffer_size, size_t growthLimit)
- : mGrowthLimit(growthLimit) {
+ : mInternal(new InternalData()) {
+ mInternal->mGrowthLimit = growthLimit;
- if (mGrowthLimit > 0 && buffers > mGrowthLimit) {
+ if (mInternal->mGrowthLimit > 0 && buffers > mInternal->mGrowthLimit) {
ALOGW("Preallocated buffers %zu > growthLimit %zu, increasing growthLimit",
- buffers, mGrowthLimit);
- mGrowthLimit = buffers;
+ buffers, mInternal->mGrowthLimit);
+ mInternal->mGrowthLimit = buffers;
}
if (buffer_size >= kSharedMemoryThreshold) {
@@ -81,7 +94,7 @@
}
MediaBufferGroup::~MediaBufferGroup() {
- for (MediaBuffer *buffer : mBuffers) {
+ for (MediaBuffer *buffer : mInternal->mBuffers) {
if (buffer->refcount() != 0) {
const int localRefcount = buffer->localRefcount();
const int remoteRefcount = buffer->remoteRefcount();
@@ -103,34 +116,35 @@
buffer->setObserver(nullptr);
buffer->release();
}
+ delete mInternal;
}
void MediaBufferGroup::add_buffer(MediaBuffer *buffer) {
- Mutex::Autolock autoLock(mLock);
+ Mutex::Autolock autoLock(mInternal->mLock);
// if we're above our growth limit, release buffers if we can
- for (auto it = mBuffers.begin();
- mGrowthLimit > 0
- && mBuffers.size() >= mGrowthLimit
- && it != mBuffers.end();) {
+ for (auto it = mInternal->mBuffers.begin();
+ mInternal->mGrowthLimit > 0
+ && mInternal->mBuffers.size() >= mInternal->mGrowthLimit
+ && it != mInternal->mBuffers.end();) {
if ((*it)->refcount() == 0) {
(*it)->setObserver(nullptr);
(*it)->release();
- it = mBuffers.erase(it);
+ it = mInternal->mBuffers.erase(it);
} else {
++it;
}
}
buffer->setObserver(this);
- mBuffers.emplace_back(buffer);
+ mInternal->mBuffers.emplace_back(buffer);
}
bool MediaBufferGroup::has_buffers() {
- if (mBuffers.size() < mGrowthLimit) {
+ if (mInternal->mBuffers.size() < mInternal->mGrowthLimit) {
return true; // We can add more buffers internally.
}
- for (MediaBuffer *buffer : mBuffers) {
+ for (MediaBuffer *buffer : mInternal->mBuffers) {
if (buffer->refcount() == 0) {
return true;
}
@@ -140,12 +154,12 @@
status_t MediaBufferGroup::acquire_buffer(
MediaBuffer **out, bool nonBlocking, size_t requestedSize) {
- Mutex::Autolock autoLock(mLock);
+ Mutex::Autolock autoLock(mInternal->mLock);
for (;;) {
size_t smallest = requestedSize;
MediaBuffer *buffer = nullptr;
- auto free = mBuffers.end();
- for (auto it = mBuffers.begin(); it != mBuffers.end(); ++it) {
+ auto free = mInternal->mBuffers.end();
+ for (auto it = mInternal->mBuffers.begin(); it != mInternal->mBuffers.end(); ++it) {
if ((*it)->refcount() == 0) {
const size_t size = (*it)->size();
if (size >= requestedSize) {
@@ -159,7 +173,8 @@
}
}
if (buffer == nullptr
- && (free != mBuffers.end() || mBuffers.size() < mGrowthLimit)) {
+ && (free != mInternal->mBuffers.end()
+ || mInternal->mBuffers.size() < mInternal->mGrowthLimit)) {
// We alloc before we free so failure leaves group unchanged.
const size_t allocateSize = requestedSize < SIZE_MAX / 3 * 2 /* NB: ordering */ ?
requestedSize * 3 / 2 : requestedSize;
@@ -170,7 +185,7 @@
buffer = nullptr;
} else {
buffer->setObserver(this);
- if (free != mBuffers.end()) {
+ if (free != mInternal->mBuffers.end()) {
ALOGV("reallocate buffer, requested size %zu vs available %zu",
requestedSize, (*free)->size());
(*free)->setObserver(nullptr);
@@ -178,7 +193,7 @@
*free = buffer; // in-place replace
} else {
ALOGV("allocate buffer, requested size %zu", requestedSize);
- mBuffers.emplace_back(buffer);
+ mInternal->mBuffers.emplace_back(buffer);
}
}
}
@@ -193,14 +208,18 @@
return WOULD_BLOCK;
}
// All buffers are in use, block until one of them is returned.
- mCondition.wait(mLock);
+ mInternal->mCondition.wait(mInternal->mLock);
}
// Never gets here.
}
+size_t MediaBufferGroup::buffers() const {
+ return mInternal->mBuffers.size();
+}
+
void MediaBufferGroup::signalBufferReturned(MediaBuffer *) {
- Mutex::Autolock autoLock(mLock);
- mCondition.signal();
+ Mutex::Autolock autoLock(mInternal->mLock);
+ mInternal->mCondition.signal();
}
} // namespace android
diff --git a/media/libmediaextractor/include/media/MediaExtractor.h b/media/libmediaextractor/include/media/MediaExtractor.h
index 73c5f10..2e65620 100644
--- a/media/libmediaextractor/include/media/MediaExtractor.h
+++ b/media/libmediaextractor/include/media/MediaExtractor.h
@@ -29,8 +29,6 @@
class DataSourceBase;
class MetaData;
-class String8;
-struct AMessage;
struct MediaSourceBase;
@@ -87,14 +85,16 @@
virtual const char * name() { return "<unspecified>"; }
typedef MediaExtractor* (*CreatorFunc)(
- DataSourceBase *source, const sp<AMessage> &meta);
+ DataSourceBase *source, void *meta);
+ typedef void (*FreeMetaFunc)(void *meta);
- // The sniffer can optionally fill in "meta" with an AMessage containing
- // a dictionary of values that helps the corresponding extractor initialize
- // its state without duplicating effort already exerted by the sniffer.
+ // The sniffer can optionally fill in an opaque object, "meta", that helps
+ // the corresponding extractor initialize its state without duplicating
+ // effort already exerted by the sniffer. If "freeMeta" is given, it will be
+ // called against the opaque object when it is no longer used.
typedef CreatorFunc (*SnifferFunc)(
- DataSourceBase *source, String8 *mimeType,
- float *confidence, sp<AMessage> *meta);
+ DataSourceBase *source, float *confidence,
+ void **meta, FreeMetaFunc *freeMeta);
typedef struct {
const uint8_t b[16];
diff --git a/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h b/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h
index 3041181..63d0a18 100644
--- a/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h
+++ b/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h
@@ -19,8 +19,6 @@
#define MEDIA_BUFFER_GROUP_H_
#include <media/stagefright/MediaBuffer.h>
-#include <utils/Errors.h>
-#include <utils/threads.h>
namespace android {
@@ -50,7 +48,7 @@
status_t acquire_buffer(
MediaBuffer **buffer, bool nonBlocking = false, size_t requestedSize = 0);
- size_t buffers() const { return mBuffers.size(); }
+ size_t buffers() const;
// If buffer is nullptr, have acquire_buffer() check for remote release.
virtual void signalBufferReturned(MediaBuffer *buffer);
@@ -58,10 +56,8 @@
private:
friend class MediaBuffer;
- Mutex mLock;
- Condition mCondition;
- size_t mGrowthLimit; // Do not automatically grow group larger than this.
- std::list<MediaBuffer *> mBuffers;
+ struct InternalData;
+ InternalData *mInternal;
MediaBufferGroup(const MediaBufferGroup &);
MediaBufferGroup &operator=(const MediaBufferGroup &);
diff --git a/media/libmediaplayer2/JAudioTrack.cpp b/media/libmediaplayer2/JAudioTrack.cpp
index 6d9605a..ac0cc57 100644
--- a/media/libmediaplayer2/JAudioTrack.cpp
+++ b/media/libmediaplayer2/JAudioTrack.cpp
@@ -32,6 +32,8 @@
uint32_t sampleRate, // AudioFormat && bufferSizeInBytes
audio_format_t format, // AudioFormat && bufferSizeInBytes
audio_channel_mask_t channelMask, // AudioFormat && bufferSizeInBytes
+ callback_t cbf, // Offload
+ void* user, // Offload
size_t frameCount, // bufferSizeInBytes
audio_session_t sessionId, // AudioTrack
const audio_attributes_t* pAttributes, // AudioAttributes
@@ -90,8 +92,27 @@
jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetSessionId, sessionId);
}
+ if (cbf != NULL) {
+ jmethodID jSetOffloadedPlayback = env->GetMethodID(jBuilderCls, "setOffloadedPlayback",
+ "(Z)Landroid/media/AudioTrack$Builder;");
+ jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetOffloadedPlayback, true);
+ mFlags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
+ }
+
jmethodID jBuild = env->GetMethodID(jBuilderCls, "build", "()Landroid/media/AudioTrack;");
mAudioTrackObj = env->CallObjectMethod(jBuilderObj, jBuild);
+
+ if (cbf != NULL) {
+ // Set offload mode callback
+ jobject jStreamEventCallbackObj = createStreamEventCallback(cbf, user);
+ jobject jExecutorObj = createCallbackExecutor();
+ jmethodID jSetStreamEventCallback = env->GetMethodID(
+ jAudioTrackCls,
+ "setStreamEventCallback",
+ "(Ljava/util/concurrent/Executor;Landroid/media/AudioTrack$StreamEventCallback;)V");
+ env->CallVoidMethod(
+ mAudioTrackObj, jSetStreamEventCallback, jExecutorObj, jStreamEventCallbackObj);
+ }
}
JAudioTrack::~JAudioTrack() {
@@ -160,6 +181,11 @@
return true;
}
+status_t JAudioTrack::getTimestamp(ExtendedTimestamp *timestamp __unused) {
+ // TODO: Implement this after appropriate Java AudioTrack method is available.
+ return NO_ERROR;
+}
+
status_t JAudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) {
// TODO: existing native AudioTrack returns INVALID_OPERATION on offload/direct/fast tracks.
// Should we do the same thing?
@@ -442,6 +468,80 @@
return routedDeviceId;
}
+audio_session_t JAudioTrack::getAudioSessionId() {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jmethodID jGetAudioSessionId = env->GetMethodID(mAudioTrackCls, "getAudioSessionId", "()I");
+ jint sessionId = env->CallIntMethod(mAudioTrackObj, jGetAudioSessionId);
+ return (audio_session_t) sessionId;
+}
+
+status_t JAudioTrack::setOutputDevice(audio_port_handle_t deviceId) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass jMP2ImplCls = env->FindClass("android/media/MediaPlayer2Impl");
+ jmethodID jSetAudioOutputDeviceById = env->GetMethodID(
+ jMP2ImplCls, "setAudioOutputDeviceById", "(Landroid/media/AudioTrack;I)Z");
+ jboolean result = env->CallStaticBooleanMethod(
+ jMP2ImplCls, jSetAudioOutputDeviceById, mAudioTrackObj, deviceId);
+ return result == true ? NO_ERROR : BAD_VALUE;
+}
+
+status_t JAudioTrack::pendingDuration(int32_t *msec) {
+ if (msec == nullptr) {
+ return BAD_VALUE;
+ }
+
+ bool isPurePcmData = audio_is_linear_pcm(format()) && (getFlags() & AUDIO_FLAG_HW_AV_SYNC) == 0;
+ if (!isPurePcmData) {
+ return INVALID_OPERATION;
+ }
+
+ // TODO: Need to know the difference btw. client and server time.
+ // If getTimestamp(ExtendedTimestamp) is ready, and un-comment below and modify appropriately.
+ // (copied from AudioTrack.cpp)
+
+// ExtendedTimestamp ets;
+// ExtendedTimestamp::LOCATION location = ExtendedTimestamp::LOCATION_SERVER;
+// if (getTimestamp_l(&ets) == OK && ets.mTimeNs[location] > 0) {
+// int64_t diff = ets.mPosition[ExtendedTimestamp::LOCATION_CLIENT]
+// - ets.mPosition[location];
+// if (diff < 0) {
+// *msec = 0;
+// } else {
+// // ms is the playback time by frames
+// int64_t ms = (int64_t)((double)diff * 1000 /
+// ((double)mSampleRate * mPlaybackRate.mSpeed));
+// // clockdiff is the timestamp age (negative)
+// int64_t clockdiff = (mState != STATE_ACTIVE) ? 0 :
+// ets.mTimeNs[location]
+// + ets.mTimebaseOffset[ExtendedTimestamp::TIMEBASE_MONOTONIC]
+// - systemTime(SYSTEM_TIME_MONOTONIC);
+//
+// //ALOGV("ms: %lld clockdiff: %lld", (long long)ms, (long long)clockdiff);
+// static const int NANOS_PER_MILLIS = 1000000;
+// *msec = (int32_t)(ms + clockdiff / NANOS_PER_MILLIS);
+// }
+// return NO_ERROR;
+// }
+
+ return NO_ERROR;
+}
+
+status_t JAudioTrack::addAudioDeviceCallback(
+ const sp<AudioSystem::AudioDeviceCallback>& callback __unused) {
+ // TODO: Implement this after appropriate Java AudioTrack method is available.
+ return NO_ERROR;
+}
+
+status_t JAudioTrack::removeAudioDeviceCallback(
+ const sp<AudioSystem::AudioDeviceCallback>& callback __unused) {
+ // TODO: Implement this after appropriate Java AudioTrack method is available.
+ return NO_ERROR;
+}
+
+/////////////////////////////////////////////////////////////
+/// Private method begins ///
+/////////////////////////////////////////////////////////////
+
jobject JAudioTrack::createVolumeShaperConfigurationObj(
const sp<media::VolumeShaper::Configuration>& config) {
@@ -546,6 +646,24 @@
return env->CallObjectMethod(jBuilderObj, jBuild);
}
+jobject JAudioTrack::createStreamEventCallback(callback_t cbf, void* user) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass jCallbackCls = env->FindClass("android/media/MediaPlayer2Impl$StreamEventCallback");
+ jmethodID jCallbackCtor = env->GetMethodID(jCallbackCls, "<init>", "(JJJ)V");
+ jobject jCallbackObj = env->NewObject(jCallbackCls, jCallbackCtor, this, cbf, user);
+ return jCallbackObj;
+}
+
+jobject JAudioTrack::createCallbackExecutor() {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass jExecutorsCls = env->FindClass("java/util/concurrent/Executors");
+ jmethodID jNewSingleThreadExecutor = env->GetStaticMethodID(jExecutorsCls,
+ "newSingleThreadExecutor", "()Ljava/util/concurrent/ExecutorService;");
+ jobject jSingleThreadExecutorObj =
+ env->CallStaticObjectMethod(jExecutorsCls, jNewSingleThreadExecutor);
+ return jSingleThreadExecutorObj;
+}
+
status_t JAudioTrack::javaToNativeStatus(int javaStatus) {
switch (javaStatus) {
case AUDIO_JAVA_SUCCESS:
diff --git a/media/libmediaplayer2/MediaPlayer2Manager.cpp b/media/libmediaplayer2/MediaPlayer2Manager.cpp
index b254456..44df2ac 100644
--- a/media/libmediaplayer2/MediaPlayer2Manager.cpp
+++ b/media/libmediaplayer2/MediaPlayer2Manager.cpp
@@ -580,7 +580,7 @@
audio_port_handle_t deviceId) {
sp<MediaPlayer2Interface> listener = mListener.promote();
if (listener != NULL) {
- listener->sendEvent(MEDIA2_AUDIO_ROUTING_CHANGED, audioIo, deviceId);
+ listener->sendEvent(0, MEDIA2_AUDIO_ROUTING_CHANGED, audioIo, deviceId);
} else {
ALOGW("listener for process %d death is gone", MEDIA2_AUDIO_ROUTING_CHANGED);
}
@@ -1031,7 +1031,8 @@
}
void MediaPlayer2Manager::Client::notify(
- const wp<MediaPlayer2Engine> &listener, int msg, int ext1, int ext2, const Parcel *obj)
+ const wp<MediaPlayer2Engine> &listener, int64_t srcId,
+ int msg, int ext1, int ext2, const Parcel *obj)
{
sp<MediaPlayer2Engine> spListener = listener.promote();
if (spListener == NULL) {
@@ -1063,9 +1064,9 @@
}
if (nc != NULL) {
if (errStartNext == NO_ERROR) {
- nc->notify(MEDIA2_INFO, MEDIA2_INFO_STARTED_AS_NEXT, 0, obj);
+ nc->notify(srcId, MEDIA2_INFO, MEDIA2_INFO_STARTED_AS_NEXT, 0, obj);
} else {
- nc->notify(MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN , 0, obj);
+ nc->notify(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN , 0, obj);
ALOGE("gapless:start playback for next track failed, err(%d)", errStartNext);
}
}
@@ -1085,8 +1086,9 @@
}
if (c != NULL) {
- ALOGV("[%d] notify (%p, %d, %d, %d)", client->mConnId, spListener.get(), msg, ext1, ext2);
- c->notify(msg, ext1, ext2, obj);
+ ALOGV("[%d] notify (%p, %lld, %d, %d, %d)", client->mConnId, spListener.get(),
+ (long long)srcId, msg, ext1, ext2);
+ c->notify(srcId, msg, ext1, ext2, obj);
}
}
diff --git a/media/libmediaplayer2/MediaPlayer2Manager.h b/media/libmediaplayer2/MediaPlayer2Manager.h
index fde1381..7e6f0de 100644
--- a/media/libmediaplayer2/MediaPlayer2Manager.h
+++ b/media/libmediaplayer2/MediaPlayer2Manager.h
@@ -276,8 +276,8 @@
virtual status_t setDataSource(const sp<DataSourceDesc> &dsd);
- static void notify(const wp<MediaPlayer2Engine> &listener, int msg,
- int ext1, int ext2, const Parcel *obj);
+ static void notify(const wp<MediaPlayer2Engine> &listener, int64_t srcId,
+ int msg, int ext1, int ext2, const Parcel *obj);
pid_t pid() const { return mPid; }
virtual status_t dump(int fd, const Vector<String16>& args);
diff --git a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
index 10fa5e8..301825b 100644
--- a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
+++ b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
@@ -19,6 +19,7 @@
#include <jni.h>
#include <media/AudioResamplerPublic.h>
+#include <media/AudioSystem.h>
#include <media/VolumeShaper.h>
#include <system/audio.h>
#include <utils/Errors.h>
@@ -31,6 +32,42 @@
class JAudioTrack {
public:
+ /* Events used by AudioTrack callback function (callback_t).
+ * Keep in sync with frameworks/base/media/java/android/media/AudioTrack.java NATIVE_EVENT_*.
+ */
+ enum event_type {
+ EVENT_MORE_DATA = 0, // Request to write more data to buffer.
+ EVENT_NEW_IAUDIOTRACK = 6, // IAudioTrack was re-created, either due to re-routing and
+ // voluntary invalidation by mediaserver, or mediaserver crash.
+ EVENT_STREAM_END = 7, // Sent after all the buffers queued in AF and HW are played
+ // back (after stop is called) for an offloaded track.
+ };
+
+ class Buffer
+ {
+ public:
+ size_t mSize; // input/output in bytes.
+ void* mData; // pointer to the audio data.
+ };
+
+ /* As a convenience, if a callback is supplied, a handler thread
+ * is automatically created with the appropriate priority. This thread
+ * invokes the callback when a new buffer becomes available or various conditions occur.
+ *
+ * Parameters:
+ *
+ * event: type of event notified (see enum AudioTrack::event_type).
+ * user: Pointer to context for use by the callback receiver.
+ * info: Pointer to optional parameter according to event type:
+ * - EVENT_MORE_DATA: pointer to JAudioTrack::Buffer struct. The callback must not
+ * write more bytes than indicated by 'size' field and update 'size' if fewer bytes
+ * are written.
+ * - EVENT_NEW_IAUDIOTRACK: unused.
+ * - EVENT_STREAM_END: unused.
+ */
+
+ typedef void (*callback_t)(int event, void* user, void *info);
+
/* Creates an JAudioTrack object for non-offload mode.
* Once created, the track needs to be started before it can be used.
* Unspecified values are set to appropriate default values.
@@ -49,6 +86,9 @@
* output sink.
* (TODO: How can we check whether a format is supported?)
* channelMask: Channel mask, such that audio_is_output_channel(channelMask) is true.
+ * cbf: Callback function. If not null, this function is called periodically
+ * to provide new data and inform of marker, position updates, etc.
+ * user: Context for use by the callback receiver.
* frameCount: Minimum size of track PCM buffer in frames. This defines the
* application's contribution to the latency of the track.
* The actual size selected by the JAudioTrack could be larger if the
@@ -68,35 +108,20 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
+ callback_t cbf,
+ void* user,
size_t frameCount = 0,
audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
const audio_attributes_t* pAttributes = NULL,
float maxRequiredSpeed = 1.0f);
/*
- Temporarily removed constructor arguments:
-
- // Q. Values are in audio-base.h, but where can we find explanation for them?
- audio_output_flags_t flags,
-
// Q. May be used in AudioTrack.setPreferredDevice(AudioDeviceInfo)?
audio_port_handle_t selectedDeviceId,
- // Should be deleted, since we don't use Binder anymore.
- bool doNotReconnect,
-
- // Do we need UID and PID?
- uid_t uid,
- pid_t pid,
-
- // TODO: Uses these values when Java AudioTrack supports the offload mode.
- callback_t cbf,
- void* user,
+ // TODO: No place to use these values.
int32_t notificationFrames,
const audio_offload_info_t *offloadInfo,
-
- // Fixed to false, but what is this?
- threadCanCallJava
*/
virtual ~JAudioTrack();
@@ -138,6 +163,46 @@
*/
bool getTimestamp(AudioTimestamp& timestamp);
+ // TODO: This doc is just copied from AudioTrack.h. Revise it after implemenation.
+ /* Return the extended timestamp, with additional timebase info and improved drain behavior.
+ *
+ * This is similar to the AudioTrack.java API:
+ * getTimestamp(@NonNull AudioTimestamp timestamp, @AudioTimestamp.Timebase int timebase)
+ *
+ * Some differences between this method and the getTimestamp(AudioTimestamp& timestamp) method
+ *
+ * 1. stop() by itself does not reset the frame position.
+ * A following start() resets the frame position to 0.
+ * 2. flush() by itself does not reset the frame position.
+ * The frame position advances by the number of frames flushed,
+ * when the first frame after flush reaches the audio sink.
+ * 3. BOOTTIME clock offsets are provided to help synchronize with
+ * non-audio streams, e.g. sensor data.
+ * 4. Position is returned with 64 bits of resolution.
+ *
+ * Parameters:
+ * timestamp: A pointer to the caller allocated ExtendedTimestamp.
+ *
+ * Returns NO_ERROR on success; timestamp is filled with valid data.
+ * BAD_VALUE if timestamp is NULL.
+ * WOULD_BLOCK if called immediately after start() when the number
+ * of frames consumed is less than the
+ * overall hardware latency to physical output. In WOULD_BLOCK cases,
+ * one might poll again, or use getPosition(), or use 0 position and
+ * current time for the timestamp.
+ * If WOULD_BLOCK is returned, the timestamp is still
+ * modified with the LOCATION_CLIENT portion filled.
+ * DEAD_OBJECT if AudioFlinger dies or the output device changes and
+ * the track cannot be automatically restored.
+ * The application needs to recreate the AudioTrack
+ * because the audio device changed or AudioFlinger died.
+ * This typically occurs for direct or offloaded tracks
+ * or if mDoNotReconnect is true.
+ * INVALID_OPERATION if called on a offloaded or direct track.
+ * Use getTimestamp(AudioTimestamp& timestamp) instead.
+ */
+ status_t getTimestamp(ExtendedTimestamp *timestamp);
+
/* Set source playback rate for timestretch
* 1.0 is normal speed: < 1.0 is slower, > 1.0 is faster
* 1.0 is normal pitch: < 1.0 is lower pitch, > 1.0 is higher pitch
@@ -270,7 +335,65 @@
*/
audio_port_handle_t getRoutedDeviceId();
+ /* Returns the ID of the audio session this AudioTrack belongs to. */
+ audio_session_t getAudioSessionId();
+
+ /* Selects the audio device to use for output of this AudioTrack. A value of
+ * AUDIO_PORT_HANDLE_NONE indicates default routing.
+ *
+ * Parameters:
+ * The device ID of the selected device (as returned by the AudioDevicesManager API).
+ *
+ * Returned value:
+ * - NO_ERROR: successful operation
+ * - BAD_VALUE: failed to find the valid output device with given device Id.
+ */
+ status_t setOutputDevice(audio_port_handle_t deviceId);
+
+ // TODO: Add AUDIO_OUTPUT_FLAG_DIRECT when it is possible to check.
+ // TODO: Add AUDIO_FLAG_HW_AV_SYNC when it is possible to check.
+ /* Returns the flags */
+ audio_output_flags_t getFlags() const { return mFlags; }
+
+ /* Obtain the pending duration in milliseconds for playback of pure PCM data remaining in
+ * AudioTrack.
+ *
+ * Returns NO_ERROR if successful.
+ * INVALID_OPERATION if the AudioTrack does not contain pure PCM data.
+ * BAD_VALUE if msec is nullptr.
+ */
+ status_t pendingDuration(int32_t *msec);
+
+ /* Adds an AudioDeviceCallback. The caller will be notified when the audio device to which this
+ * AudioTrack is routed is updated.
+ * Replaces any previously installed callback.
+ *
+ * Parameters:
+ *
+ * callback: The callback interface
+ *
+ * Returns NO_ERROR if successful.
+ * INVALID_OPERATION if the same callback is already installed.
+ * NO_INIT or PREMISSION_DENIED if AudioFlinger service is not reachable
+ * BAD_VALUE if the callback is NULL
+ */
+ status_t addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
+
+ /* Removes an AudioDeviceCallback.
+ *
+ * Parameters:
+ *
+ * callback: The callback interface
+ *
+ * Returns NO_ERROR if successful.
+ * INVALID_OPERATION if the callback is not installed
+ * BAD_VALUE if the callback is NULL
+ */
+ status_t removeAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
+
private:
+ audio_output_flags_t mFlags;
+
jclass mAudioTrackCls;
jobject mAudioTrackObj;
@@ -282,6 +405,12 @@
jobject createVolumeShaperOperationObj(
const sp<media::VolumeShaper::Operation>& operation);
+ /* Creates a Java StreamEventCallback object */
+ jobject createStreamEventCallback(callback_t cbf, void* user);
+
+ /* Creates a Java Executor object for running a callback */
+ jobject createCallbackExecutor();
+
status_t javaToNativeStatus(int javaStatus);
};
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2EngineClient.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2EngineClient.h
index 22df095..0b066aa 100644
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2EngineClient.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2EngineClient.h
@@ -25,7 +25,7 @@
class MediaPlayer2EngineClient: public RefBase
{
public:
- virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) = 0;
+ virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
};
}; // namespace android
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
index 1de4348..b1cdf96 100644
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
@@ -64,7 +64,7 @@
public:
// callback mechanism for passing messages to MediaPlayer2 object
typedef void (*NotifyCallback)(const wp<MediaPlayer2Engine> &listener,
- int msg, int ext1, int ext2, const Parcel *obj);
+ int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj);
// AudioSink: abstraction layer for audio output
class AudioSink : public RefBase {
@@ -240,7 +240,7 @@
mClient = client; mNotify = notifyFunc;
}
- void sendEvent(int msg, int ext1=0, int ext2=0,
+ void sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0,
const Parcel *obj=NULL) {
NotifyCallback notifyCB;
wp<MediaPlayer2Engine> client;
@@ -250,7 +250,7 @@
client = mClient;
}
- if (notifyCB) notifyCB(client, msg, ext1, ext2, obj);
+ if (notifyCB) notifyCB(client, srcId, msg, ext1, ext2, obj);
}
virtual status_t dump(int /* fd */, const Vector<String16>& /* args */) const {
diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
index 8e188eb..e9d6f84 100644
--- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
+++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
@@ -194,7 +194,7 @@
class MediaPlayer2Listener: virtual public RefBase
{
public:
- virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) = 0;
+ virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
};
class MediaPlayer2 : public MediaPlayer2EngineClient
@@ -204,6 +204,7 @@
~MediaPlayer2();
void disconnect();
+ status_t getSrcId(int64_t *srcId);
status_t setDataSource(const sp<DataSourceDesc> &dsd);
status_t setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww);
status_t setListener(const sp<MediaPlayer2Listener>& listener);
@@ -235,7 +236,8 @@
status_t setLooping(int loop);
bool isLooping();
status_t setVolume(float leftVolume, float rightVolume);
- void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
+ void notify(int64_t srcId, int msg, int ext1, int ext2,
+ const Parcel *obj = NULL);
status_t invoke(const Parcel& request, Parcel *reply);
status_t setMetadataFilter(const Parcel& filter);
status_t getMetadata(bool update_only, bool apply_filter, Parcel *metadata);
@@ -260,11 +262,12 @@
status_t seekTo_l(int msec, MediaPlayer2SeekMode mode);
status_t prepareAsync_l();
status_t getDuration_l(int *msec);
- status_t attachNewPlayer(const sp<MediaPlayer2Engine>& player);
+ status_t attachNewPlayer(const sp<MediaPlayer2Engine>& player, long srcId);
status_t reset_l();
status_t checkStateForKeySet_l(int key);
sp<MediaPlayer2Engine> mPlayer;
+ int64_t mSrcId;
thread_id_t mLockThreadId;
Mutex mLock;
Mutex mNotifyLock;
diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp
index b858783..ab30273 100644
--- a/media/libmediaplayer2/mediaplayer2.cpp
+++ b/media/libmediaplayer2/mediaplayer2.cpp
@@ -53,6 +53,7 @@
MediaPlayer2::MediaPlayer2()
{
ALOGV("constructor");
+ mSrcId = 0;
mListener = NULL;
mCookie = NULL;
mStreamType = AUDIO_STREAM_MUSIC;
@@ -118,8 +119,17 @@
return NO_ERROR;
}
+status_t MediaPlayer2::getSrcId(int64_t *srcId) {
+ if (srcId == NULL) {
+ return BAD_VALUE;
+ }
-status_t MediaPlayer2::attachNewPlayer(const sp<MediaPlayer2Engine>& player)
+ Mutex::Autolock _l(mLock);
+ *srcId = mSrcId;
+ return OK;
+}
+
+status_t MediaPlayer2::attachNewPlayer(const sp<MediaPlayer2Engine>& player, long srcId)
{
status_t err = UNKNOWN_ERROR;
sp<MediaPlayer2Engine> p;
@@ -135,6 +145,7 @@
clear_l();
p = mPlayer;
mPlayer = player;
+ mSrcId = srcId;
if (player != 0) {
mCurrentState = MEDIA_PLAYER2_INITIALIZED;
err = NO_ERROR;
@@ -161,7 +172,7 @@
if (NO_ERROR != player->setDataSource(dsd)) {
player.clear();
}
- err = attachNewPlayer(player);
+ err = attachNewPlayer(player, dsd->mId);
return err;
}
@@ -763,9 +774,10 @@
return INVALID_OPERATION;
}
-void MediaPlayer2::notify(int msg, int ext1, int ext2, const Parcel *obj)
+void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj)
{
- ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
+ ALOGV("message received srcId=%lld, msg=%d, ext1=%d, ext2=%d",
+ (long long)srcId, msg, ext1, ext2);
bool send = true;
bool locked = false;
@@ -784,7 +796,8 @@
// Allows calls from JNI in idle state to notify errors
if (!(msg == MEDIA2_ERROR && mCurrentState == MEDIA_PLAYER2_IDLE) && mPlayer == 0) {
- ALOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2);
+ ALOGV("notify(%lld, %d, %d, %d) callback on disconnected mediaplayer",
+ (long long)srcId, msg, ext1, ext2);
if (locked) mLock.unlock(); // release the lock when done.
return;
}
@@ -803,7 +816,8 @@
}
break;
case MEDIA2_DRM_INFO:
- ALOGV("MediaPlayer2::notify() MEDIA2_DRM_INFO(%d, %d, %d, %p)", msg, ext1, ext2, obj);
+ ALOGV("MediaPlayer2::notify() MEDIA2_DRM_INFO(%lld, %d, %d, %d, %p)",
+ (long long)srcId, msg, ext1, ext2, obj);
break;
case MEDIA2_PLAYBACK_COMPLETE:
ALOGV("playback complete");
@@ -882,7 +896,7 @@
if ((listener != 0) && send) {
Mutex::Autolock _l(mNotifyLock);
ALOGV("callback application");
- listener->notify(msg, ext1, ext2, obj);
+ listener->notify(srcId, msg, ext1, ext2, obj);
ALOGV("back from callback");
}
}
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
index ea9d270..462a904 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
@@ -314,6 +314,7 @@
if (err == OK) {
*source = genericSource;
} else {
+ *source = NULL;
ALOGE("Failed to create NuPlayer2Source!");
}
@@ -364,7 +365,7 @@
default:
err = BAD_TYPE;
- source = NULL;
+ *source = NULL;
*dataSourceType = DATA_SOURCE_TYPE_NONE;
ALOGE("invalid data source type!");
break;
@@ -376,13 +377,22 @@
void NuPlayer2::setDataSourceAsync(const sp<DataSourceDesc> &dsd) {
DATA_SOURCE_TYPE dataSourceType;
sp<Source> source;
- status_t err = createNuPlayer2Source(dsd, &source, &dataSourceType);
+ createNuPlayer2Source(dsd, &source, &dataSourceType);
+ // TODO: currently NuPlayer2Driver makes blocking call to setDataSourceAsync
+ // and expects notifySetDataSourceCompleted regardless of success or failure.
+ // This will be changed since setDataSource should be asynchronous at JAVA level.
+ // When it succeeds, app will get onInfo notification. Otherwise, onError
+ // will be called.
+ /*
if (err != OK) {
- notifyListener(MEDIA2_ERROR, MEDIA2_ERROR_FAILED_TO_SET_DATA_SOURCE, err);
+ notifyListener(dsd->mId, MEDIA2_ERROR, MEDIA2_ERROR_FAILED_TO_SET_DATA_SOURCE, err);
return;
}
+ // Now, source != NULL.
+ */
+
mDataSourceType = dataSourceType;
sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
@@ -394,13 +404,17 @@
void NuPlayer2::prepareNextDataSourceAsync(const sp<DataSourceDesc> &dsd) {
DATA_SOURCE_TYPE dataSourceType;
sp<Source> source;
- status_t err = createNuPlayer2Source(dsd, &source, &dataSourceType);
+ createNuPlayer2Source(dsd, &source, &dataSourceType);
+ /*
if (err != OK) {
- notifyListener(MEDIA2_ERROR, MEDIA2_ERROR_FAILED_TO_SET_DATA_SOURCE, err);
+ notifyListener(dsd->mId, MEDIA2_ERROR, MEDIA2_ERROR_FAILED_TO_SET_DATA_SOURCE, err);
return;
}
+ // Now, source != NULL.
+ */
+
mNextDataSourceType = dataSourceType;
sp<AMessage> msg = new AMessage(kWhatPrepareNextDataSource, this);
@@ -627,12 +641,13 @@
mSource = static_cast<Source *>(obj.get());
} else {
err = UNKNOWN_ERROR;
+ ALOGE("kWhatSetDataSource, source should not be NULL");
}
CHECK(mDriver != NULL);
sp<NuPlayer2Driver> driver = mDriver.promote();
if (driver != NULL) {
- driver->notifySetDataSourceCompleted(err);
+ driver->notifySetDataSourceCompleted(mSrcId, err);
}
break;
}
@@ -831,7 +846,7 @@
if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) {
sp<NuPlayer2Driver> driver = mDriver.promote();
if (driver != NULL) {
- driver->notifyDuration(durationUs);
+ driver->notifyDuration(mSrcId, durationUs);
}
}
@@ -921,6 +936,7 @@
onStart();
}
mPausedByClient = false;
+ notifyListener(mSrcId, MEDIA2_STARTED, 0, 0);
break;
}
@@ -960,6 +976,7 @@
if (err == OK) {
if (rate.mSpeed == 0.f) {
onPause();
+ notifyListener(mSrcId, MEDIA2_PAUSED, 0, 0);
mPausedByClient = true;
// save all other settings (using non-paused speed)
// so we can restore them on start
@@ -1114,9 +1131,9 @@
// video tracks found) and we just ran out of input data.
if (err == ERROR_END_OF_STREAM) {
- notifyListener(MEDIA2_PLAYBACK_COMPLETE, 0, 0);
+ notifyListener(mSrcId, MEDIA2_PLAYBACK_COMPLETE, 0, 0);
} else {
- notifyListener(MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
+ notifyListener(mSrcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
}
}
break;
@@ -1201,7 +1218,7 @@
mSource->getFormat(false /* audio */);
setVideoScalingMode(mVideoScalingMode);
- updateVideoSize(inputFormat, format);
+ updateVideoSize(mSrcId, inputFormat, format);
} else if (what == DecoderBase::kWhatShutdownCompleted) {
ALOGV("%s shutdown completed", audio ? "audio" : "video");
if (audio) {
@@ -1275,10 +1292,10 @@
|| mVideoDecoder == NULL) {
// When both audio and video have error, or this stream has only audio
// which has error, notify client of error.
- notifyListener(MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
+ notifyListener(mSrcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
} else {
// Only audio track has error. Video track could be still good to play.
- notifyListener(MEDIA2_INFO, MEDIA2_INFO_PLAY_AUDIO_ERROR, err);
+ notifyListener(mSrcId, MEDIA2_INFO, MEDIA2_INFO_PLAY_AUDIO_ERROR, err);
}
mAudioDecoderError = true;
} else {
@@ -1286,10 +1303,10 @@
|| mAudioSink == NULL || mAudioDecoder == NULL) {
// When both audio and video have error, or this stream has only video
// which has error, notify client of error.
- notifyListener(MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
+ notifyListener(mSrcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
} else {
// Only video track has error. Audio track could be still good to play.
- notifyListener(MEDIA2_INFO, MEDIA2_INFO_PLAY_VIDEO_ERROR, err);
+ notifyListener(mSrcId, MEDIA2_INFO, MEDIA2_INFO_PLAY_VIDEO_ERROR, err);
}
mVideoDecoderError = true;
}
@@ -1339,12 +1356,12 @@
audio ? "audio" : "video", finalResult);
notifyListener(
- MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, finalResult);
+ mSrcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, finalResult);
}
if ((mAudioEOS || mAudioDecoder == NULL)
&& (mVideoEOS || mVideoDecoder == NULL)) {
- notifyListener(MEDIA2_PLAYBACK_COMPLETE, 0, 0);
+ notifyListener(mSrcId, MEDIA2_PLAYBACK_COMPLETE, 0, 0);
}
} else if (what == Renderer::kWhatFlushComplete) {
int32_t audio;
@@ -1365,10 +1382,10 @@
handleFlushComplete(audio, false /* isDecoder */);
finishFlushIfPossible();
} else if (what == Renderer::kWhatVideoRenderingStart) {
- notifyListener(MEDIA2_INFO, MEDIA2_INFO_RENDERING_START, 0);
+ notifyListener(mSrcId, MEDIA2_INFO, MEDIA2_INFO_RENDERING_START, 0);
} else if (what == Renderer::kWhatMediaRenderingStart) {
ALOGV("media rendering started");
- notifyListener(MEDIA2_STARTED, 0, 0);
+ notifyListener(mSrcId, MEDIA2_STARTED, 0, 0);
} else if (what == Renderer::kWhatAudioTearDown) {
int32_t reason;
CHECK(msg->findInt32("reason", &reason));
@@ -1421,7 +1438,7 @@
int64_t timerUs;
CHECK(msg->findInt64("timerUs", &timerUs));
- notifyListener(MEDIA2_NOTIFY_TIME, timerUs, 0);
+ notifyListener(mSrcId, MEDIA2_NOTIFY_TIME, timerUs, 0);
break;
}
@@ -1438,22 +1455,23 @@
(long long)seekTimeUs, mode, needNotify);
if (!mStarted) {
- // Seek before the player is started. In order to preview video,
- // need to start the player and pause it. This branch is called
- // only once if needed. After the player is started, any seek
- // operation will go through normal path.
- // Audio-only cases are handled separately.
- onStart(seekTimeUs, (MediaPlayer2SeekMode)mode);
- if (mStarted) {
- onPause();
- mPausedByClient = true;
+ if (!mSourceStarted) {
+ mSourceStarted = true;
+ mSource->start();
}
+ if (seekTimeUs > 0) {
+ performSeek(seekTimeUs, (MediaPlayer2SeekMode)mode);
+ }
+
if (needNotify) {
- notifyDriverSeekComplete();
+ notifyDriverSeekComplete(mSrcId);
}
break;
}
+ // seeks can take a while, so we essentially paused
+ notifyListener(mSrcId, MEDIA2_PAUSED, 0, 0);
+
mDeferredActions.push_back(
new FlushDecoderAction(FLUSH_CMD_FLUSH /* audio */,
FLUSH_CMD_FLUSH /* video */));
@@ -1474,6 +1492,7 @@
case kWhatPause:
{
onPause();
+ notifyListener(mSrcId, MEDIA2_PAUSED, 0, 0);
mPausedByClient = true;
break;
}
@@ -1545,19 +1564,13 @@
startPlaybackTimer("onresume");
}
-void NuPlayer2::onStart(int64_t startPositionUs, MediaPlayer2SeekMode mode) {
+void NuPlayer2::onStart() {
ALOGV("onStart: mCrypto: %p", mCrypto.get());
if (!mSourceStarted) {
mSourceStarted = true;
mSource->start();
}
- if (startPositionUs > 0) {
- performSeek(startPositionUs, mode);
- if (mSource->getFormat(false /* audio */) == NULL) {
- return;
- }
- }
mOffloadAudio = false;
mAudioEOS = false;
@@ -1577,7 +1590,7 @@
ALOGE("no metadata for either audio or video source");
mSource->stop();
mSourceStarted = false;
- notifyListener(MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, ERROR_MALFORMED);
+ notifyListener(mSrcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, ERROR_MALFORMED);
return;
}
ALOGV_IF(!hasAudio, "no metadata for audio source"); // video only stream
@@ -1616,7 +1629,7 @@
if (err != OK) {
mSource->stop();
mSourceStarted = false;
- notifyListener(MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
+ notifyListener(mSrcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
return;
}
@@ -1658,7 +1671,7 @@
ALOGV("stopPlaybackTimer() log %20" PRId64 "", played);
if (played > 0) {
- driver->notifyMorePlayingTimeUs((played+500)/1000);
+ driver->notifyMorePlayingTimeUs(mSrcId, (played+500)/1000);
}
}
mLastStartedPlayingTimeNs = 0;
@@ -1686,9 +1699,9 @@
ALOGV("stopRebufferingTimer() log %20" PRId64 "", rebuffered);
if (rebuffered > 0) {
- driver->notifyMoreRebufferingTimeUs((rebuffered+500)/1000);
+ driver->notifyMoreRebufferingTimeUs(mSrcId, (rebuffered+500)/1000);
if (exitingPlayback) {
- driver->notifyRebufferingWhenExit(true);
+ driver->notifyRebufferingWhenExit(mSrcId, true);
}
}
}
@@ -2005,11 +2018,12 @@
}
void NuPlayer2::updateVideoSize(
+ int64_t srcId,
const sp<AMessage> &inputFormat,
const sp<AMessage> &outputFormat) {
if (inputFormat == NULL) {
ALOGW("Unknown video size, reporting 0x0!");
- notifyListener(MEDIA2_SET_VIDEO_SIZE, 0, 0);
+ notifyListener(srcId, MEDIA2_SET_VIDEO_SIZE, 0, 0);
return;
}
int32_t err = OK;
@@ -2088,12 +2102,13 @@
}
notifyListener(
+ srcId,
MEDIA2_SET_VIDEO_SIZE,
displayWidth,
displayHeight);
}
-void NuPlayer2::notifyListener(int msg, int ext1, int ext2, const Parcel *in) {
+void NuPlayer2::notifyListener(int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
if (mDriver == NULL) {
return;
}
@@ -2104,7 +2119,7 @@
return;
}
- driver->notifyListener(msg, ext1, ext2, in);
+ driver->notifyListener(srcId, msg, ext1, ext2, in);
}
void NuPlayer2::flushDecoder(bool audio, bool needShutdown) {
@@ -2369,7 +2384,7 @@
if (mDriver != NULL) {
sp<NuPlayer2Driver> driver = mDriver.promote();
if (driver != NULL) {
- driver->notifyResetComplete();
+ driver->notifyResetComplete(mSrcId);
}
}
@@ -2411,7 +2426,7 @@
if (mDriver != NULL) {
sp<NuPlayer2Driver> driver = mDriver.promote();
if (driver != NULL) {
- driver->notifySetSurfaceComplete();
+ driver->notifySetSurfaceComplete(mSrcId);
}
}
}
@@ -2443,15 +2458,15 @@
void NuPlayer2::finishResume() {
if (mResumePending) {
mResumePending = false;
- notifyDriverSeekComplete();
+ notifyDriverSeekComplete(mSrcId);
}
}
-void NuPlayer2::notifyDriverSeekComplete() {
+void NuPlayer2::notifyDriverSeekComplete(int64_t srcId) {
if (mDriver != NULL) {
sp<NuPlayer2Driver> driver = mDriver.promote();
if (driver != NULL) {
- driver->notifySeekComplete();
+ driver->notifySeekComplete(srcId);
}
}
}
@@ -2460,7 +2475,8 @@
int32_t what;
CHECK(msg->findInt32("what", &what));
- // TODO: tell this is for mSource or mNextSource.
+ int64_t srcId;
+ CHECK(msg->findInt64("srcId", &srcId));
switch (what) {
case Source::kWhatPrepared:
{
@@ -2491,9 +2507,9 @@
// the app received the "prepare complete" callback.
int64_t durationUs;
if (mSource->getDuration(&durationUs) == OK) {
- driver->notifyDuration(durationUs);
+ driver->notifyDuration(srcId, durationUs);
}
- driver->notifyPrepareCompleted(err);
+ driver->notifyPrepareCompleted(srcId, err);
}
break;
@@ -2510,7 +2526,7 @@
ALOGV("onSourceNotify() kWhatDrmInfo MEDIA2_DRM_INFO drmInfo: %p parcel size: %zu",
drmInfo.get(), parcel.dataSize());
- notifyListener(MEDIA2_DRM_INFO, 0 /* ext1 */, 0 /* ext2 */, &parcel);
+ notifyListener(srcId, MEDIA2_DRM_INFO, 0 /* ext1 */, 0 /* ext2 */, &parcel);
break;
}
@@ -2537,9 +2553,9 @@
if ((flags & NuPlayer2::Source::FLAG_CAN_SEEK) == 0) {
driver->notifyListener(
- MEDIA2_INFO, MEDIA2_INFO_NOT_SEEKABLE, 0);
+ srcId, MEDIA2_INFO, MEDIA2_INFO_NOT_SEEKABLE, 0);
}
- driver->notifyFlagsChanged(flags);
+ driver->notifyFlagsChanged(srcId, flags);
}
if ((mSourceFlags & Source::FLAG_DYNAMIC_DURATION)
@@ -2560,7 +2576,7 @@
sp<AMessage> format;
CHECK(msg->findMessage("format", &format));
- updateVideoSize(format);
+ updateVideoSize(srcId, format);
break;
}
@@ -2569,7 +2585,7 @@
int32_t percentage;
CHECK(msg->findInt32("percentage", &percentage));
- notifyListener(MEDIA2_BUFFERING_UPDATE, percentage, 0);
+ notifyListener(srcId, MEDIA2_BUFFERING_UPDATE, percentage, 0);
break;
}
@@ -2583,7 +2599,7 @@
mPausedForBuffering = true;
onPause();
}
- notifyListener(MEDIA2_INFO, MEDIA2_INFO_BUFFERING_START, 0);
+ notifyListener(srcId, MEDIA2_INFO, MEDIA2_INFO_BUFFERING_START, 0);
break;
}
@@ -2601,7 +2617,7 @@
onResume();
}
}
- notifyListener(MEDIA2_INFO, MEDIA2_INFO_BUFFERING_END, 0);
+ notifyListener(srcId, MEDIA2_INFO, MEDIA2_INFO_BUFFERING_END, 0);
break;
}
@@ -2610,7 +2626,7 @@
int32_t kbps;
CHECK(msg->findInt32("bandwidth", &kbps));
- notifyListener(MEDIA2_INFO, MEDIA2_INFO_NETWORK_BANDWIDTH, kbps);
+ notifyListener(srcId, MEDIA2_INFO, MEDIA2_INFO_NETWORK_BANDWIDTH, kbps);
break;
}
@@ -2627,7 +2643,7 @@
{
sp<ABuffer> buffer;
if (!msg->findBuffer("buffer", &buffer)) {
- notifyListener(MEDIA2_INFO, MEDIA2_INFO_METADATA_UPDATE, 0);
+ notifyListener(srcId, MEDIA2_INFO, MEDIA2_INFO_METADATA_UPDATE, 0);
} else {
sendTimedMetaData(buffer);
}
@@ -2682,7 +2698,7 @@
case Source::kWhatDrmNoLicense:
{
- notifyListener(MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE);
+ notifyListener(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE);
break;
}
@@ -2712,7 +2728,7 @@
case NuPlayer2::CCDecoder::kWhatTrackAdded:
{
- notifyListener(MEDIA2_INFO, MEDIA2_INFO_METADATA_UPDATE, 0);
+ notifyListener(mSrcId, MEDIA2_INFO, MEDIA2_INFO_METADATA_UPDATE, 0);
break;
}
@@ -2739,7 +2755,7 @@
in.writeInt32(buffer->size());
in.write(buffer->data(), buffer->size());
- notifyListener(MEDIA2_SUBTITLE_DATA, 0, 0, &in);
+ notifyListener(mSrcId, MEDIA2_SUBTITLE_DATA, 0, 0, &in);
}
void NuPlayer2::sendTimedMetaData(const sp<ABuffer> &buffer) {
@@ -2752,7 +2768,7 @@
in.writeInt32(buffer->size());
in.write(buffer->data(), buffer->size());
- notifyListener(MEDIA2_META_DATA, 0, 0, &in);
+ notifyListener(mSrcId, MEDIA2_META_DATA, 0, 0, &in);
}
void NuPlayer2::sendTimedTextData(const sp<ABuffer> &buffer) {
@@ -2782,9 +2798,9 @@
}
if ((parcel.dataSize() > 0)) {
- notifyListener(MEDIA2_TIMED_TEXT, 0, 0, &parcel);
+ notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0, &parcel);
} else { // send an empty timed text
- notifyListener(MEDIA2_TIMED_TEXT, 0, 0);
+ notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0);
}
}
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.h b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
index bf55b65..e7b774c 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
@@ -284,17 +284,16 @@
bool audio, sp<DecoderBase> *decoder, bool checkAudioModeChange = true);
void updateVideoSize(
+ int64_t srcId,
const sp<AMessage> &inputFormat,
const sp<AMessage> &outputFormat = NULL);
- void notifyListener(int msg, int ext1, int ext2, const Parcel *in = NULL);
+ void notifyListener(int64_t srcId, int msg, int ext1, int ext2, const Parcel *in = NULL);
void handleFlushComplete(bool audio, bool isDecoder);
void finishFlushIfPossible();
- void onStart(
- int64_t startPositionUs = -1,
- MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC);
+ void onStart();
void onResume();
void onPause();
@@ -303,7 +302,7 @@
void flushDecoder(bool audio, bool needShutdown);
void finishResume();
- void notifyDriverSeekComplete();
+ void notifyDriverSeekComplete(int64_t srcId);
void postScanSources();
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
index 607d406..60a07a3 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
@@ -25,6 +25,7 @@
#include "NuPlayer2.h"
#include "NuPlayer2Source.h"
+#include <media/DataSourceDesc.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/foundation/AUtils.h>
@@ -108,6 +109,7 @@
: mState(STATE_IDLE),
mIsAsyncPrepare(false),
mAsyncResult(UNKNOWN_ERROR),
+ mSrcId(0),
mSetSurfaceInProgress(false),
mDurationUs(-1),
mPositionUs(-1),
@@ -179,6 +181,7 @@
return INVALID_OPERATION;
}
+ mSrcId = dsd->mId;
mState = STATE_SET_DATASOURCE_PENDING;
mPlayer->setDataSourceAsync(dsd);
@@ -361,7 +364,7 @@
case STATE_PAUSED:
mState = STATE_STOPPED;
- notifyListener_l(MEDIA2_STOPPED);
+ //notifyListener_l(MEDIA2_STOPPED);
break;
case STATE_PREPARED:
@@ -396,7 +399,6 @@
case STATE_RUNNING:
mState = STATE_PAUSED;
- notifyListener_l(MEDIA2_PAUSED);
mPlayer->pause();
break;
@@ -420,7 +422,6 @@
Mutex::Autolock autoLock(mLock);
if (rate.mSpeed == 0.f && mState == STATE_RUNNING) {
mState = STATE_PAUSED;
- notifyListener_l(MEDIA2_PAUSED);
} else if (rate.mSpeed != 0.f
&& (mState == STATE_PAUSED
|| mState == STATE_STOPPED_AND_PREPARED
@@ -457,8 +458,6 @@
{
mAtEOS = false;
mSeekInProgress = true;
- // seeks can take a while, so we essentially paused
- notifyListener_l(MEDIA2_PAUSED);
mPlayer->seekToAsync(seekTimeUs, mode, true /* needNotify */);
break;
}
@@ -629,7 +628,7 @@
{
CHECK(mIsAsyncPrepare);
- notifyListener_l(MEDIA2_PREPARED);
+ notifyListener_l(mSrcId, MEDIA2_PREPARED);
break;
}
@@ -638,7 +637,7 @@
}
if (mState != STATE_STOPPED) {
- notifyListener_l(MEDIA2_STOPPED);
+ // notifyListener_l(MEDIA2_STOPPED);
}
mState = STATE_RESET_IN_PROGRESS;
@@ -772,7 +771,7 @@
return OK;
}
-void NuPlayer2Driver::notifyResetComplete() {
+void NuPlayer2Driver::notifyResetComplete(int64_t /* srcId */) {
ALOGD("notifyResetComplete(%p)", this);
Mutex::Autolock autoLock(mLock);
@@ -781,7 +780,7 @@
mCondition.broadcast();
}
-void NuPlayer2Driver::notifySetSurfaceComplete() {
+void NuPlayer2Driver::notifySetSurfaceComplete(int64_t /* srcId */) {
ALOGV("notifySetSurfaceComplete(%p)", this);
Mutex::Autolock autoLock(mLock);
@@ -791,35 +790,35 @@
mCondition.broadcast();
}
-void NuPlayer2Driver::notifyDuration(int64_t durationUs) {
+void NuPlayer2Driver::notifyDuration(int64_t /* srcId */, int64_t durationUs) {
Mutex::Autolock autoLock(mLock);
mDurationUs = durationUs;
}
-void NuPlayer2Driver::notifyMorePlayingTimeUs(int64_t playingUs) {
+void NuPlayer2Driver::notifyMorePlayingTimeUs(int64_t /* srcId */, int64_t playingUs) {
Mutex::Autolock autoLock(mLock);
mPlayingTimeUs += playingUs;
}
-void NuPlayer2Driver::notifyMoreRebufferingTimeUs(int64_t rebufferingUs) {
+void NuPlayer2Driver::notifyMoreRebufferingTimeUs(int64_t /* srcId */, int64_t rebufferingUs) {
Mutex::Autolock autoLock(mLock);
mRebufferingTimeUs += rebufferingUs;
mRebufferingEvents++;
}
-void NuPlayer2Driver::notifyRebufferingWhenExit(bool status) {
+void NuPlayer2Driver::notifyRebufferingWhenExit(int64_t /* srcId */, bool status) {
Mutex::Autolock autoLock(mLock);
mRebufferingAtExit = status;
}
-void NuPlayer2Driver::notifySeekComplete() {
+void NuPlayer2Driver::notifySeekComplete(int64_t srcId) {
ALOGV("notifySeekComplete(%p)", this);
Mutex::Autolock autoLock(mLock);
mSeekInProgress = false;
- notifySeekComplete_l();
+ notifySeekComplete_l(srcId);
}
-void NuPlayer2Driver::notifySeekComplete_l() {
+void NuPlayer2Driver::notifySeekComplete_l(int64_t srcId) {
bool wasSeeking = true;
if (mState == STATE_STOPPED_AND_PREPARING) {
wasSeeking = false;
@@ -833,7 +832,7 @@
// no need to notify listener
return;
}
- notifyListener_l(wasSeeking ? MEDIA2_SEEK_COMPLETE : MEDIA2_PREPARED);
+ notifyListener_l(srcId, wasSeeking ? MEDIA2_SEEK_COMPLETE : MEDIA2_PREPARED);
}
status_t NuPlayer2Driver::dump(
@@ -916,9 +915,11 @@
void NuPlayer2Driver::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatNotifyListener: {
+ int64_t srcId;
int32_t msgId;
int32_t ext1 = 0;
int32_t ext2 = 0;
+ CHECK(msg->findInt64("srcId", &srcId));
CHECK(msg->findInt32("messageId", &msgId));
msg->findInt32("ext1", &ext1);
msg->findInt32("ext2", &ext2);
@@ -927,7 +928,7 @@
if (msg->findObject("parcel", &obj) && obj != NULL) {
in = static_cast<ParcelWrapper *>(obj.get());
}
- sendEvent(msgId, ext1, ext2, (in == NULL ? NULL : in->getParcel()));
+ sendEvent(srcId, msgId, ext1, ext2, (in == NULL ? NULL : in->getParcel()));
break;
}
default:
@@ -936,15 +937,16 @@
}
void NuPlayer2Driver::notifyListener(
- int msg, int ext1, int ext2, const Parcel *in) {
+ int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
Mutex::Autolock autoLock(mLock);
- notifyListener_l(msg, ext1, ext2, in);
+ notifyListener_l(srcId, msg, ext1, ext2, in);
}
void NuPlayer2Driver::notifyListener_l(
- int msg, int ext1, int ext2, const Parcel *in) {
- ALOGD("notifyListener_l(%p), (%d, %d, %d, %d), loop setting(%d, %d)",
- this, msg, ext1, ext2, (in == NULL ? -1 : (int)in->dataSize()), mAutoLoop, mLooping);
+ int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
+ ALOGD("notifyListener_l(%p), (%lld, %d, %d, %d, %d), loop setting(%d, %d)",
+ this, (long long)srcId, msg, ext1, ext2,
+ (in == NULL ? -1 : (int)in->dataSize()), mAutoLoop, mLooping);
switch (msg) {
case MEDIA2_PLAYBACK_COMPLETE:
{
@@ -1000,6 +1002,7 @@
}
sp<AMessage> notify = new AMessage(kWhatNotifyListener, this);
+ notify->setInt64("srcId", srcId);
notify->setInt32("messageId", msg);
notify->setInt32("ext1", ext1);
notify->setInt32("ext2", ext2);
@@ -1007,7 +1010,7 @@
notify->post();
}
-void NuPlayer2Driver::notifySetDataSourceCompleted(status_t err) {
+void NuPlayer2Driver::notifySetDataSourceCompleted(int64_t /* srcId */, status_t err) {
Mutex::Autolock autoLock(mLock);
CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING);
@@ -1017,7 +1020,7 @@
mCondition.broadcast();
}
-void NuPlayer2Driver::notifyPrepareCompleted(status_t err) {
+void NuPlayer2Driver::notifyPrepareCompleted(int64_t srcId, status_t err) {
ALOGV("notifyPrepareCompleted %d", err);
Mutex::Autolock autoLock(mLock);
@@ -1039,12 +1042,12 @@
// in response, NuPlayer2Driver has the right state
mState = STATE_PREPARED;
if (mIsAsyncPrepare) {
- notifyListener_l(MEDIA2_PREPARED);
+ notifyListener_l(srcId, MEDIA2_PREPARED);
}
} else {
mState = STATE_UNPREPARED;
if (mIsAsyncPrepare) {
- notifyListener_l(MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
+ notifyListener_l(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
}
}
@@ -1058,7 +1061,7 @@
mCondition.broadcast();
}
-void NuPlayer2Driver::notifyFlagsChanged(uint32_t flags) {
+void NuPlayer2Driver::notifyFlagsChanged(int64_t /* srcId */, uint32_t flags) {
Mutex::Autolock autoLock(mLock);
mPlayerFlags = flags;
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
index 087d2c2..4c57cfd 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
@@ -67,18 +67,19 @@
virtual void onMessageReceived(const sp<AMessage> &msg) override;
- void notifySetDataSourceCompleted(status_t err);
- void notifyPrepareCompleted(status_t err);
- void notifyResetComplete();
- void notifySetSurfaceComplete();
- void notifyDuration(int64_t durationUs);
- void notifyMorePlayingTimeUs(int64_t timeUs);
- void notifyMoreRebufferingTimeUs(int64_t timeUs);
- void notifyRebufferingWhenExit(bool status);
- void notifySeekComplete();
- void notifySeekComplete_l();
- void notifyListener(int msg, int ext1 = 0, int ext2 = 0, const Parcel *in = NULL);
- void notifyFlagsChanged(uint32_t flags);
+ void notifySetDataSourceCompleted(int64_t srcId, status_t err);
+ void notifyPrepareCompleted(int64_t srcId, status_t err);
+ void notifyResetComplete(int64_t srcId);
+ void notifySetSurfaceComplete(int64_t srcId);
+ void notifyDuration(int64_t srcId, int64_t durationUs);
+ void notifyMorePlayingTimeUs(int64_t srcId, int64_t timeUs);
+ void notifyMoreRebufferingTimeUs(int64_t srcId, int64_t timeUs);
+ void notifyRebufferingWhenExit(int64_t srcId, bool status);
+ void notifySeekComplete(int64_t srcId);
+ void notifySeekComplete_l(int64_t srcId);
+ void notifyListener(int64_t srcId, int msg, int ext1 = 0, int ext2 = 0,
+ const Parcel *in = NULL);
+ void notifyFlagsChanged(int64_t srcId, uint32_t flags);
// Modular DRM
virtual status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId);
@@ -118,6 +119,7 @@
// The following are protected through "mLock"
// >>>
+ int64_t mSrcId;
bool mSetSurfaceInProgress;
int64_t mDurationUs;
int64_t mPositionUs;
@@ -147,7 +149,8 @@
status_t prepare_l();
status_t start_l();
- void notifyListener_l(int msg, int ext1 = 0, int ext2 = 0, const Parcel *in = NULL);
+ void notifyListener_l(int64_t srcId, int msg, int ext1 = 0, int ext2 = 0,
+ const Parcel *in = NULL);
DISALLOW_EVIL_CONSTRUCTORS(NuPlayer2Driver);
};
diff --git a/media/libmediaplayerservice/tests/Android.bp b/media/libmediaplayerservice/tests/Android.bp
index d6c1d27..e936bdd 100644
--- a/media/libmediaplayerservice/tests/Android.bp
+++ b/media/libmediaplayerservice/tests/Android.bp
@@ -12,6 +12,7 @@
"libmediadrm",
"libutils",
"android.hardware.drm@1.0",
+ "android.hardware.drm@1.1",
],
compile_multilib: "32",
diff --git a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
index c5212fc..2c6a41b 100644
--- a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
+++ b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
@@ -20,7 +20,7 @@
#include <gtest/gtest.h>
-#include <media/Drm.h>
+#include <media/DrmHal.h>
#include <media/DrmSessionClientInterface.h>
#include <media/DrmSessionManager.h>
#include <media/stagefright/foundation/ADebug.h>
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 08b2775..905817f 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -60,6 +60,7 @@
"AudioPresentationInfo.cpp",
"AudioSource.cpp",
"BufferImpl.cpp",
+ "C2OMXNode.cpp",
"CCodec.cpp",
"CCodecBufferChannel.cpp",
"CodecBase.cpp",
@@ -130,7 +131,6 @@
"libui",
"libutils",
"libmedia_helper",
- "libstagefright_bufferqueue_helper",
"libstagefright_codec2",
"libstagefright_codec2_vndk",
"libstagefright_foundation",
@@ -149,6 +149,10 @@
"android.hardware.media.omx@1.0",
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.mapper@2.0",
+
+ // TODO: do not link directly with impl
+ "android.hardware.media.c2@1.0-service-impl",
+ "libstagefright_bufferqueue_helper",
],
static_libs: [
@@ -215,6 +219,7 @@
"InterfaceUtils.cpp",
"MediaClock.cpp",
"MediaExtractorFactory.cpp",
+ "NdkUtils.cpp",
"NuCachedSource2.cpp",
"RemoteMediaExtractor.cpp",
"RemoteMediaSource.cpp",
diff --git a/media/libstagefright/C2OMXNode.cpp b/media/libstagefright/C2OMXNode.cpp
new file mode 100644
index 0000000..e6f81af
--- /dev/null
+++ b/media/libstagefright/C2OMXNode.cpp
@@ -0,0 +1,281 @@
+/*
+ * 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.
+ */
+
+#ifdef __LP64__
+#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
+#endif
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2OMXNode"
+#include <log/log.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2BlockInternal.h>
+#include <C2Component.h>
+#include <C2PlatformSupport.h>
+
+#include <OMX_Component.h>
+#include <OMX_Index.h>
+#include <OMX_IndexExt.h>
+
+#include <media/stagefright/omx/OMXUtils.h>
+#include <media/stagefright/MediaErrors.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+
+#include "include/C2OMXNode.h"
+
+namespace android {
+
+namespace {
+
+class Buffer2D : public C2Buffer {
+public:
+ explicit Buffer2D(C2ConstGraphicBlock block) : C2Buffer({ block }) {}
+};
+
+} // namespace
+
+C2OMXNode::C2OMXNode(const std::shared_ptr<C2Component> &comp) : mComp(comp) {}
+
+status_t C2OMXNode::freeNode() {
+ mComp.reset();
+ return OK;
+}
+
+status_t C2OMXNode::sendCommand(OMX_COMMANDTYPE cmd, OMX_S32 param) {
+ (void)cmd;
+ (void)param;
+ return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::getParameter(OMX_INDEXTYPE index, void *params, size_t size) {
+ status_t err = ERROR_UNSUPPORTED;
+ switch ((uint32_t)index) {
+ case OMX_IndexParamConsumerUsageBits: {
+ // TODO: read from intf()
+ OMX_U32 *usage = (OMX_U32 *)params;
+ *usage = GRALLOC_USAGE_SW_READ_OFTEN;
+ err = OK;
+ break;
+ }
+ case OMX_IndexParamPortDefinition: {
+ if (size < sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) {
+ return BAD_VALUE;
+ }
+ OMX_PARAM_PORTDEFINITIONTYPE *pDef = (OMX_PARAM_PORTDEFINITIONTYPE *)params;
+ // TODO: read these from intf()
+ pDef->nBufferCountActual = 16;
+ pDef->eDomain = OMX_PortDomainVideo;
+ pDef->format.video.nFrameWidth = 1080;
+ pDef->format.video.nFrameHeight = 1920;
+ err = OK;
+ break;
+ }
+ default:
+ break;
+ }
+ return err;
+}
+
+status_t C2OMXNode::setParameter(OMX_INDEXTYPE index, const void *params, size_t size) {
+ (void)index;
+ (void)params;
+ (void)size;
+ return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::getConfig(OMX_INDEXTYPE index, void *config, size_t size) {
+ (void)index;
+ (void)config;
+ (void)size;
+ return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::setConfig(OMX_INDEXTYPE index, const void *config, size_t size) {
+ (void)index;
+ (void)config;
+ (void)size;
+ return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::setPortMode(OMX_U32 portIndex, IOMX::PortMode mode) {
+ (void)portIndex;
+ (void)mode;
+ return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::prepareForAdaptivePlayback(
+ OMX_U32 portIndex, OMX_BOOL enable,
+ OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) {
+ (void)portIndex;
+ (void)enable;
+ (void)maxFrameWidth;
+ (void)maxFrameHeight;
+ return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::configureVideoTunnelMode(
+ OMX_U32 portIndex, OMX_BOOL tunneled,
+ OMX_U32 audioHwSync, native_handle_t **sidebandHandle) {
+ (void)portIndex;
+ (void)tunneled;
+ (void)audioHwSync;
+ *sidebandHandle = nullptr;
+ return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::getGraphicBufferUsage(OMX_U32 portIndex, OMX_U32* usage) {
+ (void)portIndex;
+ *usage = 0;
+ return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::setInputSurface(const sp<IOMXBufferSource> &bufferSource) {
+ c2_status_t err = GetCodec2PlatformAllocatorStore()->fetchAllocator(
+ C2PlatformAllocatorStore::GRALLOC,
+ &mAllocator);
+ if (err != OK) {
+ return UNKNOWN_ERROR;
+ }
+ mBufferSource = bufferSource;
+ return OK;
+}
+
+status_t C2OMXNode::allocateSecureBuffer(
+ OMX_U32 portIndex, size_t size, buffer_id *buffer,
+ void **bufferData, sp<NativeHandle> *nativeHandle) {
+ (void)portIndex;
+ (void)size;
+ (void)nativeHandle;
+ *buffer = 0;
+ *bufferData = nullptr;
+ return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::useBuffer(
+ OMX_U32 portIndex, const OMXBuffer &omxBuf, buffer_id *buffer) {
+ (void)portIndex;
+ (void)omxBuf;
+ *buffer = 0;
+ return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::freeBuffer(OMX_U32 portIndex, buffer_id buffer) {
+ (void)portIndex;
+ (void)buffer;
+ return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::fillBuffer(
+ buffer_id buffer, const OMXBuffer &omxBuf, int fenceFd) {
+ (void)buffer;
+ (void)omxBuf;
+ (void)fenceFd;
+ return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::emptyBuffer(
+ buffer_id buffer, const OMXBuffer &omxBuf,
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
+ // TODO: better fence handling
+ if (fenceFd >= 0) {
+ sp<Fence> fence = new Fence(fenceFd);
+ fence->waitForever(LOG_TAG);
+ }
+ std::shared_ptr<C2Component> comp = mComp.lock();
+ if (!comp) {
+ return NO_INIT;
+ }
+
+ uint32_t c2Flags = 0;
+ std::shared_ptr<C2GraphicBlock> block;
+
+ C2Handle *handle = nullptr;
+ if (omxBuf.mBufferType == OMXBuffer::kBufferTypeANWBuffer) {
+ std::shared_ptr<C2GraphicAllocation> alloc;
+ handle = WrapNativeCodec2GrallocHandle(
+ native_handle_clone(omxBuf.mGraphicBuffer->handle),
+ omxBuf.mGraphicBuffer->width,
+ omxBuf.mGraphicBuffer->height,
+ omxBuf.mGraphicBuffer->format,
+ omxBuf.mGraphicBuffer->usage,
+ omxBuf.mGraphicBuffer->stride);
+ c2_status_t err = mAllocator->priorGraphicAllocation(handle, &alloc);
+ if (err != OK) {
+ return UNKNOWN_ERROR;
+ }
+ block = _C2BlockFactory::CreateGraphicBlock(alloc);
+ } else if (flags & OMX_BUFFERFLAG_EOS) {
+ c2Flags = C2FrameData::FLAG_END_OF_STREAM;
+ } else {
+ return BAD_VALUE;
+ }
+
+ std::unique_ptr<C2Work> work(new C2Work);
+ work->input.flags = (C2FrameData::flags_t)c2Flags;
+ work->input.ordinal.timestamp = timestamp;
+ work->input.ordinal.frameIndex = mFrameIndex++;
+ work->input.buffers.clear();
+ if (block) {
+ std::shared_ptr<C2Buffer> c2Buffer(
+ // TODO: fence
+ new Buffer2D(block->share(
+ C2Rect(block->width(), block->height()), ::android::C2Fence())),
+ [handle, buffer, source = getSource()](C2Buffer *ptr) {
+ delete ptr;
+ native_handle_delete(handle);
+ // TODO: fence
+ (void)source->onInputBufferEmptied(buffer, -1);
+ });
+ work->input.buffers.push_back(c2Buffer);
+ }
+ work->worklets.clear();
+ work->worklets.emplace_back(new C2Worklet);
+ std::list<std::unique_ptr<C2Work>> items;
+ items.push_back(std::move(work));
+
+ c2_status_t err = comp->queue_nb(&items);
+ if (err != C2_OK) {
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+status_t C2OMXNode::getExtensionIndex(
+ const char *parameterName, OMX_INDEXTYPE *index) {
+ (void)parameterName;
+ *index = OMX_IndexMax;
+ return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::dispatchMessage(const omx_message& msg) {
+ if (msg.type != omx_message::EVENT) {
+ return ERROR_UNSUPPORTED;
+ }
+ if (msg.u.event_data.event != OMX_EventDataSpaceChanged) {
+ return ERROR_UNSUPPORTED;
+ }
+ // TODO: fill intf() with info inside |msg|.
+ return OK;
+}
+
+sp<IOMXBufferSource> C2OMXNode::getSource() {
+ return mBufferSource;
+}
+
+} // namespace android
diff --git a/media/libstagefright/CCodec.cpp b/media/libstagefright/CCodec.cpp
index 84e98f8..bb70458 100644
--- a/media/libstagefright/CCodec.cpp
+++ b/media/libstagefright/CCodec.cpp
@@ -24,17 +24,23 @@
#include <C2PlatformSupport.h>
#include <C2V4l2Support.h>
+#include <android/IOMXBufferSource.h>
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
#include <gui/Surface.h>
+#include <media/stagefright/codec2/1.0/InputSurface.h>
#include <media/stagefright/BufferProducerWrapper.h>
#include <media/stagefright/CCodec.h>
#include <media/stagefright/PersistentSurface.h>
+#include "include/C2OMXNode.h"
#include "include/CCodecBufferChannel.h"
-
-using namespace std::chrono_literals;
+#include "include/InputSurfaceWrapper.h"
namespace android {
+using namespace std::chrono_literals;
+using ::android::hardware::graphics::bufferqueue::V1_0::utils::H2BGraphicBufferProducer;
+
namespace {
class CCodecWatchdog : public AHandler {
@@ -146,6 +152,76 @@
wp<CCodec> mCodec;
};
+class C2InputSurfaceWrapper : public InputSurfaceWrapper {
+public:
+ explicit C2InputSurfaceWrapper(const sp<InputSurface> &surface) : mSurface(surface) {}
+ ~C2InputSurfaceWrapper() override = default;
+
+ status_t connect(const std::shared_ptr<C2Component> &comp) override {
+ if (mConnection != nullptr) {
+ return ALREADY_EXISTS;
+ }
+ mConnection = mSurface->connectToComponent(comp);
+ return OK;
+ }
+
+ void disconnect() override {
+ if (mConnection != nullptr) {
+ mConnection->disconnect();
+ mConnection.clear();
+ }
+ }
+
+private:
+ sp<InputSurface> mSurface;
+ sp<InputSurfaceConnection> mConnection;
+};
+
+class GraphicBufferSourceWrapper : public InputSurfaceWrapper {
+public:
+ explicit GraphicBufferSourceWrapper(const sp<IGraphicBufferSource> &source) : mSource(source) {}
+ ~GraphicBufferSourceWrapper() override = default;
+
+ status_t connect(const std::shared_ptr<C2Component> &comp) override {
+ // TODO: proper color aspect & dataspace
+ android_dataspace dataSpace = HAL_DATASPACE_BT709;
+
+ mNode = new C2OMXNode(comp);
+ mSource->configure(mNode, dataSpace);
+
+ // TODO: configure according to intf().
+
+ sp<IOMXBufferSource> source = mNode->getSource();
+ if (source == nullptr) {
+ return NO_INIT;
+ }
+ constexpr size_t kNumSlots = 16;
+ for (size_t i = 0; i < kNumSlots; ++i) {
+ source->onInputBufferAdded(i);
+ }
+ source->onOmxExecuting();
+ return OK;
+ }
+
+ void disconnect() override {
+ if (mNode == nullptr) {
+ return;
+ }
+ sp<IOMXBufferSource> source = mNode->getSource();
+ if (source == nullptr) {
+ ALOGD("GBSWrapper::disconnect: node is not configured with OMXBufferSource.");
+ return;
+ }
+ source->onOmxIdle();
+ source->onOmxLoaded();
+ mNode.clear();
+ }
+
+private:
+ sp<IGraphicBufferSource> mSource;
+ sp<C2OMXNode> mNode;
+};
+
} // namespace
CCodec::CCodec()
@@ -300,18 +376,18 @@
}
void CCodec::createInputSurface() {
- sp<IGraphicBufferProducer> producer;
- sp<GraphicBufferSource> source(new GraphicBufferSource);
+ // TODO: get this from codec process
+ sp<InputSurface> surface(InputSurface::Create());
- status_t err = source->initCheck();
+ // TODO: get proper error code.
+ status_t err = (surface == nullptr) ? UNKNOWN_ERROR : OK;
if (err != OK) {
- ALOGE("Failed to initialize graphic buffer source: %d", err);
+ ALOGE("Failed to initialize input surface: %d", err);
mCallback->onInputSurfaceCreationFailed(err);
return;
}
- producer = source->getIGraphicBufferProducer();
- err = setupInputSurface(source);
+ err = setupInputSurface(std::make_shared<C2InputSurfaceWrapper>(surface));
if (err != OK) {
ALOGE("Failed to set up input surface: %d", err);
mCallback->onInputSurfaceCreationFailed(err);
@@ -328,16 +404,16 @@
mCallback->onInputSurfaceCreated(
inputFormat,
outputFormat,
- new BufferProducerWrapper(producer));
+ new BufferProducerWrapper(new H2BGraphicBufferProducer(surface)));
}
-status_t CCodec::setupInputSurface(const sp<GraphicBufferSource> &source) {
- status_t err = mChannel->setGraphicBufferSource(source);
+status_t CCodec::setupInputSurface(const std::shared_ptr<InputSurfaceWrapper> &surface) {
+ status_t err = mChannel->setInputSurface(surface);
if (err != OK) {
return err;
}
- // TODO: configure |source| with other settings.
+ // TODO: configure |surface| with other settings.
return OK;
}
@@ -348,10 +424,22 @@
}
void CCodec::setInputSurface(const sp<PersistentSurface> &surface) {
- // TODO
- (void)surface;
+ status_t err = setupInputSurface(std::make_shared<GraphicBufferSourceWrapper>(
+ surface->getBufferSource()));
+ if (err != OK) {
+ ALOGE("Failed to set up input surface: %d", err);
+ mCallback->onInputSurfaceDeclined(err);
+ return;
+ }
- mCallback->onInputSurfaceDeclined(ERROR_UNSUPPORTED);
+ sp<AMessage> inputFormat;
+ sp<AMessage> outputFormat;
+ {
+ Mutexed<Formats>::Locked formats(mFormats);
+ inputFormat = formats->inputFormat;
+ outputFormat = formats->outputFormat;
+ }
+ mCallback->onInputSurfaceAccepted(inputFormat, outputFormat);
}
void CCodec::initiateStart() {
diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp
index 27060e1..6fba890 100644
--- a/media/libstagefright/CCodecBufferChannel.cpp
+++ b/media/libstagefright/CCodecBufferChannel.cpp
@@ -101,15 +101,6 @@
* Release the buffer obtained from requestNewBuffer() and get the
* associated C2Buffer object back. Returns empty shared_ptr if the
* buffer is not on file.
- *
- * XXX: this is a quick hack to be removed
- */
- virtual std::shared_ptr<C2Buffer> releaseBufferIndex(size_t /* index */) { return nullptr; }
-
- /**
- * Release the buffer obtained from requestNewBuffer() and get the
- * associated C2Buffer object back. Returns empty shared_ptr if the
- * buffer is not on file.
*/
virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) = 0;
@@ -134,47 +125,6 @@
DISALLOW_EVIL_CONSTRUCTORS(InputBuffers);
};
-class CCodecBufferChannel::InputBufferClient {
-public:
- explicit InputBufferClient(
- const std::shared_ptr<CCodecBufferChannel> &channel) : mChannel(channel) {}
- virtual ~InputBufferClient() = default;
-
- virtual void onInputBufferAdded(size_t index, const sp<MediaCodecBuffer> &buffer) {
- std::shared_ptr<CCodecBufferChannel> channel = mChannel.lock();
- if (!channel) {
- return;
- }
- channel->mCallback->onInputBufferAvailable(index, buffer);
- }
-
- virtual void onStart() {
- // no-op
- }
-
- virtual void onStop() {
- // no-op
- }
-
- virtual void onRelease() {
- // no-op
- }
-
- virtual void onInputBufferAvailable(size_t index, const sp<MediaCodecBuffer> &buffer) {
- std::shared_ptr<CCodecBufferChannel> channel = mChannel.lock();
- if (!channel) {
- return;
- }
- channel->mCallback->onInputBufferAvailable(index, buffer);
- }
-
-protected:
- InputBufferClient() = default;
- std::weak_ptr<CCodecBufferChannel> mChannel;
-
- DISALLOW_EVIL_CONSTRUCTORS(InputBufferClient);
-};
-
class CCodecBufferChannel::OutputBuffers : public CCodecBufferChannel::Buffers {
public:
OutputBuffers() = default;
@@ -227,8 +177,6 @@
namespace {
-constexpr int32_t kMaskI32 = ~0;
-
// TODO: get this info from component
const static size_t kMinBufferArraySize = 16;
const static size_t kLinearBufferSize = 524288;
@@ -422,23 +370,8 @@
public:
GraphicInputBuffers() = default;
- bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
- *buffer = nullptr;
- for (size_t i = 0; i < mAvailable.size(); ++i) {
- if (mAvailable[i]) {
- *index = i;
- mAvailable[i] = false;
- return true;
- }
- }
- *index = mAvailable.size();
- mAvailable.push_back(false);
- return true;
- }
-
- std::shared_ptr<C2Buffer> releaseBufferIndex(size_t index) override {
- mAvailable[index] = true;
- return nullptr;
+ bool requestNewBuffer(size_t *, sp<MediaCodecBuffer> *) override {
+ return false;
}
std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &) override {
@@ -452,8 +385,11 @@
return nullptr;
}
-private:
- std::vector<bool> mAvailable;
+ bool isArrayMode() const final { return true; }
+
+ void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
+ array->clear();
+ }
};
class OutputBuffersArray : public CCodecBufferChannel::OutputBuffers {
@@ -753,164 +689,8 @@
}
};
-class BufferQueueClient : public CCodecBufferChannel::InputBufferClient {
-public:
- explicit BufferQueueClient(const sp<GraphicBufferSource> &source) : mSource(source) {}
- virtual ~BufferQueueClient() = default;
-
- void onInputBufferAdded(size_t index, const sp<MediaCodecBuffer> &buffer) override {
- (void)buffer;
- mSource->onInputBufferAdded(index & kMaskI32);
- }
-
- void onStart() override {
- mSource->start();
- }
-
- void onStop() override {
- mSource->stop();
- }
-
- void onRelease() override {
- mSource->release();
- }
-
- void onInputBufferAvailable(size_t index, const sp<MediaCodecBuffer> &buffer) override {
- ALOGV("onInputBufferEmptied index = %zu", index);
- (void)buffer;
- // TODO: can we really ignore fence here?
- mSource->onInputBufferEmptied(index & kMaskI32, -1 /* fenceFd */);
- }
-
-private:
- sp<GraphicBufferSource> mSource;
-};
-
-class GraphicBlock : public C2GraphicBlock {
- using C2GraphicBlock::C2GraphicBlock;
- friend class ::android::CCodecBufferChannel;
-};
-
} // namespace
-class CCodecBufferChannel::C2ComponentWrapper : public ComponentWrapper {
-public:
- explicit C2ComponentWrapper(
- const std::shared_ptr<CCodecBufferChannel> &channel)
- : mChannel(channel), mLastTimestamp(0) {}
-
- virtual ~C2ComponentWrapper() {
- for (const std::pair<int32_t, C2Handle *> &entry : mHandles) {
- native_handle_delete(entry.second);
- }
- }
-
- status_t submitBuffer(
- int32_t bufferId, const sp<GraphicBuffer> &buffer,
- int64_t timestamp, int fenceFd) override {
- ALOGV("submitBuffer bufferId = %d", bufferId);
- // TODO: Use fd to construct fence
- (void)fenceFd;
-
- std::shared_ptr<CCodecBufferChannel> channel = mChannel.lock();
- if (!channel) {
- return NO_INIT;
- }
-
- std::shared_ptr<C2Allocator> allocator = mAllocator.lock();
- if (!allocator) {
- c2_status_t err = GetCodec2PlatformAllocatorStore()->fetchAllocator(
- C2PlatformAllocatorStore::GRALLOC,
- &allocator);
- if (err != OK) {
- return UNKNOWN_ERROR;
- }
- mAllocator = allocator;
- }
-
- std::shared_ptr<C2GraphicAllocation> alloc;
- C2Handle *handle = WrapNativeCodec2GrallocHandle(
- buffer->handle, buffer->width, buffer->height,
- buffer->format, buffer->usage, buffer->stride);
- c2_status_t err = allocator->priorGraphicAllocation(handle, &alloc);
- if (err != OK) {
- return UNKNOWN_ERROR;
- }
-
- std::shared_ptr<C2GraphicBlock> block = _C2BlockFactory::CreateGraphicBlock(alloc);
-
- std::unique_ptr<C2Work> work(new C2Work);
- work->input.flags = (C2FrameData::flags_t)0;
- work->input.ordinal.timestamp = timestamp;
- work->input.ordinal.frameIndex = channel->mFrameIndex++;
- work->input.buffers.clear();
- work->input.buffers.emplace_back(new Buffer2D(
- // TODO: fence
- block->share(C2Rect(block->width(), block->height()), ::android::C2Fence())));
- work->worklets.clear();
- work->worklets.emplace_back(new C2Worklet);
- std::list<std::unique_ptr<C2Work>> items;
- items.push_back(std::move(work));
-
- err = channel->mComponent->queue_nb(&items);
- if (err != OK) {
- native_handle_delete(handle);
- return UNKNOWN_ERROR;
- }
-
- mLastTimestamp = timestamp;
- if (mHandles.count(bufferId) > 0) {
- native_handle_delete(mHandles[bufferId]);
- }
- mHandles[bufferId] = handle;
-
- Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(channel->mInputBuffers);
- ALOGV("releaseBufferIndex index = %d", bufferId);
- (*buffers)->releaseBufferIndex(bufferId);
-
- return OK;
- }
-
- status_t submitEos(int32_t bufferId) override {
- std::shared_ptr<CCodecBufferChannel> channel = mChannel.lock();
- if (!channel) {
- return NO_INIT;
- }
-
- std::unique_ptr<C2Work> work(new C2Work);
- work->input.flags = C2FrameData::FLAG_END_OF_STREAM;
- work->input.ordinal.timestamp = mLastTimestamp;
- work->input.ordinal.frameIndex = channel->mFrameIndex++;
- work->input.buffers.clear();
- work->input.buffers.push_back(nullptr);
- work->worklets.clear();
- work->worklets.emplace_back(new C2Worklet);
- std::list<std::unique_ptr<C2Work>> items;
- items.push_back(std::move(work));
-
- c2_status_t err = channel->mComponent->queue_nb(&items);
-
- Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(channel->mInputBuffers);
- (*buffers)->releaseBufferIndex(bufferId);
-
- return (err == C2_OK) ? OK : UNKNOWN_ERROR;
- }
-
- void dispatchDataSpaceChanged(
- int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override {
- // TODO
- (void)dataSpace;
- (void)aspects;
- (void)pixelFormat;
- }
-
-private:
- std::weak_ptr<CCodecBufferChannel> mChannel;
- std::map<int32_t, C2Handle *> mHandles;
- int64_t mLastTimestamp;
- std::weak_ptr<C2Allocator> mAllocator;
-};
-
CCodecBufferChannel::QueueGuard::QueueGuard(
CCodecBufferChannel::QueueSync &sync) : mSync(sync) {
std::unique_lock<std::mutex> l(mSync.mMutex);
@@ -967,13 +747,10 @@
if (mCrypto != nullptr && mDealer != nullptr && mHeapSeqNum >= 0) {
mCrypto->unsetHeap(mHeapSeqNum);
}
- // TODO: is this the right place?
- mInputClient->onRelease();
}
void CCodecBufferChannel::setComponent(const std::shared_ptr<C2Component> &component) {
mComponent = component;
- mInputClient.reset(new InputBufferClient(shared_from_this()));
C2StreamFormatConfig::input inputFormat(0u);
C2StreamFormatConfig::output outputFormat(0u);
@@ -1026,16 +803,11 @@
}
}
-status_t CCodecBufferChannel::setGraphicBufferSource(
- const sp<GraphicBufferSource> &source) {
- ALOGV("setGraphicBufferSource");
- mInputClient.reset(new BufferQueueClient(source));
-
- // TODO: proper color aspect & dataspace
- android_dataspace dataSpace = HAL_DATASPACE_BT709;
- // TODO: read settings properly from the interface
- return source->configure(new C2ComponentWrapper(
- shared_from_this()), dataSpace, 16, 1080, 1920, GRALLOC_USAGE_SW_READ_OFTEN);
+status_t CCodecBufferChannel::setInputSurface(
+ const std::shared_ptr<InputSurfaceWrapper> &surface) {
+ ALOGV("setInputSurface");
+ mInputSurface = surface;
+ return OK;
}
status_t CCodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) {
@@ -1100,12 +872,13 @@
{
Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
if (!(*buffers)->requestNewBuffer(&index, &inBuffer)) {
- ALOGW("no new buffer available");
+ ALOGV("no new buffer available");
inBuffer = nullptr;
+ return;
}
}
ALOGV("new input index = %zu", index);
- mInputClient->onInputBufferAvailable(index, inBuffer);
+ mCallback->onInputBufferAvailable(index, inBuffer);
}
status_t CCodecBufferChannel::renderOutputBuffer(
@@ -1182,7 +955,9 @@
{
Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
if((*buffers)->releaseBuffer(buffer)) {
+ buffers.unlock();
feedInputBufferIfAvailable();
+ buffers.lock();
}
}
return OK;
@@ -1221,29 +996,38 @@
}
mSync.start();
- // TODO: use proper buffer depth instead of this random value
- for (size_t i = 0; i < kMinBufferArraySize; ++i) {
- size_t index;
- sp<MediaCodecBuffer> buffer;
- {
- Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
- if (!(*buffers)->requestNewBuffer(&index, &buffer)) {
- buffers.unlock();
- ALOGE("start: cannot allocate memory");
- mOnError(NO_MEMORY, ACTION_CODE_FATAL);
- buffers.lock();
- return;
+ if (mInputSurface == nullptr) {
+ // TODO: use proper buffer depth instead of this random value
+ for (size_t i = 0; i < kMinBufferArraySize; ++i) {
+ size_t index;
+ sp<MediaCodecBuffer> buffer;
+ {
+ Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+ if (!(*buffers)->requestNewBuffer(&index, &buffer)) {
+ if (i == 0) {
+ ALOGE("start: cannot allocate memory at all");
+ buffers.unlock();
+ mOnError(NO_MEMORY, ACTION_CODE_FATAL);
+ buffers.lock();
+ } else {
+ ALOGV("start: cannot allocate memory, only %zu buffers allocated", i);
+ }
+ break;
+ }
}
+ mCallback->onInputBufferAvailable(index, buffer);
}
- mInputClient->onInputBufferAdded(index, buffer);
+ } else {
+ (void)mInputSurface->connect(mComponent);
}
- mInputClient->onStart();
}
void CCodecBufferChannel::stop() {
mSync.stop();
mFirstValidFrameIndex = mFrameIndex.load();
- mInputClient->onStop();
+ if (mInputSurface != nullptr) {
+ mInputSurface->disconnect();
+ }
}
void CCodecBufferChannel::flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) {
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index db07afe..2b33708 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -2955,8 +2955,8 @@
if (mSoftRenderer != NULL) {
std::list<FrameRenderTracker::Info> doneFrames = mSoftRenderer->render(
- buffer->data(), buffer->size(),
- mediaTimeUs, renderTimeNs, NULL, buffer->format());
+ buffer->data(), buffer->size(), mediaTimeUs, renderTimeNs,
+ mPortBuffers[kPortIndexOutput].size(), buffer->format());
// if we are running, notify rendered frames
if (!doneFrames.empty() && mState == STARTED && mOnFrameRenderedNotification != NULL) {
diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp
index bb72167..543c274 100644
--- a/media/libstagefright/MediaExtractorFactory.cpp
+++ b/media/libstagefright/MediaExtractorFactory.cpp
@@ -112,23 +112,25 @@
// initialize source decryption if needed
source->DrmInitialization(nullptr /* mime */);
- sp<AMessage> meta;
-
+ void *meta = nullptr;
MediaExtractor::CreatorFunc creator = NULL;
- String8 tmp;
+ MediaExtractor::FreeMetaFunc freeMeta = nullptr;
float confidence;
sp<ExtractorPlugin> plugin;
- creator = sniff(source.get(), &tmp, &confidence, &meta, plugin);
+ creator = sniff(source.get(), &confidence, &meta, &freeMeta, plugin);
if (!creator) {
ALOGV("FAILED to autodetect media content.");
return NULL;
}
- mime = tmp.string();
- ALOGV("Autodetected media content as '%s' with confidence %.2f",
- mime, confidence);
-
MediaExtractor *ret = creator(source.get(), meta);
+ if (meta != nullptr && freeMeta != nullptr) {
+ freeMeta(meta);
+ }
+
+ ALOGV("Created an extractor '%s' with confidence %.2f",
+ ret != nullptr ? ret->name() : "<null>", confidence);
+
return CreateIMediaExtractorFromMediaExtractor(ret, source, plugin);
}
@@ -165,11 +167,10 @@
// static
MediaExtractor::CreatorFunc MediaExtractorFactory::sniff(
- DataSourceBase *source, String8 *mimeType, float *confidence, sp<AMessage> *meta,
- sp<ExtractorPlugin> &plugin) {
- *mimeType = "";
+ DataSourceBase *source, float *confidence, void **meta,
+ MediaExtractor::FreeMetaFunc *freeMeta, sp<ExtractorPlugin> &plugin) {
*confidence = 0.0f;
- meta->clear();
+ *meta = nullptr;
std::shared_ptr<List<sp<ExtractorPlugin>>> plugins;
{
@@ -183,16 +184,23 @@
MediaExtractor::CreatorFunc curCreator = NULL;
MediaExtractor::CreatorFunc bestCreator = NULL;
for (auto it = plugins->begin(); it != plugins->end(); ++it) {
- String8 newMimeType;
float newConfidence;
- sp<AMessage> newMeta;
- if ((curCreator = (*it)->def.sniff(source, &newMimeType, &newConfidence, &newMeta))) {
+ void *newMeta = nullptr;
+ MediaExtractor::FreeMetaFunc newFreeMeta = nullptr;
+ if ((curCreator = (*it)->def.sniff(source, &newConfidence, &newMeta, &newFreeMeta))) {
if (newConfidence > *confidence) {
- *mimeType = newMimeType;
*confidence = newConfidence;
+ if (*meta != nullptr && *freeMeta != nullptr) {
+ (*freeMeta)(*meta);
+ }
*meta = newMeta;
+ *freeMeta = newFreeMeta;
plugin = *it;
bestCreator = curCreator;
+ } else {
+ if (newMeta != nullptr && newFreeMeta != nullptr) {
+ newFreeMeta(newMeta);
+ }
}
}
}
diff --git a/media/libstagefright/NdkUtils.cpp b/media/libstagefright/NdkUtils.cpp
new file mode 100644
index 0000000..904fe72
--- /dev/null
+++ b/media/libstagefright/NdkUtils.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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
+
+#include <media/stagefright/NdkUtils.h>
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+namespace android {
+
+sp<MetaData> convertMediaFormatWrapperToMetaData(const sp<AMediaFormatWrapper> &fmt) {
+ sp<AMessage> msg = fmt->toAMessage();
+ sp<MetaData> meta = new MetaData;
+ convertMessageToMetaData(msg, meta);
+ return meta;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/bqhelper/Android.bp b/media/libstagefright/bqhelper/Android.bp
index b5b4a2a..388ed6b 100644
--- a/media/libstagefright/bqhelper/Android.bp
+++ b/media/libstagefright/bqhelper/Android.bp
@@ -43,6 +43,7 @@
export_shared_lib_headers: [
"libstagefright_foundation",
+ "libhidlmemory",
],
cflags: [
diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WGraphicBufferProducer.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WGraphicBufferProducer.h
index 6f594fd..8ddf20f 100644
--- a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WGraphicBufferProducer.h
+++ b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WGraphicBufferProducer.h
@@ -20,7 +20,6 @@
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
-#include <android-base/logging.h>
#include <binder/Binder.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/IProducerListener.h>
@@ -52,6 +51,15 @@
typedef ::android::IProducerListener BProducerListener;
using ::android::BnGraphicBufferProducer;
+#ifndef LOG
+struct LOG_dummy {
+ template <typename T>
+ LOG_dummy& operator<< (const T&) { return *this; }
+};
+
+#define LOG(x) LOG_dummy()
+#endif
+
// Instantiate only if HGraphicBufferProducer is base of BASE.
template <typename BASE,
typename = typename std::enable_if<std::is_base_of<HGraphicBufferProducer, BASE>::value>::type>
diff --git a/media/libstagefright/codec2/1.0/Android.bp b/media/libstagefright/codec2/1.0/Android.bp
new file mode 100644
index 0000000..84d301a
--- /dev/null
+++ b/media/libstagefright/codec2/1.0/Android.bp
@@ -0,0 +1,43 @@
+cc_library_shared {
+ name: "android.hardware.media.c2@1.0-service-impl",
+ // relative_install_path: "hw",
+ // TODO: vendor: true,
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+
+ srcs: [
+ //"ComponentAuth.cpp",
+ //"Component.cpp",
+ //"ComponentListener.cpp",
+ //"ComponentStore.cpp",
+ //"Configurable.cpp",
+ "InputSurface.cpp",
+ "InputSurfaceConnection.cpp",
+ //"types.cpp",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/libstagefright/codec2/include",
+ "frameworks/av/media/libstagefright/codec2/vndk/internal",
+ ],
+
+ shared_libs: [
+ "libcutils",
+ "libgui",
+ "libhidlbase",
+ "libhidltransport",
+ "liblog",
+ "libnativewindow",
+ "libstagefright_bufferqueue_helper",
+ "libstagefright_codec2_vndk",
+ "libui",
+ "libutils",
+
+ //"android.hardware.media.c2@1.0",
+ "android.hardware.graphics.bufferqueue@1.0",
+ "android.hidl.token@1.0-utils",
+ ],
+}
+
diff --git a/media/libstagefright/codec2/1.0/InputSurface.cpp b/media/libstagefright/codec2/1.0/InputSurface.cpp
new file mode 100644
index 0000000..977d410
--- /dev/null
+++ b/media/libstagefright/codec2/1.0/InputSurface.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 "InputSurface"
+#include <utils/Log.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2PlatformSupport.h>
+
+#include <media/stagefright/bqhelper/GraphicBufferSource.h>
+#include <media/stagefright/codec2/1.0/InputSurface.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::GraphicBufferSource;
+
+sp<InputSurface> InputSurface::Create() {
+ sp<GraphicBufferSource> source = new GraphicBufferSource;
+ if (source->initCheck() != OK) {
+ return nullptr;
+ }
+ return new InputSurface(source->getIGraphicBufferProducer(), source);
+}
+
+InputSurface::InputSurface(
+ const sp<BGraphicBufferProducer> &base, const sp<GraphicBufferSource> &source)
+ : InputSurfaceBase(base),
+ mSource(source) {
+}
+
+sp<InputSurfaceConnection> InputSurface::connectToComponent(
+ const std::shared_ptr<C2Component> &comp) {
+ sp<InputSurfaceConnection> conn = new InputSurfaceConnection(mSource, comp);
+ if (!conn->init()) {
+ return nullptr;
+ }
+ return conn;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/codec2/1.0/InputSurfaceConnection.cpp b/media/libstagefright/codec2/1.0/InputSurfaceConnection.cpp
new file mode 100644
index 0000000..32d6404
--- /dev/null
+++ b/media/libstagefright/codec2/1.0/InputSurfaceConnection.cpp
@@ -0,0 +1,225 @@
+/*
+ * 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 "InputSurfaceConnection"
+#include <utils/Log.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2BlockInternal.h>
+#include <C2PlatformSupport.h>
+
+#include <gui/Surface.h>
+#include <media/stagefright/codec2/1.0/InputSurfaceConnection.h>
+#include <system/window.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::status_t;
+
+namespace {
+
+class Buffer2D : public C2Buffer {
+public:
+ explicit Buffer2D(C2ConstGraphicBlock block) : C2Buffer({ block }) {}
+};
+
+} // namespace
+
+constexpr int32_t kBufferCount = 16;
+
+class InputSurfaceConnection::Impl : public ComponentWrapper {
+public:
+ Impl(const sp<GraphicBufferSource> &source, const std::shared_ptr<C2Component> &comp)
+ : mSource(source), mComp(comp) {
+ }
+
+ virtual ~Impl() = default;
+
+ bool init() {
+ sp<GraphicBufferSource> source = mSource.promote();
+ if (source == nullptr) {
+ return false;
+ }
+ status_t err = source->initCheck();
+ if (err != OK) {
+ ALOGE("Impl::init: GBS init failed: %d", err);
+ return false;
+ }
+ // TODO: proper color aspect & dataspace
+ android_dataspace dataSpace = HAL_DATASPACE_BT709;
+ // TODO: read settings properly from the interface
+ err = source->configure(
+ this, dataSpace, kBufferCount, 1080, 1920, GRALLOC_USAGE_SW_READ_OFTEN);
+ if (err != OK) {
+ ALOGE("Impl::init: GBS configure failed: %d", err);
+ return false;
+ }
+ for (int32_t i = 0; i < kBufferCount; ++i) {
+ if (!source->onInputBufferAdded(i).isOk()) {
+ ALOGE("Impl::init: population GBS slots failed");
+ return false;
+ }
+ }
+ if (!source->start().isOk()) {
+ ALOGE("Impl::init: GBS start failed");
+ return false;
+ }
+ c2_status_t c2err = GetCodec2PlatformAllocatorStore()->fetchAllocator(
+ C2AllocatorStore::PLATFORM_START + 1, // GRALLOC
+ &mAllocator);
+ if (c2err != OK) {
+ ALOGE("Impl::init: failed to fetch gralloc allocator: %d", c2err);
+ return false;
+ }
+ return true;
+ }
+
+ // From ComponentWrapper
+ status_t submitBuffer(
+ int32_t bufferId, const sp<GraphicBuffer> &buffer,
+ int64_t timestamp, int fenceFd) override {
+ ALOGV("Impl::submitBuffer bufferId = %d", bufferId);
+ // TODO: Use fd to construct fence
+ (void)fenceFd;
+
+ std::shared_ptr<C2Component> comp = mComp.lock();
+ if (!comp) {
+ return NO_INIT;
+ }
+
+ std::shared_ptr<C2GraphicAllocation> alloc;
+ C2Handle *handle = WrapNativeCodec2GrallocHandle(
+ buffer->handle, buffer->width, buffer->height,
+ buffer->format, buffer->usage, buffer->stride);
+ c2_status_t err = mAllocator->priorGraphicAllocation(handle, &alloc);
+ if (err != OK) {
+ return UNKNOWN_ERROR;
+ }
+ std::shared_ptr<C2GraphicBlock> block = _C2BlockFactory::CreateGraphicBlock(alloc);
+
+ std::unique_ptr<C2Work> work(new C2Work);
+ work->input.flags = (C2FrameData::flags_t)0;
+ work->input.ordinal.timestamp = timestamp;
+ work->input.ordinal.frameIndex = mFrameIndex++;
+ work->input.buffers.clear();
+ std::shared_ptr<C2Buffer> c2Buffer(
+ // TODO: fence
+ new Buffer2D(block->share(
+ C2Rect(block->width(), block->height()), ::android::C2Fence())),
+ [handle, bufferId, src = mSource](C2Buffer *ptr) {
+ delete ptr;
+ native_handle_delete(handle);
+ sp<GraphicBufferSource> source = src.promote();
+ if (source != nullptr) {
+ // TODO: fence
+ (void)source->onInputBufferEmptied(bufferId, -1);
+ }
+ });
+ work->input.buffers.push_back(c2Buffer);
+ work->worklets.clear();
+ work->worklets.emplace_back(new C2Worklet);
+ std::list<std::unique_ptr<C2Work>> items;
+ items.push_back(std::move(work));
+
+ err = comp->queue_nb(&items);
+ if (err != C2_OK) {
+ return UNKNOWN_ERROR;
+ }
+
+ mLastTimestamp = timestamp;
+
+ return OK;
+ }
+
+ status_t submitEos(int32_t) override {
+ std::shared_ptr<C2Component> comp = mComp.lock();
+ if (!comp) {
+ return NO_INIT;
+ }
+
+ std::unique_ptr<C2Work> work(new C2Work);
+ work->input.flags = C2FrameData::FLAG_END_OF_STREAM;
+ work->input.ordinal.timestamp = mLastTimestamp;
+ work->input.ordinal.frameIndex = mFrameIndex++;
+ work->input.buffers.clear();
+ work->worklets.clear();
+ work->worklets.emplace_back(new C2Worklet);
+ std::list<std::unique_ptr<C2Work>> items;
+ items.push_back(std::move(work));
+
+ c2_status_t err = comp->queue_nb(&items);
+ return (err == C2_OK) ? OK : UNKNOWN_ERROR;
+ }
+
+ void dispatchDataSpaceChanged(
+ int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override {
+ // TODO
+ (void)dataSpace;
+ (void)aspects;
+ (void)pixelFormat;
+ }
+
+private:
+ wp<GraphicBufferSource> mSource;
+ std::weak_ptr<C2Component> mComp;
+
+ // Needed for ComponentWrapper implementation
+ int64_t mLastTimestamp;
+ std::shared_ptr<C2Allocator> mAllocator;
+ std::atomic_uint64_t mFrameIndex;
+};
+
+InputSurfaceConnection::InputSurfaceConnection(
+ const sp<GraphicBufferSource> &source,
+ const std::shared_ptr<C2Component> &comp)
+ : mSource(source),
+ mImpl(new Impl(source, comp)) {
+}
+
+InputSurfaceConnection::~InputSurfaceConnection() {
+ disconnect();
+}
+
+bool InputSurfaceConnection::init() {
+ if (mImpl == nullptr) {
+ return false;
+ }
+ return mImpl->init();
+}
+
+void InputSurfaceConnection::disconnect() {
+ ALOGV("disconnect");
+ if (mSource != nullptr) {
+ (void)mSource->stop();
+ (void)mSource->release();
+ }
+ mImpl.clear();
+ mSource.clear();
+ ALOGV("disconnected");
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/codec2/Android.bp b/media/libstagefright/codec2/Android.bp
index ee5c3eb..e1ac44e 100644
--- a/media/libstagefright/codec2/Android.bp
+++ b/media/libstagefright/codec2/Android.bp
@@ -1,5 +1,6 @@
cc_library_shared {
name: "libstagefright_codec2",
+ vendor_available: true,
tags: [
"optional",
@@ -21,6 +22,16 @@
"include",
],
+ header_libs: [
+ "libhardware_headers",
+ "libutils_headers",
+ ],
+
+ export_header_lib_headers: [
+ "libhardware_headers",
+ "libutils_headers",
+ ],
+
sanitize: {
misc_undefined: [
"unsigned-integer-overflow",
@@ -37,6 +48,7 @@
cc_library_shared {
name: "libstagefright_simple_c2component",
+ vendor_available: true,
tags: [
"optional",
diff --git a/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurface.h b/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurface.h
new file mode 100644
index 0000000..e46d03c
--- /dev/null
+++ b/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurface.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_H
+#define ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_H
+
+#include <memory>
+
+#include <C2Component.h>
+#include <media/stagefright/bqhelper/WGraphicBufferProducer.h>
+#include <media/stagefright/codec2/1.0/InputSurfaceConnection.h>
+
+namespace android {
+
+class GraphicBufferSource;
+
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::sp;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer
+ HGraphicBufferProducer;
+typedef ::android::IGraphicBufferProducer BGraphicBufferProducer;
+
+// TODO: ::android::TWGraphicBufferProducer<IInputSurface>
+typedef ::android::TWGraphicBufferProducer<HGraphicBufferProducer> InputSurfaceBase;
+
+class InputSurface : public InputSurfaceBase {
+public:
+ virtual ~InputSurface() = default;
+
+ // Methods from IInputSurface
+ sp<InputSurfaceConnection> connectToComponent(
+ const std::shared_ptr<::android::C2Component> &comp);
+ // TODO: intf()
+
+ static sp<InputSurface> Create();
+
+private:
+ InputSurface(
+ const sp<BGraphicBufferProducer> &base,
+ const sp<::android::GraphicBufferSource> &source);
+
+ sp<::android::GraphicBufferSource> mSource;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_H
diff --git a/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h b/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
new file mode 100644
index 0000000..fc19acd
--- /dev/null
+++ b/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_CONNECTION_H
+#define ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_CONNECTION_H
+
+#include <memory>
+
+#include <C2Component.h>
+#include <media/stagefright/bqhelper/GraphicBufferSource.h>
+#include <media/stagefright/bqhelper/WGraphicBufferProducer.h>
+#include <media/stagefright/codec2/1.0/InputSurfaceConnection.h>
+
+namespace android {
+
+class C2Allocator;
+
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+// TODO: inherit from IInputSurfaceConnection
+class InputSurfaceConnection : public RefBase {
+public:
+ virtual ~InputSurfaceConnection();
+
+ // From IInputSurfaceConnection
+ void disconnect();
+
+private:
+ friend class InputSurface;
+
+ // For InputSurface
+ InputSurfaceConnection(
+ const sp<GraphicBufferSource> &source, const std::shared_ptr<C2Component> &comp);
+ bool init();
+
+ InputSurfaceConnection() = delete;
+
+ class Impl;
+
+ sp<GraphicBufferSource> mSource;
+ sp<Impl> mImpl;
+
+ DISALLOW_EVIL_CONSTRUCTORS(InputSurfaceConnection);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_CONNECTION_H
diff --git a/media/libstagefright/codec2/vndk/Android.bp b/media/libstagefright/codec2/vndk/Android.bp
index cdd0488..d6cbe96 100644
--- a/media/libstagefright/codec2/vndk/Android.bp
+++ b/media/libstagefright/codec2/vndk/Android.bp
@@ -10,6 +10,10 @@
cc_library_shared {
name: "libstagefright_codec2_vndk",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"C2AllocatorIon.cpp",
@@ -23,12 +27,9 @@
"include",
],
- header_libs:[
- "libstagefright_codec2_internal",
- ],
-
include_dirs: [
"frameworks/av/media/libstagefright/codec2/include",
+ "frameworks/av/media/libstagefright/codec2/vndk/internal",
"frameworks/native/include/media/hardware",
],
@@ -42,7 +43,6 @@
"libhidlbase",
"libion",
"liblog",
- "libmedia",
"libstagefright_foundation",
"libui",
"libutils",
diff --git a/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp
index 911f0f8..9ea3589 100644
--- a/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp
+++ b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp
@@ -15,7 +15,7 @@
*/
#define LOG_NDEBUG 0
-#define LOG_TAG "C2SoftAvcEncEnc"
+#define LOG_TAG "C2SoftAvcEnc"
#include <utils/Log.h>
#include <utils/misc.h>
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.h b/media/libstagefright/codecs/on2/dec/SoftVPX.h
index a01a4f3..9ac2791 100644
--- a/media/libstagefright/codecs/on2/dec/SoftVPX.h
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.h
@@ -44,7 +44,7 @@
private:
enum {
- kNumBuffers = 4
+ kNumBuffers = 16
};
enum {
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index c9ebbdc..838bc5f 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -60,7 +60,8 @@
mConverter = NULL;
}
-void SoftwareRenderer::resetFormatIfChanged(const sp<AMessage> &format) {
+void SoftwareRenderer::resetFormatIfChanged(
+ const sp<AMessage> &format, size_t numOutputBuffers) {
CHECK(format != NULL);
int32_t colorFormatNew;
@@ -168,7 +169,7 @@
CHECK_EQ(0,
native_window_set_usage(
mNativeWindow.get(),
- GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_OFTEN
+ GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_RARELY
| GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP));
CHECK_EQ(0,
@@ -184,6 +185,11 @@
CHECK_EQ(0, native_window_set_buffers_format(
mNativeWindow.get(),
halFormat));
+ if (OK != native_window_set_buffer_count(
+ mNativeWindow.get(), numOutputBuffers + 4)) {
+ ALOGE("Failed to set native window buffer count to (%zu + 4)",
+ numOutputBuffers);
+ }
// NOTE: native window uses extended right-bottom coordinate
android_native_rect_t crop;
@@ -219,8 +225,8 @@
std::list<FrameRenderTracker::Info> SoftwareRenderer::render(
const void *data, size_t , int64_t mediaTimeUs, nsecs_t renderTimeNs,
- void* /*platformPrivate*/, const sp<AMessage>& format) {
- resetFormatIfChanged(format);
+ size_t numOutputBuffers, const sp<AMessage>& format) {
+ resetFormatIfChanged(format, numOutputBuffers);
FrameRenderTracker::Info *info = NULL;
ANativeWindowBuffer *buf;
@@ -243,8 +249,9 @@
Rect bounds(mCropWidth, mCropHeight);
void *dst;
- CHECK_EQ(0, mapper.lock(
- buf->handle, GRALLOC_USAGE_SW_WRITE_OFTEN, bounds, &dst));
+ CHECK_EQ(0, mapper.lock(buf->handle,
+ GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_RARELY,
+ bounds, &dst));
// TODO move the other conversions also into ColorConverter, and
// fix cropping issues (when mCropLeft/Top != 0 or mWidth != mCropWidth)
diff --git a/media/libstagefright/include/C2OMXNode.h b/media/libstagefright/include/C2OMXNode.h
new file mode 100644
index 0000000..3c007c4
--- /dev/null
+++ b/media/libstagefright/include/C2OMXNode.h
@@ -0,0 +1,89 @@
+/*
+ * 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 C2_OMX_NODE_H_
+#define C2_OMX_NODE_H_
+
+#include <atomic>
+
+#include <android/IOMXBufferSource.h>
+#include <media/IOMX.h>
+#include <media/OMXBuffer.h>
+
+namespace android {
+
+/**
+ * IOmxNode implementation around codec 2.0 component, only to be used in
+ * IGraphicBufferSource::configure. Only subset of IOmxNode API is implemented
+ * and others are left as stub. As a result, one cannot expect this IOmxNode
+ * to work in any other usage than IGraphicBufferSource.
+ */
+struct C2OMXNode : public BnOMXNode {
+ // TODO: this should take android::hardware::media::c2::V1_0::IComponent
+ explicit C2OMXNode(const std::shared_ptr<C2Component> &comp);
+ ~C2OMXNode() override = default;
+
+ // IOMXNode
+ status_t freeNode() override;
+ status_t sendCommand(OMX_COMMANDTYPE cmd, OMX_S32 param) override;
+ status_t getParameter(
+ OMX_INDEXTYPE index, void *params, size_t size) override;
+ status_t setParameter(
+ OMX_INDEXTYPE index, const void *params, size_t size) override;
+ status_t getConfig(
+ OMX_INDEXTYPE index, void *params, size_t size) override;
+ status_t setConfig(
+ OMX_INDEXTYPE index, const void *params, size_t size) override;
+ status_t setPortMode(OMX_U32 port_index, IOMX::PortMode mode) override;
+ status_t prepareForAdaptivePlayback(
+ OMX_U32 portIndex, OMX_BOOL enable,
+ OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) override;
+ status_t configureVideoTunnelMode(
+ OMX_U32 portIndex, OMX_BOOL tunneled,
+ OMX_U32 audioHwSync, native_handle_t **sidebandHandle) override;
+ status_t getGraphicBufferUsage(
+ OMX_U32 port_index, OMX_U32* usage) override;
+ status_t setInputSurface(
+ const sp<IOMXBufferSource> &bufferSource) override;
+ status_t allocateSecureBuffer(
+ OMX_U32 port_index, size_t size, buffer_id *buffer,
+ void **buffer_data, sp<NativeHandle> *native_handle) override;
+ status_t useBuffer(
+ OMX_U32 port_index, const OMXBuffer &omxBuf, buffer_id *buffer) override;
+ status_t freeBuffer(
+ OMX_U32 port_index, buffer_id buffer) override;
+ status_t fillBuffer(
+ buffer_id buffer, const OMXBuffer &omxBuf, int fenceFd) override;
+ status_t emptyBuffer(
+ buffer_id buffer, const OMXBuffer &omxBuf,
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) override;
+ status_t getExtensionIndex(
+ const char *parameter_name,
+ OMX_INDEXTYPE *index) override;
+ status_t dispatchMessage(const omx_message &msg) override;
+
+ sp<IOMXBufferSource> getSource();
+
+private:
+ std::weak_ptr<C2Component> mComp;
+ sp<IOMXBufferSource> mBufferSource;
+ std::shared_ptr<C2Allocator> mAllocator;
+ std::atomic_uint64_t mFrameIndex;
+};
+
+} // namespace android
+
+#endif // C2_OMX_NODE_H_
diff --git a/media/libstagefright/include/CCodecBufferChannel.h b/media/libstagefright/include/CCodecBufferChannel.h
index e64f984..eb3255f 100644
--- a/media/libstagefright/include/CCodecBufferChannel.h
+++ b/media/libstagefright/include/CCodecBufferChannel.h
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#ifndef A_BUFFER_CHANNEL_H_
+#ifndef CCODEC_BUFFER_CHANNEL_H_
-#define A_BUFFER_CHANNEL_H_
+#define CCODEC_BUFFER_CHANNEL_H_
#include <map>
#include <memory>
@@ -26,13 +26,19 @@
#include <C2Buffer.h>
#include <C2Component.h>
-#include <media/stagefright/foundation/Mutexed.h>
#include <media/stagefright/bqhelper/GraphicBufferSource.h>
+#include <media/stagefright/codec2/1.0/InputSurface.h>
+#include <media/stagefright/foundation/Mutexed.h>
#include <media/stagefright/CodecBase.h>
#include <media/ICrypto.h>
+#include "InputSurfaceWrapper.h"
+
namespace android {
+using ::android::hardware::media::c2::V1_0::implementation::InputSurface;
+using ::android::hardware::media::c2::V1_0::implementation::InputSurfaceConnection;
+
/**
* BufferChannelBase implementation for CCodec.
*/
@@ -76,7 +82,7 @@
* Set GraphicBufferSource object from which the component extracts input
* buffers.
*/
- status_t setGraphicBufferSource(const sp<GraphicBufferSource> &source);
+ status_t setInputSurface(const std::shared_ptr<InputSurfaceWrapper> &surface);
/**
* Start queueing buffers to the component. This object should never queue
@@ -103,7 +109,6 @@
class Buffers;
class InputBuffers;
class OutputBuffers;
- class InputBufferClient;
private:
class QueueGuard;
@@ -156,8 +161,6 @@
bool mRunning;
};
- class C2ComponentWrapper;
-
void feedInputBufferIfAvailable();
QueueSync mSync;
@@ -166,7 +169,6 @@
int32_t mHeapSeqNum;
std::shared_ptr<C2Component> mComponent;
- std::shared_ptr<InputBufferClient> mInputClient;
std::function<void(status_t, enum ActionCode)> mOnError;
std::shared_ptr<C2BlockPool> mInputAllocator;
QueueSync mQueueSync;
@@ -180,6 +182,8 @@
sp<MemoryDealer> makeMemoryDealer(size_t heapSize);
Mutexed<sp<Surface>> mSurface;
+ std::shared_ptr<InputSurfaceWrapper> mInputSurface;
+
inline bool hasCryptoOrDescrambler() {
return mCrypto != NULL || mDescrambler != NULL;
}
@@ -187,4 +191,4 @@
} // namespace android
-#endif // A_BUFFER_CHANNEL_H_
+#endif // CCODEC_BUFFER_CHANNEL_H_
diff --git a/media/libstagefright/include/InputSurfaceWrapper.h b/media/libstagefright/include/InputSurfaceWrapper.h
new file mode 100644
index 0000000..a4d8f29
--- /dev/null
+++ b/media/libstagefright/include/InputSurfaceWrapper.h
@@ -0,0 +1,49 @@
+/*
+ * 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 INPUT_SURFACE_WRAPPER_H_
+
+#define INPUT_SURFACE_WRAPPER_H_
+
+namespace android {
+
+/**
+ * Wrapper interface around InputSurface.
+ */
+class InputSurfaceWrapper {
+public:
+ virtual ~InputSurfaceWrapper() = default;
+
+ /**
+ * Connect the surface with |comp| and start pushing buffers. A surface can
+ * connect to at most one component at a time.
+ *
+ * \return OK successfully connected to |comp|
+ * \return ALREADY_EXISTS already connected to another component.
+ */
+ virtual status_t connect(const std::shared_ptr<C2Component> &comp) = 0;
+
+ /**
+ * Disconnect the surface from the component if any.
+ */
+ virtual void disconnect() = 0;
+
+ // TODO: intf()
+};
+
+} // namespace android
+
+#endif // INPUT_SURFACE_WRAPPER_H_
diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
index e04b59f..c286516 100644
--- a/media/libstagefright/include/SoftwareRenderer.h
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -39,7 +39,7 @@
std::list<FrameRenderTracker::Info> render(
const void *data, size_t size, int64_t mediaTimeUs, nsecs_t renderTimeNs,
- void *platformPrivate, const sp<AMessage> &format);
+ size_t numOutputBuffers, const sp<AMessage> &format);
void clearTracker();
private:
@@ -59,10 +59,11 @@
HDRStaticInfo mHDRStaticInfo;
FrameRenderTracker mRenderTracker;
+ void resetFormatIfChanged(
+ const sp<AMessage> &format, size_t numOutputBuffers);
+
SoftwareRenderer(const SoftwareRenderer &);
SoftwareRenderer &operator=(const SoftwareRenderer &);
-
- void resetFormatIfChanged(const sp<AMessage> &format);
};
} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/CCodec.h b/media/libstagefright/include/media/stagefright/CCodec.h
index 9307f3f..24ee0a3 100644
--- a/media/libstagefright/include/media/stagefright/CCodec.h
+++ b/media/libstagefright/include/media/stagefright/CCodec.h
@@ -24,7 +24,6 @@
#include <android/native_window.h>
#include <media/hardware/MetadataBufferType.h>
#include <media/stagefright/foundation/Mutexed.h>
-#include <media/stagefright/bqhelper/GraphicBufferSource.h>
#include <media/stagefright/CodecBase.h>
#include <media/stagefright/FrameRenderTracker.h>
#include <media/stagefright/MediaDefs.h>
@@ -36,6 +35,7 @@
namespace android {
class CCodecBufferChannel;
+class InputSurfaceWrapper;
class CCodec : public CodecBase {
public:
@@ -81,7 +81,7 @@
void createInputSurface();
void setInputSurface(const sp<PersistentSurface> &surface);
- status_t setupInputSurface(const sp<GraphicBufferSource> &source);
+ status_t setupInputSurface(const std::shared_ptr<InputSurfaceWrapper> &surface);
void setDeadline(const TimePoint &deadline);
diff --git a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
index 4d2f4f0..90c66eb 100644
--- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
+++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
@@ -57,7 +57,7 @@
const sp<ExtractorPlugin> &plugin, List<sp<ExtractorPlugin>> &pluginList);
static MediaExtractor::CreatorFunc sniff(DataSourceBase *source,
- String8 *mimeType, float *confidence, sp<AMessage> *meta,
+ float *confidence, void **meta, MediaExtractor::FreeMetaFunc *freeMeta,
sp<ExtractorPlugin> &plugin);
static void UpdateExtractors(const char *newUpdateApkPath);
diff --git a/media/libstagefright/include/media/stagefright/NdkUtils.h b/media/libstagefright/include/media/stagefright/NdkUtils.h
new file mode 100644
index 0000000..a68884a
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/NdkUtils.h
@@ -0,0 +1,31 @@
+/*
+ * 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 NDK_UTILS_H_
+
+#define NDK_UTILS_H_
+
+#include <media/stagefright/MetaData.h>
+#include <media/NdkWrapper.h>
+
+namespace android {
+
+sp<MetaData> convertMediaFormatWrapperToMetaData(
+ const sp<AMediaFormatWrapper> &fmt);
+
+} // namespace android
+
+#endif // NDK_UTILS_H_
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index 1ba5852..d650224 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -41,7 +41,7 @@
mLooper->start(
false, // runOnCallingThread
false, // canCallJava
- ANDROID_PRIORITY_FOREGROUND);
+ ANDROID_PRIORITY_AUDIO);
}
void SimpleSoftOMXComponent::prepareForDestruction() {
diff --git a/media/ndk/NdkMediaDataSource.cpp b/media/ndk/NdkMediaDataSource.cpp
index f190f80..9d00e5e 100644
--- a/media/ndk/NdkMediaDataSource.cpp
+++ b/media/ndk/NdkMediaDataSource.cpp
@@ -44,7 +44,15 @@
};
NdkDataSource::NdkDataSource(AMediaDataSource *dataSource)
- : mDataSource(dataSource) {
+ : mDataSource(AMediaDataSource_new()) {
+ AMediaDataSource_setReadAt(mDataSource, dataSource->readAt);
+ AMediaDataSource_setGetSize(mDataSource, dataSource->getSize);
+ AMediaDataSource_setClose(mDataSource, dataSource->close);
+ AMediaDataSource_setUserdata(mDataSource, dataSource->userdata);
+}
+
+NdkDataSource::~NdkDataSource() {
+ AMediaDataSource_delete(mDataSource);
}
status_t NdkDataSource::initCheck() const {
diff --git a/media/ndk/NdkMediaDataSourcePriv.h b/media/ndk/NdkMediaDataSourcePriv.h
index 65ddd2a..ea9c865 100644
--- a/media/ndk/NdkMediaDataSourcePriv.h
+++ b/media/ndk/NdkMediaDataSourcePriv.h
@@ -49,6 +49,9 @@
virtual String8 getMIMEType() const;
virtual void close();
+protected:
+ virtual ~NdkDataSource();
+
private:
Mutex mLock;
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index eecc858..fe08ab9 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -240,7 +240,7 @@
return AMEDIA_ERROR_INVALID_PARAMETER;
}
Vector<uint8_t> session;
- status_t status = mObj->mDrm->openSession(session);
+ status_t status = mObj->mDrm->openSession(DrmPlugin::kSecurityLevelMax, session);
if (status == OK) {
mObj->mIds.push_front(session);
List<idvec_t>::iterator iter = mObj->mIds.begin();
diff --git a/packages/MediaComponents/res/drawable/ic_arrow_back.xml b/packages/MediaComponents/res/drawable/ic_arrow_back.xml
index 5aba8c6..2b5f71e 100644
--- a/packages/MediaComponents/res/drawable/ic_arrow_back.xml
+++ b/packages/MediaComponents/res/drawable/ic_arrow_back.xml
@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="40dp"
- android:height="40dp"
+ android:width="24dp"
+ android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
diff --git a/packages/MediaComponents/res/drawable/ic_launch.xml b/packages/MediaComponents/res/drawable/ic_launch.xml
new file mode 100644
index 0000000..f7af6aa
--- /dev/null
+++ b/packages/MediaComponents/res/drawable/ic_launch.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/media_controller.xml b/packages/MediaComponents/res/layout/media_controller.xml
index dd56e7c..f9ebd44 100644
--- a/packages/MediaComponents/res/layout/media_controller.xml
+++ b/packages/MediaComponents/res/layout/media_controller.xml
@@ -24,35 +24,88 @@
<RelativeLayout
android:id="@+id/title_bar"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ style="@style/TitleBar">
- <RadioButton
- android:id="@+id/back"
+ <LinearLayout
+ android:id="@+id/title_bar_left"
+ android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
+ android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
- android:checked="true"
- android:visibility="gone"/>
+ android:orientation="horizontal">
- <TextView
- android:id="@+id/title_text"
+ <ImageButton
+ android:id="@+id/back"
+ android:clickable="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:paddingLeft="5dip"
+ android:visibility="visible"
+ style="@style/TitleBarButton.Back"/>
+
+ <TextView
+ android:id="@+id/title_text"
+ android:ellipsize="end"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/back"
+ android:layout_centerVertical="true"
+ android:maxLines="1"
+ android:paddingLeft="5dip"
+ android:paddingRight="5dip"
+ android:textSize="15sp"
+ android:textColor="#FFFFFFFF"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/title_bar_right"
+ android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_toRightOf="@id/back"
+ android:layout_alignParentRight="true"
android:layout_centerVertical="true"
- android:layout_marginLeft="15dp"
- android:paddingTop="4dp"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
- android:textSize="20sp"
- android:textColor="#FFFFFFFF" />
+ android:orientation="horizontal">
- <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" />
+ <LinearLayout
+ android:id="@+id/ad"
+ android:clickable="true"
+ android:gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:paddingLeft="5dip"
+ android:paddingRight="10dip"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/ad_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:paddingRight="5dip"
+ android:text="Visit Advertiser"
+ android:textSize="10sp"
+ android:textColor="#FFFFFFFF" />
+
+ <ImageButton
+ android:id="@+id/ad_launch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ style="@style/TitleBarButton.Launch" />
+ </LinearLayout>
+
+ <view class="com.android.support.mediarouter.app.MediaRouteButton"
+ android:id="@+id/cast"
+ android:layout_centerVertical="true"
+ android:visibility="visible"
+ style="@style/TitleBarButton" />
+ </LinearLayout>
</RelativeLayout>
diff --git a/packages/MediaComponents/res/values/style.xml b/packages/MediaComponents/res/values/style.xml
index c59380c..db5e8f3 100644
--- a/packages/MediaComponents/res/values/style.xml
+++ b/packages/MediaComponents/res/values/style.xml
@@ -26,12 +26,22 @@
<item name="android:src">@drawable/ic_rewind_10</item>
</style>
+ <style name="TitleBar">
+ <item name="android:layout_height">46dp</item>
+ </style>
<style name="TitleBarButton">
<item name="android:background">@null</item>
<item name="android:layout_width">36dp</item>
<item name="android:layout_height">36dp</item>
- <item name="android:layout_margin">10dp</item>
+ </style>
+
+ <style name="TitleBarButton.Back">
+ <item name="android:src">@drawable/ic_arrow_back</item>
+ </style>
+
+ <style name="TitleBarButton.Launch">
+ <item name="android:src">@drawable/ic_launch</item>
</style>
<style name="BottomBarButton">
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
index 7702bda..9538c3d 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
@@ -63,4 +63,10 @@
// Get library service specific
//////////////////////////////////////////////////////////////////////////////////////////////
void getBrowserRoot(IMediaSession2Callback callback, in Bundle rootHints);
+ void getItem(IMediaSession2Callback callback, String mediaId);
+ void getChildren(IMediaSession2Callback callback, String parentId, int page, int pageSize,
+ in Bundle extras);
+ void search(IMediaSession2Callback callback, String query, in Bundle extras);
+ void getSearchResult(IMediaSession2Callback callback, String query, int page, int pageSize,
+ in Bundle extras);
}
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
index a443bf8..b3aa59c 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
@@ -48,4 +48,9 @@
// Browser sepcific
//////////////////////////////////////////////////////////////////////////////////////////////
void onGetRootResult(in Bundle rootHints, String rootMediaId, in Bundle rootExtra);
+ void onItemLoaded(String mediaId, in Bundle result);
+ void onChildrenLoaded(String parentId, int page, int pageSize, in Bundle extras,
+ in List<Bundle> result);
+ void onSearchResultLoaded(String query, int page, int pageSize, in Bundle extras,
+ in List<Bundle> result);
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
index 3e6d98f..76da42b 100644
--- a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
@@ -19,11 +19,13 @@
import android.content.Context;
import android.media.MediaBrowser2;
import android.media.MediaBrowser2.BrowserCallback;
+import android.media.MediaItem2;
import android.media.MediaSession2.CommandButton;
import android.media.SessionToken2;
import android.media.update.MediaBrowser2Provider;
import android.os.Bundle;
import android.os.RemoteException;
+import android.text.TextUtils;
import android.util.Log;
import java.util.List;
@@ -61,28 +63,98 @@
}
@Override
- public void subscribe_impl(String parentId, Bundle options) {
+ public void subscribe_impl(String parentId, Bundle extras) {
// TODO(jaewan): Implement
}
@Override
- public void unsubscribe_impl(String parentId, Bundle options) {
+ public void unsubscribe_impl(String parentId, Bundle extras) {
// TODO(jaewan): Implement
}
@Override
public void getItem_impl(String mediaId) {
- // TODO(jaewan): Implement
+ if (mediaId == null) {
+ throw new IllegalArgumentException("mediaId shouldn't be null");
+ }
+
+ final IMediaSession2 binder = getSessionBinder();
+ if (binder != null) {
+ try {
+ binder.getItem(getControllerStub(), mediaId);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle disconnect.
+ if (DEBUG) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ }
+ } else {
+ Log.w(TAG, "Session isn't active", new IllegalStateException());
+ }
}
@Override
- public void getChildren_impl(String parentId, int page, int pageSize, Bundle options) {
- // TODO(jaewan): Implement
+ public void getChildren_impl(String parentId, int page, int pageSize, Bundle extras) {
+ if (parentId == null) {
+ throw new IllegalArgumentException("parentId shouldn't be null");
+ }
+ if (page < 1 || pageSize < 1) {
+ throw new IllegalArgumentException("Neither page nor pageSize should be less than 1");
+ }
+
+ final IMediaSession2 binder = getSessionBinder();
+ if (binder != null) {
+ try {
+ binder.getChildren(getControllerStub(), parentId, page, pageSize, extras);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle disconnect.
+ if (DEBUG) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ }
+ } else {
+ Log.w(TAG, "Session isn't active", new IllegalStateException());
+ }
}
@Override
- public void search_impl(String query, int page, int pageSize, Bundle extras) {
- // TODO(jaewan): Implement
+ public void search_impl(String query, Bundle extras) {
+ if (TextUtils.isEmpty(query)) {
+ throw new IllegalArgumentException("query shouldn't be empty");
+ }
+ final IMediaSession2 binder = getSessionBinder();
+ if (binder != null) {
+ try {
+ binder.search(getControllerStub(), query, extras);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle disconnect.
+ if (DEBUG) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ }
+ } else {
+ Log.w(TAG, "Session isn't active", new IllegalStateException());
+ }
+ }
+
+ @Override
+ public void getSearchResult_impl(String query, int page, int pageSize, Bundle extras) {
+ if (TextUtils.isEmpty(query)) {
+ throw new IllegalArgumentException("query shouldn't be empty");
+ }
+ final IMediaSession2 binder = getSessionBinder();
+ if (binder != null) {
+ try {
+ binder.getSearchResult(getControllerStub(), query, page, pageSize, extras);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle disconnect.
+ if (DEBUG) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ }
+ } else {
+ Log.w(TAG, "Session isn't active", new IllegalStateException());
+ }
}
public void onGetRootResult(
@@ -92,9 +164,23 @@
});
}
- public void onCustomLayoutChanged(final List<CommandButton> layout) {
+ public void onItemLoaded(String mediaId, MediaItem2 item) {
getCallbackExecutor().execute(() -> {
- mCallback.onCustomLayoutChanged(layout);
+ mCallback.onItemLoaded(mediaId, item);
+ });
+ }
+
+ public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle extras,
+ List<MediaItem2> result) {
+ getCallbackExecutor().execute(() -> {
+ mCallback.onChildrenLoaded(parentId, page, pageSize, extras, result);
+ });
+ }
+
+ public void onSearchResultLoaded(String query, int page, int pageSize, Bundle extras,
+ List<MediaItem2> result) {
+ getCallbackExecutor().execute(() -> {
+ mCallback.onSearchResultLoaded(query, page, pageSize, extras, result);
});
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 1401ab8..5af4240 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -22,14 +22,14 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.media.AudioAttributes;
+import android.media.MediaController2;
+import android.media.MediaController2.ControllerCallback;
import android.media.MediaController2.PlaybackInfo;
import android.media.MediaItem2;
import android.media.MediaSession2;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
import android.media.MediaSession2.CommandGroup;
-import android.media.MediaController2;
-import android.media.MediaController2.ControllerCallback;
import android.media.MediaSession2.PlaylistParams;
import android.media.MediaSessionService2;
import android.media.PlaybackState2;
@@ -44,9 +44,6 @@
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;
import java.util.concurrent.Executor;
@@ -220,7 +217,11 @@
}
Context getContext() {
- return mContext;
+ return mContext;
+ }
+
+ MediaController2 getInstance() {
+ return mInstance;
}
@Override
@@ -503,10 +504,7 @@
sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS, args);
}
- ///////////////////////////////////////////////////
- // Protected or private methods
- ///////////////////////////////////////////////////
- private void pushPlaybackStateChanges(final PlaybackState2 state) {
+ void pushPlaybackStateChanges(final PlaybackState2 state) {
synchronized (mLock) {
mPlaybackState = state;
}
@@ -518,7 +516,7 @@
});
}
- private void pushPlaylistParamsChanges(final PlaylistParams params) {
+ void pushPlaylistParamsChanges(final PlaylistParams params) {
synchronized (mLock) {
mPlaylistParams = params;
}
@@ -530,7 +528,7 @@
});
}
- private void pushPlaybackInfoChanges(final PlaybackInfo info) {
+ void pushPlaybackInfoChanges(final PlaybackInfo info) {
synchronized (mLock) {
mPlaybackInfo = info;
}
@@ -542,7 +540,7 @@
});
}
- private void pushPlaylistChanges(final List<Bundle> list) {
+ void pushPlaylistChanges(final List<Bundle> list) {
final List<MediaItem2> playlist = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
MediaItem2 item = MediaItem2.fromBundle(mContext, list.get(i));
@@ -563,7 +561,7 @@
}
// Should be used without a lock to prevent potential deadlock.
- private void onConnectedNotLocked(IMediaSession2 sessionBinder,
+ void onConnectedNotLocked(IMediaSession2 sessionBinder,
final CommandGroup commandGroup, final PlaybackState2 state, final PlaybackInfo info,
final PlaylistParams params, final List<MediaItem2> playlist, final int ratingType,
final PendingIntent sessionActivity) {
@@ -624,7 +622,7 @@
}
}
- private void onCustomCommand(final Command command, final Bundle args,
+ void onCustomCommand(final Command command, final Bundle args,
final ResultReceiver receiver) {
if (DEBUG) {
Log.d(TAG, "onCustomCommand cmd=" + command);
@@ -635,193 +633,10 @@
});
}
- // TODO(jaewan): Pull out this from the controller2, and rename it to the MediaController2Stub
- // or MediaBrowser2Stub.
- static class MediaSession2CallbackStub extends IMediaSession2Callback.Stub {
- private final WeakReference<MediaController2Impl> mController;
-
- private MediaSession2CallbackStub(MediaController2Impl controller) {
- mController = new WeakReference<>(controller);
- }
-
- private MediaController2Impl getController() throws IllegalStateException {
- final MediaController2Impl controller = mController.get();
- if (controller == null) {
- throw new IllegalStateException("Controller is released");
- }
- return controller;
- }
-
- // TODO(jaewan): Refactor code to get rid of these pattern.
- private MediaBrowser2Impl getBrowser() throws IllegalStateException {
- final MediaController2Impl controller = getController();
- if (controller instanceof MediaBrowser2Impl) {
- return (MediaBrowser2Impl) controller;
- }
- return null;
- }
-
- public void destroy() {
- mController.clear();
- }
-
- @Override
- public void onPlaybackStateChanged(Bundle state) throws RuntimeException {
- final MediaController2Impl controller;
- try {
- controller = getController();
- } catch (IllegalStateException e) {
- Log.w(TAG, "Don't fail silently here. Highly likely a bug");
- return;
- }
- controller.pushPlaybackStateChanges(
- PlaybackState2.fromBundle(controller.getContext(), state));
- }
-
- @Override
- public void onPlaylistChanged(List<Bundle> playlist) throws RuntimeException {
- final MediaController2Impl controller;
- try {
- controller = getController();
- } catch (IllegalStateException e) {
- Log.w(TAG, "Don't fail silently here. Highly likely a bug");
- return;
- }
- if (playlist == null) {
- return;
- }
- controller.pushPlaylistChanges(playlist);
- }
-
- @Override
- public void onPlaylistParamsChanged(Bundle params) throws RuntimeException {
- final MediaController2Impl controller;
- try {
- controller = getController();
- } catch (IllegalStateException e) {
- Log.w(TAG, "Don't fail silently here. Highly likely a bug");
- return;
- }
- controller.pushPlaylistParamsChanges(
- PlaylistParams.fromBundle(controller.getContext(), params));
- }
-
- @Override
- public void onPlaybackInfoChanged(Bundle playbackInfo) throws RuntimeException {
- if (DEBUG) {
- Log.d(TAG, "onPlaybackInfoChanged");
- }
- final MediaController2Impl controller;
- try {
- controller = getController();
- } catch (IllegalStateException e) {
- Log.w(TAG, "Don't fail silently here. Highly likely a bug");
- return;
- }
- controller.pushPlaybackInfoChanges(
- PlaybackInfoImpl.fromBundle(controller.getContext(), playbackInfo));
- }
-
- @Override
- public void onConnected(IMediaSession2 sessionBinder, Bundle commandGroup,
- Bundle playbackState, Bundle playbackInfo, Bundle playlistParams, List<Bundle>
- playlist, int ratingType, PendingIntent sessionActivity) {
- final MediaController2Impl controller = mController.get();
- if (controller == null) {
- if (DEBUG) {
- Log.d(TAG, "onConnected after MediaController2.close()");
- }
- return;
- }
- final Context context = controller.getContext();
- List<MediaItem2> list = new ArrayList<>();
- for (int i = 0; i < playlist.size(); i++) {
- MediaItem2 item = MediaItem2.fromBundle(context, playlist.get(i));
- if (item != null) {
- list.add(item);
- }
- }
- controller.onConnectedNotLocked(sessionBinder,
- CommandGroup.fromBundle(context, commandGroup),
- PlaybackState2.fromBundle(context, playbackState),
- PlaybackInfoImpl.fromBundle(context, playbackInfo),
- PlaylistParams.fromBundle(context, playlistParams),
- list, ratingType, sessionActivity);
- }
-
- @Override
- public void onDisconnected() {
- final MediaController2Impl controller = mController.get();
- if (controller == null) {
- if (DEBUG) {
- Log.d(TAG, "onDisconnected after MediaController2.close()");
- }
- return;
- }
- controller.mInstance.close();
- }
-
- @Override
- public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra)
- throws RuntimeException {
- final MediaBrowser2Impl browser;
- try {
- browser = getBrowser();
- } catch (IllegalStateException e) {
- Log.w(TAG, "Don't fail silently here. Highly likely a bug");
- return;
- }
- if (browser == null) {
- // TODO(jaewan): Revisit here. Could be a bug
- return;
- }
- browser.onGetRootResult(rootHints, rootMediaId, rootExtra);
- }
-
- @Override
- public void onCustomLayoutChanged(List<Bundle> commandButtonlist) {
- if (commandButtonlist == null) {
- // Illegal call. Ignore
- return;
- }
- // TODO(jaewan): Fix here. It's controller feature so shouldn't use browser
- final MediaBrowser2Impl browser;
- try {
- browser = getBrowser();
- } catch (IllegalStateException e) {
- Log.w(TAG, "Don't fail silently here. Highly likely a bug");
- return;
- }
- if (browser == null) {
- // TODO(jaewan): Revisit here. Could be a bug
- return;
- }
- List<CommandButton> layout = new ArrayList<>();
- for (int i = 0; i < commandButtonlist.size(); i++) {
- CommandButton button = CommandButtonImpl.fromBundle(
- browser.getContext(), commandButtonlist.get(i));
- if (button != null) {
- layout.add(button);
- }
- }
- browser.onCustomLayoutChanged(layout);
- }
-
- @Override
- public void sendCustomCommand(Bundle commandBundle, Bundle args, ResultReceiver receiver) {
- final MediaController2Impl controller;
- try {
- controller = getController();
- } catch (IllegalStateException e) {
- Log.w(TAG, "Don't fail silently here. Highly likely a bug");
- return;
- }
- Command command = Command.fromBundle(controller.getContext(), commandBundle);
- if (command == null) {
- return;
- }
- controller.onCustomCommand(command, args, receiver);
- }
+ void onCustomLayoutChanged(final List<CommandButton> layout) {
+ mCallbackExecutor.execute(() -> {
+ mCallback.onCustomLayoutChanged(layout);
+ });
}
// This will be called on the main thread.
diff --git a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
index f51e246..4c4ef24 100644
--- a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
@@ -52,7 +52,7 @@
throw new IllegalArgumentException("dsd shouldn't be null");
}
if (metadata != null && !TextUtils.equals(mediaId, metadata.getMediaId())) {
- throw new IllegalArgumentException("metadata's id should be match with the mediaid");
+ throw new IllegalArgumentException("metadata's id should be matched with the mediaid");
}
mContext = context;
@@ -71,9 +71,9 @@
throw new IllegalArgumentException("mediaId shouldn't be null");
}
if (metadata != null && !TextUtils.equals(mediaId, metadata.getMediaId())) {
- throw new IllegalArgumentException("metadata's id should be match with the mediaid");
+ throw new IllegalArgumentException("metadata's id should be matched with the mediaid");
}
- mContext =context;
+ mContext = context;
mId = mediaId;
mMetadata = metadata;
mFlags = flags;
@@ -136,24 +136,21 @@
}
@Override
- public void setMetadata_impl(@NonNull MediaMetadata2 metadata) {
- if (metadata == null) {
- throw new IllegalArgumentException("metadata shouldn't be null");
- }
- if (TextUtils.isEmpty(metadata.getMediaId())) {
- throw new IllegalArgumentException("metadata must have a non-empty media id");
+ public void setMetadata_impl(@Nullable MediaMetadata2 metadata) {
+ if (metadata != null && !TextUtils.equals(mId, metadata.getMediaId())) {
+ throw new IllegalArgumentException("metadata's id should be matched with the mediaId");
}
mMetadata = metadata;
}
@Override
- public MediaMetadata2 getMetadata_impl() {
+ public @Nullable MediaMetadata2 getMetadata_impl() {
return mMetadata;
}
@Override
- public @Nullable String getMediaId_impl() {
- return mMetadata.getMediaId();
+ public @NonNull String getMediaId_impl() {
+ return mId;
}
@Override
diff --git a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
index 77bd334..b9dffcf 100644
--- a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
@@ -26,6 +26,7 @@
import android.media.MediaPlayerInterface;
import android.media.MediaSession2;
import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
import android.media.MediaSessionService2;
import android.media.SessionToken2;
import android.media.VolumeProvider2;
@@ -87,6 +88,12 @@
}
@Override
+ MediaLibrarySessionCallback getCallback() {
+ // Equivalent to the (MediaLibrarySessionCallback) super.getCallback().
+ return mCallback;
+ }
+
+ @Override
public void notifyChildrenChanged_impl(ControllerInfo controller, String parentId,
Bundle options) {
// TODO(jaewan): Implements
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
new file mode 100644
index 0000000..852029a
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
@@ -0,0 +1,295 @@
+/*
+ * 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.app.PendingIntent;
+import android.content.Context;
+import android.media.MediaItem2;
+import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandButton;
+import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.PlaylistParams;
+import android.media.PlaybackState2;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+import com.android.media.MediaController2Impl.PlaybackInfoImpl;
+import com.android.media.MediaSession2Impl.CommandButtonImpl;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MediaSession2CallbackStub extends IMediaSession2Callback.Stub {
+ private static final String TAG = "MS2CallbackStub";
+ private static final boolean DEBUG = true; // TODO(jaewan): Change
+
+ private final WeakReference<MediaController2Impl> mController;
+
+ MediaSession2CallbackStub(MediaController2Impl controller) {
+ mController = new WeakReference<>(controller);
+ }
+
+ private MediaController2Impl getController() throws IllegalStateException {
+ final MediaController2Impl controller = mController.get();
+ if (controller == null) {
+ throw new IllegalStateException("Controller is released");
+ }
+ return controller;
+ }
+
+ private MediaBrowser2Impl getBrowser() throws IllegalStateException {
+ final MediaController2Impl controller = getController();
+ if (controller instanceof MediaBrowser2Impl) {
+ return (MediaBrowser2Impl) controller;
+ }
+ return null;
+ }
+
+ public void destroy() {
+ mController.clear();
+ }
+
+ @Override
+ public void onPlaybackStateChanged(Bundle state) throws RuntimeException {
+ final MediaController2Impl controller;
+ try {
+ controller = getController();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ controller.pushPlaybackStateChanges(
+ PlaybackState2.fromBundle(controller.getContext(), state));
+ }
+
+ @Override
+ public void onPlaylistChanged(List<Bundle> playlist) throws RuntimeException {
+ final MediaController2Impl controller;
+ try {
+ controller = getController();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ if (playlist == null) {
+ return;
+ }
+ controller.pushPlaylistChanges(playlist);
+ }
+
+ @Override
+ public void onPlaylistParamsChanged(Bundle params) throws RuntimeException {
+ final MediaController2Impl controller;
+ try {
+ controller = getController();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ controller.pushPlaylistParamsChanges(
+ PlaylistParams.fromBundle(controller.getContext(), params));
+ }
+
+ @Override
+ public void onPlaybackInfoChanged(Bundle playbackInfo) throws RuntimeException {
+ if (DEBUG) {
+ Log.d(TAG, "onPlaybackInfoChanged");
+ }
+ final MediaController2Impl controller;
+ try {
+ controller = getController();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ controller.pushPlaybackInfoChanges(
+ PlaybackInfoImpl.fromBundle(controller.getContext(), playbackInfo));
+ }
+
+ @Override
+ public void onConnected(IMediaSession2 sessionBinder, Bundle commandGroup,
+ Bundle playbackState, Bundle playbackInfo, Bundle playlistParams, List<Bundle>
+ playlist, int ratingType, PendingIntent sessionActivity) {
+ final MediaController2Impl controller = mController.get();
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "onConnected after MediaController2.close()");
+ }
+ return;
+ }
+ final Context context = controller.getContext();
+ List<MediaItem2> list = new ArrayList<>();
+ for (int i = 0; i < playlist.size(); i++) {
+ MediaItem2 item = MediaItem2.fromBundle(context, playlist.get(i));
+ if (item != null) {
+ list.add(item);
+ }
+ }
+ controller.onConnectedNotLocked(sessionBinder,
+ CommandGroup.fromBundle(context, commandGroup),
+ PlaybackState2.fromBundle(context, playbackState),
+ PlaybackInfoImpl.fromBundle(context, playbackInfo),
+ PlaylistParams.fromBundle(context, playlistParams),
+ list, ratingType, sessionActivity);
+ }
+
+ @Override
+ public void onDisconnected() {
+ final MediaController2Impl controller = mController.get();
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "onDisconnected after MediaController2.close()");
+ }
+ return;
+ }
+ controller.getInstance().close();
+ }
+
+ @Override
+ public void onCustomLayoutChanged(List<Bundle> commandButtonlist) {
+ if (commandButtonlist == null) {
+ // Illegal call. Ignore
+ return;
+ }
+ final MediaController2Impl controller;
+ try {
+ controller = getController();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ if (controller == null) {
+ // TODO(jaewan): Revisit here. Could be a bug
+ return;
+ }
+ List<CommandButton> layout = new ArrayList<>();
+ for (int i = 0; i < commandButtonlist.size(); i++) {
+ CommandButton button = CommandButtonImpl.fromBundle(
+ controller.getContext(), commandButtonlist.get(i));
+ if (button != null) {
+ layout.add(button);
+ }
+ }
+ controller.onCustomLayoutChanged(layout);
+ }
+
+ @Override
+ public void sendCustomCommand(Bundle commandBundle, Bundle args, ResultReceiver receiver) {
+ final MediaController2Impl controller;
+ try {
+ controller = getController();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ Command command = Command.fromBundle(controller.getContext(), commandBundle);
+ if (command == null) {
+ return;
+ }
+ controller.onCustomCommand(command, args, receiver);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // MediaBrowser specific
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ @Override
+ public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra)
+ throws RuntimeException {
+ final MediaBrowser2Impl browser;
+ try {
+ browser = getBrowser();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ if (browser == null) {
+ // TODO(jaewan): Revisit here. Could be a bug
+ return;
+ }
+ browser.onGetRootResult(rootHints, rootMediaId, rootExtra);
+ }
+
+
+ @Override
+ public void onItemLoaded(String mediaId, Bundle itemBundle) throws RuntimeException {
+ final MediaBrowser2Impl browser;
+ try {
+ browser = getBrowser();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ if (browser == null) {
+ // TODO(jaewan): Revisit here. Could be a bug
+ return;
+ }
+ browser.onItemLoaded(mediaId,
+ MediaItem2Impl.fromBundle(browser.getContext(), itemBundle));
+ }
+
+ @Override
+ public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle extras,
+ List<Bundle> itemBundleList) throws RuntimeException {
+ final MediaBrowser2Impl browser;
+ try {
+ browser = getBrowser();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ if (browser == null) {
+ // TODO(jaewan): Revisit here. Could be a bug
+ return;
+ }
+
+ List<MediaItem2> result = null;
+ if (itemBundleList != null) {
+ result = new ArrayList<>();
+ for (Bundle bundle : itemBundleList) {
+ result.add(MediaItem2.fromBundle(browser.getContext(), bundle));
+ }
+ }
+ browser.onChildrenLoaded(parentId, page, pageSize, extras, result);
+ }
+
+ @Override
+ public void onSearchResultLoaded(String query, int page, int pageSize, Bundle extras,
+ List<Bundle> itemBundleList) throws RuntimeException {
+ final MediaBrowser2Impl browser;
+ try {
+ browser = getBrowser();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ if (browser == null) {
+ // TODO(jaewan): Revisit here. Could be a bug
+ return;
+ }
+
+ List<MediaItem2> result = null;
+ if (itemBundleList != null) {
+ result = new ArrayList<>();
+ for (Bundle bundle : itemBundleList) {
+ result.add(MediaItem2.fromBundle(browser.getContext(), bundle));
+ }
+ }
+ browser.onSearchResultLoaded(query, page, pageSize, extras, result);
+ }
+}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 74ac5b3..4a9a729 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -88,6 +88,22 @@
private final int mRatingType;
private final PendingIntent mSessionActivity;
+ // mPlayer is set to null when the session is closed, and we shouldn't throw an exception
+ // nor leave log always for using mPlayer when it's null. Here's the reason.
+ // When a MediaSession2 is closed, there could be a pended operation in the session callback
+ // executor that may want to access the player. Here's the sample code snippet for that.
+ //
+ // public void onFoo() {
+ // if (mPlayer == null) return; // first check
+ // mSessionCallbackExecutor.executor(() -> {
+ // // Error. Session may be closed and mPlayer can be null here.
+ // mPlayer.foo();
+ // });
+ // }
+ //
+ // By adding protective code, we can also protect APIs from being called after the close()
+ //
+ // TODO(jaewan): Should we put volatile here?
@GuardedBy("mLock")
private MediaPlayerInterface mPlayer;
@GuardedBy("mLock")
@@ -96,10 +112,6 @@
private PlaybackInfo mPlaybackInfo;
@GuardedBy("mLock")
private MyPlaybackListener mListener;
- @GuardedBy("mLock")
- private PlaylistParams mPlaylistParams;
- @GuardedBy("mLock")
- private List<MediaItem2> mPlaylist;
/**
* Can be only called by the {@link Builder#build()}.
@@ -146,9 +158,7 @@
mContext.getPackageName(), null, id, mSessionStub).getInstance();
}
- setPlayerLocked(player);
- mVolumeProvider = volumeProvider;
- mPlaybackInfo = createPlaybackInfo(volumeProvider, player.getAudioAttributes());
+ setPlayer(player, volumeProvider);
// Ask server for the sanity check, and starts
// Sanity check for making session ID unique 'per package' cannot be done in here.
@@ -198,14 +208,7 @@
if (player == null) {
throw new IllegalArgumentException("player shouldn't be null");
}
- PlaybackInfo info =
- createPlaybackInfo(null /* VolumeProvider */, player.getAudioAttributes());
- synchronized (mLock) {
- setPlayerLocked(player);
- mVolumeProvider = null;
- mPlaybackInfo = info;
- }
- mSessionStub.notifyPlaybackInfoChanged(info);
+ setPlayer(player, null);
}
@Override
@@ -218,25 +221,25 @@
if (volumeProvider == null) {
throw new IllegalArgumentException("volumeProvider shouldn't be null");
}
+ setPlayer(player, volumeProvider);
+ }
+
+ private void setPlayer(MediaPlayerInterface player, VolumeProvider2 volumeProvider) {
PlaybackInfo info = createPlaybackInfo(volumeProvider, player.getAudioAttributes());
synchronized (mLock) {
- setPlayerLocked(player);
+ if (mPlayer != null && mListener != null) {
+ // This might not work for a poorly implemented player.
+ mPlayer.removePlaybackListener(mListener);
+ }
+ mPlayer = player;
+ mListener = new MyPlaybackListener(this, player);
+ player.addPlaybackListener(mCallbackExecutor, mListener);
mVolumeProvider = volumeProvider;
mPlaybackInfo = info;
}
mSessionStub.notifyPlaybackInfoChanged(info);
}
- private void setPlayerLocked(MediaPlayerInterface player) {
- if (mPlayer != null && mListener != null) {
- // This might not work for a poorly implemented player.
- mPlayer.removePlaybackListener(mListener);
- }
- mPlayer = player;
- mListener = new MyPlaybackListener(this, player);
- player.addPlaybackListener(mCallbackExecutor, mListener);
- }
-
private PlaybackInfo createPlaybackInfo(VolumeProvider2 volumeProvider, AudioAttributes attrs) {
PlaybackInfo info;
if (volumeProvider == null) {
@@ -320,36 +323,56 @@
@Override
public void play_impl() {
ensureCallingThread();
- ensurePlayer();
- mPlayer.play();
+ final MediaPlayerInterface player = mPlayer;
+ if (player != null) {
+ player.play();
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
+ }
}
@Override
public void pause_impl() {
ensureCallingThread();
- ensurePlayer();
- mPlayer.pause();
+ final MediaPlayerInterface player = mPlayer;
+ if (player != null) {
+ player.pause();
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
+ }
}
@Override
public void stop_impl() {
ensureCallingThread();
- ensurePlayer();
- mPlayer.stop();
+ final MediaPlayerInterface player = mPlayer;
+ if (player != null) {
+ player.stop();
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
+ }
}
@Override
public void skipToPrevious_impl() {
ensureCallingThread();
- ensurePlayer();
- mPlayer.skipToPrevious();
+ final MediaPlayerInterface player = mPlayer;
+ if (player != null) {
+ player.skipToPrevious();
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
+ }
}
@Override
public void skipToNext_impl() {
ensureCallingThread();
- ensurePlayer();
- mPlayer.skipToNext();
+ final MediaPlayerInterface player = mPlayer;
+ if (player != null) {
+ player.skipToNext();
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
+ }
}
@Override
@@ -367,21 +390,27 @@
@Override
public void setPlaylistParams_impl(PlaylistParams params) {
if (params == null) {
- throw new IllegalArgumentException("PlaylistParams should not be null!");
+ throw new IllegalArgumentException("params shouldn't be null");
}
ensureCallingThread();
- ensurePlayer();
- synchronized (mLock) {
- mPlaylistParams = params;
+ final MediaPlayerInterface player = mPlayer;
+ if (player != null) {
+ player.setPlaylistParams(params);
+ mSessionStub.notifyPlaylistParamsChanged(params);
}
- mPlayer.setPlaylistParams(params);
- mSessionStub.notifyPlaylistParamsChanged(params);
}
@Override
public PlaylistParams getPlaylistParams_impl() {
- // TODO: Do we need to synchronize here for preparing Controller2.setPlaybackParams?
- return mPlaylistParams;
+ final MediaPlayerInterface player = mPlayer;
+ if (player != null) {
+ // TODO(jaewan): Is it safe to be called on any thread?
+ // Otherwise MediaSession2 should cache parameter of setPlaylistParams.
+ return player.getPlaylistParams();
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
+ }
+ return null;
}
//////////////////////////////////////////////////////////////////////////////////////
@@ -412,57 +441,84 @@
@Override
public void setPlaylist_impl(List<MediaItem2> playlist) {
if (playlist == null) {
- throw new IllegalArgumentException("Playlist should not be null!");
+ throw new IllegalArgumentException("playlist shouldn't be null");
}
ensureCallingThread();
- ensurePlayer();
- synchronized (mLock) {
- mPlaylist = playlist;
+ final MediaPlayerInterface player = mPlayer;
+ if (player != null) {
+ player.setPlaylist(playlist);
+ mSessionStub.notifyPlaylistChanged(playlist);
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
}
- mPlayer.setPlaylist(playlist);
- mSessionStub.notifyPlaylistChanged(playlist);
}
@Override
public List<MediaItem2> getPlaylist_impl() {
- synchronized (mLock) {
- return mPlaylist;
+ final MediaPlayerInterface player = mPlayer;
+ if (player != null) {
+ // TODO(jaewan): Is it safe to be called on any thread?
+ // Otherwise MediaSession2 should cache parameter of setPlaylist.
+ return player.getPlaylist();
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
}
+ return null;
}
@Override
public void prepare_impl() {
ensureCallingThread();
- ensurePlayer();
- mPlayer.prepare();
+ final MediaPlayerInterface player = mPlayer;
+ if (player != null) {
+ player.prepare();
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
+ }
}
@Override
public void fastForward_impl() {
ensureCallingThread();
- ensurePlayer();
- mPlayer.fastForward();
+ final MediaPlayerInterface player = mPlayer;
+ if (player != null) {
+ player.fastForward();
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
+ }
}
@Override
public void rewind_impl() {
ensureCallingThread();
- ensurePlayer();
- mPlayer.rewind();
+ final MediaPlayerInterface player = mPlayer;
+ if (player != null) {
+ player.rewind();
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
+ }
}
@Override
public void seekTo_impl(long pos) {
ensureCallingThread();
- ensurePlayer();
- mPlayer.seekTo(pos);
+ final MediaPlayerInterface player = mPlayer;
+ if (player != null) {
+ player.seekTo(pos);
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
+ }
}
@Override
public void setCurrentPlaylistItem_impl(int index) {
ensureCallingThread();
- ensurePlayer();
- mPlayer.setCurrentPlaylistItem(index);
+ final MediaPlayerInterface player = mPlayer;
+ if (player != null) {
+ player.setCurrentPlaylistItem(index);
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
+ }
}
@Override
@@ -497,10 +553,15 @@
@Override
public PlaybackState2 getPlaybackState_impl() {
ensureCallingThread();
- ensurePlayer();
- // TODO(jaewan): Is it safe to be called on any thread?
- // Otherwise we should cache the result from listener.
- return mPlayer.getPlaybackState();
+ final MediaPlayerInterface player = mPlayer;
+ if (player != null) {
+ // TODO(jaewan): Is it safe to be called on any thread?
+ // Otherwise MediaSession2 should cache the result from listener.
+ return player.getPlaybackState();
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
+ }
+ return null;
}
///////////////////////////////////////////////////
@@ -527,14 +588,6 @@
}*/
}
- private void ensurePlayer() {
- // TODO(jaewan): Should we pend command instead? Follow the decision from MP2.
- // Alternatively we can add a API like setAcceptsPendingCommands(boolean).
- if (mPlayer == null) {
- throw new IllegalStateException("Player isn't set");
- }
- }
-
private void notifyPlaybackStateChangedNotLocked(PlaybackState2 state) {
List<PlaybackListenerHolder> listeners = new ArrayList<>();
synchronized (mLock) {
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index d396d73..759d580 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -21,7 +21,6 @@
import android.media.MediaController2;
import android.media.MediaItem2;
import android.media.MediaLibraryService2.LibraryRoot;
-import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
import android.media.MediaSession2;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
@@ -37,9 +36,11 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.support.annotation.GuardedBy;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import com.android.media.MediaLibraryService2Impl.MediaLibrarySessionImpl;
import com.android.media.MediaSession2Impl.CommandButtonImpl;
import com.android.media.MediaSession2Impl.ControllerInfoImpl;
@@ -94,6 +95,27 @@
return session;
}
+ private MediaLibrarySessionImpl getLibrarySession() throws IllegalStateException {
+ final MediaSession2Impl session = getSession();
+ if (!(session instanceof MediaLibrarySessionImpl)) {
+ throw new RuntimeException("Session isn't a library session");
+ }
+ return (MediaLibrarySessionImpl) session;
+ }
+
+ private ControllerInfo getController(IMediaSession2Callback caller) {
+ // TODO(jaewan): Find a way to return connection-in-progress-controller
+ // to be included here, because session owner may want to send some datas
+ // while onConnected() hasn't returned.
+ synchronized (mLock) {
+ return mControllers.get(caller.asBinder());
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ // AIDL methods for session overrides
+ //////////////////////////////////////////////////////////////////////////////////////////////
+
@Override
public void connect(String callingPackage, final IMediaSession2Callback callback)
throws RuntimeException {
@@ -485,32 +507,28 @@
});
}
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ // AIDL methods for LibrarySession overrides
+ //////////////////////////////////////////////////////////////////////////////////////////////
+
@Override
public void getBrowserRoot(IMediaSession2Callback caller, Bundle rootHints)
throws RuntimeException {
- final MediaSession2Impl sessionImpl = getSession();
- if (!(sessionImpl.getCallback() instanceof MediaLibrarySessionCallback)) {
- if (DEBUG) {
- Log.d(TAG, "Session cannot hand getLibraryRoot()");
- }
- return;
- }
+ final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
if (DEBUG) {
- Log.d(TAG, "getBrowerRoot from a controller that hasn't connected. Ignore");
+ Log.d(TAG, "getBrowerRoot() from a controller that hasn't connected. Ignore");
}
return;
}
sessionImpl.getCallbackExecutor().execute(() -> {
- final MediaSession2Impl session = mSession.get();
+ final MediaLibrarySessionImpl session = getLibrarySession();
if (session == null) {
return;
}
- final MediaLibrarySessionCallback libraryCallback =
- (MediaLibrarySessionCallback) session.getCallback();
final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
- LibraryRoot root = libraryCallback.onGetRoot(controller, rootHints);
+ LibraryRoot root = session.getCallback().onGetRoot(controller, rootHints);
try {
controllerImpl.getControllerBinder().onGetRootResult(rootHints,
root == null ? null : root.getRootId(),
@@ -522,15 +540,182 @@
});
}
- private ControllerInfo getController(IMediaSession2Callback caller) {
- // TODO(jaewan): Device a way to return connection-in-progress-controller
- // to be included here, because session owner may want to send some datas
- // while onConnected() hasn't returned.
- synchronized (mLock) {
- return mControllers.get(caller.asBinder());
+ @Override
+ public void getItem(IMediaSession2Callback caller, String mediaId) throws RuntimeException {
+ final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "getItem() from a controller that hasn't connected. Ignore");
+ }
+ return;
}
+ if (mediaId == null) {
+ if (DEBUG) {
+ Log.d(TAG, "mediaId shouldn't be null");
+ }
+ return;
+ }
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaLibrarySessionImpl session = getLibrarySession();
+ if (session == null) {
+ return;
+ }
+ final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
+ MediaItem2 result = session.getCallback().onLoadItem(controller, mediaId);
+ try {
+ controllerImpl.getControllerBinder().onItemLoaded(
+ mediaId, result == null ? null : result.toBundle());
+ } catch (RemoteException e) {
+ // Controller may be died prematurely.
+ // TODO(jaewan): Handle this.
+ }
+ });
}
+ @Override
+ public void getChildren(IMediaSession2Callback caller, String parentId, int page,
+ int pageSize, Bundle extras) throws RuntimeException {
+ final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "getChildren() from a controller that hasn't connected. Ignore");
+ }
+ return;
+ }
+ if (parentId == null) {
+ if (DEBUG) {
+ Log.d(TAG, "parentId shouldn't be null");
+ }
+ return;
+ }
+ if (page < 1 || pageSize < 1) {
+ if (DEBUG) {
+ Log.d(TAG, "Neither page nor pageSize should be less than 1");
+ }
+ return;
+ }
+
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaLibrarySessionImpl session = getLibrarySession();
+ if (session == null) {
+ return;
+ }
+ final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
+ List<MediaItem2> result = session.getCallback().onLoadChildren(
+ controller, parentId, page, pageSize, extras);
+ if (result != null && result.size() > pageSize) {
+ throw new IllegalArgumentException("onLoadChildren() shouldn't return media items "
+ + "more than pageSize. result.size()=" + result.size() + " pageSize="
+ + pageSize);
+ }
+
+ List<Bundle> bundleList = null;
+ if (result != null) {
+ bundleList = new ArrayList<>();
+ for (MediaItem2 item : result) {
+ bundleList.add(item == null ? null : item.toBundle());
+ }
+ }
+
+ try {
+ controllerImpl.getControllerBinder().onChildrenLoaded(
+ parentId, page, pageSize, extras, bundleList);
+ } catch (RemoteException e) {
+ // Controller may be died prematurely.
+ // TODO(jaewan): Handle this.
+ }
+ });
+ }
+
+ @Override
+ public void search(IMediaSession2Callback caller, String query, Bundle extras) {
+ final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "search() from a controller that hasn't connected. Ignore");
+ }
+ return;
+ }
+ if (TextUtils.isEmpty(query)) {
+ if (DEBUG) {
+ Log.d(TAG, "query shouldn't be empty");
+ }
+ return;
+ }
+
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaLibrarySessionImpl session = getLibrarySession();
+ if (session == null) {
+ return;
+ }
+ final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
+ session.getCallback().onSearch(controller, query, extras);
+ });
+ }
+
+ @Override
+ public void getSearchResult(IMediaSession2Callback caller, String query, int page,
+ int pageSize, Bundle extras) {
+ final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "getSearchResult() from a controller that hasn't connected. Ignore");
+ }
+ return;
+ }
+ if (TextUtils.isEmpty(query)) {
+ if (DEBUG) {
+ Log.d(TAG, "query shouldn't be empty");
+ }
+ return;
+ }
+ if (page < 1 || pageSize < 1) {
+ if (DEBUG) {
+ Log.d(TAG, "Neither page nor pageSize should be less than 1");
+ }
+ return;
+ }
+
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaLibrarySessionImpl session = getLibrarySession();
+ if (session == null) {
+ return;
+ }
+ final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
+ List<MediaItem2> result = session.getCallback().onLoadSearchResult(
+ controller, query, page, pageSize, extras);
+ if (result != null && result.size() > pageSize) {
+ throw new IllegalArgumentException("onLoadSearchResult() shouldn't return media "
+ + "items more than pageSize. result.size()=" + result.size() + " pageSize="
+ + pageSize);
+ }
+
+ List<Bundle> bundleList = null;
+ if (result != null) {
+ bundleList = new ArrayList<>();
+ for (MediaItem2 item : result) {
+ bundleList.add(item == null ? null : item.toBundle());
+ }
+ }
+
+ try {
+ controllerImpl.getControllerBinder().onSearchResultLoaded(
+ query, page, pageSize, extras, bundleList);
+ } catch (RemoteException e) {
+ // Controller may be died prematurely.
+ // TODO(jaewan): Handle this.
+ }
+ });
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ // APIs for MediaSession2Impl
+ //////////////////////////////////////////////////////////////////////////////////////////////
+
// TODO(jaewan): Need a way to get controller with permissions
public List<ControllerInfo> getControllers() {
ArrayList<ControllerInfo> controllers = new ArrayList<>();
diff --git a/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java b/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
index b2b7959..f36aa43 100644
--- a/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
@@ -34,6 +34,8 @@
import android.os.IBinder;
import android.text.TextUtils;
+import java.util.List;
+
public class SessionToken2Impl implements SessionToken2Provider {
private static final String KEY_UID = "android.media.token.uid";
private static final String KEY_TYPE = "android.media.token.type";
@@ -73,25 +75,24 @@
}
}
mUid = uid;
- // calculate id and type
- Intent serviceIntent = new Intent(MediaLibraryService2.SERVICE_INTERFACE);
- serviceIntent.setClassName(packageName, serviceName);
- String id = getSessionId(manager.resolveService(serviceIntent,
- PackageManager.GET_META_DATA));
- int type = TYPE_LIBRARY_SERVICE;
- if (id == null) {
+
+ // Infer id and type from package name and service name
+ // TODO(jaewan): Handle multi-user.
+ String id = getSessionIdFromService(manager, MediaLibraryService2.SERVICE_INTERFACE,
+ packageName, serviceName);
+ if (id != null) {
+ mId = id;
+ mType = TYPE_LIBRARY_SERVICE;
+ } else {
// retry with session service
- serviceIntent.setClassName(packageName, serviceName);
- id = getSessionId(manager.resolveService(serviceIntent,
- PackageManager.GET_META_DATA));
- type = TYPE_SESSION_SERVICE;
+ mId = getSessionIdFromService(manager, MediaSessionService2.SERVICE_INTERFACE,
+ packageName, serviceName);
+ mType = TYPE_SESSION_SERVICE;
}
- if (id == null) {
+ if (mId == null) {
throw new IllegalArgumentException("service " + serviceName + " doesn't implement"
- + " session service nor library service");
+ + " session service nor library service. Use service's full name.");
}
- mId = id;
- mType = type;
mPackageName = packageName;
mServiceName = serviceName;
mSessionBinder = null;
@@ -109,6 +110,29 @@
mInstance = new SessionToken2(this);
}
+ private static String getSessionIdFromService(PackageManager manager, String serviceInterface,
+ String packageName, String serviceName) {
+ Intent serviceIntent = new Intent(serviceInterface);
+ serviceIntent.setPackage(packageName);
+ // Use queryIntentServices to find services with MediaLibraryService2.SERVICE_INTERFACE.
+ // We cannot use resolveService with intent specified class name, because resolveService
+ // ignores actions if Intent.setClassName() is specified.
+ List<ResolveInfo> list = manager.queryIntentServices(
+ serviceIntent, PackageManager.GET_META_DATA);
+ if (list != null) {
+ for (int i = 0; i < list.size(); i++) {
+ ResolveInfo resolveInfo = list.get(i);
+ if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+ continue;
+ }
+ if (TextUtils.equals(resolveInfo.serviceInfo.name, serviceName)) {
+ return getSessionId(resolveInfo);
+ }
+ }
+ }
+ return null;
+ }
+
public static String getSessionId(ResolveInfo resolveInfo) {
if (resolveInfo == null || resolveInfo.serviceInfo == null) {
return null;
diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
index dfc0792..318cbf9 100644
--- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
@@ -54,6 +54,7 @@
static final String ARGUMENT_KEY_FULLSCREEN = "fullScreen";
+ // TODO: Move these constants to public api to support custom video view.
static final String KEY_STATE_CONTAINS_SUBTITLE = "StateContainsSubtitle";
static final String EVENT_UPDATE_SUBTITLE_STATUS = "UpdateSubtitleStatus";
@@ -72,9 +73,7 @@
private TextView mTitleView;
private int mDuration;
private int mPrevState;
- private int mCurrentVisibility;
private long mPlaybackActions;
- private boolean mShowing;
private boolean mDragging;
private boolean mIsFullScreen;
private boolean mOverflowExpanded;
@@ -233,6 +232,7 @@
public void setEnabled_impl(boolean enabled) {
super.setEnabled_impl(enabled);
+ // TODO: Merge the below code with disableUnsupportedButtons().
if (mPlayPauseButton != null) {
mPlayPauseButton.setEnabled(enabled);
}
@@ -255,24 +255,15 @@
}
@Override
- public void onVisibilityAggregated_impl(boolean invisible) {
- super.onVisibilityAggregated_impl(invisible);
+ public void onVisibilityAggregated_impl(boolean isVisible) {
+ super.onVisibilityAggregated_impl(isVisible);
- int visibility = mInstance.getVisibility();
- if (mCurrentVisibility != visibility) {
- mInstance.setVisibility(visibility);
- mCurrentVisibility = visibility;
-
- if (visibility == View.VISIBLE) {
- setProgress();
- disableUnsupportedButtons();
- // cause the progress bar to be updated even if mShowing
- // was already true. This happens, for example, if we're
- // paused with the progress bar showing the user hits play.
- mInstance.post(mShowProgress);
- } else if (visibility == View.GONE) {
- mInstance.removeCallbacks(mShowProgress);
- }
+ if (isVisible) {
+ disableUnsupportedButtons();
+ mInstance.removeCallbacks(mUpdateProgress);
+ mInstance.post(mUpdateProgress);
+ } else {
+ mInstance.removeCallbacks(mUpdateProgress);
}
}
@@ -465,12 +456,13 @@
}
}
- private final Runnable mShowProgress = new Runnable() {
+ private final Runnable mUpdateProgress = new Runnable() {
@Override
public void run() {
int pos = setProgress();
- if (!mDragging && mShowing && isPlaying()) {
- mInstance.postDelayed(mShowProgress,
+ boolean isShowing = mInstance.getVisibility() == View.VISIBLE;
+ if (!mDragging && isShowing && isPlaying()) {
+ mInstance.postDelayed(mUpdateProgress,
DEFAULT_PROGRESS_UPDATE_TIME_MS - (pos % DEFAULT_PROGRESS_UPDATE_TIME_MS));
}
}
@@ -557,7 +549,7 @@
// the seekbar and b) once the user is done dragging the thumb
// we will post one of these messages to the queue again and
// this ensures that there will be exactly one message queued up.
- mInstance.removeCallbacks(mShowProgress);
+ mInstance.removeCallbacks(mUpdateProgress);
// Check if playback is currently stopped. In this case, update the pause button to
// show the play image instead of the replay image.
@@ -602,7 +594,7 @@
// Ensure that progress is properly updated in the future,
// the call to show() does not guarantee this because it is a
// no-op if we are already showing.
- mInstance.post(mShowProgress);
+ mInstance.post(mUpdateProgress);
}
};
@@ -735,6 +727,8 @@
ApiHelper.getLibResources().getDrawable(
R.drawable.ic_pause_circle_filled, null));
mPlayPauseButton.setContentDescription(mPauseDescription);
+ mInstance.removeCallbacks(mUpdateProgress);
+ mInstance.post(mUpdateProgress);
break;
case PlaybackState.STATE_PAUSED:
mPlayPauseButton.setImageDrawable(
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index 256003f..ea7e714 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -31,7 +31,9 @@
import android.media.ClosedCaptionRenderer;
import android.media.Metadata;
import android.media.PlaybackParams;
+import android.media.SRTRenderer;
import android.media.SubtitleController;
+import android.media.TimedText;
import android.media.TtmlRenderer;
import android.media.WebVttRenderer;
import android.media.session.MediaController;
@@ -122,6 +124,7 @@
private int mVideoWidth;
private int mVideoHeight;
+ private ArrayList<Integer> mSubtitleTrackIndices;
private SubtitleView mSubtitleView;
private boolean mSubtitleEnabled;
private int mSelectedTrackIndex; // selected subtitle track index as MediaPlayer2 returns
@@ -191,13 +194,11 @@
if (enableControlView) {
mMediaControlView = new MediaControlView2(mInstance.getContext());
}
- boolean enableSubtitle = (attrs == null) || attrs.getAttributeBooleanValue(
+
+ mSubtitleEnabled = (attrs == null) || attrs.getAttributeBooleanValue(
"http://schemas.android.com/apk/res/android",
- "enableSubtitle", true);
- if (enableSubtitle) {
- Log.d(TAG, "enableSubtitle attribute is true.");
- // TODO: implement
- }
+ "enableSubtitle", false);
+
int viewType = (attrs == null) ? VideoView2.VIEW_TYPE_SURFACEVIEW
: attrs.getAttributeIntValue(
"http://schemas.android.com/apk/res/android",
@@ -240,27 +241,8 @@
@Override
public void setSubtitleEnabled_impl(boolean enable) {
- if (enable) {
- // 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);
- }
- }
- if (subtitleTrackIndices.size() > 0) {
- // Select first subtitle track
- mSelectedTrackIndex = subtitleTrackIndices.get(0);
- mMediaPlayer.selectTrack(mSelectedTrackIndex);
- }
- } else {
- if (mSelectedTrackIndex != INVALID_TRACK_INDEX) {
- mMediaPlayer.deselectTrack(mSelectedTrackIndex);
- mSelectedTrackIndex = INVALID_TRACK_INDEX;
- }
+ if (enable != mSubtitleEnabled) {
+ selectOrDeselectSubtitle(enable);
}
mSubtitleEnabled = enable;
}
@@ -572,7 +554,13 @@
controller.registerRenderer(new TtmlRenderer(context));
controller.registerRenderer(new Cea708CaptionRenderer(context));
controller.registerRenderer(new ClosedCaptionRenderer(context));
- mMediaPlayer.setSubtitleAnchor(controller, (SubtitleController.Anchor) mSubtitleView);
+ controller.registerRenderer(new SRTRenderer(context));
+ mMediaPlayer.setSubtitleAnchor(
+ controller, (SubtitleController.Anchor) mSubtitleView);
+ // TODO: Remove timed text related code later once relevant Renderer is defined.
+ // This is just for debugging purpose.
+ mMediaPlayer.setOnTimedTextListener(mTimedTextListener);
+
mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
mMediaPlayer.setOnCompletionListener(mCompletionListener);
@@ -622,7 +610,6 @@
/*
* Reset the media player in any state
*/
- // TODO: Figure out if the legacy code's boolean parameter: cleartargetstate is necessary.
private void resetPlayer() {
if (mMediaPlayer != null) {
mMediaPlayer.reset();
@@ -774,7 +761,58 @@
return false;
}
PlaybackInfo playbackInfo = mMediaController.getPlaybackInfo();
- return (playbackInfo != null) && (playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE);
+ return (playbackInfo != null)
+ && (playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE);
+ }
+
+ private void selectOrDeselectSubtitle(boolean select) {
+ if (!isInPlaybackState()) {
+ return;
+ }
+ if (select) {
+ if (mSubtitleTrackIndices.size() > 0) {
+ // Select first subtitle track
+ mSelectedTrackIndex = mSubtitleTrackIndices.get(0);
+ mMediaPlayer.selectTrack(mSelectedTrackIndex);
+ mSubtitleView.setVisibility(View.VISIBLE);
+ }
+ } else {
+ if (mSelectedTrackIndex != INVALID_TRACK_INDEX) {
+ mMediaPlayer.deselectTrack(mSelectedTrackIndex);
+ mSelectedTrackIndex = INVALID_TRACK_INDEX;
+ mSubtitleView.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ private void extractSubtitleTracks() {
+ MediaPlayer.TrackInfo[] trackInfos = mMediaPlayer.getTrackInfo();
+ boolean previouslyNoTracks = mSubtitleTrackIndices == null
+ || mSubtitleTrackIndices.size() == 0;
+ mSubtitleTrackIndices = new ArrayList<>();
+ for (int i = 0; i < trackInfos.length; ++i) {
+ int trackType = trackInfos[i].getTrackType();
+ if (trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE
+ || trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) {
+ mSubtitleTrackIndices.add(i);
+ }
+ }
+ if (mSubtitleTrackIndices.size() > 0) {
+ if (previouslyNoTracks) {
+ selectOrDeselectSubtitle(mSubtitleEnabled);
+ // Notify MediaControlView that subtitle track exists
+ // TODO: Send the subtitle track list to MediaSession for MCV2.
+ Bundle data = new Bundle();
+ data.putBoolean(MediaControlView2Impl.KEY_STATE_CONTAINS_SUBTITLE, true);
+ mMediaSession.sendSessionEvent(
+ MediaControlView2Impl.EVENT_UPDATE_SUBTITLE_STATUS, data);
+ }
+ } else {
+ Bundle data = new Bundle();
+ data.putBoolean(MediaControlView2Impl.KEY_STATE_CONTAINS_SUBTITLE, false);
+ mMediaSession.sendSessionEvent(
+ MediaControlView2Impl.EVENT_UPDATE_SUBTITLE_STATUS, data);
+ }
}
MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =
@@ -805,6 +843,7 @@
mCurrentState = STATE_PREPARED;
// Create and set playback state for MediaControlView2
updatePlaybackState();
+ extractSubtitleTracks();
if (mOnPreparedListener != null) {
mOnPreparedListener.onPrepared(mInstance);
@@ -884,6 +923,10 @@
if (mOnInfoListener != null) {
mOnInfoListener.onInfo(mInstance, what, extra);
}
+
+ if (what == MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
+ extractSubtitleTracks();
+ }
return true;
}
};
@@ -954,6 +997,15 @@
}
};
+ // TODO: Remove timed text related code later once relevant Renderer is defined.
+ // This is just for debugging purpose.
+ private MediaPlayer.OnTimedTextListener mTimedTextListener =
+ new MediaPlayer.OnTimedTextListener() {
+ public void onTimedText(MediaPlayer mp, TimedText text) {
+ Log.d(TAG, "TimedText: " + text.getText());
+ }
+ };
+
private class MediaSessionCallback extends MediaSession.Callback {
@Override
public void onCommand(String command, Bundle args, ResultReceiver receiver) {
diff --git a/packages/MediaComponents/test/AndroidManifest.xml b/packages/MediaComponents/test/AndroidManifest.xml
index 48e4292..5ebe31a 100644
--- a/packages/MediaComponents/test/AndroidManifest.xml
+++ b/packages/MediaComponents/test/AndroidManifest.xml
@@ -34,7 +34,7 @@
<intent-filter>
<action android:name="android.media.MediaLibraryService2" />
</intent-filter>
- <meta-data android:name="android.media.session" android:value="TestBrowser" />
+ <meta-data android:name="android.media.session" android:value="TestLibrary" />
</service>
</application>
diff --git a/packages/MediaComponents/test/runtest.sh b/packages/MediaComponents/test/runtest.sh
index 920fa96..edce230 100644
--- a/packages/MediaComponents/test/runtest.sh
+++ b/packages/MediaComponents/test/runtest.sh
@@ -146,9 +146,10 @@
fi
target_dir=$(dirname ${target_dir})
local package=$(sed -n 's/^.*\bpackage\b="\([a-z0-9\._]*\)".*$/\1/p' ${target_dir}/AndroidManifest.xml)
- local apk_path=$(find ${OUT} -name ${target}.apk)
- if [[ -z "${apk_path}" ]]; then
- echo "Cannot locate ${target}.apk" && break
+ local apk_path=$(find ${OUT}/system ${OUT}/data -name ${target}.apk)
+ local apk_num=$(find ${OUT}/system ${OUT}/data -name ${target}.apk | wc -l)
+ if [[ "${apk_num}" != "1" ]]; then
+ echo "Cannot locate a ${target}.apk. Found ${apk_num} apks" && break
fi
echo "Installing ${target}.apk. path=${apk_path}"
${adb} install -r ${apk_path}
diff --git a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
index 4cd8177..b60fde3 100644
--- a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
@@ -18,12 +18,15 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertNull;
import android.annotation.Nullable;
import android.content.Context;
import android.media.MediaBrowser2.BrowserCallback;
import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandButton;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.PlaylistParams;
import android.os.Bundle;
@@ -36,6 +39,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -63,6 +67,9 @@
interface TestBrowserCallbackInterface extends TestControllerCallbackInterface {
// Browser specific callbacks
default void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {}
+ default void onItemLoaded(String mediaId, MediaItem2 result) {}
+ default void onChildrenLoaded(String parentId, int page, int pageSize, Bundle options,
+ List<MediaItem2> result) {}
}
@Test
@@ -88,6 +95,129 @@
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
+ @Test
+ public void testGetItem() throws InterruptedException {
+ final String mediaId = MockMediaLibraryService2.MEDIA_ID_GET_ITEM;
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+ @Override
+ public void onItemLoaded(String mediaIdOut, MediaItem2 result) {
+ assertEquals(mediaId, mediaIdOut);
+ assertNotNull(result);
+ assertEquals(mediaId, result.getMediaId());
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.getItem(mediaId);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testGetItemNullResult() throws InterruptedException {
+ final String mediaId = "random_media_id";
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+ @Override
+ public void onItemLoaded(String mediaIdOut, MediaItem2 result) {
+ assertEquals(mediaId, mediaIdOut);
+ assertNull(result);
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.getItem(mediaId);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testGetChildren() throws InterruptedException {
+ final String parentId = MockMediaLibraryService2.PARENT_ID;
+ final int page = 4;
+ final int pageSize = 10;
+ final Bundle options = new Bundle();
+ options.putString(TAG, TAG);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+ @Override
+ public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
+ Bundle optionsOut, List<MediaItem2> result) {
+ assertEquals(parentId, parentIdOut);
+ assertEquals(page, pageOut);
+ assertEquals(pageSize, pageSizeOut);
+ assertTrue(TestUtils.equals(options, optionsOut));
+ assertNotNull(result);
+
+ int fromIndex = (page - 1) * pageSize;
+ int toIndex = Math.min(page * pageSize,
+ MockMediaLibraryService2.GET_CHILDREN_RESULT.size());
+
+ // Compare the given results with originals.
+ for (int originalIndex = fromIndex; originalIndex < toIndex; originalIndex++) {
+ int relativeIndex = originalIndex - fromIndex;
+ assertEquals(
+ MockMediaLibraryService2.GET_CHILDREN_RESULT.get(originalIndex)
+ .getMediaId(),
+ result.get(relativeIndex).getMediaId());
+ }
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.getChildren(parentId, page, pageSize, options);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testGetChildrenEmptyResult() throws InterruptedException {
+ final String parentId = MockMediaLibraryService2.PARENT_ID_NO_CHILDREN;
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+ @Override
+ public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
+ Bundle optionsOut, List<MediaItem2> result) {
+ assertNotNull(result);
+ assertEquals(0, result.size());
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.getChildren(parentId, 1, 1, null);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testGetChildrenNullResult() throws InterruptedException {
+ final String parentId = MockMediaLibraryService2.PARENT_ID_ERROR;
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+ @Override
+ public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
+ Bundle optionsOut, List<MediaItem2> result) {
+ assertNull(result);
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.getChildren(parentId, 1, 1, null);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
public static class TestBrowserCallback extends BrowserCallback
implements WaitForConnectionInterface {
private final TestControllerCallbackInterface mCallbackProxy;
@@ -104,42 +234,41 @@
@CallSuper
@Override
public void onConnected(CommandGroup commands) {
- super.onConnected(commands);
connectLatch.countDown();
}
@CallSuper
@Override
public void onDisconnected() {
- super.onDisconnected();
disconnectLatch.countDown();
}
@Override
public void onPlaybackStateChanged(PlaybackState2 state) {
- super.onPlaybackStateChanged(state);
mCallbackProxy.onPlaybackStateChanged(state);
}
@Override
public void onPlaylistParamsChanged(PlaylistParams params) {
- super.onPlaylistParamsChanged(params);
mCallbackProxy.onPlaylistParamsChanged(params);
}
@Override
public void onPlaybackInfoChanged(MediaController2.PlaybackInfo info) {
- if (mCallbackProxy != null) {
- mCallbackProxy.onPlaybackInfoChanged(info);
- }
+ mCallbackProxy.onPlaybackInfoChanged(info);
}
@Override
public void onCustomCommand(Command command, Bundle args, ResultReceiver receiver) {
- super.onCustomCommand(command, args, receiver);
mCallbackProxy.onCustomCommand(command, args, receiver);
}
+
+ @Override
+ public void onCustomLayoutChanged(List<CommandButton> layout) {
+ mCallbackProxy.onCustomLayoutChanged(layout);
+ }
+
@Override
public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {
super.onGetRootResult(rootHints, rootMediaId, rootExtra);
@@ -150,6 +279,24 @@
}
@Override
+ public void onItemLoaded(String mediaId, MediaItem2 result) {
+ super.onItemLoaded(mediaId, result);
+ if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+ ((TestBrowserCallbackInterface) mCallbackProxy).onItemLoaded(mediaId, result);
+ }
+ }
+
+ @Override
+ public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle options,
+ List<MediaItem2> result) {
+ super.onChildrenLoaded(parentId, page, pageSize, options, result);
+ if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+ ((TestBrowserCallbackInterface) mCallbackProxy)
+ .onChildrenLoaded(parentId, page, pageSize, options, result);
+ }
+ }
+
+ @Override
public void waitForConnect(boolean expect) throws InterruptedException {
if (expect) {
assertTrue(connectLatch.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 07baf58..7bf0fd2 100644
--- a/packages/MediaComponents/test/src/android/media/MediaController2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -21,9 +21,11 @@
import android.content.Intent;
import android.media.MediaPlayerInterface.PlaybackListener;
import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.PlaylistParams;
import android.media.MediaSession2.SessionCallback;
+import android.media.TestServiceRegistry.SessionCallbackProxy;
import android.media.TestUtils.SyncHandler;
import android.net.Uri;
import android.os.Bundle;
@@ -34,6 +36,7 @@
import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
import org.junit.After;
import org.junit.Before;
@@ -618,24 +621,32 @@
@Ignore
@Test
public void testConnectToService_sessionService() throws InterruptedException {
- connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
- testConnectToService();
+ testConnectToService(MockMediaSessionService2.ID);
}
// TODO(jaewan): Reenable when session manager detects app installs
@Ignore
@Test
public void testConnectToService_libraryService() throws InterruptedException {
- connectToService(TestUtils.getServiceToken(mContext, MockMediaLibraryService2.ID));
- testConnectToService();
+ testConnectToService(MockMediaLibraryService2.ID);
}
- public void testConnectToService() throws InterruptedException {
- TestServiceRegistry serviceInfo = TestServiceRegistry.getInstance();
- ControllerInfo info = serviceInfo.getOnConnectControllerInfo();
- assertEquals(mContext.getPackageName(), info.getPackageName());
- assertEquals(Process.myUid(), info.getUid());
- assertFalse(info.isTrusted());
+ public void testConnectToService(String id) throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallbackProxy proxy = new SessionCallbackProxy(mContext) {
+ @Override
+ public CommandGroup onConnect(ControllerInfo controller) {
+ if (Process.myUid() == controller.getUid()) {
+ assertEquals(mContext.getPackageName(), controller.getPackageName());
+ assertFalse(controller.isTrusted());
+ latch.countDown();;
+ }
+ return super.onConnect(controller);
+ }
+ };
+ TestServiceRegistry.getInstance().setSessionCallbackProxy(proxy);
+ mController = createController(TestUtils.getServiceToken(mContext, id));
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// Test command from controller to session service
mController.play();
@@ -701,27 +712,26 @@
@Ignore
@Test
public void testClose_sessionService() throws InterruptedException {
- connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
- testCloseFromService();
+ testCloseFromService(MockMediaSessionService2.ID);
}
// TODO(jaewan): Reenable when session manager detects app installs
@Ignore
@Test
public void testClose_libraryService() throws InterruptedException {
- connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
- testCloseFromService();
+ testCloseFromService(MockMediaLibraryService2.ID);
}
- private void testCloseFromService() throws InterruptedException {
- final String id = mController.getSessionToken().getId();
+ private void testCloseFromService(String id) throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
- TestServiceRegistry.getInstance().setServiceInstanceChangedCallback((service) -> {
- if (service == null) {
- // Destroying..
+ final SessionCallbackProxy proxy = new SessionCallbackProxy(mContext) {
+ @Override
+ public void onServiceDestroyed() {
latch.countDown();
}
- });
+ };
+ TestServiceRegistry.getInstance().setSessionCallbackProxy(proxy);
+ mController = createController(TestUtils.getServiceToken(mContext, id));
mController.close();
// Wait until close triggers onDestroy() of the session service.
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
index b00633b..f5ac6aa 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
@@ -33,6 +33,8 @@
import android.media.MediaPlayerInterface.PlaybackListener;
import android.media.MediaSession2.Builder;
import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandButton;
+import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.PlaylistParams;
import android.media.MediaSession2.SessionCallback;
@@ -353,6 +355,45 @@
}
@Test
+ public void testSetCustomLayout() throws InterruptedException {
+ final List<CommandButton> buttons = new ArrayList<>();
+ buttons.add(new CommandButton.Builder(mContext)
+ .setCommand(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PLAY))
+ .setDisplayName("button").build());
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallback sessionCallback = new SessionCallback(mContext) {
+ @Override
+ public CommandGroup onConnect(ControllerInfo controller) {
+ if (mContext.getPackageName().equals(controller.getPackageName())) {
+ mSession.setCustomLayout(controller, buttons);
+ }
+ return super.onConnect(controller);
+ }
+ };
+
+ try (final MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
+ .setId("testSetCustomLayout")
+ .setSessionCallback(sHandlerExecutor, sessionCallback)
+ .build()) {
+ final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+ @Override
+ public void onCustomLayoutChanged(List<CommandButton> layout) {
+ assertEquals(layout.size(), buttons.size());
+ for (int i = 0; i < layout.size(); i++) {
+ assertEquals(layout.get(i).getCommand(), buttons.get(i).getCommand());
+ assertEquals(layout.get(i).getDisplayName(),
+ buttons.get(i).getDisplayName());
+ }
+ latch.countDown();
+ }
+ };
+ final MediaController2 controller =
+ createController(session.getToken(), true, callback);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
public void testSendCustomAction() throws InterruptedException {
final Command testCommand =
new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE);
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
index 513fa29..f5abfff 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.media.MediaController2.ControllerCallback;
import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandButton;
import android.media.MediaSession2.CommandGroup;
import android.os.Bundle;
import android.os.HandlerThread;
@@ -59,15 +60,15 @@
ControllerCallback getCallback();
}
+ // Any change here should be also reflected to the TestControllerCallback and
+ // TestBrowserCallback
interface TestControllerCallbackInterface {
- // Add methods in ControllerCallback/BrowserCallback that you want to test.
+ // Add methods in ControllerCallback that you want to test.
default void onPlaylistChanged(List<MediaItem2> playlist) {}
default void onPlaylistParamsChanged(MediaSession2.PlaylistParams params) {}
default void onPlaybackInfoChanged(MediaController2.PlaybackInfo info) {}
-
- // Currently empty. Add methods in ControllerCallback/BrowserCallback that you want to test.
- default void onPlaybackStateChanged(PlaybackState2 state) { }
-
+ default void onPlaybackStateChanged(PlaybackState2 state) {}
+ default void onCustomLayoutChanged(List<CommandButton> layout) {}
default void onCustomCommand(Command command, Bundle args, ResultReceiver receiver) {}
}
@@ -188,26 +189,22 @@
@CallSuper
@Override
public void onConnected(CommandGroup commands) {
- super.onConnected(commands);
connectLatch.countDown();
}
@CallSuper
@Override
public void onDisconnected() {
- super.onDisconnected();
disconnectLatch.countDown();
}
@Override
public void onPlaybackStateChanged(PlaybackState2 state) {
- super.onPlaybackStateChanged(state);
mCallbackProxy.onPlaybackStateChanged(state);
}
@Override
public void onCustomCommand(Command command, Bundle args, ResultReceiver receiver) {
- super.onCustomCommand(command, args, receiver);
mCallbackProxy.onCustomCommand(command, args, receiver);
}
@@ -231,23 +228,22 @@
@Override
public void onPlaylistChanged(List<MediaItem2> params) {
- if (mCallbackProxy != null) {
- mCallbackProxy.onPlaylistChanged(params);
- }
+ mCallbackProxy.onPlaylistChanged(params);
}
@Override
public void onPlaylistParamsChanged(MediaSession2.PlaylistParams params) {
- if (mCallbackProxy != null) {
- mCallbackProxy.onPlaylistParamsChanged(params);
- }
+ mCallbackProxy.onPlaylistParamsChanged(params);
}
@Override
public void onPlaybackInfoChanged(MediaController2.PlaybackInfo info) {
- if (mCallbackProxy != null) {
- mCallbackProxy.onPlaybackInfoChanged(info);
- }
+ mCallbackProxy.onPlaybackInfoChanged(info);
+ }
+
+ @Override
+ public void onCustomLayoutChanged(List<CommandButton> layout) {
+ mCallbackProxy.onCustomLayoutChanged(layout);
}
}
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
index ec69ff6..5fabebc 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
@@ -23,9 +23,17 @@
import android.content.Context;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
+import android.media.TestServiceRegistry.SessionCallbackProxy;
import android.media.TestUtils.SyncHandler;
import android.os.Bundle;
import android.os.Process;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+
+import java.util.concurrent.Executor;
import javax.annotation.concurrent.GuardedBy;
@@ -38,6 +46,20 @@
public static final String ROOT_ID = "rootId";
public static final Bundle EXTRA = new Bundle();
+
+ public static final String MEDIA_ID_GET_ITEM = "media_id_get_item";
+
+ public static final String PARENT_ID = "parent_id";
+ public static final String PARENT_ID_NO_CHILDREN = "parent_id_no_children";
+ public static final String PARENT_ID_ERROR = "parent_id_error";
+ public static final List<MediaItem2> GET_CHILDREN_RESULT = new ArrayList<>();
+
+ private static final int CHILDREN_COUNT = 100;
+ private static final DataSourceDesc DATA_SOURCE_DESC =
+ new DataSourceDesc.Builder().setDataSource(new FileDescriptor()).build();
+
+ private static final String TAG = "MockMediaLibrarySvc2";
+
static {
EXTRA.putString(ROOT_ID, ROOT_ID);
}
@@ -46,20 +68,36 @@
private MediaLibrarySession mSession;
+ public MockMediaLibraryService2() {
+ super();
+ GET_CHILDREN_RESULT.clear();
+ String mediaIdPrefix = "media_id_";
+ for (int i = 0; i < CHILDREN_COUNT; i++) {
+ GET_CHILDREN_RESULT.add(createMediaItem(mediaIdPrefix + i));
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ TestServiceRegistry.getInstance().setServiceInstance(this);
+ }
+
@Override
public MediaLibrarySession onCreateSession(String sessionId) {
final MockPlayer player = new MockPlayer(1);
final SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
- try {
- handler.postAndSync(() -> {
- TestLibrarySessionCallback callback = new TestLibrarySessionCallback();
- mSession = new MediaLibrarySessionBuilder(MockMediaLibraryService2.this,
- player, (runnable) -> handler.post(runnable), callback)
- .setId(sessionId).build();
- });
- } catch (InterruptedException e) {
- fail(e.toString());
+ final Executor executor = (runnable) -> handler.post(runnable);
+ SessionCallbackProxy sessionCallbackProxy = TestServiceRegistry.getInstance()
+ .getSessionCallbackProxy();
+ if (sessionCallbackProxy == null) {
+ // Ensures non-null
+ sessionCallbackProxy = new SessionCallbackProxy(this) {};
}
+ TestLibrarySessionCallback callback =
+ new TestLibrarySessionCallback(sessionCallbackProxy);
+ mSession = new MediaLibrarySessionBuilder(MockMediaLibraryService2.this, player,
+ executor, callback).setId(sessionId).build();
return mSession;
}
@@ -81,24 +119,76 @@
}
private class TestLibrarySessionCallback extends MediaLibrarySessionCallback {
- public TestLibrarySessionCallback() {
+ private final SessionCallbackProxy mCallbackProxy;
+
+ public TestLibrarySessionCallback(SessionCallbackProxy callbackProxy) {
super(MockMediaLibraryService2.this);
+ mCallbackProxy = callbackProxy;
}
@Override
public CommandGroup onConnect(ControllerInfo controller) {
- if (Process.myUid() != controller.getUid()) {
- // It's system app wants to listen changes. Ignore.
- return super.onConnect(controller);
- }
- TestServiceRegistry.getInstance().setServiceInstance(
- MockMediaLibraryService2.this, controller);
- return super.onConnect(controller);
+ return mCallbackProxy.onConnect(controller);
}
@Override
public LibraryRoot onGetRoot(ControllerInfo controller, Bundle rootHints) {
return new LibraryRoot(MockMediaLibraryService2.this, ROOT_ID, EXTRA);
}
+
+ @Override
+ public MediaItem2 onLoadItem(ControllerInfo controller, String mediaId) {
+ if (MEDIA_ID_GET_ITEM.equals(mediaId)) {
+ return createMediaItem(mediaId);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public List<MediaItem2> onLoadChildren(ControllerInfo controller, String parentId, int page,
+ int pageSize, Bundle options) {
+ if (PARENT_ID.equals(parentId)) {
+ return getPaginatedResult(GET_CHILDREN_RESULT, page, pageSize);
+ } else if (PARENT_ID_ERROR.equals(parentId)) {
+ return null;
+ }
+ // Includes the case of PARENT_ID_NO_CHILDREN.
+ return new ArrayList<>();
+ }
+ }
+
+ private List<MediaItem2> getPaginatedResult(List<MediaItem2> items, int page, int pageSize) {
+ if (items == null) {
+ return null;
+ } else if (items.size() == 0) {
+ return new ArrayList<>();
+ }
+
+ final int totalItemCount = items.size();
+ int fromIndex = (page - 1) * pageSize;
+ int toIndex = Math.min(page * pageSize, totalItemCount);
+
+ List<MediaItem2> paginatedResult = new ArrayList<>();
+ try {
+ // The case of (fromIndex >= totalItemCount) will throw exception below.
+ paginatedResult = items.subList(fromIndex, toIndex);
+ } catch (IndexOutOfBoundsException | IllegalArgumentException ex) {
+ Log.d(TAG, "Result is empty for given pagination arguments: totalItemCount="
+ + totalItemCount + ", page=" + page + ", pageSize=" + pageSize, ex);
+ }
+ return paginatedResult;
+ }
+
+ private MediaItem2 createMediaItem(String mediaId) {
+ Context context = MockMediaLibraryService2.this;
+ return new MediaItem2(
+ context,
+ mediaId,
+ DATA_SOURCE_DESC,
+ new MediaMetadata2.Builder(context)
+ .putString(MediaMetadata2.METADATA_KEY_MEDIA_ID, mediaId)
+ .build(),
+ 0 /* Flags */);
}
}
\ 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 c8ed184..12c2c9f 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
@@ -22,11 +22,11 @@
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
+import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.SessionCallback;
+import android.media.TestServiceRegistry.SessionCallbackProxy;
import android.media.TestUtils.SyncHandler;
-import android.media.session.PlaybackState;
-import android.os.Process;
import java.util.concurrent.Executor;
@@ -45,29 +45,32 @@
private NotificationManager mNotificationManager;
@Override
+ public void onCreate() {
+ super.onCreate();
+ TestServiceRegistry.getInstance().setServiceInstance(this);
+ mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ @Override
public MediaSession2 onCreateSession(String sessionId) {
final MockPlayer player = new MockPlayer(1);
final SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
final Executor executor = (runnable) -> handler.post(runnable);
- try {
- handler.postAndSync(() -> {
- mSession = new MediaSession2.Builder(MockMediaSessionService2.this, player)
- .setSessionCallback(executor, new MySessionCallback())
- .setId(sessionId).build();
- });
- } catch (InterruptedException e) {
- fail(e.toString());
+ SessionCallbackProxy sessionCallbackProxy = TestServiceRegistry.getInstance()
+ .getSessionCallbackProxy();
+ if (sessionCallbackProxy == null) {
+ // Ensures non-null
+ sessionCallbackProxy = new SessionCallbackProxy(this) {};
}
+ TestSessionServiceCallback callback =
+ new TestSessionServiceCallback(sessionCallbackProxy);
+ mSession = new MediaSession2.Builder(this, player)
+ .setSessionCallback(executor, callback)
+ .setId(sessionId).build();
return mSession;
}
@Override
- public void onCreate() {
- super.onCreate();
- mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- }
-
- @Override
public void onDestroy() {
TestServiceRegistry.getInstance().cleanUp();
super.onDestroy();
@@ -90,20 +93,17 @@
return new MediaNotification(this, DEFAULT_MEDIA_NOTIFICATION_ID, notification);
}
- private class MySessionCallback extends SessionCallback {
- public MySessionCallback() {
+ private class TestSessionServiceCallback extends SessionCallback {
+ private final SessionCallbackProxy mCallbackProxy;
+
+ public TestSessionServiceCallback(SessionCallbackProxy callbackProxy) {
super(MockMediaSessionService2.this);
+ mCallbackProxy = callbackProxy;
}
@Override
- public MediaSession2.CommandGroup onConnect(ControllerInfo controller) {
- if (Process.myUid() != controller.getUid()) {
- // It's system app wants to listen changes. Ignore.
- return super.onConnect(controller);
- }
- TestServiceRegistry.getInstance().setServiceInstance(
- MockMediaSessionService2.this, controller);
- return super.onConnect(controller);
+ public CommandGroup onConnect(ControllerInfo controller) {
+ return mCallbackProxy.onConnect(controller);
}
}
}
diff --git a/packages/MediaComponents/test/src/android/media/SessionToken2Test.java b/packages/MediaComponents/test/src/android/media/SessionToken2Test.java
new file mode 100644
index 0000000..efde78a
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/SessionToken2Test.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 android.content.Context;
+import android.os.Process;
+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;
+
+/**
+ * Tests {@link SessionToken2}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SessionToken2Test {
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ @Test
+ public void testConstructor_sessionService() {
+ SessionToken2 token = new SessionToken2(mContext, mContext.getPackageName(),
+ MockMediaSessionService2.class.getCanonicalName());
+ assertEquals(MockMediaSessionService2.ID, token.getId());
+ assertEquals(mContext.getPackageName(), token.getPackageName());
+ assertEquals(Process.myUid(), token.getUid());
+ assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
+ }
+
+ @Test
+ public void testConstructor_libraryService() {
+ SessionToken2 token = new SessionToken2(mContext, mContext.getPackageName(),
+ MockMediaLibraryService2.class.getCanonicalName());
+ assertEquals(MockMediaLibraryService2.ID, token.getId());
+ assertEquals(mContext.getPackageName(), token.getPackageName());
+ assertEquals(Process.myUid(), token.getUid());
+ assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, token.getType());
+ }
+}
\ No newline at end of file
diff --git a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
index 6f5512e..3800c28 100644
--- a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
+++ b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
@@ -18,10 +18,12 @@
import static org.junit.Assert.fail;
+import android.content.Context;
+import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
import android.media.TestUtils.SyncHandler;
import android.os.Handler;
-import android.os.Looper;
+import android.os.Process;
import android.support.annotation.GuardedBy;
/**
@@ -31,8 +33,46 @@
* It only support only one service at a time.
*/
public class TestServiceRegistry {
- public interface ServiceInstanceChangedCallback {
- void OnServiceInstanceChanged(MediaSessionService2 service);
+ /**
+ * Proxy for both {@link MediaSession2.SessionCallback} and
+ * {@link MediaLibraryService2.MediaLibrarySessionCallback}.
+ */
+ public static abstract class SessionCallbackProxy {
+ private final Context mContext;
+
+ /**
+ * Constructor
+ */
+ public SessionCallbackProxy(Context context) {
+ mContext = context;
+ }
+
+ public final Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * @param controller
+ * @return
+ */
+ public CommandGroup onConnect(ControllerInfo controller) {
+ if (Process.myUid() == controller.getUid()) {
+ CommandGroup commands = new CommandGroup(mContext);
+ commands.addAllPredefinedCommands();
+ return commands;
+ }
+ return null;
+ }
+
+ /**
+ * Called when enclosing service is created.
+ */
+ public void onServiceCreated(MediaSessionService2 service) { }
+
+ /**
+ * Called when enclosing service is destroyed.
+ */
+ public void onServiceDestroyed() { }
}
@GuardedBy("TestServiceRegistry.class")
@@ -42,9 +82,7 @@
@GuardedBy("TestServiceRegistry.class")
private SyncHandler mHandler;
@GuardedBy("TestServiceRegistry.class")
- private ControllerInfo mOnConnectControllerInfo;
- @GuardedBy("TestServiceRegistry.class")
- private ServiceInstanceChangedCallback mCallback;
+ private SessionCallbackProxy mCallbackProxy;
public static TestServiceRegistry getInstance() {
synchronized (TestServiceRegistry.class) {
@@ -61,28 +99,33 @@
}
}
- public void setServiceInstanceChangedCallback(ServiceInstanceChangedCallback callback) {
- synchronized (TestServiceRegistry.class) {
- mCallback = callback;
- }
- }
-
public Handler getHandler() {
synchronized (TestServiceRegistry.class) {
return mHandler;
}
}
- public void setServiceInstance(MediaSessionService2 service, ControllerInfo controller) {
+ public void setSessionCallbackProxy(SessionCallbackProxy callbackProxy) {
+ synchronized (TestServiceRegistry.class) {
+ mCallbackProxy = callbackProxy;
+ }
+ }
+
+ public SessionCallbackProxy getSessionCallbackProxy() {
+ synchronized (TestServiceRegistry.class) {
+ return mCallbackProxy;
+ }
+ }
+
+ public void setServiceInstance(MediaSessionService2 service) {
synchronized (TestServiceRegistry.class) {
if (mService != null) {
fail("Previous service instance is still running. Clean up manually to ensure"
+ " previoulsy running service doesn't break current test");
}
mService = service;
- mOnConnectControllerInfo = controller;
- if (mCallback != null) {
- mCallback.OnServiceInstanceChanged(service);
+ if (mCallbackProxy != null) {
+ mCallbackProxy.onServiceCreated(service);
}
}
}
@@ -93,28 +136,11 @@
}
}
- public ControllerInfo getOnConnectControllerInfo() {
- synchronized (TestServiceRegistry.class) {
- return mOnConnectControllerInfo;
- }
- }
-
-
public void cleanUp() {
synchronized (TestServiceRegistry.class) {
- final ServiceInstanceChangedCallback callback = mCallback;
+ final SessionCallbackProxy callbackProxy = mCallbackProxy;
if (mService != null) {
- try {
- if (mHandler.getLooper() == Looper.myLooper()) {
- mService.getSession().close();
- } else {
- mHandler.postAndSync(() -> {
- mService.getSession().close();
- });
- }
- } catch (InterruptedException e) {
- // No-op. Service containing session will die, but shouldn't be a huge issue.
- }
+ mService.getSession().close();
// stopSelf() would not kill service while the binder connection established by
// bindService() exists, and close() above will do the job instead.
// So stopSelf() isn't really needed, but just for sure.
@@ -124,11 +150,10 @@
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
}
- mCallback = null;
- mOnConnectControllerInfo = null;
+ mCallbackProxy = null;
- if (callback != null) {
- callback.OnServiceInstanceChanged(null);
+ if (callbackProxy != null) {
+ callbackProxy.onServiceDestroyed();
}
}
}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index e7140c2..a3ce1f6 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -321,6 +321,7 @@
actualSessionId,
client.clientPid,
client.clientUid,
+ client.packageName,
config,
AUDIO_INPUT_FLAG_MMAP_NOIRQ, deviceId, &portId);
}
@@ -340,7 +341,7 @@
if (direction == MmapStreamInterface::DIRECTION_OUTPUT) {
AudioSystem::releaseOutput(io, streamType, actualSessionId);
} else {
- AudioSystem::releaseInput(io, actualSessionId);
+ AudioSystem::releaseInput(portId);
}
ret = NO_INIT;
}
@@ -658,7 +659,7 @@
sp<Client> client;
status_t lStatus;
audio_stream_type_t streamType;
- audio_port_handle_t portId;
+ audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
bool updatePid = (input.clientInfo.clientPid == -1);
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
@@ -1596,7 +1597,7 @@
sp<Client> client;
status_t lStatus;
audio_session_t sessionId = input.sessionId;
- audio_port_handle_t portId;
+ audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
output.cblk.clear();
output.buffers.clear();
@@ -1621,12 +1622,6 @@
clientPid = callingPid;
}
- // check calling permissions
- if (!recordingAllowed(input.opPackageName, input.clientInfo.clientTid, clientUid)) {
- ALOGE("createRecord() permission denied: recording not allowed");
- lStatus = PERMISSION_DENIED;
- goto Exit;
- }
// we don't yet support anything other than linear PCM
if (!audio_is_valid_format(input.config.format) || !audio_is_linear_pcm(input.config.format)) {
ALOGE("createRecord() invalid format %#x", input.config.format);
@@ -1663,14 +1658,16 @@
// release previously opened input if retrying.
if (output.inputId != AUDIO_IO_HANDLE_NONE) {
recordTrack.clear();
- AudioSystem::releaseInput(output.inputId, sessionId);
+ AudioSystem::releaseInput(portId);
output.inputId = AUDIO_IO_HANDLE_NONE;
+ portId = AUDIO_PORT_HANDLE_NONE;
}
lStatus = AudioSystem::getInputForAttr(&input.attr, &output.inputId,
sessionId,
// FIXME compare to AudioTrack
clientPid,
clientUid,
+ input.opPackageName,
&input.config,
output.flags, &output.selectedDeviceId, &portId);
@@ -1739,7 +1736,7 @@
}
recordTrack.clear();
if (output.inputId != AUDIO_IO_HANDLE_NONE) {
- AudioSystem::releaseInput(output.inputId, sessionId);
+ AudioSystem::releaseInput(portId);
}
}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 24d862f..d6021b3 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -6936,8 +6936,7 @@
if (recordTrack->isExternalTrack()) {
mLock.unlock();
bool silenced;
- status = AudioSystem::startInput(mId, recordTrack->sessionId(),
- mInDevice, recordTrack->uid(), &silenced);
+ status = AudioSystem::startInput(recordTrack->portId(), &silenced);
mLock.lock();
// FIXME should verify that recordTrack is still in mActiveTracks
if (status != NO_ERROR) {
@@ -6969,7 +6968,7 @@
startError:
if (recordTrack->isExternalTrack()) {
- AudioSystem::stopInput(mId, recordTrack->sessionId());
+ AudioSystem::stopInput(recordTrack->portId());
}
recordTrack->clearSyncStartEvent();
// FIXME I wonder why we do not reset the state here?
@@ -7785,7 +7784,7 @@
if (isOutput()) {
AudioSystem::releaseOutput(mId, streamType(), mSessionId);
} else {
- AudioSystem::releaseInput(mId, mSessionId);
+ AudioSystem::releaseInput(mPortId);
}
}
@@ -7845,10 +7844,6 @@
return NO_ERROR;
}
- if (!isOutput() && !recordingAllowed(client.packageName, client.clientPid, client.clientUid)) {
- return PERMISSION_DENIED;
- }
-
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
audio_io_handle_t io = mId;
@@ -7880,6 +7875,7 @@
mSessionId,
client.clientPid,
client.clientUid,
+ client.packageName,
&config,
AUDIO_INPUT_FLAG_MMAP_NOIRQ,
&deviceId,
@@ -7898,7 +7894,7 @@
} else {
// TODO: Block recording for idle UIDs (b/72134552)
bool silenced;
- ret = AudioSystem::startInput(mId, mSessionId, mInDevice, client.clientUid, &silenced);
+ ret = AudioSystem::startInput(portId, &silenced);
}
// abort if start is rejected by audio policy manager
@@ -7908,7 +7904,7 @@
if (isOutput()) {
AudioSystem::releaseOutput(mId, streamType(), mSessionId);
} else {
- AudioSystem::releaseInput(mId, mSessionId);
+ AudioSystem::releaseInput(portId);
}
} else {
mHalStream->stop();
@@ -7965,8 +7961,8 @@
AudioSystem::stopOutput(mId, streamType(), track->sessionId());
AudioSystem::releaseOutput(mId, streamType(), track->sessionId());
} else {
- AudioSystem::stopInput(mId, track->sessionId());
- AudioSystem::releaseInput(mId, track->sessionId());
+ AudioSystem::stopInput(track->portId());
+ AudioSystem::releaseInput(track->portId());
}
sp<EffectChain> chain = getEffectChain_l(track->sessionId());
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index ce5c53b..67f27d0 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1694,7 +1694,7 @@
if (thread != 0) {
RecordThread *recordThread = (RecordThread *)thread.get();
if (recordThread->stop(this) && isExternalTrack()) {
- AudioSystem::stopInput(mThreadIoHandle, mSessionId);
+ AudioSystem::stopInput(mPortId);
}
}
}
@@ -1706,9 +1706,9 @@
{
if (isExternalTrack()) {
if (mState == ACTIVE || mState == RESUMING) {
- AudioSystem::stopInput(mThreadIoHandle, mSessionId);
+ AudioSystem::stopInput(mPortId);
}
- AudioSystem::releaseInput(mThreadIoHandle, mSessionId);
+ AudioSystem::releaseInput(mPortId);
}
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index f1d7d86..306de3f 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -287,6 +287,7 @@
audio_session_t session,
pid_t pid,
uid_t uid,
+ const String16& opPackageName,
const audio_config_base_t *config,
audio_input_flags_t flags,
audio_port_handle_t *selectedDeviceId,
@@ -295,6 +296,7 @@
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
+
// already checked by client, but double-check in case the client wrapper is bypassed
if (attr->source < AUDIO_SOURCE_DEFAULT && attr->source >= AUDIO_SOURCE_CNT &&
attr->source != AUDIO_SOURCE_HOTWORD && attr->source != AUDIO_SOURCE_FM_TUNER) {
@@ -318,6 +320,13 @@
pid = callingPid;
}
+ // check calling permissions
+ if (!recordingAllowed(opPackageName, pid, uid)) {
+ ALOGE("%s permission denied: recording not allowed for uid %d pid %d",
+ __func__, uid, pid);
+ return PERMISSION_DENIED;
+ }
+
if ((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed(pid, uid)) {
return BAD_VALUE;
}
@@ -367,6 +376,13 @@
}
return status;
}
+
+ sp<AudioRecordClient> client =
+ new AudioRecordClient(*attr, *input, uid, pid, opPackageName, session);
+ client->active = false;
+ client->isConcurrent = false;
+ client->isVirtualDevice = false; //TODO : update from APM->getInputForAttr()
+ mAudioRecordClients.add(*portId, client);
}
if (audioPolicyEffects != 0) {
@@ -379,23 +395,38 @@
return NO_ERROR;
}
-status_t AudioPolicyService::startInput(audio_io_handle_t input,
- audio_session_t session,
- audio_devices_t device,
- uid_t uid,
- bool *silenced)
+status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenced)
{
- // If UID inactive it records silence until becoming active
- *silenced = !mUidPolicy->isUidActive(uid) && !is_virtual_input_device(device);
-
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
+ sp<AudioRecordClient> client;
+ {
+ Mutex::Autolock _l(mLock);
+
+ ssize_t index = mAudioRecordClients.indexOfKey(portId);
+ if (index < 0) {
+ return INVALID_OPERATION;
+ }
+ client = mAudioRecordClients.valueAt(index);
+ }
+
+ // check calling permissions
+ if (!recordingAllowed(client->opPackageName, client->pid, client->uid)) {
+ ALOGE("%s permission denied: recording not allowed for uid %d pid %d",
+ __func__, client->uid, client->pid);
+ return PERMISSION_DENIED;
+ }
+
+ // If UID inactive it records silence until becoming active
+ *silenced = !mUidPolicy->isUidActive(client->uid) && !client->isVirtualDevice;
Mutex::Autolock _l(mLock);
AudioPolicyInterface::concurrency_type__mask_t concurrency =
AudioPolicyInterface::API_INPUT_CONCURRENCY_NONE;
- status_t status = mAudioPolicyManager->startInput(input, session, *silenced, &concurrency);
+
+ status_t status = mAudioPolicyManager->startInput(
+ client->input, client->session, *silenced, &concurrency);
if (status == NO_ERROR) {
LOG_ALWAYS_FATAL_IF(concurrency & ~AudioPolicyInterface::API_INPUT_CONCURRENCY_ALL,
@@ -413,38 +444,52 @@
return status;
}
-status_t AudioPolicyService::stopInput(audio_io_handle_t input,
- audio_session_t session)
+status_t AudioPolicyService::stopInput(audio_port_handle_t portId)
{
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
Mutex::Autolock _l(mLock);
- return mAudioPolicyManager->stopInput(input, session);
+ ssize_t index = mAudioRecordClients.indexOfKey(portId);
+ if (index < 0) {
+ return INVALID_OPERATION;
+ }
+ sp<AudioRecordClient> client = mAudioRecordClients.valueAt(index);
+
+ return mAudioPolicyManager->stopInput(client->input, client->session);
}
-void AudioPolicyService::releaseInput(audio_io_handle_t input,
- audio_session_t session)
+void AudioPolicyService::releaseInput(audio_port_handle_t portId)
{
if (mAudioPolicyManager == NULL) {
return;
}
sp<AudioPolicyEffects>audioPolicyEffects;
+ sp<AudioRecordClient> client;
{
Mutex::Autolock _l(mLock);
audioPolicyEffects = mAudioPolicyEffects;
+ ssize_t index = mAudioRecordClients.indexOfKey(portId);
+ if (index < 0) {
+ return;
+ }
+ client = mAudioRecordClients.valueAt(index);
+ mAudioRecordClients.removeItem(portId);
+ }
+ if (client == 0) {
+ return;
}
if (audioPolicyEffects != 0) {
// release audio processors from the input
- status_t status = audioPolicyEffects->releaseInputEffects(input, session);
+ status_t status = audioPolicyEffects->releaseInputEffects(client->input, client->session);
if(status != NO_ERROR) {
- ALOGW("Failed to release effects on input %d", input);
+ ALOGW("Failed to release effects on input %d", client->input);
}
}
{
Mutex::Autolock _l(mLock);
- mAudioPolicyManager->releaseInput(input, session);
+ mAudioPolicyManager->releaseInput(client->input, client->session);
}
}
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index c21aa58..bfa3ef4 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -98,19 +98,15 @@
audio_session_t session,
pid_t pid,
uid_t uid,
+ const String16& opPackageName,
const audio_config_base_t *config,
audio_input_flags_t flags,
audio_port_handle_t *selectedDeviceId = NULL,
audio_port_handle_t *portId = NULL);
- virtual status_t startInput(audio_io_handle_t input,
- audio_session_t session,
- audio_devices_t device,
- uid_t uid,
+ virtual status_t startInput(audio_port_handle_t portId,
bool *silenced);
- virtual status_t stopInput(audio_io_handle_t input,
- audio_session_t session);
- virtual void releaseInput(audio_io_handle_t input,
- audio_session_t session);
+ virtual status_t stopInput(audio_port_handle_t portId);
+ virtual void releaseInput(audio_port_handle_t portId);
virtual status_t initStreamVolume(audio_stream_type_t stream,
int indexMin,
int indexMax);
@@ -611,6 +607,31 @@
bool mAudioPortCallbacksEnabled;
};
+ // --- AudioRecordClient ---
+ // Information about each registered AudioRecord client
+ // (between calls to getInputForAttr() and releaseInput())
+ class AudioRecordClient : public RefBase {
+ public:
+ AudioRecordClient(const audio_attributes_t attributes,
+ const audio_io_handle_t input, uid_t uid, pid_t pid,
+ const String16& opPackageName, const audio_session_t session) :
+ attributes(attributes),
+ input(input), uid(uid), pid(pid),
+ opPackageName(opPackageName), session(session),
+ active(false), isConcurrent(false), isVirtualDevice(false) {}
+ virtual ~AudioRecordClient() {}
+
+ const audio_attributes_t attributes; // source, flags ...
+ const audio_io_handle_t input; // audio HAL input IO handle
+ const uid_t uid; // client UID
+ const pid_t pid; // client PID
+ const String16 opPackageName; // client package name
+ const audio_session_t session; // audio session ID
+ bool active; // Capture is active or inactive
+ bool isConcurrent; // is allowed to concurrent capture
+ bool isVirtualDevice; // uses vitual device: updated by APM::getInputForAttr()
+ };
+
// Internal dump utilities.
status_t dumpPermissionDenial(int fd);
@@ -636,6 +657,7 @@
audio_mode_t mPhoneState;
sp<UidPolicy> mUidPolicy;
+ DefaultKeyedVector< audio_port_handle_t, sp<AudioRecordClient> > mAudioRecordClients;
};
} // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 324201b..59bb2e2 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -676,8 +676,6 @@
"TEMPLATE_VIDEO_SNAPSHOT",
"TEMPLATE_ZERO_SHUTTER_LAG",
"TEMPLATE_MANUAL",
- "TEMPLATE_MOTION_TRACKING_PREVIEW",
- "TEMPALTE_MOTION_TRACKING_BEST"
};
for (int i = 1; i < CAMERA3_TEMPLATE_COUNT; i++) {
@@ -3435,65 +3433,31 @@
}
};
hardware::Return<void> err;
- if (mHidlSession_3_4 != nullptr) {
- device::V3_4::RequestTemplate id;
- switch (templateId) {
- case CAMERA3_TEMPLATE_PREVIEW:
- id = device::V3_4::RequestTemplate::PREVIEW;
- break;
- case CAMERA3_TEMPLATE_STILL_CAPTURE:
- id = device::V3_4::RequestTemplate::STILL_CAPTURE;
- break;
- case CAMERA3_TEMPLATE_VIDEO_RECORD:
- id = device::V3_4::RequestTemplate::VIDEO_RECORD;
- break;
- case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
- id = device::V3_4::RequestTemplate::VIDEO_SNAPSHOT;
- break;
- case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
- id = device::V3_4::RequestTemplate::ZERO_SHUTTER_LAG;
- break;
- case CAMERA3_TEMPLATE_MANUAL:
- id = device::V3_4::RequestTemplate::MANUAL;
- break;
- case CAMERA3_TEMPLATE_MOTION_TRACKING_PREVIEW:
- id = device::V3_4::RequestTemplate::MOTION_TRACKING_PREVIEW;
- break;
- case CAMERA3_TEMPLATE_MOTION_TRACKING_BEST:
- id = device::V3_4::RequestTemplate::MOTION_TRACKING_BEST;
- break;
- default:
- // Unknown template ID
- return BAD_VALUE;
- }
- err = mHidlSession_3_4->constructDefaultRequestSettings_3_4(id, requestCallback);
- } else {
- RequestTemplate id;
- switch (templateId) {
- case CAMERA3_TEMPLATE_PREVIEW:
- id = RequestTemplate::PREVIEW;
- break;
- case CAMERA3_TEMPLATE_STILL_CAPTURE:
- id = RequestTemplate::STILL_CAPTURE;
- break;
- case CAMERA3_TEMPLATE_VIDEO_RECORD:
- id = RequestTemplate::VIDEO_RECORD;
- break;
- case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
- id = RequestTemplate::VIDEO_SNAPSHOT;
- break;
- case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
- id = RequestTemplate::ZERO_SHUTTER_LAG;
- break;
- case CAMERA3_TEMPLATE_MANUAL:
- id = RequestTemplate::MANUAL;
- break;
- default:
- // Unknown template ID, or this HAL is too old to support it
- return BAD_VALUE;
- }
- err = mHidlSession->constructDefaultRequestSettings(id, requestCallback);
+ RequestTemplate id;
+ switch (templateId) {
+ case CAMERA3_TEMPLATE_PREVIEW:
+ id = RequestTemplate::PREVIEW;
+ break;
+ case CAMERA3_TEMPLATE_STILL_CAPTURE:
+ id = RequestTemplate::STILL_CAPTURE;
+ break;
+ case CAMERA3_TEMPLATE_VIDEO_RECORD:
+ id = RequestTemplate::VIDEO_RECORD;
+ break;
+ case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
+ id = RequestTemplate::VIDEO_SNAPSHOT;
+ break;
+ case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
+ id = RequestTemplate::ZERO_SHUTTER_LAG;
+ break;
+ case CAMERA3_TEMPLATE_MANUAL:
+ id = RequestTemplate::MANUAL;
+ break;
+ default:
+ // Unknown template ID, or this HAL is too old to support it
+ return BAD_VALUE;
}
+ err = mHidlSession->constructDefaultRequestSettings(id, requestCallback);
if (!err.isOk()) {
ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());