Merge "MediaMuxer prefer not to use the MPEG4Writer in real time recording mode." into jb-mr2-dev
diff --git a/camera/Android.mk b/camera/Android.mk
index e33fb50..fa518ff 100644
--- a/camera/Android.mk
+++ b/camera/Android.mk
@@ -22,6 +22,7 @@
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
+ liblog \
libbinder \
libhardware \
libui \
diff --git a/camera/tests/Android.mk b/camera/tests/Android.mk
index e455943..ec13911 100644
--- a/camera/tests/Android.mk
+++ b/camera/tests/Android.mk
@@ -35,4 +35,4 @@
LOCAL_MODULE:= camera_client_test
LOCAL_MODULE_TAGS := tests
-include $(BUILD_EXECUTABLE)
+include $(BUILD_NATIVE_TEST)
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index d583e65..3844487 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -9,7 +9,7 @@
LOCAL_SHARED_LIBRARIES := \
libstagefright libmedia libutils libbinder libstagefright_foundation \
- libjpeg libgui libcutils
+ libjpeg libgui libcutils liblog
LOCAL_C_INCLUDES:= \
frameworks/av/media/libstagefright \
@@ -187,4 +187,3 @@
LOCAL_MODULE:= muxer
include $(BUILD_EXECUTABLE)
-
diff --git a/drm/drmserver/Android.mk b/drm/drmserver/Android.mk
index 96205a1..dc973da 100644
--- a/drm/drmserver/Android.mk
+++ b/drm/drmserver/Android.mk
@@ -24,6 +24,7 @@
LOCAL_SHARED_LIBRARIES := \
libmedia \
libutils \
+ liblog \
libbinder \
libdl
diff --git a/drm/libdrmframework/Android.mk b/drm/libdrmframework/Android.mk
index 9e07fe3..49c4f9b 100644
--- a/drm/libdrmframework/Android.mk
+++ b/drm/libdrmframework/Android.mk
@@ -25,6 +25,7 @@
LOCAL_SHARED_LIBRARIES := \
libutils \
+ liblog \
libbinder \
libdl
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
index 205b9a5..e251f82 100644
--- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
@@ -36,6 +36,7 @@
libicui18n \
libicuuc \
libutils \
+ liblog \
libdl \
libcrypto \
libssl \
diff --git a/drm/libdrmframework/plugins/passthru/Android.mk b/drm/libdrmframework/plugins/passthru/Android.mk
index d170d49..cb3a2e2 100644
--- a/drm/libdrmframework/plugins/passthru/Android.mk
+++ b/drm/libdrmframework/plugins/passthru/Android.mk
@@ -25,6 +25,7 @@
LOCAL_SHARED_LIBRARIES := \
libutils \
+ liblog \
libdl
diff --git a/drm/mediadrm/plugins/mock/Android.mk b/drm/mediadrm/plugins/mock/Android.mk
index a056cd8..ada23a2 100644
--- a/drm/mediadrm/plugins/mock/Android.mk
+++ b/drm/mediadrm/plugins/mock/Android.mk
@@ -24,7 +24,7 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)/mediadrm
LOCAL_SHARED_LIBRARIES := \
- libutils
+ libutils liblog
LOCAL_C_INCLUDES += \
$(TOP)/frameworks/av/include \
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
index 91f5c9c..00f6de3 100644
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
@@ -21,6 +21,7 @@
#include "drm/DrmAPI.h"
#include "MockDrmCryptoPlugin.h"
+#include "media/stagefright/MediaErrors.h"
using namespace android;
@@ -98,17 +99,17 @@
}
- status_t MockDrmPlugin::getLicenseRequest(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &initData,
- String8 const &mimeType, LicenseType licenseType,
- KeyedVector<String8, String8> const &optionalParameters,
- Vector<uint8_t> &request, String8 &defaultUrl)
+ status_t MockDrmPlugin::getKeyRequest(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &initData,
+ String8 const &mimeType, KeyType keyType,
+ KeyedVector<String8, String8> const &optionalParameters,
+ Vector<uint8_t> &request, String8 &defaultUrl)
{
Mutex::Autolock lock(mLock);
- ALOGD("MockDrmPlugin::getLicenseRequest(sessionId=%s, initData=%s, mimeType=%s"
- ", licenseType=%d, optionalParameters=%s))",
+ ALOGD("MockDrmPlugin::getKeyRequest(sessionId=%s, initData=%s, mimeType=%s"
+ ", keyType=%d, optionalParameters=%s))",
vectorToString(sessionId).string(), vectorToString(initData).string(), mimeType.string(),
- licenseType, stringMapToString(optionalParameters).string());
+ keyType, stringMapToString(optionalParameters).string());
ssize_t index = findSession(sessionId);
if (index == kNotFound) {
@@ -119,15 +120,15 @@
// Properties used in mock test, set by mock plugin and verifed cts test app
// byte[] initData -> mock-initdata
// string mimeType -> mock-mimetype
- // string licenseType -> mock-licensetype
+ // string keyType -> mock-keytype
// string optionalParameters -> mock-optparams formatted as {key1,value1},{key2,value2}
mByteArrayProperties.add(String8("mock-initdata"), initData);
mStringProperties.add(String8("mock-mimetype"), mimeType);
- String8 licenseTypeStr;
- licenseTypeStr.appendFormat("%d", (int)licenseType);
- mStringProperties.add(String8("mock-licensetype"), licenseTypeStr);
+ String8 keyTypeStr;
+ keyTypeStr.appendFormat("%d", (int)keyType);
+ mStringProperties.add(String8("mock-keytype"), keyTypeStr);
String8 params;
for (size_t i = 0; i < optionalParameters.size(); i++) {
@@ -159,11 +160,12 @@
return OK;
}
- status_t MockDrmPlugin::provideLicenseResponse(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &response)
+ status_t MockDrmPlugin::provideKeyResponse(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &response,
+ Vector<uint8_t> &keySetId)
{
Mutex::Autolock lock(mLock);
- ALOGD("MockDrmPlugin::provideLicenseResponse(sessionId=%s, response=%s)",
+ ALOGD("MockDrmPlugin::provideKeyResponse(sessionId=%s, response=%s)",
vectorToString(sessionId).string(), vectorToString(response).string());
ssize_t index = findSession(sessionId);
if (index == kNotFound) {
@@ -176,30 +178,61 @@
// Properties used in mock test, set by mock plugin and verifed cts test app
// byte[] response -> mock-response
-
mByteArrayProperties.add(String8("mock-response"), response);
+ const size_t kKeySetIdSize = 8;
+
+ for (size_t i = 0; i < kKeySetIdSize / sizeof(long); i++) {
+ long r = random();
+ keySetId.appendArray((uint8_t *)&r, sizeof(long));
+ }
+ mKeySets.add(keySetId);
+
return OK;
}
- status_t MockDrmPlugin::removeLicense(Vector<uint8_t> const &sessionId)
+ status_t MockDrmPlugin::removeKeys(Vector<uint8_t> const &keySetId)
{
Mutex::Autolock lock(mLock);
- ALOGD("MockDrmPlugin::removeLicense(sessionId=%s)",
- vectorToString(sessionId).string());
+ ALOGD("MockDrmPlugin::removeKeys(keySetId=%s)",
+ vectorToString(keySetId).string());
+
+ ssize_t index = findKeySet(keySetId);
+ if (index == kNotFound) {
+ ALOGD("Invalid keySetId");
+ return BAD_VALUE;
+ }
+ mKeySets.removeAt(index);
+
+ return OK;
+ }
+
+ status_t MockDrmPlugin::restoreKeys(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keySetId)
+ {
+ Mutex::Autolock lock(mLock);
+ ALOGD("MockDrmPlugin::restoreKeys(sessionId=%s, keySetId=%s)",
+ vectorToString(sessionId).string(),
+ vectorToString(keySetId).string());
ssize_t index = findSession(sessionId);
if (index == kNotFound) {
ALOGD("Invalid sessionId");
return BAD_VALUE;
}
+ index = findKeySet(keySetId);
+ if (index == kNotFound) {
+ ALOGD("Invalid keySetId");
+ return BAD_VALUE;
+ }
+
return OK;
}
- status_t MockDrmPlugin::queryLicenseStatus(Vector<uint8_t> const &sessionId,
+ status_t MockDrmPlugin::queryKeyStatus(Vector<uint8_t> const &sessionId,
KeyedVector<String8, String8> &infoMap) const
{
- ALOGD("MockDrmPlugin::queryLicenseStatus(sessionId=%s)",
+ ALOGD("MockDrmPlugin::queryKeyStatus(sessionId=%s)",
vectorToString(sessionId).string());
ssize_t index = findSession(sessionId);
@@ -310,7 +343,31 @@
Mutex::Autolock lock(mLock);
ALOGD("MockDrmPlugin::setPropertyString(name=%s, value=%s)",
name.string(), value.string());
- mStringProperties.add(name, value);
+
+ if (name == "mock-send-event") {
+ unsigned code, extra;
+ sscanf(value.string(), "%d %d", &code, &extra);
+ DrmPlugin::EventType eventType = (DrmPlugin::EventType)code;
+
+ Vector<uint8_t> const *pSessionId = NULL;
+ ssize_t index = mByteArrayProperties.indexOfKey(String8("mock-event-session-id"));
+ if (index >= 0) {
+ pSessionId = &mByteArrayProperties[index];
+ }
+
+ Vector<uint8_t> const *pData = NULL;
+ index = mByteArrayProperties.indexOfKey(String8("mock-event-data"));
+ if (index >= 0) {
+ pData = &mByteArrayProperties[index];
+ }
+ ALOGD("sending event from mock drm plugin: %d %d %s %s",
+ (int)code, extra, pSessionId ? vectorToString(*pSessionId) : "{}",
+ pData ? vectorToString(*pData) : "{}");
+
+ sendEvent(eventType, extra, pSessionId, pData);
+ } else {
+ mStringProperties.add(name, value);
+ }
return OK;
}
@@ -324,6 +381,198 @@
return OK;
}
+ status_t MockDrmPlugin::setCipherAlgorithm(Vector<uint8_t> const &sessionId,
+ String8 const &algorithm)
+ {
+ Mutex::Autolock lock(mLock);
+
+ ALOGD("MockDrmPlugin::setCipherAlgorithm(sessionId=%s, algorithm=%s)",
+ vectorToString(sessionId).string(), algorithm.string());
+
+ ssize_t index = findSession(sessionId);
+ if (index == kNotFound) {
+ ALOGD("Invalid sessionId");
+ return BAD_VALUE;
+ }
+
+ if (algorithm == "AES/CBC/NoPadding") {
+ return OK;
+ }
+ return BAD_VALUE;
+ }
+
+ status_t MockDrmPlugin::setMacAlgorithm(Vector<uint8_t> const &sessionId,
+ String8 const &algorithm)
+ {
+ Mutex::Autolock lock(mLock);
+
+ ALOGD("MockDrmPlugin::setMacAlgorithm(sessionId=%s, algorithm=%s)",
+ vectorToString(sessionId).string(), algorithm.string());
+
+ ssize_t index = findSession(sessionId);
+ if (index == kNotFound) {
+ ALOGD("Invalid sessionId");
+ return BAD_VALUE;
+ }
+
+ if (algorithm == "HmacSHA256") {
+ return OK;
+ }
+ return BAD_VALUE;
+ }
+
+ status_t MockDrmPlugin::encrypt(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &input,
+ Vector<uint8_t> const &iv,
+ Vector<uint8_t> &output)
+ {
+ Mutex::Autolock lock(mLock);
+ ALOGD("MockDrmPlugin::encrypt(sessionId=%s, keyId=%s, input=%s, iv=%s)",
+ vectorToString(sessionId).string(),
+ vectorToString(keyId).string(),
+ vectorToString(input).string(),
+ vectorToString(iv).string());
+
+ ssize_t index = findSession(sessionId);
+ if (index == kNotFound) {
+ ALOGD("Invalid sessionId");
+ return BAD_VALUE;
+ }
+
+ // Properties used in mock test, set by mock plugin and verifed cts test app
+ // byte[] keyId -> mock-keyid
+ // byte[] input -> mock-input
+ // byte[] iv -> mock-iv
+ mByteArrayProperties.add(String8("mock-keyid"), keyId);
+ mByteArrayProperties.add(String8("mock-input"), input);
+ mByteArrayProperties.add(String8("mock-iv"), iv);
+
+ // Properties used in mock test, set by cts test app returned from mock plugin
+ // byte[] mock-output -> output
+ index = mByteArrayProperties.indexOfKey(String8("mock-output"));
+ if (index < 0) {
+ ALOGD("Missing 'mock-request' parameter for mock");
+ return BAD_VALUE;
+ } else {
+ output = mByteArrayProperties.valueAt(index);
+ }
+ return OK;
+ }
+
+ status_t MockDrmPlugin::decrypt(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &input,
+ Vector<uint8_t> const &iv,
+ Vector<uint8_t> &output)
+ {
+ Mutex::Autolock lock(mLock);
+ ALOGD("MockDrmPlugin::decrypt(sessionId=%s, keyId=%s, input=%s, iv=%s)",
+ vectorToString(sessionId).string(),
+ vectorToString(keyId).string(),
+ vectorToString(input).string(),
+ vectorToString(iv).string());
+
+ ssize_t index = findSession(sessionId);
+ if (index == kNotFound) {
+ ALOGD("Invalid sessionId");
+ return BAD_VALUE;
+ }
+
+ // Properties used in mock test, set by mock plugin and verifed cts test app
+ // byte[] keyId -> mock-keyid
+ // byte[] input -> mock-input
+ // byte[] iv -> mock-iv
+ mByteArrayProperties.add(String8("mock-keyid"), keyId);
+ mByteArrayProperties.add(String8("mock-input"), input);
+ mByteArrayProperties.add(String8("mock-iv"), iv);
+
+ // Properties used in mock test, set by cts test app returned from mock plugin
+ // byte[] mock-output -> output
+ index = mByteArrayProperties.indexOfKey(String8("mock-output"));
+ if (index < 0) {
+ ALOGD("Missing 'mock-request' parameter for mock");
+ return BAD_VALUE;
+ } else {
+ output = mByteArrayProperties.valueAt(index);
+ }
+ return OK;
+ }
+
+ status_t MockDrmPlugin::sign(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &message,
+ Vector<uint8_t> &signature)
+ {
+ Mutex::Autolock lock(mLock);
+ ALOGD("MockDrmPlugin::sign(sessionId=%s, keyId=%s, message=%s)",
+ vectorToString(sessionId).string(),
+ vectorToString(keyId).string(),
+ vectorToString(message).string());
+
+ ssize_t index = findSession(sessionId);
+ if (index == kNotFound) {
+ ALOGD("Invalid sessionId");
+ return BAD_VALUE;
+ }
+
+ // Properties used in mock test, set by mock plugin and verifed cts test app
+ // byte[] keyId -> mock-keyid
+ // byte[] message -> mock-message
+ mByteArrayProperties.add(String8("mock-keyid"), keyId);
+ mByteArrayProperties.add(String8("mock-message"), message);
+
+ // Properties used in mock test, set by cts test app returned from mock plugin
+ // byte[] mock-signature -> signature
+ index = mByteArrayProperties.indexOfKey(String8("mock-signature"));
+ if (index < 0) {
+ ALOGD("Missing 'mock-request' parameter for mock");
+ return BAD_VALUE;
+ } else {
+ signature = mByteArrayProperties.valueAt(index);
+ }
+ return OK;
+ }
+
+ status_t MockDrmPlugin::verify(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &message,
+ Vector<uint8_t> const &signature,
+ bool &match)
+ {
+ Mutex::Autolock lock(mLock);
+ ALOGD("MockDrmPlugin::verify(sessionId=%s, keyId=%s, message=%s, signature=%s)",
+ vectorToString(sessionId).string(),
+ vectorToString(keyId).string(),
+ vectorToString(message).string(),
+ vectorToString(signature).string());
+
+ ssize_t index = findSession(sessionId);
+ if (index == kNotFound) {
+ ALOGD("Invalid sessionId");
+ return BAD_VALUE;
+ }
+
+ // Properties used in mock test, set by mock plugin and verifed cts test app
+ // byte[] keyId -> mock-keyid
+ // byte[] message -> mock-message
+ // byte[] signature -> mock-signature
+ mByteArrayProperties.add(String8("mock-keyid"), keyId);
+ mByteArrayProperties.add(String8("mock-message"), message);
+ mByteArrayProperties.add(String8("mock-signature"), signature);
+
+ // Properties used in mock test, set by cts test app returned from mock plugin
+ // String mock-match "1" or "0" -> match
+ index = mStringProperties.indexOfKey(String8("mock-match"));
+ if (index < 0) {
+ ALOGD("Missing 'mock-request' parameter for mock");
+ return BAD_VALUE;
+ } else {
+ match = atol(mStringProperties.valueAt(index).string());
+ }
+ return OK;
+ }
+
ssize_t MockDrmPlugin::findSession(Vector<uint8_t> const &sessionId) const
{
ALOGD("findSession: nsessions=%d, size=%d", mSessions.size(), sessionId.size());
@@ -335,6 +584,18 @@
return kNotFound;
}
+ ssize_t MockDrmPlugin::findKeySet(Vector<uint8_t> const &keySetId) const
+ {
+ ALOGD("findKeySet: nkeySets=%d, size=%d", mKeySets.size(), keySetId.size());
+ for (size_t i = 0; i < mKeySets.size(); ++i) {
+ if (memcmp(mKeySets[i].array(), keySetId.array(), keySetId.size()) == 0) {
+ return i;
+ }
+ }
+ return kNotFound;
+ }
+
+
// Conversion utilities
String8 MockDrmPlugin::vectorToString(Vector<uint8_t> const &vector) const
{
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
index d46a127..ca9eac7 100644
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
@@ -57,21 +57,23 @@
status_t openSession(Vector<uint8_t> &sessionId);
status_t closeSession(Vector<uint8_t> const &sessionId);
- status_t
- getLicenseRequest(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &initData,
- String8 const &mimeType, LicenseType licenseType,
- KeyedVector<String8, String8> const &optionalParameters,
- Vector<uint8_t> &request, String8 &defaultUrl);
+ status_t getKeyRequest(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &initData,
+ String8 const &mimeType, KeyType keyType,
+ KeyedVector<String8, String8> const &optionalParameters,
+ Vector<uint8_t> &request, String8 &defaultUrl);
- status_t provideLicenseResponse(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &response);
+ status_t provideKeyResponse(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &response,
+ Vector<uint8_t> &keySetId);
- status_t removeLicense(Vector<uint8_t> const &sessionId);
+ status_t removeKeys(Vector<uint8_t> const &keySetId);
- status_t
- queryLicenseStatus(Vector<uint8_t> const &sessionId,
- KeyedVector<String8, String8> &infoMap) const;
+ status_t restoreKeys(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keySetId);
+
+ status_t queryKeyStatus(Vector<uint8_t> const &sessionId,
+ KeyedVector<String8, String8> &infoMap) const;
status_t getProvisionRequest(Vector<uint8_t> &request,
String8 &defaultUrl);
@@ -90,15 +92,46 @@
status_t setPropertyByteArray(String8 const &name,
Vector<uint8_t> const &value );
+ status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
+ String8 const &algorithm);
+
+ status_t setMacAlgorithm(Vector<uint8_t> const &sessionId,
+ String8 const &algorithm);
+
+ status_t encrypt(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &input,
+ Vector<uint8_t> const &iv,
+ Vector<uint8_t> &output);
+
+ status_t decrypt(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &input,
+ Vector<uint8_t> const &iv,
+ Vector<uint8_t> &output);
+
+ status_t sign(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &message,
+ Vector<uint8_t> &signature);
+
+ status_t verify(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &message,
+ Vector<uint8_t> const &signature,
+ bool &match);
+
private:
String8 vectorToString(Vector<uint8_t> const &vector) const;
String8 arrayToString(uint8_t const *array, size_t len) const;
String8 stringMapToString(KeyedVector<String8, String8> map) const;
SortedVector<Vector<uint8_t> > mSessions;
+ SortedVector<Vector<uint8_t> > mKeySets;
static const ssize_t kNotFound = -1;
ssize_t findSession(Vector<uint8_t> const &sessionId) const;
+ ssize_t findKeySet(Vector<uint8_t> const &keySetId) const;
Mutex mLock;
KeyedVector<String8, String8> mStringProperties;
diff --git a/include/media/IDrm.h b/include/media/IDrm.h
index 38e2378..d630c40 100644
--- a/include/media/IDrm.h
+++ b/include/media/IDrm.h
@@ -17,6 +17,7 @@
#include <binder/IInterface.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/drm/DrmAPI.h>
+#include <media/IDrmClient.h>
#ifndef ANDROID_IDRM_H_
@@ -42,19 +43,23 @@
virtual status_t closeSession(Vector<uint8_t> const &sessionId) = 0;
virtual status_t
- getLicenseRequest(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &initData,
- String8 const &mimeType, DrmPlugin::LicenseType licenseType,
- KeyedVector<String8, String8> const &optionalParameters,
- Vector<uint8_t> &request, String8 &defaultUrl) = 0;
+ getKeyRequest(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &initData,
+ String8 const &mimeType, DrmPlugin::KeyType keyType,
+ KeyedVector<String8, String8> const &optionalParameters,
+ Vector<uint8_t> &request, String8 &defaultUrl) = 0;
- virtual status_t provideLicenseResponse(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &response) = 0;
+ virtual status_t provideKeyResponse(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &response,
+ Vector<uint8_t> &keySetId) = 0;
- virtual status_t removeLicense(Vector<uint8_t> const &sessionId) = 0;
+ virtual status_t removeKeys(Vector<uint8_t> const &keySetId) = 0;
- virtual status_t queryLicenseStatus(Vector<uint8_t> const &sessionId,
- KeyedVector<String8, String8> &infoMap) const = 0;
+ virtual status_t restoreKeys(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keySetId) = 0;
+
+ virtual status_t queryKeyStatus(Vector<uint8_t> const &sessionId,
+ KeyedVector<String8, String8> &infoMap) const = 0;
virtual status_t getProvisionRequest(Vector<uint8_t> &request,
String8 &defaulUrl) = 0;
@@ -65,13 +70,44 @@
virtual status_t releaseSecureStops(Vector<uint8_t> const &ssRelease) = 0;
- virtual status_t getPropertyString(String8 const &name, String8 &value ) const = 0;
+ virtual status_t getPropertyString(String8 const &name, String8 &value) const = 0;
virtual status_t getPropertyByteArray(String8 const &name,
- Vector<uint8_t> &value ) const = 0;
+ Vector<uint8_t> &value) const = 0;
virtual status_t setPropertyString(String8 const &name,
String8 const &value ) const = 0;
virtual status_t setPropertyByteArray(String8 const &name,
- Vector<uint8_t> const &value ) const = 0;
+ Vector<uint8_t> const &value) const = 0;
+
+ virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
+ String8 const &algorithm) = 0;
+
+ virtual status_t setMacAlgorithm(Vector<uint8_t> const &sessionId,
+ String8 const &algorithm) = 0;
+
+ virtual status_t encrypt(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &input,
+ Vector<uint8_t> const &iv,
+ Vector<uint8_t> &output) = 0;
+
+ virtual status_t decrypt(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &input,
+ Vector<uint8_t> const &iv,
+ Vector<uint8_t> &output) = 0;
+
+ virtual status_t sign(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &message,
+ Vector<uint8_t> &signature) = 0;
+
+ virtual status_t verify(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &message,
+ Vector<uint8_t> const &signature,
+ bool &match) = 0;
+
+ virtual status_t setListener(const sp<IDrmClient>& listener) = 0;
private:
DISALLOW_EVIL_CONSTRUCTORS(IDrm);
@@ -81,6 +117,9 @@
virtual status_t onTransact(
uint32_t code, const Parcel &data, Parcel *reply,
uint32_t flags = 0);
+private:
+ void readVector(const Parcel &data, Vector<uint8_t> &vector) const;
+ void writeVector(Parcel *reply, Vector<uint8_t> const &vector) const;
};
} // namespace android
diff --git a/include/media/IDrmClient.h b/include/media/IDrmClient.h
new file mode 100644
index 0000000..3b2fc7c
--- /dev/null
+++ b/include/media/IDrmClient.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2013 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_IDRMCLIENT_H
+#define ANDROID_IDRMCLIENT_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <media/drm/DrmAPI.h>
+
+namespace android {
+
+class IDrmClient: public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(DrmClient);
+
+ virtual void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnDrmClient: public BnInterface<IDrmClient>
+{
+public:
+ virtual status_t onTransact(uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif // ANDROID_IDRMCLIENT_H
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 96baf34..5cfe5bc 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -46,6 +46,7 @@
kWhatInputSurfaceCreated = 'isfc',
kWhatSignaledInputEOS = 'seos',
kWhatBuffersAllocated = 'allc',
+ kWhatOMXDied = 'OMXd',
};
ACodec();
@@ -97,6 +98,7 @@
struct ExecutingToIdleState;
struct IdleToLoadedState;
struct FlushingState;
+ struct DeathNotifier;
enum {
kWhatSetup = 'setu',
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index b0c1b34..742bc0e 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -54,6 +54,7 @@
// Convenience methods:
bool getUInt16(off64_t offset, uint16_t *x);
+ bool getUInt24(off64_t offset, uint32_t *x); // 3 byte int, returned as a 32-bit int
bool getUInt32(off64_t offset, uint32_t *x);
bool getUInt64(off64_t offset, uint64_t *x);
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index df54923..de3fc36 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -157,6 +157,10 @@
kKeyCryptoKey = 'cryK', // uint8_t[16]
kKeyCryptoIV = 'cryI', // uint8_t[16]
kKeyCryptoMode = 'cryM', // int32_t
+
+ kKeyCryptoDefaultIVSize = 'cryS', // int32_t
+
+ kKeyPssh = 'pssh', // raw data
};
enum {
diff --git a/include/media/stagefright/NuMediaExtractor.h b/include/media/stagefright/NuMediaExtractor.h
index 0833110..5ae6f6b 100644
--- a/include/media/stagefright/NuMediaExtractor.h
+++ b/include/media/stagefright/NuMediaExtractor.h
@@ -55,6 +55,8 @@
size_t countTracks() const;
status_t getTrackFormat(size_t index, sp<AMessage> *format) const;
+ status_t getFileFormat(sp<AMessage> *format) const;
+
status_t selectTrack(size_t index);
status_t unselectTrack(size_t index);
diff --git a/libvideoeditor/lvpp/Android.mk b/libvideoeditor/lvpp/Android.mk
index 778c5ac..2286827 100755
--- a/libvideoeditor/lvpp/Android.mk
+++ b/libvideoeditor/lvpp/Android.mk
@@ -50,6 +50,7 @@
libaudioutils \
libbinder \
libcutils \
+ liblog \
libEGL \
libGLESv2 \
libgui \
diff --git a/libvideoeditor/osal/src/Android.mk b/libvideoeditor/osal/src/Android.mk
index b73b9ae..4f38b0c 100755
--- a/libvideoeditor/osal/src/Android.mk
+++ b/libvideoeditor/osal/src/Android.mk
@@ -41,7 +41,7 @@
LOCAL_MODULE_TAGS := optional
-LOCAL_SHARED_LIBRARIES := libcutils libutils
+LOCAL_SHARED_LIBRARIES := libcutils libutils liblog
LOCAL_C_INCLUDES += \
$(TOP)/frameworks/av/libvideoeditor/osal/inc \
@@ -64,4 +64,3 @@
-DUSE_STAGEFRIGHT_3GPP_READER
include $(BUILD_SHARED_LIBRARY)
-
diff --git a/libvideoeditor/vss/src/Android.mk b/libvideoeditor/vss/src/Android.mk
index cda7a83..0caa15b 100755
--- a/libvideoeditor/vss/src/Android.mk
+++ b/libvideoeditor/vss/src/Android.mk
@@ -57,6 +57,7 @@
libaudioutils \
libbinder \
libcutils \
+ liblog \
libmedia \
libstagefright \
libstagefright_foundation \
@@ -96,4 +97,3 @@
-DDECODE_GIF_ON_SAVING
include $(BUILD_SHARED_LIBRARY)
-
diff --git a/media/common_time/Android.mk b/media/common_time/Android.mk
index 526f17b..632acbc 100644
--- a/media/common_time/Android.mk
+++ b/media/common_time/Android.mk
@@ -16,6 +16,7 @@
utils.cpp
LOCAL_SHARED_LIBRARIES := libbinder \
libhardware \
- libutils
+ libutils \
+ liblog
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libeffects/downmix/Android.mk b/media/libeffects/downmix/Android.mk
index 3052ad9..5d0a87c 100644
--- a/media/libeffects/downmix/Android.mk
+++ b/media/libeffects/downmix/Android.mk
@@ -7,7 +7,7 @@
EffectDownmix.c
LOCAL_SHARED_LIBRARIES := \
- libcutils
+ libcutils liblog
LOCAL_MODULE:= libdownmix
diff --git a/media/libeffects/factory/Android.mk b/media/libeffects/factory/Android.mk
index 6e69151..60a6ce5 100644
--- a/media/libeffects/factory/Android.mk
+++ b/media/libeffects/factory/Android.mk
@@ -7,7 +7,7 @@
EffectsFactory.c
LOCAL_SHARED_LIBRARIES := \
- libcutils
+ libcutils liblog
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)
LOCAL_MODULE:= libeffects
diff --git a/media/libeffects/preprocessing/Android.mk b/media/libeffects/preprocessing/Android.mk
index dfa1711..c344352 100644
--- a/media/libeffects/preprocessing/Android.mk
+++ b/media/libeffects/preprocessing/Android.mk
@@ -21,7 +21,8 @@
LOCAL_SHARED_LIBRARIES := \
libwebrtc_audio_preprocessing \
libspeexresampler \
- libutils
+ libutils \
+ liblog
ifeq ($(TARGET_SIMULATOR),true)
LOCAL_LDLIBS += -ldl
diff --git a/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk
index 49cf4fa..e196eb2 100644
--- a/media/libeffects/visualizer/Android.mk
+++ b/media/libeffects/visualizer/Android.mk
@@ -10,6 +10,7 @@
LOCAL_SHARED_LIBRARIES := \
libcutils \
+ liblog \
libdl
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 1ada9c3..2c0c3a5 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -20,6 +20,7 @@
IAudioRecord.cpp \
ICrypto.cpp \
IDrm.cpp \
+ IDrmClient.cpp \
IHDCP.cpp \
AudioRecord.cpp \
AudioSystem.cpp \
@@ -62,7 +63,7 @@
LOCAL_CFLAGS += -DSINGLE_STATE_QUEUE_INSTANTIATIONS='"SingleStateQueueInstantiations.cpp"'
LOCAL_SHARED_LIBRARIES := \
- libui libcutils libutils libbinder libsonivox libicuuc libexpat \
+ libui liblog libcutils libutils libbinder libsonivox libicuuc libexpat \
libcamera_client libstagefright_foundation \
libgui libdl libaudioutils
diff --git a/media/libmedia/IDrm.cpp b/media/libmedia/IDrm.cpp
index 3b13ec6..1578846 100644
--- a/media/libmedia/IDrm.cpp
+++ b/media/libmedia/IDrm.cpp
@@ -33,10 +33,11 @@
DESTROY_PLUGIN,
OPEN_SESSION,
CLOSE_SESSION,
- GET_LICENSE_REQUEST,
- PROVIDE_LICENSE_RESPONSE,
- REMOVE_LICENSE,
- QUERY_LICENSE_STATUS,
+ GET_KEY_REQUEST,
+ PROVIDE_KEY_RESPONSE,
+ REMOVE_KEYS,
+ RESTORE_KEYS,
+ QUERY_KEY_STATUS,
GET_PROVISION_REQUEST,
PROVIDE_PROVISION_RESPONSE,
GET_SECURE_STOPS,
@@ -44,7 +45,14 @@
GET_PROPERTY_STRING,
GET_PROPERTY_BYTE_ARRAY,
SET_PROPERTY_STRING,
- SET_PROPERTY_BYTE_ARRAY
+ SET_PROPERTY_BYTE_ARRAY,
+ SET_CIPHER_ALGORITHM,
+ SET_MAC_ALGORITHM,
+ ENCRYPT,
+ DECRYPT,
+ SIGN,
+ VERIFY,
+ SET_LISTENER
};
struct BpDrm : public BpInterface<IDrm> {
@@ -92,9 +100,7 @@
data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
remote()->transact(OPEN_SESSION, data, &reply);
- uint32_t size = reply.readInt32();
- sessionId.insertAt((size_t)0, size);
- reply.read(sessionId.editArray(), size);
+ readVector(reply, sessionId);
return reply.readInt32();
}
@@ -103,80 +109,81 @@
Parcel data, reply;
data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
- data.writeInt32(sessionId.size());
- data.write(sessionId.array(), sessionId.size());
+ writeVector(data, sessionId);
remote()->transact(CLOSE_SESSION, data, &reply);
return reply.readInt32();
}
virtual status_t
- getLicenseRequest(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &initData,
- String8 const &mimeType, DrmPlugin::LicenseType licenseType,
- KeyedVector<String8, String8> const &optionalParameters,
- Vector<uint8_t> &request, String8 &defaultUrl) {
+ getKeyRequest(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &initData,
+ String8 const &mimeType, DrmPlugin::KeyType keyType,
+ KeyedVector<String8, String8> const &optionalParameters,
+ Vector<uint8_t> &request, String8 &defaultUrl) {
Parcel data, reply;
data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
- data.writeInt32(sessionId.size());
- data.write(sessionId.array(), sessionId.size());
-
- data.writeInt32(initData.size());
- data.write(initData.array(), initData.size());
-
+ writeVector(data, sessionId);
+ writeVector(data, initData);
data.writeString8(mimeType);
- data.writeInt32((uint32_t)licenseType);
+ data.writeInt32((uint32_t)keyType);
data.writeInt32(optionalParameters.size());
for (size_t i = 0; i < optionalParameters.size(); ++i) {
data.writeString8(optionalParameters.keyAt(i));
data.writeString8(optionalParameters.valueAt(i));
}
- remote()->transact(GET_LICENSE_REQUEST, data, &reply);
+ remote()->transact(GET_KEY_REQUEST, data, &reply);
- uint32_t len = reply.readInt32();
- request.insertAt((size_t)0, len);
- reply.read(request.editArray(), len);
+ readVector(reply, request);
defaultUrl = reply.readString8();
return reply.readInt32();
}
- virtual status_t provideLicenseResponse(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &response) {
+ virtual status_t provideKeyResponse(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &response,
+ Vector<uint8_t> &keySetId) {
Parcel data, reply;
data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
- data.writeInt32(sessionId.size());
- data.write(sessionId.array(), sessionId.size());
- data.writeInt32(response.size());
- data.write(response.array(), response.size());
- remote()->transact(PROVIDE_LICENSE_RESPONSE, data, &reply);
+ writeVector(data, sessionId);
+ writeVector(data, response);
+ remote()->transact(PROVIDE_KEY_RESPONSE, data, &reply);
+ readVector(reply, keySetId);
return reply.readInt32();
}
- virtual status_t removeLicense(Vector<uint8_t> const &sessionId) {
+ virtual status_t removeKeys(Vector<uint8_t> const &keySetId) {
Parcel data, reply;
data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
- data.writeInt32(sessionId.size());
- data.write(sessionId.array(), sessionId.size());
- remote()->transact(REMOVE_LICENSE, data, &reply);
+ writeVector(data, keySetId);
+ remote()->transact(REMOVE_KEYS, data, &reply);
return reply.readInt32();
}
- virtual status_t queryLicenseStatus(Vector<uint8_t> const &sessionId,
+ virtual status_t restoreKeys(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keySetId) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+ writeVector(data, sessionId);
+ writeVector(data, keySetId);
+ remote()->transact(RESTORE_KEYS, data, &reply);
+
+ return reply.readInt32();
+ }
+
+ virtual status_t queryKeyStatus(Vector<uint8_t> const &sessionId,
KeyedVector<String8, String8> &infoMap) const {
Parcel data, reply;
data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
- data.writeInt32(sessionId.size());
- data.write(sessionId.array(), sessionId.size());
-
- remote()->transact(QUERY_LICENSE_STATUS, data, &reply);
+ writeVector(data, sessionId);
+ remote()->transact(QUERY_KEY_STATUS, data, &reply);
infoMap.clear();
size_t count = reply.readInt32();
@@ -195,9 +202,7 @@
remote()->transact(GET_PROVISION_REQUEST, data, &reply);
- uint32_t len = reply.readInt32();
- request.insertAt((size_t)0, len);
- reply.read(request.editArray(), len);
+ readVector(reply, request);
defaultUrl = reply.readString8();
return reply.readInt32();
@@ -207,8 +212,7 @@
Parcel data, reply;
data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
- data.writeInt32(response.size());
- data.write(response.array(), response.size());
+ writeVector(data, response);
remote()->transact(PROVIDE_PROVISION_RESPONSE, data, &reply);
return reply.readInt32();
@@ -224,9 +228,7 @@
uint32_t count = reply.readInt32();
for (size_t i = 0; i < count; i++) {
Vector<uint8_t> secureStop;
- uint32_t len = reply.readInt32();
- secureStop.insertAt((size_t)0, len);
- reply.read(secureStop.editArray(), len);
+ readVector(reply, secureStop);
secureStops.push_back(secureStop);
}
return reply.readInt32();
@@ -236,8 +238,7 @@
Parcel data, reply;
data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
- data.writeInt32(ssRelease.size());
- data.write(ssRelease.array(), ssRelease.size());
+ writeVector(data, ssRelease);
remote()->transact(RELEASE_SECURE_STOPS, data, &reply);
return reply.readInt32();
@@ -261,10 +262,7 @@
data.writeString8(name);
remote()->transact(GET_PROPERTY_BYTE_ARRAY, data, &reply);
- uint32_t len = reply.readInt32();
- value.insertAt((size_t)0, len);
- reply.read(value.editArray(), len);
-
+ readVector(reply, value);
return reply.readInt32();
}
@@ -285,15 +283,128 @@
data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
data.writeString8(name);
- data.writeInt32(value.size());
- data.write(value.array(), value.size());
+ writeVector(data, value);
remote()->transact(SET_PROPERTY_BYTE_ARRAY, data, &reply);
return reply.readInt32();
}
+ virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
+ String8 const &algorithm) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+ writeVector(data, sessionId);
+ data.writeString8(algorithm);
+ remote()->transact(SET_CIPHER_ALGORITHM, data, &reply);
+ return reply.readInt32();
+ }
+
+ virtual status_t setMacAlgorithm(Vector<uint8_t> const &sessionId,
+ String8 const &algorithm) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+ writeVector(data, sessionId);
+ data.writeString8(algorithm);
+ remote()->transact(SET_MAC_ALGORITHM, data, &reply);
+ return reply.readInt32();
+ }
+
+ virtual status_t encrypt(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &input,
+ Vector<uint8_t> const &iv,
+ Vector<uint8_t> &output) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+ writeVector(data, sessionId);
+ writeVector(data, keyId);
+ writeVector(data, input);
+ writeVector(data, iv);
+
+ remote()->transact(ENCRYPT, data, &reply);
+ readVector(reply, output);
+
+ return reply.readInt32();
+ }
+
+ virtual status_t decrypt(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &input,
+ Vector<uint8_t> const &iv,
+ Vector<uint8_t> &output) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+ writeVector(data, sessionId);
+ writeVector(data, keyId);
+ writeVector(data, input);
+ writeVector(data, iv);
+
+ remote()->transact(DECRYPT, data, &reply);
+ readVector(reply, output);
+
+ return reply.readInt32();
+ }
+
+ virtual status_t sign(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &message,
+ Vector<uint8_t> &signature) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+ writeVector(data, sessionId);
+ writeVector(data, keyId);
+ writeVector(data, message);
+
+ remote()->transact(SIGN, data, &reply);
+ readVector(reply, signature);
+
+ return reply.readInt32();
+ }
+
+ virtual status_t verify(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &message,
+ Vector<uint8_t> const &signature,
+ bool &match) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+ writeVector(data, sessionId);
+ writeVector(data, keyId);
+ writeVector(data, message);
+ writeVector(data, signature);
+
+ remote()->transact(VERIFY, data, &reply);
+ match = (bool)reply.readInt32();
+ return reply.readInt32();
+ }
+
+ virtual status_t setListener(const sp<IDrmClient>& listener) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+ data.writeStrongBinder(listener->asBinder());
+ remote()->transact(SET_LISTENER, data, &reply);
+ return reply.readInt32();
+ }
+
private:
+ void readVector(Parcel &reply, Vector<uint8_t> &vector) const {
+ uint32_t size = reply.readInt32();
+ vector.insertAt((size_t)0, size);
+ reply.read(vector.editArray(), size);
+ }
+
+ void writeVector(Parcel &data, Vector<uint8_t> const &vector) const {
+ data.writeInt32(vector.size());
+ data.write(vector.array(), vector.size());
+ }
+
DISALLOW_EVIL_CONSTRUCTORS(BpDrm);
};
@@ -301,6 +412,17 @@
////////////////////////////////////////////////////////////////////////////////
+void BnDrm::readVector(const Parcel &data, Vector<uint8_t> &vector) const {
+ uint32_t size = data.readInt32();
+ vector.insertAt((size_t)0, size);
+ data.read(vector.editArray(), size);
+}
+
+void BnDrm::writeVector(Parcel *reply, Vector<uint8_t> const &vector) const {
+ reply->writeInt32(vector.size());
+ reply->write(vector.array(), vector.size());
+}
+
status_t BnDrm::onTransact(
uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
switch (code) {
@@ -341,8 +463,7 @@
CHECK_INTERFACE(IDrm, data, reply);
Vector<uint8_t> sessionId;
status_t result = openSession(sessionId);
- reply->writeInt32(sessionId.size());
- reply->write(sessionId.array(), sessionId.size());
+ writeVector(reply, sessionId);
reply->writeInt32(result);
return OK;
}
@@ -351,28 +472,20 @@
{
CHECK_INTERFACE(IDrm, data, reply);
Vector<uint8_t> sessionId;
- uint32_t size = data.readInt32();
- sessionId.insertAt((size_t)0, size);
- data.read(sessionId.editArray(), size);
+ readVector(data, sessionId);
reply->writeInt32(closeSession(sessionId));
return OK;
}
- case GET_LICENSE_REQUEST:
+ case GET_KEY_REQUEST:
{
CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId;
- uint32_t size = data.readInt32();
- sessionId.insertAt((size_t)0, size);
- data.read(sessionId.editArray(), size);
+ Vector<uint8_t> sessionId, initData;
- Vector<uint8_t> initData;
- size = data.readInt32();
- initData.insertAt((size_t)0, size);
- data.read(initData.editArray(), size);
-
+ readVector(data, sessionId);
+ readVector(data, initData);
String8 mimeType = data.readString8();
- DrmPlugin::LicenseType licenseType = (DrmPlugin::LicenseType)data.readInt32();
+ DrmPlugin::KeyType keyType = (DrmPlugin::KeyType)data.readInt32();
KeyedVector<String8, String8> optionalParameters;
uint32_t count = data.readInt32();
@@ -386,55 +499,54 @@
Vector<uint8_t> request;
String8 defaultUrl;
- status_t result = getLicenseRequest(sessionId, initData,
- mimeType, licenseType,
- optionalParameters,
- request, defaultUrl);
- reply->writeInt32(request.size());
- reply->write(request.array(), request.size());
+ status_t result = getKeyRequest(sessionId, initData,
+ mimeType, keyType,
+ optionalParameters,
+ request, defaultUrl);
+ writeVector(reply, request);
reply->writeString8(defaultUrl);
reply->writeInt32(result);
return OK;
}
- case PROVIDE_LICENSE_RESPONSE:
+ case PROVIDE_KEY_RESPONSE:
{
CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId;
- uint32_t size = data.readInt32();
- sessionId.insertAt((size_t)0, size);
- data.read(sessionId.editArray(), size);
- Vector<uint8_t> response;
- size = data.readInt32();
- response.insertAt((size_t)0, size);
- data.read(response.editArray(), size);
-
- reply->writeInt32(provideLicenseResponse(sessionId, response));
+ Vector<uint8_t> sessionId, response, keySetId;
+ readVector(data, sessionId);
+ readVector(data, response);
+ uint32_t result = provideKeyResponse(sessionId, response, keySetId);
+ writeVector(reply, keySetId);
+ reply->writeInt32(result);
return OK;
}
- case REMOVE_LICENSE:
+ case REMOVE_KEYS:
{
CHECK_INTERFACE(IDrm, data, reply);
- Vector<uint8_t> sessionId;
- uint32_t size = data.readInt32();
- sessionId.insertAt((size_t)0, size);
- data.read(sessionId.editArray(), size);
- reply->writeInt32(removeLicense(sessionId));
+ Vector<uint8_t> keySetId;
+ readVector(data, keySetId);
+ reply->writeInt32(removeKeys(keySetId));
return OK;
}
- case QUERY_LICENSE_STATUS:
+ case RESTORE_KEYS:
+ {
+ CHECK_INTERFACE(IDrm, data, reply);
+ Vector<uint8_t> sessionId, keySetId;
+ readVector(data, sessionId);
+ readVector(data, keySetId);
+ reply->writeInt32(restoreKeys(sessionId, keySetId));
+ return OK;
+ }
+
+ case QUERY_KEY_STATUS:
{
CHECK_INTERFACE(IDrm, data, reply);
Vector<uint8_t> sessionId;
- uint32_t size = data.readInt32();
- sessionId.insertAt((size_t)0, size);
- data.read(sessionId.editArray(), size);
+ readVector(data, sessionId);
KeyedVector<String8, String8> infoMap;
-
- status_t result = queryLicenseStatus(sessionId, infoMap);
-
+ status_t result = queryKeyStatus(sessionId, infoMap);
size_t count = infoMap.size();
reply->writeInt32(count);
for (size_t i = 0; i < count; ++i) {
@@ -451,8 +563,7 @@
Vector<uint8_t> request;
String8 defaultUrl;
status_t result = getProvisionRequest(request, defaultUrl);
- reply->writeInt32(request.size());
- reply->write(request.array(), request.size());
+ writeVector(reply, request);
reply->writeString8(defaultUrl);
reply->writeInt32(result);
return OK;
@@ -462,11 +573,8 @@
{
CHECK_INTERFACE(IDrm, data, reply);
Vector<uint8_t> response;
- uint32_t size = data.readInt32();
- response.insertAt((size_t)0, size);
- data.read(response.editArray(), size);
+ readVector(data, response);
reply->writeInt32(provideProvisionResponse(response));
-
return OK;
}
@@ -491,9 +599,7 @@
{
CHECK_INTERFACE(IDrm, data, reply);
Vector<uint8_t> ssRelease;
- uint32_t size = data.readInt32();
- ssRelease.insertAt((size_t)0, size);
- data.read(ssRelease.editArray(), size);
+ readVector(data, ssRelease);
reply->writeInt32(releaseSecureStops(ssRelease));
return OK;
}
@@ -515,8 +621,7 @@
String8 name = data.readString8();
Vector<uint8_t> value;
status_t result = getPropertyByteArray(name, value);
- reply->writeInt32(value.size());
- reply->write(value.array(), value.size());
+ writeVector(reply, value);
reply->writeInt32(result);
return OK;
}
@@ -535,15 +640,97 @@
CHECK_INTERFACE(IDrm, data, reply);
String8 name = data.readString8();
Vector<uint8_t> value;
- size_t count = data.readInt32();
- value.insertAt((size_t)0, count);
- data.read(value.editArray(), count);
+ readVector(data, value);
reply->writeInt32(setPropertyByteArray(name, value));
return OK;
}
- default:
- return BBinder::onTransact(code, data, reply, flags);
+ case SET_CIPHER_ALGORITHM:
+ {
+ CHECK_INTERFACE(IDrm, data, reply);
+ Vector<uint8_t> sessionId;
+ readVector(data, sessionId);
+ String8 algorithm = data.readString8();
+ reply->writeInt32(setCipherAlgorithm(sessionId, algorithm));
+ return OK;
+ }
+
+ case SET_MAC_ALGORITHM:
+ {
+ CHECK_INTERFACE(IDrm, data, reply);
+ Vector<uint8_t> sessionId;
+ readVector(data, sessionId);
+ String8 algorithm = data.readString8();
+ reply->writeInt32(setMacAlgorithm(sessionId, algorithm));
+ return OK;
+ }
+
+ case ENCRYPT:
+ {
+ CHECK_INTERFACE(IDrm, data, reply);
+ Vector<uint8_t> sessionId, keyId, input, iv, output;
+ readVector(data, sessionId);
+ readVector(data, keyId);
+ readVector(data, input);
+ readVector(data, iv);
+ uint32_t result = encrypt(sessionId, keyId, input, iv, output);
+ writeVector(reply, output);
+ reply->writeInt32(result);
+ return OK;
+ }
+
+ case DECRYPT:
+ {
+ CHECK_INTERFACE(IDrm, data, reply);
+ Vector<uint8_t> sessionId, keyId, input, iv, output;
+ readVector(data, sessionId);
+ readVector(data, keyId);
+ readVector(data, input);
+ readVector(data, iv);
+ uint32_t result = decrypt(sessionId, keyId, input, iv, output);
+ writeVector(reply, output);
+ reply->writeInt32(result);
+ return OK;
+ }
+
+ case SIGN:
+ {
+ CHECK_INTERFACE(IDrm, data, reply);
+ Vector<uint8_t> sessionId, keyId, message, signature;
+ readVector(data, sessionId);
+ readVector(data, keyId);
+ readVector(data, message);
+ uint32_t result = sign(sessionId, keyId, message, signature);
+ writeVector(reply, signature);
+ reply->writeInt32(result);
+ return OK;
+ }
+
+ case VERIFY:
+ {
+ CHECK_INTERFACE(IDrm, data, reply);
+ Vector<uint8_t> sessionId, keyId, message, signature;
+ readVector(data, sessionId);
+ readVector(data, keyId);
+ readVector(data, message);
+ readVector(data, signature);
+ bool match;
+ uint32_t result = verify(sessionId, keyId, message, signature, match);
+ reply->writeInt32(match);
+ reply->writeInt32(result);
+ return OK;
+ }
+
+ case SET_LISTENER: {
+ CHECK_INTERFACE(IDrm, data, reply);
+ sp<IDrmClient> listener =
+ interface_cast<IDrmClient>(data.readStrongBinder());
+ reply->writeInt32(setListener(listener));
+ return NO_ERROR;
+ } break;
+
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
}
}
diff --git a/media/libmedia/IDrmClient.cpp b/media/libmedia/IDrmClient.cpp
new file mode 100644
index 0000000..f50715e
--- /dev/null
+++ b/media/libmedia/IDrmClient.cpp
@@ -0,0 +1,81 @@
+/*
+**
+** Copyright 2013, 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 "IDrmClient"
+#include <utils/Log.h>
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+#include <media/IMediaPlayerClient.h>
+#include <media/IDrmClient.h>
+
+namespace android {
+
+enum {
+ NOTIFY = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class BpDrmClient: public BpInterface<IDrmClient>
+{
+public:
+ BpDrmClient(const sp<IBinder>& impl)
+ : BpInterface<IDrmClient>(impl)
+ {
+ }
+
+ virtual void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrmClient::getInterfaceDescriptor());
+ data.writeInt32((int)eventType);
+ data.writeInt32(extra);
+ if (obj && obj->dataSize() > 0) {
+ data.appendFrom(const_cast<Parcel *>(obj), 0, obj->dataSize());
+ }
+ remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(DrmClient, "android.media.IDrmClient");
+
+// ----------------------------------------------------------------------
+
+status_t BnDrmClient::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch (code) {
+ case NOTIFY: {
+ CHECK_INTERFACE(IDrmClient, data, reply);
+ int eventType = data.readInt32();
+ int extra = data.readInt32();
+ Parcel obj;
+ if (data.dataAvail() > 0) {
+ obj.appendFrom(const_cast<Parcel *>(&data), data.dataPosition(), data.dataAvail());
+ }
+
+ notify((DrmPlugin::EventType)eventType, extra, &obj);
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+}; // namespace android
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 3defec3..ecae3d3 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -398,6 +398,13 @@
if (mPlayer != 0 && isValidState) {
int durationMs;
status_t ret = mPlayer->getDuration(&durationMs);
+
+ if (ret != OK) {
+ // Do not enter error state just because no duration was available.
+ durationMs = -1;
+ ret = OK;
+ }
+
if (msec) {
*msec = durationMs;
}
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 2a6f3c7..d87bc7f 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -27,6 +27,7 @@
libbinder \
libcamera_client \
libcutils \
+ liblog \
libdl \
libgui \
libmedia \
diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp
index 6ac7530..1e6cd94 100644
--- a/media/libmediaplayerservice/Drm.cpp
+++ b/media/libmediaplayerservice/Drm.cpp
@@ -47,6 +47,7 @@
Drm::Drm()
: mInitCheck(NO_INIT),
+ mListener(NULL),
mFactory(NULL),
mPlugin(NULL) {
}
@@ -67,6 +68,41 @@
return mInitCheck;
}
+status_t Drm::setListener(const sp<IDrmClient>& listener)
+{
+ Mutex::Autolock lock(mEventLock);
+ mListener = listener;
+ return NO_ERROR;
+}
+
+void Drm::sendEvent(DrmPlugin::EventType eventType, int extra,
+ Vector<uint8_t> const *sessionId,
+ Vector<uint8_t> const *data)
+{
+ mEventLock.lock();
+ sp<IDrmClient> listener = mListener;
+ mEventLock.unlock();
+
+ if (listener != NULL) {
+ Parcel obj;
+ if (sessionId && sessionId->size()) {
+ obj.writeInt32(sessionId->size());
+ obj.write(sessionId->array(), sessionId->size());
+ } else {
+ obj.writeInt32(0);
+ }
+
+ if (data && data->size()) {
+ obj.writeInt32(data->size());
+ obj.write(data->array(), data->size());
+ } else {
+ obj.writeInt32(0);
+ }
+
+ Mutex::Autolock lock(mNotifyLock);
+ listener->notify(eventType, extra, &obj);
+ }
+}
/*
* Search the plugins directory for a plugin that supports the scheme
@@ -195,7 +231,9 @@
return mInitCheck;
}
- return mFactory->createDrmPlugin(uuid, &mPlugin);
+ status_t result = mFactory->createDrmPlugin(uuid, &mPlugin);
+ mPlugin->setListener(this);
+ return result;
}
status_t Drm::destroyPlugin() {
@@ -243,11 +281,11 @@
return mPlugin->closeSession(sessionId);
}
-status_t Drm::getLicenseRequest(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &initData,
- String8 const &mimeType, DrmPlugin::LicenseType licenseType,
- KeyedVector<String8, String8> const &optionalParameters,
- Vector<uint8_t> &request, String8 &defaultUrl) {
+status_t Drm::getKeyRequest(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &initData,
+ String8 const &mimeType, DrmPlugin::KeyType keyType,
+ KeyedVector<String8, String8> const &optionalParameters,
+ Vector<uint8_t> &request, String8 &defaultUrl) {
Mutex::Autolock autoLock(mLock);
if (mInitCheck != OK) {
@@ -258,12 +296,13 @@
return -EINVAL;
}
- return mPlugin->getLicenseRequest(sessionId, initData, mimeType, licenseType,
- optionalParameters, request, defaultUrl);
+ return mPlugin->getKeyRequest(sessionId, initData, mimeType, keyType,
+ optionalParameters, request, defaultUrl);
}
-status_t Drm::provideLicenseResponse(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &response) {
+status_t Drm::provideKeyResponse(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &response,
+ Vector<uint8_t> &keySetId) {
Mutex::Autolock autoLock(mLock);
if (mInitCheck != OK) {
@@ -274,10 +313,10 @@
return -EINVAL;
}
- return mPlugin->provideLicenseResponse(sessionId, response);
+ return mPlugin->provideKeyResponse(sessionId, response, keySetId);
}
-status_t Drm::removeLicense(Vector<uint8_t> const &sessionId) {
+status_t Drm::removeKeys(Vector<uint8_t> const &keySetId) {
Mutex::Autolock autoLock(mLock);
if (mInitCheck != OK) {
@@ -288,11 +327,11 @@
return -EINVAL;
}
- return mPlugin->removeLicense(sessionId);
+ return mPlugin->removeKeys(keySetId);
}
-status_t Drm::queryLicenseStatus(Vector<uint8_t> const &sessionId,
- KeyedVector<String8, String8> &infoMap) const {
+status_t Drm::restoreKeys(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keySetId) {
Mutex::Autolock autoLock(mLock);
if (mInitCheck != OK) {
@@ -303,7 +342,22 @@
return -EINVAL;
}
- return mPlugin->queryLicenseStatus(sessionId, infoMap);
+ return mPlugin->restoreKeys(sessionId, keySetId);
+}
+
+status_t Drm::queryKeyStatus(Vector<uint8_t> const &sessionId,
+ KeyedVector<String8, String8> &infoMap) const {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ if (mPlugin == NULL) {
+ return -EINVAL;
+ }
+
+ return mPlugin->queryKeyStatus(sessionId, infoMap);
}
status_t Drm::getProvisionRequest(Vector<uint8_t> &request, String8 &defaultUrl) {
@@ -420,4 +474,106 @@
return mPlugin->setPropertyByteArray(name, value);
}
+
+status_t Drm::setCipherAlgorithm(Vector<uint8_t> const &sessionId,
+ String8 const &algorithm) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ if (mPlugin == NULL) {
+ return -EINVAL;
+ }
+
+ return mPlugin->setCipherAlgorithm(sessionId, algorithm);
+}
+
+status_t Drm::setMacAlgorithm(Vector<uint8_t> const &sessionId,
+ String8 const &algorithm) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ if (mPlugin == NULL) {
+ return -EINVAL;
+ }
+
+ return mPlugin->setMacAlgorithm(sessionId, algorithm);
+}
+
+status_t Drm::encrypt(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &input,
+ Vector<uint8_t> const &iv,
+ Vector<uint8_t> &output) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ if (mPlugin == NULL) {
+ return -EINVAL;
+ }
+
+ return mPlugin->encrypt(sessionId, keyId, input, iv, output);
+}
+
+status_t Drm::decrypt(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &input,
+ Vector<uint8_t> const &iv,
+ Vector<uint8_t> &output) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ if (mPlugin == NULL) {
+ return -EINVAL;
+ }
+
+ return mPlugin->decrypt(sessionId, keyId, input, iv, output);
+}
+
+status_t Drm::sign(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &message,
+ Vector<uint8_t> &signature) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ if (mPlugin == NULL) {
+ return -EINVAL;
+ }
+
+ return mPlugin->sign(sessionId, keyId, message, signature);
+}
+
+status_t Drm::verify(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &message,
+ Vector<uint8_t> const &signature,
+ bool &match) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ if (mPlugin == NULL) {
+ return -EINVAL;
+ }
+
+ return mPlugin->verify(sessionId, keyId, message, signature, match);
+}
+
} // namespace android
diff --git a/media/libmediaplayerservice/Drm.h b/media/libmediaplayerservice/Drm.h
index 1b10958..3da8ad4 100644
--- a/media/libmediaplayerservice/Drm.h
+++ b/media/libmediaplayerservice/Drm.h
@@ -21,6 +21,7 @@
#include "SharedLibrary.h"
#include <media/IDrm.h>
+#include <media/IDrmClient.h>
#include <utils/threads.h>
namespace android {
@@ -28,7 +29,7 @@
struct DrmFactory;
struct DrmPlugin;
-struct Drm : public BnDrm {
+struct Drm : public BnDrm, public DrmPluginListener {
Drm();
virtual ~Drm();
@@ -45,19 +46,23 @@
virtual status_t closeSession(Vector<uint8_t> const &sessionId);
virtual status_t
- getLicenseRequest(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &initData,
- String8 const &mimeType, DrmPlugin::LicenseType licenseType,
- KeyedVector<String8, String8> const &optionalParameters,
- Vector<uint8_t> &request, String8 &defaultUrl);
+ getKeyRequest(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &initData,
+ String8 const &mimeType, DrmPlugin::KeyType keyType,
+ KeyedVector<String8, String8> const &optionalParameters,
+ Vector<uint8_t> &request, String8 &defaultUrl);
- virtual status_t provideLicenseResponse(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &response);
+ virtual status_t provideKeyResponse(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &response,
+ Vector<uint8_t> &keySetId);
- virtual status_t removeLicense(Vector<uint8_t> const &sessionId);
+ virtual status_t removeKeys(Vector<uint8_t> const &keySetId);
- virtual status_t queryLicenseStatus(Vector<uint8_t> const &sessionId,
- KeyedVector<String8, String8> &infoMap) const;
+ virtual status_t restoreKeys(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keySetId);
+
+ virtual status_t queryKeyStatus(Vector<uint8_t> const &sessionId,
+ KeyedVector<String8, String8> &infoMap) const;
virtual status_t getProvisionRequest(Vector<uint8_t> &request,
String8 &defaulUrl);
@@ -75,10 +80,50 @@
virtual status_t setPropertyByteArray(String8 const &name,
Vector<uint8_t> const &value ) const;
+ virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
+ String8 const &algorithm);
+
+ virtual status_t setMacAlgorithm(Vector<uint8_t> const &sessionId,
+ String8 const &algorithm);
+
+ virtual status_t encrypt(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &input,
+ Vector<uint8_t> const &iv,
+ Vector<uint8_t> &output);
+
+ virtual status_t decrypt(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &input,
+ Vector<uint8_t> const &iv,
+ Vector<uint8_t> &output);
+
+ virtual status_t sign(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &message,
+ Vector<uint8_t> &signature);
+
+ virtual status_t verify(Vector<uint8_t> const &sessionId,
+ Vector<uint8_t> const &keyId,
+ Vector<uint8_t> const &message,
+ Vector<uint8_t> const &signature,
+ bool &match);
+
+ virtual status_t setListener(const sp<IDrmClient>& listener);
+
+ virtual void sendEvent(DrmPlugin::EventType eventType, int extra,
+ Vector<uint8_t> const *sessionId,
+ Vector<uint8_t> const *data);
+
private:
mutable Mutex mLock;
status_t mInitCheck;
+
+ sp<IDrmClient> mListener;
+ mutable Mutex mEventLock;
+ mutable Mutex mNotifyLock;
+
sp<SharedLibrary> mLibrary;
DrmFactory *mFactory;
DrmPlugin *mPlugin;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 723af09..bdafb29 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -314,11 +314,11 @@
Mutex::Autolock autoLock(mLock);
if (mDurationUs < 0) {
- *msec = 0;
- } else {
- *msec = (mDurationUs + 500ll) / 1000;
+ return UNKNOWN_ERROR;
}
+ *msec = (mDurationUs + 500ll) / 1000;
+
return OK;
}
diff --git a/media/libnbaio/Android.mk b/media/libnbaio/Android.mk
index d372d20..5d00d15 100644
--- a/media/libnbaio/Android.mk
+++ b/media/libnbaio/Android.mk
@@ -30,6 +30,7 @@
libbinder \
libcommon_time_client \
libcutils \
- libutils
+ libutils \
+ liblog
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index c9f8741..ee49033 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -166,6 +166,24 @@
////////////////////////////////////////////////////////////////////////////////
+struct ACodec::DeathNotifier : public IBinder::DeathRecipient {
+ DeathNotifier(const sp<AMessage> ¬ify)
+ : mNotify(notify) {
+ }
+
+ virtual void binderDied(const wp<IBinder> &) {
+ mNotify->post();
+ }
+
+protected:
+ virtual ~DeathNotifier() {}
+
+private:
+ sp<AMessage> mNotify;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DeathNotifier);
+};
+
struct ACodec::UninitializedState : public ACodec::BaseState {
UninitializedState(ACodec *codec);
@@ -177,6 +195,8 @@
void onSetup(const sp<AMessage> &msg);
bool onAllocateComponent(const sp<AMessage> &msg);
+ sp<DeathNotifier> mDeathNotifier;
+
DISALLOW_EVIL_CONSTRUCTORS(UninitializedState);
};
@@ -1432,14 +1452,6 @@
CHECK_EQ(err, (status_t)OK);
CHECK_EQ((int)format.eCompressionFormat, (int)OMX_VIDEO_CodingUnused);
- CHECK(format.eColorFormat == OMX_COLOR_FormatYUV420Planar
- || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar
- || format.eColorFormat == OMX_COLOR_FormatCbYCrY
- || format.eColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
- || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar
- || format.eColorFormat == OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka
- || format.eColorFormat == OMX_SEC_COLOR_FormatNV12Tiled);
-
return mOMX->setParameter(
mNode, OMX_IndexParamVideoPortFormat,
&format, sizeof(format));
@@ -2487,6 +2499,13 @@
return true;
}
+ case ACodec::kWhatOMXDied:
+ {
+ ALOGE("OMX/mediaserver died, signalling error!");
+ mCodec->signalError(OMX_ErrorResourcesLost, DEAD_OBJECT);
+ break;
+ }
+
default:
return false;
}
@@ -3035,6 +3054,18 @@
void ACodec::UninitializedState::stateEntered() {
ALOGV("Now uninitialized");
+
+ if (mDeathNotifier != NULL) {
+ mCodec->mOMX->asBinder()->unlinkToDeath(mDeathNotifier);
+ mDeathNotifier.clear();
+ }
+
+ mCodec->mNativeWindow.clear();
+ mCodec->mNode = NULL;
+ mCodec->mOMX.clear();
+ mCodec->mQuirks = 0;
+ mCodec->mFlags = 0;
+ mCodec->mComponentName.clear();
}
bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) {
@@ -3106,6 +3137,15 @@
sp<IOMX> omx = client.interface();
+ sp<AMessage> notify = new AMessage(kWhatOMXDied, mCodec->id());
+
+ mDeathNotifier = new DeathNotifier(notify);
+ if (omx->asBinder()->linkToDeath(mDeathNotifier) != OK) {
+ // This was a local binder, if it dies so do we, we won't care
+ // about any notifications in the afterlife.
+ mDeathNotifier.clear();
+ }
+
Vector<OMXCodec::CodecNameAndQuirks> matchingCodecs;
AString mime;
@@ -3170,7 +3210,7 @@
return false;
}
- sp<AMessage> notify = new AMessage(kWhatOMXMessage, mCodec->id());
+ notify = new AMessage(kWhatOMXMessage, mCodec->id());
observer->setNotificationMessage(notify);
mCodec->mComponentName = componentName;
@@ -3224,13 +3264,6 @@
if (!keepComponentAllocated) {
CHECK_EQ(mCodec->mOMX->freeNode(mCodec->mNode), (status_t)OK);
- mCodec->mNativeWindow.clear();
- mCodec->mNode = NULL;
- mCodec->mOMX.clear();
- mCodec->mQuirks = 0;
- mCodec->mFlags = 0;
- mCodec->mComponentName.clear();
-
mCodec->changeState(mCodec->mUninitializedState);
}
@@ -3416,6 +3449,21 @@
return true;
}
+ case kWhatResume:
+ {
+ // We'll be active soon enough.
+ return true;
+ }
+
+ case kWhatFlush:
+ {
+ // We haven't even started yet, so we're flushed alright...
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatFlushCompleted);
+ notify->post();
+ return true;
+ }
+
default:
return BaseState::onMessageReceived(msg);
}
@@ -3611,7 +3659,6 @@
(status_t)OK);
mCodec->changeState(mCodec->mFlushingState);
-
handled = true;
break;
}
@@ -4126,6 +4173,10 @@
mCodec->mInputEOSResult = OK;
+ if (mCodec->mSkipCutBuffer != NULL) {
+ mCodec->mSkipCutBuffer->clear();
+ }
+
mCodec->changeState(mCodec->mExecutingState);
}
}
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 19b38ee..fc6fd9c 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -58,6 +58,19 @@
return true;
}
+bool DataSource::getUInt24(off64_t offset, uint32_t *x) {
+ *x = 0;
+
+ uint8_t byte[3];
+ if (readAt(offset, byte, 3) != 3) {
+ return false;
+ }
+
+ *x = (byte[0] << 16) | (byte[1] << 8) | byte[2];
+
+ return true;
+}
+
bool DataSource::getUInt32(off64_t offset, uint32_t *x) {
*x = 0;
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 56fad60..3503aaf 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -78,6 +78,19 @@
int32_t mLastParsedTrackId;
int32_t mTrackId;
+ int32_t mCryptoMode; // passed in from extractor
+ int32_t mDefaultIVSize; // passed in from extractor
+ uint8_t mCryptoKey[16]; // passed in from extractor
+ uint32_t mCurrentAuxInfoType;
+ uint32_t mCurrentAuxInfoTypeParameter;
+ uint32_t mCurrentDefaultSampleInfoSize;
+ uint32_t mCurrentSampleInfoCount;
+ uint32_t mCurrentSampleInfoAllocSize;
+ uint8_t* mCurrentSampleInfoSizes;
+ uint32_t mCurrentSampleInfoOffsetCount;
+ uint32_t mCurrentSampleInfoOffsetsAllocSize;
+ uint64_t* mCurrentSampleInfoOffsets;
+
bool mIsAVC;
size_t mNALLengthSize;
@@ -95,6 +108,8 @@
status_t parseChunk(off64_t *offset);
status_t parseTrackFragmentHeader(off64_t offset, off64_t size);
status_t parseTrackFragmentRun(off64_t offset, off64_t size);
+ status_t parseSampleAuxiliaryInformationSizes(off64_t offset, off64_t size);
+ status_t parseSampleAuxiliaryInformationOffsets(off64_t offset, off64_t size);
struct TrackFragmentHeaderInfo {
enum Flags {
@@ -122,6 +137,9 @@
off64_t offset;
size_t size;
uint32_t duration;
+ uint8_t iv[16];
+ Vector<size_t> clearsizes;
+ Vector<size_t> encryptedsizes;
};
Vector<Sample> mCurrentSamples;
@@ -333,6 +351,10 @@
sinf = next;
}
mFirstSINF = NULL;
+
+ for (size_t i = 0; i < mPssh.size(); i++) {
+ delete [] mPssh[i].data;
+ }
}
uint32_t MPEG4Extractor::flags() const {
@@ -353,6 +375,7 @@
size_t MPEG4Extractor::countTracks() {
status_t err;
if ((err = readMetaData()) != OK) {
+ ALOGV("MPEG4Extractor::countTracks: no tracks");
return 0;
}
@@ -363,6 +386,7 @@
track = track->next;
}
+ ALOGV("MPEG4Extractor::countTracks: %d tracks", n);
return n;
}
@@ -461,6 +485,23 @@
}
CHECK_NE(err, (status_t)NO_INIT);
+
+ // copy pssh data into file metadata
+ int psshsize = 0;
+ for (size_t i = 0; i < mPssh.size(); i++) {
+ psshsize += 20 + mPssh[i].datalen;
+ }
+ if (psshsize) {
+ char *buf = (char*)malloc(psshsize);
+ char *ptr = buf;
+ for (size_t i = 0; i < mPssh.size(); i++) {
+ memcpy(ptr, mPssh[i].uuid, 20); // uuid + length
+ memcpy(ptr + 20, mPssh[i].data, mPssh[i].datalen);
+ ptr += (20 + mPssh[i].datalen);
+ }
+ mFileMetaData->setData(kKeyPssh, 'pssh', buf, psshsize);
+ free(buf);
+ }
return mInitCheck;
}
@@ -759,6 +800,8 @@
case FOURCC('m', 'f', 'r', 'a'):
case FOURCC('u', 'd', 't', 'a'):
case FOURCC('i', 'l', 's', 't'):
+ case FOURCC('s', 'i', 'n', 'f'):
+ case FOURCC('s', 'c', 'h', 'i'):
{
if (chunk_type == FOURCC('s', 't', 'b', 'l')) {
ALOGV("sampleTable chunk is %d bytes long.", (size_t)chunk_size);
@@ -846,6 +889,69 @@
break;
}
+ case FOURCC('f', 'r', 'm', 'a'):
+ {
+ int32_t original_fourcc;
+ if (mDataSource->readAt(data_offset, &original_fourcc, 4) < 4) {
+ return ERROR_IO;
+ }
+ original_fourcc = ntohl(original_fourcc);
+ ALOGV("read original format: %d", original_fourcc);
+ mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(original_fourcc));
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('t', 'e', 'n', 'c'):
+ {
+ if (chunk_size < 32) {
+ return ERROR_MALFORMED;
+ }
+
+ // tenc box contains 1 byte version, 3 byte flags, 3 byte default algorithm id, one byte
+ // default IV size, 16 bytes default KeyID
+ // (ISO 23001-7)
+ char buf[4];
+ memset(buf, 0, 4);
+ if (mDataSource->readAt(data_offset + 4, buf + 1, 3) < 3) {
+ return ERROR_IO;
+ }
+ uint32_t defaultAlgorithmId = ntohl(*((int32_t*)buf));
+ if (defaultAlgorithmId > 1) {
+ // only 0 (clear) and 1 (AES-128) are valid
+ return ERROR_MALFORMED;
+ }
+
+ memset(buf, 0, 4);
+ if (mDataSource->readAt(data_offset + 7, buf + 3, 1) < 1) {
+ return ERROR_IO;
+ }
+ uint32_t defaultIVSize = ntohl(*((int32_t*)buf));
+
+ if ((defaultAlgorithmId == 0 && defaultIVSize != 0) ||
+ (defaultAlgorithmId != 0 && defaultIVSize == 0)) {
+ // only unencrypted data must have 0 IV size
+ return ERROR_MALFORMED;
+ } else if (defaultIVSize != 0 &&
+ defaultIVSize != 8 &&
+ defaultIVSize != 16) {
+ // only supported sizes are 0, 8 and 16
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t defaultKeyId[16];
+
+ if (mDataSource->readAt(data_offset + 8, &defaultKeyId, 16) < 16) {
+ return ERROR_IO;
+ }
+
+ mLastTrack->meta->setInt32(kKeyCryptoMode, defaultAlgorithmId);
+ mLastTrack->meta->setInt32(kKeyCryptoDefaultIVSize, defaultIVSize);
+ mLastTrack->meta->setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16);
+ *offset += chunk_size;
+ break;
+ }
+
case FOURCC('t', 'k', 'h', 'd'):
{
status_t err;
@@ -857,6 +963,37 @@
break;
}
+ case FOURCC('p', 's', 's', 'h'):
+ {
+ PsshInfo pssh;
+
+ if (mDataSource->readAt(data_offset + 4, &pssh.uuid, 16) < 16) {
+ return ERROR_IO;
+ }
+
+ uint32_t psshdatalen = 0;
+ if (mDataSource->readAt(data_offset + 20, &psshdatalen, 4) < 4) {
+ return ERROR_IO;
+ }
+ pssh.datalen = ntohl(psshdatalen);
+ ALOGV("pssh data size: %d", pssh.datalen);
+ if (pssh.datalen + 20 > chunk_size) {
+ // pssh data length exceeds size of containing box
+ return ERROR_MALFORMED;
+ }
+
+ pssh.data = new uint8_t[pssh.datalen];
+ ALOGV("allocated pssh @ %p", pssh.data);
+ ssize_t requested = (ssize_t) pssh.datalen;
+ if (mDataSource->readAt(data_offset + 24, pssh.data, requested) < requested) {
+ return ERROR_IO;
+ }
+ mPssh.push_back(pssh);
+
+ *offset += chunk_size;
+ break;
+ }
+
case FOURCC('m', 'd', 'h', 'd'):
{
if (chunk_data_size < 4) {
@@ -970,16 +1107,17 @@
// For 3GPP timed text, there could be multiple tx3g boxes contain
// multiple text display formats. These formats will be used to
// display the timed text.
+ // For encrypted files, there may also be more than one entry.
const char *mime;
CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
- if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
+ if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) &&
+ strcasecmp(mime, "application/octet-stream")) {
// For now we only support a single type of media per track.
mLastTrack->skipTrack = true;
*offset += chunk_size;
break;
}
}
-
off64_t stop_offset = *offset + chunk_size;
*offset = data_offset + 8;
for (uint32_t i = 0; i < entry_count; ++i) {
@@ -1053,6 +1191,7 @@
}
case FOURCC('m', 'p', '4', 'v'):
+ case FOURCC('e', 'n', 'c', 'v'):
case FOURCC('s', '2', '6', '3'):
case FOURCC('H', '2', '6', '3'):
case FOURCC('h', '2', '6', '3'):
@@ -1075,7 +1214,7 @@
uint16_t width = U16_AT(&buffer[6 + 18]);
uint16_t height = U16_AT(&buffer[6 + 20]);
- // The video sample is not stand-compliant if it has invalid dimension.
+ // The video sample is not standard-compliant if it has invalid dimension.
// Use some default width and height value, and
// let the decoder figure out the actual width and height (and thus
// be prepared for INFO_FOMRAT_CHANGED event).
@@ -1085,7 +1224,10 @@
// printf("*** coding='%s' width=%d height=%d\n",
// chunk, width, height);
- mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
+ if (chunk_type != FOURCC('e', 'n', 'c', 'v')) {
+ // if the chunk type is encv, we'll get the type from the sinf/frma box later
+ mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
+ }
mLastTrack->meta->setInt32(kKeyWidth, width);
mLastTrack->meta->setInt32(kKeyHeight, height);
@@ -1442,6 +1584,7 @@
case FOURCC('m', 'd', 'a', 't'):
{
+ ALOGV("mdat chunk, drm: %d", mIsDrm);
if (!mIsDrm) {
*offset += chunk_size;
break;
@@ -1968,6 +2111,8 @@
return NULL;
}
+ ALOGV("getTrack called, pssh: %d", mPssh.size());
+
return new MPEG4Source(
track->meta, mDataSource, track->timescale, track->sampleTable,
mSidxEntries, mMoofOffset);
@@ -2139,6 +2284,10 @@
mFirstMoofOffset(firstMoofOffset),
mCurrentMoofOffset(firstMoofOffset),
mCurrentTime(0),
+ mCurrentSampleInfoAllocSize(0),
+ mCurrentSampleInfoSizes(NULL),
+ mCurrentSampleInfoOffsetsAllocSize(0),
+ mCurrentSampleInfoOffsets(NULL),
mIsAVC(false),
mNALLengthSize(0),
mStarted(false),
@@ -2146,6 +2295,18 @@
mBuffer(NULL),
mWantsNALFragments(false),
mSrcBuffer(NULL) {
+
+ mFormat->findInt32(kKeyCryptoMode, &mCryptoMode);
+ mFormat->findInt32(kKeyCryptoDefaultIVSize, &mDefaultIVSize);
+ uint32_t keytype;
+ const void *key;
+ size_t keysize;
+ if (mFormat->findData(kKeyCryptoKey, &keytype, &key, &keysize)) {
+ CHECK(keysize <= 16);
+ memset(mCryptoKey, 0, 16);
+ memcpy(mCryptoKey, key, keysize);
+ }
+
const char *mime;
bool success = mFormat->findCString(kKeyMIMEType, &mime);
CHECK(success);
@@ -2179,6 +2340,8 @@
if (mStarted) {
stop();
}
+ free(mCurrentSampleInfoSizes);
+ free(mCurrentSampleInfoOffsets);
}
status_t MPEG4Source::start(MetaData *params) {
@@ -2274,7 +2437,7 @@
}
}
if (chunk_type == FOURCC('m', 'o', 'o', 'f')) {
- // *offset points to then mdat box following this moof
+ // *offset points to the mdat box following this moof
parseChunk(offset); // doesn't actually parse it, just updates offset
mNextMoofOffset = *offset;
}
@@ -2302,6 +2465,31 @@
break;
}
+ case FOURCC('s', 'a', 'i', 'z'): {
+ status_t err;
+ if ((err = parseSampleAuxiliaryInformationSizes(data_offset, chunk_data_size)) != OK) {
+ return err;
+ }
+ *offset += chunk_size;
+ break;
+ }
+ case FOURCC('s', 'a', 'i', 'o'): {
+ status_t err;
+ if ((err = parseSampleAuxiliaryInformationOffsets(data_offset, chunk_data_size)) != OK) {
+ return err;
+ }
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('m', 'd', 'a', 't'): {
+ // parse DRM info if present
+ ALOGV("MPEG4Source::parseChunk mdat");
+ // if saiz/saoi was previously observed, do something with the sampleinfos
+ *offset += chunk_size;
+ break;
+ }
+
default: {
*offset += chunk_size;
break;
@@ -2310,6 +2498,152 @@
return OK;
}
+status_t MPEG4Source::parseSampleAuxiliaryInformationSizes(off64_t offset, off64_t size) {
+ ALOGV("parseSampleAuxiliaryInformationSizes");
+ // 14496-12 8.7.12
+ uint8_t version;
+ if (mDataSource->readAt(
+ offset, &version, sizeof(version))
+ < (ssize_t)sizeof(version)) {
+ return ERROR_IO;
+ }
+
+ if (version != 0) {
+ return ERROR_UNSUPPORTED;
+ }
+ offset++;
+
+ uint32_t flags;
+ if (!mDataSource->getUInt24(offset, &flags)) {
+ return ERROR_IO;
+ }
+ offset += 3;
+
+ if (flags & 1) {
+ uint32_t tmp;
+ if (!mDataSource->getUInt32(offset, &tmp)) {
+ return ERROR_MALFORMED;
+ }
+ mCurrentAuxInfoType = tmp;
+ offset += 4;
+ if (!mDataSource->getUInt32(offset, &tmp)) {
+ return ERROR_MALFORMED;
+ }
+ mCurrentAuxInfoTypeParameter = tmp;
+ offset += 4;
+ }
+
+ uint8_t defsize;
+ if (mDataSource->readAt(offset, &defsize, 1) != 1) {
+ return ERROR_MALFORMED;
+ }
+ mCurrentDefaultSampleInfoSize = defsize;
+ offset++;
+
+ uint32_t smplcnt;
+ if (!mDataSource->getUInt32(offset, &smplcnt)) {
+ return ERROR_MALFORMED;
+ }
+ offset += 4;
+
+ if (smplcnt > mCurrentSampleInfoAllocSize) {
+ mCurrentSampleInfoSizes = (uint8_t*) realloc(mCurrentSampleInfoSizes, smplcnt);
+ mCurrentSampleInfoAllocSize = smplcnt;
+ }
+ mCurrentSampleInfoCount = smplcnt;
+
+ mDataSource->readAt(offset, mCurrentSampleInfoSizes, smplcnt);
+ return OK;
+}
+
+status_t MPEG4Source::parseSampleAuxiliaryInformationOffsets(off64_t offset, off64_t size) {
+ ALOGV("parseSampleAuxiliaryInformationOffsets");
+ // 14496-12 8.7.13
+ uint8_t version;
+ if (mDataSource->readAt(offset, &version, sizeof(version)) != 1) {
+ return ERROR_IO;
+ }
+ offset++;
+
+ uint32_t flags;
+ if (!mDataSource->getUInt24(offset, &flags)) {
+ return ERROR_IO;
+ }
+ offset += 3;
+
+ uint32_t entrycount;
+ if (!mDataSource->getUInt32(offset, &entrycount)) {
+ return ERROR_IO;
+ }
+ offset += 4;
+
+ if (entrycount > mCurrentSampleInfoOffsetsAllocSize) {
+ mCurrentSampleInfoOffsets = (uint64_t*) realloc(mCurrentSampleInfoOffsets, entrycount * 8);
+ mCurrentSampleInfoOffsetsAllocSize = entrycount;
+ }
+ mCurrentSampleInfoOffsetCount = entrycount;
+
+ for (size_t i = 0; i < entrycount; i++) {
+ if (version == 0) {
+ uint32_t tmp;
+ if (!mDataSource->getUInt32(offset, &tmp)) {
+ return ERROR_IO;
+ }
+ mCurrentSampleInfoOffsets[i] = tmp;
+ offset += 4;
+ } else {
+ uint64_t tmp;
+ if (!mDataSource->getUInt64(offset, &tmp)) {
+ return ERROR_IO;
+ }
+ mCurrentSampleInfoOffsets[i] = tmp;
+ offset += 8;
+ }
+ }
+
+ // parse clear/encrypted data
+
+ off64_t drmoffset = mCurrentSampleInfoOffsets[0]; // from moof
+
+ drmoffset += mCurrentMoofOffset;
+ int ivlength;
+ CHECK(mFormat->findInt32(kKeyCryptoDefaultIVSize, &ivlength));
+ int foo = 1;
+ for (size_t i = 0; i < mCurrentSampleInfoCount; i++) {
+ Sample *smpl = &mCurrentSamples.editItemAt(i);
+
+ memset(smpl->iv, 0, 16);
+ if (mDataSource->readAt(drmoffset, smpl->iv, ivlength) != ivlength) {
+ return ERROR_IO;
+ }
+
+ drmoffset += ivlength;
+
+ uint16_t numsubsamples;
+ if (!mDataSource->getUInt16(drmoffset, &numsubsamples)) {
+ return ERROR_IO;
+ }
+ drmoffset += 2;
+ for (size_t j = 0; j < numsubsamples; j++) {
+ uint16_t numclear;
+ uint32_t numencrypted;
+ if (!mDataSource->getUInt16(drmoffset, &numclear)) {
+ return ERROR_IO;
+ }
+ drmoffset += 2;
+ if (!mDataSource->getUInt32(drmoffset, &numencrypted)) {
+ return ERROR_IO;
+ }
+ drmoffset += 4;
+ smpl->clearsizes.add(numclear);
+ smpl->encryptedsizes.add(numencrypted);
+ }
+ }
+
+
+ return OK;
+}
+
status_t MPEG4Source::parseTrackFragmentHeader(off64_t offset, off64_t size) {
if (size < 8) {
@@ -2317,7 +2651,7 @@
}
uint32_t flags;
- if (!mDataSource->getUInt32(offset, &flags)) {
+ if (!mDataSource->getUInt32(offset, &flags)) { // actually version + flags
return ERROR_MALFORMED;
}
@@ -2550,8 +2884,8 @@
offset += 4;
}
- ALOGV("adding sample at offset 0x%08llx, size %u, duration %u, "
- " flags 0x%08x",
+ ALOGV("adding sample %d at offset 0x%08llx, size %u, duration %u, "
+ " flags 0x%08x", i + 1,
dataOffset, sampleSize, sampleDuration,
(flags & kFirstSampleFlagsPresent) && i == 0
? firstSampleFlags : sampleFlags);
@@ -3111,6 +3445,20 @@
mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
}
+ const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex];
+ if (smpl->encryptedsizes.size()) {
+ // store clear/encrypted lengths in metadata
+ sp<MetaData> bufmeta = mBuffer->meta_data();
+ bufmeta->setData(kKeyPlainSizes, 0,
+ smpl->clearsizes.array(), smpl->clearsizes.size() * 4);
+ bufmeta->setData(kKeyEncryptedSizes, 0,
+ smpl->encryptedsizes.array(), smpl->encryptedsizes.size() * 4);
+ bufmeta->setData(kKeyCryptoIV, 0, smpl->iv, 16); // use 16 or the actual size?
+ bufmeta->setInt32(kKeyCryptoDefaultIVSize, mDefaultIVSize);
+ bufmeta->setInt32(kKeyCryptoMode, mCryptoMode);
+ bufmeta->setData(kKeyCryptoKey, 0, mCryptoKey, 16);
+ }
+
++mCurrentSampleIndex;
*out = mBuffer;
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index 404fa94..7bc7da2 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -228,6 +228,34 @@
return convertMetaDataToMessage(meta, format);
}
+status_t NuMediaExtractor::getFileFormat(sp<AMessage> *format) const {
+ Mutex::Autolock autoLock(mLock);
+
+ *format = NULL;
+
+ if (mImpl == NULL) {
+ return -EINVAL;
+ }
+
+ sp<MetaData> meta = mImpl->getMetaData();
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+ *format = new AMessage();
+ (*format)->setString("mime", mime);
+
+ uint32_t type;
+ const void *pssh;
+ size_t psshsize;
+ if (meta->findData(kKeyPssh, &type, &pssh, &psshsize)) {
+ sp<ABuffer> buf = new ABuffer(psshsize);
+ memcpy(buf->data(), pssh, psshsize);
+ (*format)->setBuffer("pssh", buf);
+ }
+
+ return OK;
+}
+
status_t NuMediaExtractor::selectTrack(size_t index) {
Mutex::Autolock autoLock(mLock);
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index ff72e0e..1822f07 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -32,7 +32,7 @@
MuxOMX(const sp<IOMX> &remoteOMX);
virtual ~MuxOMX();
- virtual IBinder *onAsBinder() { return NULL; }
+ virtual IBinder *onAsBinder() { return mRemoteOMX->asBinder().get(); }
virtual bool livesLocally(node_id node, pid_t pid);
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 22aefcc..6c0779d 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1213,13 +1213,6 @@
CHECK_EQ(err, (status_t)OK);
CHECK_EQ((int)format.eCompressionFormat, (int)OMX_VIDEO_CodingUnused);
- CHECK(format.eColorFormat == OMX_COLOR_FormatYUV420Planar
- || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar
- || format.eColorFormat == OMX_COLOR_FormatCbYCrY
- || format.eColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
- || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar
- || format.eColorFormat == OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka);
-
int32_t colorFormat;
if (meta->findInt32(kKeyColorFormat, &colorFormat)
&& colorFormat != OMX_COLOR_FormatUnused
diff --git a/media/libstagefright/chromium_http/Android.mk b/media/libstagefright/chromium_http/Android.mk
index 2c6d84c..f26f386 100644
--- a/media/libstagefright/chromium_http/Android.mk
+++ b/media/libstagefright/chromium_http/Android.mk
@@ -22,6 +22,7 @@
libchromium_net \
libutils \
libcutils \
+ liblog \
libstagefright_foundation \
libstagefright \
libdrmframework
diff --git a/media/libstagefright/codecs/aacdec/Android.mk b/media/libstagefright/codecs/aacdec/Android.mk
index 4dc38a8..ffa64f9 100644
--- a/media/libstagefright/codecs/aacdec/Android.mk
+++ b/media/libstagefright/codecs/aacdec/Android.mk
@@ -20,7 +20,7 @@
LOCAL_STATIC_LIBRARIES := libFraunhoferAAC
LOCAL_SHARED_LIBRARIES := \
- libstagefright_omx libstagefright_foundation libutils libcutils
+ libstagefright_omx libstagefright_foundation libutils libcutils liblog
LOCAL_MODULE := libstagefright_soft_aacdec
LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index a8ab2ac..8ba2afb 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -118,7 +118,7 @@
status = OK;
}
}
- mIsFirst = true;
+ mDecoderHasData = false;
// for streams that contain metadata, use the mobile profile DRC settings unless overridden
// by platform properties:
@@ -327,6 +327,7 @@
notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
return;
}
+
inQueue.erase(inQueue.begin());
info->mOwnedByUs = false;
notifyEmptyBufferDone(header);
@@ -358,7 +359,7 @@
inInfo->mOwnedByUs = false;
notifyEmptyBufferDone(inHeader);
- if (!mIsFirst) {
+ if (mDecoderHasData) {
// flush out the decoder's delayed data by calling DecodeFrame
// one more time, with the AACDEC_FLUSH flag set
INT_PCM *outBuffer =
@@ -370,6 +371,7 @@
outBuffer,
outHeader->nAllocLen,
AACDEC_FLUSH);
+ mDecoderHasData = false;
if (decoderErr != AAC_DEC_OK) {
mSignalledError = true;
@@ -385,9 +387,7 @@
* sizeof(int16_t)
* mStreamInfo->numChannels;
} else {
- // Since we never discarded frames from the start, we won't have
- // to add any padding at the end either.
-
+ // we never submitted any data to the decoder, so there's nothing to flush out
outHeader->nFilledLen = 0;
}
@@ -473,6 +473,7 @@
inBuffer,
inBufferLength,
bytesValid);
+ mDecoderHasData = true;
decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
outBuffer,
@@ -484,45 +485,6 @@
}
}
- /*
- * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
- * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
- * rate system and the sampling rate in the final output is actually
- * doubled compared with the core AAC decoder sampling rate.
- *
- * Explicit signalling is done by explicitly defining SBR audio object
- * type in the bitstream. Implicit signalling is done by embedding
- * SBR content in AAC extension payload specific to SBR, and hence
- * requires an AAC decoder to perform pre-checks on actual audio frames.
- *
- * Thus, we could not say for sure whether a stream is
- * AAC+/eAAC+ until the first data frame is decoded.
- */
- if (mInputBufferCount <= 2) {
- if (mStreamInfo->sampleRate != prevSampleRate ||
- mStreamInfo->numChannels != prevNumChannels) {
- maybeConfigureDownmix();
- ALOGI("Reconfiguring decoder: %d Hz, %d channels",
- mStreamInfo->sampleRate,
- mStreamInfo->numChannels);
-
- // We're going to want to revisit this input buffer, but
- // may have already advanced the offset. Undo that if
- // necessary.
- inHeader->nOffset -= adtsHeaderSize;
- inHeader->nFilledLen += adtsHeaderSize;
-
- notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
- mOutputPortSettingsChange = AWAITING_DISABLED;
- return;
- }
- } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
- ALOGW("Invalid AAC stream");
- mSignalledError = true;
- notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
- return;
- }
-
size_t numOutBytes =
mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
@@ -544,17 +506,51 @@
// fall through
}
+ if (inHeader->nFilledLen == 0) {
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
+
+ /*
+ * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
+ * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
+ * rate system and the sampling rate in the final output is actually
+ * doubled compared with the core AAC decoder sampling rate.
+ *
+ * Explicit signalling is done by explicitly defining SBR audio object
+ * type in the bitstream. Implicit signalling is done by embedding
+ * SBR content in AAC extension payload specific to SBR, and hence
+ * requires an AAC decoder to perform pre-checks on actual audio frames.
+ *
+ * Thus, we could not say for sure whether a stream is
+ * AAC+/eAAC+ until the first data frame is decoded.
+ */
+ if (mInputBufferCount <= 2) {
+ if (mStreamInfo->sampleRate != prevSampleRate ||
+ mStreamInfo->numChannels != prevNumChannels) {
+ maybeConfigureDownmix();
+ ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
+ prevSampleRate, mStreamInfo->sampleRate,
+ prevNumChannels, mStreamInfo->numChannels);
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ return;
+ }
+ } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
+ ALOGW("Invalid AAC stream");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ return;
+ }
+
if (decoderErr == AAC_DEC_OK || mNumSamplesOutput > 0) {
// We'll only output data if we successfully decoded it or
// we've previously decoded valid data, in the latter case
// (decode failed) we'll output a silent frame.
- if (mIsFirst) {
- mIsFirst = false;
- // the first decoded frame should be discarded to account
- // for decoder delay
- numOutBytes = 0;
- }
-
outHeader->nFilledLen = numOutBytes;
outHeader->nFlags = 0;
@@ -571,14 +567,6 @@
outHeader = NULL;
}
- if (inHeader->nFilledLen == 0) {
- inInfo->mOwnedByUs = false;
- inQueue.erase(inQueue.begin());
- inInfo = NULL;
- notifyEmptyBufferDone(inHeader);
- inHeader = NULL;
- }
-
if (decoderErr == AAC_DEC_OK) {
++mInputBufferCount;
}
@@ -589,14 +577,21 @@
if (portIndex == 0) {
// Make sure that the next buffer output does not still
// depend on fragments from the last one decoded.
- aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
- mIsFirst = true;
+ // drain all existing data
+ drainDecoder();
}
}
-void SoftAAC2::onReset() {
+void SoftAAC2::drainDecoder() {
+ short buf [2048];
+ aacDecoder_DecodeFrame(mAACDecoder, buf, 4096, AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR);
+ aacDecoder_DecodeFrame(mAACDecoder, buf, 4096, AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR);
aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
- mIsFirst = true;
+ mDecoderHasData = false;
+}
+
+void SoftAAC2::onReset() {
+ drainDecoder();
}
void SoftAAC2::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h
index 6957ade..2d960ab 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.h
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h
@@ -52,7 +52,7 @@
HANDLE_AACDECODER mAACDecoder;
CStreamInfo *mStreamInfo;
bool mIsADTS;
- bool mIsFirst;
+ bool mDecoderHasData;
size_t mInputBufferCount;
bool mSignalledError;
int64_t mAnchorTimeUs;
@@ -68,6 +68,7 @@
status_t initDecoder();
bool isConfigured() const;
void maybeConfigureDownmix() const;
+ void drainDecoder();
DISALLOW_EVIL_CONSTRUCTORS(SoftAAC2);
};
diff --git a/media/libstagefright/codecs/aacenc/Android.mk b/media/libstagefright/codecs/aacenc/Android.mk
index 820734d..057c69b 100644
--- a/media/libstagefright/codecs/aacenc/Android.mk
+++ b/media/libstagefright/codecs/aacenc/Android.mk
@@ -109,7 +109,7 @@
LOCAL_STATIC_LIBRARIES := libFraunhoferAAC
LOCAL_SHARED_LIBRARIES := \
- libstagefright_omx libstagefright_foundation libutils
+ libstagefright_omx libstagefright_foundation libutils liblog
LOCAL_MODULE := libstagefright_soft_aacenc
LOCAL_MODULE_TAGS := optional
@@ -132,7 +132,7 @@
libstagefright_aacenc
LOCAL_SHARED_LIBRARIES := \
- libstagefright_omx libstagefright_foundation libutils \
+ libstagefright_omx libstagefright_foundation libutils liblog \
libstagefright_enc_common
LOCAL_MODULE := libstagefright_soft_aacenc
diff --git a/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp
index 7719435..5749733 100644
--- a/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp
+++ b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp
@@ -481,7 +481,7 @@
void* inBuffer[] = { (unsigned char *)mInputFrame };
INT inBufferIds[] = { IN_AUDIO_DATA };
- INT inBufferSize[] = { numBytesPerInputFrame };
+ INT inBufferSize[] = { (INT)numBytesPerInputFrame };
INT inBufferElSize[] = { sizeof(int16_t) };
AACENC_BufDesc inBufDesc;
diff --git a/media/libstagefright/codecs/amrnb/dec/Android.mk b/media/libstagefright/codecs/amrnb/dec/Android.mk
index b48a459..8d6c6f8 100644
--- a/media/libstagefright/codecs/amrnb/dec/Android.mk
+++ b/media/libstagefright/codecs/amrnb/dec/Android.mk
@@ -72,7 +72,7 @@
libstagefright_amrnbdec libstagefright_amrwbdec
LOCAL_SHARED_LIBRARIES := \
- libstagefright_omx libstagefright_foundation libutils \
+ libstagefright_omx libstagefright_foundation libutils liblog \
libstagefright_amrnb_common
LOCAL_MODULE := libstagefright_soft_amrdec
diff --git a/media/libstagefright/codecs/amrnb/enc/Android.mk b/media/libstagefright/codecs/amrnb/enc/Android.mk
index 457656a..f4e467a 100644
--- a/media/libstagefright/codecs/amrnb/enc/Android.mk
+++ b/media/libstagefright/codecs/amrnb/enc/Android.mk
@@ -92,7 +92,7 @@
libstagefright_amrnbenc
LOCAL_SHARED_LIBRARIES := \
- libstagefright_omx libstagefright_foundation libutils \
+ libstagefright_omx libstagefright_foundation libutils liblog \
libstagefright_amrnb_common
LOCAL_MODULE := libstagefright_soft_amrnbenc
diff --git a/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp
index 07f8b4f..50b739c 100644
--- a/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp
@@ -257,7 +257,7 @@
}
if (pcmParams->nChannels != 1
- || pcmParams->nSamplingRate != kSampleRate) {
+ || pcmParams->nSamplingRate != (OMX_U32)kSampleRate) {
return OMX_ErrorUndefined;
}
diff --git a/media/libstagefright/codecs/amrwbenc/Android.mk b/media/libstagefright/codecs/amrwbenc/Android.mk
index edfd7b7..c5b8e0c 100644
--- a/media/libstagefright/codecs/amrwbenc/Android.mk
+++ b/media/libstagefright/codecs/amrwbenc/Android.mk
@@ -130,7 +130,7 @@
libstagefright_amrwbenc
LOCAL_SHARED_LIBRARIES := \
- libstagefright_omx libstagefright_foundation libutils \
+ libstagefright_omx libstagefright_foundation libutils liblog \
libstagefright_enc_common
LOCAL_MODULE := libstagefright_soft_amrwbenc
diff --git a/media/libstagefright/codecs/avc/enc/Android.mk b/media/libstagefright/codecs/avc/enc/Android.mk
index cffe469..7d17c2a 100644
--- a/media/libstagefright/codecs/avc/enc/Android.mk
+++ b/media/libstagefright/codecs/avc/enc/Android.mk
@@ -62,6 +62,7 @@
libstagefright_foundation \
libstagefright_omx \
libutils \
+ liblog \
libui
diff --git a/media/libstagefright/codecs/flac/enc/Android.mk b/media/libstagefright/codecs/flac/enc/Android.mk
index 546a357..f01d605 100644
--- a/media/libstagefright/codecs/flac/enc/Android.mk
+++ b/media/libstagefright/codecs/flac/enc/Android.mk
@@ -10,7 +10,7 @@
external/flac/include
LOCAL_SHARED_LIBRARIES := \
- libstagefright libstagefright_omx libstagefright_foundation libutils
+ libstagefright libstagefright_omx libstagefright_foundation libutils liblog
LOCAL_STATIC_LIBRARIES := \
libFLAC \
diff --git a/media/libstagefright/codecs/g711/dec/Android.mk b/media/libstagefright/codecs/g711/dec/Android.mk
index 28be646..4c80da6 100644
--- a/media/libstagefright/codecs/g711/dec/Android.mk
+++ b/media/libstagefright/codecs/g711/dec/Android.mk
@@ -9,7 +9,7 @@
frameworks/native/include/media/openmax
LOCAL_SHARED_LIBRARIES := \
- libstagefright libstagefright_omx libstagefright_foundation libutils
+ libstagefright libstagefright_omx libstagefright_foundation libutils liblog
LOCAL_MODULE := libstagefright_soft_g711dec
LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/codecs/gsm/dec/Android.mk b/media/libstagefright/codecs/gsm/dec/Android.mk
index 9c0c6ae..71613d2 100644
--- a/media/libstagefright/codecs/gsm/dec/Android.mk
+++ b/media/libstagefright/codecs/gsm/dec/Android.mk
@@ -10,7 +10,7 @@
external/libgsm/inc
LOCAL_SHARED_LIBRARIES := \
- libstagefright libstagefright_omx libstagefright_foundation libutils
+ libstagefright libstagefright_omx libstagefright_foundation libutils liblog
LOCAL_STATIC_LIBRARIES := \
libgsm
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.mk b/media/libstagefright/codecs/m4v_h263/dec/Android.mk
index a6b1edc..a3d5779 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/Android.mk
+++ b/media/libstagefright/codecs/m4v_h263/dec/Android.mk
@@ -67,7 +67,7 @@
libstagefright_m4vh263dec
LOCAL_SHARED_LIBRARIES := \
- libstagefright libstagefright_omx libstagefright_foundation libutils
+ libstagefright libstagefright_omx libstagefright_foundation libutils liblog
LOCAL_MODULE := libstagefright_soft_mpeg4dec
LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.mk b/media/libstagefright/codecs/m4v_h263/enc/Android.mk
index 865cc9c..83a2dd2 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/Android.mk
+++ b/media/libstagefright/codecs/m4v_h263/enc/Android.mk
@@ -65,6 +65,7 @@
libstagefright_foundation \
libstagefright_omx \
libutils \
+ liblog \
libui
diff --git a/media/libstagefright/codecs/mp3dec/Android.mk b/media/libstagefright/codecs/mp3dec/Android.mk
index ec8d7ec..135c715 100644
--- a/media/libstagefright/codecs/mp3dec/Android.mk
+++ b/media/libstagefright/codecs/mp3dec/Android.mk
@@ -70,7 +70,7 @@
$(LOCAL_PATH)/include
LOCAL_SHARED_LIBRARIES := \
- libstagefright libstagefright_omx libstagefright_foundation libutils
+ libstagefright libstagefright_omx libstagefright_foundation libutils liblog
LOCAL_STATIC_LIBRARIES := \
libstagefright_mp3dec
diff --git a/media/libstagefright/codecs/on2/dec/Android.mk b/media/libstagefright/codecs/on2/dec/Android.mk
index 0082d7c..7f2c46d 100644
--- a/media/libstagefright/codecs/on2/dec/Android.mk
+++ b/media/libstagefright/codecs/on2/dec/Android.mk
@@ -15,7 +15,7 @@
libvpx
LOCAL_SHARED_LIBRARIES := \
- libstagefright libstagefright_omx libstagefright_foundation libutils
+ libstagefright libstagefright_omx libstagefright_foundation libutils liblog
LOCAL_MODULE := libstagefright_soft_vpxdec
LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/codecs/on2/enc/Android.mk b/media/libstagefright/codecs/on2/enc/Android.mk
index 5d3317c..a92d376 100644
--- a/media/libstagefright/codecs/on2/enc/Android.mk
+++ b/media/libstagefright/codecs/on2/enc/Android.mk
@@ -16,7 +16,7 @@
libvpx
LOCAL_SHARED_LIBRARIES := \
- libstagefright libstagefright_omx libstagefright_foundation libutils \
+ libstagefright libstagefright_omx libstagefright_foundation libutils liblog \
LOCAL_MODULE := libstagefright_soft_vpxenc
LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
index cc38dc3..e25637a 100644
--- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
+++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
@@ -655,7 +655,8 @@
vpx_codec_iter_t encoded_packet_iterator = NULL;
const vpx_codec_cx_pkt_t* encoded_packet;
- while (encoded_packet = vpx_codec_get_cx_data(mCodecContext, &encoded_packet_iterator)) {
+ while ((encoded_packet = vpx_codec_get_cx_data(
+ mCodecContext, &encoded_packet_iterator))) {
if (encoded_packet->kind == VPX_CODEC_CX_FRAME_PKT) {
outputBufferHeader->nTimeStamp = encoded_packet->data.frame.pts;
outputBufferHeader->nFlags = 0;
diff --git a/media/libstagefright/codecs/on2/h264dec/Android.mk b/media/libstagefright/codecs/on2/h264dec/Android.mk
index 772fd60..2539f98 100644
--- a/media/libstagefright/codecs/on2/h264dec/Android.mk
+++ b/media/libstagefright/codecs/on2/h264dec/Android.mk
@@ -97,7 +97,7 @@
endif
LOCAL_SHARED_LIBRARIES := \
- libstagefright libstagefright_omx libstagefright_foundation libutils \
+ libstagefright libstagefright_omx libstagefright_foundation libutils liblog \
LOCAL_MODULE := libstagefright_soft_h264dec
@@ -124,4 +124,3 @@
LOCAL_MODULE := decoder
include $(BUILD_EXECUTABLE)
-
diff --git a/media/libstagefright/codecs/raw/Android.mk b/media/libstagefright/codecs/raw/Android.mk
index 285c747..fe90a03 100644
--- a/media/libstagefright/codecs/raw/Android.mk
+++ b/media/libstagefright/codecs/raw/Android.mk
@@ -9,7 +9,7 @@
frameworks/native/include/media/openmax
LOCAL_SHARED_LIBRARIES := \
- libstagefright_omx libstagefright_foundation libutils
+ libstagefright_omx libstagefright_foundation libutils liblog
LOCAL_MODULE := libstagefright_soft_rawdec
LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/codecs/vorbis/dec/Android.mk b/media/libstagefright/codecs/vorbis/dec/Android.mk
index 395dd6b..2232353 100644
--- a/media/libstagefright/codecs/vorbis/dec/Android.mk
+++ b/media/libstagefright/codecs/vorbis/dec/Android.mk
@@ -11,10 +11,9 @@
LOCAL_SHARED_LIBRARIES := \
libvorbisidec libstagefright libstagefright_omx \
- libstagefright_foundation libutils
+ libstagefright_foundation libutils liblog
LOCAL_MODULE := libstagefright_soft_vorbisdec
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)
-
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
index b7577d6..d65e213 100644
--- a/media/libstagefright/foundation/Android.mk
+++ b/media/libstagefright/foundation/Android.mk
@@ -20,6 +20,7 @@
LOCAL_SHARED_LIBRARIES := \
libbinder \
libutils \
+ liblog
LOCAL_CFLAGS += -Wno-multichar
diff --git a/media/libstagefright/id3/Android.mk b/media/libstagefright/id3/Android.mk
index ff35d4a..80a1a3a 100644
--- a/media/libstagefright/id3/Android.mk
+++ b/media/libstagefright/id3/Android.mk
@@ -16,7 +16,7 @@
testid3.cpp
LOCAL_SHARED_LIBRARIES := \
- libstagefright libutils libbinder libstagefright_foundation
+ libstagefright libutils liblog libbinder libstagefright_foundation
LOCAL_STATIC_LIBRARIES := \
libstagefright_id3
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index c68623a..35eff96 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -59,6 +59,11 @@
private:
+ struct PsshInfo {
+ uint8_t uuid[16];
+ uint32_t datalen;
+ uint8_t *data;
+ };
struct Track {
Track *next;
sp<MetaData> meta;
@@ -72,6 +77,8 @@
uint64_t mSidxDuration;
off64_t mMoofOffset;
+ Vector<PsshInfo> mPssh;
+
sp<DataSource> mDataSource;
status_t mInitCheck;
bool mHasVideo;
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 9499712..9f3b19c 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -147,9 +147,9 @@
}
if (startOffset > 0) {
- ALOGI("found something resembling an H.264/MPEG syncword at "
- "offset %ld",
- startOffset);
+ ALOGI("found something resembling an H.264/MPEG syncword "
+ "at offset %d",
+ startOffset);
}
data = &ptr[startOffset];
@@ -180,9 +180,9 @@
}
if (startOffset > 0) {
- ALOGI("found something resembling an H.264/MPEG syncword at "
- "offset %ld",
- startOffset);
+ ALOGI("found something resembling an H.264/MPEG syncword "
+ "at offset %d",
+ startOffset);
}
data = &ptr[startOffset];
@@ -213,8 +213,9 @@
}
if (startOffset > 0) {
- ALOGI("found something resembling an AAC syncword at offset %ld",
- startOffset);
+ ALOGI("found something resembling an AAC syncword at "
+ "offset %d",
+ startOffset);
}
data = &ptr[startOffset];
@@ -241,8 +242,8 @@
if (startOffset > 0) {
ALOGI("found something resembling an MPEG audio "
- "syncword at offset %ld",
- startOffset);
+ "syncword at offset %d",
+ startOffset);
}
data = &ptr[startOffset];
@@ -394,10 +395,30 @@
}
sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAAC() {
- int64_t timeUs;
+ if (mBuffer->size() == 0) {
+ return NULL;
+ }
+ CHECK(!mRangeInfos.empty());
+
+ const RangeInfo &info = *mRangeInfos.begin();
+ if (mBuffer->size() < info.mLength) {
+ return NULL;
+ }
+
+ CHECK_GE(info.mTimestampUs, 0ll);
+
+ // The idea here is consume all AAC frames starting at offsets before
+ // info.mLength so we can assign a meaningful timestamp without
+ // having to interpolate.
+ // The final AAC frame may well extend into the next RangeInfo but
+ // that's ok.
size_t offset = 0;
- while (offset + 7 <= mBuffer->size()) {
+ while (offset < info.mLength) {
+ if (offset + 7 > mBuffer->size()) {
+ return NULL;
+ }
+
ABitReader bits(mBuffer->data() + offset, mBuffer->size() - offset);
// adts_fixed_header
@@ -450,24 +471,15 @@
}
if (offset + aac_frame_length > mBuffer->size()) {
- break;
+ return NULL;
}
size_t headerSize = protection_absent ? 7 : 9;
- int64_t tmpUs = fetchTimestamp(aac_frame_length);
- CHECK_GE(tmpUs, 0ll);
-
- if (offset == 0) {
- timeUs = tmpUs;
- }
-
offset += aac_frame_length;
}
- if (offset == 0) {
- return NULL;
- }
+ int64_t timeUs = fetchTimestamp(offset);
sp<ABuffer> accessUnit = new ABuffer(offset);
memcpy(accessUnit->data(), mBuffer->data(), offset);
@@ -492,7 +504,6 @@
if (first) {
timeUs = info->mTimestampUs;
- first = false;
}
if (info->mLength > size) {
@@ -509,6 +520,8 @@
mRangeInfos.erase(mRangeInfos.begin());
info = NULL;
}
+
+ first = false;
}
if (timeUs == 0ll) {
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index 9129f08..a8b4939 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -19,6 +19,7 @@
libbinder \
libmedia \
libutils \
+ liblog \
libui \
libgui \
libcutils \
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 46ff22f..971875f 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -292,15 +292,14 @@
status_t OMXNodeInstance::enableGraphicBuffers(
OMX_U32 portIndex, OMX_BOOL enable) {
Mutex::Autolock autoLock(mLock);
+ OMX_STRING name = const_cast<OMX_STRING>(
+ "OMX.google.android.index.enableAndroidNativeBuffers");
OMX_INDEXTYPE index;
- OMX_ERRORTYPE err = OMX_GetExtensionIndex(
- mHandle,
- const_cast<OMX_STRING>("OMX.google.android.index.enableAndroidNativeBuffers"),
- &index);
+ OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);
if (err != OMX_ErrorNone) {
- ALOGE("OMX_GetExtensionIndex failed");
+ ALOGE("OMX_GetExtensionIndex %s failed", name);
return StatusFromOMXError(err);
}
@@ -331,14 +330,12 @@
Mutex::Autolock autoLock(mLock);
OMX_INDEXTYPE index;
- OMX_ERRORTYPE err = OMX_GetExtensionIndex(
- mHandle,
- const_cast<OMX_STRING>(
- "OMX.google.android.index.getAndroidNativeBufferUsage"),
- &index);
+ OMX_STRING name = const_cast<OMX_STRING>(
+ "OMX.google.android.index.getAndroidNativeBufferUsage");
+ OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);
if (err != OMX_ErrorNone) {
- ALOGE("OMX_GetExtensionIndex failed");
+ ALOGE("OMX_GetExtensionIndex %s failed", name);
return StatusFromOMXError(err);
}
@@ -381,7 +378,9 @@
OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);
if (err != OMX_ErrorNone) {
- ALOGE("OMX_GetExtensionIndex %s failed", name);
+ if (enable) {
+ ALOGE("OMX_GetExtensionIndex %s failed", name);
+ }
return StatusFromOMXError(err);
}
@@ -507,13 +506,12 @@
return useGraphicBuffer2_l(portIndex, graphicBuffer, buffer);
}
- OMX_ERRORTYPE err = OMX_GetExtensionIndex(
- mHandle,
- const_cast<OMX_STRING>("OMX.google.android.index.useAndroidNativeBuffer"),
- &index);
+ OMX_STRING name = const_cast<OMX_STRING>(
+ "OMX.google.android.index.useAndroidNativeBuffer");
+ OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);
if (err != OMX_ErrorNone) {
- ALOGE("OMX_GetExtensionIndex failed");
+ ALOGE("OMX_GetExtensionIndex %s failed", name);
return StatusFromOMXError(err);
}
diff --git a/media/libstagefright/omx/tests/Android.mk b/media/libstagefright/omx/tests/Android.mk
index 04441ca..1061c39 100644
--- a/media/libstagefright/omx/tests/Android.mk
+++ b/media/libstagefright/omx/tests/Android.mk
@@ -5,7 +5,7 @@
OMXHarness.cpp \
LOCAL_SHARED_LIBRARIES := \
- libstagefright libbinder libmedia libutils libstagefright_foundation
+ libstagefright libbinder libmedia libutils liblog libstagefright_foundation
LOCAL_C_INCLUDES := \
$(TOP)/frameworks/av/media/libstagefright \
diff --git a/media/libstagefright/tests/Android.mk b/media/libstagefright/tests/Android.mk
index 57fff0b..06ce16b 100644
--- a/media/libstagefright/tests/Android.mk
+++ b/media/libstagefright/tests/Android.mk
@@ -26,6 +26,7 @@
libsync \
libui \
libutils \
+ liblog
LOCAL_STATIC_LIBRARIES := \
libgtest \
diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp
index 88ca1cc..f074438 100644
--- a/media/libstagefright/wifi-display/ANetworkSession.cpp
+++ b/media/libstagefright/wifi-display/ANetworkSession.cpp
@@ -451,24 +451,6 @@
const Fragment &frag = *mOutFragments.begin();
const sp<ABuffer> &datagram = frag.mBuffer;
- uint8_t *data = datagram->data();
- if (data[0] == 0x80 && (data[1] & 0x7f) == 33) {
- int64_t nowUs = ALooper::GetNowUs();
-
- uint32_t prevRtpTime = U32_AT(&data[4]);
-
- // 90kHz time scale
- uint32_t rtpTime = (nowUs * 9ll) / 100ll;
- int32_t diffTime = (int32_t)rtpTime - (int32_t)prevRtpTime;
-
- ALOGV("correcting rtpTime by %.0f ms", diffTime / 90.0);
-
- data[4] = rtpTime >> 24;
- data[5] = (rtpTime >> 16) & 0xff;
- data[6] = (rtpTime >> 8) & 0xff;
- data[7] = rtpTime & 0xff;
- }
-
int n;
do {
n = send(mSocket, datagram->data(), datagram->size(), 0);
@@ -874,6 +856,14 @@
err = -errno;
goto bail2;
}
+
+ int tos = 224; // VOICE
+ res = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
+
+ if (res < 0) {
+ err = -errno;
+ goto bail2;
+ }
}
err = MakeSocketNonBlocking(s);
diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk
index f1f9f45..f99ef60 100644
--- a/media/libstagefright/wifi-display/Android.mk
+++ b/media/libstagefright/wifi-display/Android.mk
@@ -31,6 +31,7 @@
LOCAL_SHARED_LIBRARIES:= \
libbinder \
libcutils \
+ liblog \
libgui \
libmedia \
libstagefright \
@@ -59,6 +60,7 @@
libstagefright_foundation \
libstagefright_wfd \
libutils \
+ liblog \
LOCAL_MODULE:= wfd
@@ -81,6 +83,7 @@
libstagefright_foundation \
libstagefright_wfd \
libutils \
+ liblog \
LOCAL_MODULE:= udptest
@@ -103,9 +106,33 @@
libstagefright_foundation \
libstagefright_wfd \
libutils \
+ liblog \
LOCAL_MODULE:= rtptest
LOCAL_MODULE_TAGS := debug
include $(BUILD_EXECUTABLE)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ nettest.cpp \
+
+LOCAL_SHARED_LIBRARIES:= \
+ libbinder \
+ libgui \
+ libmedia \
+ libstagefright \
+ libstagefright_foundation \
+ libstagefright_wfd \
+ libutils \
+ liblog \
+
+LOCAL_MODULE:= nettest
+
+LOCAL_MODULE_TAGS := debug
+
+include $(BUILD_EXECUTABLE)
diff --git a/media/libstagefright/wifi-display/TimeSyncer.cpp b/media/libstagefright/wifi-display/TimeSyncer.cpp
index 64e182e..cb429bc 100644
--- a/media/libstagefright/wifi-display/TimeSyncer.cpp
+++ b/media/libstagefright/wifi-display/TimeSyncer.cpp
@@ -102,6 +102,10 @@
case kWhatSendPacket:
{
+ if (mHistory.size() == 0) {
+ ALOGI("starting batch");
+ }
+
TimeInfo ti;
memset(&ti, 0, sizeof(ti));
@@ -229,6 +233,8 @@
} else {
notifyOffset();
+ ALOGI("batch done");
+
mHistory.clear();
postSendPacket(kBatchDelayUs);
}
diff --git a/media/libstagefright/wifi-display/TimeSyncer.h b/media/libstagefright/wifi-display/TimeSyncer.h
index 0e3aed7..4e7571f 100644
--- a/media/libstagefright/wifi-display/TimeSyncer.h
+++ b/media/libstagefright/wifi-display/TimeSyncer.h
@@ -75,7 +75,7 @@
kNumPacketsPerBatch = 30,
};
static const int64_t kTimeoutDelayUs = 500000ll;
- static const int64_t kBatchDelayUs = 10000000ll; // every 10 secs
+ static const int64_t kBatchDelayUs = 60000000ll; // every minute
sp<ANetworkSession> mNetSession;
sp<AMessage> mNotify;
diff --git a/media/libstagefright/wifi-display/nettest.cpp b/media/libstagefright/wifi-display/nettest.cpp
new file mode 100644
index 0000000..0779bf5
--- /dev/null
+++ b/media/libstagefright/wifi-display/nettest.cpp
@@ -0,0 +1,400 @@
+/*
+ * Copyright 2013, 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_NEBUG 0
+#define LOG_TAG "nettest"
+#include <utils/Log.h>
+
+#include "ANetworkSession.h"
+#include "TimeSyncer.h"
+
+#include <binder/ProcessState.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/NuMediaExtractor.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+struct TestHandler : public AHandler {
+ TestHandler(const sp<ANetworkSession> &netSession);
+
+ void listen(int32_t port);
+ void connect(const char *host, int32_t port);
+
+protected:
+ virtual ~TestHandler();
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+ enum {
+ kTimeSyncerPort = 8123,
+ };
+
+ enum {
+ kWhatListen,
+ kWhatConnect,
+ kWhatTimeSyncerNotify,
+ kWhatNetNotify,
+ kWhatSendMore,
+ kWhatStop,
+ };
+
+ sp<ANetworkSession> mNetSession;
+ sp<TimeSyncer> mTimeSyncer;
+
+ int32_t mServerSessionID;
+ int32_t mSessionID;
+
+ int64_t mTimeOffsetUs;
+ bool mTimeOffsetValid;
+
+ int32_t mCounter;
+
+ int64_t mMaxDelayMs;
+
+ void dumpDelay(int32_t counter, int64_t delayMs);
+
+ DISALLOW_EVIL_CONSTRUCTORS(TestHandler);
+};
+
+TestHandler::TestHandler(const sp<ANetworkSession> &netSession)
+ : mNetSession(netSession),
+ mServerSessionID(0),
+ mSessionID(0),
+ mTimeOffsetUs(-1ll),
+ mTimeOffsetValid(false),
+ mCounter(0),
+ mMaxDelayMs(-1ll) {
+}
+
+TestHandler::~TestHandler() {
+}
+
+void TestHandler::listen(int32_t port) {
+ sp<AMessage> msg = new AMessage(kWhatListen, id());
+ msg->setInt32("port", port);
+ msg->post();
+}
+
+void TestHandler::connect(const char *host, int32_t port) {
+ sp<AMessage> msg = new AMessage(kWhatConnect, id());
+ msg->setString("host", host);
+ msg->setInt32("port", port);
+ msg->post();
+}
+
+void TestHandler::dumpDelay(int32_t counter, int64_t delayMs) {
+ static const int64_t kMinDelayMs = 0;
+ static const int64_t kMaxDelayMs = 300;
+
+ const char *kPattern = "########################################";
+ size_t kPatternSize = strlen(kPattern);
+
+ int n = (kPatternSize * (delayMs - kMinDelayMs))
+ / (kMaxDelayMs - kMinDelayMs);
+
+ if (n < 0) {
+ n = 0;
+ } else if ((size_t)n > kPatternSize) {
+ n = kPatternSize;
+ }
+
+ if (delayMs > mMaxDelayMs) {
+ mMaxDelayMs = delayMs;
+ }
+
+ ALOGI("[%d] (%4lld ms / %4lld ms) %s",
+ counter,
+ delayMs,
+ mMaxDelayMs,
+ kPattern + kPatternSize - n);
+}
+
+void TestHandler::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatListen:
+ {
+ sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id());
+ mTimeSyncer = new TimeSyncer(mNetSession, notify);
+ looper()->registerHandler(mTimeSyncer);
+
+ notify = new AMessage(kWhatNetNotify, id());
+
+ int32_t port;
+ CHECK(msg->findInt32("port", &port));
+
+ struct in_addr ifaceAddr;
+ ifaceAddr.s_addr = INADDR_ANY;
+
+ CHECK_EQ((status_t)OK,
+ mNetSession->createTCPDatagramSession(
+ ifaceAddr,
+ port,
+ notify,
+ &mServerSessionID));
+ break;
+ }
+
+ case kWhatConnect:
+ {
+ sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id());
+ mTimeSyncer = new TimeSyncer(mNetSession, notify);
+ looper()->registerHandler(mTimeSyncer);
+ mTimeSyncer->startServer(kTimeSyncerPort);
+
+ AString host;
+ CHECK(msg->findString("host", &host));
+
+ int32_t port;
+ CHECK(msg->findInt32("port", &port));
+
+ notify = new AMessage(kWhatNetNotify, id());
+
+ CHECK_EQ((status_t)OK,
+ mNetSession->createTCPDatagramSession(
+ 0 /* localPort */,
+ host.c_str(),
+ port,
+ notify,
+ &mSessionID));
+ break;
+ }
+
+ case kWhatNetNotify:
+ {
+ int32_t reason;
+ CHECK(msg->findInt32("reason", &reason));
+
+ switch (reason) {
+ case ANetworkSession::kWhatConnected:
+ {
+ ALOGI("kWhatConnected");
+
+ (new AMessage(kWhatSendMore, id()))->post();
+ break;
+ }
+
+ case ANetworkSession::kWhatClientConnected:
+ {
+ ALOGI("kWhatClientConnected");
+
+ CHECK_EQ(mSessionID, 0);
+ CHECK(msg->findInt32("sessionID", &mSessionID));
+
+ AString clientIP;
+ CHECK(msg->findString("client-ip", &clientIP));
+
+ mTimeSyncer->startClient(clientIP.c_str(), kTimeSyncerPort);
+ break;
+ }
+
+ case ANetworkSession::kWhatDatagram:
+ {
+ sp<ABuffer> packet;
+ CHECK(msg->findBuffer("data", &packet));
+
+ CHECK_EQ(packet->size(), 12u);
+
+ int32_t counter = U32_AT(packet->data());
+ int64_t timeUs = U64_AT(packet->data() + 4);
+
+ if (mTimeOffsetValid) {
+ timeUs -= mTimeOffsetUs;
+ int64_t nowUs = ALooper::GetNowUs();
+ int64_t delayMs = (nowUs - timeUs) / 1000ll;
+
+ dumpDelay(counter, delayMs);
+ } else {
+ ALOGI("received %d", counter);
+ }
+ break;
+ }
+
+ case ANetworkSession::kWhatError:
+ {
+ ALOGE("kWhatError");
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+ break;
+ }
+
+ case kWhatTimeSyncerNotify:
+ {
+ CHECK(msg->findInt64("offset", &mTimeOffsetUs));
+ mTimeOffsetValid = true;
+ break;
+ }
+
+ case kWhatSendMore:
+ {
+ uint8_t buffer[4 + 8];
+ buffer[0] = mCounter >> 24;
+ buffer[1] = (mCounter >> 16) & 0xff;
+ buffer[2] = (mCounter >> 8) & 0xff;
+ buffer[3] = mCounter & 0xff;
+
+ int64_t nowUs = ALooper::GetNowUs();
+
+ buffer[4] = nowUs >> 56;
+ buffer[5] = (nowUs >> 48) & 0xff;
+ buffer[6] = (nowUs >> 40) & 0xff;
+ buffer[7] = (nowUs >> 32) & 0xff;
+ buffer[8] = (nowUs >> 24) & 0xff;
+ buffer[9] = (nowUs >> 16) & 0xff;
+ buffer[10] = (nowUs >> 8) & 0xff;
+ buffer[11] = nowUs & 0xff;
+
+ ++mCounter;
+
+ CHECK_EQ((status_t)OK,
+ mNetSession->sendRequest(
+ mSessionID,
+ buffer,
+ sizeof(buffer),
+ true /* timeValid */,
+ nowUs));
+
+ msg->post(100000ll);
+ break;
+ }
+
+ case kWhatStop:
+ {
+ if (mSessionID != 0) {
+ mNetSession->destroySession(mSessionID);
+ mSessionID = 0;
+ }
+
+ if (mServerSessionID != 0) {
+ mNetSession->destroySession(mServerSessionID);
+ mServerSessionID = 0;
+ }
+
+ looper()->stop();
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+}
+
+} // namespace android
+
+static void usage(const char *me) {
+ fprintf(stderr,
+ "usage: %s -c host:port\tconnect to remote host\n"
+ " -l port \tlisten\n",
+ me);
+}
+
+int main(int argc, char **argv) {
+ using namespace android;
+
+ // srand(time(NULL));
+
+ ProcessState::self()->startThreadPool();
+
+ DataSource::RegisterDefaultSniffers();
+
+ int32_t connectToPort = -1;
+ AString connectToHost;
+
+ int32_t listenOnPort = -1;
+
+ int res;
+ while ((res = getopt(argc, argv, "hc:l:")) >= 0) {
+ switch (res) {
+ case 'c':
+ {
+ const char *colonPos = strrchr(optarg, ':');
+
+ if (colonPos == NULL) {
+ usage(argv[0]);
+ exit(1);
+ }
+
+ connectToHost.setTo(optarg, colonPos - optarg);
+
+ char *end;
+ connectToPort = strtol(colonPos + 1, &end, 10);
+
+ if (*end != '\0' || end == colonPos + 1
+ || connectToPort < 0 || connectToPort > 65535) {
+ fprintf(stderr, "Illegal port specified.\n");
+ exit(1);
+ }
+ break;
+ }
+
+ case 'l':
+ {
+ char *end;
+ listenOnPort = strtol(optarg, &end, 10);
+
+ if (*end != '\0' || end == optarg
+ || listenOnPort < 0 || listenOnPort > 65535) {
+ fprintf(stderr, "Illegal port specified.\n");
+ exit(1);
+ }
+ break;
+ }
+
+ case '?':
+ case 'h':
+ usage(argv[0]);
+ exit(1);
+ }
+ }
+
+ if ((listenOnPort < 0 && connectToPort < 0)
+ || (listenOnPort >= 0 && connectToPort >= 0)) {
+ fprintf(stderr,
+ "You need to select either client or server mode.\n");
+ exit(1);
+ }
+
+ sp<ANetworkSession> netSession = new ANetworkSession;
+ netSession->start();
+
+ sp<ALooper> looper = new ALooper;
+
+ sp<TestHandler> handler = new TestHandler(netSession);
+ looper->registerHandler(handler);
+
+ if (listenOnPort) {
+ handler->listen(listenOnPort);
+ }
+
+ if (connectToPort >= 0) {
+ handler->connect(connectToHost.c_str(), connectToPort);
+ }
+
+ looper->start(true /* runOnCallingThread */);
+
+ return 0;
+}
diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp
index 5f189e7..7a96081 100644
--- a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp
+++ b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp
@@ -40,7 +40,6 @@
notify->setInt32("followsDiscontinuity", followsDiscontinuity);
notify->post();
}
-
////////////////////////////////////////////////////////////////////////////////
RTPReceiver::TSAssembler::TSAssembler(const sp<AMessage> ¬ify)
diff --git a/media/libstagefright/wifi-display/rtp/RTPBase.h b/media/libstagefright/wifi-display/rtp/RTPBase.h
index e3fa845..6178f00 100644
--- a/media/libstagefright/wifi-display/rtp/RTPBase.h
+++ b/media/libstagefright/wifi-display/rtp/RTPBase.h
@@ -25,6 +25,7 @@
PACKETIZATION_TRANSPORT_STREAM,
PACKETIZATION_H264,
PACKETIZATION_AAC,
+ PACKETIZATION_NONE,
};
enum TransportMode {
diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp
index 238fb82..8fa1dae 100644
--- a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp
+++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp
@@ -30,11 +30,13 @@
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/Utils.h>
+#define TRACK_PACKET_LOSS 0
+
namespace android {
////////////////////////////////////////////////////////////////////////////////
-struct RTPReceiver::Source : public RefBase {
+struct RTPReceiver::Source : public AHandler {
Source(RTPReceiver *receiver, uint32_t ssrc);
void onPacketReceived(uint16_t seq, const sp<ABuffer> &buffer);
@@ -44,7 +46,14 @@
protected:
virtual ~Source();
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
private:
+ enum {
+ kWhatRetransmit,
+ kWhatDeclareLost,
+ };
+
static const uint32_t kMinSequential = 2;
static const uint32_t kMaxDropout = 3000;
static const uint32_t kMaxMisorder = 100;
@@ -67,6 +76,17 @@
// Ordered by extended seq number.
List<sp<ABuffer> > mPackets;
+ enum StatusBits {
+ STATUS_DECLARED_LOST = 1,
+ STATUS_REQUESTED_RETRANSMISSION = 2,
+ STATUS_ARRIVED_LATE = 4,
+ };
+#if TRACK_PACKET_LOSS
+ KeyedVector<int32_t, uint32_t> mLostPackets;
+#endif
+
+ void modifyPacketStatus(int32_t extSeqNo, uint32_t mask);
+
int32_t mAwaitingExtSeqNo;
bool mRequestedRetransmission;
@@ -78,12 +98,20 @@
int32_t mNumDeclaredLost;
int32_t mNumDeclaredLostPrior;
+ int32_t mRetransmitGeneration;
+ int32_t mDeclareLostGeneration;
+ bool mDeclareLostTimerPending;
+
void queuePacket(const sp<ABuffer> &packet);
void dequeueMore();
sp<ABuffer> getNextPacket();
void resync();
+ void postRetransmitTimer(int64_t delayUs);
+ void postDeclareLostTimer(int64_t delayUs);
+ void cancelTimers();
+
DISALLOW_EVIL_CONSTRUCTORS(Source);
};
@@ -106,12 +134,71 @@
mActivePacketType(-1),
mNextReportTimeUs(-1ll),
mNumDeclaredLost(0),
- mNumDeclaredLostPrior(0) {
+ mNumDeclaredLostPrior(0),
+ mRetransmitGeneration(0),
+ mDeclareLostGeneration(0),
+ mDeclareLostTimerPending(false) {
}
RTPReceiver::Source::~Source() {
}
+void RTPReceiver::Source::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatRetransmit:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+
+ if (generation != mRetransmitGeneration) {
+ break;
+ }
+
+ mRequestedRetransmission = true;
+ mReceiver->requestRetransmission(mSSRC, mAwaitingExtSeqNo);
+
+ modifyPacketStatus(
+ mAwaitingExtSeqNo, STATUS_REQUESTED_RETRANSMISSION);
+ break;
+ }
+
+ case kWhatDeclareLost:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+
+ if (generation != mDeclareLostGeneration) {
+ break;
+ }
+
+ cancelTimers();
+
+ ALOGV("Lost packet extSeqNo %d %s",
+ mAwaitingExtSeqNo,
+ mRequestedRetransmission ? "*" : "");
+
+ mRequestedRetransmission = false;
+ if (mActiveAssembler != NULL) {
+ mActiveAssembler->signalDiscontinuity();
+ }
+
+ modifyPacketStatus(mAwaitingExtSeqNo, STATUS_DECLARED_LOST);
+
+ // resync();
+ ++mAwaitingExtSeqNo;
+ ++mNumDeclaredLost;
+
+ mReceiver->notifyPacketLost();
+
+ dequeueMore();
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+}
+
void RTPReceiver::Source::onPacketReceived(
uint16_t seq, const sp<ABuffer> &buffer) {
if (mFirst) {
@@ -164,6 +251,8 @@
if (mAwaitingExtSeqNo >= 0 && newExtendedSeqNo < mAwaitingExtSeqNo) {
// We're no longer interested in these. They're old.
ALOGV("dropping stale extSeqNo %d", newExtendedSeqNo);
+
+ modifyPacketStatus(newExtendedSeqNo, STATUS_ARRIVED_LATE);
return;
}
@@ -230,68 +319,34 @@
}
mNextReportTimeUs = nowUs + kReportIntervalUs;
+
+#if TRACK_PACKET_LOSS
+ for (size_t i = 0; i < mLostPackets.size(); ++i) {
+ int32_t key = mLostPackets.keyAt(i);
+ uint32_t value = mLostPackets.valueAt(i);
+
+ AString status;
+ if (value & STATUS_REQUESTED_RETRANSMISSION) {
+ status.append("retrans ");
+ }
+ if (value & STATUS_ARRIVED_LATE) {
+ status.append("arrived-late ");
+ }
+ ALOGI("Packet %d declared lost %s", key, status.c_str());
+ }
+#endif
}
- for (;;) {
- sp<ABuffer> packet = getNextPacket();
-
- if (packet == NULL) {
- if (mPackets.empty()) {
- break;
- }
-
- CHECK_GE(mAwaitingExtSeqNo, 0);
-
- const sp<ABuffer> &firstPacket = *mPackets.begin();
-
- uint32_t rtpTime;
- CHECK(firstPacket->meta()->findInt32(
- "rtp-time", (int32_t *)&rtpTime));
-
-
- int64_t rtpUs = (rtpTime * 100ll) / 9ll;
-
- int64_t maxArrivalTimeUs =
- mFirstArrivalTimeUs + rtpUs - mFirstRTPTimeUs;
-
- int64_t nowUs = ALooper::GetNowUs();
-
- CHECK_LT(mAwaitingExtSeqNo, firstPacket->int32Data());
-
- ALOGV("waiting for %d, comparing against %d, %lld us left",
- mAwaitingExtSeqNo,
- firstPacket->int32Data(),
- maxArrivalTimeUs - nowUs);
-
- if (maxArrivalTimeUs + kPacketLostAfterUs <= nowUs) {
- ALOGV("Lost packet extSeqNo %d %s",
- mAwaitingExtSeqNo,
- mRequestedRetransmission ? "*" : "");
-
- mRequestedRetransmission = false;
- if (mActiveAssembler != NULL) {
- mActiveAssembler->signalDiscontinuity();
- }
-
- // resync();
- ++mAwaitingExtSeqNo;
- ++mNumDeclaredLost;
-
- mReceiver->notifyPacketLost();
- continue;
- } else if (kRequestRetransmissionAfterUs > 0
- && maxArrivalTimeUs + kRequestRetransmissionAfterUs <= nowUs
- && !mRequestedRetransmission
- && mAwaitingExtSeqNo >= 0) {
- mRequestedRetransmission = true;
- mReceiver->requestRetransmission(mSSRC, mAwaitingExtSeqNo);
- break;
- } else {
- break;
- }
+ sp<ABuffer> packet;
+ while ((packet = getNextPacket()) != NULL) {
+ if (mDeclareLostTimerPending) {
+ cancelTimers();
}
- mRequestedRetransmission = false;
+ CHECK_GE(mAwaitingExtSeqNo, 0);
+#if TRACK_PACKET_LOSS
+ mLostPackets.removeItem(mAwaitingExtSeqNo);
+#endif
int32_t packetType;
CHECK(packet->meta()->findInt32("PT", &packetType));
@@ -301,14 +356,52 @@
mActivePacketType = packetType;
}
- if (mActiveAssembler == NULL) {
- continue;
+ if (mActiveAssembler != NULL) {
+ status_t err = mActiveAssembler->processPacket(packet);
+ if (err != OK) {
+ ALOGV("assembler returned error %d", err);
+ }
}
- status_t err = mActiveAssembler->processPacket(packet);
- if (err != OK) {
- ALOGV("assembler returned error %d", err);
- }
+ ++mAwaitingExtSeqNo;
+ }
+
+ if (mDeclareLostTimerPending) {
+ return;
+ }
+
+ if (mPackets.empty()) {
+ return;
+ }
+
+ CHECK_GE(mAwaitingExtSeqNo, 0);
+
+ const sp<ABuffer> &firstPacket = *mPackets.begin();
+
+ uint32_t rtpTime;
+ CHECK(firstPacket->meta()->findInt32(
+ "rtp-time", (int32_t *)&rtpTime));
+
+
+ int64_t rtpUs = (rtpTime * 100ll) / 9ll;
+
+ int64_t maxArrivalTimeUs =
+ mFirstArrivalTimeUs + rtpUs - mFirstRTPTimeUs;
+
+ nowUs = ALooper::GetNowUs();
+
+ CHECK_LT(mAwaitingExtSeqNo, firstPacket->int32Data());
+
+ ALOGV("waiting for %d, comparing against %d, %lld us left",
+ mAwaitingExtSeqNo,
+ firstPacket->int32Data(),
+ maxArrivalTimeUs - nowUs);
+
+ postDeclareLostTimer(maxArrivalTimeUs + kPacketLostAfterUs);
+
+ if (kRequestRetransmissionAfterUs > 0ll) {
+ postRetransmitTimer(
+ maxArrivalTimeUs + kRequestRetransmissionAfterUs);
}
}
@@ -328,8 +421,6 @@
sp<ABuffer> packet = *mPackets.begin();
mPackets.erase(mPackets.begin());
- ++mAwaitingExtSeqNo;
-
return packet;
}
@@ -404,9 +495,11 @@
RTPReceiver::RTPReceiver(
const sp<ANetworkSession> &netSession,
- const sp<AMessage> ¬ify)
+ const sp<AMessage> ¬ify,
+ uint32_t flags)
: mNetSession(netSession),
mNotify(notify),
+ mFlags(flags),
mRTPMode(TRANSPORT_UNDEFINED),
mRTCPMode(TRANSPORT_UNDEFINED),
mRTPSessionID(0),
@@ -693,6 +786,20 @@
CHECK(msg->findBuffer("data", &data));
if (isRTP) {
+ if (mFlags & FLAG_AUTO_CONNECT) {
+ AString fromAddr;
+ CHECK(msg->findString("fromAddr", &fromAddr));
+
+ int32_t fromPort;
+ CHECK(msg->findInt32("fromPort", &fromPort));
+
+ CHECK_EQ((status_t)OK,
+ connect(
+ fromAddr.c_str(), fromPort, fromPort + 1));
+
+ mFlags &= ~FLAG_AUTO_CONNECT;
+ }
+
onRTPData(data);
} else {
onRTCPData(data);
@@ -835,6 +942,8 @@
sp<Source> source;
if (index < 0) {
source = new Source(this, srcId);
+ looper()->registerHandler(source);
+
mSources.add(srcId, source);
} else {
source = mSources.valueAt(index);
@@ -965,6 +1074,7 @@
PacketizationMode mode = mPacketTypes.valueAt(index);
switch (mode) {
+ case PACKETIZATION_NONE:
case PACKETIZATION_TRANSPORT_STREAM:
return new TSAssembler(mNotify);
@@ -1005,5 +1115,39 @@
mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size());
}
+void RTPReceiver::Source::modifyPacketStatus(int32_t extSeqNo, uint32_t mask) {
+#if TRACK_PACKET_LOSS
+ ssize_t index = mLostPackets.indexOfKey(extSeqNo);
+ if (index < 0) {
+ mLostPackets.add(extSeqNo, mask);
+ } else {
+ mLostPackets.editValueAt(index) |= mask;
+ }
+#endif
+}
+
+void RTPReceiver::Source::postRetransmitTimer(int64_t timeUs) {
+ int64_t delayUs = timeUs - ALooper::GetNowUs();
+ sp<AMessage> msg = new AMessage(kWhatRetransmit, id());
+ msg->setInt32("generation", mRetransmitGeneration);
+ msg->post(delayUs);
+}
+
+void RTPReceiver::Source::postDeclareLostTimer(int64_t timeUs) {
+ CHECK(!mDeclareLostTimerPending);
+ mDeclareLostTimerPending = true;
+
+ int64_t delayUs = timeUs - ALooper::GetNowUs();
+ sp<AMessage> msg = new AMessage(kWhatDeclareLost, id());
+ msg->setInt32("generation", mDeclareLostGeneration);
+ msg->post(delayUs);
+}
+
+void RTPReceiver::Source::cancelTimers() {
+ ++mRetransmitGeneration;
+ ++mDeclareLostGeneration;
+ mDeclareLostTimerPending = false;
+}
+
} // namespace android
diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.h b/media/libstagefright/wifi-display/rtp/RTPReceiver.h
index 630bce9..240ab2e 100644
--- a/media/libstagefright/wifi-display/rtp/RTPReceiver.h
+++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.h
@@ -39,9 +39,14 @@
kWhatAccessUnit,
kWhatPacketLost,
};
+
+ enum Flags {
+ FLAG_AUTO_CONNECT = 1,
+ };
RTPReceiver(
const sp<ANetworkSession> &netSession,
- const sp<AMessage> ¬ify);
+ const sp<AMessage> ¬ify,
+ uint32_t flags = 0);
status_t registerPacketType(
uint8_t packetType, PacketizationMode mode);
@@ -82,6 +87,7 @@
sp<ANetworkSession> mNetSession;
sp<AMessage> mNotify;
+ uint32_t mFlags;
TransportMode mRTPMode;
TransportMode mRTCPMode;
int32_t mRTPSessionID;
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
index 9eeeabd..6bbe650 100644
--- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp
+++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
@@ -187,6 +187,10 @@
status_t err;
switch (mode) {
+ case PACKETIZATION_NONE:
+ err = queueRawPacket(buffer, packetType);
+ break;
+
case PACKETIZATION_TRANSPORT_STREAM:
err = queueTSPackets(buffer, packetType);
break;
@@ -202,6 +206,46 @@
return err;
}
+status_t RTPSender::queueRawPacket(
+ const sp<ABuffer> &packet, uint8_t packetType) {
+ CHECK_LE(packet->size(), kMaxUDPPacketSize - 12);
+
+ int64_t timeUs;
+ CHECK(packet->meta()->findInt64("timeUs", &timeUs));
+
+ sp<ABuffer> udpPacket = new ABuffer(12 + packet->size());
+
+ udpPacket->setInt32Data(mRTPSeqNo);
+
+ uint8_t *rtp = udpPacket->data();
+ rtp[0] = 0x80;
+ rtp[1] = packetType;
+
+ rtp[2] = (mRTPSeqNo >> 8) & 0xff;
+ rtp[3] = mRTPSeqNo & 0xff;
+ ++mRTPSeqNo;
+
+ uint32_t rtpTime = (timeUs * 9) / 100ll;
+
+ rtp[4] = rtpTime >> 24;
+ rtp[5] = (rtpTime >> 16) & 0xff;
+ rtp[6] = (rtpTime >> 8) & 0xff;
+ rtp[7] = rtpTime & 0xff;
+
+ rtp[8] = kSourceID >> 24;
+ rtp[9] = (kSourceID >> 16) & 0xff;
+ rtp[10] = (kSourceID >> 8) & 0xff;
+ rtp[11] = kSourceID & 0xff;
+
+ memcpy(&rtp[12], packet->data(), packet->size());
+
+ return sendRTPPacket(
+ udpPacket,
+ true /* storeInHistory */,
+ true /* timeValid */,
+ ALooper::GetNowUs());
+}
+
status_t RTPSender::queueTSPackets(
const sp<ABuffer> &tsPackets, uint8_t packetType) {
CHECK_EQ(0, tsPackets->size() % 188);
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h
index 3a926ea..fefcab7 100644
--- a/media/libstagefright/wifi-display/rtp/RTPSender.h
+++ b/media/libstagefright/wifi-display/rtp/RTPSender.h
@@ -94,6 +94,7 @@
static uint64_t GetNowNTP();
+ status_t queueRawPacket(const sp<ABuffer> &tsPackets, uint8_t packetType);
status_t queueTSPackets(const sp<ABuffer> &tsPackets, uint8_t packetType);
status_t queueAVCBuffer(const sp<ABuffer> &accessUnit, uint8_t packetType);
diff --git a/media/libstagefright/wifi-display/rtptest.cpp b/media/libstagefright/wifi-display/rtptest.cpp
index eade832..764a38b 100644
--- a/media/libstagefright/wifi-display/rtptest.cpp
+++ b/media/libstagefright/wifi-display/rtptest.cpp
@@ -21,6 +21,7 @@
#include "ANetworkSession.h"
#include "rtp/RTPSender.h"
#include "rtp/RTPReceiver.h"
+#include "TimeSyncer.h"
#include <binder/ProcessState.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -28,12 +29,116 @@
#include <media/stagefright/foundation/AHandler.h>
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/NuMediaExtractor.h>
+#include <media/stagefright/Utils.h>
+
+#define MEDIA_FILENAME "/sdcard/Frame Counter HD 30FPS_1080p.mp4"
namespace android {
+struct PacketSource : public RefBase {
+ PacketSource() {}
+
+ virtual sp<ABuffer> getNextAccessUnit() = 0;
+
+protected:
+ virtual ~PacketSource() {}
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(PacketSource);
+};
+
+struct MediaPacketSource : public PacketSource {
+ MediaPacketSource()
+ : mMaxSampleSize(1024 * 1024) {
+ mExtractor = new NuMediaExtractor;
+ CHECK_EQ((status_t)OK,
+ mExtractor->setDataSource(MEDIA_FILENAME));
+
+ bool haveVideo = false;
+ for (size_t i = 0; i < mExtractor->countTracks(); ++i) {
+ sp<AMessage> format;
+ CHECK_EQ((status_t)OK, mExtractor->getTrackFormat(i, &format));
+
+ AString mime;
+ CHECK(format->findString("mime", &mime));
+
+ if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str())) {
+ mExtractor->selectTrack(i);
+ haveVideo = true;
+ break;
+ }
+ }
+
+ CHECK(haveVideo);
+ }
+
+ virtual sp<ABuffer> getNextAccessUnit() {
+ int64_t timeUs;
+ status_t err = mExtractor->getSampleTime(&timeUs);
+
+ if (err != OK) {
+ return NULL;
+ }
+
+ sp<ABuffer> accessUnit = new ABuffer(mMaxSampleSize);
+ CHECK_EQ((status_t)OK, mExtractor->readSampleData(accessUnit));
+
+ accessUnit->meta()->setInt64("timeUs", timeUs);
+
+ CHECK_EQ((status_t)OK, mExtractor->advance());
+
+ return accessUnit;
+ }
+
+protected:
+ virtual ~MediaPacketSource() {
+ }
+
+private:
+ sp<NuMediaExtractor> mExtractor;
+ size_t mMaxSampleSize;
+
+ DISALLOW_EVIL_CONSTRUCTORS(MediaPacketSource);
+};
+
+struct SimplePacketSource : public PacketSource {
+ SimplePacketSource()
+ : mCounter(0) {
+ }
+
+ virtual sp<ABuffer> getNextAccessUnit() {
+ sp<ABuffer> buffer = new ABuffer(4);
+ uint8_t *dst = buffer->data();
+ dst[0] = mCounter >> 24;
+ dst[1] = (mCounter >> 16) & 0xff;
+ dst[2] = (mCounter >> 8) & 0xff;
+ dst[3] = mCounter & 0xff;
+
+ buffer->meta()->setInt64("timeUs", mCounter * 1000000ll / kFrameRate);
+
+ ++mCounter;
+
+ return buffer;
+ }
+
+protected:
+ virtual ~SimplePacketSource() {
+ }
+
+private:
+ enum {
+ kFrameRate = 30
+ };
+
+ uint32_t mCounter;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SimplePacketSource);
+};
+
struct TestHandler : public AHandler {
TestHandler(const sp<ANetworkSession> &netSession);
@@ -52,18 +157,39 @@
kWhatSenderNotify,
kWhatSendMore,
kWhatStop,
+ kWhatTimeSyncerNotify,
};
+#if 1
+ static const RTPBase::TransportMode kRTPMode = RTPBase::TRANSPORT_UDP;
+ static const RTPBase::TransportMode kRTCPMode = RTPBase::TRANSPORT_UDP;
+#else
+ static const RTPBase::TransportMode kRTPMode = RTPBase::TRANSPORT_TCP;
+ static const RTPBase::TransportMode kRTCPMode = RTPBase::TRANSPORT_NONE;
+#endif
+
+#if 1
+ static const RTPBase::PacketizationMode kPacketizationMode
+ = RTPBase::PACKETIZATION_H264;
+#else
+ static const RTPBase::PacketizationMode kPacketizationMode
+ = RTPBase::PACKETIZATION_NONE;
+#endif
+
sp<ANetworkSession> mNetSession;
- sp<NuMediaExtractor> mExtractor;
+ sp<PacketSource> mSource;
sp<RTPSender> mSender;
sp<RTPReceiver> mReceiver;
- size_t mMaxSampleSize;
+ sp<TimeSyncer> mTimeSyncer;
+ bool mTimeSyncerStarted;
int64_t mFirstTimeRealUs;
int64_t mFirstTimeMediaUs;
+ int64_t mTimeOffsetUs;
+ bool mTimeOffsetValid;
+
status_t readMore();
DISALLOW_EVIL_CONSTRUCTORS(TestHandler);
@@ -71,9 +197,11 @@
TestHandler::TestHandler(const sp<ANetworkSession> &netSession)
: mNetSession(netSession),
- mMaxSampleSize(1024 * 1024),
+ mTimeSyncerStarted(false),
mFirstTimeRealUs(-1ll),
- mFirstTimeMediaUs(-1ll) {
+ mFirstTimeMediaUs(-1ll),
+ mTimeOffsetUs(-1ll),
+ mTimeOffsetValid(false) {
}
TestHandler::~TestHandler() {
@@ -91,23 +219,48 @@
msg->post();
}
+static void dumpDelay(int64_t delayMs) {
+ static const int64_t kMinDelayMs = 0;
+ static const int64_t kMaxDelayMs = 300;
+
+ const char *kPattern = "########################################";
+ size_t kPatternSize = strlen(kPattern);
+
+ int n = (kPatternSize * (delayMs - kMinDelayMs))
+ / (kMaxDelayMs - kMinDelayMs);
+
+ if (n < 0) {
+ n = 0;
+ } else if ((size_t)n > kPatternSize) {
+ n = kPatternSize;
+ }
+
+ ALOGI("(%4lld ms) %s\n",
+ delayMs,
+ kPattern + kPatternSize - n);
+}
+
void TestHandler::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatListen:
{
- sp<AMessage> notify = new AMessage(kWhatReceiverNotify, id());
- mReceiver = new RTPReceiver(mNetSession, notify);
+ sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id());
+ mTimeSyncer = new TimeSyncer(mNetSession, notify);
+ looper()->registerHandler(mTimeSyncer);
+
+ notify = new AMessage(kWhatReceiverNotify, id());
+ mReceiver = new RTPReceiver(
+ mNetSession, notify, RTPReceiver::FLAG_AUTO_CONNECT);
looper()->registerHandler(mReceiver);
CHECK_EQ((status_t)OK,
- mReceiver->registerPacketType(
- 33, RTPReceiver::PACKETIZATION_H264));
+ mReceiver->registerPacketType(33, kPacketizationMode));
int32_t receiverRTPPort;
CHECK_EQ((status_t)OK,
mReceiver->initAsync(
- RTPReceiver::TRANSPORT_UDP, // rtpMode
- RTPReceiver::TRANSPORT_UDP, // rtcpMode
+ kRTPMode,
+ kRTCPMode,
&receiverRTPPort));
printf("picked receiverRTPPort %d\n", receiverRTPPort);
@@ -125,33 +278,23 @@
AString host;
CHECK(msg->findString("host", &host));
+ sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id());
+ mTimeSyncer = new TimeSyncer(mNetSession, notify);
+ looper()->registerHandler(mTimeSyncer);
+ mTimeSyncer->startServer(8123);
+
int32_t receiverRTPPort;
CHECK(msg->findInt32("port", &receiverRTPPort));
- mExtractor = new NuMediaExtractor;
- CHECK_EQ((status_t)OK,
- mExtractor->setDataSource(
- "/sdcard/Frame Counter HD 30FPS_1080p.mp4"));
+#if 1
+ mSource = new MediaPacketSource;
+#else
+ mSource = new SimplePacketSource;
+#endif
- bool haveVideo = false;
- for (size_t i = 0; i < mExtractor->countTracks(); ++i) {
- sp<AMessage> format;
- CHECK_EQ((status_t)OK, mExtractor->getTrackFormat(i, &format));
-
- AString mime;
- CHECK(format->findString("mime", &mime));
-
- if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str())) {
- mExtractor->selectTrack(i);
- haveVideo = true;
- break;
- }
- }
-
- CHECK(haveVideo);
-
- sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
+ notify = new AMessage(kWhatSenderNotify, id());
mSender = new RTPSender(mNetSession, notify);
+
looper()->registerHandler(mSender);
int32_t senderRTPPort;
@@ -159,9 +302,10 @@
mSender->initAsync(
host.c_str(),
receiverRTPPort,
- RTPSender::TRANSPORT_UDP, // rtpMode
- receiverRTPPort + 1,
- RTPSender::TRANSPORT_UDP, // rtcpMode
+ kRTPMode,
+ kRTCPMode == RTPBase::TRANSPORT_NONE
+ ? -1 : receiverRTPPort + 1,
+ kRTCPMode,
&senderRTPPort));
printf("picked senderRTPPort %d\n", senderRTPPort);
@@ -201,7 +345,7 @@
case kWhatReceiverNotify:
{
- ALOGI("kWhatReceiverNotify");
+ ALOGV("kWhatReceiverNotify");
int32_t what;
CHECK(msg->findInt32("what", &what));
@@ -216,8 +360,40 @@
break;
}
- case RTPSender::kWhatError:
+ case RTPReceiver::kWhatError:
break;
+
+ case RTPReceiver::kWhatAccessUnit:
+ {
+#if 0
+ if (!mTimeSyncerStarted) {
+ mTimeSyncer->startClient("172.18.41.216", 8123);
+ mTimeSyncerStarted = true;
+ }
+
+ sp<ABuffer> accessUnit;
+ CHECK(msg->findBuffer("accessUnit", &accessUnit));
+
+ int64_t timeUs;
+ CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+
+ if (mTimeOffsetValid) {
+ timeUs -= mTimeOffsetUs;
+ int64_t nowUs = ALooper::GetNowUs();
+ int64_t delayMs = (nowUs - timeUs) / 1000ll;
+
+ dumpDelay(delayMs);
+ }
+#endif
+ break;
+ }
+
+ case RTPReceiver::kWhatPacketLost:
+ ALOGV("kWhatPacketLost");
+ break;
+
+ default:
+ TRESPASS();
}
break;
}
@@ -231,7 +407,7 @@
mSender->queueBuffer(
accessUnit,
33,
- RTPSender::PACKETIZATION_H264));
+ kPacketizationMode));
status_t err = readMore();
@@ -253,31 +429,33 @@
mSender.clear();
}
- mExtractor.clear();
+ mSource.clear();
looper()->stop();
break;
}
+ case kWhatTimeSyncerNotify:
+ {
+ CHECK(msg->findInt64("offset", &mTimeOffsetUs));
+ mTimeOffsetValid = true;
+ break;
+ }
+
default:
TRESPASS();
}
}
status_t TestHandler::readMore() {
- int64_t timeUs;
- status_t err = mExtractor->getSampleTime(&timeUs);
+ sp<ABuffer> accessUnit = mSource->getNextAccessUnit();
- if (err != OK) {
- return err;
+ if (accessUnit == NULL) {
+ return ERROR_END_OF_STREAM;
}
- sp<ABuffer> accessUnit = new ABuffer(mMaxSampleSize);
- CHECK_EQ((status_t)OK, mExtractor->readSampleData(accessUnit));
-
- accessUnit->meta()->setInt64("timeUs", timeUs);
-
- CHECK_EQ((status_t)OK, mExtractor->advance());
+ int64_t timeUs;
+ CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
int64_t nowUs = ALooper::GetNowUs();
int64_t whenUs;
@@ -289,6 +467,8 @@
whenUs = mFirstTimeRealUs + timeUs - mFirstTimeMediaUs;
}
+ accessUnit->meta()->setInt64("timeUs", whenUs);
+
sp<AMessage> msg = new AMessage(kWhatSendMore, id());
msg->setBuffer("accessUnit", accessUnit);
msg->post(whenUs - nowUs);
diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
index 1a08bf5..5db2099 100644
--- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
+++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
@@ -57,7 +57,8 @@
mSetupDeferred(false),
mLatencyCount(0),
mLatencySumUs(0ll),
- mLatencyMaxUs(0ll) {
+ mLatencyMaxUs(0ll),
+ mMaxDelayMs(-1ll) {
// We support any and all resolutions, but prefer 720p30
mSinkSupportedVideoFormats.setNativeResolution(
VideoFormats::RESOLUTION_CEA, 5); // 1280 x 720 p30
@@ -296,9 +297,13 @@
}
}
-static void dumpDelay(size_t trackIndex, int64_t timeUs) {
+void WifiDisplaySink::dumpDelay(size_t trackIndex, int64_t timeUs) {
int64_t delayMs = (ALooper::GetNowUs() - timeUs) / 1000ll;
+ if (delayMs > mMaxDelayMs) {
+ mMaxDelayMs = delayMs;
+ }
+
static const int64_t kMinDelayMs = 0;
static const int64_t kMaxDelayMs = 300;
@@ -314,9 +319,10 @@
n = kPatternSize;
}
- ALOGI("[%lld]: (%4lld ms) %s",
+ ALOGI("[%lld]: (%4lld ms / %4lld ms) %s",
timeUs / 1000,
delayMs,
+ mMaxDelayMs,
kPattern + kPatternSize - n);
}
@@ -350,14 +356,19 @@
looper()->registerHandler(mRenderer);
}
- CHECK(mTimeOffsetValid);
-
sp<ABuffer> accessUnit;
CHECK(msg->findBuffer("accessUnit", &accessUnit));
int64_t timeUs;
CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+ if (!mTimeOffsetValid && !(mFlags & FLAG_SPECIAL_MODE)) {
+ mTimeOffsetUs = timeUs - ALooper::GetNowUs();
+ mTimeOffsetValid = true;
+ }
+
+ CHECK(mTimeOffsetValid);
+
// We are the timesync _client_,
// client time = server time - time offset.
timeUs -= mTimeOffsetUs;
diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
index 7c62057..adb9d89 100644
--- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
+++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
@@ -132,6 +132,8 @@
int64_t mLatencySumUs;
int64_t mLatencyMaxUs;
+ int64_t mMaxDelayMs;
+
status_t sendM2(int32_t sessionID);
status_t sendSetup(int32_t sessionID, const char *uri);
status_t sendPlay(int32_t sessionID, const char *uri);
@@ -184,6 +186,8 @@
const char *url, AString *host, int32_t *port, AString *path,
AString *user, AString *pass);
+ void dumpDelay(size_t trackIndex, int64_t timeUs);
+
DISALLOW_EVIL_CONSTRUCTORS(WifiDisplaySink);
};
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index 715d0b5..cacfcca 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -709,8 +709,11 @@
Converter::GetInt32Property(
"media.wfd.video-framerate", -1);
- if (rateHz < 0.0) {
- rateHz = repeaterSource->getFrameRate();
+ char val[PROPERTY_VALUE_MAX];
+ if (rateHz < 0.0
+ && property_get("media.wfd.video-framerate", val, NULL)
+ && !strcasecmp("adaptive", val)) {
+ rateHz = repeaterSource->getFrameRate();
if (avgLatencyUs > 300000ll) {
rateHz *= 0.9;
diff --git a/media/libstagefright/yuv/Android.mk b/media/libstagefright/yuv/Android.mk
index a4253f6..b3f7b1b 100644
--- a/media/libstagefright/yuv/Android.mk
+++ b/media/libstagefright/yuv/Android.mk
@@ -6,7 +6,8 @@
YUVCanvas.cpp
LOCAL_SHARED_LIBRARIES := \
- libcutils
+ libcutils \
+ liblog
LOCAL_MODULE:= libstagefright_yuv
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index a485646..1ac647a 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -22,6 +22,7 @@
libmedia \
libmediaplayerservice \
libutils \
+ liblog \
libbinder
LOCAL_STATIC_LIBRARIES := \
diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk
index bee28d4..ac608a1 100644
--- a/media/mtp/Android.mk
+++ b/media/mtp/Android.mk
@@ -42,6 +42,6 @@
# Needed for <bionic_time.h>
LOCAL_C_INCLUDES := bionic/libc/private
-LOCAL_SHARED_LIBRARIES := libutils libcutils libusbhost libbinder
+LOCAL_SHARED_LIBRARIES := libutils libcutils liblog libusbhost libbinder
include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 7806f48..061a079 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -22,7 +22,7 @@
AudioResampler.cpp.arm \
AudioPolicyService.cpp \
ServiceUtilities.cpp \
- AudioResamplerCubic.cpp.arm \
+ AudioResamplerCubic.cpp.arm \
AudioResamplerSinc.cpp.arm
LOCAL_SRC_FILES += StateQueue.cpp
@@ -39,6 +39,7 @@
libcommon_time_client \
libcutils \
libutils \
+ liblog \
libbinder \
libmedia \
libnbaio \
@@ -94,9 +95,10 @@
AudioResamplerSinc.cpp.arm
LOCAL_SHARED_LIBRARIES := \
- libdl \
+ libdl \
libcutils \
- libutils
+ libutils \
+ liblog
LOCAL_MODULE:= test-resample
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 74ba59e..d66294c 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -225,12 +225,18 @@
0,
mConfig.inputCfg.buffer.frameCount*sizeof(int32_t));
}
- start_l();
- mState = ACTIVE;
+ if (start_l() == NO_ERROR) {
+ mState = ACTIVE;
+ } else {
+ mState = IDLE;
+ }
break;
case STOPPING:
- stop_l();
- mDisableWaitCnt = mMaxDisableWaitCnt;
+ if (stop_l() == NO_ERROR) {
+ mDisableWaitCnt = mMaxDisableWaitCnt;
+ } else {
+ mDisableWaitCnt = 1; // will cause immediate transition to IDLE
+ }
mState = STOPPED;
break;
case STOPPED:
@@ -297,7 +303,7 @@
void AudioFlinger::EffectModule::reset_l()
{
- if (mEffectInterface == NULL) {
+ if (mStatus != NO_ERROR || mEffectInterface == NULL) {
return;
}
(*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_RESET, 0, NULL, 0, NULL);
@@ -305,17 +311,24 @@
status_t AudioFlinger::EffectModule::configure()
{
+ status_t status;
+ sp<ThreadBase> thread;
+ uint32_t size;
+ audio_channel_mask_t channelMask;
+
if (mEffectInterface == NULL) {
- return NO_INIT;
+ status = NO_INIT;
+ goto exit;
}
- sp<ThreadBase> thread = mThread.promote();
+ thread = mThread.promote();
if (thread == 0) {
- return DEAD_OBJECT;
+ status = DEAD_OBJECT;
+ goto exit;
}
// TODO: handle configuration of effects replacing track process
- audio_channel_mask_t channelMask = thread->channelMask();
+ channelMask = thread->channelMask();
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_MONO;
@@ -357,8 +370,8 @@
this, thread.get(), mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount);
status_t cmdStatus;
- uint32_t size = sizeof(int);
- status_t status = (*mEffectInterface)->command(mEffectInterface,
+ size = sizeof(int);
+ status = (*mEffectInterface)->command(mEffectInterface,
EFFECT_CMD_SET_CONFIG,
sizeof(effect_config_t),
&mConfig,
@@ -396,6 +409,8 @@
mMaxDisableWaitCnt = (MAX_DISABLE_TIME_MS * mConfig.outputCfg.samplingRate) /
(1000 * mConfig.outputCfg.buffer.frameCount);
+exit:
+ mStatus = status;
return status;
}
@@ -430,6 +445,9 @@
if (mEffectInterface == NULL) {
return NO_INIT;
}
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
status_t cmdStatus;
uint32_t size = sizeof(status_t);
status_t status = (*mEffectInterface)->command(mEffectInterface,
@@ -466,6 +484,9 @@
if (mEffectInterface == NULL) {
return NO_INIT;
}
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
status_t cmdStatus;
uint32_t size = sizeof(status_t);
status_t status = (*mEffectInterface)->command(mEffectInterface,
@@ -503,6 +524,9 @@
if (mState == DESTROYED || mEffectInterface == NULL) {
return NO_INIT;
}
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
status_t status = (*mEffectInterface)->command(mEffectInterface,
cmdCode,
cmdSize,
@@ -592,6 +616,10 @@
bool AudioFlinger::EffectModule::isProcessEnabled() const
{
+ if (mStatus != NO_ERROR) {
+ return false;
+ }
+
switch (mState) {
case RESTART:
case ACTIVE:
@@ -609,8 +637,10 @@
status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, bool controller)
{
Mutex::Autolock _l(mLock);
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
status_t status = NO_ERROR;
-
// Send volume indication if EFFECT_FLAG_VOLUME_IND is set and read back altered volume
// if controller flag is set (Note that controller == TRUE => EFFECT_FLAG_VOLUME_CTRL set)
if (isProcessEnabled() &&
@@ -646,6 +676,9 @@
}
Mutex::Autolock _l(mLock);
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
status_t status = NO_ERROR;
if (device && (mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_IND) {
status_t cmdStatus;
@@ -665,6 +698,9 @@
status_t AudioFlinger::EffectModule::setMode(audio_mode_t mode)
{
Mutex::Autolock _l(mLock);
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
status_t status = NO_ERROR;
if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_MODE_MASK) == EFFECT_FLAG_AUDIO_MODE_IND) {
status_t cmdStatus;
@@ -685,6 +721,9 @@
status_t AudioFlinger::EffectModule::setAudioSource(audio_source_t source)
{
Mutex::Autolock _l(mLock);
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
status_t status = NO_ERROR;
if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_SOURCE_MASK) == EFFECT_FLAG_AUDIO_SOURCE_IND) {
uint32_t size = 0;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 9d98f0b..47ca100 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3122,16 +3122,15 @@
Vector< sp<Track> > *tracksToRemove
)
{
- sp<Track> trackToRemove;
-
+ size_t count = mActiveTracks.size();
mixer_state mixerStatus = MIXER_IDLE;
// find out which tracks need to be processed
- if (mActiveTracks.size() != 0) {
- sp<Track> t = mActiveTracks[0].promote();
+ for (size_t i = 0; i < count; i++) {
+ sp<Track> t = mActiveTracks[i].promote();
// The track died recently
if (t == 0) {
- return MIXER_IDLE;
+ continue;
}
Track* const track = t.get();
@@ -3180,35 +3179,40 @@
}
right = v_clamped/MAX_GAIN;
}
+ // Only consider last track started for volume and mixer state control.
+ // This is the last entry in mActiveTracks unless a track underruns.
+ // As we only care about the transition phase between two tracks on a
+ // direct output, it is not a problem to ignore the underrun case.
+ if (i == (count - 1)) {
+ if (left != mLeftVolFloat || right != mRightVolFloat) {
+ mLeftVolFloat = left;
+ mRightVolFloat = right;
- if (left != mLeftVolFloat || right != mRightVolFloat) {
- mLeftVolFloat = left;
- mRightVolFloat = right;
+ // Convert volumes from float to 8.24
+ uint32_t vl = (uint32_t)(left * (1 << 24));
+ uint32_t vr = (uint32_t)(right * (1 << 24));
- // Convert volumes from float to 8.24
- uint32_t vl = (uint32_t)(left * (1 << 24));
- uint32_t vr = (uint32_t)(right * (1 << 24));
-
- // Delegate volume control to effect in track effect chain if needed
- // only one effect chain can be present on DirectOutputThread, so if
- // there is one, the track is connected to it
- if (!mEffectChains.isEmpty()) {
- // Do not ramp volume if volume is controlled by effect
- mEffectChains[0]->setVolume_l(&vl, &vr);
- left = (float)vl / (1 << 24);
- right = (float)vr / (1 << 24);
+ // Delegate volume control to effect in track effect chain if needed
+ // only one effect chain can be present on DirectOutputThread, so if
+ // there is one, the track is connected to it
+ if (!mEffectChains.isEmpty()) {
+ // Do not ramp volume if volume is controlled by effect
+ mEffectChains[0]->setVolume_l(&vl, &vr);
+ left = (float)vl / (1 << 24);
+ right = (float)vr / (1 << 24);
+ }
+ mOutput->stream->set_volume(mOutput->stream, left, right);
}
- mOutput->stream->set_volume(mOutput->stream, left, right);
- }
- // reset retry count
- track->mRetryCount = kMaxTrackRetriesDirect;
- mActiveTrack = t;
- mixerStatus = MIXER_TRACKS_READY;
+ // reset retry count
+ track->mRetryCount = kMaxTrackRetriesDirect;
+ mActiveTrack = t;
+ mixerStatus = MIXER_TRACKS_READY;
+ }
} else {
- // clear effect chain input buffer if an active track underruns to avoid sending
- // previous audio buffer again to effects
- if (!mEffectChains.isEmpty()) {
+ // clear effect chain input buffer if the last active track started underruns
+ // to avoid sending previous audio buffer again to effects
+ if (!mEffectChains.isEmpty() && (i == (count -1))) {
mEffectChains[0]->clearInputBuffer();
}
@@ -3224,33 +3228,36 @@
if (track->isStopped()) {
track->reset();
}
- trackToRemove = track;
+ tracksToRemove->add(track);
}
} else {
// No buffers for this track. Give it a few chances to
// fill a buffer, then remove it from active list.
+ // Only consider last track started for mixer state control
if (--(track->mRetryCount) <= 0) {
ALOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
- trackToRemove = track;
- } else {
+ tracksToRemove->add(track);
+ } else if (i == (count -1)){
mixerStatus = MIXER_TRACKS_ENABLED;
}
}
}
}
- // FIXME merge this with similar code for removing multiple tracks
// remove all the tracks that need to be...
- if (CC_UNLIKELY(trackToRemove != 0)) {
- tracksToRemove->add(trackToRemove);
- mActiveTracks.remove(trackToRemove);
- if (!mEffectChains.isEmpty()) {
- ALOGV("stopping track on chain %p for session Id: %d", mEffectChains[0].get(),
- trackToRemove->sessionId());
- mEffectChains[0]->decActiveTrackCnt();
- }
- if (trackToRemove->isTerminated()) {
- removeTrack_l(trackToRemove);
+ count = tracksToRemove->size();
+ if (CC_UNLIKELY(count)) {
+ for (size_t i = 0 ; i < count ; i++) {
+ const sp<Track>& track = tracksToRemove->itemAt(i);
+ mActiveTracks.remove(track);
+ if (!mEffectChains.isEmpty()) {
+ ALOGV("stopping track on chain %p for session Id: %d", mEffectChains[0].get(),
+ track->sessionId());
+ mEffectChains[0]->decActiveTrackCnt();
+ }
+ if (track->isTerminated()) {
+ removeTrack_l(track);
+ }
}
}
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 6847bf8..3c84703 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -32,6 +32,7 @@
LOCAL_SHARED_LIBRARIES:= \
libui \
+ liblog \
libutils \
libbinder \
libcutils \
diff --git a/services/camera/libcameraservice/Camera3Device.cpp b/services/camera/libcameraservice/Camera3Device.cpp
index 6cf652c..08aef83 100644
--- a/services/camera/libcameraservice/Camera3Device.cpp
+++ b/services/camera/libcameraservice/Camera3Device.cpp
@@ -25,6 +25,18 @@
#define ALOGVV(...) ((void)0)
#endif
+// Convenience macro for transient errors
+#define CLOGE(fmt, ...) ALOGE("Camera %d: %s: " fmt, mId, __FUNCTION__, \
+ ##__VA_ARGS__)
+
+// Convenience macros for transitioning to the error state
+#define SET_ERR(fmt, ...) setErrorState( \
+ "%s: " fmt, __FUNCTION__, \
+ ##__VA_ARGS__)
+#define SET_ERR_L(fmt, ...) setErrorStateLocked( \
+ "%s: " fmt, __FUNCTION__, \
+ ##__VA_ARGS__)
+
#include <utils/Log.h>
#include <utils/Trace.h>
#include <utils/Timers.h>
@@ -69,7 +81,7 @@
ALOGV("%s: Initializing device for camera %d", __FUNCTION__, mId);
if (mStatus != STATUS_UNINITIALIZED) {
- ALOGE("%s: Already initialized!", __FUNCTION__);
+ CLOGE("Already initialized!");
return INVALID_OPERATION;
}
@@ -84,21 +96,18 @@
reinterpret_cast<hw_device_t**>(&device));
if (res != OK) {
- ALOGE("%s: Could not open camera %d: %s (%d)", __FUNCTION__,
- mId, strerror(-res), res);
- mStatus = STATUS_ERROR;
+ SET_ERR_L("Could not open camera: %s (%d)", strerror(-res), res);
return res;
}
/** Cross-check device version */
if (device->common.version != CAMERA_DEVICE_API_VERSION_3_0) {
- ALOGE("%s: Could not open camera %d: "
+ SET_ERR_L("Could not open camera: "
"Camera device is not version %x, reports %x instead",
- __FUNCTION__, mId, CAMERA_DEVICE_API_VERSION_3_0,
+ CAMERA_DEVICE_API_VERSION_3_0,
device->common.version);
device->common.close(&device->common);
- mStatus = STATUS_ERROR;
return BAD_VALUE;
}
@@ -107,11 +116,10 @@
if (res != OK) return res;
if (info.device_version != device->common.version) {
- ALOGE("%s: HAL reporting mismatched camera_info version (%x)"
- " and device version (%x).", __FUNCTION__,
+ SET_ERR_L("HAL reporting mismatched camera_info version (%x)"
+ " and device version (%x).",
device->common.version, info.device_version);
device->common.close(&device->common);
- mStatus = STATUS_ERROR;
return BAD_VALUE;
}
@@ -119,10 +127,9 @@
res = device->ops->initialize(device, this);
if (res != OK) {
- ALOGE("%s: Camera %d: Unable to initialize HAL device: %s (%d)",
- __FUNCTION__, mId, strerror(-res), res);
+ SET_ERR_L("Unable to initialize HAL device: %s (%d)",
+ strerror(-res), res);
device->common.close(&device->common);
- mStatus = STATUS_ERROR;
return BAD_VALUE;
}
@@ -135,10 +142,9 @@
if (mVendorTagOps.get_camera_vendor_section_name != NULL) {
res = set_camera_metadata_vendor_tag_ops(&mVendorTagOps);
if (res != OK) {
- ALOGE("%s: Camera %d: Unable to set tag ops: %s (%d)",
- __FUNCTION__, mId, strerror(-res), res);
+ SET_ERR_L("Unable to set tag ops: %s (%d)",
+ strerror(-res), res);
device->common.close(&device->common);
- mStatus = STATUS_ERROR;
return res;
}
}
@@ -148,11 +154,10 @@
mRequestThread = new RequestThread(this, device);
res = mRequestThread->run(String8::format("C3Dev-%d-ReqQueue", mId).string());
if (res != OK) {
- ALOGE("%s: Camera %d: Unable to start request queue thread: %s (%d)",
- __FUNCTION__, mId, strerror(-res), res);
+ SET_ERR_L("Unable to start request queue thread: %s (%d)",
+ strerror(-res), res);
device->common.close(&device->common);
mRequestThread.clear();
- mStatus = STATUS_ERROR;
return res;
}
@@ -179,12 +184,12 @@
(mStatus == STATUS_ERROR && mRequestThread != NULL)) {
res = mRequestThread->clearRepeatingRequests();
if (res != OK) {
- ALOGE("%s: Can't stop streaming", __FUNCTION__);
+ SET_ERR_L("Can't stop streaming");
return res;
}
res = waitUntilDrainedLocked();
if (res != OK) {
- ALOGE("%s: Timeout waiting for HAL to drain", __FUNCTION__);
+ SET_ERR_L("Timeout waiting for HAL to drain");
return res;
}
}
@@ -225,6 +230,9 @@
mStatus == STATUS_ACTIVE ? "ACTIVE" :
"Unknown";
lines.appendFormat(" Device status: %s\n", status);
+ if (mStatus == STATUS_ERROR) {
+ lines.appendFormat(" Error cause: %s\n", mErrorCause.string());
+ }
lines.appendFormat(" Stream configuration:\n");
if (mInputStream != NULL) {
@@ -251,7 +259,7 @@
ALOGVV("%s: E", __FUNCTION__);
if (CC_UNLIKELY(mStatus == STATUS_UNINITIALIZED ||
mStatus == STATUS_ERROR)) {
- ALOGE("%s: Access to static info %s!", __FUNCTION__,
+ ALOGW("%s: Access to static info %s!", __FUNCTION__,
mStatus == STATUS_ERROR ?
"when in error state" : "before init");
}
@@ -262,25 +270,27 @@
ATRACE_CALL();
Mutex::Autolock l(mLock);
+ // TODO: take ownership of the request
+
switch (mStatus) {
case STATUS_ERROR:
- ALOGE("%s: Device has encountered a serious error", __FUNCTION__);
+ CLOGE("Device has encountered a serious error");
return INVALID_OPERATION;
case STATUS_UNINITIALIZED:
- ALOGE("%s: Device not initialized", __FUNCTION__);
+ CLOGE("Device not initialized");
return INVALID_OPERATION;
case STATUS_IDLE:
case STATUS_ACTIVE:
// OK
break;
default:
- ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+ SET_ERR_L("Unexpected status: %d", mStatus);
return INVALID_OPERATION;
}
sp<CaptureRequest> newRequest = setUpRequestLocked(request);
if (newRequest == NULL) {
- ALOGE("%s: Can't create capture request", __FUNCTION__);
+ CLOGE("Can't create capture request");
return BAD_VALUE;
}
@@ -294,23 +304,23 @@
switch (mStatus) {
case STATUS_ERROR:
- ALOGE("%s: Device has encountered a serious error", __FUNCTION__);
+ CLOGE("Device has encountered a serious error");
return INVALID_OPERATION;
case STATUS_UNINITIALIZED:
- ALOGE("%s: Device not initialized", __FUNCTION__);
+ CLOGE("Device not initialized");
return INVALID_OPERATION;
case STATUS_IDLE:
case STATUS_ACTIVE:
// OK
break;
default:
- ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+ SET_ERR_L("Unexpected status: %d", mStatus);
return INVALID_OPERATION;
}
sp<CaptureRequest> newRepeatingRequest = setUpRequestLocked(request);
if (newRepeatingRequest == NULL) {
- ALOGE("%s: Can't create repeating request", __FUNCTION__);
+ CLOGE("Can't create repeating request");
return BAD_VALUE;
}
@@ -328,8 +338,7 @@
if (mStatus == STATUS_IDLE) {
res = configureStreamsLocked();
if (res != OK) {
- ALOGE("%s: Can't set up streams: %s (%d)",
- __FUNCTION__, strerror(-res), res);
+ SET_ERR_L("Can't set up streams: %s (%d)", strerror(-res), res);
return NULL;
}
}
@@ -344,17 +353,17 @@
switch (mStatus) {
case STATUS_ERROR:
- ALOGE("%s: Device has encountered a serious error", __FUNCTION__);
+ CLOGE("Device has encountered a serious error");
return INVALID_OPERATION;
case STATUS_UNINITIALIZED:
- ALOGE("%s: Device not initialized", __FUNCTION__);
+ CLOGE("Device not initialized");
return INVALID_OPERATION;
case STATUS_IDLE:
case STATUS_ACTIVE:
// OK
break;
default:
- ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+ SET_ERR_L("Unexpected status: %d", mStatus);
return INVALID_OPERATION;
}
@@ -363,10 +372,8 @@
status_t Camera3Device::waitUntilRequestReceived(int32_t requestId, nsecs_t timeout) {
ATRACE_CALL();
- (void)requestId; (void)timeout;
- ALOGE("%s: Unimplemented", __FUNCTION__);
- return INVALID_OPERATION;
+ return mRequestThread->waitUntilRequestProcessed(requestId, timeout);
}
status_t Camera3Device::createStream(sp<ANativeWindow> consumer,
@@ -379,10 +386,10 @@
switch (mStatus) {
case STATUS_ERROR:
- ALOGE("%s: Device has encountered a serious error", __FUNCTION__);
+ CLOGE("Device has encountered a serious error");
return INVALID_OPERATION;
case STATUS_UNINITIALIZED:
- ALOGE("%s: Device not initialized", __FUNCTION__);
+ CLOGE("Device not initialized");
return INVALID_OPERATION;
case STATUS_IDLE:
// OK
@@ -394,13 +401,12 @@
if (res != OK) {
ALOGE("%s: Can't pause captures to reconfigure streams!",
__FUNCTION__);
- mStatus = STATUS_ERROR;
return res;
}
wasActive = true;
break;
default:
- ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+ SET_ERR_L("Unexpected status: %d", mStatus);
return INVALID_OPERATION;
}
assert(mStatus == STATUS_IDLE);
@@ -416,8 +422,7 @@
res = mOutputStreams.add(mNextStreamId, newStream);
if (res < 0) {
- ALOGE("%s: Can't add new stream to set: %s (%d)",
- __FUNCTION__, strerror(-res), res);
+ SET_ERR_L("Can't add new stream to set: %s (%d)", strerror(-res), res);
return res;
}
@@ -428,8 +433,8 @@
ALOGV("%s: Restarting activity to reconfigure streams", __FUNCTION__);
res = configureStreamsLocked();
if (res != OK) {
- ALOGE("%s: Can't reconfigure device for new stream %d: %s (%d)",
- __FUNCTION__, mNextStreamId, strerror(-res), res);
+ CLOGE("Can't reconfigure device for new stream %d: %s (%d)",
+ mNextStreamId, strerror(-res), res);
return res;
}
mRequestThread->setPaused(false);
@@ -442,7 +447,7 @@
ATRACE_CALL();
(void)outputId; (void)id;
- ALOGE("%s: Unimplemented", __FUNCTION__);
+ CLOGE("Unimplemented");
return INVALID_OPERATION;
}
@@ -454,23 +459,23 @@
switch (mStatus) {
case STATUS_ERROR:
- ALOGE("%s: Device has encountered a serious error", __FUNCTION__);
+ CLOGE("Device has encountered a serious error");
return INVALID_OPERATION;
case STATUS_UNINITIALIZED:
- ALOGE("%s: Device not initialized!", __FUNCTION__);
+ CLOGE("Device not initialized!");
return INVALID_OPERATION;
case STATUS_IDLE:
case STATUS_ACTIVE:
// OK
break;
default:
- ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+ SET_ERR_L("Unexpected status: %d", mStatus);
return INVALID_OPERATION;
}
ssize_t idx = mOutputStreams.indexOfKey(id);
if (idx == NAME_NOT_FOUND) {
- ALOGE("%s: Stream %d is unknown", __FUNCTION__, id);
+ CLOGE("Stream %d is unknown", id);
return idx;
}
@@ -488,24 +493,24 @@
switch (mStatus) {
case STATUS_ERROR:
- ALOGE("%s: Device has encountered a serious error", __FUNCTION__);
+ CLOGE("Device has encountered a serious error");
return INVALID_OPERATION;
case STATUS_UNINITIALIZED:
- ALOGE("%s: Device not initialized", __FUNCTION__);
+ CLOGE("Device not initialized");
return INVALID_OPERATION;
case STATUS_IDLE:
case STATUS_ACTIVE:
// OK
break;
default:
- ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+ SET_ERR_L("Unexpected status: %d", mStatus);
return INVALID_OPERATION;
}
ssize_t idx = mOutputStreams.indexOfKey(id);
if (idx == NAME_NOT_FOUND) {
- ALOGE("%s: Stream %d does not exist",
- __FUNCTION__, id);
+ CLOGE("Stream %d does not exist",
+ id);
return BAD_VALUE;
}
@@ -520,7 +525,7 @@
// CameraDevice semantics require device to already be idle before
// deleteStream is called, unlike for createStream.
if (mStatus != STATUS_IDLE) {
- ALOGE("%s: Device not idle", __FUNCTION__);
+ CLOGE("Device not idle");
return INVALID_OPERATION;
}
@@ -531,8 +536,7 @@
} else {
ssize_t idx = mOutputStreams.indexOfKey(id);
if (idx == NAME_NOT_FOUND) {
- ALOGE("%s: Stream %d does not exist",
- __FUNCTION__, id);
+ CLOGE("Stream %d does not exist", id);
return BAD_VALUE;
}
deletedStream = mOutputStreams.editValueAt(idx);
@@ -542,7 +546,7 @@
// Free up the stream endpoint so that it can be used by some other stream
res = deletedStream->disconnect();
if (res != OK) {
- ALOGE("%s: Can't disconnect deleted stream", __FUNCTION__);
+ SET_ERR_L("Can't disconnect deleted stream %d", id);
// fall through since we want to still list the stream as deleted.
}
mDeletedStreams.add(deletedStream);
@@ -554,7 +558,7 @@
ATRACE_CALL();
(void)id;
- ALOGE("%s: Unimplemented", __FUNCTION__);
+ CLOGE("Unimplemented");
return INVALID_OPERATION;
}
@@ -567,24 +571,28 @@
switch (mStatus) {
case STATUS_ERROR:
- ALOGE("%s: Device has encountered a serious error", __FUNCTION__);
+ CLOGE("Device has encountered a serious error");
return INVALID_OPERATION;
case STATUS_UNINITIALIZED:
- ALOGE("%s: Device is not initialized!", __FUNCTION__);
+ CLOGE("Device is not initialized!");
return INVALID_OPERATION;
case STATUS_IDLE:
case STATUS_ACTIVE:
// OK
break;
default:
- ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+ SET_ERR_L("Unexpected status: %d", mStatus);
return INVALID_OPERATION;
}
const camera_metadata_t *rawRequest;
rawRequest = mHal3Device->ops->construct_default_request_settings(
mHal3Device, templateId);
- if (rawRequest == NULL) return DEAD_OBJECT;
+ if (rawRequest == NULL) {
+ SET_ERR_L("HAL is unable to construct default settings for template %d",
+ templateId);
+ return DEAD_OBJECT;
+ }
*request = rawRequest;
return OK;
@@ -611,35 +619,31 @@
// Need to shut down
break;
default:
- ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+ SET_ERR_L("Unexpected status: %d",mStatus);
return INVALID_OPERATION;
}
if (mRequestThread != NULL) {
res = mRequestThread->waitUntilPaused(kShutdownTimeout);
if (res != OK) {
- ALOGE("%s: Can't stop request thread in %f seconds!",
- __FUNCTION__, kShutdownTimeout/1e9);
- mStatus = STATUS_ERROR;
+ SET_ERR_L("Can't stop request thread in %f seconds!",
+ kShutdownTimeout/1e9);
return res;
}
}
if (mInputStream != NULL) {
res = mInputStream->waitUntilIdle(kShutdownTimeout);
if (res != OK) {
- ALOGE("%s: Can't idle input stream %d in %f seconds!",
- __FUNCTION__, mInputStream->getId(), kShutdownTimeout/1e9);
- mStatus = STATUS_ERROR;
+ SET_ERR_L("Can't idle input stream %d in %f seconds!",
+ mInputStream->getId(), kShutdownTimeout/1e9);
return res;
}
}
for (size_t i = 0; i < mOutputStreams.size(); i++) {
res = mOutputStreams.editValueAt(i)->waitUntilIdle(kShutdownTimeout);
if (res != OK) {
- ALOGE("%s: Can't idle output stream %d in %f seconds!",
- __FUNCTION__, mOutputStreams.keyAt(i),
- kShutdownTimeout/1e9);
- mStatus = STATUS_ERROR;
+ SET_ERR_L("Can't idle output stream %d in %f seconds!",
+ mOutputStreams.keyAt(i), kShutdownTimeout/1e9);
return res;
}
}
@@ -673,8 +677,8 @@
if (res == TIMED_OUT) {
return res;
} else if (res != OK) {
- ALOGE("%s: Camera %d: Error waiting for frame: %s (%d)",
- __FUNCTION__, mId, strerror(-res), res);
+ ALOGW("%s: Camera %d: No frame in %lld ns: %s (%d)",
+ __FUNCTION__, mId, timeout, strerror(-res), res);
return res;
}
}
@@ -698,28 +702,62 @@
status_t Camera3Device::triggerAutofocus(uint32_t id) {
ATRACE_CALL();
- (void)id;
- ALOGE("%s: Unimplemented", __FUNCTION__);
- return INVALID_OPERATION;
+ ALOGV("%s: Triggering autofocus, id %d", __FUNCTION__, id);
+ // Mix-in this trigger into the next request and only the next request.
+ RequestTrigger trigger[] = {
+ {
+ ANDROID_CONTROL_AF_TRIGGER,
+ ANDROID_CONTROL_AF_TRIGGER_START
+ },
+ {
+ ANDROID_CONTROL_AF_TRIGGER_ID,
+ static_cast<int32_t>(id)
+ },
+ };
+
+ return mRequestThread->queueTrigger(trigger,
+ sizeof(trigger)/sizeof(trigger[0]));
}
status_t Camera3Device::triggerCancelAutofocus(uint32_t id) {
ATRACE_CALL();
- (void)id;
- ALOGE("%s: Unimplemented", __FUNCTION__);
- return INVALID_OPERATION;
+ ALOGV("%s: Triggering cancel autofocus, id %d", __FUNCTION__, id);
+ // Mix-in this trigger into the next request and only the next request.
+ RequestTrigger trigger[] = {
+ {
+ ANDROID_CONTROL_AF_TRIGGER,
+ ANDROID_CONTROL_AF_TRIGGER_CANCEL
+ },
+ {
+ ANDROID_CONTROL_AF_TRIGGER_ID,
+ static_cast<int32_t>(id)
+ },
+ };
+ return mRequestThread->queueTrigger(trigger,
+ sizeof(trigger)/sizeof(trigger[0]));
}
status_t Camera3Device::triggerPrecaptureMetering(uint32_t id) {
ATRACE_CALL();
- (void)id;
- ALOGE("%s: Unimplemented", __FUNCTION__);
- return INVALID_OPERATION;
+ ALOGV("%s: Triggering precapture metering, id %d", __FUNCTION__, id);
+ // Mix-in this trigger into the next request and only the next request.
+ RequestTrigger trigger[] = {
+ {
+ ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+ ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_START
+ },
+ {
+ ANDROID_CONTROL_AE_PRECAPTURE_ID,
+ static_cast<int32_t>(id)
+ },
+ };
+ return mRequestThread->queueTrigger(trigger,
+ sizeof(trigger)/sizeof(trigger[0]));
}
status_t Camera3Device::pushReprocessBuffer(int reprocessStreamId,
@@ -727,7 +765,7 @@
ATRACE_CALL();
(void)reprocessStreamId; (void)buffer; (void)listener;
- ALOGE("%s: Unimplemented", __FUNCTION__);
+ CLOGE("Unimplemented");
return INVALID_OPERATION;
}
@@ -748,8 +786,8 @@
if (inputStreams.count > 0) {
if (mInputStream == NULL ||
mInputStream->getId() != inputStreams.data.u8[0]) {
- ALOGE("%s: Request references unknown input stream %d",
- __FUNCTION__, inputStreams.data.u8[0]);
+ CLOGE("Request references unknown input stream %d",
+ inputStreams.data.u8[0]);
return NULL;
}
// Lazy completion of stream configuration (allocation/registration)
@@ -757,10 +795,9 @@
if (mInputStream->isConfiguring()) {
res = mInputStream->finishConfiguration(mHal3Device);
if (res != OK) {
- ALOGE("%s: Unable to finish configuring input stream %d:"
+ SET_ERR_L("Unable to finish configuring input stream %d:"
" %s (%d)",
- __FUNCTION__, mInputStream->getId(),
- strerror(-res), res);
+ mInputStream->getId(), strerror(-res), res);
return NULL;
}
}
@@ -772,15 +809,15 @@
camera_metadata_entry_t streams =
newRequest->mSettings.find(ANDROID_REQUEST_OUTPUT_STREAMS);
if (streams.count == 0) {
- ALOGE("%s: Zero output streams specified!", __FUNCTION__);
+ CLOGE("Zero output streams specified!");
return NULL;
}
for (size_t i = 0; i < streams.count; i++) {
int idx = mOutputStreams.indexOfKey(streams.data.u8[i]);
if (idx == NAME_NOT_FOUND) {
- ALOGE("%s: Request references unknown stream %d",
- __FUNCTION__, streams.data.u8[i]);
+ CLOGE("Request references unknown stream %d",
+ streams.data.u8[i]);
return NULL;
}
sp<Camera3OutputStream> stream = mOutputStreams.editValueAt(idx);
@@ -790,8 +827,8 @@
if (stream->isConfiguring()) {
res = stream->finishConfiguration(mHal3Device);
if (res != OK) {
- ALOGE("%s: Unable to finish configuring stream %d: %s (%d)",
- __FUNCTION__, stream->getId(), strerror(-res), res);
+ SET_ERR_L("Unable to finish configuring stream %d: %s (%d)",
+ stream->getId(), strerror(-res), res);
return NULL;
}
}
@@ -808,7 +845,7 @@
status_t res;
if (mStatus != STATUS_IDLE) {
- ALOGE("%s: Not idle", __FUNCTION__);
+ CLOGE("Not idle");
return INVALID_OPERATION;
}
@@ -825,9 +862,7 @@
camera3_stream_t *inputStream;
inputStream = mInputStream->startConfiguration();
if (inputStream == NULL) {
- ALOGE("%s: Can't start input stream configuration",
- __FUNCTION__);
- // TODO: Make sure the error flow here is correct
+ SET_ERR_L("Can't start input stream configuration");
return INVALID_OPERATION;
}
streams.add(inputStream);
@@ -837,9 +872,7 @@
camera3_stream_t *outputStream;
outputStream = mOutputStreams.editValueAt(i)->startConfiguration();
if (outputStream == NULL) {
- ALOGE("%s: Can't start output stream configuration",
- __FUNCTION__);
- // TODO: Make sure the error flow here is correct
+ SET_ERR_L("Can't start output stream configuration");
return INVALID_OPERATION;
}
streams.add(outputStream);
@@ -853,8 +886,8 @@
res = mHal3Device->ops->configure_streams(mHal3Device, &config);
if (res != OK) {
- ALOGE("%s: Unable to configure streams with HAL: %s (%d)",
- __FUNCTION__, strerror(-res), res);
+ SET_ERR_L("Unable to configure streams with HAL: %s (%d)",
+ strerror(-res), res);
return res;
}
@@ -869,6 +902,38 @@
return OK;
}
+void Camera3Device::setErrorState(const char *fmt, ...) {
+ Mutex::Autolock l(mLock);
+ va_list args;
+ va_start(args, fmt);
+
+ setErrorStateLockedV(fmt, args);
+
+ va_end(args);
+}
+
+void Camera3Device::setErrorStateV(const char *fmt, va_list args) {
+ Mutex::Autolock l(mLock);
+ setErrorStateLockedV(fmt, args);
+}
+
+void Camera3Device::setErrorStateLocked(const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+
+ setErrorStateLockedV(fmt, args);
+
+ va_end(args);
+}
+
+void Camera3Device::setErrorStateLockedV(const char *fmt, va_list args) {
+ // Only accept the first failure cause
+ if (mStatus == STATUS_ERROR) return;
+
+ mErrorCause = String8::formatV(fmt, args);
+ ALOGE("Camera %d: %s", mId, mErrorCause.string());
+ mStatus = STATUS_ERROR;
+}
/**
* Camera HAL device callback methods
@@ -880,8 +945,7 @@
status_t res;
if (result->result == NULL) {
- // TODO: Report error upstream
- ALOGW("%s: No metadata for frame %d", __FUNCTION__,
+ SET_ERR("No metadata provided by HAL for frame %d",
result->frame_number);
return;
}
@@ -899,20 +963,26 @@
// Push result metadata into queue
mResultQueue.push_back(CameraMetadata());
- CameraMetadata &captureResult = *(mResultQueue.end());
+ // Lets avoid copies! Too bad there's not a #back method
+ CameraMetadata &captureResult = *(--mResultQueue.end());
captureResult = result->result;
- captureResult.update(ANDROID_REQUEST_FRAME_COUNT,
- (int32_t*)&result->frame_number, 1);
+ if (captureResult.update(ANDROID_REQUEST_FRAME_COUNT,
+ (int32_t*)&result->frame_number, 1) != OK) {
+ SET_ERR("Failed to set frame# in metadata (%d)",
+ result->frame_number);
+ } else {
+ ALOGVV("%s: Camera %d: Set frame# in metadata (%d)",
+ __FUNCTION__, mId, result->frame_number);
+ }
// Get timestamp from result metadata
camera_metadata_entry entry =
captureResult.find(ANDROID_SENSOR_TIMESTAMP);
if (entry.count == 0) {
- ALOGE("%s: Camera %d: No timestamp provided by HAL for frame %d!",
- __FUNCTION__, mId, result->frame_number);
- // TODO: Report error upstream
+ SET_ERR("No timestamp provided by HAL for frame %d!",
+ result->frame_number);
} else {
timestamp = entry.data.i64[0];
}
@@ -921,8 +991,8 @@
entry = captureResult.find(ANDROID_CONTROL_AE_STATE);
if (entry.count == 0) {
- ALOGE("%s: Camera %d: No AE state provided by HAL for frame %d!",
- __FUNCTION__, mId, result->frame_number);
+ CLOGE("No AE state provided by HAL for frame %d!",
+ result->frame_number);
} else {
new3aState.aeState =
static_cast<camera_metadata_enum_android_control_ae_state>(
@@ -931,8 +1001,8 @@
entry = captureResult.find(ANDROID_CONTROL_AF_STATE);
if (entry.count == 0) {
- ALOGE("%s: Camera %d: No AF state provided by HAL for frame %d!",
- __FUNCTION__, mId, result->frame_number);
+ CLOGE("No AF state provided by HAL for frame %d!",
+ result->frame_number);
} else {
new3aState.afState =
static_cast<camera_metadata_enum_android_control_af_state>(
@@ -941,8 +1011,8 @@
entry = captureResult.find(ANDROID_CONTROL_AWB_STATE);
if (entry.count == 0) {
- ALOGE("%s: Camera %d: No AWB state provided by HAL for frame %d!",
- __FUNCTION__, mId, result->frame_number);
+ CLOGE("No AWB state provided by HAL for frame %d!",
+ result->frame_number);
} else {
new3aState.awbState =
static_cast<camera_metadata_enum_android_control_awb_state>(
@@ -951,16 +1021,16 @@
entry = captureResult.find(ANDROID_CONTROL_AF_TRIGGER_ID);
if (entry.count == 0) {
- ALOGE("%s: Camera %d: No AF trigger ID provided by HAL for frame %d!",
- __FUNCTION__, mId, result->frame_number);
+ CLOGE("No AF trigger ID provided by HAL for frame %d!",
+ result->frame_number);
} else {
afTriggerId = entry.data.i32[0];
}
entry = captureResult.find(ANDROID_CONTROL_AE_PRECAPTURE_ID);
if (entry.count == 0) {
- ALOGE("%s: Camera %d: No AE precapture trigger ID provided by HAL"
- " for frame %d!", __FUNCTION__, mId, result->frame_number);
+ CLOGE("No AE precapture trigger ID provided by HAL"
+ " for frame %d!", result->frame_number);
} else {
aeTriggerId = entry.data.i32[0];
}
@@ -979,19 +1049,21 @@
// Note: stream may be deallocated at this point, if this buffer was the
// last reference to it.
if (res != OK) {
- ALOGE("%s: Camera %d: Can't return buffer %d for frame %d to its"
- " stream:%s (%d)", __FUNCTION__, mId, i,
- result->frame_number, strerror(-res), res);
- // TODO: Report error upstream
+ SET_ERR("Can't return buffer %d for frame %d to its stream: "
+ " %s (%d)", i, result->frame_number, strerror(-res), res);
}
}
// Dispatch any 3A change events to listeners
if (listener != NULL) {
if (new3aState.aeState != cur3aState.aeState) {
+ ALOGVV("%s: AE state changed from 0x%x to 0x%x",
+ __FUNCTION__, cur3aState.aeState, new3aState.aeState);
listener->notifyAutoExposure(new3aState.aeState, aeTriggerId);
}
if (new3aState.afState != cur3aState.afState) {
+ ALOGVV("%s: AF state changed from 0x%x to 0x%x",
+ __FUNCTION__, cur3aState.afState, new3aState.afState);
listener->notifyAutoFocus(new3aState.afState, afTriggerId);
}
if (new3aState.awbState != cur3aState.awbState) {
@@ -1010,8 +1082,7 @@
}
if (msg == NULL) {
- ALOGE("%s: Camera %d: HAL sent NULL notify message!",
- __FUNCTION__, mId);
+ SET_ERR_L("HAL sent NULL notify message!");
return;
}
@@ -1034,8 +1105,8 @@
break;
}
default:
- ALOGE("%s: Camera %d: Unknown notify message from HAL: %d",
- __FUNCTION__, mId, msg->type);
+ SET_ERR_L("Unknown notify message from HAL: %d",
+ msg->type);
}
}
@@ -1051,7 +1122,8 @@
mReconfigured(false),
mDoPause(false),
mPaused(true),
- mFrameNumber(0) {
+ mFrameNumber(0),
+ mLatestRequestId(NAME_NOT_FOUND) {
}
void Camera3Device::RequestThread::configurationComplete() {
@@ -1067,6 +1139,57 @@
return OK;
}
+
+status_t Camera3Device::RequestThread::queueTrigger(
+ RequestTrigger trigger[],
+ size_t count) {
+
+ Mutex::Autolock l(mTriggerMutex);
+ status_t ret;
+
+ for (size_t i = 0; i < count; ++i) {
+ ret = queueTriggerLocked(trigger[i]);
+
+ if (ret != OK) {
+ return ret;
+ }
+ }
+
+ return OK;
+}
+
+status_t Camera3Device::RequestThread::queueTriggerLocked(
+ RequestTrigger trigger) {
+
+ uint32_t tag = trigger.metadataTag;
+ ssize_t index = mTriggerMap.indexOfKey(tag);
+
+ switch (trigger.getTagType()) {
+ case TYPE_BYTE:
+ // fall-through
+ case TYPE_INT32:
+ break;
+ default:
+ ALOGE("%s: Type not supported: 0x%x",
+ __FUNCTION__,
+ trigger.getTagType());
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * Collect only the latest trigger, since we only have 1 field
+ * in the request settings per trigger tag, and can't send more than 1
+ * trigger per request.
+ */
+ if (index != NAME_NOT_FOUND) {
+ mTriggerMap.editValueAt(index) = trigger;
+ } else {
+ mTriggerMap.add(tag, trigger);
+ }
+
+ return OK;
+}
+
status_t Camera3Device::RequestThread::setRepeatingRequests(
const RequestList &requests) {
Mutex::Autolock l(mRequestLock);
@@ -1100,6 +1223,24 @@
return OK;
}
+status_t Camera3Device::RequestThread::waitUntilRequestProcessed(
+ int32_t requestId, nsecs_t timeout) {
+ Mutex::Autolock l(mLatestRequestMutex);
+ status_t res;
+ while (mLatestRequestId != requestId) {
+ nsecs_t startTime = systemTime();
+
+ res = mLatestRequestSignal.waitRelative(mLatestRequestMutex, timeout);
+ if (res != OK) return res;
+
+ timeout -= (systemTime() - startTime);
+ }
+
+ return OK;
+}
+
+
+
bool Camera3Device::RequestThread::threadLoop() {
status_t res;
@@ -1117,16 +1258,55 @@
}
// Create request to HAL
-
camera3_capture_request_t request = camera3_capture_request_t();
+ Vector<camera3_stream_buffer_t> outputBuffers;
- if (mPrevRequest != nextRequest) {
+ // Insert any queued triggers (before metadata is locked)
+ int32_t triggerCount;
+ res = insertTriggers(nextRequest);
+ if (res < 0) {
+ SET_ERR("RequestThread: Unable to insert triggers "
+ "(capture request %d, HAL device: %s (%d)",
+ (mFrameNumber+1), strerror(-res), res);
+ cleanUpFailedRequest(request, nextRequest, outputBuffers);
+ return false;
+ }
+ triggerCount = res;
+
+ bool triggersMixedIn = (triggerCount > 0 || mPrevTriggers > 0);
+
+ // If the request is the same as last, or we had triggers last time
+ if (mPrevRequest != nextRequest || triggersMixedIn) {
+ /**
+ * The request should be presorted so accesses in HAL
+ * are O(logn). Sidenote, sorting a sorted metadata is nop.
+ */
+ nextRequest->mSettings.sort();
request.settings = nextRequest->mSettings.getAndLock();
mPrevRequest = nextRequest;
- } // else leave request.settings NULL to indicate 'reuse latest given'
+ ALOGVV("%s: Request settings are NEW", __FUNCTION__);
+
+ IF_ALOGV() {
+ camera_metadata_ro_entry_t e = camera_metadata_ro_entry_t();
+ find_camera_metadata_ro_entry(
+ request.settings,
+ ANDROID_CONTROL_AF_TRIGGER,
+ &e
+ );
+ if (e.count > 0) {
+ ALOGV("%s: Request (frame num %d) had AF trigger 0x%x",
+ __FUNCTION__,
+ mFrameNumber+1,
+ e.data.u8[0]);
+ }
+ }
+ } else {
+ // leave request.settings NULL to indicate 'reuse latest given'
+ ALOGVV("%s: Request settings are REUSED",
+ __FUNCTION__);
+ }
camera3_stream_buffer_t inputBuffer;
- Vector<camera3_stream_buffer_t> outputBuffers;
// Fill in buffers
@@ -1134,7 +1314,7 @@
request.input_buffer = &inputBuffer;
res = nextRequest->mInputStream->getBuffer(&inputBuffer);
if (res != OK) {
- ALOGE("RequestThread: Can't get input buffer, skipping request:"
+ SET_ERR("RequestThread: Can't get input buffer, skipping request:"
" %s (%d)", strerror(-res), res);
cleanUpFailedRequest(request, nextRequest, outputBuffers);
return true;
@@ -1150,7 +1330,7 @@
res = nextRequest->mOutputStreams.editItemAt(i)->
getBuffer(&outputBuffers.editItemAt(i));
if (res != OK) {
- ALOGE("RequestThread: Can't get output buffer, skipping request:"
+ SET_ERR("RequestThread: Can't get output buffer, skipping request:"
"%s (%d)", strerror(-res), res);
cleanUpFailedRequest(request, nextRequest, outputBuffers);
return true;
@@ -1160,11 +1340,12 @@
request.frame_number = mFrameNumber++;
+
// Submit request and block until ready for next one
res = mHal3Device->ops->process_capture_request(mHal3Device, &request);
if (res != OK) {
- ALOGE("RequestThread: Unable to submit capture request %d to HAL"
+ SET_ERR("RequestThread: Unable to submit capture request %d to HAL"
" device: %s (%d)", request.frame_number, strerror(-res), res);
cleanUpFailedRequest(request, nextRequest, outputBuffers);
return false;
@@ -1173,6 +1354,35 @@
if (request.settings != NULL) {
nextRequest->mSettings.unlock(request.settings);
}
+
+ // Remove any previously queued triggers (after unlock)
+ res = removeTriggers(mPrevRequest);
+ if (res != OK) {
+ SET_ERR("RequestThread: Unable to remove triggers "
+ "(capture request %d, HAL device: %s (%d)",
+ request.frame_number, strerror(-res), res);
+ return false;
+ }
+ mPrevTriggers = triggerCount;
+
+ // Read android.request.id from the request settings metadata
+ // - inform waitUntilRequestProcessed thread of a new request ID
+ {
+ Mutex::Autolock al(mLatestRequestMutex);
+
+ camera_metadata_entry_t requestIdEntry =
+ nextRequest->mSettings.find(ANDROID_REQUEST_ID);
+ if (requestIdEntry.count > 0) {
+ mLatestRequestId = requestIdEntry.data.i32[0];
+ } else {
+ ALOGW("%s: Did not have android.request.id set in the request",
+ __FUNCTION__);
+ mLatestRequestId = NAME_NOT_FOUND;
+ }
+
+ mLatestRequestSignal.signal();
+ }
+
return true;
}
@@ -1193,7 +1403,6 @@
nextRequest->mOutputStreams.editItemAt(i)->returnBuffer(
outputBuffers[i], 0);
}
- // TODO: Report error upstream
}
sp<Camera3Device::CaptureRequest>
@@ -1277,6 +1486,153 @@
return false;
}
+void Camera3Device::RequestThread::setErrorState(const char *fmt, ...) {
+ sp<Camera3Device> parent = mParent.promote();
+ if (parent != NULL) {
+ va_list args;
+ va_start(args, fmt);
+
+ parent->setErrorStateV(fmt, args);
+
+ va_end(args);
+ }
+}
+
+status_t Camera3Device::RequestThread::insertTriggers(
+ const sp<CaptureRequest> &request) {
+
+ Mutex::Autolock al(mTriggerMutex);
+
+ CameraMetadata &metadata = request->mSettings;
+ size_t count = mTriggerMap.size();
+
+ for (size_t i = 0; i < count; ++i) {
+ RequestTrigger trigger = mTriggerMap.valueAt(i);
+
+ uint32_t tag = trigger.metadataTag;
+ camera_metadata_entry entry = metadata.find(tag);
+
+ if (entry.count > 0) {
+ /**
+ * Already has an entry for this trigger in the request.
+ * Rewrite it with our requested trigger value.
+ */
+ RequestTrigger oldTrigger = trigger;
+
+ oldTrigger.entryValue = entry.data.u8[0];
+
+ mTriggerReplacedMap.add(tag, oldTrigger);
+ } else {
+ /**
+ * More typical, no trigger entry, so we just add it
+ */
+ mTriggerRemovedMap.add(tag, trigger);
+ }
+
+ status_t res;
+
+ switch (trigger.getTagType()) {
+ case TYPE_BYTE: {
+ uint8_t entryValue = static_cast<uint8_t>(trigger.entryValue);
+ res = metadata.update(tag,
+ &entryValue,
+ /*count*/1);
+ break;
+ }
+ case TYPE_INT32:
+ res = metadata.update(tag,
+ &trigger.entryValue,
+ /*count*/1);
+ break;
+ default:
+ ALOGE("%s: Type not supported: 0x%x",
+ __FUNCTION__,
+ trigger.getTagType());
+ return INVALID_OPERATION;
+ }
+
+ if (res != OK) {
+ ALOGE("%s: Failed to update request metadata with trigger tag %s"
+ ", value %d", __FUNCTION__, trigger.getTagName(),
+ trigger.entryValue);
+ return res;
+ }
+
+ ALOGV("%s: Mixed in trigger %s, value %d", __FUNCTION__,
+ trigger.getTagName(),
+ trigger.entryValue);
+ }
+
+ mTriggerMap.clear();
+
+ return count;
+}
+
+status_t Camera3Device::RequestThread::removeTriggers(
+ const sp<CaptureRequest> &request) {
+ Mutex::Autolock al(mTriggerMutex);
+
+ CameraMetadata &metadata = request->mSettings;
+
+ /**
+ * Replace all old entries with their old values.
+ */
+ for (size_t i = 0; i < mTriggerReplacedMap.size(); ++i) {
+ RequestTrigger trigger = mTriggerReplacedMap.valueAt(i);
+
+ status_t res;
+
+ uint32_t tag = trigger.metadataTag;
+ switch (trigger.getTagType()) {
+ case TYPE_BYTE: {
+ uint8_t entryValue = static_cast<uint8_t>(trigger.entryValue);
+ res = metadata.update(tag,
+ &entryValue,
+ /*count*/1);
+ break;
+ }
+ case TYPE_INT32:
+ res = metadata.update(tag,
+ &trigger.entryValue,
+ /*count*/1);
+ break;
+ default:
+ ALOGE("%s: Type not supported: 0x%x",
+ __FUNCTION__,
+ trigger.getTagType());
+ return INVALID_OPERATION;
+ }
+
+ if (res != OK) {
+ ALOGE("%s: Failed to restore request metadata with trigger tag %s"
+ ", trigger value %d", __FUNCTION__,
+ trigger.getTagName(), trigger.entryValue);
+ return res;
+ }
+ }
+ mTriggerReplacedMap.clear();
+
+ /**
+ * Remove all new entries.
+ */
+ for (size_t i = 0; i < mTriggerRemovedMap.size(); ++i) {
+ RequestTrigger trigger = mTriggerRemovedMap.valueAt(i);
+ status_t res = metadata.erase(trigger.metadataTag);
+
+ if (res != OK) {
+ ALOGE("%s: Failed to erase metadata with trigger tag %s"
+ ", trigger value %d", __FUNCTION__,
+ trigger.getTagName(), trigger.entryValue);
+ return res;
+ }
+ }
+ mTriggerRemovedMap.clear();
+
+ return OK;
+}
+
+
+
/**
* Static callback forwarding methods from HAL to instance
*/
diff --git a/services/camera/libcameraservice/Camera3Device.h b/services/camera/libcameraservice/Camera3Device.h
index 8600c6c..2e4a303 100644
--- a/services/camera/libcameraservice/Camera3Device.h
+++ b/services/camera/libcameraservice/Camera3Device.h
@@ -109,7 +109,7 @@
private:
static const nsecs_t kShutdownTimeout = 5000000000; // 5 sec
-
+ struct RequestTrigger;
Mutex mLock;
@@ -128,6 +128,9 @@
STATUS_ACTIVE
} mStatus;
+ // Tracking cause of fatal errors when in STATUS_ERROR
+ String8 mErrorCause;
+
// Mapping of stream IDs to stream instances
typedef KeyedVector<int, sp<camera3::Camera3OutputStream> > StreamSet;
@@ -173,6 +176,33 @@
status_t configureStreamsLocked();
/**
+ * Set device into an error state due to some fatal failure, and set an
+ * error message to indicate why. Only the first call's message will be
+ * used. The message is also sent to the log.
+ */
+ void setErrorState(const char *fmt, ...);
+ void setErrorStateV(const char *fmt, va_list args);
+ void setErrorStateLocked(const char *fmt, ...);
+ void setErrorStateLockedV(const char *fmt, va_list args);
+
+ struct RequestTrigger {
+ // Metadata tag number, e.g. android.control.aePrecaptureTrigger
+ uint32_t metadataTag;
+ // Metadata value, e.g. 'START' or the trigger ID
+ int32_t entryValue;
+
+ // The last part of the fully qualified path, e.g. afTrigger
+ const char *getTagName() const {
+ return get_camera_metadata_tag_name(metadataTag) ?: "NULL";
+ }
+
+ // e.g. TYPE_BYTE, TYPE_INT32, etc.
+ int getTagType() const {
+ return get_camera_metadata_tag_type(metadataTag);
+ }
+ };
+
+ /**
* Thread for managing capture request submission to HAL device.
*/
class RequestThread : public Thread {
@@ -198,6 +228,14 @@
status_t queueRequest(sp<CaptureRequest> request);
/**
+ * Queue a trigger to be dispatched with the next outgoing
+ * process_capture_request. The settings for that request only
+ * will be temporarily rewritten to add the trigger tag/value.
+ * Subsequent requests will not be rewritten (for this tag).
+ */
+ status_t queueTrigger(RequestTrigger trigger[], size_t count);
+
+ /**
* Pause/unpause the capture thread. Doesn't block, so use
* waitUntilPaused to wait until the thread is paused.
*/
@@ -210,11 +248,27 @@
*/
status_t waitUntilPaused(nsecs_t timeout);
+ /**
+ * Wait until thread processes the capture request with settings'
+ * android.request.id == requestId.
+ *
+ * Returns TIMED_OUT in case the thread does not process the request
+ * within the timeout.
+ */
+ status_t waitUntilRequestProcessed(int32_t requestId, nsecs_t timeout);
+
protected:
virtual bool threadLoop();
private:
+ status_t queueTriggerLocked(RequestTrigger trigger);
+ // Mix-in queued triggers into this request
+ int32_t insertTriggers(const sp<CaptureRequest> &request);
+ // Purge the queued triggers from this request,
+ // restoring the old field values for those tags.
+ status_t removeTriggers(const sp<CaptureRequest> &request);
+
static const nsecs_t kRequestTimeout = 50e6; // 50 ms
// Waits for a request, or returns NULL if times out.
@@ -231,6 +285,9 @@
// Pause handling
bool waitIfPaused();
+ // Relay error to parent device object setErrorState
+ void setErrorState(const char *fmt, ...);
+
wp<Camera3Device> mParent;
camera3_device_t *mHal3Device;
@@ -249,8 +306,20 @@
Condition mPausedSignal;
sp<CaptureRequest> mPrevRequest;
+ int32_t mPrevTriggers;
int32_t mFrameNumber;
+
+ Mutex mLatestRequestMutex;
+ Condition mLatestRequestSignal;
+ // android.request.id for latest process_capture_request
+ int32_t mLatestRequestId;
+
+ typedef KeyedVector<uint32_t/*tag*/, RequestTrigger> TriggerMap;
+ Mutex mTriggerMutex;
+ TriggerMap mTriggerMap;
+ TriggerMap mTriggerRemovedMap;
+ TriggerMap mTriggerReplacedMap;
};
sp<RequestThread> mRequestThread;
diff --git a/services/camera/libcameraservice/camera2/CaptureSequencer.cpp b/services/camera/libcameraservice/camera2/CaptureSequencer.cpp
index 1880912..ee03329 100644
--- a/services/camera/libcameraservice/camera2/CaptureSequencer.cpp
+++ b/services/camera/libcameraservice/camera2/CaptureSequencer.cpp
@@ -270,6 +270,9 @@
processor->clearZslQueue();
}
+ /**
+ * Fire the jpegCallback in Camera#takePicture(..., jpegCallback)
+ */
if (mCaptureBuffer != 0 && res == OK) {
Camera2Client::SharedCameraCallbacks::Lock
l(client->mSharedCameraCallbacks);
@@ -367,6 +370,8 @@
CaptureSequencer::CaptureState CaptureSequencer::manageStandardStart(
sp<Camera2Client> &client) {
ATRACE_CALL();
+
+ // Get the onFrameAvailable callback when the requestID == mCaptureId
client->registerFrameListener(mCaptureId, mCaptureId + 1,
this);
{
@@ -426,6 +431,13 @@
SharedParameters::Lock l(client->getParameters());
Vector<uint8_t> outputStreams;
+ /**
+ * Set up output streams in the request
+ * - preview
+ * - capture/jpeg
+ * - callback (if preview callbacks enabled)
+ * - recording (if recording enabled)
+ */
outputStreams.push(client->getPreviewStreamId());
outputStreams.push(client->getCaptureStreamId());
@@ -454,6 +466,7 @@
return DONE;
}
+ // Create a capture copy since CameraDeviceBase#capture takes ownership
CameraMetadata captureCopy = mCaptureRequest;
if (captureCopy.entryCount() == 0) {
ALOGE("%s: Camera %d: Unable to copy capture request for HAL device",
@@ -461,7 +474,12 @@
return DONE;
}
+ /**
+ * Clear the streaming request for still-capture pictures
+ * (as opposed to i.e. video snapshots)
+ */
if (l.mParameters.state == Parameters::STILL_CAPTURE) {
+ // API definition of takePicture() - stop preview before taking pic
res = client->stopStream();
if (res != OK) {
ALOGE("%s: Camera %d: Unable to stop preview for still capture: "
@@ -488,6 +506,8 @@
status_t res;
ATRACE_CALL();
Mutex::Autolock l(mInputMutex);
+
+ // Wait for new metadata result (mNewFrame)
while (!mNewFrameReceived) {
res = mNewFrameSignal.waitRelative(mInputMutex, kWaitDuration);
if (res == TIMED_OUT) {
@@ -495,12 +515,17 @@
break;
}
}
+
+ // Approximation of the shutter being closed
+ // - TODO: use the hal3 exposure callback in Camera3Device instead
if (mNewFrameReceived && !mShutterNotified) {
SharedParameters::Lock l(client->getParameters());
/* warning: this also locks a SharedCameraCallbacks */
shutterNotifyLocked(l.mParameters, client, mMsgType);
mShutterNotified = true;
}
+
+ // Wait until jpeg was captured by JpegProcessor
while (mNewFrameReceived && !mNewCaptureReceived) {
res = mNewCaptureSignal.waitRelative(mInputMutex, kWaitDuration);
if (res == TIMED_OUT) {
@@ -524,7 +549,9 @@
}
if (entry.data.i64[0] != mCaptureTimestamp) {
ALOGW("Mismatched capture timestamps: Metadata frame %lld,"
- " captured buffer %lld", entry.data.i64[0], mCaptureTimestamp);
+ " captured buffer %lld",
+ entry.data.i64[0],
+ mCaptureTimestamp);
}
client->removeFrameListener(mCaptureId, mCaptureId + 1, this);
diff --git a/services/camera/libcameraservice/camera2/JpegProcessor.h b/services/camera/libcameraservice/camera2/JpegProcessor.h
index 2283f28..74f4738 100644
--- a/services/camera/libcameraservice/camera2/JpegProcessor.h
+++ b/services/camera/libcameraservice/camera2/JpegProcessor.h
@@ -44,6 +44,7 @@
JpegProcessor(wp<Camera2Client> client, wp<CaptureSequencer> sequencer);
~JpegProcessor();
+ // CpuConsumer listener implementation
void onFrameAvailable();
status_t updateStream(const Parameters ¶ms);
diff --git a/services/camera/libcameraservice/camera3/Camera3OutputStream.cpp b/services/camera/libcameraservice/camera3/Camera3OutputStream.cpp
index d07ae94..276b940 100644
--- a/services/camera/libcameraservice/camera3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/camera3/Camera3OutputStream.cpp
@@ -57,7 +57,14 @@
uint32_t width, uint32_t height, size_t maxSize, int format) :
Camera3Stream(id, CAMERA3_STREAM_OUTPUT,
width, height, maxSize, format),
- mConsumer(consumer) {
+ mConsumer(consumer),
+ mTransform(0),
+ mTotalBufferCount(0),
+ mDequeuedBufferCount(0),
+ mFrameCount(0),
+ mLastTimestamp(0) {
+
+ mCombinedFence = new Fence();
if (format != HAL_PIXEL_FORMAT_BLOB) {
ALOGE("%s: Bad format for size-only stream: %d", __FUNCTION__,
diff --git a/services/medialog/Android.mk b/services/medialog/Android.mk
index 559b1ed..08006c8 100644
--- a/services/medialog/Android.mk
+++ b/services/medialog/Android.mk
@@ -4,7 +4,7 @@
LOCAL_SRC_FILES := MediaLogService.cpp
-LOCAL_SHARED_LIBRARIES := libmedia libbinder libutils libnbaio
+LOCAL_SHARED_LIBRARIES := libmedia libbinder libutils liblog libnbaio
LOCAL_MODULE:= libmedialogservice