Fix potential NULL dereference in Visualizer effect am: 874f9e0b8e am: f060479b9a
am: bcc7230bb9
Change-Id: If5d6fefb276341472d827bac3f349561f7d1d990
diff --git a/CleanSpec.mk b/CleanSpec.mk
index e6d9ebf..d0890fe 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -47,6 +47,19 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/libmedia_native.so)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/libmedia_native.so)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libmedia_native.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudioflinger_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudioflinger.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicy_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudiopolicy.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicy_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudiopolicy.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicyservice_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicymanager_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudiopolicyservice.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudiopolicymanager.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicyservice_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicymanager_intermediates)
+
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/camera/Android.mk b/camera/Android.mk
index 5cedab0..da5ac59 100644
--- a/camera/Android.mk
+++ b/camera/Android.mk
@@ -1,3 +1,17 @@
+# Copyright 2010 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.
+
CAMERA_CLIENT_LOCAL_PATH:= $(call my-dir)
include $(call all-subdir-makefiles)
include $(CLEAR_VARS)
@@ -8,6 +22,7 @@
Camera.cpp \
CameraMetadata.cpp \
CameraParameters.cpp \
+ CaptureResult.cpp \
CameraParameters2.cpp \
ICamera.cpp \
ICameraClient.cpp \
@@ -22,6 +37,8 @@
camera2/CaptureRequest.cpp \
ProCamera.cpp \
CameraBase.cpp \
+ CameraUtils.cpp \
+ VendorTagDescriptor.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
@@ -35,6 +52,7 @@
LOCAL_C_INCLUDES += \
system/media/camera/include \
+ system/media/private/camera/include \
LOCAL_MODULE:= libcamera_client
diff --git a/camera/Camera.cpp b/camera/Camera.cpp
index 22199fa..85f44f0 100644
--- a/camera/Camera.cpp
+++ b/camera/Camera.cpp
@@ -77,6 +77,32 @@
return CameraBaseT::connect(cameraId, clientPackageName, clientUid);
}
+status_t Camera::connectLegacy(int cameraId, int halVersion,
+ const String16& clientPackageName,
+ int clientUid,
+ sp<Camera>& camera)
+{
+ ALOGV("%s: connect legacy camera device", __FUNCTION__);
+ sp<Camera> c = new Camera(cameraId);
+ sp<ICameraClient> cl = c;
+ status_t status = NO_ERROR;
+ const sp<ICameraService>& cs = CameraBaseT::getCameraService();
+
+ if (cs != 0) {
+ status = cs.get()->connectLegacy(cl, cameraId, halVersion, clientPackageName,
+ clientUid, /*out*/c->mCamera);
+ }
+ if (status == OK && c->mCamera != 0) {
+ c->mCamera->asBinder()->linkToDeath(c);
+ c->mStatus = NO_ERROR;
+ camera = c;
+ } else {
+ ALOGW("An error occurred while connecting to camera: %d", cameraId);
+ c.clear();
+ }
+ return status;
+}
+
status_t Camera::reconnect()
{
ALOGV("reconnect");
diff --git a/camera/CameraBase.cpp b/camera/CameraBase.cpp
index 55376b0..04694cd 100644
--- a/camera/CameraBase.cpp
+++ b/camera/CameraBase.cpp
@@ -49,7 +49,7 @@
DeathNotifier() {
}
- virtual void binderDied(const wp<IBinder>& who) {
+ virtual void binderDied(const wp<IBinder>& /*who*/) {
ALOGV("binderDied");
Mutex::Autolock _l(gLock);
gCameraService.clear();
@@ -153,7 +153,7 @@
}
template <typename TCam, typename TCamTraits>
-void CameraBase<TCam, TCamTraits>::binderDied(const wp<IBinder>& who) {
+void CameraBase<TCam, TCamTraits>::binderDied(const wp<IBinder>& /*who*/) {
ALOGW("mediaserver's remote binder Camera object died");
notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_SERVER_DIED, /*ext2*/0);
}
diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp
index 7765914..043437f 100644
--- a/camera/CameraMetadata.cpp
+++ b/camera/CameraMetadata.cpp
@@ -25,6 +25,9 @@
namespace android {
+#define ALIGN_TO(val, alignment) \
+ (((uintptr_t)(val) + ((alignment) - 1)) & ~((alignment) - 1))
+
typedef Parcel::WritableBlob WritableBlob;
typedef Parcel::ReadableBlob ReadableBlob;
@@ -270,7 +273,8 @@
if ( (res = checkType(tag, TYPE_BYTE)) != OK) {
return res;
}
- return updateImpl(tag, (const void*)string.string(), string.size());
+ // string.size() doesn't count the null termination character.
+ return updateImpl(tag, (const void*)string.string(), string.size() + 1);
}
status_t CameraMetadata::updateImpl(uint32_t tag, const void *data,
@@ -431,40 +435,70 @@
*out = NULL;
}
- // arg0 = metadataSize (int32)
- int32_t metadataSizeTmp = -1;
- if ((err = data.readInt32(&metadataSizeTmp)) != OK) {
+ // See CameraMetadata::writeToParcel for parcel data layout diagram and explanation.
+ // arg0 = blobSize (int32)
+ int32_t blobSizeTmp = -1;
+ if ((err = data.readInt32(&blobSizeTmp)) != OK) {
ALOGE("%s: Failed to read metadata size (error %d %s)",
__FUNCTION__, err, strerror(-err));
return err;
}
- const size_t metadataSize = static_cast<size_t>(metadataSizeTmp);
+ const size_t blobSize = static_cast<size_t>(blobSizeTmp);
+ const size_t alignment = get_camera_metadata_alignment();
- if (metadataSize == 0) {
+ // Special case: zero blob size means zero sized (NULL) metadata.
+ if (blobSize == 0) {
ALOGV("%s: Read 0-sized metadata", __FUNCTION__);
return OK;
}
- // NOTE: this doesn't make sense to me. shouldnt the blob
+ if (blobSize <= alignment) {
+ ALOGE("%s: metadata blob is malformed, blobSize(%zu) should be larger than alignment(%zu)",
+ __FUNCTION__, blobSize, alignment);
+ return BAD_VALUE;
+ }
+
+ const size_t metadataSize = blobSize - alignment;
+
+ // NOTE: this doesn't make sense to me. shouldn't the blob
// know how big it is? why do we have to specify the size
// to Parcel::readBlob ?
-
ReadableBlob blob;
// arg1 = metadata (blob)
do {
- if ((err = data.readBlob(metadataSize, &blob)) != OK) {
- ALOGE("%s: Failed to read metadata blob (sized %d). Possible "
+ if ((err = data.readBlob(blobSize, &blob)) != OK) {
+ ALOGE("%s: Failed to read metadata blob (sized %zu). Possible "
" serialization bug. Error %d %s",
- __FUNCTION__, metadataSize, err, strerror(-err));
+ __FUNCTION__, blobSize, err, strerror(-err));
break;
}
- const camera_metadata_t* tmp =
- reinterpret_cast<const camera_metadata_t*>(blob.data());
+ // arg2 = offset (blob)
+ // Must be after blob since we don't know offset until after writeBlob.
+ int32_t offsetTmp;
+ if ((err = data.readInt32(&offsetTmp)) != OK) {
+ ALOGE("%s: Failed to read metadata offsetTmp (error %d %s)",
+ __FUNCTION__, err, strerror(-err));
+ break;
+ }
+ const size_t offset = static_cast<size_t>(offsetTmp);
+ if (offset >= alignment) {
+ ALOGE("%s: metadata offset(%zu) should be less than alignment(%zu)",
+ __FUNCTION__, blobSize, alignment);
+ err = BAD_VALUE;
+ break;
+ }
+
+ const uintptr_t metadataStart = reinterpret_cast<uintptr_t>(blob.data()) + offset;
+ const camera_metadata_t* tmp =
+ reinterpret_cast<const camera_metadata_t*>(metadataStart);
+ ALOGV("%s: alignment is: %zu, metadata start: %p, offset: %zu",
+ __FUNCTION__, alignment, tmp, offset);
metadata = allocate_copy_camera_metadata_checked(tmp, metadataSize);
if (metadata == NULL) {
// We consider that allocation only fails if the validation
// also failed, therefore the readFromParcel was a failure.
+ ALOGE("%s: metadata allocation and copy failed", __FUNCTION__);
err = BAD_VALUE;
}
} while(0);
@@ -485,38 +519,80 @@
const camera_metadata_t* metadata) {
status_t res = OK;
- // arg0 = metadataSize (int32)
+ /**
+ * Below is the camera metadata parcel layout:
+ *
+ * |--------------------------------------------|
+ * | arg0: blobSize |
+ * | (length = 4) |
+ * |--------------------------------------------|<--Skip the rest if blobSize == 0.
+ * | |
+ * | |
+ * | arg1: blob |
+ * | (length = variable, see arg1 layout below) |
+ * | |
+ * | |
+ * |--------------------------------------------|
+ * | arg2: offset |
+ * | (length = 4) |
+ * |--------------------------------------------|
+ */
+ // arg0 = blobSize (int32)
if (metadata == NULL) {
+ // Write zero blobSize for null metadata.
return data.writeInt32(0);
}
+ /**
+ * Always make the blob size sufficiently larger, as we need put alignment
+ * padding and metadata into the blob. Since we don't know the alignment
+ * offset before writeBlob. Then write the metadata to aligned offset.
+ */
const size_t metadataSize = get_camera_metadata_compact_size(metadata);
- res = data.writeInt32(static_cast<int32_t>(metadataSize));
+ const size_t alignment = get_camera_metadata_alignment();
+ const size_t blobSize = metadataSize + alignment;
+ res = data.writeInt32(static_cast<int32_t>(blobSize));
if (res != OK) {
return res;
}
- // arg1 = metadata (blob)
+ size_t offset = 0;
+ /**
+ * arg1 = metadata (blob).
+ *
+ * The blob size is the sum of front padding size, metadata size and back padding
+ * size, which is equal to metadataSize + alignment.
+ *
+ * The blob layout is:
+ * |------------------------------------|<----Start address of the blob (unaligned).
+ * | front padding |
+ * | (size = offset) |
+ * |------------------------------------|<----Aligned start address of metadata.
+ * | |
+ * | |
+ * | metadata |
+ * | (size = metadataSize) |
+ * | |
+ * | |
+ * |------------------------------------|
+ * | back padding |
+ * | (size = alignment - offset) |
+ * |------------------------------------|<----End address of blob.
+ * (Blob start address + blob size).
+ */
WritableBlob blob;
do {
- res = data.writeBlob(metadataSize, &blob);
+ res = data.writeBlob(blobSize, &blob);
if (res != OK) {
break;
}
- copy_camera_metadata(blob.data(), metadataSize, metadata);
-
- IF_ALOGV() {
- if (validate_camera_metadata_structure(
- (const camera_metadata_t*)blob.data(),
- &metadataSize) != OK) {
- ALOGV("%s: Failed to validate metadata %p after writing blob",
- __FUNCTION__, blob.data());
- } else {
- ALOGV("%s: Metadata written to blob. Validation success",
- __FUNCTION__);
- }
- }
+ const uintptr_t metadataStart = ALIGN_TO(blob.data(), alignment);
+ offset = metadataStart - reinterpret_cast<uintptr_t>(blob.data());
+ ALOGV("%s: alignment is: %zu, metadata start: %p, offset: %zu",
+ __FUNCTION__, alignment,
+ reinterpret_cast<const void *>(metadataStart), offset);
+ copy_camera_metadata(reinterpret_cast<void*>(metadataStart), metadataSize, metadata);
// Not too big of a problem since receiving side does hard validation
// Don't check the size since the compact size could be larger
@@ -528,6 +604,9 @@
} while(false);
blob.release();
+ // arg2 = offset (int32)
+ res = data.writeInt32(static_cast<int32_t>(offset));
+
return res;
}
diff --git a/camera/CameraParameters.cpp b/camera/CameraParameters.cpp
index c51f265..25d632d 100644
--- a/camera/CameraParameters.cpp
+++ b/camera/CameraParameters.cpp
@@ -21,6 +21,7 @@
#include <string.h>
#include <stdlib.h>
#include <camera/CameraParameters.h>
+#include <system/graphics.h>
namespace android {
// Parameter keys to communicate between camera application and driver.
@@ -456,7 +457,7 @@
void CameraParameters::dump() const
{
- ALOGD("dump: mMap.size = %d", mMap.size());
+ ALOGD("dump: mMap.size = %zu", mMap.size());
for (size_t i = 0; i < mMap.size(); i++) {
String8 k, v;
k = mMap.keyAt(i);
@@ -465,12 +466,12 @@
}
}
-status_t CameraParameters::dump(int fd, const Vector<String16>& args) const
+status_t CameraParameters::dump(int fd, const Vector<String16>& /*args*/) const
{
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
- snprintf(buffer, 255, "CameraParameters::dump: mMap.size = %d\n", mMap.size());
+ snprintf(buffer, 255, "CameraParameters::dump: mMap.size = %zu\n", mMap.size());
result.append(buffer);
for (size_t i = 0; i < mMap.size(); i++) {
String8 k, v;
@@ -483,4 +484,45 @@
return NO_ERROR;
}
+void CameraParameters::getSupportedPreviewFormats(Vector<int>& formats) const {
+ const char* supportedPreviewFormats =
+ get(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS);
+
+ String8 fmtStr(supportedPreviewFormats);
+ char* prevFmts = fmtStr.lockBuffer(fmtStr.size());
+
+ char* savePtr;
+ char* fmt = strtok_r(prevFmts, ",", &savePtr);
+ while (fmt) {
+ int actual = previewFormatToEnum(fmt);
+ if (actual != -1) {
+ formats.add(actual);
+ }
+ fmt = strtok_r(NULL, ",", &savePtr);
+ }
+ fmtStr.unlockBuffer(fmtStr.size());
+}
+
+
+int CameraParameters::previewFormatToEnum(const char* format) {
+ return
+ !format ?
+ HAL_PIXEL_FORMAT_YCrCb_420_SP :
+ !strcmp(format, PIXEL_FORMAT_YUV422SP) ?
+ HAL_PIXEL_FORMAT_YCbCr_422_SP : // NV16
+ !strcmp(format, PIXEL_FORMAT_YUV420SP) ?
+ HAL_PIXEL_FORMAT_YCrCb_420_SP : // NV21
+ !strcmp(format, PIXEL_FORMAT_YUV422I) ?
+ HAL_PIXEL_FORMAT_YCbCr_422_I : // YUY2
+ !strcmp(format, PIXEL_FORMAT_YUV420P) ?
+ HAL_PIXEL_FORMAT_YV12 : // YV12
+ !strcmp(format, PIXEL_FORMAT_RGB565) ?
+ HAL_PIXEL_FORMAT_RGB_565 : // RGB565
+ !strcmp(format, PIXEL_FORMAT_RGBA8888) ?
+ HAL_PIXEL_FORMAT_RGBA_8888 : // RGB8888
+ !strcmp(format, PIXEL_FORMAT_BAYER_RGGB) ?
+ HAL_PIXEL_FORMAT_RAW_SENSOR : // Raw sensor data
+ -1;
+}
+
}; // namespace android
diff --git a/camera/CameraParameters2.cpp b/camera/CameraParameters2.cpp
index eac79e1..378afeb 100644
--- a/camera/CameraParameters2.cpp
+++ b/camera/CameraParameters2.cpp
@@ -362,6 +362,7 @@
status_t CameraParameters2::dump(int fd, const Vector<String16>& args) const
{
+ (void)args;
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
diff --git a/camera/CameraUtils.cpp b/camera/CameraUtils.cpp
new file mode 100644
index 0000000..04244ac
--- /dev/null
+++ b/camera/CameraUtils.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2014 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_TAG "CameraUtils"
+//#define LOG_NDEBUG 0
+
+#include <camera/CameraUtils.h>
+
+#include <system/window.h>
+#include <system/graphics.h>
+
+#include <utils/Log.h>
+
+namespace android {
+
+status_t CameraUtils::getRotationTransform(const CameraMetadata& staticInfo,
+ /*out*/int32_t* transform) {
+ ALOGV("%s", __FUNCTION__);
+
+ if (transform == NULL) {
+ ALOGW("%s: null transform", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ *transform = 0;
+
+ camera_metadata_ro_entry_t entry = staticInfo.find(ANDROID_SENSOR_ORIENTATION);
+ if (entry.count == 0) {
+ ALOGE("%s: Can't find android.sensor.orientation in static metadata!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ camera_metadata_ro_entry_t entryFacing = staticInfo.find(ANDROID_LENS_FACING);
+ if (entry.count == 0) {
+ ALOGE("%s: Can't find android.lens.facing in static metadata!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ int32_t& flags = *transform;
+
+ bool mirror = (entryFacing.data.u8[0] == ANDROID_LENS_FACING_FRONT);
+ int orientation = entry.data.i32[0];
+ if (!mirror) {
+ switch (orientation) {
+ case 0:
+ flags = 0;
+ break;
+ case 90:
+ flags = NATIVE_WINDOW_TRANSFORM_ROT_90;
+ break;
+ case 180:
+ flags = NATIVE_WINDOW_TRANSFORM_ROT_180;
+ break;
+ case 270:
+ flags = NATIVE_WINDOW_TRANSFORM_ROT_270;
+ break;
+ default:
+ ALOGE("%s: Invalid HAL android.sensor.orientation value: %d",
+ __FUNCTION__, orientation);
+ return INVALID_OPERATION;
+ }
+ } else {
+ // Front camera needs to be horizontally flipped for mirror-like behavior.
+ // Note: Flips are applied before rotates; using XOR here as some of these flags are
+ // composed in terms of other flip/rotation flags, and are not bitwise-ORable.
+ switch (orientation) {
+ case 0:
+ flags = NATIVE_WINDOW_TRANSFORM_FLIP_H;
+ break;
+ case 90:
+ flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^
+ NATIVE_WINDOW_TRANSFORM_ROT_270;
+ break;
+ case 180:
+ flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^
+ NATIVE_WINDOW_TRANSFORM_ROT_180;
+ break;
+ case 270:
+ flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^
+ NATIVE_WINDOW_TRANSFORM_ROT_90;
+
+ break;
+ default:
+ ALOGE("%s: Invalid HAL android.sensor.orientation value: %d",
+ __FUNCTION__, orientation);
+ return INVALID_OPERATION;
+ }
+
+ }
+
+ /**
+ * This magic flag makes surfaceflinger un-rotate the buffers
+ * to counter the extra global device UI rotation whenever the user
+ * physically rotates the device.
+ *
+ * By doing this, the camera buffer always ends up aligned
+ * with the physical camera for a "see through" effect.
+ *
+ * In essence, the buffer only gets rotated during preview use-cases.
+ * The user is still responsible to re-create streams of the proper
+ * aspect ratio, or the preview will end up looking non-uniformly
+ * stretched.
+ */
+ flags |= NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
+
+ ALOGV("%s: final transform = 0x%x", __FUNCTION__, flags);
+
+ return OK;
+}
+
+
+} /* namespace android */
diff --git a/camera/CaptureResult.cpp b/camera/CaptureResult.cpp
new file mode 100644
index 0000000..4e36160
--- /dev/null
+++ b/camera/CaptureResult.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 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_TAG "Camera-CaptureResult"
+#include <utils/Log.h>
+
+#include <camera/CaptureResult.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+bool CaptureResultExtras::isValid() {
+ return requestId >= 0;
+}
+
+status_t CaptureResultExtras::readFromParcel(Parcel *parcel) {
+ if (parcel == NULL) {
+ ALOGE("%s: Null parcel", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ parcel->readInt32(&requestId);
+ parcel->readInt32(&burstId);
+ parcel->readInt32(&afTriggerId);
+ parcel->readInt32(&precaptureTriggerId);
+ parcel->readInt64(&frameNumber);
+ parcel->readInt32(&partialResultCount);
+
+ return OK;
+}
+
+status_t CaptureResultExtras::writeToParcel(Parcel *parcel) const {
+ if (parcel == NULL) {
+ ALOGE("%s: Null parcel", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ parcel->writeInt32(requestId);
+ parcel->writeInt32(burstId);
+ parcel->writeInt32(afTriggerId);
+ parcel->writeInt32(precaptureTriggerId);
+ parcel->writeInt64(frameNumber);
+ parcel->writeInt32(partialResultCount);
+
+ return OK;
+}
+
+CaptureResult::CaptureResult() :
+ mMetadata(), mResultExtras() {
+}
+
+CaptureResult::CaptureResult(const CaptureResult &otherResult) {
+ mResultExtras = otherResult.mResultExtras;
+ mMetadata = otherResult.mMetadata;
+}
+
+status_t CaptureResult::readFromParcel(Parcel *parcel) {
+
+ ALOGV("%s: parcel = %p", __FUNCTION__, parcel);
+
+ if (parcel == NULL) {
+ ALOGE("%s: parcel is null", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ mMetadata.clear();
+
+ status_t res = OK;
+ res = mMetadata.readFromParcel(parcel);
+ if (res != OK) {
+ ALOGE("%s: Failed to read metadata from parcel.",
+ __FUNCTION__);
+ return res;
+ }
+ ALOGV("%s: Read metadata from parcel", __FUNCTION__);
+
+ res = mResultExtras.readFromParcel(parcel);
+ if (res != OK) {
+ ALOGE("%s: Failed to read result extras from parcel.",
+ __FUNCTION__);
+ return res;
+ }
+ ALOGV("%s: Read result extras from parcel", __FUNCTION__);
+
+ return OK;
+}
+
+status_t CaptureResult::writeToParcel(Parcel *parcel) const {
+
+ ALOGV("%s: parcel = %p", __FUNCTION__, parcel);
+
+ if (parcel == NULL) {
+ ALOGE("%s: parcel is null", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ status_t res;
+
+ res = mMetadata.writeToParcel(parcel);
+ if (res != OK) {
+ ALOGE("%s: Failed to write metadata to parcel", __FUNCTION__);
+ return res;
+ }
+ ALOGV("%s: Wrote metadata to parcel", __FUNCTION__);
+
+ res = mResultExtras.writeToParcel(parcel);
+ if (res != OK) {
+ ALOGE("%s: Failed to write result extras to parcel", __FUNCTION__);
+ return res;
+ }
+ ALOGV("%s: Wrote result extras to parcel", __FUNCTION__);
+
+ return OK;
+}
+
+}
diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp
index 5fc89fb..5485205 100644
--- a/camera/ICameraService.cpp
+++ b/camera/ICameraService.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "BpCameraService"
#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/String16.h>
#include <stdint.h>
#include <sys/types.h>
@@ -34,6 +36,7 @@
#include <camera/camera2/ICameraDeviceUser.h>
#include <camera/camera2/ICameraDeviceCallbacks.h>
#include <camera/CameraMetadata.h>
+#include <camera/VendorTagDescriptor.h>
namespace android {
@@ -143,6 +146,24 @@
return result;
}
+ // Get enumeration and description of vendor tags for camera
+ virtual status_t getCameraVendorTagDescriptor(/*out*/sp<VendorTagDescriptor>& desc) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
+ remote()->transact(BnCameraService::GET_CAMERA_VENDOR_TAG_DESCRIPTOR, data, &reply);
+
+ if (readExceptionCode(reply)) return -EPROTO;
+ status_t result = reply.readInt32();
+
+ if (reply.readInt32() != 0) {
+ sp<VendorTagDescriptor> d;
+ if (VendorTagDescriptor::createFromParcel(&reply, /*out*/d) == OK) {
+ desc = d;
+ }
+ }
+ return result;
+ }
+
// connect to camera service (android.hardware.Camera)
virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId,
const String16 &clientPackageName, int clientUid,
@@ -165,6 +186,29 @@
return status;
}
+ // connect to camera service (android.hardware.Camera)
+ virtual status_t connectLegacy(const sp<ICameraClient>& cameraClient, int cameraId,
+ int halVersion,
+ const String16 &clientPackageName, int clientUid,
+ /*out*/sp<ICamera>& device)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
+ data.writeStrongBinder(cameraClient->asBinder());
+ data.writeInt32(cameraId);
+ data.writeInt32(halVersion);
+ data.writeString16(clientPackageName);
+ data.writeInt32(clientUid);
+ remote()->transact(BnCameraService::CONNECT_LEGACY, data, &reply);
+
+ if (readExceptionCode(reply)) return -EPROTO;
+ status_t status = reply.readInt32();
+ if (reply.readInt32() != 0) {
+ device = interface_cast<ICamera>(reply.readStrongBinder());
+ }
+ return status;
+ }
+
// connect to camera service (pro client)
virtual status_t connectPro(const sp<IProCameraCallbacks>& cameraCb, int cameraId,
const String16 &clientPackageName, int clientUid,
@@ -233,6 +277,41 @@
if (readExceptionCode(reply)) return -EPROTO;
return reply.readInt32();
}
+
+ virtual status_t getLegacyParameters(int cameraId, String16* parameters) {
+ if (parameters == NULL) {
+ ALOGE("%s: parameters must not be null", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ Parcel data, reply;
+
+ data.writeInt32(cameraId);
+ remote()->transact(BnCameraService::GET_LEGACY_PARAMETERS, data, &reply);
+ if (readExceptionCode(reply)) return -EPROTO;
+
+ status_t res = data.readInt32();
+ int32_t length = data.readInt32(); // -1 means null
+ if (length > 0) {
+ *parameters = data.readString16();
+ } else {
+ *parameters = String16();
+ }
+
+ return res;
+ }
+
+ virtual status_t supportsCameraApi(int cameraId, int apiVersion) {
+ Parcel data, reply;
+
+ data.writeInt32(cameraId);
+ data.writeInt32(apiVersion);
+ remote()->transact(BnCameraService::SUPPORTS_CAMERA_API, data, &reply);
+ if (readExceptionCode(reply)) return -EPROTO;
+
+ status_t res = data.readInt32();
+ return res;
+ }
};
IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService");
@@ -275,6 +354,22 @@
info.writeToParcel(reply);
return NO_ERROR;
} break;
+ case GET_CAMERA_VENDOR_TAG_DESCRIPTOR: {
+ CHECK_INTERFACE(ICameraService, data, reply);
+ sp<VendorTagDescriptor> d;
+ status_t result = getCameraVendorTagDescriptor(d);
+ reply->writeNoException();
+ reply->writeInt32(result);
+
+ // out-variables are after exception and return value
+ if (d == NULL) {
+ reply->writeInt32(0);
+ } else {
+ reply->writeInt32(1); // means the parcelable is included
+ d->writeToParcel(reply);
+ }
+ return NO_ERROR;
+ } break;
case CONNECT: {
CHECK_INTERFACE(ICameraService, data, reply);
sp<ICameraClient> cameraClient =
@@ -284,7 +379,7 @@
int32_t clientUid = data.readInt32();
sp<ICamera> camera;
status_t status = connect(cameraClient, cameraId,
- clientName, clientUid, /*out*/ camera);
+ clientName, clientUid, /*out*/camera);
reply->writeNoException();
reply->writeInt32(status);
if (camera != NULL) {
@@ -304,7 +399,7 @@
int32_t clientUid = data.readInt32();
sp<IProCameraUser> camera;
status_t status = connectPro(cameraClient, cameraId,
- clientName, clientUid, /*out*/ camera);
+ clientName, clientUid, /*out*/camera);
reply->writeNoException();
reply->writeInt32(status);
if (camera != NULL) {
@@ -324,7 +419,7 @@
int32_t clientUid = data.readInt32();
sp<ICameraDeviceUser> camera;
status_t status = connectDevice(cameraClient, cameraId,
- clientName, clientUid, /*out*/ camera);
+ clientName, clientUid, /*out*/camera);
reply->writeNoException();
reply->writeInt32(status);
if (camera != NULL) {
@@ -351,6 +446,50 @@
reply->writeInt32(removeListener(listener));
return NO_ERROR;
} break;
+ case GET_LEGACY_PARAMETERS: {
+ CHECK_INTERFACE(ICameraService, data, reply);
+ int cameraId = data.readInt32();
+ String16 parameters;
+
+ reply->writeNoException();
+ // return value
+ reply->writeInt32(getLegacyParameters(cameraId, ¶meters));
+ // out parameters
+ reply->writeInt32(1); // parameters is always available
+ reply->writeString16(parameters);
+ return NO_ERROR;
+ } break;
+ case SUPPORTS_CAMERA_API: {
+ CHECK_INTERFACE(ICameraService, data, reply);
+ int cameraId = data.readInt32();
+ int apiVersion = data.readInt32();
+
+ reply->writeNoException();
+ // return value
+ reply->writeInt32(supportsCameraApi(cameraId, apiVersion));
+ return NO_ERROR;
+ } break;
+ case CONNECT_LEGACY: {
+ CHECK_INTERFACE(ICameraService, data, reply);
+ sp<ICameraClient> cameraClient =
+ interface_cast<ICameraClient>(data.readStrongBinder());
+ int32_t cameraId = data.readInt32();
+ int32_t halVersion = data.readInt32();
+ const String16 clientName = data.readString16();
+ int32_t clientUid = data.readInt32();
+ sp<ICamera> camera;
+ status_t status = connectLegacy(cameraClient, cameraId, halVersion,
+ clientName, clientUid, /*out*/camera);
+ reply->writeNoException();
+ reply->writeInt32(status);
+ if (camera != NULL) {
+ reply->writeInt32(1);
+ reply->writeStrongBinder(camera->asBinder());
+ } else {
+ reply->writeInt32(0);
+ }
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/camera/ProCamera.cpp b/camera/ProCamera.cpp
index ba5a48c..48f8e8e 100644
--- a/camera/ProCamera.cpp
+++ b/camera/ProCamera.cpp
@@ -249,11 +249,14 @@
sp <IProCameraUser> c = mCamera;
if (c == 0) return NO_INIT;
- sp<BufferQueue> bq = new BufferQueue();
- sp<CpuConsumer> cc = new CpuConsumer(bq, heapCount/*, synchronousMode*/);
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+ sp<CpuConsumer> cc = new CpuConsumer(consumer, heapCount
+ /*, synchronousMode*/);
cc->setName(String8("ProCamera::mCpuConsumer"));
- sp<Surface> stc = new Surface(bq);
+ sp<Surface> stc = new Surface(producer);
status_t s = createStream(width, height, format,
stc->getIGraphicBufferProducer(),
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
new file mode 100644
index 0000000..0dda6b6
--- /dev/null
+++ b/camera/VendorTagDescriptor.cpp
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2014 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_TAG "VendorTagDescriptor"
+
+#include <binder/Parcel.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+#include <utils/Vector.h>
+#include <utils/SortedVector.h>
+#include <system/camera_metadata.h>
+#include <camera_metadata_hidden.h>
+
+#include "camera/VendorTagDescriptor.h"
+
+#include <stdio.h>
+#include <string.h>
+
+namespace android {
+
+extern "C" {
+
+static int vendor_tag_descriptor_get_tag_count(const vendor_tag_ops_t* v);
+static void vendor_tag_descriptor_get_all_tags(const vendor_tag_ops_t* v, uint32_t* tagArray);
+static const char* vendor_tag_descriptor_get_section_name(const vendor_tag_ops_t* v, uint32_t tag);
+static const char* vendor_tag_descriptor_get_tag_name(const vendor_tag_ops_t* v, uint32_t tag);
+static int vendor_tag_descriptor_get_tag_type(const vendor_tag_ops_t* v, uint32_t tag);
+
+} /* extern "C" */
+
+
+static Mutex sLock;
+static sp<VendorTagDescriptor> sGlobalVendorTagDescriptor;
+
+VendorTagDescriptor::VendorTagDescriptor() {}
+
+VendorTagDescriptor::~VendorTagDescriptor() {
+ size_t len = mReverseMapping.size();
+ for (size_t i = 0; i < len; ++i) {
+ delete mReverseMapping[i];
+ }
+}
+
+status_t VendorTagDescriptor::createDescriptorFromOps(const vendor_tag_ops_t* vOps,
+ /*out*/
+ sp<VendorTagDescriptor>& descriptor) {
+ if (vOps == NULL) {
+ ALOGE("%s: vendor_tag_ops argument was NULL.", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ int tagCount = vOps->get_tag_count(vOps);
+ if (tagCount < 0 || tagCount > INT32_MAX) {
+ ALOGE("%s: tag count %d from vendor ops is invalid.", __FUNCTION__, tagCount);
+ return BAD_VALUE;
+ }
+
+ Vector<uint32_t> tagArray;
+ LOG_ALWAYS_FATAL_IF(tagArray.resize(tagCount) != tagCount,
+ "%s: too many (%u) vendor tags defined.", __FUNCTION__, tagCount);
+
+ vOps->get_all_tags(vOps, /*out*/tagArray.editArray());
+
+ sp<VendorTagDescriptor> desc = new VendorTagDescriptor();
+ desc->mTagCount = tagCount;
+
+ SortedVector<String8> sections;
+ KeyedVector<uint32_t, String8> tagToSectionMap;
+
+ for (size_t i = 0; i < static_cast<size_t>(tagCount); ++i) {
+ uint32_t tag = tagArray[i];
+ if (tag < CAMERA_METADATA_VENDOR_TAG_BOUNDARY) {
+ ALOGE("%s: vendor tag %d not in vendor tag section.", __FUNCTION__, tag);
+ return BAD_VALUE;
+ }
+ const char *tagName = vOps->get_tag_name(vOps, tag);
+ if (tagName == NULL) {
+ ALOGE("%s: no tag name defined for vendor tag %d.", __FUNCTION__, tag);
+ return BAD_VALUE;
+ }
+ desc->mTagToNameMap.add(tag, String8(tagName));
+ const char *sectionName = vOps->get_section_name(vOps, tag);
+ if (sectionName == NULL) {
+ ALOGE("%s: no section name defined for vendor tag %d.", __FUNCTION__, tag);
+ return BAD_VALUE;
+ }
+
+ String8 sectionString(sectionName);
+
+ sections.add(sectionString);
+ tagToSectionMap.add(tag, sectionString);
+
+ int tagType = vOps->get_tag_type(vOps, tag);
+ if (tagType < 0 || tagType >= NUM_TYPES) {
+ ALOGE("%s: tag type %d from vendor ops does not exist.", __FUNCTION__, tagType);
+ return BAD_VALUE;
+ }
+ desc->mTagToTypeMap.add(tag, tagType);
+ }
+
+ desc->mSections = sections;
+
+ for (size_t i = 0; i < static_cast<size_t>(tagCount); ++i) {
+ uint32_t tag = tagArray[i];
+ String8 sectionString = tagToSectionMap.valueFor(tag);
+
+ // Set up tag to section index map
+ ssize_t index = sections.indexOf(sectionString);
+ LOG_ALWAYS_FATAL_IF(index < 0, "index %zd must be non-negative", index);
+ desc->mTagToSectionMap.add(tag, static_cast<uint32_t>(index));
+
+ // Set up reverse mapping
+ ssize_t reverseIndex = -1;
+ if ((reverseIndex = desc->mReverseMapping.indexOfKey(sectionString)) < 0) {
+ KeyedVector<String8, uint32_t>* nameMapper = new KeyedVector<String8, uint32_t>();
+ reverseIndex = desc->mReverseMapping.add(sectionString, nameMapper);
+ }
+ desc->mReverseMapping[reverseIndex]->add(desc->mTagToNameMap.valueFor(tag), tag);
+ }
+
+ descriptor = desc;
+ return OK;
+}
+
+status_t VendorTagDescriptor::createFromParcel(const Parcel* parcel,
+ /*out*/
+ sp<VendorTagDescriptor>& descriptor) {
+ status_t res = OK;
+ if (parcel == NULL) {
+ ALOGE("%s: parcel argument was NULL.", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ int32_t tagCount = 0;
+ if ((res = parcel->readInt32(&tagCount)) != OK) {
+ ALOGE("%s: could not read tag count from parcel", __FUNCTION__);
+ return res;
+ }
+
+ if (tagCount < 0 || tagCount > INT32_MAX) {
+ ALOGE("%s: tag count %d from vendor ops is invalid.", __FUNCTION__, tagCount);
+ return BAD_VALUE;
+ }
+
+ sp<VendorTagDescriptor> desc = new VendorTagDescriptor();
+ desc->mTagCount = tagCount;
+
+ uint32_t tag, sectionIndex;
+ uint32_t maxSectionIndex = 0;
+ int32_t tagType;
+ Vector<uint32_t> allTags;
+ for (int32_t i = 0; i < tagCount; ++i) {
+ if ((res = parcel->readInt32(reinterpret_cast<int32_t*>(&tag))) != OK) {
+ ALOGE("%s: could not read tag id from parcel for index %d", __FUNCTION__, i);
+ break;
+ }
+ if (tag < CAMERA_METADATA_VENDOR_TAG_BOUNDARY) {
+ ALOGE("%s: vendor tag %d not in vendor tag section.", __FUNCTION__, tag);
+ res = BAD_VALUE;
+ break;
+ }
+ if ((res = parcel->readInt32(&tagType)) != OK) {
+ ALOGE("%s: could not read tag type from parcel for tag %d", __FUNCTION__, tag);
+ break;
+ }
+ if (tagType < 0 || tagType >= NUM_TYPES) {
+ ALOGE("%s: tag type %d from vendor ops does not exist.", __FUNCTION__, tagType);
+ res = BAD_VALUE;
+ break;
+ }
+ String8 tagName = parcel->readString8();
+ if (tagName.isEmpty()) {
+ ALOGE("%s: parcel tag name was NULL for tag %d.", __FUNCTION__, tag);
+ res = NOT_ENOUGH_DATA;
+ break;
+ }
+
+ if ((res = parcel->readInt32(reinterpret_cast<int32_t*>(§ionIndex))) != OK) {
+ ALOGE("%s: could not read section index for tag %d.", __FUNCTION__, tag);
+ break;
+ }
+
+ maxSectionIndex = (maxSectionIndex >= sectionIndex) ? maxSectionIndex : sectionIndex;
+
+ allTags.add(tag);
+ desc->mTagToNameMap.add(tag, tagName);
+ desc->mTagToSectionMap.add(tag, sectionIndex);
+ desc->mTagToTypeMap.add(tag, tagType);
+ }
+
+ if (res != OK) {
+ return res;
+ }
+
+ size_t sectionCount;
+ if (tagCount > 0) {
+ if ((res = parcel->readInt32(reinterpret_cast<int32_t*>(§ionCount))) != OK) {
+ ALOGE("%s: could not read section count for.", __FUNCTION__);
+ return res;
+ }
+ if (sectionCount < (maxSectionIndex + 1)) {
+ ALOGE("%s: Incorrect number of sections defined, received %zu, needs %d.",
+ __FUNCTION__, sectionCount, (maxSectionIndex + 1));
+ return BAD_VALUE;
+ }
+ LOG_ALWAYS_FATAL_IF(desc->mSections.setCapacity(sectionCount) <= 0,
+ "Vector capacity must be positive");
+ for (size_t i = 0; i < sectionCount; ++i) {
+ String8 sectionName = parcel->readString8();
+ if (sectionName.isEmpty()) {
+ ALOGE("%s: parcel section name was NULL for section %zu.",
+ __FUNCTION__, i);
+ return NOT_ENOUGH_DATA;
+ }
+ desc->mSections.add(sectionName);
+ }
+ }
+
+ LOG_ALWAYS_FATAL_IF(static_cast<size_t>(tagCount) != allTags.size(),
+ "tagCount must be the same as allTags size");
+ // Set up reverse mapping
+ for (size_t i = 0; i < static_cast<size_t>(tagCount); ++i) {
+ uint32_t tag = allTags[i];
+ String8 sectionString = desc->mSections[desc->mTagToSectionMap.valueFor(tag)];
+
+ ssize_t reverseIndex = -1;
+ if ((reverseIndex = desc->mReverseMapping.indexOfKey(sectionString)) < 0) {
+ KeyedVector<String8, uint32_t>* nameMapper = new KeyedVector<String8, uint32_t>();
+ reverseIndex = desc->mReverseMapping.add(sectionString, nameMapper);
+ }
+ desc->mReverseMapping[reverseIndex]->add(desc->mTagToNameMap.valueFor(tag), tag);
+ }
+
+ descriptor = desc;
+ return res;
+}
+
+int VendorTagDescriptor::getTagCount() const {
+ size_t size = mTagToNameMap.size();
+ if (size == 0) {
+ return VENDOR_TAG_COUNT_ERR;
+ }
+ return size;
+}
+
+void VendorTagDescriptor::getTagArray(uint32_t* tagArray) const {
+ size_t size = mTagToNameMap.size();
+ for (size_t i = 0; i < size; ++i) {
+ tagArray[i] = mTagToNameMap.keyAt(i);
+ }
+}
+
+const char* VendorTagDescriptor::getSectionName(uint32_t tag) const {
+ ssize_t index = mTagToSectionMap.indexOfKey(tag);
+ if (index < 0) {
+ return VENDOR_SECTION_NAME_ERR;
+ }
+ return mSections[mTagToSectionMap.valueAt(index)].string();
+}
+
+const char* VendorTagDescriptor::getTagName(uint32_t tag) const {
+ ssize_t index = mTagToNameMap.indexOfKey(tag);
+ if (index < 0) {
+ return VENDOR_TAG_NAME_ERR;
+ }
+ return mTagToNameMap.valueAt(index).string();
+}
+
+int VendorTagDescriptor::getTagType(uint32_t tag) const {
+ ssize_t index = mTagToNameMap.indexOfKey(tag);
+ if (index < 0) {
+ return VENDOR_TAG_TYPE_ERR;
+ }
+ return mTagToTypeMap.valueFor(tag);
+}
+
+status_t VendorTagDescriptor::writeToParcel(Parcel* parcel) const {
+ status_t res = OK;
+ if (parcel == NULL) {
+ ALOGE("%s: parcel argument was NULL.", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ if ((res = parcel->writeInt32(mTagCount)) != OK) {
+ return res;
+ }
+
+ size_t size = mTagToNameMap.size();
+ uint32_t tag, sectionIndex;
+ int32_t tagType;
+ for (size_t i = 0; i < size; ++i) {
+ tag = mTagToNameMap.keyAt(i);
+ String8 tagName = mTagToNameMap[i];
+ sectionIndex = mTagToSectionMap.valueFor(tag);
+ tagType = mTagToTypeMap.valueFor(tag);
+ if ((res = parcel->writeInt32(tag)) != OK) break;
+ if ((res = parcel->writeInt32(tagType)) != OK) break;
+ if ((res = parcel->writeString8(tagName)) != OK) break;
+ if ((res = parcel->writeInt32(sectionIndex)) != OK) break;
+ }
+
+ size_t numSections = mSections.size();
+ if (numSections > 0) {
+ if ((res = parcel->writeInt32(numSections)) != OK) return res;
+ for (size_t i = 0; i < numSections; ++i) {
+ if ((res = parcel->writeString8(mSections[i])) != OK) return res;
+ }
+ }
+
+ return res;
+}
+
+SortedVector<String8> VendorTagDescriptor::getAllSectionNames() const {
+ return mSections;
+}
+
+status_t VendorTagDescriptor::lookupTag(String8 name, String8 section, /*out*/uint32_t* tag) const {
+ ssize_t index = mReverseMapping.indexOfKey(section);
+ if (index < 0) {
+ ALOGE("%s: Section '%s' does not exist.", __FUNCTION__, section.string());
+ return BAD_VALUE;
+ }
+
+ ssize_t nameIndex = mReverseMapping[index]->indexOfKey(name);
+ if (nameIndex < 0) {
+ ALOGE("%s: Tag name '%s' does not exist.", __FUNCTION__, name.string());
+ return BAD_VALUE;
+ }
+
+ if (tag != NULL) {
+ *tag = mReverseMapping[index]->valueAt(nameIndex);
+ }
+ return OK;
+}
+
+void VendorTagDescriptor::dump(int fd, int verbosity, int indentation) const {
+
+ size_t size = mTagToNameMap.size();
+ if (size == 0) {
+ dprintf(fd, "%*sDumping configured vendor tag descriptors: None set\n",
+ indentation, "");
+ return;
+ }
+
+ dprintf(fd, "%*sDumping configured vendor tag descriptors: %zu entries\n",
+ indentation, "", size);
+ for (size_t i = 0; i < size; ++i) {
+ uint32_t tag = mTagToNameMap.keyAt(i);
+
+ if (verbosity < 1) {
+ dprintf(fd, "%*s0x%x\n", indentation + 2, "", tag);
+ continue;
+ }
+ String8 name = mTagToNameMap.valueAt(i);
+ uint32_t sectionId = mTagToSectionMap.valueFor(tag);
+ String8 sectionName = mSections[sectionId];
+ int type = mTagToTypeMap.valueFor(tag);
+ const char* typeName = (type >= 0 && type < NUM_TYPES) ?
+ camera_metadata_type_names[type] : "UNKNOWN";
+ dprintf(fd, "%*s0x%x (%s) with type %d (%s) defined in section %s\n", indentation + 2,
+ "", tag, name.string(), type, typeName, sectionName.string());
+ }
+
+}
+
+status_t VendorTagDescriptor::setAsGlobalVendorTagDescriptor(const sp<VendorTagDescriptor>& desc) {
+ status_t res = OK;
+ Mutex::Autolock al(sLock);
+ sGlobalVendorTagDescriptor = desc;
+
+ vendor_tag_ops_t* opsPtr = NULL;
+ if (desc != NULL) {
+ opsPtr = &(desc->mVendorOps);
+ opsPtr->get_tag_count = vendor_tag_descriptor_get_tag_count;
+ opsPtr->get_all_tags = vendor_tag_descriptor_get_all_tags;
+ opsPtr->get_section_name = vendor_tag_descriptor_get_section_name;
+ opsPtr->get_tag_name = vendor_tag_descriptor_get_tag_name;
+ opsPtr->get_tag_type = vendor_tag_descriptor_get_tag_type;
+ }
+ if((res = set_camera_metadata_vendor_ops(opsPtr)) != OK) {
+ ALOGE("%s: Could not set vendor tag descriptor, received error %s (%d)."
+ , __FUNCTION__, strerror(-res), res);
+ }
+ return res;
+}
+
+void VendorTagDescriptor::clearGlobalVendorTagDescriptor() {
+ Mutex::Autolock al(sLock);
+ set_camera_metadata_vendor_ops(NULL);
+ sGlobalVendorTagDescriptor.clear();
+}
+
+sp<VendorTagDescriptor> VendorTagDescriptor::getGlobalVendorTagDescriptor() {
+ Mutex::Autolock al(sLock);
+ return sGlobalVendorTagDescriptor;
+}
+
+extern "C" {
+
+int vendor_tag_descriptor_get_tag_count(const vendor_tag_ops_t* /*v*/) {
+ Mutex::Autolock al(sLock);
+ if (sGlobalVendorTagDescriptor == NULL) {
+ ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__);
+ return VENDOR_TAG_COUNT_ERR;
+ }
+ return sGlobalVendorTagDescriptor->getTagCount();
+}
+
+void vendor_tag_descriptor_get_all_tags(const vendor_tag_ops_t* /*v*/, uint32_t* tagArray) {
+ Mutex::Autolock al(sLock);
+ if (sGlobalVendorTagDescriptor == NULL) {
+ ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__);
+ return;
+ }
+ sGlobalVendorTagDescriptor->getTagArray(tagArray);
+}
+
+const char* vendor_tag_descriptor_get_section_name(const vendor_tag_ops_t* /*v*/, uint32_t tag) {
+ Mutex::Autolock al(sLock);
+ if (sGlobalVendorTagDescriptor == NULL) {
+ ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__);
+ return VENDOR_SECTION_NAME_ERR;
+ }
+ return sGlobalVendorTagDescriptor->getSectionName(tag);
+}
+
+const char* vendor_tag_descriptor_get_tag_name(const vendor_tag_ops_t* /*v*/, uint32_t tag) {
+ Mutex::Autolock al(sLock);
+ if (sGlobalVendorTagDescriptor == NULL) {
+ ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__);
+ return VENDOR_TAG_NAME_ERR;
+ }
+ return sGlobalVendorTagDescriptor->getTagName(tag);
+}
+
+int vendor_tag_descriptor_get_tag_type(const vendor_tag_ops_t* /*v*/, uint32_t tag) {
+ Mutex::Autolock al(sLock);
+ if (sGlobalVendorTagDescriptor == NULL) {
+ ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__);
+ return VENDOR_TAG_TYPE_ERR;
+ }
+ return sGlobalVendorTagDescriptor->getTagType(tag);
+}
+
+} /* extern "C" */
+} /* namespace android */
diff --git a/camera/camera2/ICameraDeviceCallbacks.cpp b/camera/camera2/ICameraDeviceCallbacks.cpp
index 613358a..4cc7b5d 100644
--- a/camera/camera2/ICameraDeviceCallbacks.cpp
+++ b/camera/camera2/ICameraDeviceCallbacks.cpp
@@ -28,6 +28,7 @@
#include <camera/camera2/ICameraDeviceCallbacks.h>
#include "camera/CameraMetadata.h"
+#include "camera/CaptureResult.h"
namespace android {
@@ -46,12 +47,14 @@
{
}
- void onDeviceError(CameraErrorCode errorCode)
+ void onDeviceError(CameraErrorCode errorCode, const CaptureResultExtras& resultExtras)
{
ALOGV("onDeviceError");
Parcel data, reply;
data.writeInterfaceToken(ICameraDeviceCallbacks::getInterfaceDescriptor());
data.writeInt32(static_cast<int32_t>(errorCode));
+ data.writeInt32(1); // to mark presence of CaptureResultExtras object
+ resultExtras.writeToParcel(&data);
remote()->transact(CAMERA_ERROR, data, &reply, IBinder::FLAG_ONEWAY);
data.writeNoException();
}
@@ -65,25 +68,28 @@
data.writeNoException();
}
- void onCaptureStarted(int32_t requestId, int64_t timestamp)
+ void onCaptureStarted(const CaptureResultExtras& result, int64_t timestamp)
{
ALOGV("onCaptureStarted");
Parcel data, reply;
data.writeInterfaceToken(ICameraDeviceCallbacks::getInterfaceDescriptor());
- data.writeInt32(requestId);
+ data.writeInt32(1); // to mark presence of CaptureResultExtras object
+ result.writeToParcel(&data);
data.writeInt64(timestamp);
remote()->transact(CAPTURE_STARTED, data, &reply, IBinder::FLAG_ONEWAY);
data.writeNoException();
}
- void onResultReceived(int32_t requestId, const CameraMetadata& result) {
+ void onResultReceived(const CameraMetadata& metadata,
+ const CaptureResultExtras& resultExtras) {
ALOGV("onResultReceived");
Parcel data, reply;
data.writeInterfaceToken(ICameraDeviceCallbacks::getInterfaceDescriptor());
- data.writeInt32(requestId);
data.writeInt32(1); // to mark presence of metadata object
- result.writeToParcel(&data);
+ metadata.writeToParcel(&data);
+ data.writeInt32(1); // to mark presence of CaptureResult object
+ resultExtras.writeToParcel(&data);
remote()->transact(RESULT_RECEIVED, data, &reply, IBinder::FLAG_ONEWAY);
data.writeNoException();
}
@@ -104,7 +110,13 @@
CHECK_INTERFACE(ICameraDeviceCallbacks, data, reply);
CameraErrorCode errorCode =
static_cast<CameraErrorCode>(data.readInt32());
- onDeviceError(errorCode);
+ CaptureResultExtras resultExtras;
+ if (data.readInt32() != 0) {
+ resultExtras.readFromParcel(const_cast<Parcel*>(&data));
+ } else {
+ ALOGE("No CaptureResultExtras object is present!");
+ }
+ onDeviceError(errorCode, resultExtras);
data.readExceptionCode();
return NO_ERROR;
} break;
@@ -118,23 +130,33 @@
case CAPTURE_STARTED: {
ALOGV("onCaptureStarted");
CHECK_INTERFACE(ICameraDeviceCallbacks, data, reply);
- int32_t requestId = data.readInt32();
+ CaptureResultExtras result;
+ if (data.readInt32() != 0) {
+ result.readFromParcel(const_cast<Parcel*>(&data));
+ } else {
+ ALOGE("No CaptureResultExtras object is present in result!");
+ }
int64_t timestamp = data.readInt64();
- onCaptureStarted(requestId, timestamp);
+ onCaptureStarted(result, timestamp);
data.readExceptionCode();
return NO_ERROR;
} break;
case RESULT_RECEIVED: {
ALOGV("onResultReceived");
CHECK_INTERFACE(ICameraDeviceCallbacks, data, reply);
- int32_t requestId = data.readInt32();
- CameraMetadata result;
+ CameraMetadata metadata;
if (data.readInt32() != 0) {
- result.readFromParcel(const_cast<Parcel*>(&data));
+ metadata.readFromParcel(const_cast<Parcel*>(&data));
} else {
ALOGW("No metadata object is present in result");
}
- onResultReceived(requestId, result);
+ CaptureResultExtras resultExtras;
+ if (data.readInt32() != 0) {
+ resultExtras.readFromParcel(const_cast<Parcel*>(&data));
+ } else {
+ ALOGW("No capture result extras object is present in result");
+ }
+ onResultReceived(metadata, resultExtras);
data.readExceptionCode();
return NO_ERROR;
} break;
diff --git a/camera/camera2/ICameraDeviceUser.cpp b/camera/camera2/ICameraDeviceUser.cpp
index 1e5822f..ff4a0c2 100644
--- a/camera/camera2/ICameraDeviceUser.cpp
+++ b/camera/camera2/ICameraDeviceUser.cpp
@@ -35,7 +35,10 @@
enum {
DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
SUBMIT_REQUEST,
+ SUBMIT_REQUEST_LIST,
CANCEL_REQUEST,
+ BEGIN_CONFIGURE,
+ END_CONFIGURE,
DELETE_STREAM,
CREATE_STREAM,
CREATE_DEFAULT_REQUEST,
@@ -75,7 +78,8 @@
reply.readExceptionCode();
}
- virtual int submitRequest(sp<CaptureRequest> request, bool streaming)
+ virtual status_t submitRequest(sp<CaptureRequest> request, bool repeating,
+ int64_t *lastFrameNumber)
{
Parcel data, reply;
data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
@@ -89,15 +93,67 @@
}
// arg1 = streaming (bool)
- data.writeInt32(streaming);
+ data.writeInt32(repeating);
remote()->transact(SUBMIT_REQUEST, data, &reply);
reply.readExceptionCode();
- return reply.readInt32();
+ status_t res = reply.readInt32();
+
+ status_t resFrameNumber = BAD_VALUE;
+ if (reply.readInt32() != 0) {
+ if (lastFrameNumber != NULL) {
+ resFrameNumber = reply.readInt64(lastFrameNumber);
+ }
+ }
+
+ if ((res != NO_ERROR) || (resFrameNumber != NO_ERROR)) {
+ res = FAILED_TRANSACTION;
+ }
+ return res;
}
- virtual status_t cancelRequest(int requestId)
+ virtual status_t submitRequestList(List<sp<CaptureRequest> > requestList, bool repeating,
+ int64_t *lastFrameNumber)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
+
+ data.writeInt32(requestList.size());
+
+ for (List<sp<CaptureRequest> >::iterator it = requestList.begin();
+ it != requestList.end(); ++it) {
+ sp<CaptureRequest> request = *it;
+ if (request != 0) {
+ data.writeInt32(1);
+ if (request->writeToParcel(&data) != OK) {
+ return BAD_VALUE;
+ }
+ } else {
+ data.writeInt32(0);
+ }
+ }
+
+ data.writeInt32(repeating);
+
+ remote()->transact(SUBMIT_REQUEST_LIST, data, &reply);
+
+ reply.readExceptionCode();
+ status_t res = reply.readInt32();
+
+ status_t resFrameNumber = BAD_VALUE;
+ if (reply.readInt32() != 0) {
+ if (lastFrameNumber != NULL) {
+ resFrameNumber = reply.readInt64(lastFrameNumber);
+ }
+ }
+ if ((res != NO_ERROR) || (resFrameNumber != NO_ERROR)) {
+ res = FAILED_TRANSACTION;
+ }
+ return res;
+ }
+
+ virtual status_t cancelRequest(int requestId, int64_t *lastFrameNumber)
{
Parcel data, reply;
data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
@@ -106,6 +162,37 @@
remote()->transact(CANCEL_REQUEST, data, &reply);
reply.readExceptionCode();
+ status_t res = reply.readInt32();
+
+ status_t resFrameNumber = BAD_VALUE;
+ if (reply.readInt32() != 0) {
+ if (lastFrameNumber != NULL) {
+ res = reply.readInt64(lastFrameNumber);
+ }
+ }
+ if ((res != NO_ERROR) || (resFrameNumber != NO_ERROR)) {
+ res = FAILED_TRANSACTION;
+ }
+ return res;
+ }
+
+ virtual status_t beginConfigure()
+ {
+ ALOGV("beginConfigure");
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
+ remote()->transact(BEGIN_CONFIGURE, data, &reply);
+ reply.readExceptionCode();
+ return reply.readInt32();
+ }
+
+ virtual status_t endConfigure()
+ {
+ ALOGV("endConfigure");
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
+ remote()->transact(END_CONFIGURE, data, &reply);
+ reply.readExceptionCode();
return reply.readInt32();
}
@@ -197,14 +284,25 @@
return reply.readInt32();
}
- virtual status_t flush()
+ virtual status_t flush(int64_t *lastFrameNumber)
{
ALOGV("flush");
Parcel data, reply;
data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
remote()->transact(FLUSH, data, &reply);
reply.readExceptionCode();
- return reply.readInt32();
+ status_t res = reply.readInt32();
+
+ status_t resFrameNumber = BAD_VALUE;
+ if (reply.readInt32() != 0) {
+ if (lastFrameNumber != NULL) {
+ res = reply.readInt64(lastFrameNumber);
+ }
+ }
+ if ((res != NO_ERROR) || (resFrameNumber != NO_ERROR)) {
+ res = FAILED_TRANSACTION;
+ }
+ return res;
}
private:
@@ -239,11 +337,43 @@
}
// arg1 = streaming (bool)
- bool streaming = data.readInt32();
+ bool repeating = data.readInt32();
// return code: requestId (int32)
reply->writeNoException();
- reply->writeInt32(submitRequest(request, streaming));
+ int64_t lastFrameNumber = -1;
+ reply->writeInt32(submitRequest(request, repeating, &lastFrameNumber));
+ reply->writeInt32(1);
+ reply->writeInt64(lastFrameNumber);
+
+ return NO_ERROR;
+ } break;
+ case SUBMIT_REQUEST_LIST: {
+ CHECK_INTERFACE(ICameraDeviceUser, data, reply);
+
+ List<sp<CaptureRequest> > requestList;
+ int requestListSize = data.readInt32();
+ for (int i = 0; i < requestListSize; i++) {
+ if (data.readInt32() != 0) {
+ sp<CaptureRequest> request = new CaptureRequest();
+ if (request->readFromParcel(const_cast<Parcel*>(&data)) != OK) {
+ return BAD_VALUE;
+ }
+ requestList.push_back(request);
+ } else {
+ sp<CaptureRequest> request = 0;
+ requestList.push_back(request);
+ ALOGE("A request is missing. Sending in null request.");
+ }
+ }
+
+ bool repeating = data.readInt32();
+
+ reply->writeNoException();
+ int64_t lastFrameNumber = -1;
+ reply->writeInt32(submitRequestList(requestList, repeating, &lastFrameNumber));
+ reply->writeInt32(1);
+ reply->writeInt64(lastFrameNumber);
return NO_ERROR;
} break;
@@ -251,7 +381,10 @@
CHECK_INTERFACE(ICameraDeviceUser, data, reply);
int requestId = data.readInt32();
reply->writeNoException();
- reply->writeInt32(cancelRequest(requestId));
+ int64_t lastFrameNumber = -1;
+ reply->writeInt32(cancelRequest(requestId, &lastFrameNumber));
+ reply->writeInt32(1);
+ reply->writeInt64(lastFrameNumber);
return NO_ERROR;
} break;
case DELETE_STREAM: {
@@ -339,9 +472,24 @@
case FLUSH: {
CHECK_INTERFACE(ICameraDeviceUser, data, reply);
reply->writeNoException();
- reply->writeInt32(flush());
+ int64_t lastFrameNumber = -1;
+ reply->writeInt32(flush(&lastFrameNumber));
+ reply->writeInt32(1);
+ reply->writeInt64(lastFrameNumber);
return NO_ERROR;
}
+ case BEGIN_CONFIGURE: {
+ CHECK_INTERFACE(ICameraDeviceUser, data, reply);
+ reply->writeNoException();
+ reply->writeInt32(beginConfigure());
+ return NO_ERROR;
+ } break;
+ case END_CONFIGURE: {
+ CHECK_INTERFACE(ICameraDeviceUser, data, reply);
+ reply->writeNoException();
+ reply->writeInt32(endConfigure());
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/camera/tests/Android.mk b/camera/tests/Android.mk
index ec13911..61385e5 100644
--- a/camera/tests/Android.mk
+++ b/camera/tests/Android.mk
@@ -1,9 +1,24 @@
+# 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.
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
main.cpp \
ProCameraTests.cpp \
+ VendorTagDescriptorTests.cpp
LOCAL_SHARED_LIBRARIES := \
libutils \
@@ -26,6 +41,8 @@
external/gtest/include \
external/stlport/stlport \
system/media/camera/include \
+ system/media/private/camera/include \
+ system/media/camera/tests \
frameworks/av/services/camera/libcameraservice \
frameworks/av/include/camera \
frameworks/native/include \
diff --git a/camera/tests/VendorTagDescriptorTests.cpp b/camera/tests/VendorTagDescriptorTests.cpp
new file mode 100644
index 0000000..6624e79
--- /dev/null
+++ b/camera/tests/VendorTagDescriptorTests.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2014 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 "VendorTagDescriptorTests"
+
+#include <binder/Parcel.h>
+#include <camera/VendorTagDescriptor.h>
+#include <camera_metadata_tests_fake_vendor.h>
+#include <camera_metadata_hidden.h>
+#include <system/camera_vendor_tags.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+
+#include <gtest/gtest.h>
+#include <stdint.h>
+
+using namespace android;
+
+enum {
+ BAD_TAG_ARRAY = 0xDEADBEEFu,
+ BAD_TAG = 0x8DEADBADu,
+};
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+static bool ContainsTag(uint32_t* tagArray, size_t size, uint32_t tag) {
+ for (size_t i = 0; i < size; ++i) {
+ if (tag == tagArray[i]) return true;
+ }
+ return false;
+}
+
+#define EXPECT_CONTAINS_TAG(t, a) \
+ EXPECT_TRUE(ContainsTag(a, ARRAY_SIZE(a), t))
+
+#define ASSERT_NOT_NULL(x) \
+ ASSERT_TRUE((x) != NULL)
+
+extern "C" {
+
+static int default_get_tag_count(const vendor_tag_ops_t* vOps) {
+ return VENDOR_TAG_COUNT_ERR;
+}
+
+static void default_get_all_tags(const vendor_tag_ops_t* vOps, uint32_t* tagArray) {
+ //Noop
+}
+
+static const char* default_get_section_name(const vendor_tag_ops_t* vOps, uint32_t tag) {
+ return VENDOR_SECTION_NAME_ERR;
+}
+
+static const char* default_get_tag_name(const vendor_tag_ops_t* vOps, uint32_t tag) {
+ return VENDOR_TAG_NAME_ERR;
+}
+
+static int default_get_tag_type(const vendor_tag_ops_t* vOps, uint32_t tag) {
+ return VENDOR_TAG_TYPE_ERR;
+}
+
+} /*extern "C"*/
+
+// Set default vendor operations for a vendor_tag_ops struct
+static void FillWithDefaults(vendor_tag_ops_t* vOps) {
+ ASSERT_NOT_NULL(vOps);
+ vOps->get_tag_count = default_get_tag_count;
+ vOps->get_all_tags = default_get_all_tags;
+ vOps->get_section_name = default_get_section_name;
+ vOps->get_tag_name = default_get_tag_name;
+ vOps->get_tag_type = default_get_tag_type;
+}
+
+/**
+ * Test if values from VendorTagDescriptor methods match corresponding values
+ * from vendor_tag_ops functions.
+ */
+TEST(VendorTagDescriptorTest, ConsistentWithVendorTags) {
+ sp<VendorTagDescriptor> vDesc;
+ const vendor_tag_ops_t *vOps = &fakevendor_ops;
+ EXPECT_EQ(OK, VendorTagDescriptor::createDescriptorFromOps(vOps, /*out*/vDesc));
+
+ ASSERT_NOT_NULL(vDesc);
+
+ // Ensure reasonable tag count
+ int tagCount = vDesc->getTagCount();
+ EXPECT_EQ(tagCount, vOps->get_tag_count(vOps));
+
+ uint32_t descTagArray[tagCount];
+ uint32_t opsTagArray[tagCount];
+
+ // Get all tag ids
+ vDesc->getTagArray(descTagArray);
+ vOps->get_all_tags(vOps, opsTagArray);
+
+ ASSERT_NOT_NULL(descTagArray);
+ ASSERT_NOT_NULL(opsTagArray);
+
+ uint32_t tag;
+ for (int i = 0; i < tagCount; ++i) {
+ // For each tag id, check whether type, section name, tag name match
+ tag = descTagArray[i];
+ EXPECT_CONTAINS_TAG(tag, opsTagArray);
+ EXPECT_EQ(vDesc->getTagType(tag), vOps->get_tag_type(vOps, tag));
+ EXPECT_STREQ(vDesc->getSectionName(tag), vOps->get_section_name(vOps, tag));
+ EXPECT_STREQ(vDesc->getTagName(tag), vOps->get_tag_name(vOps, tag));
+ }
+}
+
+/**
+ * Test if values from VendorTagDescriptor methods stay consistent after being
+ * parcelled/unparcelled.
+ */
+TEST(VendorTagDescriptorTest, ConsistentAcrossParcel) {
+ sp<VendorTagDescriptor> vDescOriginal, vDescParceled;
+ const vendor_tag_ops_t *vOps = &fakevendor_ops;
+ EXPECT_EQ(OK, VendorTagDescriptor::createDescriptorFromOps(vOps, /*out*/vDescOriginal));
+
+ ASSERT_TRUE(vDescOriginal != NULL);
+
+ Parcel p;
+
+ // Check whether parcel read/write succeed
+ EXPECT_EQ(OK, vDescOriginal->writeToParcel(&p));
+ p.setDataPosition(0);
+ ASSERT_EQ(OK, VendorTagDescriptor::createFromParcel(&p, vDescParceled));
+
+ // Ensure consistent tag count
+ int tagCount = vDescOriginal->getTagCount();
+ ASSERT_EQ(tagCount, vDescParceled->getTagCount());
+
+ uint32_t descTagArray[tagCount];
+ uint32_t desc2TagArray[tagCount];
+
+ // Get all tag ids
+ vDescOriginal->getTagArray(descTagArray);
+ vDescParceled->getTagArray(desc2TagArray);
+
+ ASSERT_NOT_NULL(descTagArray);
+ ASSERT_NOT_NULL(desc2TagArray);
+
+ uint32_t tag;
+ for (int i = 0; i < tagCount; ++i) {
+ // For each tag id, check consistency between the two vendor tag
+ // descriptors for each type, section name, tag name
+ tag = descTagArray[i];
+ EXPECT_CONTAINS_TAG(tag, desc2TagArray);
+ EXPECT_EQ(vDescOriginal->getTagType(tag), vDescParceled->getTagType(tag));
+ EXPECT_STREQ(vDescOriginal->getSectionName(tag), vDescParceled->getSectionName(tag));
+ EXPECT_STREQ(vDescOriginal->getTagName(tag), vDescParceled->getTagName(tag));
+ }
+}
+
+/**
+ * Test defaults and error conditions.
+ */
+TEST(VendorTagDescriptorTest, ErrorConditions) {
+ sp<VendorTagDescriptor> vDesc;
+ vendor_tag_ops_t vOps;
+ FillWithDefaults(&vOps);
+
+ // Ensure create fails when using null vOps
+ EXPECT_EQ(BAD_VALUE, VendorTagDescriptor::createDescriptorFromOps(/*vOps*/NULL, vDesc));
+
+ // Ensure create works when there are no vtags defined in a well-formed vOps
+ ASSERT_EQ(OK, VendorTagDescriptor::createDescriptorFromOps(&vOps, vDesc));
+
+ // Ensure defaults are returned when no vtags are defined, or tag is unknown
+ EXPECT_EQ(VENDOR_TAG_COUNT_ERR, vDesc->getTagCount());
+ uint32_t* tagArray = reinterpret_cast<uint32_t*>(BAD_TAG_ARRAY);
+ uint32_t* testArray = tagArray;
+ vDesc->getTagArray(tagArray);
+ EXPECT_EQ(testArray, tagArray);
+ EXPECT_EQ(VENDOR_SECTION_NAME_ERR, vDesc->getSectionName(BAD_TAG));
+ EXPECT_EQ(VENDOR_TAG_NAME_ERR, vDesc->getTagName(BAD_TAG));
+ EXPECT_EQ(VENDOR_TAG_TYPE_ERR, vDesc->getTagType(BAD_TAG));
+
+ // Make sure global can be set/cleared
+ const vendor_tag_ops_t *fakeOps = &fakevendor_ops;
+ sp<VendorTagDescriptor> prevGlobal = VendorTagDescriptor::getGlobalVendorTagDescriptor();
+ VendorTagDescriptor::clearGlobalVendorTagDescriptor();
+
+ EXPECT_TRUE(VendorTagDescriptor::getGlobalVendorTagDescriptor() == NULL);
+ EXPECT_EQ(OK, VendorTagDescriptor::setAsGlobalVendorTagDescriptor(vDesc));
+ EXPECT_TRUE(VendorTagDescriptor::getGlobalVendorTagDescriptor() != NULL);
+ EXPECT_EQ(VENDOR_SECTION_NAME_ERR, vDesc->getSectionName(BAD_TAG));
+ EXPECT_EQ(OK, VendorTagDescriptor::setAsGlobalVendorTagDescriptor(prevGlobal));
+ EXPECT_EQ(prevGlobal, VendorTagDescriptor::getGlobalVendorTagDescriptor());
+}
+
diff --git a/cmds/screenrecord/FrameOutput.cpp b/cmds/screenrecord/FrameOutput.cpp
index 6c37501..03e0062 100644
--- a/cmds/screenrecord/FrameOutput.cpp
+++ b/cmds/screenrecord/FrameOutput.cpp
@@ -67,9 +67,11 @@
return UNKNOWN_ERROR;
}
- mBufferQueue = new BufferQueue(/*new GraphicBufferAlloc()*/);
- mGlConsumer = new GLConsumer(mBufferQueue, mExtTextureName,
- GL_TEXTURE_EXTERNAL_OES);
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+ mGlConsumer = new GLConsumer(consumer, mExtTextureName,
+ GL_TEXTURE_EXTERNAL_OES, true, false);
mGlConsumer->setName(String8("virtual display"));
mGlConsumer->setDefaultBufferSize(width, height);
mGlConsumer->setDefaultMaxBufferCount(5);
@@ -79,7 +81,7 @@
mPixelBuf = new uint8_t[width * height * kGlBytesPerPixel];
- *pBufferProducer = mBufferQueue;
+ *pBufferProducer = producer;
ALOGD("FrameOutput::createInputSurface OK");
return NO_ERROR;
diff --git a/cmds/screenrecord/FrameOutput.h b/cmds/screenrecord/FrameOutput.h
index 4ac3e8a..c49ec3b 100644
--- a/cmds/screenrecord/FrameOutput.h
+++ b/cmds/screenrecord/FrameOutput.h
@@ -34,9 +34,6 @@
mExtTextureName(0),
mPixelBuf(NULL)
{}
- virtual ~FrameOutput() {
- delete[] mPixelBuf;
- }
// Create an "input surface", similar in purpose to a MediaCodec input
// surface, that the virtual display can send buffers to. Also configures
@@ -59,6 +56,11 @@
FrameOutput(const FrameOutput&);
FrameOutput& operator=(const FrameOutput&);
+ // Destruction via RefBase.
+ virtual ~FrameOutput() {
+ delete[] mPixelBuf;
+ }
+
// (overrides GLConsumer::FrameAvailableListener method)
virtual void onFrameAvailable();
@@ -75,10 +77,6 @@
// Set by the FrameAvailableListener callback.
bool mFrameAvailable;
- // Our queue. The producer side is passed to the virtual display, the
- // consumer side feeds into our GLConsumer.
- sp<BufferQueue> mBufferQueue;
-
// This receives frames from the virtual display and makes them available
// as an external texture.
sp<GLConsumer> mGlConsumer;
diff --git a/cmds/screenrecord/Overlay.cpp b/cmds/screenrecord/Overlay.cpp
index 96e25b8..7fef53d 100644
--- a/cmds/screenrecord/Overlay.cpp
+++ b/cmds/screenrecord/Overlay.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#include <assert.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
#define LOG_TAG "ScreenRecord"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
@@ -27,9 +31,6 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
-#include <stdlib.h>
-#include <assert.h>
-
#include "screenrecord.h"
#include "Overlay.h"
#include "TextRenderer.h"
@@ -47,7 +48,7 @@
"ro.revision",
"dalvik.vm.heapgrowthlimit",
"dalvik.vm.heapsize",
- "persist.sys.dalvik.vm.lib",
+ "persist.sys.dalvik.vm.lib.2",
//"ro.product.cpu.abi",
//"ro.bootloader",
//"this-never-appears!",
@@ -84,7 +85,7 @@
assert(mState == RUNNING);
ALOGV("Overlay::start successful");
- *pBufferProducer = mBufferQueue;
+ *pBufferProducer = mProducer;
return NO_ERROR;
}
@@ -169,9 +170,10 @@
return UNKNOWN_ERROR;
}
- mBufferQueue = new BufferQueue(/*new GraphicBufferAlloc()*/);
- mGlConsumer = new GLConsumer(mBufferQueue, mExtTextureName,
- GL_TEXTURE_EXTERNAL_OES);
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&mProducer, &consumer);
+ mGlConsumer = new GLConsumer(consumer, mExtTextureName,
+ GL_TEXTURE_EXTERNAL_OES, true, false);
mGlConsumer->setName(String8("virtual display"));
mGlConsumer->setDefaultBufferSize(width, height);
mGlConsumer->setDefaultMaxBufferCount(5);
@@ -187,7 +189,7 @@
ALOGV("Overlay::release_l");
mOutputSurface.clear();
mGlConsumer.clear();
- mBufferQueue.clear();
+ mProducer.clear();
mTexProgram.release();
mExtTexProgram.release();
@@ -234,7 +236,7 @@
char textBuf[64];
getTimeString_l(monotonicNsec, textBuf, sizeof(textBuf));
- String8 timeStr(String8::format("%s f=%lld (%zd)",
+ String8 timeStr(String8::format("%s f=%" PRId64 " (%zd)",
textBuf, frameNumber, mTotalDroppedFrames));
mTextRenderer.drawString(mTexProgram, Program::kIdentity, 0, 0, timeStr);
diff --git a/cmds/screenrecord/Overlay.h b/cmds/screenrecord/Overlay.h
index b8473b4..b1b5c29 100644
--- a/cmds/screenrecord/Overlay.h
+++ b/cmds/screenrecord/Overlay.h
@@ -47,7 +47,6 @@
mLastFrameNumber(-1),
mTotalDroppedFrames(0)
{}
- virtual ~Overlay() { assert(mState == UNINITIALIZED || mState == STOPPED); }
// Creates a thread that performs the overlay. Pass in the surface that
// output will be sent to.
@@ -71,6 +70,9 @@
Overlay(const Overlay&);
Overlay& operator=(const Overlay&);
+ // Destruction via RefBase.
+ virtual ~Overlay() { assert(mState == UNINITIALIZED || mState == STOPPED); }
+
// Draw the initial info screen.
static void doDrawInfoPage(const EglWindow& window,
const Program& texRender, TextRenderer& textRenderer);
@@ -120,9 +122,9 @@
// surface.
sp<IGraphicBufferProducer> mOutputSurface;
- // Our queue. The producer side is passed to the virtual display, the
- // consumer side feeds into our GLConsumer.
- sp<BufferQueue> mBufferQueue;
+ // Producer side of queue, passed into the virtual display.
+ // The consumer end feeds into our GLConsumer.
+ sp<IGraphicBufferProducer> mProducer;
// This receives frames from the virtual display and makes them available
// as an external texture.
diff --git a/cmds/screenrecord/TextRenderer.cpp b/cmds/screenrecord/TextRenderer.cpp
index 784055c..6a9176b 100644
--- a/cmds/screenrecord/TextRenderer.cpp
+++ b/cmds/screenrecord/TextRenderer.cpp
@@ -353,6 +353,6 @@
}
}
- ALOGV("goodPos=%d for str='%s'", goodPos, str);
+ ALOGV("goodPos=%zu for str='%s'", goodPos, str);
return const_cast<char*>(str + goodPos);
}
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 02ed53a..02df1d2 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -14,6 +14,19 @@
* limitations under the License.
*/
+#include <assert.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
#define LOG_TAG "ScreenRecord"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
@@ -36,18 +49,6 @@
#include <media/stagefright/MediaMuxer.h>
#include <media/ICrypto.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <getopt.h>
-#include <sys/wait.h>
-#include <termios.h>
-#include <assert.h>
-
#include "screenrecord.h"
#include "Overlay.h"
#include "FrameOutput.h"
@@ -354,7 +355,7 @@
case NO_ERROR:
// got a buffer
if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) != 0) {
- ALOGV("Got codec config buffer (%u bytes)", size);
+ ALOGV("Got codec config buffer (%zu bytes)", size);
if (muxer != NULL) {
// ignore this -- we passed the CSD into MediaMuxer when
// we got the format change notification
@@ -362,7 +363,7 @@
}
}
if (size != 0) {
- ALOGV("Got data in buffer %d, size=%d, pts=%lld",
+ ALOGV("Got data in buffer %zu, size=%zu, pts=%" PRId64,
bufIndex, size, ptsUsec);
{ // scope
@@ -473,7 +474,7 @@
ALOGV("Encoder stopping (req=%d)", gStopRequested);
if (gVerbose) {
- printf("Encoder stopping; recorded %u frames in %lld seconds\n",
+ printf("Encoder stopping; recorded %u frames in %" PRId64 " seconds\n",
debugNumFrames, nanoseconds_to_seconds(
systemTime(CLOCK_MONOTONIC) - startWhenNsec));
}
diff --git a/cmds/stagefright/SimplePlayer.cpp b/cmds/stagefright/SimplePlayer.cpp
index 5d2d721..1b2f792 100644
--- a/cmds/stagefright/SimplePlayer.cpp
+++ b/cmds/stagefright/SimplePlayer.cpp
@@ -23,6 +23,7 @@
#include <gui/Surface.h>
#include <media/AudioTrack.h>
#include <media/ICrypto.h>
+#include <media/IMediaHTTPService.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -275,7 +276,8 @@
mExtractor = new NuMediaExtractor;
- status_t err = mExtractor->setDataSource(mPath.c_str());
+ status_t err = mExtractor->setDataSource(
+ NULL /* httpService */, mPath.c_str());
if (err != OK) {
mExtractor.clear();
diff --git a/cmds/stagefright/SineSource.cpp b/cmds/stagefright/SineSource.cpp
index 14b4306..587077a 100644
--- a/cmds/stagefright/SineSource.cpp
+++ b/cmds/stagefright/SineSource.cpp
@@ -24,7 +24,7 @@
}
}
-status_t SineSource::start(MetaData *params) {
+status_t SineSource::start(MetaData * /* params */) {
CHECK(!mStarted);
mGroup = new MediaBufferGroup;
@@ -58,7 +58,7 @@
}
status_t SineSource::read(
- MediaBuffer **out, const ReadOptions *options) {
+ MediaBuffer **out, const ReadOptions * /* options */) {
*out = NULL;
MediaBuffer *buffer;
diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp
index ed7d6cb..96073f1 100644
--- a/cmds/stagefright/audioloop.cpp
+++ b/cmds/stagefright/audioloop.cpp
@@ -1,4 +1,18 @@
-#include "SineSource.h"
+/*
+ * Copyright (C) 2014 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.
+ */
#include <binder/ProcessState.h>
#include <media/mediarecorder.h>
@@ -10,41 +24,79 @@
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
-
-#include <system/audio.h>
+#include "SineSource.h"
using namespace android;
-int main() {
- // We only have an AMR-WB encoder on sholes...
- static bool outputWBAMR = false;
- static const int32_t kSampleRate = outputWBAMR ? 16000 : 8000;
- static const int32_t kNumChannels = 1;
+static void usage(const char* name)
+{
+ fprintf(stderr, "Usage: %s [-d duration] [-m] [-w] [<output-file>]\n", name);
+ fprintf(stderr, "Encodes either a sine wave or microphone input to AMR format\n");
+ fprintf(stderr, " -d duration in seconds, default 5 seconds\n");
+ fprintf(stderr, " -m use microphone for input, default sine source\n");
+ fprintf(stderr, " -w use AMR wideband (default narrowband)\n");
+ fprintf(stderr, " <output-file> output file for AMR encoding,"
+ " if unspecified, decode to speaker.\n");
+}
+
+int main(int argc, char* argv[])
+{
+ static const int channels = 1; // not permitted to be stereo now
+ unsigned duration = 5;
+ bool useMic = false;
+ bool outputWBAMR = false;
+ bool playToSpeaker = true;
+ const char* fileOut = NULL;
+ int ch;
+ while ((ch = getopt(argc, argv, "d:mw")) != -1) {
+ switch (ch) {
+ case 'd':
+ duration = atoi(optarg);
+ break;
+ case 'm':
+ useMic = true;
+ break;
+ case 'w':
+ outputWBAMR = true;
+ break;
+ default:
+ usage(argv[0]);
+ return -1;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc == 1) {
+ fileOut = argv[0];
+ }
+ const int32_t kSampleRate = outputWBAMR ? 16000 : 8000;
+ const int32_t kBitRate = outputWBAMR ? 16000 : 8000;
android::ProcessState::self()->startThreadPool();
-
OMXClient client;
CHECK_EQ(client.connect(), (status_t)OK);
+ sp<MediaSource> source;
-#if 0
- sp<MediaSource> source = new SineSource(kSampleRate, kNumChannels);
-#else
- sp<MediaSource> source = new AudioSource(
- AUDIO_SOURCE_DEFAULT,
- kSampleRate,
- audio_channel_in_mask_from_count(kNumChannels));
-#endif
+ if (useMic) {
+ // talk into the appropriate microphone for the duration
+ source = new AudioSource(
+ AUDIO_SOURCE_MIC,
+ kSampleRate,
+ channels);
+ } else {
+ // use a sine source at 500 hz.
+ source = new SineSource(kSampleRate, channels);
+ }
sp<MetaData> meta = new MetaData;
-
meta->setCString(
kKeyMIMEType,
outputWBAMR ? MEDIA_MIMETYPE_AUDIO_AMR_WB
- : MEDIA_MIMETYPE_AUDIO_AMR_NB);
+ : MEDIA_MIMETYPE_AUDIO_AMR_NB);
- meta->setInt32(kKeyChannelCount, kNumChannels);
+ meta->setInt32(kKeyChannelCount, channels);
meta->setInt32(kKeySampleRate, kSampleRate);
-
+ meta->setInt32(kKeyBitRate, kBitRate);
int32_t maxInputSize;
if (source->getFormat()->findInt32(kKeyMaxInputSize, &maxInputSize)) {
meta->setInt32(kKeyMaxInputSize, maxInputSize);
@@ -55,47 +107,41 @@
meta, true /* createEncoder */,
source);
-#if 1
- sp<AMRWriter> writer = new AMRWriter("/sdcard/out.amr");
- writer->addSource(encoder);
- writer->start();
- sleep(10);
- writer->stop();
-#else
- sp<MediaSource> decoder = OMXCodec::Create(
- client.interface(),
- meta, false /* createEncoder */,
- encoder);
+ if (fileOut != NULL) {
+ // target file specified, write encoded AMR output
+ sp<AMRWriter> writer = new AMRWriter(fileOut);
+ writer->addSource(encoder);
+ writer->start();
+ sleep(duration);
+ writer->stop();
+ } else {
+ // otherwise decode to speaker
+ sp<MediaSource> decoder = OMXCodec::Create(
+ client.interface(),
+ meta, false /* createEncoder */,
+ encoder);
-#if 0
- AudioPlayer *player = new AudioPlayer(NULL);
- player->setSource(decoder);
-
- player->start();
-
- sleep(10);
-
- player->stop();
-
- delete player;
- player = NULL;
-#elif 0
- CHECK_EQ(decoder->start(), (status_t)OK);
-
- MediaBuffer *buffer;
- while (decoder->read(&buffer) == OK) {
- // do something with buffer
-
- putchar('.');
- fflush(stdout);
-
- buffer->release();
- buffer = NULL;
+ if (playToSpeaker) {
+ AudioPlayer *player = new AudioPlayer(NULL);
+ player->setSource(decoder);
+ player->start();
+ sleep(duration);
+ source->stop(); // must stop source otherwise delete player will hang
+ delete player; // there is no player->stop()...
+ } else {
+ CHECK_EQ(decoder->start(), (status_t)OK);
+ MediaBuffer* buffer;
+ while (decoder->read(&buffer) == OK) {
+ // do something with buffer (save it eventually?)
+ // need to stop after some count though...
+ putchar('.');
+ fflush(stdout);
+ buffer->release();
+ buffer = NULL;
+ }
+ CHECK_EQ(decoder->stop(), (status_t)OK);
+ }
}
- CHECK_EQ(decoder->stop(), (status_t)OK);
-#endif
-#endif
-
return 0;
}
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
index fdfefdf..fd02bcc 100644
--- a/cmds/stagefright/codec.cpp
+++ b/cmds/stagefright/codec.cpp
@@ -16,6 +16,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "codec"
+#include <inttypes.h>
#include <utils/Log.h>
#include "SimplePlayer.h"
@@ -23,6 +24,7 @@
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <media/ICrypto.h>
+#include <media/IMediaHTTPService.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -75,7 +77,7 @@
static int64_t kTimeout = 500ll;
sp<NuMediaExtractor> extractor = new NuMediaExtractor;
- if (extractor->setDataSource(path) != OK) {
+ if (extractor->setDataSource(NULL /* httpService */, path) != OK) {
fprintf(stderr, "unable to instantiate extractor.\n");
return 1;
}
@@ -291,13 +293,13 @@
CHECK_EQ((status_t)OK, state->mCodec->release());
if (state->mIsAudio) {
- printf("track %d: %lld bytes received. %.2f KB/sec\n",
+ printf("track %zu: %" PRId64 " bytes received. %.2f KB/sec\n",
i,
state->mNumBytesDecoded,
state->mNumBytesDecoded * 1E6 / 1024 / elapsedTimeUs);
} else {
- printf("track %d: %lld frames decoded, %.2f fps. %lld bytes "
- "received. %.2f KB/sec\n",
+ printf("track %zu: %" PRId64 " frames decoded, %.2f fps. %" PRId64
+ " bytes received. %.2f KB/sec\n",
i,
state->mNumBuffersDecoded,
state->mNumBuffersDecoded * 1E6 / elapsedTimeUs,
diff --git a/cmds/stagefright/muxer.cpp b/cmds/stagefright/muxer.cpp
index cca33e0..f4a33e8 100644
--- a/cmds/stagefright/muxer.cpp
+++ b/cmds/stagefright/muxer.cpp
@@ -16,9 +16,11 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "muxer"
+#include <inttypes.h>
#include <utils/Log.h>
#include <binder/ProcessState.h>
+#include <media/IMediaHTTPService.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
@@ -58,7 +60,7 @@
int trimEndTimeMs,
int rotationDegrees) {
sp<NuMediaExtractor> extractor = new NuMediaExtractor;
- if (extractor->setDataSource(path) != OK) {
+ if (extractor->setDataSource(NULL /* httpService */, path) != OK) {
fprintf(stderr, "unable to instantiate extractor. %s\n", path);
return 1;
}
@@ -198,7 +200,7 @@
trackIndexMap.clear();
int64_t elapsedTimeUs = ALooper::GetNowUs() - muxerStartTimeUs;
- fprintf(stderr, "SUCCESS: muxer generate the video in %lld ms\n",
+ fprintf(stderr, "SUCCESS: muxer generate the video in %" PRId64 " ms\n",
elapsedTimeUs / 1000);
return 0;
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index b7a40c2..fdc352e 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -296,7 +296,7 @@
}
#else
-int main(int argc, char **argv) {
+int main(int /* argc */, char ** /* argv */) {
android::ProcessState::self()->startThreadPool();
OMXClient client;
diff --git a/cmds/stagefright/recordvideo.cpp b/cmds/stagefright/recordvideo.cpp
index c30c122..9f547c7 100644
--- a/cmds/stagefright/recordvideo.cpp
+++ b/cmds/stagefright/recordvideo.cpp
@@ -16,6 +16,7 @@
#include "SineSource.h"
+#include <inttypes.h>
#include <binder/ProcessState.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/AudioPlayer.h>
@@ -72,7 +73,7 @@
return meta;
}
- virtual status_t start(MetaData *params) {
+ virtual status_t start(MetaData *params __unused) {
mNumFramesOutput = 0;
return OK;
}
@@ -82,7 +83,7 @@
}
virtual status_t read(
- MediaBuffer **buffer, const MediaSource::ReadOptions *options) {
+ MediaBuffer **buffer, const MediaSource::ReadOptions *options __unused) {
if (mNumFramesOutput % 10 == 0) {
fprintf(stderr, ".");
@@ -99,8 +100,12 @@
// We don't care about the contents. we just test video encoder
// Also, by skipping the content generation, we can return from
// read() much faster.
- //char x = (char)((double)rand() / RAND_MAX * 255);
- //memset((*buffer)->data(), x, mSize);
+#if 0
+ // iterate through solid planes of color.
+ static unsigned char x = 0x60;
+ memset((*buffer)->data(), x, mSize);
+ x = x >= 0xa0 ? 0x60 : x + 1;
+#endif
(*buffer)->set_range(0, mSize);
(*buffer)->meta_data()->clear();
(*buffer)->meta_data()->setInt64(
@@ -162,7 +167,7 @@
int level = -1; // Encoder specific default
int profile = -1; // Encoder specific default
int codec = 0;
- char *fileName = "/sdcard/output.mp4";
+ const char *fileName = "/sdcard/output.mp4";
bool preferSoftwareCodec = false;
android::ProcessState::self()->startThreadPool();
@@ -312,7 +317,7 @@
fprintf(stderr, "record failed: %d\n", err);
return 1;
}
- fprintf(stderr, "encoding %d frames in %lld us\n", nFrames, (end-start)/1000);
+ fprintf(stderr, "encoding %d frames in %" PRId64 " us\n", nFrames, (end-start)/1000);
fprintf(stderr, "encoding speed is: %.2f fps\n", (nFrames * 1E9) / (end-start));
return 0;
}
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index c817443..0f729a3 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -16,10 +16,15 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "sf2"
+#include <inttypes.h>
#include <utils/Log.h>
+#include <signal.h>
+
#include <binder/ProcessState.h>
+#include <media/IMediaHTTPService.h>
+
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -42,6 +47,18 @@
using namespace android;
+volatile static bool ctrlc = false;
+
+static sighandler_t oldhandler = NULL;
+
+static void mysighandler(int signum) {
+ if (signum == SIGINT) {
+ ctrlc = true;
+ return;
+ }
+ oldhandler(signum);
+}
+
struct Controller : public AHandler {
Controller(const char *uri, bool decodeAudio,
const sp<Surface> &surface, bool renderToSurface)
@@ -62,7 +79,30 @@
virtual ~Controller() {
}
+ virtual void printStatistics() {
+ int64_t delayUs = ALooper::GetNowUs() - mStartTimeUs;
+
+ if (mDecodeAudio) {
+ printf("%" PRId64 " bytes received. %.2f KB/sec\n",
+ mTotalBytesReceived,
+ mTotalBytesReceived * 1E6 / 1024 / delayUs);
+ } else {
+ printf("%d frames decoded, %.2f fps. %" PRId64 " bytes "
+ "received. %.2f KB/sec\n",
+ mNumOutputBuffersReceived,
+ mNumOutputBuffersReceived * 1E6 / delayUs,
+ mTotalBytesReceived,
+ mTotalBytesReceived * 1E6 / 1024 / delayUs);
+ }
+ }
+
virtual void onMessageReceived(const sp<AMessage> &msg) {
+ if (ctrlc) {
+ printf("\n");
+ printStatistics();
+ (new AMessage(kWhatStop, id()))->post();
+ ctrlc = false;
+ }
switch (msg->what()) {
case kWhatStart:
{
@@ -75,7 +115,8 @@
#endif
sp<DataSource> dataSource =
- DataSource::CreateFromURI(mURI.c_str());
+ DataSource::CreateFromURI(
+ NULL /* httpService */, mURI.c_str());
sp<MediaExtractor> extractor =
MediaExtractor::Create(dataSource);
@@ -98,7 +139,10 @@
break;
}
}
- CHECK(mSource != NULL);
+ if (mSource == NULL) {
+ printf("no %s track found\n", mDecodeAudio ? "audio" : "video");
+ exit (1);
+ }
CHECK_EQ(mSource->start(), (status_t)OK);
@@ -167,42 +211,28 @@
int32_t what;
CHECK(msg->findInt32("what", &what));
- if (what == ACodec::kWhatFillThisBuffer) {
+ if (what == CodecBase::kWhatFillThisBuffer) {
onFillThisBuffer(msg);
- } else if (what == ACodec::kWhatDrainThisBuffer) {
+ } else if (what == CodecBase::kWhatDrainThisBuffer) {
if ((mNumOutputBuffersReceived++ % 16) == 0) {
printf(".");
fflush(stdout);
}
onDrainThisBuffer(msg);
- } else if (what == ACodec::kWhatEOS
- || what == ACodec::kWhatError) {
- printf((what == ACodec::kWhatEOS) ? "$\n" : "E\n");
+ } else if (what == CodecBase::kWhatEOS
+ || what == CodecBase::kWhatError) {
+ printf((what == CodecBase::kWhatEOS) ? "$\n" : "E\n");
- int64_t delayUs = ALooper::GetNowUs() - mStartTimeUs;
-
- if (mDecodeAudio) {
- printf("%lld bytes received. %.2f KB/sec\n",
- mTotalBytesReceived,
- mTotalBytesReceived * 1E6 / 1024 / delayUs);
- } else {
- printf("%d frames decoded, %.2f fps. %lld bytes "
- "received. %.2f KB/sec\n",
- mNumOutputBuffersReceived,
- mNumOutputBuffersReceived * 1E6 / delayUs,
- mTotalBytesReceived,
- mTotalBytesReceived * 1E6 / 1024 / delayUs);
- }
-
+ printStatistics();
(new AMessage(kWhatStop, id()))->post();
- } else if (what == ACodec::kWhatFlushCompleted) {
+ } else if (what == CodecBase::kWhatFlushCompleted) {
mSeekState = SEEK_FLUSH_COMPLETED;
mCodec->signalResume();
(new AMessage(kWhatSeek, id()))->post(5000000ll);
- } else if (what == ACodec::kWhatOutputFormatChanged) {
- } else if (what == ACodec::kWhatShutdownCompleted) {
+ } else if (what == CodecBase::kWhatOutputFormatChanged) {
+ } else if (what == CodecBase::kWhatShutdownCompleted) {
mDecodeLooper->unregisterHandler(mCodec->id());
if (mDecodeLooper != looper()) {
@@ -210,12 +240,6 @@
}
looper()->stop();
- } else if (what == ACodec::kWhatError) {
- ALOGE("something went wrong, codec reported an error.");
-
- printf("E\n");
-
- (new AMessage(kWhatStop, id()))->post();
}
break;
}
@@ -638,6 +662,8 @@
looper->registerHandler(controller);
+ signal(SIGINT, mysighandler);
+
controller->startAsync();
CHECK_EQ(looper->start(true /* runOnCallingThread */), (status_t)OK);
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 030bf1b..81edcb4 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -14,20 +14,22 @@
* limitations under the License.
*/
+#include <inttypes.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
//#define LOG_NDEBUG 0
#define LOG_TAG "stagefright"
#include <media/stagefright/foundation/ADebug.h>
-#include <sys/time.h>
-
-#include <stdlib.h>
-#include <string.h>
-
#include "jpeg.h"
#include "SineSource.h"
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
+#include <media/IMediaHTTPService.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/ALooper.h>
#include "include/NuCachedSource2.h"
@@ -49,8 +51,6 @@
#include <private/media/VideoFrame.h>
-#include <fcntl.h>
-
#include <gui/GLConsumer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -89,8 +89,8 @@
int64_t minUs = decodeTimesUs->itemAt(0);
int64_t maxUs = decodeTimesUs->itemAt(n - 1);
- printf("min decode time %lld us (%.2f secs)\n", minUs, minUs / 1E6);
- printf("max decode time %lld us (%.2f secs)\n", maxUs, maxUs / 1E6);
+ printf("min decode time %" PRId64 " us (%.2f secs)\n", minUs, minUs / 1E6);
+ printf("max decode time %" PRId64 " us (%.2f secs)\n", maxUs, maxUs / 1E6);
size_t counts[100];
for (size_t i = 0; i < 100; ++i) {
@@ -110,7 +110,7 @@
int64_t slotUs = minUs + (i * (maxUs - minUs) / 100);
double fps = 1E6 / slotUs;
- printf("[%.2f fps]: %d\n", fps, counts[i]);
+ printf("[%.2f fps]: %zu\n", fps, counts[i]);
}
}
@@ -262,7 +262,7 @@
}
}
- printf("buffer has timestamp %lld us (%.2f secs)\n",
+ printf("buffer has timestamp %" PRId64 " us (%.2f secs)\n",
timestampUs, timestampUs / 1E6);
buffer->release();
@@ -285,7 +285,7 @@
seekTimeUs = (rand() * (float)durationUs) / RAND_MAX;
options.setSeekTo(seekTimeUs);
- printf("seeking to %lld us (%.2f secs)\n",
+ printf("seeking to %" PRId64 " us (%.2f secs)\n",
seekTimeUs, seekTimeUs / 1E6);
}
}
@@ -388,7 +388,7 @@
// sizes may be different across decoders.
printf("avg. %.2f KB/sec\n", totalBytes / 1024 * 1E6 / delay);
- printf("decoded a total of %lld bytes\n", totalBytes);
+ printf("decoded a total of %" PRId64 " bytes\n", totalBytes);
}
}
@@ -574,7 +574,8 @@
int64_t timeUs;
CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
- printf("%lld\t%lld\t%lld\n", seekTimeUs, timeUs, seekTimeUs - timeUs);
+ printf("%" PRId64 "\t%" PRId64 "\t%" PRId64 "\n",
+ seekTimeUs, timeUs, seekTimeUs - timeUs);
buffer->release();
buffer = NULL;
@@ -645,7 +646,7 @@
const CodecProfileLevel &profileLevel =
results[i].mProfileLevels[j];
- printf("%s%ld/%ld", j > 0 ? ", " : "",
+ printf("%s%" PRIu32 "/%" PRIu32, j > 0 ? ", " : "",
profileLevel.mProfile, profileLevel.mLevel);
}
@@ -938,9 +939,13 @@
} else {
CHECK(useSurfaceTexAlloc);
- sp<BufferQueue> bq = new BufferQueue();
- sp<GLConsumer> texture = new GLConsumer(bq, 0 /* tex */);
- gSurface = new Surface(bq);
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+ sp<GLConsumer> texture = new GLConsumer(consumer, 0 /* tex */,
+ GLConsumer::TEXTURE_EXTERNAL, true /* useFenceSync */,
+ false /* isControlledByApp */);
+ gSurface = new Surface(producer);
}
CHECK_EQ((status_t)OK,
@@ -958,7 +963,8 @@
const char *filename = argv[k];
- sp<DataSource> dataSource = DataSource::CreateFromURI(filename);
+ sp<DataSource> dataSource =
+ DataSource::CreateFromURI(NULL /* httpService */, filename);
if (strncasecmp(filename, "sine:", 5) && dataSource == NULL) {
fprintf(stderr, "Unable to create data source.\n");
@@ -1071,7 +1077,7 @@
int64_t thumbTimeUs;
if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) {
- printf("thumbnailTime: %lld us (%.2f secs)\n",
+ printf("thumbnailTime: %" PRId64 " us (%.2f secs)\n",
thumbTimeUs, thumbTimeUs / 1E6);
}
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index dba67a9..0566d14 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -21,6 +21,7 @@
#include <binder/ProcessState.h>
#include <cutils/properties.h> // for property_get
+#include <media/IMediaHTTPService.h>
#include <media/IStreamSource.h>
#include <media/mediaplayer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -159,7 +160,9 @@
MyConvertingStreamSource::MyConvertingStreamSource(const char *filename)
: mCurrentBufferIndex(-1),
mCurrentBufferOffset(0) {
- sp<DataSource> dataSource = DataSource::CreateFromURI(filename);
+ sp<DataSource> dataSource =
+ DataSource::CreateFromURI(NULL /* httpService */, filename);
+
CHECK(dataSource != NULL);
sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
@@ -371,7 +374,7 @@
}
sp<IMediaPlayer> player =
- service->create(client, 0);
+ service->create(client, AUDIO_SESSION_ALLOCATE);
if (player != NULL && player->setDataSource(source) == NO_ERROR) {
player->setVideoSurfaceTexture(surface->getIGraphicBufferProducer());
diff --git a/drm/common/DrmSupportInfo.cpp b/drm/common/DrmSupportInfo.cpp
index 5400bdd..584c6a6 100644
--- a/drm/common/DrmSupportInfo.cpp
+++ b/drm/common/DrmSupportInfo.cpp
@@ -47,7 +47,7 @@
return false;
}
- for (unsigned int i = 0; i < mMimeTypeVector.size(); i++) {
+ for (size_t i = 0; i < mMimeTypeVector.size(); i++) {
const String8 item = mMimeTypeVector.itemAt(i);
if (!strcasecmp(item.string(), mimeType.string())) {
@@ -58,7 +58,7 @@
}
bool DrmSupportInfo::isSupportedFileSuffix(const String8& fileType) const {
- for (unsigned int i = 0; i < mFileSuffixVector.size(); i++) {
+ for (size_t i = 0; i < mFileSuffixVector.size(); i++) {
const String8 item = mFileSuffixVector.itemAt(i);
if (!strcasecmp(item.string(), fileType.string())) {
diff --git a/drm/drmserver/Android.mk b/drm/drmserver/Android.mk
index dc973da..aa0ab9b 100644
--- a/drm/drmserver/Android.mk
+++ b/drm/drmserver/Android.mk
@@ -39,4 +39,6 @@
LOCAL_MODULE_TAGS := optional
+LOCAL_32_BIT_ONLY := true
+
include $(BUILD_EXECUTABLE)
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index dccd23d..d8aeb0c 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -101,7 +101,7 @@
status_t DrmManager::loadPlugIns(const String8& plugInDirPath) {
mPlugInManager.loadPlugIns(plugInDirPath);
Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList();
- for (unsigned int i = 0; i < plugInPathList.size(); ++i) {
+ for (size_t i = 0; i < plugInPathList.size(); ++i) {
String8 plugInPath = plugInPathList[i];
DrmSupportInfo* info = mPlugInManager.getPlugIn(plugInPath).getSupportInfo(0);
if (NULL != info) {
@@ -138,7 +138,7 @@
Mutex::Autolock _l(mLock);
if (!mSupportInfoToPlugInIdMap.isEmpty()) {
Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
- for (unsigned int index = 0; index < plugInIdList.size(); index++) {
+ for (size_t index = 0; index < plugInIdList.size(); index++) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index));
rDrmEngine.initialize(uniqueId);
rDrmEngine.setOnInfoListener(uniqueId, this);
@@ -149,7 +149,7 @@
void DrmManager::removeClient(int uniqueId) {
Mutex::Autolock _l(mLock);
Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
- for (unsigned int index = 0; index < plugInIdList.size(); index++) {
+ for (size_t index = 0; index < plugInIdList.size(); index++) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index));
rDrmEngine.terminate(uniqueId);
}
@@ -208,7 +208,7 @@
bool result = false;
Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList();
- for (unsigned int i = 0; i < plugInPathList.size(); ++i) {
+ for (size_t i = 0; i < plugInPathList.size(); ++i) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInPathList[i]);
result = rDrmEngine.canHandle(uniqueId, path);
@@ -318,7 +318,7 @@
status_t DrmManager::removeAllRights(int uniqueId) {
Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
status_t result = DRM_ERROR_UNKNOWN;
- for (unsigned int index = 0; index < plugInIdList.size(); index++) {
+ for (size_t index = 0; index < plugInIdList.size(); index++) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index));
result = rDrmEngine.removeAllRights(uniqueId);
if (DRM_NO_ERROR != result) {
@@ -412,7 +412,7 @@
if (NULL != handle) {
handle->decryptId = mDecryptSessionId + 1;
- for (unsigned int index = 0; index < plugInIdList.size(); index++) {
+ for (size_t index = 0; index < plugInIdList.size(); index++) {
String8 plugInId = plugInIdList.itemAt(index);
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
result = rDrmEngine.openDecryptSession(uniqueId, handle, fd, offset, length, mime);
@@ -440,7 +440,7 @@
if (NULL != handle) {
handle->decryptId = mDecryptSessionId + 1;
- for (unsigned int index = 0; index < plugInIdList.size(); index++) {
+ for (size_t index = 0; index < plugInIdList.size(); index++) {
String8 plugInId = plugInIdList.itemAt(index);
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
result = rDrmEngine.openDecryptSession(uniqueId, handle, uri, mime);
@@ -565,7 +565,7 @@
String8 plugInId("");
if (EMPTY_STRING != mimeType) {
- for (unsigned int index = 0; index < mSupportInfoToPlugInIdMap.size(); index++) {
+ for (size_t index = 0; index < mSupportInfoToPlugInIdMap.size(); index++) {
const DrmSupportInfo& drmSupportInfo = mSupportInfoToPlugInIdMap.keyAt(index);
if (drmSupportInfo.isSupportedMimeType(mimeType)) {
@@ -581,7 +581,7 @@
String8 plugInId("");
const String8 fileSuffix = path.getPathExtension();
- for (unsigned int index = 0; index < mSupportInfoToPlugInIdMap.size(); index++) {
+ for (size_t index = 0; index < mSupportInfoToPlugInIdMap.size(); index++) {
const DrmSupportInfo& drmSupportInfo = mSupportInfoToPlugInIdMap.keyAt(index);
if (drmSupportInfo.isSupportedFileSuffix(fileSuffix)) {
@@ -599,7 +599,7 @@
void DrmManager::onInfo(const DrmInfoEvent& event) {
Mutex::Autolock _l(mListenerLock);
- for (unsigned int index = 0; index < mServiceListeners.size(); index++) {
+ for (size_t index = 0; index < mServiceListeners.size(); index++) {
int uniqueId = mServiceListeners.keyAt(index);
if (uniqueId == event.getUniqueId()) {
diff --git a/drm/drmserver/DrmManagerService.cpp b/drm/drmserver/DrmManagerService.cpp
index 2b71904..63341e0 100644
--- a/drm/drmserver/DrmManagerService.cpp
+++ b/drm/drmserver/DrmManagerService.cpp
@@ -34,7 +34,18 @@
static Vector<uid_t> trustedUids;
static bool isProtectedCallAllowed() {
- return true;
+ // TODO
+ // Following implementation is just for reference.
+ // Each OEM manufacturer should implement/replace with their own solutions.
+ IPCThreadState* ipcState = IPCThreadState::self();
+ uid_t uid = ipcState->getCallingUid();
+
+ for (unsigned int i = 0; i < trustedUids.size(); ++i) {
+ if (trustedUids[i] == uid) {
+ return true;
+ }
+ }
+ return false;
}
void DrmManagerService::instantiate() {
diff --git a/drm/libdrmframework/include/PlugInManager.h b/drm/libdrmframework/include/PlugInManager.h
index 7bb143f..c1d019a 100644
--- a/drm/libdrmframework/include/PlugInManager.h
+++ b/drm/libdrmframework/include/PlugInManager.h
@@ -80,7 +80,7 @@
Vector<String8> plugInFileList = getPlugInPathList(rsPlugInDirPath);
if (!plugInFileList.isEmpty()) {
- for (unsigned int i = 0; i < plugInFileList.size(); ++i) {
+ for (size_t i = 0; i < plugInFileList.size(); ++i) {
loadPlugIn(plugInFileList[i]);
}
}
@@ -91,7 +91,7 @@
*
*/
void unloadPlugIns() {
- for (unsigned int i = 0; i < m_plugInIdList.size(); ++i) {
+ for (size_t i = 0; i < m_plugInIdList.size(); ++i) {
unloadPlugIn(m_plugInIdList[i]);
}
m_plugInIdList.clear();
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
index 234aef2..f400732 100644
--- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
@@ -316,6 +316,7 @@
if (-1 < fileDesc) {
if (FwdLockFile_attach(fileDesc) < 0) {
+ close(fileDesc);
return mimeString;
}
const char* pMimeType = FwdLockFile_GetContentType(fileDesc);
diff --git a/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.cpp b/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.cpp
new file mode 100644
index 0000000..01f8d65
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 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 "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <openssl/aes.h>
+
+#include "AesCtrDecryptor.h"
+
+namespace clearkeydrm {
+
+static const size_t kBlockBitCount = kBlockSize * 8;
+
+android::status_t AesCtrDecryptor::decrypt(const android::Vector<uint8_t>& key,
+ const Iv iv, const uint8_t* source,
+ uint8_t* destination,
+ const SubSample* subSamples,
+ size_t numSubSamples,
+ size_t* bytesDecryptedOut) {
+ uint32_t blockOffset = 0;
+ uint8_t previousEncryptedCounter[kBlockSize];
+ memset(previousEncryptedCounter, 0, kBlockSize);
+
+ size_t offset = 0;
+ AES_KEY opensslKey;
+ AES_set_encrypt_key(key.array(), kBlockBitCount, &opensslKey);
+ Iv opensslIv;
+ memcpy(opensslIv, iv, sizeof(opensslIv));
+
+ for (size_t i = 0; i < numSubSamples; ++i) {
+ const SubSample& subSample = subSamples[i];
+
+ if (subSample.mNumBytesOfClearData > 0) {
+ memcpy(destination + offset, source + offset,
+ subSample.mNumBytesOfClearData);
+ offset += subSample.mNumBytesOfClearData;
+ }
+
+ if (subSample.mNumBytesOfEncryptedData > 0) {
+ AES_ctr128_encrypt(source + offset, destination + offset,
+ subSample.mNumBytesOfEncryptedData, &opensslKey,
+ opensslIv, previousEncryptedCounter,
+ &blockOffset);
+ offset += subSample.mNumBytesOfEncryptedData;
+ }
+ }
+
+ *bytesDecryptedOut = offset;
+ return android::OK;
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.h b/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.h
new file mode 100644
index 0000000..b416266
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_AES_CTR_DECRYPTOR_H_
+#define CLEARKEY_AES_CTR_DECRYPTOR_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <Utils.h>
+#include <utils/Errors.h>
+#include <utils/Vector.h>
+
+#include "ClearKeyTypes.h"
+
+namespace clearkeydrm {
+
+class AesCtrDecryptor {
+public:
+ AesCtrDecryptor() {}
+
+ android::status_t decrypt(const android::Vector<uint8_t>& key, const Iv iv,
+ const uint8_t* source, uint8_t* destination,
+ const SubSample* subSamples, size_t numSubSamples,
+ size_t* bytesDecryptedOut);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(AesCtrDecryptor);
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_AES_CTR_DECRYPTOR_H_
diff --git a/drm/mediadrm/plugins/clearkey/Android.mk b/drm/mediadrm/plugins/clearkey/Android.mk
new file mode 100644
index 0000000..22a85b4
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/Android.mk
@@ -0,0 +1,62 @@
+#
+# Copyright (C) 2014 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.
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ AesCtrDecryptor.cpp \
+ ClearKeyUUID.cpp \
+ CreatePluginFactories.cpp \
+ CryptoFactory.cpp \
+ CryptoPlugin.cpp \
+ DrmFactory.cpp \
+ DrmPlugin.cpp \
+ InitDataParser.cpp \
+ JsonWebKey.cpp \
+ Session.cpp \
+ SessionLibrary.cpp \
+ Utils.cpp \
+
+LOCAL_C_INCLUDES := \
+ bionic \
+ external/jsmn \
+ external/openssl/include \
+ frameworks/av/drm/mediadrm/plugins/clearkey \
+ frameworks/av/include \
+ frameworks/native/include \
+
+LOCAL_MODULE := libdrmclearkeyplugin
+
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE_RELATIVE_PATH := mediadrm
+
+LOCAL_SHARED_LIBRARIES := \
+ libcrypto \
+ liblog \
+ libstagefright_foundation \
+ libutils \
+
+LOCAL_STATIC_LIBRARIES := \
+ libjsmn \
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
+#########################################################################
+# Build unit tests
+
+include $(LOCAL_PATH)/tests/Android.mk
diff --git a/drm/mediadrm/plugins/clearkey/ClearKeyTypes.h b/drm/mediadrm/plugins/clearkey/ClearKeyTypes.h
new file mode 100644
index 0000000..a28959a
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/ClearKeyTypes.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_TYPES_H_
+#define CLEARKEY_TYPES_H_
+
+#include <media/hardware/CryptoAPI.h>
+#include <openssl/aes.h>
+#include <utils/KeyedVector.h>
+#include <utils/Vector.h>
+
+namespace clearkeydrm {
+
+const uint8_t kBlockSize = AES_BLOCK_SIZE;
+typedef uint8_t KeyId[kBlockSize];
+typedef uint8_t Iv[kBlockSize];
+
+typedef android::CryptoPlugin::SubSample SubSample;
+
+typedef android::KeyedVector<android::Vector<uint8_t>,
+ android::Vector<uint8_t> > KeyMap;
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_TYPES_H_
diff --git a/drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp b/drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp
new file mode 100644
index 0000000..ed050f7
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <string.h>
+
+#include "ClearKeyUUID.h"
+
+namespace clearkeydrm {
+
+bool isClearKeyUUID(const uint8_t uuid[16]) {
+ static const uint8_t kClearKeyUUID[16] = {
+ 0x10,0x77,0xEF,0xEC,0xC0,0xB2,0x4D,0x02,
+ 0xAC,0xE3,0x3C,0x1E,0x52,0xE2,0xFB,0x4B
+ };
+
+ return !memcmp(uuid, kClearKeyUUID, sizeof(kClearKeyUUID));
+}
+
+} // namespace clearkeydrm
diff --git a/libvideoeditor/vss/common/inc/NXPSW_CompilerSwitches.h b/drm/mediadrm/plugins/clearkey/ClearKeyUUID.h
old mode 100755
new mode 100644
similarity index 67%
rename from libvideoeditor/vss/common/inc/NXPSW_CompilerSwitches.h
rename to drm/mediadrm/plugins/clearkey/ClearKeyUUID.h
index 13cac6d..ac99418
--- a/libvideoeditor/vss/common/inc/NXPSW_CompilerSwitches.h
+++ b/drm/mediadrm/plugins/clearkey/ClearKeyUUID.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2014 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.
@@ -14,13 +14,15 @@
* limitations under the License.
*/
-#ifndef NXPSW_COMPILERSWITCHES_H
-#define NXPSW_COMPILERSWITCHES_H
+#ifndef CLEARKEY_UUID_H_
+#define CLEARKEY_UUID_H_
-/* ----- Main features ----- */
-#include "NXPSW_CompilerSwitches_MCS.h" /* Transcoder */
+#include <stdint.h>
-/* ----- Add-ons ----- */
+namespace clearkeydrm {
-#endif /* NXPSW_COMPILERSWITCHES_H */
+bool isClearKeyUUID(const uint8_t uuid[16]);
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_UUID_H_
diff --git a/libvideoeditor/vss/common/inc/NXPSW_CompilerSwitches.h b/drm/mediadrm/plugins/clearkey/CreatePluginFactories.cpp
old mode 100755
new mode 100644
similarity index 61%
copy from libvideoeditor/vss/common/inc/NXPSW_CompilerSwitches.h
copy to drm/mediadrm/plugins/clearkey/CreatePluginFactories.cpp
index 13cac6d..ec1420e
--- a/libvideoeditor/vss/common/inc/NXPSW_CompilerSwitches.h
+++ b/drm/mediadrm/plugins/clearkey/CreatePluginFactories.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2014 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.
@@ -14,13 +14,19 @@
* limitations under the License.
*/
-#ifndef NXPSW_COMPILERSWITCHES_H
-#define NXPSW_COMPILERSWITCHES_H
+#include "CreatePluginFactories.h"
-/* ----- Main features ----- */
-#include "NXPSW_CompilerSwitches_MCS.h" /* Transcoder */
+#include "CryptoFactory.h"
+#include "DrmFactory.h"
-/* ----- Add-ons ----- */
+extern "C" {
-#endif /* NXPSW_COMPILERSWITCHES_H */
+android::DrmFactory* createDrmFactory() {
+ return new clearkeydrm::DrmFactory();
+}
+android::CryptoFactory* createCryptoFactory() {
+ return new clearkeydrm::CryptoFactory();
+}
+
+} // extern "C"
diff --git a/libvideoeditor/vss/common/inc/NXPSW_CompilerSwitches.h b/drm/mediadrm/plugins/clearkey/CreatePluginFactories.h
old mode 100755
new mode 100644
similarity index 60%
copy from libvideoeditor/vss/common/inc/NXPSW_CompilerSwitches.h
copy to drm/mediadrm/plugins/clearkey/CreatePluginFactories.h
index 13cac6d..d9acec1
--- a/libvideoeditor/vss/common/inc/NXPSW_CompilerSwitches.h
+++ b/drm/mediadrm/plugins/clearkey/CreatePluginFactories.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2014 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.
@@ -14,13 +14,15 @@
* limitations under the License.
*/
-#ifndef NXPSW_COMPILERSWITCHES_H
-#define NXPSW_COMPILERSWITCHES_H
+#ifndef CLEARKEY_CREATE_PLUGIN_FACTORIES_H_
+#define CLEARKEY_CREATE_PLUGIN_FACTORIES_H_
-/* ----- Main features ----- */
-#include "NXPSW_CompilerSwitches_MCS.h" /* Transcoder */
+#include <media/drm/DrmAPI.h>
+#include <media/hardware/CryptoAPI.h>
-/* ----- Add-ons ----- */
+extern "C" {
+ android::DrmFactory* createDrmFactory();
+ android::CryptoFactory* createCryptoFactory();
+}
-#endif /* NXPSW_COMPILERSWITCHES_H */
-
+#endif // CLEARKEY_CREATE_PLUGIN_FACTORIES_H_
diff --git a/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp b/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp
new file mode 100644
index 0000000..ee3189b
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 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 "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+#include "CryptoFactory.h"
+
+#include "ClearKeyUUID.h"
+#include "CryptoPlugin.h"
+#include "Session.h"
+#include "SessionLibrary.h"
+
+namespace clearkeydrm {
+
+bool CryptoFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) const {
+ return isClearKeyUUID(uuid);
+}
+
+android::status_t CryptoFactory::createPlugin(
+ const uint8_t uuid[16],
+ const void* data, size_t size,
+ android::CryptoPlugin** plugin) {
+ if (!isCryptoSchemeSupported(uuid)) {
+ *plugin = NULL;
+ return android::BAD_VALUE;
+ }
+
+ android::sp<Session> session = SessionLibrary::get()->findSession(
+ data, size);
+ *plugin = new CryptoPlugin(session);
+ return android::OK;
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/CryptoFactory.h b/drm/mediadrm/plugins/clearkey/CryptoFactory.h
new file mode 100644
index 0000000..568bc4b
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/CryptoFactory.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_CRYPTO_FACTORY_H_
+#define CLEARKEY_CRYPTO_FACTORY_H_
+
+#include <media/hardware/CryptoAPI.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+
+namespace clearkeydrm {
+
+class CryptoFactory : public android::CryptoFactory {
+public:
+ CryptoFactory() {}
+ virtual ~CryptoFactory() {}
+
+ virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) const;
+
+ virtual android::status_t createPlugin(
+ const uint8_t uuid[16],
+ const void* data, size_t size,
+ android::CryptoPlugin** plugin);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(CryptoFactory);
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_CRYPTO_FACTORY_H_
diff --git a/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp
new file mode 100644
index 0000000..adad136
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 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 "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/Errors.h>
+
+#include "CryptoPlugin.h"
+
+namespace clearkeydrm {
+
+using android::Vector;
+using android::AString;
+using android::status_t;
+
+// Returns negative values for error code and positive values for the size of
+// decrypted data. In theory, the output size can be larger than the input
+// size, but in practice this will never happen for AES-CTR.
+ssize_t CryptoPlugin::decrypt(bool secure, const KeyId keyId, const Iv iv,
+ Mode mode, const void* srcPtr,
+ const SubSample* subSamples, size_t numSubSamples,
+ void* dstPtr, AString* errorDetailMsg) {
+ if (secure) {
+ errorDetailMsg->setTo("Secure decryption is not supported with "
+ "ClearKey.");
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ if (mode == kMode_Unencrypted) {
+ size_t offset = 0;
+ for (size_t i = 0; i < numSubSamples; ++i) {
+ const SubSample& subSample = subSamples[i];
+
+ if (subSample.mNumBytesOfEncryptedData != 0) {
+ errorDetailMsg->setTo(
+ "Encrypted subsamples found in allegedly unencrypted "
+ "data.");
+ return android::ERROR_DRM_DECRYPT;
+ }
+
+ if (subSample.mNumBytesOfClearData != 0) {
+ memcpy(reinterpret_cast<uint8_t*>(dstPtr) + offset,
+ reinterpret_cast<const uint8_t*>(srcPtr) + offset,
+ subSample.mNumBytesOfClearData);
+ offset += subSample.mNumBytesOfClearData;
+ }
+ }
+ return static_cast<ssize_t>(offset);
+ } else if (mode == kMode_AES_CTR) {
+ size_t bytesDecrypted;
+ status_t res = mSession->decrypt(keyId, iv, srcPtr, dstPtr, subSamples,
+ numSubSamples, &bytesDecrypted);
+ if (res == android::OK) {
+ return static_cast<ssize_t>(bytesDecrypted);
+ } else {
+ errorDetailMsg->setTo("Decryption Error");
+ return static_cast<ssize_t>(res);
+ }
+ } else {
+ errorDetailMsg->setTo(
+ "Selected encryption mode is not supported by the ClearKey DRM "
+ "Plugin.");
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/CryptoPlugin.h b/drm/mediadrm/plugins/clearkey/CryptoPlugin.h
new file mode 100644
index 0000000..002d9e0
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/CryptoPlugin.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_CRYPTO_PLUGIN_H_
+#define CLEARKEY_CRYPTO_PLUGIN_H_
+
+#include <media/hardware/CryptoAPI.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+#include "ClearKeyTypes.h"
+#include "Session.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+class CryptoPlugin : public android::CryptoPlugin {
+public:
+ CryptoPlugin(const android::sp<Session>& session) : mSession(session) {}
+ virtual ~CryptoPlugin() {}
+
+ virtual bool requiresSecureDecoderComponent(const char* mime) const {
+ UNUSED(mime);
+ return false;
+ }
+
+ virtual ssize_t decrypt(
+ bool secure, const KeyId keyId, const Iv iv,
+ Mode mode, const void* srcPtr,
+ const SubSample* subSamples, size_t numSubSamples,
+ void* dstPtr, android::AString* errorDetailMsg);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(CryptoPlugin);
+
+ android::sp<Session> mSession;
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_CRYPTO_PLUGIN_H_
diff --git a/drm/mediadrm/plugins/clearkey/DrmFactory.cpp b/drm/mediadrm/plugins/clearkey/DrmFactory.cpp
new file mode 100644
index 0000000..40275cf
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/DrmFactory.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 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 "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <utils/Errors.h>
+
+#include "DrmFactory.h"
+
+#include "DrmPlugin.h"
+#include "ClearKeyUUID.h"
+#include "SessionLibrary.h"
+
+namespace clearkeydrm {
+
+bool DrmFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) {
+ return isClearKeyUUID(uuid);
+}
+
+bool DrmFactory::isContentTypeSupported(const android::String8 &initDataType) {
+ // This should match the types handed by InitDataParser.
+ return initDataType == "cenc" ||
+ initDataType == "webm";
+}
+
+android::status_t DrmFactory::createDrmPlugin(
+ const uint8_t uuid[16], android::DrmPlugin** plugin) {
+ if (!isCryptoSchemeSupported(uuid)) {
+ *plugin = NULL;
+ return android::BAD_VALUE;
+ }
+
+ *plugin = new DrmPlugin(SessionLibrary::get());
+ return android::OK;
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/DrmFactory.h b/drm/mediadrm/plugins/clearkey/DrmFactory.h
new file mode 100644
index 0000000..164d3d0
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/DrmFactory.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_DRM_FACTORY_H_
+#define CLEARKEY_DRM_FACTORY_H_
+
+#include <media/drm/DrmAPI.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+class DrmFactory : public android::DrmFactory {
+public:
+ DrmFactory() {}
+ virtual ~DrmFactory() {}
+
+ virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]);
+
+ virtual bool isContentTypeSupported(const android::String8 &initDataType);
+
+ virtual android::status_t createDrmPlugin(
+ const uint8_t uuid[16], android::DrmPlugin** plugin);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(DrmFactory);
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_DRM_FACTORY_H_
diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
new file mode 100644
index 0000000..96fca94
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2014 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 "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/StrongPointer.h>
+
+#include "DrmPlugin.h"
+
+#include "Session.h"
+
+namespace clearkeydrm {
+
+using android::sp;
+
+status_t DrmPlugin::openSession(Vector<uint8_t>& sessionId) {
+ sp<Session> session = mSessionLibrary->createSession();
+ sessionId = session->sessionId();
+ return android::OK;
+}
+
+status_t DrmPlugin::closeSession(const Vector<uint8_t>& sessionId) {
+ sp<Session> session = mSessionLibrary->findSession(sessionId);
+ mSessionLibrary->destroySession(session);
+ return android::OK;
+}
+
+status_t DrmPlugin::getKeyRequest(
+ const Vector<uint8_t>& scope,
+ const Vector<uint8_t>& initData,
+ const String8& initDataType,
+ KeyType keyType,
+ const KeyedVector<String8, String8>& optionalParameters,
+ Vector<uint8_t>& request,
+ String8& defaultUrl) {
+ UNUSED(optionalParameters);
+ if (keyType != kKeyType_Streaming) {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ sp<Session> session = mSessionLibrary->findSession(scope);
+ defaultUrl.clear();
+ return session->getKeyRequest(initData, initDataType, &request);
+}
+
+status_t DrmPlugin::provideKeyResponse(
+ const Vector<uint8_t>& scope,
+ const Vector<uint8_t>& response,
+ Vector<uint8_t>& keySetId) {
+ sp<Session> session = mSessionLibrary->findSession(scope);
+ status_t res = session->provideKeyResponse(response);
+ if (res == android::OK) {
+ keySetId.clear();
+ }
+ return res;
+}
+
+status_t DrmPlugin::getPropertyString(
+ const String8& name, String8& value) const {
+ if (name == "vendor") {
+ value = "Google";
+ } else if (name == "version") {
+ value = "1.0";
+ } else if (name == "description") {
+ value = "ClearKey CDM";
+ } else if (name == "algorithms") {
+ value = "";
+ } else {
+ ALOGE("App requested unknown string property %s", name.string());
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+ return android::OK;
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/DrmPlugin.h
new file mode 100644
index 0000000..27df9cd
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.h
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_DRM_PLUGIN_H_
+#define CLEARKEY_DRM_PLUGIN_H_
+
+#include <media/drm/DrmAPI.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include "SessionLibrary.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+using android::KeyedVector;
+using android::List;
+using android::status_t;
+using android::String8;
+using android::Vector;
+
+class DrmPlugin : public android::DrmPlugin {
+public:
+ DrmPlugin(SessionLibrary* sessionLibrary)
+ : mSessionLibrary(sessionLibrary) {}
+ virtual ~DrmPlugin() {}
+
+ virtual status_t openSession(Vector<uint8_t>& sessionId);
+
+ virtual status_t closeSession(const Vector<uint8_t>& sessionId);
+
+ virtual status_t getKeyRequest(
+ const Vector<uint8_t>& scope,
+ const Vector<uint8_t>& initData,
+ const String8& initDataType,
+ KeyType keyType,
+ const KeyedVector<String8, String8>& optionalParameters,
+ Vector<uint8_t>& request,
+ String8& defaultUrl);
+
+ virtual status_t provideKeyResponse(
+ const Vector<uint8_t>& scope,
+ const Vector<uint8_t>& response,
+ Vector<uint8_t>& keySetId);
+
+ virtual status_t removeKeys(const Vector<uint8_t>& sessionId) {
+ UNUSED(sessionId);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t restoreKeys(
+ const Vector<uint8_t>& sessionId,
+ const Vector<uint8_t>& keySetId) {
+ UNUSED(sessionId);
+ UNUSED(keySetId);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t queryKeyStatus(
+ const Vector<uint8_t>& sessionId,
+ KeyedVector<String8, String8>& infoMap) const {
+ UNUSED(sessionId);
+ UNUSED(infoMap);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t getProvisionRequest(
+ const String8& cert_type,
+ const String8& cert_authority,
+ Vector<uint8_t>& request,
+ String8& defaultUrl) {
+ UNUSED(cert_type);
+ UNUSED(cert_authority);
+ UNUSED(request);
+ UNUSED(defaultUrl);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t provideProvisionResponse(
+ const Vector<uint8_t>& response,
+ Vector<uint8_t>& certificate,
+ Vector<uint8_t>& wrappedKey) {
+ UNUSED(response);
+ UNUSED(certificate);
+ UNUSED(wrappedKey);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t unprovisionDevice() {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t getSecureStops(List<Vector<uint8_t> >& secureStops) {
+ UNUSED(secureStops);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t releaseSecureStops(const Vector<uint8_t>& ssRelease) {
+ UNUSED(ssRelease);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t getPropertyString(
+ const String8& name, String8& value) const;
+
+ virtual status_t getPropertyByteArray(
+ const String8& name, Vector<uint8_t>& value) const {
+ UNUSED(name);
+ UNUSED(value);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t setPropertyString(
+ const String8& name, const String8& value) {
+ UNUSED(name);
+ UNUSED(value);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t setPropertyByteArray(
+ const String8& name, const Vector<uint8_t>& value) {
+ UNUSED(name);
+ UNUSED(value);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t setCipherAlgorithm(
+ const Vector<uint8_t>& sessionId, const String8& algorithm) {
+ UNUSED(sessionId);
+ UNUSED(algorithm);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t setMacAlgorithm(
+ const Vector<uint8_t>& sessionId, const String8& algorithm) {
+ UNUSED(sessionId);
+ UNUSED(algorithm);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t encrypt(
+ const Vector<uint8_t>& sessionId,
+ const Vector<uint8_t>& keyId,
+ const Vector<uint8_t>& input,
+ const Vector<uint8_t>& iv,
+ Vector<uint8_t>& output) {
+ UNUSED(sessionId);
+ UNUSED(keyId);
+ UNUSED(input);
+ UNUSED(iv);
+ UNUSED(output);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t decrypt(
+ const Vector<uint8_t>& sessionId,
+ const Vector<uint8_t>& keyId,
+ const Vector<uint8_t>& input,
+ const Vector<uint8_t>& iv,
+ Vector<uint8_t>& output) {
+ UNUSED(sessionId);
+ UNUSED(keyId);
+ UNUSED(input);
+ UNUSED(iv);
+ UNUSED(output);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t sign(
+ const Vector<uint8_t>& sessionId,
+ const Vector<uint8_t>& keyId,
+ const Vector<uint8_t>& message,
+ Vector<uint8_t>& signature) {
+ UNUSED(sessionId);
+ UNUSED(keyId);
+ UNUSED(message);
+ UNUSED(signature);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t verify(
+ const Vector<uint8_t>& sessionId,
+ const Vector<uint8_t>& keyId,
+ const Vector<uint8_t>& message,
+ const Vector<uint8_t>& signature, bool& match) {
+ UNUSED(sessionId);
+ UNUSED(keyId);
+ UNUSED(message);
+ UNUSED(signature);
+ UNUSED(match);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t signRSA(
+ const Vector<uint8_t>& sessionId,
+ const String8& algorithm,
+ const Vector<uint8_t>& message,
+ const Vector<uint8_t>& wrappedKey,
+ Vector<uint8_t>& signature) {
+ UNUSED(sessionId);
+ UNUSED(algorithm);
+ UNUSED(message);
+ UNUSED(wrappedKey);
+ UNUSED(signature);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(DrmPlugin);
+
+ SessionLibrary* mSessionLibrary;
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_DRM_PLUGIN_H_
diff --git a/drm/mediadrm/plugins/clearkey/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
new file mode 100644
index 0000000..c22d73a
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2014 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 "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <endian.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/base64.h>
+#include <media/stagefright/MediaErrors.h>
+#include <string.h>
+
+#include "InitDataParser.h"
+
+#include "ClearKeyUUID.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+using android::AString;
+using android::String8;
+using android::Vector;
+
+namespace {
+ const size_t kKeyIdSize = 16;
+ const size_t kSystemIdSize = 16;
+}
+
+android::status_t InitDataParser::parse(const Vector<uint8_t>& initData,
+ const String8& initDataType,
+ Vector<uint8_t>* licenseRequest) {
+ // Build a list of the key IDs
+ Vector<const uint8_t*> keyIds;
+ if (initDataType == "cenc") {
+ android::status_t res = parsePssh(initData, &keyIds);
+ if (res != android::OK) {
+ return res;
+ }
+ } else if (initDataType == "webm") {
+ // WebM "init data" is just a single key ID
+ if (initData.size() != kKeyIdSize) {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+ keyIds.push(initData.array());
+ } else {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ // Build the request
+ String8 requestJson = generateRequest(keyIds);
+ licenseRequest->clear();
+ licenseRequest->appendArray(
+ reinterpret_cast<const uint8_t*>(requestJson.string()),
+ requestJson.size());
+ return android::OK;
+}
+
+android::status_t InitDataParser::parsePssh(const Vector<uint8_t>& initData,
+ Vector<const uint8_t*>* keyIds) {
+ size_t readPosition = 0;
+
+ // Validate size field
+ uint32_t expectedSize = initData.size();
+ expectedSize = htonl(expectedSize);
+ if (memcmp(&initData[readPosition], &expectedSize,
+ sizeof(expectedSize)) != 0) {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+ readPosition += sizeof(expectedSize);
+
+ // Validate PSSH box identifier
+ const char psshIdentifier[4] = {'p', 's', 's', 'h'};
+ if (memcmp(&initData[readPosition], psshIdentifier,
+ sizeof(psshIdentifier)) != 0) {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+ readPosition += sizeof(psshIdentifier);
+
+ // Validate EME version number
+ const uint8_t psshVersion1[4] = {1, 0, 0, 0};
+ if (memcmp(&initData[readPosition], psshVersion1,
+ sizeof(psshVersion1)) != 0) {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+ readPosition += sizeof(psshVersion1);
+
+ // Validate system ID
+ if (!isClearKeyUUID(&initData[readPosition])) {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+ readPosition += kSystemIdSize;
+
+ // Read key ID count
+ uint32_t keyIdCount;
+ memcpy(&keyIdCount, &initData[readPosition], sizeof(keyIdCount));
+ keyIdCount = ntohl(keyIdCount);
+ readPosition += sizeof(keyIdCount);
+ if (readPosition + (keyIdCount * kKeyIdSize) !=
+ initData.size() - sizeof(uint32_t)) {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ // Calculate the key ID offsets
+ for (uint32_t i = 0; i < keyIdCount; ++i) {
+ size_t keyIdPosition = readPosition + (i * kKeyIdSize);
+ keyIds->push(&initData[keyIdPosition]);
+ }
+ return android::OK;
+}
+
+String8 InitDataParser::generateRequest(const Vector<const uint8_t*>& keyIds) {
+ const String8 kRequestPrefix("{\"kids\":[");
+ const String8 kRequestSuffix("],\"type\":\"temporary\"}");
+ const String8 kBase64Padding("=");
+
+ String8 request(kRequestPrefix);
+ AString encodedId;
+ for (size_t i = 0; i < keyIds.size(); ++i) {
+ encodedId.clear();
+ android::encodeBase64(keyIds[i], kKeyIdSize, &encodedId);
+ if (i != 0) {
+ request.append(",");
+ }
+ request.appendFormat("\"%s\"", encodedId.c_str());
+ }
+ request.append(kRequestSuffix);
+
+ // Android's Base64 encoder produces padding. EME forbids padding.
+ request.removeAll(kBase64Padding);
+ return request;
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/InitDataParser.h b/drm/mediadrm/plugins/clearkey/InitDataParser.h
new file mode 100644
index 0000000..9505d2a
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/InitDataParser.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_INIT_DATA_PARSER_H_
+#define CLEARKEY_INIT_DATA_PARSER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+namespace clearkeydrm {
+
+class InitDataParser {
+public:
+ InitDataParser() {}
+
+ android::status_t parse(const android::Vector<uint8_t>& initData,
+ const android::String8& initDataType,
+ android::Vector<uint8_t>* licenseRequest);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(InitDataParser);
+
+ android::status_t parsePssh(const android::Vector<uint8_t>& initData,
+ android::Vector<const uint8_t*>* keyIds);
+
+ android::String8 generateRequest(
+ const android::Vector<const uint8_t*>& keyIds);
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_INIT_DATA_PARSER_H_
diff --git a/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp
new file mode 100644
index 0000000..53ffae4
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2014 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_TAG "JsonWebKey"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/base64.h>
+#include <utils/Log.h>
+
+#include "JsonWebKey.h"
+
+namespace {
+const android::String8 kKeysTag("keys");
+const android::String8 kKeyTypeTag("kty");
+const android::String8 kSymmetricKeyValue("oct");
+const android::String8 kKeyTag("k");
+const android::String8 kKeyIdTag("kid");
+const android::String8 kBase64Padding("=");
+}
+
+namespace clearkeydrm {
+
+using android::ABuffer;
+using android::AString;
+
+JsonWebKey::JsonWebKey() {
+}
+
+JsonWebKey::~JsonWebKey() {
+}
+
+/*
+ * Parses a JSON Web Key Set string, initializes a KeyMap with key id:key
+ * pairs from the JSON Web Key Set. Both key ids and keys are base64url
+ * encoded. The KeyMap contains base64url decoded key id:key pairs.
+ *
+ * @return Returns false for errors, true for success.
+ */
+bool JsonWebKey::extractKeysFromJsonWebKeySet(const String8& jsonWebKeySet,
+ KeyMap* keys) {
+
+ keys->clear();
+ if (!parseJsonWebKeySet(jsonWebKeySet, &mJsonObjects)) {
+ return false;
+ }
+
+ // mJsonObjects[0] contains the entire JSON Web Key Set, including
+ // all the base64 encoded keys. Each key is also stored separately as
+ // a JSON object in mJsonObjects[1..n] where n is the total
+ // number of keys in the set.
+ if (!isJsonWebKeySet(mJsonObjects[0])) {
+ return false;
+ }
+
+ String8 encodedKey, encodedKeyId;
+ Vector<uint8_t> decodedKey, decodedKeyId;
+
+ // mJsonObjects[1] contains the first JSON Web Key in the set
+ for (size_t i = 1; i < mJsonObjects.size(); ++i) {
+ encodedKeyId.clear();
+ encodedKey.clear();
+
+ if (!parseJsonObject(mJsonObjects[i], &mTokens))
+ return false;
+
+ if (findKey(mJsonObjects[i], &encodedKeyId, &encodedKey)) {
+ if (encodedKeyId.isEmpty() || encodedKey.isEmpty()) {
+ ALOGE("Must have both key id and key in the JsonWebKey set.");
+ continue;
+ }
+
+ if (!decodeBase64String(encodedKeyId, &decodedKeyId)) {
+ ALOGE("Failed to decode key id(%s)", encodedKeyId.string());
+ continue;
+ }
+
+ if (!decodeBase64String(encodedKey, &decodedKey)) {
+ ALOGE("Failed to decode key(%s)", encodedKey.string());
+ continue;
+ }
+
+ keys->add(decodedKeyId, decodedKey);
+ }
+ }
+ return true;
+}
+
+bool JsonWebKey::decodeBase64String(const String8& encodedText,
+ Vector<uint8_t>* decodedText) {
+
+ decodedText->clear();
+
+ // encodedText should not contain padding characters as per EME spec.
+ if (encodedText.find(kBase64Padding) != -1) {
+ return false;
+ }
+
+ // Since android::decodeBase64() requires padding characters,
+ // add them so length of encodedText is exactly a multiple of 4.
+ int remainder = encodedText.length() % 4;
+ String8 paddedText(encodedText);
+ if (remainder > 0) {
+ for (int i = 0; i < 4 - remainder; ++i) {
+ paddedText.append(kBase64Padding);
+ }
+ }
+
+ android::sp<ABuffer> buffer =
+ android::decodeBase64(AString(paddedText.string()));
+ if (buffer == NULL) {
+ ALOGE("Malformed base64 encoded content found.");
+ return false;
+ }
+
+ decodedText->appendArray(buffer->base(), buffer->size());
+ return true;
+}
+
+bool JsonWebKey::findKey(const String8& jsonObject, String8* keyId,
+ String8* encodedKey) {
+
+ String8 key, value;
+
+ // Only allow symmetric key, i.e. "kty":"oct" pair.
+ if (jsonObject.find(kKeyTypeTag) >= 0) {
+ findValue(kKeyTypeTag, &value);
+ if (0 != value.compare(kSymmetricKeyValue))
+ return false;
+ }
+
+ if (jsonObject.find(kKeyIdTag) >= 0) {
+ findValue(kKeyIdTag, keyId);
+ }
+
+ if (jsonObject.find(kKeyTag) >= 0) {
+ findValue(kKeyTag, encodedKey);
+ }
+ return true;
+}
+
+void JsonWebKey::findValue(const String8 &key, String8* value) {
+ value->clear();
+ const char* valueToken;
+ for (Vector<String8>::const_iterator nextToken = mTokens.begin();
+ nextToken != mTokens.end(); ++nextToken) {
+ if (0 == (*nextToken).compare(key)) {
+ if (nextToken + 1 == mTokens.end())
+ break;
+ valueToken = (*(nextToken + 1)).string();
+ value->setTo(valueToken);
+ nextToken++;
+ break;
+ }
+ }
+}
+
+bool JsonWebKey::isJsonWebKeySet(const String8& jsonObject) const {
+ if (jsonObject.find(kKeysTag) == -1) {
+ ALOGE("JSON Web Key does not contain keys.");
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Parses a JSON objects string and initializes a vector of tokens.
+ *
+ * @return Returns false for errors, true for success.
+ */
+bool JsonWebKey::parseJsonObject(const String8& jsonObject,
+ Vector<String8>* tokens) {
+ jsmn_parser parser;
+
+ jsmn_init(&parser);
+ int numTokens = jsmn_parse(&parser,
+ jsonObject.string(), jsonObject.size(), NULL, 0);
+ if (numTokens < 0) {
+ ALOGE("Parser returns error code=%d", numTokens);
+ return false;
+ }
+
+ unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t);
+ mJsmnTokens.clear();
+ mJsmnTokens.setCapacity(jsmnTokensSize);
+
+ jsmn_init(&parser);
+ int status = jsmn_parse(&parser, jsonObject.string(),
+ jsonObject.size(), mJsmnTokens.editArray(), numTokens);
+ if (status < 0) {
+ ALOGE("Parser returns error code=%d", status);
+ return false;
+ }
+
+ tokens->clear();
+ String8 token;
+ const char *pjs;
+ for (int j = 0; j < numTokens; ++j) {
+ pjs = jsonObject.string() + mJsmnTokens[j].start;
+ if (mJsmnTokens[j].type == JSMN_STRING ||
+ mJsmnTokens[j].type == JSMN_PRIMITIVE) {
+ token.setTo(pjs, mJsmnTokens[j].end - mJsmnTokens[j].start);
+ tokens->add(token);
+ }
+ }
+ return true;
+}
+
+/*
+ * Parses JSON Web Key Set string and initializes a vector of JSON objects.
+ *
+ * @return Returns false for errors, true for success.
+ */
+bool JsonWebKey::parseJsonWebKeySet(const String8& jsonWebKeySet,
+ Vector<String8>* jsonObjects) {
+ if (jsonWebKeySet.isEmpty()) {
+ ALOGE("Empty JSON Web Key");
+ return false;
+ }
+
+ // The jsmn parser only supports unicode encoding.
+ jsmn_parser parser;
+
+ // Computes number of tokens. A token marks the type, offset in
+ // the original string.
+ jsmn_init(&parser);
+ int numTokens = jsmn_parse(&parser,
+ jsonWebKeySet.string(), jsonWebKeySet.size(), NULL, 0);
+ if (numTokens < 0) {
+ ALOGE("Parser returns error code=%d", numTokens);
+ return false;
+ }
+
+ unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t);
+ mJsmnTokens.setCapacity(jsmnTokensSize);
+
+ jsmn_init(&parser);
+ int status = jsmn_parse(&parser, jsonWebKeySet.string(),
+ jsonWebKeySet.size(), mJsmnTokens.editArray(), numTokens);
+ if (status < 0) {
+ ALOGE("Parser returns error code=%d", status);
+ return false;
+ }
+
+ String8 token;
+ const char *pjs;
+ for (int i = 0; i < numTokens; ++i) {
+ pjs = jsonWebKeySet.string() + mJsmnTokens[i].start;
+ if (mJsmnTokens[i].type == JSMN_OBJECT) {
+ token.setTo(pjs, mJsmnTokens[i].end - mJsmnTokens[i].start);
+ jsonObjects->add(token);
+ }
+ }
+ return true;
+}
+
+} // clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/JsonWebKey.h b/drm/mediadrm/plugins/clearkey/JsonWebKey.h
new file mode 100644
index 0000000..6ae50ee
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/JsonWebKey.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_JSON_WEB_KEY_H_
+#define CLEARKEY_JSON_WEB_KEY_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+
+#include "jsmn.h"
+#include "Utils.h"
+#include "ClearKeyTypes.h"
+
+namespace clearkeydrm {
+
+using android::KeyedVector;
+using android::sp;
+using android::String8;
+using android::Vector;
+
+class JsonWebKey {
+ public:
+ JsonWebKey();
+ virtual ~JsonWebKey();
+
+ bool extractKeysFromJsonWebKeySet(const String8& jsonWebKeySet,
+ KeyMap* keys);
+
+ private:
+ Vector<jsmntok_t> mJsmnTokens;
+ Vector<String8> mJsonObjects;
+ Vector<String8> mTokens;
+
+ bool decodeBase64String(const String8& encodedText,
+ Vector<uint8_t>* decodedText);
+ bool findKey(const String8& jsonObject, String8* keyId,
+ String8* encodedKey);
+ void findValue(const String8 &key, String8* value);
+ bool isJsonWebKeySet(const String8& jsonObject) const;
+ bool parseJsonObject(const String8& jsonObject, Vector<String8>* tokens);
+ bool parseJsonWebKeySet(const String8& jsonWebKeySet, Vector<String8>* jsonObjects);
+
+ DISALLOW_EVIL_CONSTRUCTORS(JsonWebKey);
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_JSON_WEB_KEY_H_
diff --git a/drm/mediadrm/plugins/clearkey/Session.cpp b/drm/mediadrm/plugins/clearkey/Session.cpp
new file mode 100644
index 0000000..95016f5
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/Session.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 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 "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/String8.h>
+
+#include "Session.h"
+
+#include "AesCtrDecryptor.h"
+#include "InitDataParser.h"
+#include "JsonWebKey.h"
+
+namespace clearkeydrm {
+
+using android::Mutex;
+using android::String8;
+using android::Vector;
+using android::status_t;
+
+status_t Session::getKeyRequest(
+ const Vector<uint8_t>& initData,
+ const String8& initDataType,
+ Vector<uint8_t>* keyRequest) const {
+ InitDataParser parser;
+ return parser.parse(initData, initDataType, keyRequest);
+}
+
+status_t Session::provideKeyResponse(const Vector<uint8_t>& response) {
+ String8 responseString(
+ reinterpret_cast<const char*>(response.array()), response.size());
+ KeyMap keys;
+
+ Mutex::Autolock lock(mMapLock);
+ JsonWebKey parser;
+ if (parser.extractKeysFromJsonWebKeySet(responseString, &keys)) {
+ for (size_t i = 0; i < keys.size(); ++i) {
+ const KeyMap::key_type& keyId = keys.keyAt(i);
+ const KeyMap::value_type& key = keys.valueAt(i);
+ mKeyMap.add(keyId, key);
+ }
+ return android::OK;
+ } else {
+ return android::ERROR_DRM_UNKNOWN;
+ }
+}
+
+status_t Session::decrypt(
+ const KeyId keyId, const Iv iv, const void* source,
+ void* destination, const SubSample* subSamples,
+ size_t numSubSamples, size_t* bytesDecryptedOut) {
+ Mutex::Autolock lock(mMapLock);
+
+ Vector<uint8_t> keyIdVector;
+ keyIdVector.appendArray(keyId, kBlockSize);
+ if (mKeyMap.indexOfKey(keyIdVector) < 0) {
+ return android::ERROR_DRM_NO_LICENSE;
+ }
+
+ const Vector<uint8_t>& key = mKeyMap.valueFor(keyIdVector);
+ AesCtrDecryptor decryptor;
+ return decryptor.decrypt(
+ key, iv,
+ reinterpret_cast<const uint8_t*>(source),
+ reinterpret_cast<uint8_t*>(destination), subSamples,
+ numSubSamples, bytesDecryptedOut);
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/Session.h b/drm/mediadrm/plugins/clearkey/Session.h
new file mode 100644
index 0000000..cab0dc3
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/Session.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_SESSION_H_
+#define CLEARKEY_SESSION_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include "ClearKeyTypes.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+class Session : public android::RefBase {
+public:
+ explicit Session(const android::Vector<uint8_t>& sessionId)
+ : mSessionId(sessionId) {}
+ virtual ~Session() {}
+
+ const android::Vector<uint8_t>& sessionId() const { return mSessionId; }
+
+ android::status_t getKeyRequest(
+ const android::Vector<uint8_t>& initData,
+ const android::String8& initDataType,
+ android::Vector<uint8_t>* keyRequest) const;
+
+ android::status_t provideKeyResponse(
+ const android::Vector<uint8_t>& response);
+
+ android::status_t decrypt(
+ const KeyId keyId, const Iv iv, const void* source,
+ void* destination, const SubSample* subSamples,
+ size_t numSubSamples, size_t* bytesDecryptedOut);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(Session);
+
+ const android::Vector<uint8_t> mSessionId;
+
+ android::Mutex mMapLock;
+ KeyMap mKeyMap;
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_SESSION_H_
diff --git a/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp b/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp
new file mode 100644
index 0000000..d047c53
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 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 "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <utils/String8.h>
+
+#include "SessionLibrary.h"
+
+namespace clearkeydrm {
+
+using android::Mutex;
+using android::sp;
+using android::String8;
+using android::Vector;
+
+Mutex SessionLibrary::sSingletonLock;
+SessionLibrary* SessionLibrary::sSingleton = NULL;
+
+SessionLibrary* SessionLibrary::get() {
+ Mutex::Autolock lock(sSingletonLock);
+
+ if (sSingleton == NULL) {
+ ALOGD("Instantiating Session Library Singleton.");
+ sSingleton = new SessionLibrary();
+ }
+
+ return sSingleton;
+}
+
+const sp<Session>& SessionLibrary::createSession() {
+ Mutex::Autolock lock(mSessionsLock);
+
+ String8 sessionIdString = String8::format("%u", mNextSessionId);
+ mNextSessionId += 1;
+ Vector<uint8_t> sessionId;
+ sessionId.appendArray(
+ reinterpret_cast<const uint8_t*>(sessionIdString.string()),
+ sessionIdString.size());
+
+ mSessions.add(sessionId, new Session(sessionId));
+ return mSessions.valueFor(sessionId);
+}
+
+const sp<Session>& SessionLibrary::findSession(
+ const Vector<uint8_t>& sessionId) {
+ Mutex::Autolock lock(mSessionsLock);
+ return mSessions.valueFor(sessionId);
+}
+
+const sp<Session>& SessionLibrary::findSession(
+ const void* data, size_t size) {
+ Vector<uint8_t> sessionId;
+ sessionId.appendArray(reinterpret_cast<const uint8_t*>(data), size);
+ return findSession(sessionId);
+}
+
+void SessionLibrary::destroySession(const sp<Session>& session) {
+ Mutex::Autolock lock(mSessionsLock);\
+ mSessions.removeItem(session->sessionId());
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/SessionLibrary.h b/drm/mediadrm/plugins/clearkey/SessionLibrary.h
new file mode 100644
index 0000000..56c8828
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/SessionLibrary.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_SESSION_LIBRARY_H_
+#define CLEARKEY_SESSION_LIBRARY_H_
+
+#include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+
+#include "Session.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+class SessionLibrary {
+public:
+ static SessionLibrary* get();
+
+ const android::sp<Session>& createSession();
+
+ const android::sp<Session>& findSession(
+ const android::Vector<uint8_t>& sessionId);
+
+ const android::sp<Session>& findSession(const void* data, size_t size);
+
+ void destroySession(const android::sp<Session>& session);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(SessionLibrary);
+
+ SessionLibrary() : mNextSessionId(1) {}
+
+ static android::Mutex sSingletonLock;
+ static SessionLibrary* sSingleton;
+
+ android::Mutex mSessionsLock;
+ uint32_t mNextSessionId;
+ android::KeyedVector<android::Vector<uint8_t>, android::sp<Session> >
+ mSessions;
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_SESSION_LIBRARY_H_
diff --git a/drm/mediadrm/plugins/clearkey/Utils.cpp b/drm/mediadrm/plugins/clearkey/Utils.cpp
new file mode 100644
index 0000000..93c643b
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/Utils.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include "Utils.h"
+
+namespace android {
+
+bool operator<(const Vector<uint8_t> &lhs, const Vector<uint8_t> &rhs) {
+ if (lhs.size() < rhs.size()) {
+ return true;
+ } else if (lhs.size() > rhs.size()) {
+ return false;
+ }
+ return memcmp((void *)lhs.array(), (void *)rhs.array(), rhs.size()) < 0;
+}
+
+} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/Utils.h b/drm/mediadrm/plugins/clearkey/Utils.h
new file mode 100644
index 0000000..2543124
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/Utils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_UTILS_H_
+#define CLEARKEY_UTILS_H_
+
+#include <utils/Vector.h>
+
+// Add a comparison operator for this Vector specialization so that it can be
+// used as a key in a KeyedVector.
+namespace android {
+
+bool operator<(const Vector<uint8_t> &lhs, const Vector<uint8_t> &rhs);
+
+} // namespace android
+
+#define UNUSED(x) (void)(x);
+
+#endif // CLEARKEY_UTILS_H_
diff --git a/drm/mediadrm/plugins/clearkey/tests/AesCtrDecryptorUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/AesCtrDecryptorUnittest.cpp
new file mode 100644
index 0000000..039e402
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/tests/AesCtrDecryptorUnittest.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <gtest/gtest.h>
+#include <string.h>
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include "AesCtrDecryptor.h"
+
+namespace clearkeydrm {
+
+using namespace android;
+
+class AesCtrDecryptorTest : public ::testing::Test {
+ protected:
+ typedef uint8_t Key[kBlockSize];
+
+ status_t attemptDecrypt(const Key& key, const Iv& iv, const uint8_t* source,
+ uint8_t* destination, const SubSample* subSamples,
+ size_t numSubSamples, size_t* bytesDecryptedOut) {
+ Vector<uint8_t> keyVector;
+ keyVector.appendArray(key, kBlockSize);
+
+ AesCtrDecryptor decryptor;
+ return decryptor.decrypt(keyVector, iv, source, destination, subSamples,
+ numSubSamples, bytesDecryptedOut);
+ }
+
+ template <size_t totalSize>
+ void attemptDecryptExpectingSuccess(const Key& key, const Iv& iv,
+ const uint8_t* encrypted,
+ const uint8_t* decrypted,
+ const SubSample* subSamples,
+ size_t numSubSamples) {
+ uint8_t outputBuffer[totalSize] = {};
+ size_t bytesDecrypted = 0;
+ ASSERT_EQ(android::OK, attemptDecrypt(key, iv, encrypted, outputBuffer,
+ subSamples, numSubSamples,
+ &bytesDecrypted));
+ EXPECT_EQ(totalSize, bytesDecrypted);
+ EXPECT_EQ(0, memcmp(outputBuffer, decrypted, totalSize));
+ }
+};
+
+TEST_F(AesCtrDecryptorTest, DecryptsContiguousEncryptedBlock) {
+ const size_t kTotalSize = 64;
+ const size_t kNumSubsamples = 1;
+
+ // Test vectors from NIST-800-38A
+ Key key = {
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+ };
+
+ Iv iv = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+
+ uint8_t encrypted[kTotalSize] = {
+ 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+ 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+ 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+ 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+ 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+ 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+ 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+ 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+ };
+
+ uint8_t decrypted[kTotalSize] = {
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+ };
+
+ SubSample subSamples[kNumSubsamples] = {
+ {0, 64}
+ };
+
+ attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+ subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsAlignedBifurcatedEncryptedBlock) {
+ const size_t kTotalSize = 64;
+ const size_t kNumSubsamples = 2;
+
+ // Test vectors from NIST-800-38A
+ Key key = {
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+ };
+
+ Iv iv = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+
+ uint8_t encrypted[kTotalSize] = {
+ 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+ 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+ 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+ 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+ 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+ 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+ 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+ 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+ };
+
+ uint8_t decrypted[kTotalSize] = {
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+ };
+
+ SubSample subSamples[kNumSubsamples] = {
+ {0, 32},
+ {0, 32}
+ };
+
+ attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+ subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsUnalignedBifurcatedEncryptedBlock) {
+ const size_t kTotalSize = 64;
+ const size_t kNumSubsamples = 2;
+
+ // Test vectors from NIST-800-38A
+ Key key = {
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+ };
+
+ Iv iv = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+
+ uint8_t encrypted[kTotalSize] = {
+ 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+ 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+ 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+ 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+ 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+ 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+ 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+ 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+ };
+
+ uint8_t decrypted[kTotalSize] = {
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+ };
+
+ SubSample subSamples[kNumSubsamples] = {
+ {0, 29},
+ {0, 35}
+ };
+
+ attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+ subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsOneMixedSubSample) {
+ const size_t kTotalSize = 72;
+ const size_t kNumSubsamples = 1;
+
+ // Based on test vectors from NIST-800-38A
+ Key key = {
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+ };
+
+ Iv iv = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+
+ uint8_t encrypted[kTotalSize] = {
+ // 8 clear bytes
+ 0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+ // 64 encrypted bytes
+ 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+ 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+ 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+ 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+ 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+ 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+ 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+ 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+ };
+
+ uint8_t decrypted[kTotalSize] = {
+ 0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+ };
+
+ SubSample subSamples[kNumSubsamples] = {
+ {8, 64}
+ };
+
+ attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+ subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsAlignedMixedSubSamples) {
+ const size_t kTotalSize = 80;
+ const size_t kNumSubsamples = 2;
+
+ // Based on test vectors from NIST-800-38A
+ Key key = {
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+ };
+
+ Iv iv = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+
+ uint8_t encrypted[kTotalSize] = {
+ // 8 clear bytes
+ 0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+ // 32 encrypted bytes
+ 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+ 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+ 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+ 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+ // 8 clear bytes
+ 0x94, 0xba, 0x88, 0x2e, 0x0e, 0x12, 0x11, 0x55,
+ // 32 encrypted bytes
+ 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+ 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+ 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+ 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+ };
+
+ uint8_t decrypted[kTotalSize] = {
+ 0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x94, 0xba, 0x88, 0x2e, 0x0e, 0x12, 0x11, 0x55,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+ };
+
+ SubSample subSamples[kNumSubsamples] = {
+ {8, 32},
+ {8, 32}
+ };
+
+ attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+ subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsUnalignedMixedSubSamples) {
+ const size_t kTotalSize = 80;
+ const size_t kNumSubsamples = 2;
+
+ // Based on test vectors from NIST-800-38A
+ Key key = {
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+ };
+
+ Iv iv = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+
+ uint8_t encrypted[kTotalSize] = {
+ // 8 clear bytes
+ 0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+ // 30 encrypted bytes
+ 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+ 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+ 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+ 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff,
+ // 8 clear bytes
+ 0x94, 0xba, 0x88, 0x2e, 0x0e, 0x12, 0x11, 0x55,
+ // 34 encrypted bytes
+ 0xfd, 0xff, 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5,
+ 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0,
+ 0x3e, 0xab, 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe,
+ 0x03, 0xd1, 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00,
+ 0x9c, 0xee
+ };
+
+ uint8_t decrypted[kTotalSize] = {
+ 0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x94, 0xba,
+ 0x88, 0x2e, 0x0e, 0x12, 0x11, 0x55, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+ };
+
+ SubSample subSamples[kNumSubsamples] = {
+ {8, 30},
+ {8, 34}
+ };
+
+ attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+ subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsComplexMixedSubSamples) {
+ const size_t kTotalSize = 72;
+ const size_t kNumSubsamples = 6;
+
+ // Based on test vectors from NIST-800-38A
+ Key key = {
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+ };
+
+ Iv iv = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+
+ uint8_t encrypted[kTotalSize] = {
+ // 4 clear bytes
+ 0xf0, 0x13, 0xca, 0xc7,
+ // 1 encrypted bytes
+ 0x87,
+ // 9 encrypted bytes
+ 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b,
+ 0xef,
+ // 11 clear bytes
+ 0x81, 0x4f, 0x24, 0x87, 0x0e, 0xde, 0xba, 0xad,
+ 0x11, 0x9b, 0x46,
+ // 20 encrypted bytes
+ 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+ 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+ 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff,
+ // 8 clear bytes
+ 0x94, 0xba, 0x88, 0x2e, 0x0e, 0x12, 0x11, 0x55,
+ // 3 clear bytes
+ 0x10, 0xf5, 0x22,
+ // 14 encrypted bytes
+ 0xfd, 0xff, 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5,
+ 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02,
+ // 2 clear bytes
+ 0x02, 0x01
+ };
+
+ uint8_t decrypted[kTotalSize] = {
+ 0xf0, 0x13, 0xca, 0xc7, 0x6b, 0xc1, 0xbe, 0xe2,
+ 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x81, 0x4f,
+ 0x24, 0x87, 0x0e, 0xde, 0xba, 0xad, 0x11, 0x9b,
+ 0x46, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae,
+ 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e,
+ 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x94, 0xba, 0x88,
+ 0x2e, 0x0e, 0x12, 0x11, 0x55, 0x10, 0xf5, 0x22,
+ 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c,
+ 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x02, 0x01
+ };
+
+ SubSample subSamples[kNumSubsamples] = {
+ {4, 1},
+ {0, 9},
+ {11, 20},
+ {8, 0},
+ {3, 14},
+ {2, 0}
+ };
+
+ attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+ subSamples, kNumSubsamples);
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/tests/Android.mk b/drm/mediadrm/plugins/clearkey/tests/Android.mk
new file mode 100644
index 0000000..ac5bb21
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/tests/Android.mk
@@ -0,0 +1,52 @@
+#
+# Copyright (C) 2014 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.
+#
+# ----------------------------------------------------------------
+# Builds ClearKey Drm Tests
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := ClearKeyDrmUnitTest
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+ AesCtrDecryptorUnittest.cpp \
+ InitDataParserUnittest.cpp \
+ JsonWebKeyUnittest.cpp \
+
+LOCAL_C_INCLUDES := \
+ bionic \
+ external/gtest/include \
+ external/jsmn \
+ external/openssl/include \
+ external/stlport/stlport \
+ frameworks/av/drm/mediadrm/plugins/clearkey \
+ frameworks/av/include \
+ frameworks/native/include \
+
+LOCAL_STATIC_LIBRARIES := \
+ libgtest \
+ libgtest_main \
+
+LOCAL_SHARED_LIBRARIES := \
+ libcrypto \
+ libdrmclearkeyplugin \
+ liblog \
+ libstagefright_foundation \
+ libstlport \
+ libutils \
+
+include $(BUILD_NATIVE_TEST)
diff --git a/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
new file mode 100644
index 0000000..4ba65ed
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <gtest/gtest.h>
+#include <string.h>
+
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/base64.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include "InitDataParser.h"
+
+namespace clearkeydrm {
+
+using namespace android;
+
+namespace {
+ const size_t kKeyIdSize = 16;
+ const String8 kCencType("cenc");
+ const String8 kWebMType("webm");
+ const String8 kBase64Padding("=");
+}
+
+class InitDataParserTest : public ::testing::Test {
+ protected:
+ status_t attemptParse(const Vector<uint8_t>& initData,
+ const String8& initDataType,
+ Vector<uint8_t>* licenseRequest) {
+ InitDataParser parser;
+ return parser.parse(initData, initDataType, licenseRequest);
+ }
+
+ void attemptParseExpectingSuccess(const Vector<uint8_t>& initData,
+ const String8& initDataType,
+ const Vector<String8>& expectedKeys) {
+ const String8 kRequestPrefix("{\"kids\":[");
+ const String8 kRequestSuffix("],\"type\":\"temporary\"}");
+ Vector<uint8_t> request;
+ ASSERT_EQ(android::OK, attemptParse(initData, initDataType, &request));
+
+ String8 requestString(reinterpret_cast<const char*>(request.array()),
+ request.size());
+ EXPECT_EQ(0, requestString.find(kRequestPrefix));
+ EXPECT_EQ(requestString.size() - kRequestSuffix.size(),
+ requestString.find(kRequestSuffix));
+ for (size_t i = 0; i < expectedKeys.size(); ++i) {
+ AString encodedIdAString;
+ android::encodeBase64(expectedKeys[i], kKeyIdSize,
+ &encodedIdAString);
+ String8 encodedId(encodedIdAString.c_str());
+ encodedId.removeAll(kBase64Padding);
+ EXPECT_TRUE(requestString.contains(encodedId));
+ }
+ }
+
+ void attemptParseExpectingFailure(const Vector<uint8_t>& initData,
+ const String8& initDataType) {
+ Vector<uint8_t> request;
+ ASSERT_NE(android::OK, attemptParse(initData, initDataType, &request));
+ EXPECT_EQ(0, request.size());
+ }
+};
+
+TEST_F(InitDataParserTest, ParsesSingleKeyPssh) {
+ uint8_t pssh[52] = {
+ 0, 0, 0, 52, // Total Size
+ 'p', 's', 's', 'h', // PSSH
+ 1, 0, 0, 0, // Version
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // System ID
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ 0, 0, 0, 1, // Key Count
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+ 0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, // "01234567890ABCDE"
+ 0, 0, 0, 0 // Data Size (always 0)
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(pssh, 52);
+
+ Vector<String8> expectedKeys;
+ expectedKeys.push(String8("01234567890ABCDE"));
+
+ attemptParseExpectingSuccess(initData, kCencType, expectedKeys);
+}
+
+TEST_F(InitDataParserTest, ParsesMultipleKeyPssh) {
+ uint8_t pssh[84] = {
+ 0, 0, 0, 84, // Total Size
+ 'p', 's', 's', 'h', // PSSH
+ 1, 0, 0, 0, // Version
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // System ID
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ 0, 0, 0, 3, // Key Count
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+ 0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, // "01234567890ABCDE"
+ 0x43, 0x6c, 0x65, 0x61, 0x72, 0x4b, 0x65, 0x79, // Key ID #2
+ 0x43, 0x6c, 0x65, 0x61, 0x72, 0x4b, 0x65, 0x79, // "ClearKeyClearKey"
+ 0x20, 0x47, 0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x20, // Key ID #3
+ 0x20, 0x47, 0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x20, // " GOOGLE GOOGLE "
+ 0, 0, 0, 0 // Data Size (always 0)
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(pssh, 84);
+
+ Vector<String8> expectedKeys;
+ expectedKeys.push(String8("01234567890ABCDE"));
+ expectedKeys.push(String8("ClearKeyClearKey"));
+ expectedKeys.push(String8(" GOOGLE GOOGLE "));
+
+ attemptParseExpectingSuccess(initData, kCencType, expectedKeys);
+}
+
+TEST_F(InitDataParserTest, ParsesWebM) {
+ uint8_t initDataRaw[16] = {
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID
+ 0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, // "01234567890ABCDE"
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(initDataRaw, 16);
+
+ Vector<String8> expectedKeys;
+ expectedKeys.push(String8("01234567890ABCDE"));
+
+ attemptParseExpectingSuccess(initData, kWebMType, expectedKeys);
+}
+
+TEST_F(InitDataParserTest, FailsForPsshTooSmall) {
+ uint8_t pssh[16] = {
+ 0, 0, 0, 52,
+ 'p', 's', 's', 'h',
+ 1, 0, 0, 0,
+ 0x10, 0x77, 0xef, 0xec
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(pssh, 16);
+
+ attemptParseExpectingFailure(initData, kCencType);
+}
+
+TEST_F(InitDataParserTest, FailsForWebMTooSmall) {
+ uint8_t initDataRaw[8] = {
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(initDataRaw, 8);
+
+ attemptParseExpectingFailure(initData, kWebMType);
+}
+
+TEST_F(InitDataParserTest, FailsForPsshBadSystemId) {
+ uint8_t pssh[52] = {
+ 0, 0, 0, 52, // Total Size
+ 'p', 's', 's', 'h', // PSSH
+ 1, 0, 0, 0, // Version
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b, // System ID
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
+ 0, 0, 0, 1, // Key Count
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+ 0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, // "01234567890ABCDE"
+ 0, 0, 0, 0 // Data Size (always 0)
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(pssh, 52);
+
+ attemptParseExpectingFailure(initData, kCencType);
+}
+
+TEST_F(InitDataParserTest, FailsForPsshBadSize) {
+ uint8_t pssh[52] = {
+ 0, 0, 70, 200, // Total Size
+ 'p', 's', 's', 'h', // PSSH
+ 1, 0, 0, 0, // Version
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // System ID
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ 0, 0, 0, 1, // Key Count
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+ 0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, // "01234567890ABCDE"
+ 0, 0, 0, 0 // Data Size (always 0)
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(pssh, 52);
+
+ attemptParseExpectingFailure(initData, kCencType);
+}
+
+TEST_F(InitDataParserTest, FailsForPsshWrongVersion) {
+ uint8_t pssh[52] = {
+ 0, 0, 0, 52, // Total Size
+ 'p', 's', 's', 'h', // PSSH
+ 0, 0, 0, 0, // Version
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // System ID
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ 0, 0, 0, 1, // Key Count
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+ 0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, // "01234567890ABCDE"
+ 0, 0, 0, 0 // Data Size (always 0)
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(pssh, 52);
+
+ attemptParseExpectingFailure(initData, kCencType);
+}
+
+TEST_F(InitDataParserTest, FailsForPsshBadKeyCount) {
+ uint8_t pssh[52] = {
+ 0, 0, 0, 52, // Total Size
+ 'p', 's', 's', 'h', // PSSH
+ 1, 0, 0, 0, // Version
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // System ID
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ 0, 0, 0, 7, // Key Count
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+ 0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, // "01234567890ABCDE"
+ 0, 0, 0, 0 // Data Size (always 0)
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(pssh, 52);
+
+ attemptParseExpectingFailure(initData, kCencType);
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp
new file mode 100644
index 0000000..c3b0d84
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#include <utils/Log.h>
+
+#include "JsonWebKey.h"
+
+#include "gtest/gtest.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+using android::String8;
+using android::Vector;
+
+class JsonWebKeyTest : public ::testing::Test {
+ protected:
+ JsonWebKey* jwk;
+
+ JsonWebKeyTest() {
+ jwk = new JsonWebKey;
+ }
+
+ virtual ~JsonWebKeyTest() {
+ if (jwk)
+ delete jwk;
+ }
+};
+
+void stringFromVector(const Vector<uint8_t>& input,
+ String8* converted) {
+ converted->clear();
+ if (input.isEmpty()) {
+ return;
+ }
+
+ for (size_t i = 0; i < input.size(); ++i) {
+ converted->appendFormat("%c", input.itemAt(i));
+ }
+}
+
+void verifyKeys(const KeyMap& keys, const String8* clearKeys) {
+ if (keys.isEmpty()) {
+ return;
+ }
+
+ String8 keyString;
+ for (size_t i = 0; i < keys.size(); ++i) {
+ stringFromVector(keys.valueAt(i), &keyString);
+ EXPECT_EQ(keyString, clearKeys[i]);
+ }
+}
+
+TEST_F(JsonWebKeyTest, NoSymmetricKey) {
+ const String8 js(
+ "{"
+ "[{"
+ "\"kty\":\"rsa\","
+ "\"alg\":\"A128KW1\","
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\","
+ "\"k\":\"1-GawgguFyGrWKav7AX4VKUg\""
+ "}]"
+ "}");
+
+ KeyMap keys;
+ EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+ EXPECT_TRUE(keys.isEmpty());
+}
+
+TEST_F(JsonWebKeyTest, NoKeysTag) {
+ const String8 js(
+ "{"
+ "[{"
+ "\"kty\":\"oct\","
+ "\"alg\":\"A128KW1\","
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\","
+ "\"k\":\"1-GawgguFyGrWKav7AX4VKUg\""
+ "},"
+ "{"
+ "\"kty\":\"oct\","
+ "\"alg\":\"A128KW2\","
+ "\"k\":\"R29vZCBkYXkh\","
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
+ "}]"
+ "}");
+
+ KeyMap keys;
+ EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+ EXPECT_TRUE(keys.isEmpty());
+}
+
+TEST_F(JsonWebKeyTest, NoKeyId) {
+ const String8 js(
+ "{"
+ "\"keys\":"
+ "[{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "\"k\":\"SGVsbG8gRnJpZW5kISE=\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW2\""
+ "\"k\":\"R29vZCBkYXkh\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
+ "}]"
+ "}");
+
+ KeyMap keys;
+ EXPECT_TRUE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+ EXPECT_TRUE(keys.size() == 1);
+
+ const String8 clearKeys("Good day!");
+ verifyKeys(keys, &clearKeys);
+}
+
+TEST_F(JsonWebKeyTest, NoKey) {
+ const String8 js(
+ "{"
+ "\"keys\":"
+ "[{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "\"kid\":\"`\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW2\""
+ "\"k\":\"R29vZCBkYXkh\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
+ "}]"
+ "}");
+
+ KeyMap keys;
+ EXPECT_TRUE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+ EXPECT_TRUE(keys.size() == 1);
+
+ const String8 clearKeys("Good day!");
+ verifyKeys(keys, &clearKeys);
+}
+
+TEST_F(JsonWebKeyTest, MalformedKey) {
+ const String8 js(
+ "{"
+ "\"keys\":"
+ "[{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "\"k\":\"GawgguFyGrWKav7AX4V???\""
+ "\"kid\":\"67ef0gd8pvfd0=\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "\"k\":\"GawgguFyGrWKav7AX4V???\""
+ "\"kid\":"
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ ":\"GawgguFyGrWKav7AX4V???\""
+ "\"kid\":\"67ef0gd8pvfd0=\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW3\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\""
+ "\"k\":\"R29vZCBkYXkh\""
+ "}]"
+ "}");
+
+ KeyMap keys;
+ EXPECT_TRUE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+ EXPECT_TRUE(keys.size() == 1);
+
+ const String8 clearKeys("Good day!");
+ verifyKeys(keys, &clearKeys);
+}
+
+TEST_F(JsonWebKeyTest, EmptyJsonWebKey) {
+ const String8 js;
+ KeyMap keys;
+ EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+ EXPECT_TRUE(keys.isEmpty());
+}
+
+TEST_F(JsonWebKeyTest, MalformedJsonWebKey) {
+ // Missing begin array '['
+ const String8 js(
+ "{"
+ "\"keys\":"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "\"k\":\"GawgguFyGrWKav7AX4VKUg\""
+ "\"kid\":\"67ef0gd8pvfd0=\""
+ "}"
+ "]"
+ "}");
+
+ KeyMap keys;
+ EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+ EXPECT_TRUE(keys.isEmpty());
+}
+
+TEST_F(JsonWebKeyTest, SameKeyId) {
+ const String8 js(
+ "{"
+ "\"keys\":"
+ "[{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\""
+ "\"k\":\"SGVsbG8gRnJpZW5kISE\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "\"k\":\"SGVsbG8gRnJpZW5kIQ\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW3\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\""
+ "\"k\":\"R29vZCBkYXkh\""
+ "}]"
+ "}");
+
+ KeyMap keys;
+ jwk->extractKeysFromJsonWebKeySet(js, &keys);
+ EXPECT_TRUE(keys.size() == 2);
+
+ const String8 clearKeys[] =
+ { String8("Hello Friend!"), String8("Good day!") };
+ verifyKeys(keys, clearKeys);
+}
+
+TEST_F(JsonWebKeyTest, ExtractWellFormedKeys) {
+ const String8 js(
+ "{"
+ "\"keys\":"
+ "[{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW2\""
+ "\"k\":\"SGVsbG8gRnJpZW5kIQ\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW3\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\""
+ "\"k\":\"R29vZCBkYXkh\""
+ "}]"
+ "}");
+
+ KeyMap keys;
+ jwk->extractKeysFromJsonWebKeySet(js, &keys);
+ EXPECT_TRUE(keys.size() == 2);
+
+ const String8 clearKeys[] =
+ { String8("Hello Friend!"), String8("Good day!") };
+ verifyKeys(keys, clearKeys);
+}
+
+TEST_F(JsonWebKeyTest, ExtractKeys) {
+ const String8 js(
+ "{"
+ "\"keys\":"
+ "[{"
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\""
+ "\"k\":\"SGVsbG8gRnJpZW5kISE\""
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW2\""
+ "\"k\":\"SGVsbG8gRnJpZW5kIQ\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
+ "}"
+ "{"
+ "\"kty\":\"rsa\""
+ "\"alg\":\"A128KW-rsa\""
+ "\"k\":\"R29vZCBkYXkh\""
+ "\"kid\":\"rsa-67ef0gd8pvfd0=\""
+ "}"
+ "{"
+ "\"alg\":\"A128KW3\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\""
+ "\"k\":\"R29vZCBkYXkh\""
+ "\"kty\":\"oct\""
+ "}]"
+ "}");
+
+ KeyMap keys;
+ jwk->extractKeysFromJsonWebKeySet(js, &keys);
+ EXPECT_TRUE(keys.size() == 3);
+
+ const String8 clearKeys[] =
+ { String8("Hello Friend!!"), String8("Hello Friend!"),
+ String8("Good day!") };
+ verifyKeys(keys, clearKeys);
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/mock/Android.mk b/drm/mediadrm/plugins/mock/Android.mk
index ada23a2..26c245b 100644
--- a/drm/mediadrm/plugins/mock/Android.mk
+++ b/drm/mediadrm/plugins/mock/Android.mk
@@ -21,7 +21,8 @@
LOCAL_MODULE := libmockdrmcryptoplugin
-LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)/mediadrm
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE_RELATIVE_PATH := mediadrm
LOCAL_SHARED_LIBRARIES := \
libutils liblog
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
index df0bca3..2ea554b 100644
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
@@ -299,6 +299,12 @@
return OK;
}
+ status_t MockDrmPlugin::unprovisionDevice()
+ {
+ ALOGD("MockDrmPlugin::unprovisionDevice()");
+ return OK;
+ }
+
status_t MockDrmPlugin::getSecureStops(List<Vector<uint8_t> > &secureStops)
{
Mutex::Autolock lock(mLock);
@@ -726,7 +732,7 @@
{
String8 result;
for (size_t i = 0; i < numSubSamples; i++) {
- result.appendFormat("[%d] {clear:%d, encrypted:%d} ", i,
+ result.appendFormat("[%zu] {clear:%zu, encrypted:%zu} ", i,
subSamples[i].mNumBytesOfClearData,
subSamples[i].mNumBytesOfEncryptedData);
}
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
index 97d7052..4b63299 100644
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
@@ -85,6 +85,8 @@
Vector<uint8_t> &certificate,
Vector<uint8_t> &wrappedKey);
+ status_t unprovisionDevice();
+
status_t getSecureStops(List<Vector<uint8_t> > &secureStops);
status_t releaseSecureStops(Vector<uint8_t> const &ssRelease);
diff --git a/include/camera/Camera.h b/include/camera/Camera.h
index 79682b8..2b60842 100644
--- a/include/camera/Camera.h
+++ b/include/camera/Camera.h
@@ -74,6 +74,10 @@
const String16& clientPackageName,
int clientUid);
+ static status_t connectLegacy(int cameraId, int halVersion,
+ const String16& clientPackageName,
+ int clientUid, sp<Camera>& camera);
+
virtual ~Camera();
status_t reconnect();
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index d521543..c6074fc 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -102,6 +102,12 @@
void dump() const;
status_t dump(int fd, const Vector<String16>& args) const;
+ /**
+ * Returns a Vector containing the supported preview formats
+ * as enums given in graphics.h.
+ */
+ void getSupportedPreviewFormats(Vector<int>& formats) const;
+
// Parameter keys to communicate between camera application and driver.
// The access (read/write, read only, or write only) is viewed from the
// perspective of applications, not driver.
@@ -674,6 +680,13 @@
// High-dynamic range mode
static const char LIGHTFX_HDR[];
+ /**
+ * Returns the the supported preview formats as an enum given in graphics.h
+ * corrsponding to the format given in the input string or -1 if no such
+ * conversion exists.
+ */
+ static int previewFormatToEnum(const char* format);
+
private:
DefaultKeyedVector<String8,String8> mMap;
};
diff --git a/include/camera/CameraUtils.h b/include/camera/CameraUtils.h
new file mode 100644
index 0000000..c06f05d
--- /dev/null
+++ b/include/camera/CameraUtils.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 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_CAMERA_CLIENT_CAMERAUTILS_H
+#define ANDROID_CAMERA_CLIENT_CAMERAUTILS_H
+
+#include <camera/CameraMetadata.h>
+#include <utils/Errors.h>
+
+#include <stdint.h>
+
+namespace android {
+
+/**
+ * CameraUtils contains utility methods that are shared between the native
+ * camera client, and the camera service.
+ */
+class CameraUtils {
+ public:
+ /**
+ * Calculate the ANativeWindow transform from the static camera
+ * metadata. This is based on the sensor orientation and lens facing
+ * attributes of the camera device.
+ *
+ * Returns OK on success, or a negative error code.
+ */
+ static status_t getRotationTransform(const CameraMetadata& staticInfo,
+ /*out*/int32_t* transform);
+ private:
+ CameraUtils();
+};
+
+} /* namespace android */
+
+#endif /* ANDROID_CAMERA_CLIENT_CAMERAUTILS_H */
+
diff --git a/include/camera/CaptureResult.h b/include/camera/CaptureResult.h
new file mode 100644
index 0000000..0be7d6f
--- /dev/null
+++ b/include/camera/CaptureResult.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_CAPTURERESULT_H
+#define ANDROID_HARDWARE_CAPTURERESULT_H
+
+#include <utils/RefBase.h>
+#include <camera/CameraMetadata.h>
+
+namespace android {
+
+/**
+ * CaptureResultExtras is a structure to encapsulate various indices for a capture result.
+ * These indices are framework-internal and not sent to the HAL.
+ */
+struct CaptureResultExtras {
+ /**
+ * An integer to index the request sequence that this result belongs to.
+ */
+ int32_t requestId;
+
+ /**
+ * An integer to index this result inside a request sequence, starting from 0.
+ */
+ int32_t burstId;
+
+ /**
+ * TODO: Add documentation for this field.
+ */
+ int32_t afTriggerId;
+
+ /**
+ * TODO: Add documentation for this field.
+ */
+ int32_t precaptureTriggerId;
+
+ /**
+ * A 64bit integer to index the frame number associated with this result.
+ */
+ int64_t frameNumber;
+
+ /**
+ * The partial result count (index) for this capture result.
+ */
+ int32_t partialResultCount;
+
+ /**
+ * Constructor initializes object as invalid by setting requestId to be -1.
+ */
+ CaptureResultExtras()
+ : requestId(-1),
+ burstId(0),
+ afTriggerId(0),
+ precaptureTriggerId(0),
+ frameNumber(0),
+ partialResultCount(0) {
+ }
+
+ /**
+ * This function returns true if it's a valid CaptureResultExtras object.
+ * Otherwise, returns false. It is valid only when requestId is non-negative.
+ */
+ bool isValid();
+
+ status_t readFromParcel(Parcel* parcel);
+ status_t writeToParcel(Parcel* parcel) const;
+};
+
+struct CaptureResult : public virtual LightRefBase<CaptureResult> {
+ CameraMetadata mMetadata;
+ CaptureResultExtras mResultExtras;
+
+ CaptureResult();
+
+ CaptureResult(const CaptureResult& otherResult);
+
+ status_t readFromParcel(Parcel* parcel);
+ status_t writeToParcel(Parcel* parcel) const;
+};
+
+}
+
+#endif /* ANDROID_HARDWARE_CAPTURERESULT_H */
diff --git a/include/camera/ICameraService.h b/include/camera/ICameraService.h
index f342122..f7f06bb 100644
--- a/include/camera/ICameraService.h
+++ b/include/camera/ICameraService.h
@@ -31,6 +31,8 @@
class ICameraDeviceUser;
class ICameraDeviceCallbacks;
class CameraMetadata;
+class VendorTagDescriptor;
+class String16;
class ICameraService : public IInterface
{
@@ -47,21 +49,40 @@
ADD_LISTENER,
REMOVE_LISTENER,
GET_CAMERA_CHARACTERISTICS,
+ GET_CAMERA_VENDOR_TAG_DESCRIPTOR,
+ GET_LEGACY_PARAMETERS,
+ SUPPORTS_CAMERA_API,
+ CONNECT_LEGACY,
};
enum {
USE_CALLING_UID = -1
};
+ enum {
+ API_VERSION_1 = 1,
+ API_VERSION_2 = 2,
+ };
+
+ enum {
+ CAMERA_HAL_API_VERSION_UNSPECIFIED = -1
+ };
+
public:
DECLARE_META_INTERFACE(CameraService);
virtual int32_t getNumberOfCameras() = 0;
virtual status_t getCameraInfo(int cameraId,
- struct CameraInfo* cameraInfo) = 0;
+ /*out*/
+ struct CameraInfo* cameraInfo) = 0;
virtual status_t getCameraCharacteristics(int cameraId,
- CameraMetadata* cameraInfo) = 0;
+ /*out*/
+ CameraMetadata* cameraInfo) = 0;
+
+ virtual status_t getCameraVendorTagDescriptor(
+ /*out*/
+ sp<VendorTagDescriptor>& desc) = 0;
// Returns 'OK' if operation succeeded
// - Errors: ALREADY_EXISTS if the listener was already added
@@ -97,6 +118,30 @@
int clientUid,
/*out*/
sp<ICameraDeviceUser>& device) = 0;
+
+ virtual status_t getLegacyParameters(
+ int cameraId,
+ /*out*/
+ String16* parameters) = 0;
+
+ /**
+ * Returns OK if device supports camera2 api,
+ * returns -EOPNOTSUPP if it doesn't.
+ */
+ virtual status_t supportsCameraApi(
+ int cameraId, int apiVersion) = 0;
+
+ /**
+ * Connect the device as a legacy device for a given HAL version.
+ * For halVersion, use CAMERA_API_DEVICE_VERSION_* for a particular
+ * version, or CAMERA_HAL_API_VERSION_UNSPECIFIED for a service-selected version.
+ */
+ virtual status_t connectLegacy(const sp<ICameraClient>& cameraClient,
+ int cameraId, int halVersion,
+ const String16& clientPackageName,
+ int clientUid,
+ /*out*/
+ sp<ICamera>& device) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/camera/VendorTagDescriptor.h b/include/camera/VendorTagDescriptor.h
new file mode 100644
index 0000000..1758acf
--- /dev/null
+++ b/include/camera/VendorTagDescriptor.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2014 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 VENDOR_TAG_DESCRIPTOR_H
+
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/RefBase.h>
+#include <system/camera_vendor_tags.h>
+
+#include <stdint.h>
+
+namespace android {
+
+class Parcel;
+
+/**
+ * VendorTagDescriptor objects are parcelable containers for the vendor tag
+ * definitions provided, and are typically used to pass the vendor tag
+ * information enumerated by the HAL to clients of the camera service.
+ */
+class VendorTagDescriptor
+ : public LightRefBase<VendorTagDescriptor> {
+ public:
+ virtual ~VendorTagDescriptor();
+
+ /**
+ * The following 'get*' methods implement the corresponding
+ * functions defined in
+ * system/media/camera/include/system/camera_vendor_tags.h
+ */
+
+ // Returns the number of vendor tags defined.
+ int getTagCount() const;
+
+ // Returns an array containing the id's of vendor tags defined.
+ void getTagArray(uint32_t* tagArray) const;
+
+ // Returns the section name string for a given vendor tag id.
+ const char* getSectionName(uint32_t tag) const;
+
+ // Returns the tag name string for a given vendor tag id.
+ const char* getTagName(uint32_t tag) const;
+
+ // Returns the tag type for a given vendor tag id.
+ int getTagType(uint32_t tag) const;
+
+ /**
+ * Write the VendorTagDescriptor object into the given parcel.
+ *
+ * Returns OK on success, or a negative error code.
+ */
+ status_t writeToParcel(
+ /*out*/
+ Parcel* parcel) const;
+
+ /**
+ * Convenience method to get a vector containing all vendor tag
+ * sections, or an empty vector if none are defined.
+ */
+ SortedVector<String8> getAllSectionNames() const;
+
+ /**
+ * Lookup the tag id for a given tag name and section.
+ *
+ * Returns OK on success, or a negative error code.
+ */
+ status_t lookupTag(String8 name, String8 section, /*out*/uint32_t* tag) const;
+
+ /**
+ * Dump the currently configured vendor tags to a file descriptor.
+ */
+ void dump(int fd, int verbosity, int indentation) const;
+
+ // Static methods:
+
+ /**
+ * Create a VendorTagDescriptor object from the given parcel.
+ *
+ * Returns OK on success, or a negative error code.
+ */
+ static status_t createFromParcel(const Parcel* parcel,
+ /*out*/
+ sp<VendorTagDescriptor>& descriptor);
+
+ /**
+ * Create a VendorTagDescriptor object from the given vendor_tag_ops_t
+ * struct.
+ *
+ * Returns OK on success, or a negative error code.
+ */
+ static status_t createDescriptorFromOps(const vendor_tag_ops_t* vOps,
+ /*out*/
+ sp<VendorTagDescriptor>& descriptor);
+
+ /**
+ * Sets the global vendor tag descriptor to use for this process.
+ * Camera metadata operations that access vendor tags will use the
+ * vendor tag definitions set this way.
+ *
+ * Returns OK on success, or a negative error code.
+ */
+ static status_t setAsGlobalVendorTagDescriptor(const sp<VendorTagDescriptor>& desc);
+
+ /**
+ * Clears the global vendor tag descriptor used by this process.
+ */
+ static void clearGlobalVendorTagDescriptor();
+
+ /**
+ * Returns the global vendor tag descriptor used by this process.
+ * This will contain NULL if no vendor tags are defined.
+ */
+ static sp<VendorTagDescriptor> getGlobalVendorTagDescriptor();
+ protected:
+ VendorTagDescriptor();
+ KeyedVector<String8, KeyedVector<String8, uint32_t>*> mReverseMapping;
+ KeyedVector<uint32_t, String8> mTagToNameMap;
+ KeyedVector<uint32_t, uint32_t> mTagToSectionMap; // Value is offset in mSections
+ KeyedVector<uint32_t, int32_t> mTagToTypeMap;
+ SortedVector<String8> mSections;
+ // must be int32_t to be compatible with Parcel::writeInt32
+ int32_t mTagCount;
+ private:
+ vendor_tag_ops mVendorOps;
+};
+
+} /* namespace android */
+
+#define VENDOR_TAG_DESCRIPTOR_H
+#endif /* VENDOR_TAG_DESCRIPTOR_H */
diff --git a/include/camera/camera2/ICameraDeviceCallbacks.h b/include/camera/camera2/ICameraDeviceCallbacks.h
index 8dac4f2..670480b 100644
--- a/include/camera/camera2/ICameraDeviceCallbacks.h
+++ b/include/camera/camera2/ICameraDeviceCallbacks.h
@@ -24,9 +24,12 @@
#include <utils/Timers.h>
#include <system/camera.h>
+#include <camera/CaptureResult.h>
+
namespace android {
class CameraMetadata;
+
class ICameraDeviceCallbacks : public IInterface
{
/**
@@ -39,24 +42,29 @@
* Error codes for CAMERA_MSG_ERROR
*/
enum CameraErrorCode {
+ ERROR_CAMERA_INVALID_ERROR = -1, // To indicate all invalid error codes
ERROR_CAMERA_DISCONNECTED = 0,
ERROR_CAMERA_DEVICE = 1,
- ERROR_CAMERA_SERVICE = 2
+ ERROR_CAMERA_SERVICE = 2,
+ ERROR_CAMERA_REQUEST = 3,
+ ERROR_CAMERA_RESULT = 4,
+ ERROR_CAMERA_BUFFER = 5,
};
// One way
- virtual void onDeviceError(CameraErrorCode errorCode) = 0;
+ virtual void onDeviceError(CameraErrorCode errorCode,
+ const CaptureResultExtras& resultExtras) = 0;
// One way
virtual void onDeviceIdle() = 0;
// One way
- virtual void onCaptureStarted(int32_t requestId,
+ virtual void onCaptureStarted(const CaptureResultExtras& resultExtras,
int64_t timestamp) = 0;
// One way
- virtual void onResultReceived(int32_t requestId,
- const CameraMetadata& result) = 0;
+ virtual void onResultReceived(const CameraMetadata& metadata,
+ const CaptureResultExtras& resultExtras) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/camera/camera2/ICameraDeviceUser.h b/include/camera/camera2/ICameraDeviceUser.h
index f71f302..35488bb 100644
--- a/include/camera/camera2/ICameraDeviceUser.h
+++ b/include/camera/camera2/ICameraDeviceUser.h
@@ -19,6 +19,7 @@
#include <binder/IInterface.h>
#include <binder/Parcel.h>
+#include <utils/List.h>
struct camera_metadata;
@@ -30,6 +31,10 @@
class CaptureRequest;
class CameraMetadata;
+enum {
+ NO_IN_FLIGHT_REPEATING_FRAMES = -1,
+};
+
class ICameraDeviceUser : public IInterface
{
/**
@@ -44,9 +49,55 @@
* Request Handling
**/
+ /**
+ * For streaming requests, output lastFrameNumber is the last frame number
+ * of the previous repeating request.
+ * For non-streaming requests, output lastFrameNumber is the expected last
+ * frame number of the current request.
+ */
virtual int submitRequest(sp<CaptureRequest> request,
- bool streaming = false) = 0;
- virtual status_t cancelRequest(int requestId) = 0;
+ bool streaming = false,
+ /*out*/
+ int64_t* lastFrameNumber = NULL) = 0;
+
+ /**
+ * For streaming requests, output lastFrameNumber is the last frame number
+ * of the previous repeating request.
+ * For non-streaming requests, output lastFrameNumber is the expected last
+ * frame number of the current request.
+ */
+ virtual int submitRequestList(List<sp<CaptureRequest> > requestList,
+ bool streaming = false,
+ /*out*/
+ int64_t* lastFrameNumber = NULL) = 0;
+
+ /**
+ * Output lastFrameNumber is the last frame number of the previous repeating request.
+ */
+ virtual status_t cancelRequest(int requestId,
+ /*out*/
+ int64_t* lastFrameNumber = NULL) = 0;
+
+ /**
+ * Begin the device configuration.
+ *
+ * <p>
+ * beginConfigure must be called before any call to deleteStream, createStream,
+ * or endConfigure. It is not valid to call this when the device is not idle.
+ * <p>
+ */
+ virtual status_t beginConfigure() = 0;
+
+ /**
+ * End the device configuration.
+ *
+ * <p>
+ * endConfigure must be called after stream configuration is complete (i.e. after
+ * a call to beginConfigure and subsequent createStream/deleteStream calls). This
+ * must be called before any requests can be submitted.
+ * <p>
+ */
+ virtual status_t endConfigure() = 0;
virtual status_t deleteStream(int streamId) = 0;
virtual status_t createStream(
@@ -64,8 +115,12 @@
// Wait until all the submitted requests have finished processing
virtual status_t waitUntilIdle() = 0;
- // Flush all pending and in-progress work as quickly as possible.
- virtual status_t flush() = 0;
+ /**
+ * Flush all pending and in-progress work as quickly as possible.
+ * Output lastFrameNumber is the last frame number of the previous repeating request.
+ */
+ virtual status_t flush(/*out*/
+ int64_t* lastFrameNumber = NULL) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/media/AudioBufferProvider.h b/include/media/AudioBufferProvider.h
index ef392f0..7be449c 100644
--- a/include/media/AudioBufferProvider.h
+++ b/include/media/AudioBufferProvider.h
@@ -61,6 +61,17 @@
// buffer->frameCount 0
virtual status_t getNextBuffer(Buffer* buffer, int64_t pts = kInvalidPTS) = 0;
+ // Release (a portion of) the buffer previously obtained by getNextBuffer().
+ // It is permissible to call releaseBuffer() multiple times per getNextBuffer().
+ // On entry:
+ // buffer->frameCount number of frames to release, must be <= number of frames
+ // obtained but not yet released
+ // buffer->raw unused
+ // On return:
+ // buffer->frameCount 0; implementation MUST set to zero
+ // buffer->raw undefined; implementation is PERMITTED to set to any value,
+ // so if caller needs to continue using this buffer it must
+ // keep track of the pointer itself
virtual void releaseBuffer(Buffer* buffer) = 0;
};
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h
index eef1b41..583695d 100644
--- a/include/media/AudioEffect.h
+++ b/include/media/AudioEffect.h
@@ -36,7 +36,7 @@
// ----------------------------------------------------------------------------
-class effect_param_cblk_t;
+struct effect_param_cblk_t;
// ----------------------------------------------------------------------------
@@ -218,8 +218,9 @@
* higher priorities, 0 being the normal priority.
* cbf: optional callback function (see effect_callback_t)
* user: pointer to context for use by the callback receiver.
- * sessionID: audio session this effect is associated to. If 0, the effect will be global to
- * the output mix. If not 0, the effect will be applied to all players
+ * sessionID: audio session this effect is associated to.
+ * If equal to AUDIO_SESSION_OUTPUT_MIX, the effect will be global to
+ * the output mix. Otherwise, the effect will be applied to all players
* (AudioTrack or MediaPLayer) within the same audio session.
* io: HAL audio output or input stream to which this effect must be attached. Leave at 0 for
* automatic output selection by AudioFlinger.
@@ -230,8 +231,8 @@
int32_t priority = 0,
effect_callback_t cbf = NULL,
void* user = NULL,
- int sessionId = 0,
- audio_io_handle_t io = 0
+ int sessionId = AUDIO_SESSION_OUTPUT_MIX,
+ audio_io_handle_t io = AUDIO_IO_HANDLE_NONE
);
/* Constructor.
@@ -242,8 +243,8 @@
int32_t priority = 0,
effect_callback_t cbf = NULL,
void* user = NULL,
- int sessionId = 0,
- audio_io_handle_t io = 0
+ int sessionId = AUDIO_SESSION_OUTPUT_MIX,
+ audio_io_handle_t io = AUDIO_IO_HANDLE_NONE
);
/* Terminates the AudioEffect and unregisters it from AudioFlinger.
@@ -264,8 +265,8 @@
int32_t priority = 0,
effect_callback_t cbf = NULL,
void* user = NULL,
- int sessionId = 0,
- audio_io_handle_t io = 0
+ int sessionId = AUDIO_SESSION_OUTPUT_MIX,
+ audio_io_handle_t io = AUDIO_IO_HANDLE_NONE
);
/* Result of constructing the AudioEffect. This must be checked
@@ -453,6 +454,7 @@
sp<EffectClient> mIEffectClient; // IEffectClient implementation
sp<IMemory> mCblkMemory; // shared memory for deferred parameter setting
effect_param_cblk_t* mCblk; // control block for deferred parameter setting
+ pid_t mClientPid;
};
diff --git a/include/media/AudioPolicyHelper.h b/include/media/AudioPolicyHelper.h
new file mode 100644
index 0000000..f4afd45
--- /dev/null
+++ b/include/media/AudioPolicyHelper.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 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 AUDIO_POLICY_HELPER_H_
+#define AUDIO_POLICY_HELPER_H_
+
+#include <system/audio.h>
+
+audio_stream_type_t audio_attributes_to_stream_type(const audio_attributes_t *attr)
+{
+ // flags to stream type mapping
+ if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) {
+ return AUDIO_STREAM_ENFORCED_AUDIBLE;
+ }
+ if ((attr->flags & AUDIO_FLAG_SCO) == AUDIO_FLAG_SCO) {
+ return AUDIO_STREAM_BLUETOOTH_SCO;
+ }
+
+ // usage to stream type mapping
+ switch (attr->usage) {
+ case AUDIO_USAGE_MEDIA:
+ case AUDIO_USAGE_GAME:
+ case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY:
+ case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
+ return AUDIO_STREAM_MUSIC;
+ case AUDIO_USAGE_ASSISTANCE_SONIFICATION:
+ return AUDIO_STREAM_SYSTEM;
+ case AUDIO_USAGE_VOICE_COMMUNICATION:
+ return AUDIO_STREAM_VOICE_CALL;
+
+ case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING:
+ return AUDIO_STREAM_DTMF;
+
+ case AUDIO_USAGE_ALARM:
+ return AUDIO_STREAM_ALARM;
+ case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
+ return AUDIO_STREAM_RING;
+
+ case AUDIO_USAGE_NOTIFICATION:
+ case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+ case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+ case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+ case AUDIO_USAGE_NOTIFICATION_EVENT:
+ return AUDIO_STREAM_NOTIFICATION;
+
+ case AUDIO_USAGE_UNKNOWN:
+ default:
+ return AUDIO_STREAM_MUSIC;
+ }
+}
+
+#endif //AUDIO_POLICY_HELPER_H_
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 052064d..4edc1bf 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -26,7 +26,7 @@
// ----------------------------------------------------------------------------
-class audio_track_cblk_t;
+struct audio_track_cblk_t;
class AudioRecordClientProxy;
// ----------------------------------------------------------------------------
@@ -39,8 +39,12 @@
* Keep in sync with frameworks/base/media/java/android/media/AudioRecord.java NATIVE_EVENT_*.
*/
enum event_type {
- EVENT_MORE_DATA = 0, // Request to read more data from PCM buffer.
- EVENT_OVERRUN = 1, // PCM buffer overrun occurred.
+ EVENT_MORE_DATA = 0, // Request to read available data from buffer.
+ // If this event is delivered but the callback handler
+ // does not want to read the available data, the handler must
+ // explicitly
+ // ignore the event by setting frameCount to zero.
+ EVENT_OVERRUN = 1, // Buffer overrun occurred.
EVENT_MARKER = 2, // Record head is at the specified marker position
// (See setMarkerPosition()).
EVENT_NEW_POS = 3, // Record head is at a new position
@@ -60,9 +64,10 @@
size_t frameCount; // number of sample frames corresponding to size;
// on input it is the number of frames available,
// on output is the number of frames actually drained
- // (currently ignored, but will make the primary field in future)
+ // (currently ignored but will make the primary field in future)
size_t size; // input/output in bytes == frameCount * frameSize
+ // on output is the number of bytes actually drained
// FIXME this is redundant with respect to frameCount,
// and TRANSFER_OBTAIN mode is broken for 8-bit data
// since we don't define the frame format
@@ -76,7 +81,7 @@
/* As a convenience, if a callback is supplied, a handler thread
* is automatically created with the appropriate priority. This thread
- * invokes the callback when a new buffer becomes ready or various conditions occur.
+ * invokes the callback when a new buffer becomes available or various conditions occur.
* Parameters:
*
* event: type of event notified (see enum AudioRecord::event_type).
@@ -99,6 +104,8 @@
* - NO_ERROR: successful operation
* - NO_INIT: audio server or audio hardware not initialized
* - BAD_VALUE: unsupported configuration
+ * frameCount is guaranteed to be non-zero if status is NO_ERROR,
+ * and is undefined otherwise.
*/
static status_t getMinFrameCount(size_t* frameCount,
@@ -109,7 +116,7 @@
/* How data is transferred from AudioRecord
*/
enum transfer_type {
- TRANSFER_DEFAULT, // not specified explicitly; determine from other parameters
+ TRANSFER_DEFAULT, // not specified explicitly; determine from the other parameters
TRANSFER_CALLBACK, // callback EVENT_MORE_DATA
TRANSFER_OBTAIN, // FIXME deprecated: call obtainBuffer() and releaseBuffer()
TRANSFER_SYNC, // synchronous read()
@@ -137,7 +144,7 @@
* be larger if the requested size is not compatible with current audio HAL
* latency. Zero means to use a default value.
* cbf: Callback function. If not null, this function is called periodically
- * to consume new PCM data and inform of marker, position updates, etc.
+ * to consume new data and inform of marker, position updates, etc.
* user: Context for use by the callback receiver.
* notificationFrames: The callback function is called each time notificationFrames PCM
* frames are ready in record track output buffer.
@@ -151,11 +158,11 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- int frameCount = 0,
+ size_t frameCount = 0,
callback_t cbf = NULL,
void* user = NULL,
- int notificationFrames = 0,
- int sessionId = 0,
+ uint32_t notificationFrames = 0,
+ int sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE);
@@ -171,9 +178,10 @@
* Returned status (from utils/Errors.h) can be:
* - NO_ERROR: successful intialization
* - INVALID_OPERATION: AudioRecord is already initialized or record device is already in use
- * - BAD_VALUE: invalid parameter (channels, format, sampleRate...)
+ * - BAD_VALUE: invalid parameter (channelMask, format, sampleRate...)
* - NO_INIT: audio server or audio hardware not initialized
* - PERMISSION_DENIED: recording is not allowed for the requesting process
+ * If status is not equal to NO_ERROR, don't call any other APIs on this AudioRecord.
*
* Parameters not listed in the AudioRecord constructors above:
*
@@ -183,16 +191,16 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- int frameCount = 0,
+ size_t frameCount = 0,
callback_t cbf = NULL,
void* user = NULL,
- int notificationFrames = 0,
+ uint32_t notificationFrames = 0,
bool threadCanCallJava = false,
- int sessionId = 0,
+ int sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE);
- /* Result of constructing the AudioRecord. This must be checked
+ /* Result of constructing the AudioRecord. This must be checked for successful initialization
* before using any AudioRecord API (except for set()), because using
* an uninitialized AudioRecord produces undefined results.
* See set() method above for possible return codes.
@@ -221,7 +229,7 @@
status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE,
int triggerSession = 0);
- /* Stop a track. If set, the callback will cease being called. Note that obtainBuffer() still
+ /* Stop a track. The callback will cease being called. Note that obtainBuffer() still
* works and will drain buffers until the pool is exhausted, and then will return WOULD_BLOCK.
*/
void stop();
@@ -232,11 +240,16 @@
*/
uint32_t getSampleRate() const { return mSampleRate; }
+ /* Return the notification frame count.
+ * This is approximately how often the callback is invoked, for transfer type TRANSFER_CALLBACK.
+ */
+ size_t notificationFrames() const { return mNotificationFramesAct; }
+
/* Sets marker position. When record reaches the number of frames specified,
* a callback with event type EVENT_MARKER is called. Calling setMarkerPosition
* with marker == 0 cancels marker notification callback.
* To set a marker at a position which would compute as 0,
- * a workaround is to the set the marker at a nearby position such as ~0 or 1.
+ * a workaround is to set the marker at a nearby position such as ~0 or 1.
* If the AudioRecord has been opened with no callback function associated,
* the operation will fail.
*
@@ -378,8 +391,10 @@
* returning the current value by this function call. Such loss typically occurs when the
* user space process is blocked longer than the capacity of audio driver buffers.
* Units: the number of input audio frames.
+ * FIXME The side-effect of resetting the counter may be incompatible with multi-client.
+ * Consider making it more like AudioTrack::getUnderrunFrames which doesn't have side effects.
*/
- unsigned int getInputFramesLost() const;
+ uint32_t getInputFramesLost() const;
private:
/* copying audio record objects is not allowed */
@@ -412,6 +427,7 @@
bool mPaused; // whether thread is requested to pause at next loop entry
bool mPausedInt; // whether thread internally requests pause
nsecs_t mPausedNs; // if mPausedInt then associated timeout, otherwise ignored
+ bool mIgnoreNextPausedInt; // whether to ignore next mPausedInt request
};
// body of AudioRecordThread::threadLoop()
@@ -422,9 +438,10 @@
// NS_INACTIVE inactive so don't run again until re-started
// NS_NEVER never again
static const nsecs_t NS_WHENEVER = -1, NS_INACTIVE = -2, NS_NEVER = -3;
- nsecs_t processAudioBuffer(const sp<AudioRecordThread>& thread);
+ nsecs_t processAudioBuffer();
// caller must hold lock on mLock for all _l methods
+
status_t openRecord_l(size_t epoch);
// FIXME enum is faster than strcmp() for parameter 'from'
@@ -444,14 +461,16 @@
// for notification APIs
uint32_t mNotificationFramesReq; // requested number of frames between each
// notification callback
+ // as specified in constructor or set()
uint32_t mNotificationFramesAct; // actual number of frames between each
// notification callback
- bool mRefreshRemaining; // processAudioBuffer() should refresh next 2
+ bool mRefreshRemaining; // processAudioBuffer() should refresh
+ // mRemainingFrames and mRetryOnPartialBuffer
// These are private to processAudioBuffer(), and are not protected by a lock
uint32_t mRemainingFrames; // number of frames to request in obtainBuffer()
bool mRetryOnPartialBuffer; // sleep and retry after partial obtainBuffer()
- int mObservedSequence; // last observed value of mSequence
+ uint32_t mObservedSequence; // last observed value of mSequence
uint32_t mMarkerPosition; // in wrapping (overflow) frame units
bool mMarkerReached;
@@ -460,9 +479,13 @@
status_t mStatus;
+ size_t mFrameCount; // corresponds to current IAudioRecord, value is
+ // reported back by AudioFlinger to the client
+ size_t mReqFrameCount; // frame count to request the first or next time
+ // a new IAudioRecord is needed, non-decreasing
+
// constant after constructor or set()
uint32_t mSampleRate;
- size_t mFrameCount;
audio_format_t mFormat;
uint32_t mChannelCount;
size_t mFrameSize; // app-level frame size == AudioFlinger frame size
@@ -473,12 +496,13 @@
int mSessionId;
transfer_type mTransfer;
- audio_io_handle_t mInput; // returned by AudioSystem::getInput()
-
- // may be changed if IAudioRecord object is re-created
+ // Next 5 fields may be changed if IAudioRecord is re-created, but always != 0
+ // provided the initial set() was successful
sp<IAudioRecord> mAudioRecord;
sp<IMemory> mCblkMemory;
audio_track_cblk_t* mCblk; // re-load after mLock.unlock()
+ sp<IMemory> mBufferMemory;
+ audio_io_handle_t mInput; // returned by AudioSystem::getInput()
int mPreviousPriority; // before start()
SchedPolicy mPreviousSchedulingGroup;
diff --git a/include/media/AudioResamplerPublic.h b/include/media/AudioResamplerPublic.h
new file mode 100644
index 0000000..97847a0
--- /dev/null
+++ b/include/media/AudioResamplerPublic.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 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_AUDIO_RESAMPLER_PUBLIC_H
+#define ANDROID_AUDIO_RESAMPLER_PUBLIC_H
+
+// AUDIO_RESAMPLER_DOWN_RATIO_MAX is the maximum ratio between the original
+// audio sample rate and the target rate when downsampling,
+// as permitted in the audio framework, e.g. AudioTrack and AudioFlinger.
+// In practice, it is not recommended to downsample more than 6:1
+// for best audio quality, even though the audio framework permits a larger
+// downsampling ratio.
+// TODO: replace with an API
+#define AUDIO_RESAMPLER_DOWN_RATIO_MAX 256
+
+#endif // ANDROID_AUDIO_RESAMPLER_PUBLIC_H
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 225ef76..f8c0198 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -19,6 +19,7 @@
#include <hardware/audio_effect.h>
#include <media/IAudioFlingerClient.h>
+#include <media/IAudioPolicyServiceClient.h>
#include <system/audio.h>
#include <system/audio_policy.h>
#include <utils/Errors.h>
@@ -67,20 +68,24 @@
// returns true in *state if tracks are active on the specified stream or have been active
// in the past inPastMs milliseconds
- static status_t isStreamActive(audio_stream_type_t stream, bool *state, uint32_t inPastMs = 0);
+ static status_t isStreamActive(audio_stream_type_t stream, bool *state, uint32_t inPastMs);
// returns true in *state if tracks are active for what qualifies as remote playback
// on the specified stream or have been active in the past inPastMs milliseconds. Remote
// playback isn't mutually exclusive with local playback.
static status_t isStreamActiveRemotely(audio_stream_type_t stream, bool *state,
- uint32_t inPastMs = 0);
+ uint32_t inPastMs);
// returns true in *state if a recorder is currently recording with the specified source
static status_t isSourceActive(audio_source_t source, bool *state);
// set/get audio hardware parameters. The function accepts a list of parameters
// key value pairs in the form: key1=value1;key2=value2;...
// Some keys are reserved for standard parameters (See AudioParameter class).
+ // The versions with audio_io_handle_t are intended for internal media framework use only.
static status_t setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs);
static String8 getParameters(audio_io_handle_t ioHandle, const String8& keys);
+ // The versions without audio_io_handle_t are intended for JNI.
+ static status_t setParameters(const String8& keyValuePairs);
+ static String8 getParameters(const String8& keys);
static void setErrorCallback(audio_error_callback cb);
@@ -90,36 +95,37 @@
static float linearToLog(int volume);
static int logToLinear(float volume);
+ // Returned samplingRate and frameCount output values are guaranteed
+ // to be non-zero if status == NO_ERROR
static status_t getOutputSamplingRate(uint32_t* samplingRate,
- audio_stream_type_t stream = AUDIO_STREAM_DEFAULT);
+ audio_stream_type_t stream);
+ static status_t getOutputSamplingRateForAttr(uint32_t* samplingRate,
+ const audio_attributes_t *attr);
static status_t getOutputFrameCount(size_t* frameCount,
- audio_stream_type_t stream = AUDIO_STREAM_DEFAULT);
+ audio_stream_type_t stream);
static status_t getOutputLatency(uint32_t* latency,
- audio_stream_type_t stream = AUDIO_STREAM_DEFAULT);
+ audio_stream_type_t stream);
static status_t getSamplingRate(audio_io_handle_t output,
- audio_stream_type_t streamType,
uint32_t* samplingRate);
// returns the number of frames per audio HAL write buffer. Corresponds to
- // audio_stream->get_buffer_size()/audio_stream_frame_size()
+ // audio_stream->get_buffer_size()/audio_stream_out_frame_size()
static status_t getFrameCount(audio_io_handle_t output,
- audio_stream_type_t stream,
size_t* frameCount);
// returns the audio output stream latency in ms. Corresponds to
// audio_stream_out->get_latency()
static status_t getLatency(audio_io_handle_t output,
- audio_stream_type_t stream,
uint32_t* latency);
static bool routedToA2dpOutput(audio_stream_type_t streamType);
+ // return status NO_ERROR implies *buffSize > 0
static status_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,
audio_channel_mask_t channelMask, size_t* buffSize);
static status_t setVoiceVolume(float volume);
// return the number of audio frames written by AudioFlinger to audio HAL and
- // audio dsp to DAC since the output on which the specified stream is playing
- // has exited standby.
+ // audio dsp to DAC since the specified output I/O handle has exited standby.
// returned status (from utils/Errors.h) can be:
// - NO_ERROR: successful operation, halFrames and dspFrames point to valid data
// - INVALID_OPERATION: Not supported on current hardware platform
@@ -127,16 +133,26 @@
// NOTE: this feature is not supported on all hardware platforms and it is
// necessary to check returned status before using the returned values.
static status_t getRenderPosition(audio_io_handle_t output,
- size_t *halFrames,
- size_t *dspFrames,
- audio_stream_type_t stream = AUDIO_STREAM_DEFAULT);
+ uint32_t *halFrames,
+ uint32_t *dspFrames);
// return the number of input frames lost by HAL implementation, or 0 if the handle is invalid
- static size_t getInputFramesLost(audio_io_handle_t ioHandle);
+ static uint32_t getInputFramesLost(audio_io_handle_t ioHandle);
- static int newAudioSessionId();
- static void acquireAudioSessionId(int audioSession);
- static void releaseAudioSessionId(int audioSession);
+ // Allocate a new unique ID for use as an audio session ID or I/O handle.
+ // If unable to contact AudioFlinger, returns AUDIO_UNIQUE_ID_ALLOCATE instead.
+ // FIXME If AudioFlinger were to ever exhaust the unique ID namespace,
+ // this method could fail by returning either AUDIO_UNIQUE_ID_ALLOCATE
+ // or an unspecified existing unique ID.
+ static audio_unique_id_t newAudioUniqueId();
+
+ static void acquireAudioSessionId(int audioSession, pid_t pid);
+ static void releaseAudioSessionId(int audioSession, pid_t pid);
+
+ // Get the HW synchronization source used for an audio session.
+ // Return a valid source or AUDIO_HW_SYNC_INVALID if an error occurs
+ // or no HW sync source is used.
+ static audio_hw_sync_t getAudioHwSyncForSession(audio_session_t sessionId);
// types of io configuration change events received with ioConfigChanged()
enum io_config_event {
@@ -155,7 +171,8 @@
class OutputDescriptor {
public:
OutputDescriptor()
- : samplingRate(0), format(AUDIO_FORMAT_DEFAULT), channelMask(0), frameCount(0), latency(0) {}
+ : samplingRate(0), format(AUDIO_FORMAT_DEFAULT), channelMask(0), frameCount(0), latency(0)
+ {}
uint32_t samplingRate;
audio_format_t format;
@@ -193,27 +210,44 @@
static status_t setPhoneState(audio_mode_t state);
static status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config);
static audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage);
+
+ // Client must successfully hand off the handle reference to AudioFlinger via createTrack(),
+ // or release it with releaseOutput().
static audio_io_handle_t getOutput(audio_stream_type_t stream,
uint32_t samplingRate = 0,
audio_format_t format = AUDIO_FORMAT_DEFAULT,
audio_channel_mask_t channelMask = AUDIO_CHANNEL_OUT_STEREO,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
const audio_offload_info_t *offloadInfo = NULL);
+ static audio_io_handle_t getOutputForAttr(const audio_attributes_t *attr,
+ uint32_t samplingRate = 0,
+ audio_format_t format = AUDIO_FORMAT_DEFAULT,
+ audio_channel_mask_t channelMask = AUDIO_CHANNEL_OUT_STEREO,
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+ const audio_offload_info_t *offloadInfo = NULL);
static status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
- int session = 0);
+ int session);
static status_t stopOutput(audio_io_handle_t output,
audio_stream_type_t stream,
- int session = 0);
+ int session);
static void releaseOutput(audio_io_handle_t output);
+
+ // Client must successfully hand off the handle reference to AudioFlinger via openRecord(),
+ // or release it with releaseInput().
static audio_io_handle_t getInput(audio_source_t inputSource,
- uint32_t samplingRate = 0,
- audio_format_t format = AUDIO_FORMAT_DEFAULT,
- audio_channel_mask_t channelMask = AUDIO_CHANNEL_IN_MONO,
- int sessionId = 0);
- static status_t startInput(audio_io_handle_t input);
- static status_t stopInput(audio_io_handle_t input);
- static void releaseInput(audio_io_handle_t input);
+ uint32_t samplingRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ int sessionId,
+ audio_input_flags_t);
+
+ static status_t startInput(audio_io_handle_t input,
+ audio_session_t session);
+ static status_t stopInput(audio_io_handle_t input,
+ audio_session_t session);
+ static void releaseInput(audio_io_handle_t input,
+ audio_session_t session);
static status_t initStreamVolume(audio_stream_type_t stream,
int indexMin,
int indexMax);
@@ -255,8 +289,56 @@
// check presence of audio flinger service.
// returns NO_ERROR if binding to service succeeds, DEAD_OBJECT otherwise
static status_t checkAudioFlinger();
+
+ /* List available audio ports and their attributes */
+ static status_t listAudioPorts(audio_port_role_t role,
+ audio_port_type_t type,
+ unsigned int *num_ports,
+ struct audio_port *ports,
+ unsigned int *generation);
+
+ /* Get attributes for a given audio port */
+ static status_t getAudioPort(struct audio_port *port);
+
+ /* Create an audio patch between several source and sink ports */
+ static status_t createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle);
+
+ /* Release an audio patch */
+ static status_t releaseAudioPatch(audio_patch_handle_t handle);
+
+ /* List existing audio patches */
+ static status_t listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches,
+ unsigned int *generation);
+ /* Set audio port configuration */
+ static status_t setAudioPortConfig(const struct audio_port_config *config);
+
+
+ static status_t acquireSoundTriggerSession(audio_session_t *session,
+ audio_io_handle_t *ioHandle,
+ audio_devices_t *device);
+ static status_t releaseSoundTriggerSession(audio_session_t session);
+
+ static audio_mode_t getPhoneState();
+
// ----------------------------------------------------------------------------
+ class AudioPortCallback : public RefBase
+ {
+ public:
+
+ AudioPortCallback() {}
+ virtual ~AudioPortCallback() {}
+
+ virtual void onAudioPortListUpdate() = 0;
+ virtual void onAudioPatchListUpdate() = 0;
+ virtual void onServiceDied() = 0;
+
+ };
+
+ static void setAudioPortCallback(sp<AudioPortCallback> callBack);
+
private:
class AudioFlingerClient: public IBinder::DeathRecipient, public BnAudioFlingerClient
@@ -275,7 +357,8 @@
virtual void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2);
};
- class AudioPolicyServiceClient: public IBinder::DeathRecipient
+ class AudioPolicyServiceClient: public IBinder::DeathRecipient,
+ public BnAudioPolicyServiceClient
{
public:
AudioPolicyServiceClient() {
@@ -283,6 +366,10 @@
// DeathRecipient
virtual void binderDied(const wp<IBinder>& who);
+
+ // IAudioPolicyServiceClient
+ virtual void onAudioPortListUpdate();
+ virtual void onAudioPatchListUpdate();
};
static sp<AudioFlingerClient> gAudioFlingerClient;
@@ -302,11 +389,11 @@
static sp<IAudioPolicyService> gAudioPolicyService;
- // mapping between stream types and outputs
- static DefaultKeyedVector<audio_stream_type_t, audio_io_handle_t> gStreamOutputMap;
// list of output descriptors containing cached parameters
// (sampling rate, framecount, channel count...)
static DefaultKeyedVector<audio_io_handle_t, OutputDescriptor *> gOutputs;
+
+ static sp<AudioPortCallback> gAudioPortCallback;
};
}; // namespace android
diff --git a/include/media/AudioTimestamp.h b/include/media/AudioTimestamp.h
index c29c7e5..99e9c3e 100644
--- a/include/media/AudioTimestamp.h
+++ b/include/media/AudioTimestamp.h
@@ -19,6 +19,8 @@
#include <time.h>
+namespace android {
+
class AudioTimestamp {
public:
AudioTimestamp() : mPosition(0) {
@@ -30,4 +32,6 @@
struct timespec mTime; // corresponding CLOCK_MONOTONIC when frame is expected to present
};
+} // namespace
+
#endif // ANDROID_AUDIO_TIMESTAMP_H
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 4736369..b5256f0 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -27,7 +27,7 @@
// ----------------------------------------------------------------------------
-class audio_track_cblk_t;
+struct audio_track_cblk_t;
class AudioTrackClientProxy;
class StaticAudioTrackClientProxy;
@@ -36,11 +36,6 @@
class AudioTrack : public RefBase
{
public:
- enum channel_index {
- MONO = 0,
- LEFT = 0,
- RIGHT = 1
- };
/* Events used by AudioTrack callback function (callback_t).
* Keep in sync with frameworks/base/media/java/android/media/AudioTrack.java NATIVE_EVENT_*.
@@ -82,6 +77,7 @@
// (currently ignored, but will make the primary field in future)
size_t size; // input/output in bytes == frameCount * frameSize
+ // on input it is unused
// on output is the number of bytes actually filled
// FIXME this is redundant with respect to frameCount,
// and TRANSFER_OBTAIN mode is broken for 8-bit data
@@ -91,7 +87,7 @@
void* raw;
short* i16; // signed 16-bit
int8_t* i8; // unsigned 8-bit, offset by 0x80
- };
+ }; // input: unused, output: pointer to buffer
};
/* As a convenience, if a callback is supplied, a handler thread
@@ -123,6 +119,8 @@
* - NO_ERROR: successful operation
* - NO_INIT: audio server or audio hardware not initialized
* - BAD_VALUE: unsupported configuration
+ * frameCount is guaranteed to be non-zero if status is NO_ERROR,
+ * and is undefined otherwise.
*/
static status_t getMinFrameCount(size_t* frameCount,
@@ -156,9 +154,10 @@
* streamType: Select the type of audio stream this track is attached to
* (e.g. AUDIO_STREAM_MUSIC).
* sampleRate: Data source sampling rate in Hz.
- * format: Audio format (e.g AUDIO_FORMAT_PCM_16_BIT for signed
- * 16 bits per sample).
- * channelMask: Channel mask.
+ * format: Audio format. For mixed tracks, any PCM format supported by server is OK
+ * or AUDIO_FORMAT_PCM_8_BIT which is handled on client side. For direct
+ * and offloaded tracks, the possible format(s) depends on the output sink.
+ * channelMask: Channel mask, such that audio_is_output_channel(channelMask) is true.
* frameCount: Minimum size of track PCM buffer in frames. This defines the
* application's contribution to the
* latency of the track. The actual size selected by the AudioTrack could be
@@ -180,15 +179,17 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t,
- int frameCount = 0,
+ size_t frameCount = 0,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
callback_t cbf = NULL,
void* user = NULL,
- int notificationFrames = 0,
- int sessionId = 0,
+ uint32_t notificationFrames = 0,
+ int sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
const audio_offload_info_t *offloadInfo = NULL,
- int uid = -1);
+ int uid = -1,
+ pid_t pid = -1,
+ const audio_attributes_t* pAttributes = NULL);
/* Creates an audio track and registers it with AudioFlinger.
* With this constructor, the track is configured for static buffer mode.
@@ -209,11 +210,13 @@
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
callback_t cbf = NULL,
void* user = NULL,
- int notificationFrames = 0,
- int sessionId = 0,
+ uint32_t notificationFrames = 0,
+ int sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
const audio_offload_info_t *offloadInfo = NULL,
- int uid = -1);
+ int uid = -1,
+ pid_t pid = -1,
+ const audio_attributes_t* pAttributes = NULL);
/* Terminates the AudioTrack and unregisters it from AudioFlinger.
* Also destroys all resources associated with the AudioTrack.
@@ -241,17 +244,19 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- int frameCount = 0,
+ size_t frameCount = 0,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
callback_t cbf = NULL,
void* user = NULL,
- int notificationFrames = 0,
+ uint32_t notificationFrames = 0,
const sp<IMemory>& sharedBuffer = 0,
bool threadCanCallJava = false,
- int sessionId = 0,
+ int sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
const audio_offload_info_t *offloadInfo = NULL,
- int uid = -1);
+ int uid = -1,
+ pid_t pid = -1,
+ const audio_attributes_t* pAttributes = NULL);
/* Result of constructing the AudioTrack. This must be checked for successful initialization
* before using any AudioTrack API (except for set()), because using
@@ -279,7 +284,7 @@
size_t frameSize() const { return mFrameSize; }
uint32_t channelCount() const { return mChannelCount; }
- uint32_t frameCount() const { return mFrameCount; }
+ size_t frameCount() const { return mFrameCount; }
/* Return the static buffer specified in constructor or set(), or 0 for streaming mode */
sp<IMemory> sharedBuffer() const { return mSharedBuffer; }
@@ -336,7 +341,7 @@
*/
status_t setSampleRate(uint32_t sampleRate);
- /* Return current source sample rate in Hz, or 0 if unknown */
+ /* Return current source sample rate in Hz */
uint32_t getSampleRate() const;
/* Enables looping and sets the start and end points of looping.
@@ -361,7 +366,7 @@
/* Sets marker position. When playback reaches the number of frames specified, a callback with
* event type EVENT_MARKER is called. Calling setMarkerPosition with marker == 0 cancels marker
* notification callback. To set a marker at a position which would compute as 0,
- * a workaround is to the set the marker at a nearby position such as ~0 or 1.
+ * a workaround is to set the marker at a nearby position such as ~0 or 1.
* If the AudioTrack has been opened with no callback function associated, the operation will
* fail.
*
@@ -425,7 +430,7 @@
* - NO_ERROR: successful operation
* - BAD_VALUE: position is NULL
*/
- status_t getPosition(uint32_t *position) const;
+ status_t getPosition(uint32_t *position);
/* For static buffer mode only, this returns the current playback position in frames
* relative to start of buffer. It is analogous to the position units used by
@@ -450,9 +455,10 @@
* none.
*
* Returned value:
- * handle on audio hardware output
+ * handle on audio hardware output, or AUDIO_IO_HANDLE_NONE if the
+ * track needed to be re-created but that failed
*/
- audio_io_handle_t getOutput();
+ audio_io_handle_t getOutput() const;
/* Returns the unique session ID associated with this track.
*
@@ -528,15 +534,6 @@
struct timespec *elapsed = NULL, size_t *nonContig = NULL);
public:
-//EL_FIXME to be reconciled with new obtainBuffer() return codes and control block proxy
-// enum {
-// NO_MORE_BUFFERS = 0x80000001, // same name in AudioFlinger.h, ok to be different value
-// TEAR_DOWN = 0x80000002,
-// STOPPED = 1,
-// STREAM_END_WAIT,
-// STREAM_END
-// };
-
/* Release a filled buffer of "audioBuffer->frameCount" frames for AudioFlinger to process. */
// FIXME make private when obtainBuffer() for TRANSFER_OBTAIN is removed
void releaseBuffer(Buffer* audioBuffer);
@@ -551,8 +548,11 @@
* WOULD_BLOCK when obtainBuffer() returns same, or
* AudioTrack was stopped during the write
* or any other error code returned by IAudioTrack::start() or restoreTrack_l().
+ * Default behavior is to only return until all data has been transferred. Set 'blocking' to
+ * false for the method to return immediately without waiting to try multiple times to write
+ * the full content of the buffer.
*/
- ssize_t write(const void* buffer, size_t size);
+ ssize_t write(const void* buffer, size_t size, bool blocking = true);
/*
* Dumps the state of an audio track.
@@ -566,7 +566,7 @@
uint32_t getUnderrunFrames() const;
/* Get the flags */
- audio_output_flags_t getFlags() const { return mFlags; }
+ audio_output_flags_t getFlags() const { AutoMutex _l(mLock); return mFlags; }
/* Set parameters - only possible when using direct output */
status_t setParameters(const String8& keyValuePairs);
@@ -580,7 +580,15 @@
* Caution: calling this method too often may be inefficient;
* if you need a high resolution mapping between frame position and presentation time,
* consider implementing that at application level, based on the low resolution timestamps.
- * Returns NO_ERROR if timestamp is valid.
+ * Returns NO_ERROR if timestamp is valid.
+ * WOULD_BLOCK if called in STOPPED or FLUSHED state, or if called immediately after
+ * start/ACTIVE, when the number of frames consumed is less than the
+ * overall hardware latency to physical output. In WOULD_BLOCK cases,
+ * one might poll again, or use getPosition(), or use 0 position and
+ * current time for the timestamp.
+ * INVALID_OPERATION if called on a FastTrack, wrong state, or some other error.
+ *
+ * The timestamp parameter is undefined on return, if status is not NO_ERROR.
*/
status_t getTimestamp(AudioTimestamp& timestamp);
@@ -589,6 +597,11 @@
AudioTrack(const AudioTrack& other);
AudioTrack& operator = (const AudioTrack& other);
+ void setAttributesFromStreamType(audio_stream_type_t streamType);
+ void setStreamTypeFromAttributes(audio_attributes_t& aa);
+ /* paa is guaranteed non-NULL */
+ bool isValidAttributes(const audio_attributes_t *paa);
+
/* a small internal class to handle the callback */
class AudioTrackThread : public Thread
{
@@ -626,53 +639,63 @@
// NS_INACTIVE inactive so don't run again until re-started
// NS_NEVER never again
static const nsecs_t NS_WHENEVER = -1, NS_INACTIVE = -2, NS_NEVER = -3;
- nsecs_t processAudioBuffer(const sp<AudioTrackThread>& thread);
- status_t processStreamEnd(int32_t waitCount);
+ nsecs_t processAudioBuffer();
+ bool isOffloaded() const;
+ bool isDirect() const;
+ bool isOffloadedOrDirect() const;
// caller must hold lock on mLock for all _l methods
- status_t createTrack_l(audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- size_t frameCount,
- audio_output_flags_t flags,
- const sp<IMemory>& sharedBuffer,
- audio_io_handle_t output,
- size_t epoch);
+ status_t createTrack_l();
// can only be called when mState != STATE_ACTIVE
void flush_l();
void setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount);
- audio_io_handle_t getOutput_l();
// FIXME enum is faster than strcmp() for parameter 'from'
status_t restoreTrack_l(const char *from);
- bool isOffloaded() const
+ bool isOffloaded_l() const
{ return (mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0; }
- // Next 3 fields may be changed if IAudioTrack is re-created, but always != 0
+ bool isOffloadedOrDirect_l() const
+ { return (mFlags & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|
+ AUDIO_OUTPUT_FLAG_DIRECT)) != 0; }
+
+ bool isDirect_l() const
+ { return (mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0; }
+
+ // increment mPosition by the delta of mServer, and return new value of mPosition
+ uint32_t updateAndGetPosition_l();
+
+ // Next 4 fields may be changed if IAudioTrack is re-created, but always != 0
sp<IAudioTrack> mAudioTrack;
sp<IMemory> mCblkMemory;
audio_track_cblk_t* mCblk; // re-load after mLock.unlock()
+ audio_io_handle_t mOutput; // returned by AudioSystem::getOutput()
sp<AudioTrackThread> mAudioTrackThread;
+
float mVolume[2];
float mSendLevel;
mutable uint32_t mSampleRate; // mutable because getSampleRate() can update it.
- size_t mFrameCount; // corresponds to current IAudioTrack
- size_t mReqFrameCount; // frame count to request the next time a new
- // IAudioTrack is needed
-
+ size_t mFrameCount; // corresponds to current IAudioTrack, value is
+ // reported back by AudioFlinger to the client
+ size_t mReqFrameCount; // frame count to request the first or next time
+ // a new IAudioTrack is needed, non-decreasing
// constant after constructor or set()
audio_format_t mFormat; // as requested by client, not forced to 16-bit
audio_stream_type_t mStreamType;
uint32_t mChannelCount;
audio_channel_mask_t mChannelMask;
+ sp<IMemory> mSharedBuffer;
transfer_type mTransfer;
+ audio_offload_info_t mOffloadInfoCopy;
+ const audio_offload_info_t* mOffloadInfo;
+ audio_attributes_t mAttributes;
// mFrameSize is equal to mFrameSizeAF for non-PCM or 16-bit PCM data. For 8-bit PCM data, it's
// twice as large as mFrameSize because data is expanded to 16-bit before it's stored in buffer.
@@ -705,21 +728,39 @@
uint32_t mNotificationFramesAct; // actual number of frames between each
// notification callback,
// at initial source sample rate
- bool mRefreshRemaining; // processAudioBuffer() should refresh next 2
+ bool mRefreshRemaining; // processAudioBuffer() should refresh
+ // mRemainingFrames and mRetryOnPartialBuffer
// These are private to processAudioBuffer(), and are not protected by a lock
uint32_t mRemainingFrames; // number of frames to request in obtainBuffer()
bool mRetryOnPartialBuffer; // sleep and retry after partial obtainBuffer()
uint32_t mObservedSequence; // last observed value of mSequence
- sp<IMemory> mSharedBuffer;
uint32_t mLoopPeriod; // in frames, zero means looping is disabled
+
uint32_t mMarkerPosition; // in wrapping (overflow) frame units
bool mMarkerReached;
uint32_t mNewPosition; // in frames
uint32_t mUpdatePeriod; // in frames, zero means no EVENT_NEW_POS
+ uint32_t mServer; // in frames, last known mProxy->getPosition()
+ // which is count of frames consumed by server,
+ // reset by new IAudioTrack,
+ // whether it is reset by stop() is TBD
+ uint32_t mPosition; // in frames, like mServer except continues
+ // monotonically after new IAudioTrack,
+ // and could be easily widened to uint64_t
+ uint32_t mReleased; // in frames, count of frames released to server
+ // but not necessarily consumed by server,
+ // reset by stop() but continues monotonically
+ // after new IAudioTrack to restore mPosition,
+ // and could be easily widened to uint64_t
+ int64_t mStartUs; // the start time after flush or stop.
+ // only used for offloaded and direct tracks.
audio_output_flags_t mFlags;
+ // const after set(), except for bits AUDIO_OUTPUT_FLAG_FAST and AUDIO_OUTPUT_FLAG_OFFLOAD.
+ // mLock must be held to read or write those bits reliably.
+
int mSessionId;
int mAuxEffectId;
@@ -739,7 +780,6 @@
sp<AudioTrackClientProxy> mProxy; // primary owner of the memory
bool mInUnderrun; // whether track is currently in underrun state
- String8 mName; // server's name for this IAudioTrack
uint32_t mPausedPosition;
private:
@@ -754,8 +794,8 @@
sp<DeathNotifier> mDeathNotifier;
uint32_t mSequence; // incremented for each new IAudioTrack attempt
- audio_io_handle_t mOutput; // cached output io handle
int mClientUid;
+ pid_t mClientPid;
};
class TimedAudioTrack : public AudioTrack
diff --git a/include/media/CharacterEncodingDetector.h b/include/media/CharacterEncodingDetector.h
new file mode 100644
index 0000000..deaa377
--- /dev/null
+++ b/include/media/CharacterEncodingDetector.h
@@ -0,0 +1,63 @@
+/*
+ * 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 _CHARACTER_ENCODING_DETECTOR_H
+#define _CHARACTER_ENCODING_DETECTOR_H
+
+#include <media/mediascanner.h>
+
+#include "StringArray.h"
+
+#include "unicode/ucnv.h"
+#include "unicode/ucsdet.h"
+#include "unicode/ustring.h"
+
+namespace android {
+
+class CharacterEncodingDetector {
+
+ public:
+ CharacterEncodingDetector();
+ ~CharacterEncodingDetector();
+
+ void addTag(const char *name, const char *value);
+ size_t size();
+
+ void detectAndConvert();
+ status_t getTag(int index, const char **name, const char**value);
+
+ private:
+ const UCharsetMatch *getPreferred(
+ const char *input, size_t len,
+ const UCharsetMatch** ucma, size_t matches,
+ bool *goodmatch, int *highestmatch);
+
+ bool isFrequent(const uint16_t *values, uint32_t c);
+
+ // cached name and value strings, for native encoding support.
+ // TODO: replace these with byte blob arrays that don't require the data to be
+ // singlenullbyte-terminated
+ StringArray mNames;
+ StringArray mValues;
+
+ UConverter* mUtf8Conv;
+};
+
+
+
+}; // namespace android
+
+#endif
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 899d79f..31a14f0 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -50,6 +50,7 @@
TRACK_TIMED = 1, // client requests a TimedAudioTrack
TRACK_FAST = 2, // client requests a fast AudioTrack or AudioRecord
TRACK_OFFLOAD = 4, // client requests offload to hw codec
+ TRACK_DIRECT = 8, // client requests a direct output
};
typedef uint32_t track_flags_t;
@@ -64,37 +65,39 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount,
+ size_t *pFrameCount,
track_flags_t *flags,
const sp<IMemory>& sharedBuffer,
+ // On successful return, AudioFlinger takes over the handle
+ // reference and will release it when the track is destroyed.
+ // However on failure, the client is responsible for release.
audio_io_handle_t output,
pid_t tid, // -1 means unused, otherwise must be valid non-0
int *sessionId,
- // input: ignored
- // output: server's description of IAudioTrack for display in logs.
- // Don't attempt to parse, as the format could change.
- String8& name,
int clientUid,
status_t *status) = 0;
virtual sp<IAudioRecord> openRecord(
+ // On successful return, AudioFlinger takes over the handle
+ // reference and will release it when the track is destroyed.
+ // However on failure, the client is responsible for release.
audio_io_handle_t input,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount,
+ size_t *pFrameCount,
track_flags_t *flags,
pid_t tid, // -1 means unused, otherwise must be valid non-0
int *sessionId,
+ size_t *notificationFrames,
+ sp<IMemory>& cblk,
+ sp<IMemory>& buffers, // return value 0 means it follows cblk
status_t *status) = 0;
/* query the audio hardware state. This state never changes,
* and therefore can be cached.
*/
virtual uint32_t sampleRate(audio_io_handle_t output) const = 0;
-#if 0
- virtual int channelCount(audio_io_handle_t output) const = 0;
-#endif
virtual audio_format_t format(audio_io_handle_t output) const = 0;
virtual size_t frameCount(audio_io_handle_t output) const = 0;
@@ -142,40 +145,41 @@
virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,
audio_channel_mask_t channelMask) const = 0;
- virtual audio_io_handle_t openOutput(audio_module_handle_t module,
- audio_devices_t *pDevices,
- uint32_t *pSamplingRate,
- audio_format_t *pFormat,
- audio_channel_mask_t *pChannelMask,
- uint32_t *pLatencyMs,
- audio_output_flags_t flags,
- const audio_offload_info_t *offloadInfo = NULL) = 0;
+ virtual status_t openOutput(audio_module_handle_t module,
+ audio_io_handle_t *output,
+ audio_config_t *config,
+ audio_devices_t *devices,
+ const String8& address,
+ uint32_t *latencyMs,
+ audio_output_flags_t flags) = 0;
virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1,
audio_io_handle_t output2) = 0;
virtual status_t closeOutput(audio_io_handle_t output) = 0;
virtual status_t suspendOutput(audio_io_handle_t output) = 0;
virtual status_t restoreOutput(audio_io_handle_t output) = 0;
- virtual audio_io_handle_t openInput(audio_module_handle_t module,
- audio_devices_t *pDevices,
- uint32_t *pSamplingRate,
- audio_format_t *pFormat,
- audio_channel_mask_t *pChannelMask) = 0;
+ virtual status_t openInput(audio_module_handle_t module,
+ audio_io_handle_t *input,
+ audio_config_t *config,
+ audio_devices_t *device,
+ const String8& address,
+ audio_source_t source,
+ audio_input_flags_t flags) = 0;
virtual status_t closeInput(audio_io_handle_t input) = 0;
- virtual status_t setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output) = 0;
+ virtual status_t invalidateStream(audio_stream_type_t stream) = 0;
virtual status_t setVoiceVolume(float volume) = 0;
- virtual status_t getRenderPosition(size_t *halFrames, size_t *dspFrames,
+ virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames,
audio_io_handle_t output) const = 0;
- virtual size_t getInputFramesLost(audio_io_handle_t ioHandle) const = 0;
+ virtual uint32_t getInputFramesLost(audio_io_handle_t ioHandle) const = 0;
- virtual int newAudioSessionId() = 0;
+ virtual audio_unique_id_t newAudioUniqueId() = 0;
- virtual void acquireAudioSessionId(int audioSession) = 0;
- virtual void releaseAudioSessionId(int audioSession) = 0;
+ virtual void acquireAudioSessionId(int audioSession, pid_t pid) = 0;
+ virtual void releaseAudioSessionId(int audioSession, pid_t pid) = 0;
virtual status_t queryNumberEffects(uint32_t *numEffects) const = 0;
@@ -188,6 +192,7 @@
effect_descriptor_t *pDesc,
const sp<IEffectClient>& client,
int32_t priority,
+ // AudioFlinger doesn't take over handle reference from client
audio_io_handle_t output,
int sessionId,
status_t *status,
@@ -209,6 +214,29 @@
// and should be called at most once. For a definition of what "low RAM" means, see
// android.app.ActivityManager.isLowRamDevice().
virtual status_t setLowRamDevice(bool isLowRamDevice) = 0;
+
+ /* List available audio ports and their attributes */
+ virtual status_t listAudioPorts(unsigned int *num_ports,
+ struct audio_port *ports) = 0;
+
+ /* Get attributes for a given audio port */
+ virtual status_t getAudioPort(struct audio_port *port) = 0;
+
+ /* Create an audio patch between several source and sink ports */
+ virtual status_t createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle) = 0;
+
+ /* Release an audio patch */
+ virtual status_t releaseAudioPatch(audio_patch_handle_t handle) = 0;
+
+ /* List existing audio patches */
+ virtual status_t listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches) = 0;
+ /* Set audio port configuration */
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config) = 0;
+
+ /* Get the HW synchronization source used for an audio session */
+ virtual audio_hw_sync_t getAudioHwSyncForSession(audio_session_t sessionId) = 0;
};
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
index 09b9ea6..16fe9cf 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -25,6 +25,7 @@
#include <utils/Errors.h>
#include <binder/IInterface.h>
#include <media/AudioSystem.h>
+#include <media/IAudioPolicyServiceClient.h>
#include <system/audio_policy.h>
@@ -55,6 +56,12 @@
audio_channel_mask_t channelMask = 0,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
const audio_offload_info_t *offloadInfo = NULL) = 0;
+ virtual audio_io_handle_t getOutputForAttr(const audio_attributes_t *attr,
+ uint32_t samplingRate = 0,
+ audio_format_t format = AUDIO_FORMAT_DEFAULT,
+ audio_channel_mask_t channelMask = 0,
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+ const audio_offload_info_t *offloadInfo = NULL) = 0;
virtual status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
int session = 0) = 0;
@@ -63,13 +70,17 @@
int session = 0) = 0;
virtual void releaseOutput(audio_io_handle_t output) = 0;
virtual audio_io_handle_t getInput(audio_source_t inputSource,
- uint32_t samplingRate = 0,
- audio_format_t format = AUDIO_FORMAT_DEFAULT,
- audio_channel_mask_t channelMask = 0,
- int audioSession = 0) = 0;
- virtual status_t startInput(audio_io_handle_t input) = 0;
- virtual status_t stopInput(audio_io_handle_t input) = 0;
- virtual void releaseInput(audio_io_handle_t input) = 0;
+ uint32_t samplingRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ int audioSession,
+ audio_input_flags_t flags) = 0;
+ virtual status_t startInput(audio_io_handle_t input,
+ audio_session_t session) = 0;
+ virtual status_t stopInput(audio_io_handle_t input,
+ audio_session_t session) = 0;
+ virtual void releaseInput(audio_io_handle_t input,
+ audio_session_t session) = 0;
virtual status_t initStreamVolume(audio_stream_type_t stream,
int indexMin,
int indexMax) = 0;
@@ -99,6 +110,40 @@
// Check if offload is possible for given format, stream type, sample rate,
// bit rate, duration, video and streaming or offload property is enabled
virtual bool isOffloadSupported(const audio_offload_info_t& info) = 0;
+
+ /* List available audio ports and their attributes */
+ virtual status_t listAudioPorts(audio_port_role_t role,
+ audio_port_type_t type,
+ unsigned int *num_ports,
+ struct audio_port *ports,
+ unsigned int *generation) = 0;
+
+ /* Get attributes for a given audio port */
+ virtual status_t getAudioPort(struct audio_port *port) = 0;
+
+ /* Create an audio patch between several source and sink ports */
+ virtual status_t createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle) = 0;
+
+ /* Release an audio patch */
+ virtual status_t releaseAudioPatch(audio_patch_handle_t handle) = 0;
+
+ /* List existing audio patches */
+ virtual status_t listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches,
+ unsigned int *generation) = 0;
+ /* Set audio port configuration */
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config) = 0;
+
+ virtual void registerClient(const sp<IAudioPolicyServiceClient>& client) = 0;
+
+ virtual status_t acquireSoundTriggerSession(audio_session_t *session,
+ audio_io_handle_t *ioHandle,
+ audio_devices_t *device) = 0;
+
+ virtual status_t releaseSoundTriggerSession(audio_session_t session) = 0;
+
+ virtual audio_mode_t getPhoneState() = 0;
};
diff --git a/include/media/IAudioPolicyServiceClient.h b/include/media/IAudioPolicyServiceClient.h
new file mode 100644
index 0000000..59df046
--- /dev/null
+++ b/include/media/IAudioPolicyServiceClient.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 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_IAUDIOPOLICYSERVICECLIENT_H
+#define ANDROID_IAUDIOPOLICYSERVICECLIENT_H
+
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <system/audio.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IAudioPolicyServiceClient : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(AudioPolicyServiceClient);
+
+ // Notifies a change of audio port configuration.
+ virtual void onAudioPortListUpdate() = 0;
+ // Notifies a change of audio patch configuration.
+ virtual void onAudioPatchListUpdate() = 0;
+};
+
+
+// ----------------------------------------------------------------------------
+
+class BnAudioPolicyServiceClient : public BnInterface<IAudioPolicyServiceClient>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAUDIOPOLICYSERVICECLIENT_H
diff --git a/include/media/IAudioRecord.h b/include/media/IAudioRecord.h
index eccc2ca..2003985 100644
--- a/include/media/IAudioRecord.h
+++ b/include/media/IAudioRecord.h
@@ -34,9 +34,6 @@
public:
DECLARE_META_INTERFACE(AudioRecord);
- /* get this tracks control block */
- virtual sp<IMemory> getCblk() const = 0;
-
/* After it's created the track is not active. Call start() to
* make it active.
*/
diff --git a/include/media/IAudioTrack.h b/include/media/IAudioTrack.h
index 5c8a484..619ac78 100644
--- a/include/media/IAudioTrack.h
+++ b/include/media/IAudioTrack.h
@@ -88,7 +88,7 @@
/* Send parameters to the audio hardware */
virtual status_t setParameters(const String8& keyValuePairs) = 0;
- /* Return NO_ERROR if timestamp is valid */
+ /* Return NO_ERROR if timestamp is valid. timestamp is undefined otherwise. */
virtual status_t getTimestamp(AudioTimestamp& timestamp) = 0;
/* Signal the playback thread for a change in control block */
diff --git a/include/media/IDrm.h b/include/media/IDrm.h
index 32ae28e..68de87a 100644
--- a/include/media/IDrm.h
+++ b/include/media/IDrm.h
@@ -70,6 +70,8 @@
Vector<uint8_t> &certificate,
Vector<uint8_t> &wrappedKey) = 0;
+ virtual status_t unprovisionDevice() = 0;
+
virtual status_t getSecureStops(List<Vector<uint8_t> > &secureStops) = 0;
virtual status_t releaseSecureStops(Vector<uint8_t> const &ssRelease) = 0;
diff --git a/include/media/IMediaCodecList.h b/include/media/IMediaCodecList.h
new file mode 100644
index 0000000..e93ea8b
--- /dev/null
+++ b/include/media/IMediaCodecList.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 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_IMEDIACODECLIST_H
+#define ANDROID_IMEDIACODECLIST_H
+
+#include <utils/Errors.h> // for status_t
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+struct MediaCodecInfo;
+
+class IMediaCodecList: public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(MediaCodecList);
+
+ virtual size_t countCodecs() const = 0;
+ virtual sp<MediaCodecInfo> getCodecInfo(size_t index) const = 0;
+
+ virtual ssize_t findCodecByType(
+ const char *type, bool encoder, size_t startIndex = 0) const = 0;
+
+ virtual ssize_t findCodecByName(const char *name) const = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnMediaCodecList: public BnInterface<IMediaCodecList>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif // ANDROID_IMEDIACODECLIST_H
diff --git a/include/media/IMediaHTTPConnection.h b/include/media/IMediaHTTPConnection.h
new file mode 100644
index 0000000..2a63eb7
--- /dev/null
+++ b/include/media/IMediaHTTPConnection.h
@@ -0,0 +1,49 @@
+/*
+ * 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 I_MEDIA_HTTP_CONNECTION_H_
+
+#define I_MEDIA_HTTP_CONNECTION_H_
+
+#include <binder/IInterface.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+struct IMediaHTTPConnection;
+
+/** MUST stay in sync with IMediaHTTPConnection.aidl */
+
+struct IMediaHTTPConnection : public IInterface {
+ DECLARE_META_INTERFACE(MediaHTTPConnection);
+
+ virtual bool connect(
+ const char *uri, const KeyedVector<String8, String8> *headers) = 0;
+
+ virtual void disconnect() = 0;
+ virtual ssize_t readAt(off64_t offset, void *data, size_t size) = 0;
+ virtual off64_t getSize() = 0;
+ virtual status_t getMIMEType(String8 *mimeType) = 0;
+ virtual status_t getUri(String8 *uri) = 0;
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(IMediaHTTPConnection);
+};
+
+} // namespace android
+
+#endif // I_MEDIA_HTTP_CONNECTION_H_
diff --git a/include/media/IMediaHTTPService.h b/include/media/IMediaHTTPService.h
new file mode 100644
index 0000000..f66d6c8
--- /dev/null
+++ b/include/media/IMediaHTTPService.h
@@ -0,0 +1,41 @@
+/*
+ * 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 I_MEDIA_HTTP_SERVICE_H_
+
+#define I_MEDIA_HTTP_SERVICE_H_
+
+#include <binder/IInterface.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+struct IMediaHTTPConnection;
+
+/** MUST stay in sync with IMediaHTTPService.aidl */
+
+struct IMediaHTTPService : public IInterface {
+ DECLARE_META_INTERFACE(MediaHTTPService);
+
+ virtual sp<IMediaHTTPConnection> makeHTTPConnection() = 0;
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(IMediaHTTPService);
+};
+
+} // namespace android
+
+#endif // I_MEDIA_HTTP_SERVICE_H_
diff --git a/include/media/IMediaMetadataRetriever.h b/include/media/IMediaMetadataRetriever.h
index 6dbb2d7..2529800 100644
--- a/include/media/IMediaMetadataRetriever.h
+++ b/include/media/IMediaMetadataRetriever.h
@@ -26,6 +26,8 @@
namespace android {
+struct IMediaHTTPService;
+
class IMediaMetadataRetriever: public IInterface
{
public:
@@ -33,6 +35,7 @@
virtual void disconnect() = 0;
virtual status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
const char *srcUrl,
const KeyedVector<String8, String8> *headers = NULL) = 0;
diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h
index 0cbd269..db62cd5 100644
--- a/include/media/IMediaPlayer.h
+++ b/include/media/IMediaPlayer.h
@@ -33,6 +33,7 @@
class Surface;
class IStreamSource;
class IGraphicBufferProducer;
+struct IMediaHTTPService;
class IMediaPlayer: public IInterface
{
@@ -41,8 +42,11 @@
virtual void disconnect() = 0;
- virtual status_t setDataSource(const char *url,
- const KeyedVector<String8, String8>* headers) = 0;
+ virtual status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
+ const char *url,
+ const KeyedVector<String8, String8>* headers) = 0;
+
virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0;
virtual status_t setDataSource(const sp<IStreamSource>& source) = 0;
virtual status_t setVideoSurfaceTexture(
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index 2998b37..d7e584a 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -34,6 +34,8 @@
struct ICrypto;
struct IDrm;
struct IHDCP;
+struct IMediaCodecList;
+struct IMediaHTTPService;
class IMediaRecorder;
class IOMX;
class IRemoteDisplay;
@@ -49,9 +51,14 @@
virtual sp<IMediaMetadataRetriever> createMetadataRetriever() = 0;
virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client, int audioSessionId = 0) = 0;
- virtual status_t decode(const char* url, uint32_t *pSampleRate, int* pNumChannels,
- audio_format_t* pFormat,
- const sp<IMemoryHeap>& heap, size_t *pSize) = 0;
+ virtual status_t decode(
+ const sp<IMediaHTTPService> &httpService,
+ const char* url,
+ uint32_t *pSampleRate,
+ int* pNumChannels,
+ audio_format_t* pFormat,
+ const sp<IMemoryHeap>& heap, size_t *pSize) = 0;
+
virtual status_t decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate,
int* pNumChannels, audio_format_t* pFormat,
const sp<IMemoryHeap>& heap, size_t *pSize) = 0;
@@ -59,6 +66,7 @@
virtual sp<ICrypto> makeCrypto() = 0;
virtual sp<IDrm> makeDrm() = 0;
virtual sp<IHDCP> makeHDCP(bool createEncryptionModule) = 0;
+ virtual sp<IMediaCodecList> getCodecList() const = 0;
// Connects to a remote display.
// 'iface' specifies the address of the local interface on which to listen for
@@ -93,9 +101,6 @@
virtual void addBatteryData(uint32_t params) = 0;
virtual status_t pullBatteryData(Parcel* reply) = 0;
-
- virtual status_t updateProxyConfig(
- const char *host, int32_t port, const char *exclusionList) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index 86a44a9..6e4a4ac 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -38,8 +38,8 @@
public:
DECLARE_META_INTERFACE(OMX);
- typedef void *buffer_id;
- typedef void *node_id;
+ typedef uint32_t buffer_id;
+ typedef uint32_t node_id;
// Given a node_id and the calling process' pid, returns true iff
// the implementation of the OMX interface lives in the same
@@ -87,6 +87,10 @@
node_id node, OMX_U32 portIndex, OMX_BOOL enable,
OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) = 0;
+ virtual status_t configureVideoTunnelMode(
+ node_id node, OMX_U32 portIndex, OMX_BOOL tunneled,
+ OMX_U32 audioHwSync, native_handle_t **sidebandHandle) = 0;
+
virtual status_t enableGraphicBuffers(
node_id node, OMX_U32 port_index, OMX_BOOL enable) = 0;
@@ -143,6 +147,8 @@
INTERNAL_OPTION_SUSPEND, // data is a bool
INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY, // data is an int64_t
INTERNAL_OPTION_MAX_TIMESTAMP_GAP, // data is int64_t
+ INTERNAL_OPTION_START_TIME, // data is an int64_t
+ INTERNAL_OPTION_TIME_LAPSE, // data is an int64_t[2]
};
virtual status_t setInternalOption(
node_id node,
@@ -182,8 +188,6 @@
OMX_U32 range_length;
OMX_U32 flags;
OMX_TICKS timestamp;
- OMX_PTR platform_private;
- OMX_PTR data_ptr;
} extended_buffer_data;
} u;
diff --git a/include/media/MediaCodecInfo.h b/include/media/MediaCodecInfo.h
new file mode 100644
index 0000000..cd56adb
--- /dev/null
+++ b/include/media/MediaCodecInfo.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2014, 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 MEDIA_CODEC_INFO_H_
+
+#define MEDIA_CODEC_INFO_H_
+
+#include <binder/Parcel.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
+
+#include <sys/types.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+struct AMessage;
+struct Parcel;
+struct CodecCapabilities;
+
+struct MediaCodecInfo : public RefBase {
+ struct ProfileLevel {
+ uint32_t mProfile;
+ uint32_t mLevel;
+ };
+
+ struct Capabilities : public RefBase {
+ void getSupportedProfileLevels(Vector<ProfileLevel> *profileLevels) const;
+ void getSupportedColorFormats(Vector<uint32_t> *colorFormats) const;
+ uint32_t getFlags() const;
+ const sp<AMessage> getDetails() const;
+
+ private:
+ Vector<ProfileLevel> mProfileLevels;
+ Vector<uint32_t> mColorFormats;
+ uint32_t mFlags;
+ sp<AMessage> mDetails;
+
+ Capabilities();
+
+ // read object from parcel even if object creation fails
+ static sp<Capabilities> FromParcel(const Parcel &parcel);
+ status_t writeToParcel(Parcel *parcel) const;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Capabilities);
+
+ friend class MediaCodecInfo;
+ };
+
+ bool isEncoder() const;
+ bool hasQuirk(const char *name) const;
+ void getSupportedMimes(Vector<AString> *mimes) const;
+ const sp<Capabilities> getCapabilitiesFor(const char *mime) const;
+ const char *getCodecName() const;
+
+ /**
+ * Serialization over Binder
+ */
+ static sp<MediaCodecInfo> FromParcel(const Parcel &parcel);
+ status_t writeToParcel(Parcel *parcel) const;
+
+private:
+ // variable set only in constructor - these are accessed by MediaCodecList
+ // to avoid duplication of same variables
+ AString mName;
+ bool mIsEncoder;
+ bool mHasSoleMime; // was initialized with mime
+
+ Vector<AString> mQuirks;
+ KeyedVector<AString, sp<Capabilities> > mCaps;
+
+ sp<Capabilities> mCurrentCaps; // currently initalized capabilities
+
+ ssize_t getCapabilityIndex(const char *mime) const;
+
+ /* Methods used by MediaCodecList to construct the info
+ * object from XML.
+ *
+ * After info object is created:
+ * - additional quirks can be added
+ * - additional mimes can be added
+ * - OMX codec capabilities can be set for the current mime-type
+ * - a capability detail can be set for the current mime-type
+ * - a feature can be set for the current mime-type
+ * - info object can be completed when parsing of a mime-type is done
+ */
+ MediaCodecInfo(AString name, bool encoder, const char *mime);
+ void addQuirk(const char *name);
+ status_t addMime(const char *mime);
+ status_t initializeCapabilities(const CodecCapabilities &caps);
+ void addDetail(const AString &key, const AString &value);
+ void addFeature(const AString &key, int32_t value);
+ void addFeature(const AString &key, const char *value);
+ void removeMime(const char *mime);
+ void complete();
+
+ DISALLOW_EVIL_CONSTRUCTORS(MediaCodecInfo);
+
+ friend class MediaCodecList;
+};
+
+} // namespace android
+
+#endif // MEDIA_CODEC_INFO_H_
+
+
diff --git a/include/media/MediaMetadataRetrieverInterface.h b/include/media/MediaMetadataRetrieverInterface.h
index ecc3b65..38dbb20 100644
--- a/include/media/MediaMetadataRetrieverInterface.h
+++ b/include/media/MediaMetadataRetrieverInterface.h
@@ -20,10 +20,13 @@
#include <utils/RefBase.h>
#include <media/mediametadataretriever.h>
+#include <media/mediascanner.h>
#include <private/media/VideoFrame.h>
namespace android {
+struct IMediaHTTPService;
+
// Abstract base class
class MediaMetadataRetrieverBase : public RefBase
{
@@ -32,6 +35,7 @@
virtual ~MediaMetadataRetrieverBase() {}
virtual status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
const char *url,
const KeyedVector<String8, String8> *headers = NULL) = 0;
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 26d8729..cf18a45 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -27,6 +27,7 @@
#include <media/mediaplayer.h>
#include <media/AudioSystem.h>
+#include <media/AudioTimestamp.h>
#include <media/Metadata.h>
// Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
@@ -97,6 +98,7 @@
virtual uint32_t latency() const = 0;
virtual float msecsPerFrame() const = 0;
virtual status_t getPosition(uint32_t *position) const = 0;
+ virtual status_t getTimestamp(AudioTimestamp &ts) const = 0;
virtual status_t getFramesWritten(uint32_t *frameswritten) const = 0;
virtual int getSessionId() const = 0;
virtual audio_stream_type_t getAudioStreamType() const = 0;
@@ -137,6 +139,7 @@
}
virtual status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
const char *url,
const KeyedVector<String8, String8> *headers = NULL) = 0;
@@ -213,11 +216,6 @@
return INVALID_OPERATION;
}
- virtual status_t updateProxyConfig(
- const char *host, int32_t port, const char *exclusionList) {
- return INVALID_OPERATION;
- }
-
private:
friend class MediaPlayerService;
diff --git a/include/media/MediaProfiles.h b/include/media/MediaProfiles.h
index 9fc962c..f061d22 100644
--- a/include/media/MediaProfiles.h
+++ b/include/media/MediaProfiles.h
@@ -33,7 +33,8 @@
CAMCORDER_QUALITY_720P = 5,
CAMCORDER_QUALITY_1080P = 6,
CAMCORDER_QUALITY_QVGA = 7,
- CAMCORDER_QUALITY_LIST_END = 7,
+ CAMCORDER_QUALITY_2160P = 8,
+ CAMCORDER_QUALITY_LIST_END = 8,
CAMCORDER_QUALITY_TIME_LAPSE_LIST_START = 1000,
CAMCORDER_QUALITY_TIME_LAPSE_LOW = 1000,
@@ -44,7 +45,17 @@
CAMCORDER_QUALITY_TIME_LAPSE_720P = 1005,
CAMCORDER_QUALITY_TIME_LAPSE_1080P = 1006,
CAMCORDER_QUALITY_TIME_LAPSE_QVGA = 1007,
- CAMCORDER_QUALITY_TIME_LAPSE_LIST_END = 1007,
+ CAMCORDER_QUALITY_TIME_LAPSE_2160P = 1008,
+ CAMCORDER_QUALITY_TIME_LAPSE_LIST_END = 1008,
+
+ CAMCORDER_QUALITY_HIGH_SPEED_LIST_START = 2000,
+ CAMCORDER_QUALITY_HIGH_SPEED_LOW = 2000,
+ CAMCORDER_QUALITY_HIGH_SPEED_HIGH = 2001,
+ CAMCORDER_QUALITY_HIGH_SPEED_480P = 2002,
+ CAMCORDER_QUALITY_HIGH_SPEED_720P = 2003,
+ CAMCORDER_QUALITY_HIGH_SPEED_1080P = 2004,
+ CAMCORDER_QUALITY_HIGH_SPEED_2160P = 2005,
+ CAMCORDER_QUALITY_HIGH_SPEED_LIST_END = 2005,
};
/**
diff --git a/include/media/SoundPool.h b/include/media/SoundPool.h
index 1b21a36..f57313c 100644
--- a/include/media/SoundPool.h
+++ b/include/media/SoundPool.h
@@ -167,7 +167,7 @@
friend class SoundPoolThread;
friend class SoundChannel;
public:
- SoundPool(int maxChannels, audio_stream_type_t streamType, int srcQuality);
+ SoundPool(int maxChannels, const audio_attributes_t* pAttributes);
~SoundPool();
int load(const char* url, int priority);
int load(int fd, int64_t offset, int64_t length, int priority);
@@ -183,8 +183,7 @@
void setPriority(int channelID, int priority);
void setLoop(int channelID, int loop);
void setRate(int channelID, float rate);
- audio_stream_type_t streamType() const { return mStreamType; }
- int srcQuality() const { return mSrcQuality; }
+ const audio_attributes_t* attributes() { return &mAttributes; }
// called from SoundPoolThread
void sampleLoaded(int sampleID);
@@ -225,8 +224,7 @@
List<SoundChannel*> mStop;
DefaultKeyedVector< int, sp<Sample> > mSamples;
int mMaxChannels;
- audio_stream_type_t mStreamType;
- int mSrcQuality;
+ audio_attributes_t mAttributes;
int mAllocated;
int mNextSampleID;
int mNextChannelID;
diff --git a/media/libmedia/StringArray.h b/include/media/StringArray.h
similarity index 100%
rename from media/libmedia/StringArray.h
rename to include/media/StringArray.h
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index 1acfac3..84b9846 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -25,6 +25,7 @@
namespace android {
+struct IMediaHTTPService;
class IMediaPlayerService;
class IMediaMetadataRetriever;
@@ -68,6 +69,7 @@
void disconnect();
status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
const char *dataSourceUrl,
const KeyedVector<String8, String8> *headers = NULL);
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 4c05fc3..9cc208e 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -160,6 +160,9 @@
// Playback rate expressed in permille (1000 is normal speed), saved as int32_t, with negative
// values used for rewinding or reverse playback.
KEY_PARAMETER_PLAYBACK_RATE_PERMILLE = 1300, // set only
+
+ // Set a Parcel containing the value of a parcelled Java AudioAttribute instance
+ KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400 // set only
};
// Keep INVOKE_ID_* in sync with MediaPlayer.java.
@@ -170,6 +173,7 @@
INVOKE_ID_SELECT_TRACK = 4,
INVOKE_ID_UNSELECT_TRACK = 5,
INVOKE_ID_SET_VIDEO_SCALING_MODE = 6,
+ INVOKE_ID_GET_SELECTED_TRACK = 7
};
// Keep MEDIA_TRACK_TYPE_* in sync with MediaPlayer.java.
@@ -189,6 +193,8 @@
virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) = 0;
};
+struct IMediaHTTPService;
+
class MediaPlayer : public BnMediaPlayerClient,
public virtual IMediaDeathNotifier
{
@@ -199,6 +205,7 @@
void disconnect();
status_t setDataSource(
+ const sp<IMediaHTTPService> &httpService,
const char *url,
const KeyedVector<String8, String8> *headers);
@@ -220,13 +227,19 @@
status_t getDuration(int *msec);
status_t reset();
status_t setAudioStreamType(audio_stream_type_t type);
+ status_t getAudioStreamType(audio_stream_type_t *type);
status_t setLooping(int loop);
bool isLooping();
status_t setVolume(float leftVolume, float rightVolume);
void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
- static status_t decode(const char* url, uint32_t *pSampleRate, int* pNumChannels,
- audio_format_t* pFormat,
- const sp<IMemoryHeap>& heap, size_t *pSize);
+ static status_t decode(
+ const sp<IMediaHTTPService> &httpService,
+ const char* url,
+ uint32_t *pSampleRate,
+ int* pNumChannels,
+ audio_format_t* pFormat,
+ const sp<IMemoryHeap>& heap,
+ size_t *pSize);
static status_t decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate,
int* pNumChannels, audio_format_t* pFormat,
const sp<IMemoryHeap>& heap, size_t *pSize);
@@ -242,9 +255,6 @@
status_t setRetransmitEndpoint(const char* addrString, uint16_t port);
status_t setNextMediaPlayer(const sp<MediaPlayer>& player);
- status_t updateProxyConfig(
- const char *host, int32_t port, const char *exclusionList);
-
private:
void clear_l();
status_t seekTo_l(int msec);
@@ -253,6 +263,7 @@
status_t attachNewPlayer(const sp<IMediaPlayer>& player);
status_t reset_l();
status_t doSetRetransmitEndpoint(const sp<IMediaPlayer>& player);
+ status_t checkStateForKeySet_l(int key);
sp<IMediaPlayer> mPlayer;
thread_id_t mLockThreadId;
@@ -267,6 +278,7 @@
bool mPrepareSync;
status_t mPrepareStatus;
audio_stream_type_t mStreamType;
+ Parcel* mAudioAttributesParcel;
bool mLoop;
float mLeftVolume;
float mRightVolume;
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index 88a42a0..b0a62a7 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -39,7 +39,7 @@
enum video_source {
VIDEO_SOURCE_DEFAULT = 0,
VIDEO_SOURCE_CAMERA = 1,
- VIDEO_SOURCE_GRALLOC_BUFFER = 2,
+ VIDEO_SOURCE_SURFACE = 2,
VIDEO_SOURCE_LIST_END // must be last - used to validate audio source type
};
@@ -61,12 +61,18 @@
OUTPUT_FORMAT_AAC_ADIF = 5,
OUTPUT_FORMAT_AAC_ADTS = 6,
+ OUTPUT_FORMAT_AUDIO_ONLY_END = 7, // Used in validating the output format. Should be the
+ // at the end of the audio only output formats.
+
/* Stream over a socket, limited to a single stream */
OUTPUT_FORMAT_RTP_AVP = 7,
/* H.264/AAC data encapsulated in MPEG2/TS */
OUTPUT_FORMAT_MPEG2TS = 8,
+ /* VP8/VORBIS data in a WEBM container */
+ OUTPUT_FORMAT_WEBM = 9,
+
OUTPUT_FORMAT_LIST_END // must be last - used to validate format type
};
@@ -77,6 +83,7 @@
AUDIO_ENCODER_AAC = 3,
AUDIO_ENCODER_HE_AAC = 4,
AUDIO_ENCODER_AAC_ELD = 5,
+ AUDIO_ENCODER_VORBIS = 6,
AUDIO_ENCODER_LIST_END // must be the last - used to validate the audio encoder type
};
@@ -86,6 +93,7 @@
VIDEO_ENCODER_H263 = 1,
VIDEO_ENCODER_H264 = 2,
VIDEO_ENCODER_MPEG_4_SP = 3,
+ VIDEO_ENCODER_VP8 = 4,
VIDEO_ENCODER_LIST_END // must be the last - used to validate the video encoder type
};
diff --git a/include/media/mediascanner.h b/include/media/mediascanner.h
index a73403b..d555279 100644
--- a/include/media/mediascanner.h
+++ b/include/media/mediascanner.h
@@ -21,6 +21,7 @@
#include <utils/threads.h>
#include <utils/List.h>
#include <utils/Errors.h>
+#include <utils/String8.h>
#include <pthread.h>
struct dirent;
@@ -29,6 +30,7 @@
class MediaScannerClient;
class StringArray;
+class CharacterEncodingDetector;
enum MediaScanResult {
// This file or directory was scanned successfully.
@@ -41,6 +43,31 @@
MEDIA_SCAN_RESULT_ERROR,
};
+struct MediaAlbumArt {
+public:
+ static MediaAlbumArt *fromData(int32_t size, const void* data);
+
+ static void init(MediaAlbumArt* instance, int32_t size, const void* data);
+
+ MediaAlbumArt *clone();
+
+ const char *data() {
+ return &mData[0];
+ }
+
+ int32_t size() {
+ return mSize;
+ }
+
+private:
+ int32_t mSize;
+ char mData[];
+
+ // You can't construct instances of this class directly because this is a
+ // variable-sized object passed through the binder.
+ MediaAlbumArt();
+} __packed;
+
struct MediaScanner {
MediaScanner();
virtual ~MediaScanner();
@@ -53,8 +80,7 @@
void setLocale(const char *locale);
- // extracts album art as a block of data
- virtual char *extractAlbumArt(int fd) = 0;
+ virtual MediaAlbumArt *extractAlbumArt(int fd) = 0;
protected:
const char *locale() const;
@@ -94,15 +120,8 @@
virtual status_t setMimeType(const char* mimeType) = 0;
protected:
- void convertValues(uint32_t encoding);
-
-protected:
- // cached name and value strings, for native encoding support.
- StringArray* mNames;
- StringArray* mValues;
-
- // default encoding based on MediaScanner::mLocale string
- uint32_t mLocaleEncoding;
+ // default encoding from MediaScanner::mLocale
+ String8 mLocale;
};
}; // namespace android
diff --git a/include/media/nbaio/AudioBufferProviderSource.h b/include/media/nbaio/AudioBufferProviderSource.h
index 2c4aaff..b16e20a 100644
--- a/include/media/nbaio/AudioBufferProviderSource.h
+++ b/include/media/nbaio/AudioBufferProviderSource.h
@@ -27,7 +27,7 @@
class AudioBufferProviderSource : public NBAIO_Source {
public:
- AudioBufferProviderSource(AudioBufferProvider *provider, NBAIO_Format format);
+ AudioBufferProviderSource(AudioBufferProvider *provider, const NBAIO_Format& format);
virtual ~AudioBufferProviderSource();
// NBAIO_Port interface
diff --git a/include/media/nbaio/AudioStreamInSource.h b/include/media/nbaio/AudioStreamInSource.h
index 07d8c89..5169f1e 100644
--- a/include/media/nbaio/AudioStreamInSource.h
+++ b/include/media/nbaio/AudioStreamInSource.h
@@ -43,9 +43,9 @@
// This is an over-estimate, and could dupe the caller into making a blocking read()
// FIXME Use an audio HAL API to query the buffer filling status when it's available.
- virtual ssize_t availableToRead() { return mStreamBufferSizeBytes >> mBitShift; }
+ virtual ssize_t availableToRead() { return mStreamBufferSizeBytes / mFrameSize; }
- virtual ssize_t read(void *buffer, size_t count);
+ virtual ssize_t read(void *buffer, size_t count, int64_t readPTS);
// NBAIO_Sink end
diff --git a/include/media/nbaio/AudioStreamOutSink.h b/include/media/nbaio/AudioStreamOutSink.h
index 7948d40..9949b88 100644
--- a/include/media/nbaio/AudioStreamOutSink.h
+++ b/include/media/nbaio/AudioStreamOutSink.h
@@ -43,7 +43,7 @@
// This is an over-estimate, and could dupe the caller into making a blocking write()
// FIXME Use an audio HAL API to query the buffer emptying status when it's available.
- virtual ssize_t availableToWrite() const { return mStreamBufferSizeBytes >> mBitShift; }
+ virtual ssize_t availableToWrite() const { return mStreamBufferSizeBytes / mFrameSize; }
virtual ssize_t write(const void *buffer, size_t count);
diff --git a/include/media/nbaio/MonoPipe.h b/include/media/nbaio/MonoPipe.h
index d3802fe..b09b35f 100644
--- a/include/media/nbaio/MonoPipe.h
+++ b/include/media/nbaio/MonoPipe.h
@@ -41,7 +41,7 @@
// Note: whatever shares this object with another thread needs to do so in an SMP-safe way (like
// creating it the object before creating the other thread, or storing the object with a
// release_store). Otherwise the other thread could see a partially-constructed object.
- MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock = false);
+ MonoPipe(size_t reqFrames, const NBAIO_Format& format, bool writeCanBlock = false);
virtual ~MonoPipe();
// NBAIO_Port interface
diff --git a/include/media/nbaio/NBAIO.h b/include/media/nbaio/NBAIO.h
index 1da0c73..d422576 100644
--- a/include/media/nbaio/NBAIO.h
+++ b/include/media/nbaio/NBAIO.h
@@ -29,6 +29,7 @@
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <media/AudioTimestamp.h>
+#include <system/audio.h>
namespace android {
@@ -52,31 +53,41 @@
// the combinations that are actually needed within AudioFlinger. If the list of combinations grows
// too large, then this decision should be re-visited.
// Sample rate and channel count are explicit, PCM interleaved 16-bit is assumed.
-typedef unsigned NBAIO_Format;
-enum {
- Format_Invalid
+struct NBAIO_Format {
+// FIXME make this a class, and change Format_... global methods to class methods
+//private:
+ unsigned mSampleRate;
+ unsigned mChannelCount;
+ audio_format_t mFormat;
+ size_t mFrameSize;
};
-// Return the frame size of an NBAIO_Format in bytes
-size_t Format_frameSize(NBAIO_Format format);
+extern const NBAIO_Format Format_Invalid;
-// Return the frame size of an NBAIO_Format as a bit shift
-size_t Format_frameBitShift(NBAIO_Format format);
+// Return the frame size of an NBAIO_Format in bytes
+size_t Format_frameSize(const NBAIO_Format& format);
// Convert a sample rate in Hz and channel count to an NBAIO_Format
-NBAIO_Format Format_from_SR_C(unsigned sampleRate, unsigned channelCount);
+// FIXME rename
+NBAIO_Format Format_from_SR_C(unsigned sampleRate, unsigned channelCount, audio_format_t format);
// Return the sample rate in Hz of an NBAIO_Format
-unsigned Format_sampleRate(NBAIO_Format format);
+unsigned Format_sampleRate(const NBAIO_Format& format);
// Return the channel count of an NBAIO_Format
-unsigned Format_channelCount(NBAIO_Format format);
+unsigned Format_channelCount(const NBAIO_Format& format);
// Callbacks used by NBAIO_Sink::writeVia() and NBAIO_Source::readVia() below.
typedef ssize_t (*writeVia_t)(void *user, void *buffer, size_t count);
typedef ssize_t (*readVia_t)(void *user, const void *buffer,
size_t count, int64_t readPTS);
+// Check whether an NBAIO_Format is valid
+bool Format_isValid(const NBAIO_Format& format);
+
+// Compare two NBAIO_Format values
+bool Format_isEqual(const NBAIO_Format& format1, const NBAIO_Format& format2);
+
// Abstract class (interface) representing a data port.
class NBAIO_Port : public RefBase {
@@ -115,15 +126,15 @@
virtual NBAIO_Format format() const { return mNegotiated ? mFormat : Format_Invalid; }
protected:
- NBAIO_Port(NBAIO_Format format) : mNegotiated(false), mFormat(format),
- mBitShift(Format_frameBitShift(format)) { }
+ NBAIO_Port(const NBAIO_Format& format) : mNegotiated(false), mFormat(format),
+ mFrameSize(Format_frameSize(format)) { }
virtual ~NBAIO_Port() { }
// Implementations are free to ignore these if they don't need them
bool mNegotiated; // mNegotiated implies (mFormat != Format_Invalid)
NBAIO_Format mFormat; // (mFormat != Format_Invalid) does not imply mNegotiated
- size_t mBitShift; // assign in parallel with any assignment to mFormat
+ size_t mFrameSize; // assign in parallel with any assignment to mFormat
};
// Abstract class (interface) representing a non-blocking data sink, for use by a data provider.
@@ -216,11 +227,11 @@
// Returns NO_ERROR if a timestamp is available. The timestamp includes the total number
// of frames presented to an external observer, together with the value of CLOCK_MONOTONIC
- // as of this presentation count.
+ // as of this presentation count. The timestamp parameter is undefined if error is returned.
virtual status_t getTimestamp(AudioTimestamp& timestamp) { return INVALID_OPERATION; }
protected:
- NBAIO_Sink(NBAIO_Format format = Format_Invalid) : NBAIO_Port(format), mFramesWritten(0) { }
+ NBAIO_Sink(const NBAIO_Format& format = Format_Invalid) : NBAIO_Port(format), mFramesWritten(0) { }
virtual ~NBAIO_Sink() { }
// Implementations are free to ignore these if they don't need them
@@ -311,7 +322,7 @@
virtual void onTimestamp(const AudioTimestamp& timestamp) { }
protected:
- NBAIO_Source(NBAIO_Format format = Format_Invalid) : NBAIO_Port(format), mFramesRead(0) { }
+ NBAIO_Source(const NBAIO_Format& format = Format_Invalid) : NBAIO_Port(format), mFramesRead(0) { }
virtual ~NBAIO_Source() { }
// Implementations are free to ignore these if they don't need them
diff --git a/include/media/nbaio/NBLog.h b/include/media/nbaio/NBLog.h
index 6d59ea7..bcbbc04 100644
--- a/include/media/nbaio/NBLog.h
+++ b/include/media/nbaio/NBLog.h
@@ -25,6 +25,8 @@
namespace android {
+class String8;
+
class NBLog {
public:
@@ -187,6 +189,10 @@
const Shared* const mShared; // raw pointer to shared memory
const sp<IMemory> mIMemory; // ref-counted version
int32_t mFront; // index of oldest acknowledged Entry
+ int mFd; // file descriptor
+ int mIndent; // indentation level
+
+ void dumpLine(const String8& timestamp, String8& body);
static const size_t kSquashTimestamp = 5; // squash this many or more adjacent timestamps
};
diff --git a/include/media/nbaio/Pipe.h b/include/media/nbaio/Pipe.h
index 79a4eee..eba37bc 100644
--- a/include/media/nbaio/Pipe.h
+++ b/include/media/nbaio/Pipe.h
@@ -30,7 +30,11 @@
public:
// maxFrames will be rounded up to a power of 2, and all slots are available. Must be >= 2.
- Pipe(size_t maxFrames, NBAIO_Format format);
+ // buffer is an optional parameter specifying the virtual address of the pipe buffer,
+ // which must be of size roundup(maxFrames) * Format_frameSize(format) bytes.
+ Pipe(size_t maxFrames, const NBAIO_Format& format, void *buffer = NULL);
+
+ // If a buffer was specified in the constructor, it is not automatically freed by destructor.
virtual ~Pipe();
// NBAIO_Port interface
@@ -57,6 +61,7 @@
void * const mBuffer;
volatile int32_t mRear; // written by android_atomic_release_store
volatile int32_t mReaders; // number of PipeReader clients currently attached to this Pipe
+ const bool mFreeBufferInDestructor;
};
} // namespace android
diff --git a/include/media/nbaio/SourceAudioBufferProvider.h b/include/media/nbaio/SourceAudioBufferProvider.h
index cdfb6fe..daf6bc3 100644
--- a/include/media/nbaio/SourceAudioBufferProvider.h
+++ b/include/media/nbaio/SourceAudioBufferProvider.h
@@ -41,7 +41,7 @@
private:
const sp<NBAIO_Source> mSource; // the wrapped source
- /*const*/ size_t mFrameBitShift; // log2(frame size in bytes)
+ /*const*/ size_t mFrameSize; // frame size in bytes
void* mAllocated; // pointer to base of allocated memory
size_t mSize; // size of mAllocated in frames
size_t mOffset; // frame offset within mAllocated of valid data
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 9b534e7..a0e5d84 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -22,6 +22,7 @@
#include <android/native_window.h>
#include <media/IOMX.h>
#include <media/stagefright/foundation/AHierarchicalStateMachine.h>
+#include <media/stagefright/CodecBase.h>
#include <media/stagefright/SkipCutBuffer.h>
#include <OMX_Audio.h>
@@ -31,45 +32,34 @@
struct ABuffer;
struct MemoryDealer;
+struct DescribeColorFormatParams;
-struct ACodec : public AHierarchicalStateMachine {
- enum {
- kWhatFillThisBuffer = 'fill',
- kWhatDrainThisBuffer = 'drai',
- kWhatEOS = 'eos ',
- kWhatShutdownCompleted = 'scom',
- kWhatFlushCompleted = 'fcom',
- kWhatOutputFormatChanged = 'outC',
- kWhatError = 'erro',
- kWhatComponentAllocated = 'cAll',
- kWhatComponentConfigured = 'cCon',
- kWhatInputSurfaceCreated = 'isfc',
- kWhatSignaledInputEOS = 'seos',
- kWhatBuffersAllocated = 'allc',
- kWhatOMXDied = 'OMXd',
- };
-
+struct ACodec : public AHierarchicalStateMachine, public CodecBase {
ACodec();
- void setNotificationMessage(const sp<AMessage> &msg);
+ virtual void setNotificationMessage(const sp<AMessage> &msg);
+
void initiateSetup(const sp<AMessage> &msg);
- void signalFlush();
- void signalResume();
- void initiateShutdown(bool keepComponentAllocated = false);
- void signalSetParameters(const sp<AMessage> &msg);
- void signalEndOfInputStream();
+ virtual void initiateAllocateComponent(const sp<AMessage> &msg);
+ virtual void initiateConfigureComponent(const sp<AMessage> &msg);
+ virtual void initiateCreateInputSurface();
+ virtual void initiateStart();
+ virtual void initiateShutdown(bool keepComponentAllocated = false);
- void initiateAllocateComponent(const sp<AMessage> &msg);
- void initiateConfigureComponent(const sp<AMessage> &msg);
- void initiateCreateInputSurface();
- void initiateStart();
+ virtual void signalFlush();
+ virtual void signalResume();
- void signalRequestIDRFrame();
+ virtual void signalSetParameters(const sp<AMessage> &msg);
+ virtual void signalEndOfInputStream();
+ virtual void signalRequestIDRFrame();
- bool isConfiguredForAdaptivePlayback() { return mIsConfiguredForAdaptivePlayback; }
+ // AHierarchicalStateMachine implements the message handling
+ virtual void onMessageReceived(const sp<AMessage> &msg) {
+ handleMessage(msg);
+ }
- struct PortDescription : public RefBase {
+ struct PortDescription : public CodecBase::PortDescription {
size_t countBuffers();
IOMX::buffer_id bufferIDAt(size_t index) const;
sp<ABuffer> bufferAt(size_t index) const;
@@ -86,6 +76,16 @@
DISALLOW_EVIL_CONSTRUCTORS(PortDescription);
};
+ static bool isFlexibleColorFormat(
+ const sp<IOMX> &omx, IOMX::node_id node,
+ uint32_t colorFormat, OMX_U32 *flexibleEquivalent);
+
+ // Returns 0 if configuration is not supported. NOTE: this is treated by
+ // some OMX components as auto level, and by others as invalid level.
+ static int /* OMX_VIDEO_AVCLEVELTYPE */ getAVCLevelFor(
+ int width, int height, int rate, int bitrate,
+ OMX_VIDEO_AVCPROFILETYPE profile = OMX_VIDEO_AVCProfileBaseline);
+
protected:
virtual ~ACodec();
@@ -119,6 +119,8 @@
kWhatRequestIDRFrame = 'ridr',
kWhatSetParameters = 'setP',
kWhatSubmitOutputMetaDataBufferIfEOS = 'subm',
+ kWhatOMXDied = 'OMXd',
+ kWhatReleaseCodecInstance = 'relC',
};
enum {
@@ -178,6 +180,8 @@
sp<MemoryDealer> mDealer[2];
sp<ANativeWindow> mNativeWindow;
+ sp<AMessage> mInputFormat;
+ sp<AMessage> mOutputFormat;
Vector<BufferInfo> mBuffers[2];
bool mPortEOS[2];
@@ -190,7 +194,7 @@
bool mUseMetadataOnEncoderOutput;
bool mFatalError;
bool mShutdownInProgress;
- bool mIsConfiguredForAdaptivePlayback;
+ bool mExplicitShutdown;
// If "mKeepComponentAllocated" we only transition back to Loaded state
// and do not release the component instance.
@@ -198,16 +202,25 @@
int32_t mEncoderDelay;
int32_t mEncoderPadding;
+ int32_t mRotationDegrees;
bool mChannelMaskPresent;
int32_t mChannelMask;
unsigned mDequeueCounter;
bool mStoreMetaDataInOutputBuffers;
int32_t mMetaDataBuffersToSubmit;
+ size_t mNumUndequeuedBuffers;
int64_t mRepeatFrameDelayUs;
int64_t mMaxPtsGapUs;
+ int64_t mTimePerFrameUs;
+ int64_t mTimePerCaptureUs;
+
+ bool mCreateInputBuffersSuspended;
+
+ bool mTunneled;
+
status_t setCyclicIntraMacroblockRefresh(const sp<AMessage> &msg, int32_t mode);
status_t allocateBuffersOnPort(OMX_U32 portIndex);
status_t freeBuffersOnPort(OMX_U32 portIndex);
@@ -231,6 +244,9 @@
status_t setComponentRole(bool isEncoder, const char *mime);
status_t configureCodec(const char *mime, const sp<AMessage> &msg);
+ status_t configureTunneledVideoPlayback(int32_t audioHwSync,
+ const sp<ANativeWindow> &nativeWindow);
+
status_t setVideoPortFormatType(
OMX_U32 portIndex,
OMX_VIDEO_CODINGTYPE compressionFormat,
@@ -239,7 +255,7 @@
status_t setSupportedOutputFormat();
status_t setupVideoDecoder(
- const char *mime, int32_t width, int32_t height);
+ const char *mime, const sp<AMessage> &msg);
status_t setupVideoEncoder(
const char *mime, const sp<AMessage> &msg);
@@ -249,10 +265,20 @@
int32_t width, int32_t height,
OMX_VIDEO_CODINGTYPE compressionFormat);
+ typedef struct drcParams {
+ int32_t drcCut;
+ int32_t drcBoost;
+ int32_t heavyCompression;
+ int32_t targetRefLevel;
+ int32_t encodedTargetLevel;
+ } drcParams_t;
+
status_t setupAACCodec(
bool encoder,
int32_t numChannels, int32_t sampleRate, int32_t bitRate,
- int32_t aacProfile, bool isADTS);
+ int32_t aacProfile, bool isADTS, int32_t sbrMode,
+ int32_t maxOutputChannelCount, const drcParams_t& drc,
+ int32_t pcmLimiterEnable);
status_t setupAC3Codec(bool encoder, int32_t numChannels, int32_t sampleRate);
@@ -273,6 +299,7 @@
status_t setupMPEG4EncoderParameters(const sp<AMessage> &msg);
status_t setupH263EncoderParameters(const sp<AMessage> &msg);
status_t setupAVCEncoderParameters(const sp<AMessage> &msg);
+ status_t setupHEVCEncoderParameters(const sp<AMessage> &msg);
status_t setupVPXEncoderParameters(const sp<AMessage> &msg);
status_t verifySupportForProfileAndLevel(int32_t profile, int32_t level);
@@ -301,11 +328,17 @@
void processDeferredMessages();
void sendFormatChange(const sp<AMessage> &reply);
+ status_t getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify);
void signalError(
OMX_ERRORTYPE error = OMX_ErrorUndefined,
status_t internalError = UNKNOWN_ERROR);
+ static bool describeDefaultColorFormat(DescribeColorFormatParams &describeParams);
+ static bool describeColorFormat(
+ const sp<IOMX> &omx, IOMX::node_id node,
+ DescribeColorFormatParams &describeParams);
+
status_t requestIDRFrame();
status_t setParameters(const sp<AMessage> ¶ms);
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
index 32f468b..cc46801 100644
--- a/include/media/stagefright/CameraSource.h
+++ b/include/media/stagefright/CameraSource.h
@@ -172,7 +172,7 @@
const sp<IGraphicBufferProducer>& surface,
bool storeMetaDataInVideoBuffers);
- virtual void startCameraRecording();
+ virtual status_t startCameraRecording();
virtual void releaseRecordingFrame(const sp<IMemory>& frame);
// Returns true if need to skip the current frame.
@@ -185,6 +185,8 @@
virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
const sp<IMemory> &data);
+ void releaseCamera();
+
private:
friend class CameraSourceListener;
@@ -236,7 +238,6 @@
static void adjustOutgoingANWBuffer(IMemory* data);
void stopCameraRecording();
- void releaseCamera();
status_t reset();
CameraSource(const CameraSource &);
diff --git a/include/media/stagefright/ClockEstimator.h b/include/media/stagefright/ClockEstimator.h
new file mode 100644
index 0000000..2fd6e75
--- /dev/null
+++ b/include/media/stagefright/ClockEstimator.h
@@ -0,0 +1,110 @@
+/*
+**
+** Copyright 2014, 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 CLOCK_ESTIMATOR_H_
+
+#define CLOCK_ESTIMATOR_H_
+
+
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+struct ClockEstimator : RefBase {
+ virtual double estimate(double x, double y) = 0;
+ virtual void reset() = 0;
+};
+
+struct WindowedLinearFitEstimator : ClockEstimator {
+ struct LinearFit {
+ /**
+ * Fit y = a * x + b, where each input has a weight
+ */
+ double mX; // sum(w_i * x_i)
+ double mXX; // sum(w_i * x_i^2)
+ double mY; // sum(w_i * y_i)
+ double mYY; // sum(w_i * y_i^2)
+ double mXY; // sum(w_i * x_i * y_i)
+ double mW; // sum(w_i)
+
+ LinearFit();
+ void reset();
+ void combine(const LinearFit &lf);
+ void add(double x, double y, double w);
+ void scale(double w);
+ double interpolate(double x);
+ double size() const;
+
+ DISALLOW_EVIL_CONSTRUCTORS(LinearFit);
+ };
+
+ /**
+ * Estimator for f(x) = y' where input y' is noisy, but
+ * theoretically linear:
+ *
+ * y' =~ y = a * x + b
+ *
+ * It uses linear fit regression over a tapering rolling window
+ * to get an estimate for y (from the current and past inputs
+ * (x, y')).
+ *
+ * ____________
+ * /| |\
+ * / | | \
+ * / | | \ <--- new data (x, y')
+ * / | main | \
+ * <--><----------><-->
+ * tail head
+ *
+ * weight is 1 under the main window, tapers exponentially by
+ * the factors given in the head and the tail.
+ *
+ * Assuming that x and y' are monotonic, that x is somewhat
+ * evenly sampled, and that a =~ 1, the estimated y is also
+ * going to be monotonic.
+ */
+ WindowedLinearFitEstimator(
+ size_t headLength = 5, double headFactor = 0.5,
+ size_t mainLength = 0, double tailFactor = 0.99);
+
+ virtual void reset();
+
+ // add a new sample (x -> y') and return an estimated value for the true y
+ virtual double estimate(double x, double y);
+
+private:
+ Vector<double> mXHistory; // circular buffer
+ Vector<double> mYHistory; // circular buffer
+ LinearFit mHead;
+ LinearFit mMain;
+ LinearFit mTail;
+ double mHeadFactorInv;
+ double mTailFactor;
+ double mFirstWeight;
+ size_t mHistoryLength;
+ size_t mHeadLength;
+ size_t mNumSamples;
+ size_t mSampleIx;
+
+ DISALLOW_EVIL_CONSTRUCTORS(WindowedLinearFitEstimator);
+};
+
+}; // namespace android
+
+#endif
diff --git a/include/media/stagefright/CodecBase.h b/include/media/stagefright/CodecBase.h
new file mode 100644
index 0000000..1bf27a6
--- /dev/null
+++ b/include/media/stagefright/CodecBase.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 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 CODEC_BASE_H_
+
+#define CODEC_BASE_H_
+
+#include <stdint.h>
+#include <media/IOMX.h>
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct ABuffer;
+
+struct CodecBase : public AHandler {
+ enum {
+ kWhatFillThisBuffer = 'fill',
+ kWhatDrainThisBuffer = 'drai',
+ kWhatEOS = 'eos ',
+ kWhatShutdownCompleted = 'scom',
+ kWhatFlushCompleted = 'fcom',
+ kWhatOutputFormatChanged = 'outC',
+ kWhatError = 'erro',
+ kWhatComponentAllocated = 'cAll',
+ kWhatComponentConfigured = 'cCon',
+ kWhatInputSurfaceCreated = 'isfc',
+ kWhatSignaledInputEOS = 'seos',
+ kWhatBuffersAllocated = 'allc',
+ };
+
+ virtual void setNotificationMessage(const sp<AMessage> &msg) = 0;
+
+ virtual void initiateAllocateComponent(const sp<AMessage> &msg) = 0;
+ virtual void initiateConfigureComponent(const sp<AMessage> &msg) = 0;
+ virtual void initiateCreateInputSurface() = 0;
+ virtual void initiateStart() = 0;
+ virtual void initiateShutdown(bool keepComponentAllocated = false) = 0;
+
+ // require an explicit message handler
+ virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
+
+ virtual void signalFlush() = 0;
+ virtual void signalResume() = 0;
+
+ virtual void signalRequestIDRFrame() = 0;
+ virtual void signalSetParameters(const sp<AMessage> &msg) = 0;
+ virtual void signalEndOfInputStream() = 0;
+
+ struct PortDescription : public RefBase {
+ virtual size_t countBuffers() = 0;
+ virtual IOMX::buffer_id bufferIDAt(size_t index) const = 0;
+ virtual sp<ABuffer> bufferAt(size_t index) const = 0;
+
+ protected:
+ PortDescription();
+ virtual ~PortDescription();
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(PortDescription);
+ };
+
+protected:
+ CodecBase();
+ virtual ~CodecBase();
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(CodecBase);
+};
+
+} // namespace android
+
+#endif // CODEC_BASE_H_
+
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 6c64a2c..766a81d 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -31,7 +31,10 @@
namespace android {
struct AMessage;
+struct AString;
+struct IMediaHTTPService;
class String8;
+struct HTTPBase;
class DataSource : public RefBase {
public:
@@ -43,8 +46,13 @@
};
static sp<DataSource> CreateFromURI(
+ const sp<IMediaHTTPService> &httpService,
const char *uri,
- const KeyedVector<String8, String8> *headers = NULL);
+ const KeyedVector<String8, String8> *headers = NULL,
+ String8 *contentType = NULL,
+ HTTPBase *httpSource = NULL);
+
+ static sp<DataSource> CreateMediaHTTP(const sp<IMediaHTTPService> &httpService);
DataSource() {}
diff --git a/include/media/stagefright/DataURISource.h b/include/media/stagefright/DataURISource.h
new file mode 100644
index 0000000..693562e
--- /dev/null
+++ b/include/media/stagefright/DataURISource.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 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 DATA_URI_SOURCE_H_
+
+#define DATA_URI_SOURCE_H_
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+struct ABuffer;
+
+struct DataURISource : public DataSource {
+ static sp<DataURISource> Create(const char *uri);
+
+ virtual status_t initCheck() const;
+ virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+ virtual status_t getSize(off64_t *size);
+
+protected:
+ virtual ~DataURISource();
+
+private:
+ sp<ABuffer> mBuffer;
+
+ DataURISource(const sp<ABuffer> &buffer);
+
+ DISALLOW_EVIL_CONSTRUCTORS(DataURISource);
+};
+
+} // namespace android
+
+#endif // DATA_URI_SOURCE_H_
+
diff --git a/include/media/stagefright/FileSource.h b/include/media/stagefright/FileSource.h
index d994cb3..a981d1c 100644
--- a/include/media/stagefright/FileSource.h
+++ b/include/media/stagefright/FileSource.h
@@ -30,6 +30,7 @@
class FileSource : public DataSource {
public:
FileSource(const char *filename);
+ // FileSource takes ownership and will close the fd
FileSource(int fd, int64_t offset, int64_t length);
virtual status_t initCheck() const;
@@ -55,7 +56,7 @@
sp<DecryptHandle> mDecryptHandle;
DrmManagerClient *mDrmManagerClient;
int64_t mDrmBufOffset;
- int64_t mDrmBufSize;
+ size_t mDrmBufSize;
unsigned char *mDrmBuf;
ssize_t readAtDRM(off64_t offset, void *data, size_t size);
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 3ef6b9a..26ce5f9 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -63,8 +63,8 @@
int32_t getTimeScale() const { return mTimeScale; }
status_t setGeoData(int latitudex10000, int longitudex10000);
- void setStartTimeOffsetMs(int ms) { mStartTimeOffsetMs = ms; }
- int32_t getStartTimeOffsetMs() const { return mStartTimeOffsetMs; }
+ virtual void setStartTimeOffsetMs(int ms) { mStartTimeOffsetMs = ms; }
+ virtual int32_t getStartTimeOffsetMs() const { return mStartTimeOffsetMs; }
protected:
virtual ~MPEG4Writer();
diff --git a/include/media/stagefright/MediaBuffer.h b/include/media/stagefright/MediaBuffer.h
index 3d79596..c8a50e8 100644
--- a/include/media/stagefright/MediaBuffer.h
+++ b/include/media/stagefright/MediaBuffer.h
@@ -18,6 +18,8 @@
#define MEDIA_BUFFER_H_
+#include <media/stagefright/foundation/MediaBufferBase.h>
+
#include <pthread.h>
#include <utils/Errors.h>
@@ -43,7 +45,7 @@
MediaBufferObserver &operator=(const MediaBufferObserver &);
};
-class MediaBuffer {
+class MediaBuffer : public MediaBufferBase {
public:
// The underlying data remains the responsibility of the caller!
MediaBuffer(void *data, size_t size);
@@ -56,10 +58,10 @@
// Decrements the reference count and returns the buffer to its
// associated MediaBufferGroup if the reference count drops to 0.
- void release();
+ virtual void release();
// Increments the reference count.
- void add_ref();
+ virtual void add_ref();
void *data() const;
size_t size() const;
diff --git a/include/media/stagefright/MediaBufferGroup.h b/include/media/stagefright/MediaBufferGroup.h
index 0488292..a006f7f 100644
--- a/include/media/stagefright/MediaBufferGroup.h
+++ b/include/media/stagefright/MediaBufferGroup.h
@@ -34,9 +34,12 @@
void add_buffer(MediaBuffer *buffer);
- // Blocks until a buffer is available and returns it to the caller,
- // the returned buffer will have a reference count of 1.
- status_t acquire_buffer(MediaBuffer **buffer);
+ // If nonBlocking is false, it blocks until a buffer is available and
+ // passes it to the caller in *buffer, while returning OK.
+ // The returned buffer will have a reference count of 1.
+ // If nonBlocking is true and a buffer is not immediately available,
+ // buffer is set to NULL and it returns WOULD_BLOCK.
+ status_t acquire_buffer(MediaBuffer **buffer, bool nonBlocking = false);
protected:
virtual void signalBufferReturned(MediaBuffer *buffer);
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 76aa503..54a4e8b 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -26,10 +26,11 @@
namespace android {
struct ABuffer;
-struct ACodec;
struct AMessage;
struct AString;
+struct CodecBase;
struct ICrypto;
+struct IBatteryStats;
struct SoftwareRenderer;
struct Surface;
@@ -44,11 +45,20 @@
BUFFER_FLAG_EOS = 4,
};
+ enum {
+ CB_INPUT_AVAILABLE = 1,
+ CB_OUTPUT_AVAILABLE = 2,
+ CB_ERROR = 3,
+ CB_OUTPUT_FORMAT_CHANGED = 4,
+ };
+
+ struct BatteryNotifier;
+
static sp<MediaCodec> CreateByType(
- const sp<ALooper> &looper, const char *mime, bool encoder);
+ const sp<ALooper> &looper, const char *mime, bool encoder, status_t *err = NULL);
static sp<MediaCodec> CreateByComponentName(
- const sp<ALooper> &looper, const char *name);
+ const sp<ALooper> &looper, const char *name, status_t *err = NULL);
status_t configure(
const sp<AMessage> &format,
@@ -56,6 +66,8 @@
const sp<ICrypto> &crypto,
uint32_t flags);
+ status_t setCallback(const sp<AMessage> &callback);
+
status_t createInputSurface(sp<IGraphicBufferProducer>* bufferProducer);
status_t start();
@@ -64,6 +76,10 @@
// unconfigured.
status_t stop();
+ // Resets the codec to the INITIALIZED state. Can be called after an error
+ // has occured to make the codec usable.
+ status_t reset();
+
// Client MUST call release before releasing final reference to this
// object.
status_t release();
@@ -100,16 +116,22 @@
uint32_t *flags,
int64_t timeoutUs = 0ll);
+ status_t renderOutputBufferAndRelease(size_t index, int64_t timestampNs);
status_t renderOutputBufferAndRelease(size_t index);
status_t releaseOutputBuffer(size_t index);
status_t signalEndOfInputStream();
status_t getOutputFormat(sp<AMessage> *format) const;
+ status_t getInputFormat(sp<AMessage> *format) const;
status_t getInputBuffers(Vector<sp<ABuffer> > *buffers) const;
status_t getOutputBuffers(Vector<sp<ABuffer> > *buffers) const;
+ status_t getOutputBuffer(size_t index, sp<ABuffer> *buffer);
+ status_t getOutputFormat(size_t index, sp<AMessage> *format);
+ status_t getInputBuffer(size_t index, sp<ABuffer> *buffer);
+
status_t requestIDRFrame();
// Notification will be posted once there "is something to do", i.e.
@@ -135,6 +157,7 @@
STARTING,
STARTED,
FLUSHING,
+ FLUSHED,
STOPPING,
RELEASING,
};
@@ -159,6 +182,7 @@
kWhatGetBuffers = 'getB',
kWhatFlush = 'flus',
kWhatGetOutputFormat = 'getO',
+ kWhatGetInputFormat = 'getI',
kWhatDequeueInputTimedOut = 'dITO',
kWhatDequeueOutputTimedOut = 'dOTO',
kWhatCodecNotify = 'codc',
@@ -166,6 +190,7 @@
kWhatRequestActivityNotification = 'racN',
kWhatGetName = 'getN',
kWhatSetParameters = 'setP',
+ kWhatSetCallback = 'setC',
};
enum {
@@ -179,26 +204,46 @@
kFlagSawMediaServerDie = 128,
kFlagIsEncoder = 256,
kFlagGatherCodecSpecificData = 512,
+ kFlagIsAsync = 1024,
+ kFlagIsComponentAllocated = 2048,
};
struct BufferInfo {
- void *mBufferID;
+ uint32_t mBufferID;
sp<ABuffer> mData;
sp<ABuffer> mEncryptedData;
sp<AMessage> mNotify;
+ sp<AMessage> mFormat;
bool mOwnedByClient;
};
State mState;
sp<ALooper> mLooper;
sp<ALooper> mCodecLooper;
- sp<ACodec> mCodec;
+ sp<CodecBase> mCodec;
AString mComponentName;
uint32_t mReplyID;
uint32_t mFlags;
+ status_t mStickyError;
sp<Surface> mNativeWindow;
SoftwareRenderer *mSoftRenderer;
sp<AMessage> mOutputFormat;
+ sp<AMessage> mInputFormat;
+ sp<AMessage> mCallback;
+
+ bool mBatteryStatNotified;
+ bool mIsVideo;
+
+ // initial create parameters
+ AString mInitName;
+ bool mInitNameIsType;
+ bool mInitIsEncoder;
+
+ // Used only to synchronize asynchronous getBufferAndFormat
+ // across all the other (synchronous) buffer state change
+ // operations, such as de/queueIn/OutputBuffer, start and
+ // stop/flush/reset/release.
+ Mutex mBufferLock;
List<size_t> mAvailPortBuffers[2];
Vector<BufferInfo> mPortBuffers[2];
@@ -222,7 +267,9 @@
static status_t PostAndAwaitResponse(
const sp<AMessage> &msg, sp<AMessage> *response);
- status_t init(const char *name, bool nameIsType, bool encoder);
+ static void PostReplyWithError(int32_t replyID, int32_t err);
+
+ status_t init(const AString &name, bool nameIsType, bool encoder);
void setState(State newState);
void returnBuffersToCodec();
@@ -232,6 +279,10 @@
status_t onReleaseOutputBuffer(const sp<AMessage> &msg);
ssize_t dequeuePortBuffer(int32_t portIndex);
+ status_t getBufferAndFormat(
+ size_t portIndex, size_t index,
+ sp<ABuffer> *buffer, sp<AMessage> *format);
+
bool handleDequeueInputBuffer(uint32_t replyID, bool newRequest = false);
bool handleDequeueOutputBuffer(uint32_t replyID, bool newRequest = false);
void cancelPendingDequeueOperations();
@@ -244,9 +295,28 @@
void postActivityNotificationIfPossible();
+ void onInputBufferAvailable();
+ void onOutputBufferAvailable();
+ void onError(status_t err, int32_t actionCode, const char *detail = NULL);
+ void onOutputFormatChanged();
+
status_t onSetParameters(const sp<AMessage> ¶ms);
status_t amendOutputFormatWithCodecSpecificData(const sp<ABuffer> &buffer);
+ void updateBatteryStat();
+ bool isExecuting() const;
+
+ /* called to get the last codec error when the sticky flag is set.
+ * if no such codec error is found, returns UNKNOWN_ERROR.
+ */
+ inline status_t getStickyError() const {
+ return mStickyError != 0 ? mStickyError : UNKNOWN_ERROR;
+ }
+
+ inline void setStickyError(status_t err) {
+ mFlags |= kFlagStickyError;
+ mStickyError = err;
+ }
DISALLOW_EVIL_CONSTRUCTORS(MediaCodec);
};