Merge "Revert "Audio Policy: add audio.msd.disable property for MSD detour""
diff --git a/apex/Android.bp b/apex/Android.bp
new file mode 100644
index 0000000..bb73ead
--- /dev/null
+++ b/apex/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+apex {
+ name: "com.android.media",
+ manifest: "manifest.json",
+ native_shared_libs: [
+ // Extractor plugins
+ "libaacextractor",
+ "libamrextractor",
+ "libflacextractor",
+ "libmidiextractor",
+ "libmkvextractor",
+ "libmp3extractor",
+ "libmp4extractor",
+ "libmpeg2extractor",
+ "liboggextractor",
+ "libwavextractor",
+ // MediaPlayer2
+ "libmedia2_jni",
+ ],
+ key: "com.android.media.key",
+}
+
+apex_key {
+ name: "com.android.media.key",
+ public_key: "media.avbpubkey",
+ private_key: "media.pem",
+}
diff --git a/apex/OWNERS b/apex/OWNERS
new file mode 100644
index 0000000..5587f5f
--- /dev/null
+++ b/apex/OWNERS
@@ -0,0 +1,6 @@
+chz@google.com
+dwkang@google.com
+jiyong@google.com
+lajos@google.com
+marcone@google.com
+wjia@google.com
diff --git a/apex/manifest.json b/apex/manifest.json
new file mode 100644
index 0000000..e2df3a3
--- /dev/null
+++ b/apex/manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.media",
+ "version": 1
+}
diff --git a/apex/media.avbpubkey b/apex/media.avbpubkey
new file mode 100644
index 0000000..c0c8fd3
--- /dev/null
+++ b/apex/media.avbpubkey
Binary files differ
diff --git a/apex/media.pem b/apex/media.pem
new file mode 100644
index 0000000..8daa50e
--- /dev/null
+++ b/apex/media.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEA06dKiF+xQp36Xcosmac+DzJTXC9nbHy0Yqfy+zEC5hlwXbHZ
+1gAZZu8zL9p7kbBkmtSCulU0M+cTHr74gkG9UDkM/S7Z+957FzHMqWXY03gupFP7
+lcCnKtpkzsyQrABavynoxyY6dfmKZNtEFQrikK1zs80CppRoMwZS2dLogX8qO5LU
+gLe7/0PZBdbQSVA5AARE+AO6pR5Px/8QAere9TCLcm1aK9BUVOJvaAZAf7bD2f8s
+3J/lANQ1tvXXZrFL1i26H6sNja11u5/M0odg3SfqKI0x/317nLkYx8QSSHVKEjBs
+nzsyoFry4INEh/q7zSEX5+S1VA6ORjyof3u7CrGavrYwI2k6x3t+Dkc2dfNDaNY9
+9vGYD1nMyRqUzSIqaOz8q78tc1A391Lua8SB1E0Tx/FnsPjxPee0wZ1taGddkZxD
+cvMdQJhLyE6EloimFiOhkVjnAnlPYiiPEQkwJomE9kCsP9aMmyhwBOpbbRISj1ua
+edESrpTC5DHpt1owjtAfHvD8TfmPWT1KSN1iCQAuh5hnEM5LLDljc/AYvJV4L5uR
+l/6t5dE8deg9ksY6lVRrUwHsXxUtBPhM82PWSrpAPNJCHLuBBVx+zDD//kOmt5oe
+OyYJ2RcDsnLmDfFLHbPcAwuyfmxQ+pbFBfiL4NaKoUWRy0viR4/3zulysCsCAwEA
+AQKCAgAphaNIl8VVtVpdtgED79xr7MqPxjj6/ogA5sPzZY0VCR6TMwXyRri1Ce43
+0Bv32+wQt+ohlf+UwxtsJ7jnDPCP4XFb5iobkG0DguCMxw8/hU9ZK6Sqn03sSUYH
+j/g91h/3ashg8W38oQT2flGf8y+5hF2zg1+mwGykvfPZCdhVN1ZYs5h+3AzEqlHU
+JG1eRJ+6EhxZr5mZNRYfvTkttx8gaPKiczOCbu9sa7PBa6CRrZBEnxv0+GVbwUX8
+a8RjQBsJnJTsC4mwJrx3H4V2M9rb6C224ORTJBHxEBr9bcjMcD4kzV0x69IlxVHq
+m7YBGz5morxm4OZ15BkjTFkeEW8C8bdpRrYoY8ocmybWUf6g5IxpE33M699lWzdn
++xwPloJOA5sqsDIXGXt4+KPb2hjHLpqS4V9Rw1JQErBgB9/0EHqK4u1kwI1/djea
+Ny26esGgjmupq+M+G3vQysKEX8m/KhYZKw8yqG4LrzUKp1uosEXEeE+FBnPW1fwU
+OapJTAKLDAeItz2YsZ+82oTMREKR6gNoAw3yF3dxo/E4sk3IDG4y3w7A8D5dlBBM
+hx2fDqnieS/OffGndbbbIIGH0Sb1MBURNlZlXwXz1hbACc8FMYmn4iyQUJfKlCfU
+Rp1jOR4silFxEGYhSi0Jw7+AJe1prZRyZYp2ZQQ+trGvqQNhwQKCAQEA7CsaDxvP
+L9GI8yFVznAG5SZvIut3/lLA7hd4F0LKJ277hW8YMENLjvmrtEQW1dyQqQXV+CpL
+QErvgRuVj9DyV6qOKDimmpqhT+YZlbxp98N9Ba8RJ4ckw07vPNvglTjAeIZyUbRP
+VX5Onr/OFw66GLzOIhsOnRlqviOKg2wm1kfBF3OBAwVczqh4PJ4gc3rbW9jPN3pn
+eaouV4CdGIooXckV6XtQCGjWFNNzxmuknn1GbLbHslGuDUX89rQsRsk9qZGpi1M1
+Cf2yaQY0d+AjDdPslXKZ92EbDAmo3KGtoxy2hWWytZHbUhtWgguEwxDMkJhJ0yCM
+ZSLQZ2TXlC/iCwKCAQEA5W0xuNTozb2UrwqEaDCkogxgdv5NdQvlvBM7+fD4RpOY
+ItDFcudhuapnpxdbPrdl3F5bgTXZJ4jKMIKePVveugI7wNCQX7aSET3U0Qj47RzA
+vVVHb0K3SZsicpK7K067Ejk1esxrPreaTnUj14jUle20Cq+3iysJnwMFs2DkmE1B
+UK6MiBJ1+MbFuBATw7TWxu8yPyfa3JzUAEsfP8NqqIlXrK26DdGefd8C0hysiq4X
+3matZ3SDck1mCk4LS9PCZZlDqdxLM6/UIoy4cj0qbSPdFMop8zjB8lF/1OABowkT
+e/9L0dpG08G35aEJJ6vKSGnFLbstgly165PlGapeYQKCAQEAwoShuw4BsXYZIYA0
+Z4sX8secRBvDwoKwi6pi7G3DiYU8v2OIfb//zOxRg3GNiWpY8A5xdSyIvJS7/hAV
+ONY1tQUyf2hhuPdhpCh2rED62up14CeYroD+Q6uRGwRTTzTmOp8qK6eirF0TLmf2
+vEESAGwKMEcu2zBjHeayIJsExftlzAYDndRd440ZM3xeaB8p69WAn0Y/UhNchg/V
+1K9+nfiRBrTdb3/BzHd5ZVWlyjCOv94wTuw9uosJ1r0Btu/rzO2/wpSvG+KMfzpw
+HshKtwn1VAaHUBz4JQsTvV2hYba1ktv3vNs81LzVnNkV6YC9rN7x92ZYnLh3BKIn
+edOSjwKCAQAP51zeAixNLsoixCjfjBetgAwj04cNCREY04CB1/lt8wdFypEVYQK+
+OxjKVW0m0NHHz+ap81ClU+8oI7XSbQ7oeAUqXYrUh7Ria5XYE7Ylwat+tG2qQcaw
+3IcryA4fd2qyXbLeW1NH2rRgofAlHcAW0I59eybPB+G32x7HC31tLVXMwPzO5fC1
+mRnVo4+rLlsBGU2zYRDj4B82Ef8NjX9URYkFWFmgYZqKAS6R4Bj52A2hhh6ZIFOI
+VeMv7a8Mx5YfMtuk57dy0spyxqx2htTtEeJecZEs4g9Xu9yPpiOW6KcoHk9kMaxd
+O32C9oHK9TalhGd9vw7tjX2y4eKsv8mhAoIBABEtJSjQ571YUbELvJgnVILWKExj
+nURnbQ8C2j2lLdb20fi8UGJDei9g2b2oCs3SfCaMcRqoOs4uy8ZJzofPelwWY1B7
+SZezn/2d1Q/cXpaXew++krhFvJFVxya6eLD7pvEVQAsGtkp9BTi2CFIhcGo5UQdc
+pym7XjFsW0LCI8mOAleuYe7DEpEU79RdsfxgQpeh1NgFJRpupHT6I2BkVijD/TI4
+kbwz11SuznkUTB4QePIwnEzV+gqDtlny4M1Qvsk8hqcU6JUOu+MOT1qnI6ZGSsdS
+D6RhPukordC9y/cbiV6qqKBVZ39O8ydWvpto/g7G7fSL5SddFuVRnwAV7Vg=
+-----END RSA PRIVATE KEY-----
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index a86cc87..38ff37f 100644
--- a/camera/VendorTagDescriptor.cpp
+++ b/camera/VendorTagDescriptor.cpp
@@ -315,6 +315,10 @@
return OK;
}
+ssize_t VendorTagDescriptor::getSectionIndex(uint32_t tag) const {
+ return mTagToSectionMap.valueFor(tag);
+}
+
void VendorTagDescriptor::dump(int fd, int verbosity, int indentation) const {
size_t size = mTagToNameMap.size();
diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp
index feb04c2..321eb08 100644
--- a/camera/camera2/OutputConfiguration.cpp
+++ b/camera/camera2/OutputConfiguration.cpp
@@ -175,6 +175,12 @@
mIsShared = isShared;
}
+OutputConfiguration::OutputConfiguration(
+ const std::vector<sp<IGraphicBufferProducer>>& gbps,
+ int rotation, int surfaceSetID, int surfaceType, int width, int height, bool isShared)
+ : mGbps(gbps), mRotation(rotation), mSurfaceSetID(surfaceSetID), mSurfaceType(surfaceType),
+ mWidth(width), mHeight(height), mIsDeferred(false), mIsShared(isShared) { }
+
status_t OutputConfiguration::writeToParcel(android::Parcel* parcel) const {
if (parcel == nullptr) return BAD_VALUE;
diff --git a/camera/cameraserver/Android.bp b/camera/cameraserver/Android.bp
new file mode 100644
index 0000000..ef6930c
--- /dev/null
+++ b/camera/cameraserver/Android.bp
@@ -0,0 +1,46 @@
+// Copyright 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary {
+ name: "cameraserver",
+
+ srcs: ["main_cameraserver.cpp"],
+
+ shared_libs: [
+ "libcameraservice",
+ "liblog",
+ "libutils",
+ "libui",
+ "libgui",
+ "libbinder",
+ "libhidltransport",
+ "android.hardware.camera.common@1.0",
+ "android.hardware.camera.provider@2.4",
+ "android.hardware.camera.device@1.0",
+ "android.hardware.camera.device@3.2",
+ ],
+ compile_multilib: "32",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
+
+ init_rc: ["cameraserver.rc"],
+
+ vintf_fragments: [
+ "manifest_android.frameworks.cameraservice.service@2.0.xml",
+ ],
+}
diff --git a/camera/cameraserver/Android.mk b/camera/cameraserver/Android.mk
deleted file mode 100644
index b8c94e6..0000000
--- a/camera/cameraserver/Android.mk
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2015 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_cameraserver.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libcameraservice \
- liblog \
- libutils \
- libui \
- libgui \
- libbinder \
- libhidltransport \
- android.hardware.camera.common@1.0 \
- android.hardware.camera.provider@2.4 \
- android.hardware.camera.device@1.0 \
- android.hardware.camera.device@3.2
-
-LOCAL_MODULE:= cameraserver
-LOCAL_32_BIT_ONLY := true
-
-LOCAL_CFLAGS += -Wall -Wextra -Werror -Wno-unused-parameter
-
-LOCAL_INIT_RC := cameraserver.rc
-
-include $(BUILD_EXECUTABLE)
diff --git a/camera/cameraserver/main_cameraserver.cpp b/camera/cameraserver/main_cameraserver.cpp
index 3972436..53b3d84 100644
--- a/camera/cameraserver/main_cameraserver.cpp
+++ b/camera/cameraserver/main_cameraserver.cpp
@@ -26,8 +26,9 @@
{
signal(SIGPIPE, SIG_IGN);
- // Set 3 threads for HIDL calls
- hardware::configureRpcThreadpool(3, /*willjoin*/ false);
+ // Set 5 threads for HIDL calls. Now cameraserver will serve HIDL calls in
+ // addition to consuming them from the Camera HAL as well.
+ hardware::configureRpcThreadpool(5, /*willjoin*/ false);
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
diff --git a/camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.0.xml b/camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.0.xml
new file mode 100644
index 0000000..601c717
--- /dev/null
+++ b/camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.0.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+ <hal>
+ <name>android.frameworks.cameraservice.service</name>
+ <transport>hwbinder</transport>
+ <version>2.0</version>
+ <interface>
+ <name>ICameraService</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/camera/include/camera/VendorTagDescriptor.h b/camera/include/camera/VendorTagDescriptor.h
index 904fba2..c718c93 100644
--- a/camera/include/camera/VendorTagDescriptor.h
+++ b/camera/include/camera/VendorTagDescriptor.h
@@ -99,6 +99,11 @@
void dump(int fd, int verbosity, int indentation) const;
/**
+ * Get Section for corresponding tag.
+ */
+ ssize_t getSectionIndex(uint32_t tag) const;
+
+ /**
* Read values VendorTagDescriptor object from the given parcel.
*
* Returns OK on success, or a negative error code.
diff --git a/camera/include/camera/camera2/OutputConfiguration.h b/camera/include/camera/camera2/OutputConfiguration.h
index a80f44b..5b117fb 100644
--- a/camera/include/camera/camera2/OutputConfiguration.h
+++ b/camera/include/camera/camera2/OutputConfiguration.h
@@ -67,6 +67,11 @@
OutputConfiguration(sp<IGraphicBufferProducer>& gbp, int rotation,
int surfaceSetID = INVALID_SET_ID, bool isShared = false);
+ OutputConfiguration(const std::vector<sp<IGraphicBufferProducer>>& gbps,
+ int rotation, int surfaceSetID = INVALID_SET_ID,
+ int surfaceType = OutputConfiguration::SURFACE_TYPE_UNKNOWN, int width = 0,
+ int height = 0, bool isShared = false);
+
bool operator == (const OutputConfiguration& other) const {
return ( mRotation == other.mRotation &&
mSurfaceSetID == other.mSurfaceSetID &&
diff --git a/camera/ndk/Android.bp b/camera/ndk/Android.bp
index 838dd4a..e84eeef 100644
--- a/camera/ndk/Android.bp
+++ b/camera/ndk/Android.bp
@@ -32,7 +32,7 @@
}
cc_library_shared {
- name: "libcamera2",
+ name: "libcamera2ndk",
srcs: [
"NdkCameraManager.cpp",
"NdkCameraMetadata.cpp",
@@ -67,5 +67,6 @@
export_include_dirs: ["include"],
export_shared_lib_headers: [
"libnativewindow",
- ]
+ ],
+ version_script: "libcamera2ndk.map.txt",
}
diff --git a/camera/ndk/Android.mk b/camera/ndk/Android.mk
deleted file mode 100644
index 508f930..0000000
--- a/camera/ndk/Android.mk
+++ /dev/null
@@ -1,60 +0,0 @@
-#
-# Copyright (C) 2015 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.
-#
-
-# TODO(b/118434782): Remove this file and change name of the libcamera2
-# module in the existing Android.bp file to libcamera2ndk.
-LOCAL_PATH:= $(call my-dir)
-
-ifneq ($(TARGET_BUILD_PDK), true)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- NdkCameraManager.cpp \
- NdkCameraMetadata.cpp \
- NdkCameraDevice.cpp \
- NdkCaptureRequest.cpp \
- NdkCameraCaptureSession.cpp \
- impl/ACameraManager.cpp \
- impl/ACameraMetadata.cpp \
- impl/ACameraDevice.cpp \
- impl/ACameraCaptureSession.cpp
-
-LOCAL_MODULE:= libcamera2ndk
-
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-
-LOCAL_CFLAGS += -fvisibility=hidden -D EXPORT='__attribute__ ((visibility ("default")))'
-LOCAL_CFLAGS += -Wall -Wextra -Werror
-
-LOCAL_LDFLAGS += -Wl,--version-script=$(LOCAL_PATH)/libcamera2ndk.map.txt
-
-LOCAL_SHARED_LIBRARIES := \
- libbinder \
- liblog \
- libgui \
- libutils \
- libandroid_runtime \
- libcamera_client \
- libstagefright_foundation \
- libcutils \
- libcamera_metadata \
- libmediandk
-
-include $(BUILD_SHARED_LIBRARY)
-
-endif
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 8af3c7c..cb474f4 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -2783,7 +2783,7 @@
* {@link AIMAGE_FORMAT_RAW12 RAW12}.</li>
* <li>Processed (but not-stalling): any non-RAW format without a stall duration. Typically
* {@link AIMAGE_FORMAT_YUV_420_888 YUV_420_888},
- * <a href="https://developer.android.com/reference/android/graphics/ImageFormat.html#NV21">NV21</a>, or <a href="https://developer.android.com/reference/android/graphics/ImageFormat.html#YV12">YV12</a>.</li>
+ * <a href="https://developer.android.com/reference/android/graphics/ImageFormat.html#NV21">NV21</a>, <a href="https://developer.android.com/reference/android/graphics/ImageFormat.html#YV12">YV12</a>, or {@link AIMAGE_FORMAT_Y8 Y8} .</li>
* </ul>
*
* @see ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS
@@ -3251,6 +3251,7 @@
* <li>{@link AIMAGE_FORMAT_YUV_420_888 }</li>
* <li>{@link AIMAGE_FORMAT_RAW10 }</li>
* <li>{@link AIMAGE_FORMAT_RAW12 }</li>
+ * <li>{@link AIMAGE_FORMAT_Y8 }</li>
* </ul>
* <p>All other formats may or may not have an allowed stall duration on
* a per-capability basis; refer to ACAMERA_REQUEST_AVAILABLE_CAPABILITIES
@@ -3288,6 +3289,81 @@
*/
ACAMERA_SCALER_CROPPING_TYPE = // byte (acamera_metadata_enum_android_scaler_cropping_type_t)
ACAMERA_SCALER_START + 13,
+ /**
+ * <p>Recommended stream configurations for common client use cases.</p>
+ *
+ * <p>Type: int32[n*5] (acamera_metadata_enum_android_scaler_available_recommended_stream_configurations_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Optional subset of the ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS that contains
+ * similar tuples listed as
+ * (i.e. width, height, format, output/input stream, usecase bit field).
+ * Camera devices will be able to suggest particular stream configurations which are
+ * power and performance efficient for specific use cases. For more information about
+ * retrieving the suggestions see
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#getRecommendedStreamConfigurationMap">CameraCharacteristics#getRecommendedStreamConfigurationMap</a>.</p>
+ * <p>The data representation is int[5], which maps to
+ * (width, height, format, output/input stream, usecase bit field). The array can be
+ * parsed using the following pseudo code:</p>
+ * <p>struct StreamConfiguration {
+ * int32_t format;
+ * int32_t width;
+ * int32_t height;
+ * int32_t isInput; };</p>
+ * <p>void getPreferredStreamConfigurations(
+ * int32_t *array, size_t count, int32_t usecaseId,
+ * Vector < StreamConfiguration > * scs) {
+ * const size_t STREAM_CONFIGURATION_SIZE = 5;
+ * const size_t STREAM_WIDTH_OFFSET = 0;
+ * const size_t STREAM_HEIGHT_OFFSET = 1;
+ * const size_t STREAM_FORMAT_OFFSET = 2;
+ * const size_t STREAM_IS_INPUT_OFFSET = 3;
+ * const size_t STREAM_USECASE_BITMAP_OFFSET = 4;</p>
+ * <pre><code>for (size_t i = 0; i < count; i+= STREAM_CONFIGURATION_SIZE) {
+ * int32_t width = array[i + STREAM_WIDTH_OFFSET];
+ * int32_t height = array[i + STREAM_HEIGHT_OFFSET];
+ * int32_t format = array[i + STREAM_FORMAT_OFFSET];
+ * int32_t isInput = array[i + STREAM_IS_INPUT_OFFSET];
+ * int32_t supportedUsecases = array[i + STREAM_USECASE_BITMAP_OFFSET];
+ * if (supportedUsecases & (1 << usecaseId)) {
+ * StreamConfiguration sc = {format, width, height, isInput};
+ * scs->add(sc);
+ * }
+ * }
+ * </code></pre>
+ * <p>}</p>
+ *
+ * @see ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS
+ */
+ ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS =
+ // int32[n*5] (acamera_metadata_enum_android_scaler_available_recommended_stream_configurations_t)
+ ACAMERA_SCALER_START + 14,
+ /**
+ * <p>Recommended mappings of image formats that are supported by this
+ * camera device for input streams, to their corresponding output formats.</p>
+ *
+ * <p>Type: int32</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>This is a recommended subset of the complete list of mappings found in
+ * android.scaler.availableInputOutputFormatsMap. The same requirements apply here as well.
+ * The list however doesn't need to contain all available and supported mappings. Instead of
+ * this developers must list only recommended and efficient entries.
+ * If set, the information will be available in the ZERO_SHUTTER_LAG recommended stream
+ * configuration see
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#getRecommendedStreamConfigurationMap">CameraCharacteristics#getRecommendedStreamConfigurationMap</a>.</p>
+ */
+ ACAMERA_SCALER_AVAILABLE_RECOMMENDED_INPUT_OUTPUT_FORMATS_MAP =
+ // int32
+ ACAMERA_SCALER_START + 15,
ACAMERA_SCALER_END,
/**
@@ -3448,6 +3524,8 @@
* <p>Some devices may choose to provide a second set of calibration
* information for improved quality, including
* ACAMERA_SENSOR_REFERENCE_ILLUMINANT2 and its corresponding matrices.</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
*
* @see ACAMERA_SENSOR_CALIBRATION_TRANSFORM1
* @see ACAMERA_SENSOR_COLOR_TRANSFORM1
@@ -3477,6 +3555,8 @@
* <p>If this key is present, then ACAMERA_SENSOR_COLOR_TRANSFORM2,
* ACAMERA_SENSOR_CALIBRATION_TRANSFORM2, and
* ACAMERA_SENSOR_FORWARD_MATRIX2 will also be present.</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
*
* @see ACAMERA_SENSOR_CALIBRATION_TRANSFORM2
* @see ACAMERA_SENSOR_COLOR_TRANSFORM2
@@ -3504,6 +3584,8 @@
* colorspace) into this camera device's native sensor color
* space under the first reference illuminant
* (ACAMERA_SENSOR_REFERENCE_ILLUMINANT1).</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
*
* @see ACAMERA_SENSOR_REFERENCE_ILLUMINANT1
*/
@@ -3531,6 +3613,8 @@
* (ACAMERA_SENSOR_REFERENCE_ILLUMINANT2).</p>
* <p>This matrix will only be present if the second reference
* illuminant is present.</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
*
* @see ACAMERA_SENSOR_REFERENCE_ILLUMINANT2
*/
@@ -3559,6 +3643,8 @@
* and the CIE XYZ colorspace when calculating this transform will
* match the standard white point for the first reference illuminant
* (i.e. no chromatic adaptation will be applied by this transform).</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
*
* @see ACAMERA_SENSOR_REFERENCE_ILLUMINANT1
*/
@@ -3589,6 +3675,8 @@
* (i.e. no chromatic adaptation will be applied by this transform).</p>
* <p>This matrix will only be present if the second reference
* illuminant is present.</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
*
* @see ACAMERA_SENSOR_REFERENCE_ILLUMINANT2
*/
@@ -3615,6 +3703,8 @@
* this matrix is chosen so that the standard white point for this reference
* illuminant in the reference sensor colorspace is mapped to D50 in the
* CIE XYZ colorspace.</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
*
* @see ACAMERA_SENSOR_REFERENCE_ILLUMINANT1
*/
@@ -3643,6 +3733,8 @@
* CIE XYZ colorspace.</p>
* <p>This matrix will only be present if the second reference
* illuminant is present.</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
*
* @see ACAMERA_SENSOR_REFERENCE_ILLUMINANT2
*/
@@ -3675,6 +3767,7 @@
* level values. For raw capture in particular, it is recommended to use
* pixels from ACAMERA_SENSOR_OPTICAL_BLACK_REGIONS to calculate black
* level values for each frame.</p>
+ * <p>For a MONOCHROME camera device, all of the 2x2 channels must have the same values.</p>
*
* @see ACAMERA_SENSOR_DYNAMIC_BLACK_LEVEL
* @see ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
@@ -3769,6 +3862,8 @@
* used to interpolate between the provided color transforms when
* processing raw sensor data.</p>
* <p>The order of the values is R, G, B; where R is in the lowest index.</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
*/
ACAMERA_SENSOR_NEUTRAL_COLOR_POINT = // rational[3]
ACAMERA_SENSOR_START + 18,
@@ -3799,6 +3894,8 @@
* that channel.</p>
* <p>A more detailed description of the noise model can be found in the
* Adobe DNG specification for the NoiseProfile tag.</p>
+ * <p>For a MONOCHROME camera, there is only one color channel. So the noise model coefficients
+ * will only contain one S and one O.</p>
*
* @see ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
*/
@@ -3844,6 +3941,8 @@
* <li>R > 1.20 will require strong software correction to produce
* a usuable image (>20% divergence).</li>
* </ul>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
*/
ACAMERA_SENSOR_GREEN_SPLIT = // float
ACAMERA_SENSOR_START + 22,
@@ -3996,6 +4095,7 @@
* layout key (see ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT), i.e. the
* nth value given corresponds to the black level offset for the nth
* color channel listed in the CFA.</p>
+ * <p>For a MONOCHROME camera, all of the 2x2 channels must have the same values.</p>
* <p>This key will be available if ACAMERA_SENSOR_OPTICAL_BLACK_REGIONS is available or the
* camera device advertises this key via {@link ACAMERA_REQUEST_AVAILABLE_RESULT_KEYS }.</p>
*
@@ -4098,7 +4198,8 @@
/**
* <p>The arrangement of color filters on sensor;
* represents the colors in the top-left 2x2 section of
- * the sensor, in reading order.</p>
+ * the sensor, in reading order, for a Bayer camera, or the
+ * light spectrum it captures for MONOCHROME camera.</p>
*
* <p>Type: byte (acamera_metadata_enum_android_sensor_info_color_filter_arrangement_t)</p>
*
@@ -4567,13 +4668,13 @@
* (x,y) ϵ (0 ... N-1, 0 ... M-1) is the value of the shading map at
* pixel ( ((W-1)/(N-1)) * x, ((H-1)/(M-1)) * y) for the four color channels.
* The map is assumed to be bilinearly interpolated between the sample points.</p>
- * <p>The channel order is [R, Geven, Godd, B], where Geven is the green
- * channel for the even rows of a Bayer pattern, and Godd is the odd rows.
+ * <p>For a Bayer camera, the channel order is [R, Geven, Godd, B], where Geven is
+ * the green channel for the even rows of a Bayer pattern, and Godd is the odd rows.
* The shading map is stored in a fully interleaved format, and its size
* is provided in the camera static metadata by ACAMERA_LENS_INFO_SHADING_MAP_SIZE.</p>
* <p>The shading map will generally have on the order of 30-40 rows and columns,
* and will be smaller than 64x64.</p>
- * <p>As an example, given a very small map defined as:</p>
+ * <p>As an example, given a very small map for a Bayer camera defined as:</p>
* <pre><code>ACAMERA_LENS_INFO_SHADING_MAP_SIZE = [ 4, 3 ]
* ACAMERA_STATISTICS_LENS_SHADING_MAP =
* [ 1.3, 1.2, 1.15, 1.2, 1.2, 1.2, 1.15, 1.2,
@@ -4593,6 +4694,17 @@
* image of a gray wall (using bicubic interpolation for visual quality)
* as captured by the sensor gives:</p>
* <p><img alt="Image of a uniform white wall (inverse shading map)" src="../images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png" /></p>
+ * <p>For a MONOCHROME camera, all of the 2x2 channels must have the same values. An example
+ * shading map for such a camera is defined as:</p>
+ * <pre><code>ACAMERA_LENS_INFO_SHADING_MAP_SIZE = [ 4, 3 ]
+ * ACAMERA_STATISTICS_LENS_SHADING_MAP =
+ * [ 1.3, 1.3, 1.3, 1.3, 1.2, 1.2, 1.2, 1.2,
+ * 1.1, 1.1, 1.1, 1.1, 1.3, 1.3, 1.3, 1.3,
+ * 1.2, 1.2, 1.2, 1.2, 1.1, 1.1, 1.1, 1.1,
+ * 1.0, 1.0, 1.0, 1.0, 1.2, 1.2, 1.2, 1.2,
+ * 1.3, 1.3, 1.3, 1.3, 1.2, 1.2, 1.2, 1.2,
+ * 1.2, 1.2, 1.2, 1.2, 1.3, 1.3, 1.3, 1.3 ]
+ * </code></pre>
* <p>Note that the RAW image data might be subject to lens shading
* correction not reported on this map. Query
* ACAMERA_SENSOR_INFO_LENS_SHADING_APPLIED to see if RAW image data has subject
@@ -4936,8 +5048,8 @@
* of points can be less than max (that is, the request doesn't have to
* always provide a curve with number of points equivalent to
* ACAMERA_TONEMAP_MAX_CURVE_POINTS).</p>
- * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
- * are ignored.</p>
+ * <p>For devices with MONOCHROME capability, all three channels must have the same set of
+ * control points.</p>
* <p>A few examples, and their corresponding graphical mappings; these
* only specify the red channel and the precision is limited to 4
* digits, for conciseness.</p>
@@ -5411,6 +5523,32 @@
*/
ACAMERA_DEPTH_DEPTH_IS_EXCLUSIVE = // byte (acamera_metadata_enum_android_depth_depth_is_exclusive_t)
ACAMERA_DEPTH_START + 4,
+ /**
+ * <p>Recommended depth stream configurations for common client use cases.</p>
+ *
+ * <p>Type: int32[n*5]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Optional subset of the ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS that
+ * contains similar tuples listed as
+ * (i.e. width, height, format, output/input stream, usecase bit field).
+ * Camera devices will be able to suggest particular depth stream configurations which are
+ * power and performance efficient for specific use cases. For more information about
+ * retrieving the suggestions see
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#getRecommendedStreamConfigurationMap">CameraCharacteristics#getRecommendedStreamConfigurationMap</a>.</p>
+ * <p>For data representation please refer to
+ * ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS</p>
+ *
+ * @see ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS
+ * @see ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS
+ */
+ ACAMERA_DEPTH_AVAILABLE_RECOMMENDED_DEPTH_STREAM_CONFIGURATIONS =
+ // int32[n*5]
+ ACAMERA_DEPTH_START + 5,
ACAMERA_DEPTH_END,
/**
@@ -5457,8 +5595,8 @@
* will not slow down capture rate when applying correction. FAST may be the same as OFF if
* any correction at all would slow down capture rate. Every output stream will have a
* similar amount of enhancement applied.</p>
- * <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
- * applied to any RAW output.</p>
+ * <p>The correction only applies to processed outputs such as YUV, Y8, JPEG, or DEPTH16; it is
+ * not applied to any RAW output.</p>
* <p>This control will be on by default on devices that support this control. Applications
* disabling distortion correction need to pay extra attention with the coordinate system of
* metering regions, crop region, and face rectangles. When distortion correction is OFF,
@@ -7143,7 +7281,7 @@
* camera device can capture this size for at least 10 frames per second. Also the
* ACAMERA_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES entry lists at least one FPS range where
* the minimum FPS is >= 1 / minimumFrameDuration for the largest YUV_420_888 size.</p>
- * <p>If the device supports the {@link AIMAGE_FORMAT_RAW10 }, {@link AIMAGE_FORMAT_RAW12 }, then those can also be
+ * <p>If the device supports the {@link AIMAGE_FORMAT_RAW10 }, {@link AIMAGE_FORMAT_RAW12 }, {@link AIMAGE_FORMAT_Y8 }, then those can also be
* captured at the same rate as the maximum-size YUV_420_888 resolution is.</p>
* <p>In addition, the ACAMERA_SYNC_MAX_LATENCY field is guaranted to have a value between 0
* and 4, inclusive. ACAMERA_CONTROL_AE_LOCK_AVAILABLE and ACAMERA_CONTROL_AWB_LOCK_AVAILABLE
@@ -7177,8 +7315,8 @@
* <li>The ACAMERA_DEPTH_DEPTH_IS_EXCLUSIVE entry is listed by this device.</li>
* <li>As of Android P, the ACAMERA_LENS_POSE_REFERENCE entry is listed by this device.</li>
* <li>A LIMITED camera with only the DEPTH_OUTPUT capability does not have to support
- * normal YUV_420_888, JPEG, and PRIV-format outputs. It only has to support the DEPTH16
- * format.</li>
+ * normal YUV_420_888, Y8, JPEG, and PRIV-format outputs. It only has to support the
+ * DEPTH16 format.</li>
* </ul>
* <p>Generally, depth output operates at a slower frame rate than standard color capture,
* so the DEPTH16 and DEPTH_POINT_CLOUD formats will commonly have a stall duration that
@@ -7271,7 +7409,15 @@
/**
* <p>The camera device is a monochrome camera that doesn't contain a color filter array,
- * and the pixel values on U and V planes are all 128.</p>
+ * and for YUV_420_888 stream, the pixel values on U and V planes are all 128.</p>
+ * <p>A MONOCHROME camera must support the guaranteed stream combinations required for
+ * its device level and capabilities. Additionally, if the monochrome camera device
+ * supports Y8 format, all mandatory stream combination requirements related to {@link AIMAGE_FORMAT_YUV_420_888 YUV_420_888} apply
+ * to {@link AIMAGE_FORMAT_Y8 Y8} as well. There are no
+ * mandatory stream combination requirements with regard to
+ * {@link AIMAGE_FORMAT_Y8 Y8} for Bayer camera devices.</p>
+ * <p>Starting from Android Q, the SENSOR_INFO_COLOR_FILTER_ARRANGEMENT of a MONOCHROME
+ * camera will be either MONO or NIR.</p>
*/
ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME = 12,
@@ -7300,6 +7446,67 @@
} acamera_metadata_enum_android_scaler_cropping_type_t;
+// ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS
+typedef enum acamera_metadata_enum_acamera_scaler_available_recommended_stream_configurations {
+ /**
+ * <p>Preview must only include non-stalling processed stream configurations with
+ * output formats like YUV_420_888, IMPLEMENTATION_DEFINED, etc.</p>
+ */
+ ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_PREVIEW
+ = 0x0,
+
+ /**
+ * <p>Video record must include stream configurations that match the advertised
+ * supported media profiles <a href="https://developer.android.com/reference/android/media/CamcorderProfile.html">CamcorderProfile</a> with
+ * IMPLEMENTATION_DEFINED format.</p>
+ */
+ ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_RECORD
+ = 0x1,
+
+ /**
+ * <p>Video snapshot must include stream configurations at least as big as
+ * the maximum RECORD resolutions and only with format BLOB + DATASPACE_JFIF
+ * format/dataspace combination (JPEG). Additionally the configurations shouldn't cause
+ * preview glitches and also be able to run at 30 fps.</p>
+ */
+ ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_VIDEO_SNAPSHOT
+ = 0x2,
+
+ /**
+ * <p>Recommended snapshot stream configurations must include at least one with
+ * size close to ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE with BLOB + DATASPACE_JFIF
+ * format/dataspace combination (JPEG). Taking into account restrictions on aspect
+ * ratio, alignment etc. the area of the maximum suggested size shouldn’t be less than
+ * 97% of the sensor array size area.</p>
+ *
+ * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ */
+ ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_SNAPSHOT
+ = 0x3,
+
+ /**
+ * <p>If supported, recommended input stream configurations must only be advertised with
+ * ZSL along with other processed and/or stalling output formats.</p>
+ */
+ ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_ZSL = 0x4,
+
+ /**
+ * <p>If supported, recommended raw stream configurations must only include RAW based
+ * output formats.</p>
+ */
+ ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_RAW = 0x5,
+
+ ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_PUBLIC_END
+ = 0x6,
+
+ /**
+ * <p>Vendor defined use cases. These depend on the vendor implementation.</p>
+ */
+ ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_VENDOR_START
+ = 0x18,
+
+} acamera_metadata_enum_android_scaler_available_recommended_stream_configurations_t;
+
// ACAMERA_SENSOR_REFERENCE_ILLUMINANT1
typedef enum acamera_metadata_enum_acamera_sensor_reference_illuminant1 {
@@ -7476,6 +7683,21 @@
*/
ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB = 4,
+ /**
+ * <p>Sensor doesn't have any Bayer color filter.
+ * Such sensor captures visible light in monochrome. The exact weighting and
+ * wavelengths captured is not specified, but generally only includes the visible
+ * frequencies. This value implies a MONOCHROME camera.</p>
+ */
+ ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO = 5,
+
+ /**
+ * <p>Sensor has a near infrared filter capturing light with wavelength between
+ * roughly 750nm and 1400nm, and the same filter covers the whole sensor array. This
+ * value implies a MONOCHROME camera.</p>
+ */
+ ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR = 6,
+
} acamera_metadata_enum_android_sensor_info_color_filter_arrangement_t;
// ACAMERA_SENSOR_INFO_TIMESTAMP_SOURCE
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index 4991e50..94f9e02 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -32,6 +32,7 @@
"libutils",
"android.hardware.drm@1.0",
"android.hardware.drm@1.1",
+ "android.hardware.drm@1.2",
"libhidlallocatorutils",
"libhidlbase",
"libhidltransport",
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index ce9dc38..14ff493 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -23,7 +23,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
-#include <android/hardware/drm/1.0/types.h>
+#include <android/hardware/drm/1.2/types.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <hidl/ServiceManagement.h>
@@ -43,12 +43,13 @@
using drm::V1_0::KeyStatusType;
using drm::V1_0::KeyType;
using drm::V1_0::KeyValue;
-using drm::V1_1::HdcpLevel;;
using drm::V1_0::SecureStop;
-using drm::V1_1::SecureStopRelease;
using drm::V1_0::SecureStopId;
-using drm::V1_1::SecurityLevel;
using drm::V1_0::Status;
+using drm::V1_1::HdcpLevel;
+using drm::V1_1::SecureStopRelease;
+using drm::V1_1::SecurityLevel;
+using drm::V1_2::KeySetId;
using ::android::hardware::drm::V1_1::DrmMetricGroup;
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_string;
@@ -139,6 +140,18 @@
}
}
+static DrmPlugin::OfflineLicenseState toOfflineLicenseState(
+ OfflineLicenseState licenseState) {
+ switch(licenseState) {
+ case OfflineLicenseState::USABLE:
+ return DrmPlugin::kOfflineLicenseStateUsable;
+ case OfflineLicenseState::INACTIVE:
+ return DrmPlugin::kOfflineLicenseStateInactive;
+ default:
+ return DrmPlugin::kOfflineLicenseStateUnknown;
+ }
+}
+
static DrmPlugin::HdcpLevel toHdcpLevel(HdcpLevel level) {
switch(level) {
case HdcpLevel::HDCP_NONE:
@@ -199,6 +212,15 @@
return secureStopIds;
}
+static List<Vector<uint8_t>> toKeySetIds(const hidl_vec<KeySetId>&
+ hKeySetIds) {
+ List<Vector<uint8_t>> keySetIds;
+ for (size_t i = 0; i < hKeySetIds.size(); i++) {
+ keySetIds.push_back(toVector(hKeySetIds[i]));
+ }
+ return keySetIds;
+}
+
static status_t toStatusT(Status status) {
switch (status) {
case Status::OK:
@@ -305,6 +327,7 @@
}
mPlugin.clear();
mPluginV1_1.clear();
+ mPluginV1_2.clear();
}
Vector<sp<IDrmFactory>> DrmHal::makeDrmFactories() {
@@ -333,6 +356,16 @@
}
}
);
+ manager->listByInterface(drm::V1_2::IDrmFactory::descriptor,
+ [&factories](const hidl_vec<hidl_string> ®istered) {
+ for (const auto &instance : registered) {
+ auto factory = drm::V1_2::IDrmFactory::getService(instance);
+ if (factory != NULL) {
+ factories.push_back(factory);
+ }
+ }
+ }
+ );
}
if (factories.size() == 0) {
@@ -525,6 +558,7 @@
mPlugin = makeDrmPlugin(mFactories[i], uuid, appPackageName);
if (mPlugin != NULL) {
mPluginV1_1 = drm::V1_1::IDrmPlugin::castFrom(mPlugin);
+ mPluginV1_2 = drm::V1_2::IDrmPlugin::castFrom(mPlugin);
}
}
}
@@ -1063,6 +1097,73 @@
return hResult.isOk() ? err : DEAD_OBJECT;
}
+status_t DrmHal::getOfflineLicenseKeySetIds(List<Vector<uint8_t>> &keySetIds) const {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ if (mPluginV1_2 == NULL) {
+ return ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ status_t err = UNKNOWN_ERROR;
+
+ Return<void> hResult = mPluginV1_2->getOfflineLicenseKeySetIds(
+ [&](Status status, const hidl_vec<KeySetId>& hKeySetIds) {
+ if (status == Status::OK) {
+ keySetIds = toKeySetIds(hKeySetIds);
+ }
+ err = toStatusT(status);
+ }
+ );
+
+ return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+status_t DrmHal::removeOfflineLicense(Vector<uint8_t> const &keySetId) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ if (mPluginV1_2 == NULL) {
+ return ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ Return<Status> status = mPluginV1_2->removeOfflineLicense(toHidlVec(keySetId));
+ return status.isOk() ? toStatusT(status) : DEAD_OBJECT;
+}
+
+status_t DrmHal::getOfflineLicenseState(Vector<uint8_t> const &keySetId,
+ DrmPlugin::OfflineLicenseState *licenseState) const {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ if (mPluginV1_2 == NULL) {
+ return ERROR_DRM_CANNOT_HANDLE;
+ }
+ *licenseState = DrmPlugin::kOfflineLicenseStateUnknown;
+
+ status_t err = UNKNOWN_ERROR;
+
+ Return<void> hResult = mPluginV1_2->getOfflineLicenseState(toHidlVec(keySetId),
+ [&](Status status, OfflineLicenseState hLicenseState) {
+ if (status == Status::OK) {
+ *licenseState = toOfflineLicenseState(hLicenseState);
+ }
+ err = toStatusT(status);
+ }
+ );
+
+ return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
status_t DrmHal::getPropertyString(String8 const &name, String8 &value ) const {
Mutex::Autolock autoLock(mLock);
return getPropertyStringInternal(name, value);
diff --git a/drm/libmediadrm/IDrm.cpp b/drm/libmediadrm/IDrm.cpp
index 509961f..8c26317 100644
--- a/drm/libmediadrm/IDrm.cpp
+++ b/drm/libmediadrm/IDrm.cpp
@@ -61,7 +61,10 @@
GET_NUMBER_OF_SESSIONS,
GET_SECURITY_LEVEL,
REMOVE_SECURE_STOP,
- GET_SECURE_STOP_IDS
+ GET_SECURE_STOP_IDS,
+ GET_OFFLINE_LICENSE_KEYSET_IDS,
+ REMOVE_OFFLINE_LICENSE,
+ GET_OFFLINE_LICENSE_STATE
};
struct BpDrm : public BpInterface<IDrm> {
@@ -376,6 +379,52 @@
return reply.readInt32();
}
+ virtual status_t getOfflineLicenseKeySetIds(List<Vector<uint8_t> > &keySetIds) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+ status_t status = remote()->transact(GET_OFFLINE_LICENSE_KEYSET_IDS, data, &reply);
+ if (status != OK) {
+ return status;
+ }
+
+ keySetIds.clear();
+ uint32_t count = reply.readInt32();
+ for (size_t i = 0; i < count; i++) {
+ Vector<uint8_t> keySetId;
+ readVector(reply, keySetId);
+ keySetIds.push_back(keySetId);
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t removeOfflineLicense(Vector<uint8_t> const &keySetId) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+ writeVector(data, keySetId);
+ status_t status = remote()->transact(REMOVE_OFFLINE_LICENSE, data, &reply);
+ if (status != OK) {
+ return status;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t getOfflineLicenseState(Vector<uint8_t> const &keySetId,
+ DrmPlugin::OfflineLicenseState *licenseState) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+ writeVector(data, keySetId);
+ status_t status = remote()->transact(GET_OFFLINE_LICENSE_STATE, data, &reply);
+ if (status != OK) {
+ *licenseState = DrmPlugin::OfflineLicenseState::kOfflineLicenseStateUnknown;
+ return status;
+ }
+ *licenseState = static_cast<DrmPlugin::OfflineLicenseState>(reply.readInt32());
+ return reply.readInt32();
+ }
+
virtual status_t getPropertyString(String8 const &name, String8 &value) const {
Parcel data, reply;
data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
@@ -980,6 +1029,45 @@
return OK;
}
+ case GET_OFFLINE_LICENSE_KEYSET_IDS:
+ {
+ CHECK_INTERFACE(IDrm, data, reply);
+ List<Vector<uint8_t> > keySetIds;
+ status_t result = getOfflineLicenseKeySetIds(keySetIds);
+ size_t count = keySetIds.size();
+ reply->writeInt32(count);
+ List<Vector<uint8_t> >::iterator iter = keySetIds.begin();
+ while(iter != keySetIds.end()) {
+ size_t size = iter->size();
+ reply->writeInt32(size);
+ reply->write(iter->array(), iter->size());
+ iter++;
+ }
+ reply->writeInt32(result);
+ return OK;
+ }
+
+ case REMOVE_OFFLINE_LICENSE:
+ {
+ CHECK_INTERFACE(IDrm, data, reply);
+ Vector<uint8_t> keySetId;
+ readVector(data, keySetId);
+ reply->writeInt32(removeOfflineLicense(keySetId));
+ return OK;
+ }
+
+ case GET_OFFLINE_LICENSE_STATE:
+ {
+ CHECK_INTERFACE(IDrm, data, reply);
+ Vector<uint8_t> keySetId;
+ readVector(data, keySetId);
+ DrmPlugin::OfflineLicenseState state;
+ status_t result = getOfflineLicenseState(keySetId, &state);
+ reply->writeInt32(static_cast<DrmPlugin::OfflineLicenseState>(state));
+ reply->writeInt32(result);
+ return OK;
+ }
+
case GET_PROPERTY_STRING:
{
CHECK_INTERFACE(IDrm, data, reply);
diff --git a/drm/libmediadrm/tests/Android.bp b/drm/libmediadrm/tests/Android.bp
index 66c906f..dcd59b7 100644
--- a/drm/libmediadrm/tests/Android.bp
+++ b/drm/libmediadrm/tests/Android.bp
@@ -17,6 +17,7 @@
shared_libs: [
"android.hardware.drm@1.0",
"android.hardware.drm@1.1",
+ "android.hardware.drm@1.2",
"libbinder",
"libhidlbase",
"liblog",
diff --git a/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp b/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp
index cb69f91..eaa3390 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp
+++ b/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp
@@ -89,7 +89,7 @@
// asset_id change. If it sends an EcmContainer with 2 Ecms with different
// asset_ids (old and new) then it might be best to prefetch the Emm.
if ((asset_.id() != 0) && (*asset_id != asset_.id())) {
- ALOGW("Asset_id change from %" PRIu64 " to %" PRIu64, asset_.id(), *asset_id);
+ ALOGW("Asset_id change from %llu to %" PRIu64, asset_.id(), *asset_id);
asset_.Clear();
}
diff --git a/drm/mediacas/plugins/clearkey/ecm.cpp b/drm/mediacas/plugins/clearkey/ecm.cpp
index b3b5218..9fde13a 100644
--- a/drm/mediacas/plugins/clearkey/ecm.cpp
+++ b/drm/mediacas/plugins/clearkey/ecm.cpp
@@ -17,8 +17,6 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "ecm"
-#include <inttypes.h>
-
#include "ecm.h"
#include "ecm_generator.h"
#include "protos/license_protos.pb.h"
@@ -78,7 +76,7 @@
return status;
}
if (asset.id() != asset_from_emm.id()) {
- ALOGE("Asset_id from Emm (%" PRIu64 ") does not match asset_id from Ecm (%" PRIu64 ").",
+ ALOGE("Asset_id from Emm (%llu) does not match asset_id from Ecm (%llu).",
asset_from_emm.id(), asset.id());
return CLEARKEY_STATUS_INVALID_PARAMETER;
}
diff --git a/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp b/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp
index 2fce0790..0ac879c 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp
@@ -26,7 +26,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
using ::android::hardware::drm::V1_0::SubSample;
@@ -79,7 +79,7 @@
}
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/Android.bp b/drm/mediadrm/plugins/clearkey/hidl/Android.bp
index edf818e..b44a6c7 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/hidl/Android.bp
@@ -15,7 +15,7 @@
//
cc_binary {
- name: "android.hardware.drm@1.1-service.clearkey",
+ name: "android.hardware.drm@1.2-service.clearkey",
vendor: true,
srcs: [
@@ -39,11 +39,12 @@
relative_install_path: "hw",
cflags: ["-Wall", "-Werror"],
- init_rc: ["android.hardware.drm@1.1-service.clearkey.rc"],
+ init_rc: ["android.hardware.drm@1.2-service.clearkey.rc"],
shared_libs: [
"android.hardware.drm@1.0",
"android.hardware.drm@1.1",
+ "android.hardware.drm@1.2",
"libbase",
"libbinder",
"libcrypto",
@@ -69,7 +70,6 @@
integer_overflow: true,
},
}
-
cc_library_static {
name: "libclearkeydevicefiles-protos",
vendor: true,
diff --git a/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp b/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp
index c2ed751..657a42f 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp
@@ -21,7 +21,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
sp<Buffer> decodeBase64(const std::string &s) {
@@ -169,7 +169,7 @@
}
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp b/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp
index e58f58a..75f8395 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp
@@ -21,7 +21,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
Buffer::Buffer(size_t capacity)
@@ -47,7 +47,7 @@
}
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp b/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp
index 1ba5c6a..1410d77 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp
@@ -22,7 +22,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
extern "C" {
@@ -38,7 +38,7 @@
} // extern "C"
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp
index 0848cef..2a48db6 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp
@@ -27,7 +27,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
Return<bool> CryptoFactory::isCryptoSchemeSupported(
@@ -60,7 +60,7 @@
}
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
index f33f94e..a488f86 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
@@ -27,7 +27,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
using ::android::hardware::drm::V1_0::BufferType;
@@ -179,7 +179,7 @@
}
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DeviceFiles.cpp b/drm/mediadrm/plugins/clearkey/hidl/DeviceFiles.cpp
index 8fafce2..2415b6f 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DeviceFiles.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DeviceFiles.cpp
@@ -13,11 +13,11 @@
#include <openssl/sha.h>
// Protobuf generated classes.
-using android::hardware::drm::V1_1::clearkey::OfflineFile;
-using android::hardware::drm::V1_1::clearkey::HashedFile;
-using android::hardware::drm::V1_1::clearkey::License;
-using android::hardware::drm::V1_1::clearkey::License_LicenseState_ACTIVE;
-using android::hardware::drm::V1_1::clearkey::License_LicenseState_RELEASING;
+using android::hardware::drm::V1_2::clearkey::OfflineFile;
+using android::hardware::drm::V1_2::clearkey::HashedFile;
+using android::hardware::drm::V1_2::clearkey::License;
+using android::hardware::drm::V1_2::clearkey::License_LicenseState_ACTIVE;
+using android::hardware::drm::V1_2::clearkey::License_LicenseState_RELEASING;
namespace {
const char kLicenseFileNameExt[] = ".lic";
@@ -38,7 +38,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
bool DeviceFiles::StoreLicense(
@@ -57,6 +57,7 @@
break;
case kLicenseStateReleasing:
license->set_state(License_LicenseState_RELEASING);
+ license->set_license(licenseResponse);
break;
default:
ALOGW("StoreLicense: Unknown license state: %u", state);
@@ -106,8 +107,8 @@
bool DeviceFiles::RetrieveLicense(
const std::string& keySetId, LicenseState* state, std::string* offlineLicense) {
- OfflineFile file;
+ OfflineFile file;
if (!RetrieveHashedFile(keySetId + kLicenseFileNameExt, &file)) {
return false;
}
@@ -128,7 +129,6 @@
}
License license = file.license();
-
switch (license.state()) {
case License_LicenseState_ACTIVE:
*state = kLicenseStateActive;
@@ -142,11 +142,14 @@
*state = kLicenseStateUnknown;
break;
}
-
*offlineLicense = license.license();
return true;
}
+bool DeviceFiles::DeleteLicense(const std::string& keySetId) {
+ return mFileHandle.RemoveFile(keySetId + kLicenseFileNameExt);
+}
+
bool DeviceFiles::DeleteAllLicenses() {
return mFileHandle.RemoveAllFiles();
}
@@ -155,6 +158,15 @@
return mFileHandle.FileExists(keySetId + kLicenseFileNameExt);
}
+std::vector<std::string> DeviceFiles::ListLicenses() const {
+ std::vector<std::string> licenses = mFileHandle.ListFiles();
+ for (size_t i = 0; i < licenses.size(); i++) {
+ std::string& license = licenses[i];
+ license = license.substr(0, license.size() - strlen(kLicenseFileNameExt));
+ }
+ return licenses;
+}
+
bool DeviceFiles::RetrieveHashedFile(const std::string& fileName, OfflineFile* deSerializedFile) {
if (!deSerializedFile) {
ALOGE("RetrieveHashedFile: invalid file parameter");
@@ -234,7 +246,7 @@
}
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp
index 77557f9..9d040a8 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp
@@ -30,7 +30,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
using ::android::hardware::drm::V1_0::Status;
@@ -71,7 +71,7 @@
}
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index 59dfb89..7184b53 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -58,9 +58,12 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
+using ::android::hardware::drm::V1_2::KeySetId;
+using ::android::hardware::drm::V1_2::OfflineLicenseState;
+
DrmPlugin::DrmPlugin(SessionLibrary* sessionLibrary)
: mSessionLibrary(sessionLibrary),
mOpenSessionOkCount(0),
@@ -70,6 +73,7 @@
mPlayPolicy.clear();
initProperties();
mSecureStops.clear();
+ mReleaseKeysMap.clear();
std::srand(std::time(nullptr));
}
@@ -155,7 +159,7 @@
// GetKeyRequestOfflineKeyTypeNotSupported() in vts 1.0 and 1.1 expects
// KeyType::OFFLINE to return ERROR_DRM_CANNOT_HANDLE in clearkey plugin.
// Those tests pass in an empty initData, we use the empty initData to
- // signal the specific use case.
+ // signal such specific use case.
if (keyType == KeyType::OFFLINE && 0 == initData.size()) {
return Status::ERROR_DRM_CANNOT_HANDLE;
}
@@ -196,6 +200,14 @@
ALOGE("Problem releasing offline license");
return Status::ERROR_DRM_UNKNOWN;
}
+ if (mReleaseKeysMap.find(keySetIdString) == mReleaseKeysMap.end()) {
+ sp<Session> session = mSessionLibrary->createSession();
+ mReleaseKeysMap[keySetIdString] = session->sessionId();
+ } else {
+ ALOGI("key is in use, ignore release request");
+ }
+ } else {
+ ALOGE("Offline license not found, nothing to release");
}
*keyRequestType = KeyRequestType::RELEASE;
}
@@ -305,25 +317,35 @@
bool isRelease = (memcmp(scopeId.data(), kKeySetIdPrefix.data(), kKeySetIdPrefix.size()) == 0);
if (isRelease) {
keySetId.assign(scopeId.begin(), scopeId.end());
+
+ auto iter = mReleaseKeysMap.find(std::string(keySetId.begin(), keySetId.end()));
+ if (iter != mReleaseKeysMap.end()) {
+ sessionId.assign(iter->second.begin(), iter->second.end());
+ }
} else {
sessionId.assign(scopeId.begin(), scopeId.end());
- sp<Session> session = mSessionLibrary->findSession(sessionId);
- if (!session.get()) {
- _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, hidl_vec<uint8_t>());
- return Void();
- }
-
- setPlayPolicy();
// non offline license returns empty keySetId
keySetId.clear();
+ }
- status = session->provideKeyResponse(response);
- if (status == Status::OK) {
- if (isOfflineLicense) {
+ sp<Session> session = mSessionLibrary->findSession(sessionId);
+ if (!session.get()) {
+ _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, hidl_vec<uint8_t>());
+ return Void();
+ }
+ setPlayPolicy();
+
+ status = session->provideKeyResponse(response);
+ if (status == Status::OK) {
+ if (isOfflineLicense) {
+ if (isRelease) {
+ mFileHandle.DeleteLicense(keySetId);
+ } else {
if (!makeKeySetId(&keySetId)) {
_hidl_cb(Status::ERROR_DRM_UNKNOWN, hidl_vec<uint8_t>());
return Void();
}
+
bool ok = mFileHandle.StoreLicense(
keySetId,
DeviceFiles::kLicenseStateActive,
@@ -332,32 +354,32 @@
ALOGE("Failed to store offline license");
}
}
-
- // Test calling AMediaDrm listeners.
- sendEvent(EventType::VENDOR_DEFINED, sessionId, sessionId);
-
- sendExpirationUpdate(sessionId, 100);
-
- std::vector<KeyStatus> keysStatus;
- KeyStatus keyStatus;
-
- std::vector<uint8_t> keyId1 = { 0xA, 0xB, 0xC };
- keyStatus.keyId = keyId1;
- keyStatus.type = V1_0::KeyStatusType::USABLE;
- keysStatus.push_back(keyStatus);
-
- std::vector<uint8_t> keyId2 = { 0xD, 0xE, 0xF };
- keyStatus.keyId = keyId2;
- keyStatus.type = V1_0::KeyStatusType::EXPIRED;
- keysStatus.push_back(keyStatus);
-
- sendKeysChange(sessionId, keysStatus, true);
-
- installSecureStop(sessionId);
- } else {
- ALOGE("Failed to add key, error=%d", status);
}
- } // keyType::STREAMING || keyType::OFFLINE
+
+ // Test calling AMediaDrm listeners.
+ sendEvent(EventType::VENDOR_DEFINED, sessionId, sessionId);
+
+ sendExpirationUpdate(sessionId, 100);
+
+ std::vector<KeyStatus> keysStatus;
+ KeyStatus keyStatus;
+
+ std::vector<uint8_t> keyId1 = { 0xA, 0xB, 0xC };
+ keyStatus.keyId = keyId1;
+ keyStatus.type = V1_0::KeyStatusType::USABLE;
+ keysStatus.push_back(keyStatus);
+
+ std::vector<uint8_t> keyId2 = { 0xD, 0xE, 0xF };
+ keyStatus.keyId = keyId2;
+ keyStatus.type = V1_0::KeyStatusType::EXPIRED;
+ keysStatus.push_back(keyStatus);
+
+ sendKeysChange(sessionId, keysStatus, true);
+
+ installSecureStop(sessionId);
+ } else {
+ ALOGE("provideKeyResponse returns error=%d", status);
+ }
std::vector<uint8_t> keySetIdVec(keySetId.begin(), keySetId.end());
_hidl_cb(status, toHidlVec(keySetIdVec));
@@ -371,10 +393,10 @@
}
DeviceFiles::LicenseState licenseState;
- std::string keySetIdString(keySetId.begin(), keySetId.end());
std::string offlineLicense;
Status status = Status::OK;
- if (!mFileHandle.RetrieveLicense(keySetIdString, &licenseState, &offlineLicense)) {
+ if (!mFileHandle.RetrieveLicense(std::string(keySetId.begin(), keySetId.end()),
+ &licenseState, &offlineLicense)) {
ALOGE("Failed to restore offline license");
return Status::ERROR_DRM_NO_LICENSE;
}
@@ -606,6 +628,51 @@
return Void();
}
+Return<void> DrmPlugin::getOfflineLicenseKeySetIds(getOfflineLicenseKeySetIds_cb _hidl_cb) {
+ std::vector<std::string> licenseNames = mFileHandle.ListLicenses();
+ std::vector<KeySetId> keySetIds;
+ for (const auto& name : licenseNames) {
+ std::vector<uint8_t> keySetId(name.begin(), name.end());
+ keySetIds.push_back(keySetId);
+ }
+ _hidl_cb(Status::OK, keySetIds);
+ return Void();
+}
+
+
+Return<Status> DrmPlugin::removeOfflineLicense(const KeySetId& keySetId) {
+ std::string licenseName(keySetId.begin(), keySetId.end());
+ if (mFileHandle.DeleteLicense(licenseName)) {
+ return Status::OK;
+ }
+ return Status::BAD_VALUE;
+}
+
+Return<void> DrmPlugin::getOfflineLicenseState(const KeySetId& keySetId,
+ getOfflineLicenseState_cb _hidl_cb) {
+ std::string licenseName(keySetId.begin(), keySetId.end());
+ DeviceFiles::LicenseState state;
+ std::string license;
+ OfflineLicenseState hLicenseState;
+ if (mFileHandle.RetrieveLicense(licenseName, &state, &license)) {
+ switch (state) {
+ case DeviceFiles::kLicenseStateActive:
+ hLicenseState = OfflineLicenseState::USABLE;
+ break;
+ case DeviceFiles::kLicenseStateReleasing:
+ hLicenseState = OfflineLicenseState::INACTIVE;
+ break;
+ case DeviceFiles::kLicenseStateUnknown:
+ hLicenseState = OfflineLicenseState::UNKNOWN;
+ break;
+ }
+ _hidl_cb(Status::OK, hLicenseState);
+ } else {
+ _hidl_cb(Status::BAD_VALUE, OfflineLicenseState::UNKNOWN);
+ }
+ return Void();
+}
+
Return<void> DrmPlugin::getSecureStops(getSecureStops_cb _hidl_cb) {
std::vector<SecureStop> stops;
for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) {
@@ -708,7 +775,7 @@
}
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp
index dd1689f..8ebb42b 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp
@@ -31,7 +31,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
namespace {
@@ -156,7 +156,8 @@
}
if (keyType == V1_0::KeyType::STREAMING) {
request.append(kTemporarySession);
- } else if (keyType == V1_0::KeyType::OFFLINE) {
+ } else if (keyType == V1_0::KeyType::OFFLINE ||
+ keyType == V1_0::KeyType::RELEASE) {
request.append(kPersistentSession);
}
@@ -168,7 +169,7 @@
}
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp
index 1c32b1b..d93777d 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp
@@ -36,7 +36,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
JsonWebKey::JsonWebKey() {
@@ -271,7 +271,7 @@
}
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp b/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp
index 47f188d..2dcd00f 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp
@@ -3,7 +3,6 @@
// License Agreement.
#include <utils/Log.h>
-
#include <string>
#include "MemoryFileSystem.h"
@@ -12,7 +11,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
std::string MemoryFileSystem::GetFileName(const std::string& path) {
@@ -39,6 +38,14 @@
}
}
+std::vector<std::string> MemoryFileSystem::ListFiles() const {
+ std::vector<std::string> list;
+ for (const auto& filename : mMemoryFileSystem) {
+ list.push_back(filename.first);
+ }
+ return list;
+}
+
size_t MemoryFileSystem::Read(const std::string& path, std::string* buffer) {
std::string key = GetFileName(path);
auto result = mMemoryFileSystem.find(key);
@@ -54,6 +61,10 @@
size_t MemoryFileSystem::Write(const std::string& path, const MemoryFile& memoryFile) {
std::string key = GetFileName(path);
+ auto result = mMemoryFileSystem.find(key);
+ if (result != mMemoryFileSystem.end()) {
+ mMemoryFileSystem.erase(key);
+ }
mMemoryFileSystem.insert(std::pair<std::string, MemoryFile>(key, memoryFile));
return memoryFile.getFileSize();
}
@@ -75,7 +86,7 @@
}
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/Session.cpp b/drm/mediadrm/plugins/clearkey/hidl/Session.cpp
index 41a8374..f4c49b9 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/Session.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/Session.cpp
@@ -28,7 +28,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
using ::android::hardware::drm::V1_0::KeyValue;
@@ -90,7 +90,7 @@
}
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp b/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp
index b4319e6..99fb30f 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp
@@ -24,7 +24,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
using ::android::hardware::hidl_string;
@@ -59,7 +59,8 @@
mSessions.insert(std::pair<std::vector<uint8_t>,
sp<Session> >(sessionId, new Session(sessionId)));
- std::map<std::vector<uint8_t>, sp<Session> >::iterator itr = mSessions.find(sessionId);
+ std::map<std::vector<uint8_t>, sp<Session> >::iterator itr =
+ mSessions.find(sessionId);
if (itr != mSessions.end()) {
return itr->second;
} else {
@@ -70,7 +71,8 @@
sp<Session> SessionLibrary::findSession(
const std::vector<uint8_t>& sessionId) {
Mutex::Autolock lock(mSessionsLock);
- std::map<std::vector<uint8_t>, sp<Session> >::iterator itr = mSessions.find(sessionId);
+ std::map<std::vector<uint8_t>, sp<Session> >::iterator itr =
+ mSessions.find(sessionId);
if (itr != mSessions.end()) {
return itr->second;
} else {
@@ -84,7 +86,7 @@
}
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.1-service.clearkey.rc b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.1-service.clearkey.rc
deleted file mode 100644
index ffe856a..0000000
--- a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.1-service.clearkey.rc
+++ /dev/null
@@ -1,6 +0,0 @@
-service vendor.drm-clearkey-hal-1-1 /vendor/bin/hw/android.hardware.drm@1.1-service.clearkey
- class hal
- user media
- group media mediadrm
- ioprio rt 4
- writepid /dev/cpuset/foreground/tasks
diff --git a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service.clearkey.rc b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service.clearkey.rc
new file mode 100644
index 0000000..ac184f7
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service.clearkey.rc
@@ -0,0 +1,6 @@
+service vendor.drm-clearkey-hal-1-2 /vendor/bin/hw/android.hardware.drm@1.2-service.clearkey
+ class hal
+ user media
+ group media mediadrm
+ ioprio rt 4
+ writepid /dev/cpuset/foreground/tasks
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h b/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h
index 0c7ef20..721f4c0 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h
@@ -22,7 +22,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
using ::android::hardware::drm::V1_0::Status;
@@ -42,7 +42,7 @@
};
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h b/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h
index 4a385bd..ec30cc1 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h
@@ -25,7 +25,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
using ::android::sp;
@@ -38,7 +38,7 @@
void encodeBase64Url(const void *data, size_t size, std::string *out);
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h b/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h
index 5bbb28a..c497e37 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h
@@ -25,7 +25,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
using ::android::sp;
@@ -54,7 +54,7 @@
};
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h
index 3d6a4a9..f83903c 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h
@@ -22,7 +22,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
static const std::string kVendorKey("vendor");
@@ -49,7 +49,7 @@
static const uint8_t kMetricsData[] = { 0 };
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h
index 46cb5e4..7e9b6bd 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h
@@ -24,7 +24,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
using ::android::hardware::drm::V1_0::KeyValue;
@@ -47,7 +47,7 @@
void operator=(const TypeName&) = delete;
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h b/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h
index 9952027..6368f3d 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h
@@ -17,17 +17,17 @@
#ifndef CLEARKEY_CREATE_PLUGIN_FACTORIES_H_
#define CLEARKEY_CREATE_PLUGIN_FACTORIES_H_
-#include <android/hardware/drm/1.1/ICryptoFactory.h>
-#include <android/hardware/drm/1.1/IDrmFactory.h>
+#include <android/hardware/drm/1.2/ICryptoFactory.h>
+#include <android/hardware/drm/1.2/IDrmFactory.h>
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
-using ::android::hardware::drm::V1_1::ICryptoFactory;
-using ::android::hardware::drm::V1_1::IDrmFactory;
+using ::android::hardware::drm::V1_2::ICryptoFactory;
+using ::android::hardware::drm::V1_2::IDrmFactory;
extern "C" {
IDrmFactory* createDrmFactory();
@@ -35,7 +35,7 @@
}
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h
index 175ab76..203bb2d 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h
@@ -18,17 +18,17 @@
#define CLEARKEY_CRYPTO_FACTORY_H_
#include <android/hardware/drm/1.0/ICryptoPlugin.h>
-#include <android/hardware/drm/1.1/ICryptoFactory.h>
+#include <android/hardware/drm/1.2/ICryptoFactory.h>
#include "ClearKeyTypes.h"
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
-using ::android::hardware::drm::V1_1::ICryptoFactory;
+using ::android::hardware::drm::V1_2::ICryptoFactory;
using ::android::hardware::drm::V1_0::ICryptoPlugin;
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_string;
@@ -52,7 +52,7 @@
};
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h
index 6a73806..480dc7e 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h
@@ -32,7 +32,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
using ::android::hardware::drm::V1_0::DestinationBuffer;
@@ -96,7 +96,7 @@
};
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DeviceFiles.h b/drm/mediadrm/plugins/clearkey/hidl/include/DeviceFiles.h
index a201e88..554ae59 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DeviceFiles.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DeviceFiles.h
@@ -20,15 +20,15 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
class DeviceFiles {
public:
typedef enum {
+ kLicenseStateUnknown,
kLicenseStateActive,
kLicenseStateReleasing,
- kLicenseStateUnknown,
} LicenseState;
DeviceFiles() {};
@@ -42,6 +42,10 @@
virtual bool LicenseExists(const std::string& keySetId);
+ virtual std::vector<std::string> ListLicenses() const;
+
+ virtual bool DeleteLicense(const std::string& keySetId);
+
virtual bool DeleteAllLicenses();
private:
@@ -59,7 +63,7 @@
};
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h
index 6f58195..ff715ea 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h
@@ -17,15 +17,15 @@
#ifndef CLEARKEY_DRM_FACTORY_H_
#define CLEARKEY_DRM_FACTORY_H_
-#include <android/hardware/drm/1.1/IDrmPlugin.h>
-#include <android/hardware/drm/1.1/IDrmFactory.h>
+#include <android/hardware/drm/1.2/IDrmPlugin.h>
+#include <android/hardware/drm/1.2/IDrmFactory.h>
#include "ClearKeyTypes.h"
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
using ::android::hardware::hidl_array;
@@ -52,7 +52,7 @@
};
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
index 12d8608..256c5d6 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
@@ -17,10 +17,12 @@
#ifndef CLEARKEY_DRM_PLUGIN_H_
#define CLEARKEY_DRM_PLUGIN_H_
-#include <android/hardware/drm/1.1/IDrmPlugin.h>
+#include <android/hardware/drm/1.2/IDrmPlugin.h>
-#include <stdio.h>
#include <map>
+#include <stdio.h>
+
+#include <utils/List.h>
#include "DeviceFiles.h"
#include "SessionLibrary.h"
@@ -29,7 +31,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
using ::android::hardware::drm::V1_0::EventType;
@@ -42,8 +44,12 @@
using ::android::hardware::drm::V1_0::SessionId;
using ::android::hardware::drm::V1_0::Status;
using ::android::hardware::drm::V1_1::DrmMetricGroup;
-using ::android::hardware::drm::V1_1::IDrmPlugin;
+using ::android::hardware::drm::V1_1::HdcpLevel;
using ::android::hardware::drm::V1_1::KeyRequestType;
+using ::android::hardware::drm::V1_1::SecureStopRelease;
+using ::android::hardware::drm::V1_1::SecurityLevel;
+using ::android::hardware::drm::V1_2::IDrmPlugin;
+using ::android::hardware::drm::V1_2::OfflineLicenseState;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
@@ -136,6 +142,13 @@
Return<void> getMetrics(getMetrics_cb _hidl_cb) override;
+ Return<void> getOfflineLicenseKeySetIds(getOfflineLicenseKeySetIds_cb _hidl_cb) override;
+
+ Return<Status> removeOfflineLicense(const KeySetId &keySetId) override;
+
+ Return<void> getOfflineLicenseState(const KeySetId &keySetId,
+ getOfflineLicenseState_cb _hidl_cb) override;
+
Return<void> getPropertyString(
const hidl_string& name,
getPropertyString_cb _hidl_cb) override;
@@ -319,6 +332,7 @@
std::vector<KeyValue> mPlayPolicy;
std::map<std::string, std::string> mStringProperties;
std::map<std::string, std::vector<uint8_t> > mByteArrayProperties;
+ std::map<std::string, std::vector<uint8_t> > mReleaseKeysMap;
std::map<std::vector<uint8_t>, SecurityLevel> mSecurityLevel;
sp<IDrmPluginListener> mListener;
SessionLibrary *mSessionLibrary;
@@ -333,7 +347,7 @@
};
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h b/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h
index 7d7abf2..889f511 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h
@@ -24,7 +24,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
using ::android::hardware::drm::V1_0::Status;
@@ -49,7 +49,7 @@
};
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h b/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h
index 4ab034c..e57470c 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h
@@ -23,7 +23,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
class JsonWebKey {
@@ -54,7 +54,7 @@
};
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h b/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h
index db368d7..bcd9fd6 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h
@@ -13,7 +13,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
// Using android file system requires clearkey plugin to update
@@ -32,7 +32,9 @@
size_t getFileSize() const { return fileSize; }
void setContent(const std::string& file) { content = file; }
void setFileName(const std::string& name) { fileName = name; }
- void setFileSize(size_t size) { fileSize = size; }
+ void setFileSize(size_t size) {
+ content.resize(size); fileSize = size;
+ }
};
MemoryFileSystem() {};
@@ -40,6 +42,7 @@
bool FileExists(const std::string& fileName) const;
ssize_t GetFileSize(const std::string& fileName) const;
+ std::vector<std::string> ListFiles() const;
size_t Read(const std::string& pathName, std::string* buffer);
bool RemoveAllFiles();
bool RemoveFile(const std::string& fileName);
@@ -57,7 +60,7 @@
};
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Session.h b/drm/mediadrm/plugins/clearkey/hidl/include/Session.h
index 1064dc7..f35560d 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/Session.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/Session.h
@@ -27,7 +27,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
using ::android::hardware::drm::V1_0::Status;
@@ -64,7 +64,7 @@
};
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h b/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h
index 326a0c1..1e567b8 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h
@@ -26,7 +26,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
using ::android::sp;
@@ -58,7 +58,7 @@
};
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h b/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h
index cc06329..f6d30c9 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h
@@ -24,7 +24,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_1 {
+namespace V1_2 {
namespace clearkey {
using ::android::hardware::hidl_array;
@@ -69,7 +69,7 @@
}
} // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/protos/DeviceFiles.proto b/drm/mediadrm/plugins/clearkey/hidl/protos/DeviceFiles.proto
index 145ac5b..3e11f0b 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/protos/DeviceFiles.proto
+++ b/drm/mediadrm/plugins/clearkey/hidl/protos/DeviceFiles.proto
@@ -10,7 +10,7 @@
//
syntax = "proto2";
-package android.hardware.drm.V1_1.clearkey;
+package android.hardware.drm.V1_2.clearkey;
// need this if we are using libprotobuf-cpp-2.3.0-lite
option optimize_for = LITE_RUNTIME;
diff --git a/drm/mediadrm/plugins/clearkey/hidl/service.cpp b/drm/mediadrm/plugins/clearkey/hidl/service.cpp
index 6a97b72..4ca31f3 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/service.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/service.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#define LOG_TAG "android.hardware.drm@1.1-service.clearkey"
+#define LOG_TAG "android.hardware.drm@1.2-service.clearkey"
#include <CryptoFactory.h>
#include <DrmFactory.h>
@@ -26,14 +26,14 @@
using ::android::hardware::joinRpcThreadpool;
using ::android::sp;
-using android::hardware::drm::V1_1::ICryptoFactory;
-using android::hardware::drm::V1_1::IDrmFactory;
-using android::hardware::drm::V1_1::clearkey::CryptoFactory;
-using android::hardware::drm::V1_1::clearkey::DrmFactory;
+using android::hardware::drm::V1_2::ICryptoFactory;
+using android::hardware::drm::V1_2::IDrmFactory;
+using android::hardware::drm::V1_2::clearkey::CryptoFactory;
+using android::hardware::drm::V1_2::clearkey::DrmFactory;
int main(int /* argc */, char** /* argv */) {
- ALOGD("android.hardware.drm@1.1-service.clearkey starting...");
+ ALOGD("android.hardware.drm@1.2-service.clearkey starting...");
// The DRM HAL may communicate to other vendor components via
// /dev/vndbinder
diff --git a/include/soundtrigger/ISoundTrigger.h b/include/soundtrigger/ISoundTrigger.h
index ea1aea6..c357caa 100644
--- a/include/soundtrigger/ISoundTrigger.h
+++ b/include/soundtrigger/ISoundTrigger.h
@@ -40,8 +40,7 @@
virtual status_t startRecognition(sound_model_handle_t handle,
const sp<IMemory>& dataMemory) = 0;
virtual status_t stopRecognition(sound_model_handle_t handle) = 0;
- virtual status_t getModelState(sound_model_handle_t handle,
- sp<IMemory>& eventMemory) = 0;
+ virtual status_t getModelState(sound_model_handle_t handle) = 0;
};
diff --git a/include/soundtrigger/SoundTrigger.h b/include/soundtrigger/SoundTrigger.h
index dcf9ce8..2e2ff7a 100644
--- a/include/soundtrigger/SoundTrigger.h
+++ b/include/soundtrigger/SoundTrigger.h
@@ -52,7 +52,7 @@
status_t startRecognition(sound_model_handle_t handle, const sp<IMemory>& dataMemory);
status_t stopRecognition(sound_model_handle_t handle);
- status_t getModelState(sound_model_handle_t handle, sp<IMemory>& eventMemory);
+ status_t getModelState(sound_model_handle_t handle);
// BpSoundTriggerClient
virtual void onRecognitionEvent(const sp<IMemory>& eventMemory);
diff --git a/media/bufferpool/1.0/Accessor.cpp b/media/bufferpool/1.0/Accessor.cpp
new file mode 100644
index 0000000..b1dfc08
--- /dev/null
+++ b/media/bufferpool/1.0/Accessor.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "BufferPoolConnection"
+
+#include "Accessor.h"
+#include "AccessorImpl.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+void ConnectionDeathRecipient::add(
+ int64_t connectionId,
+ const sp<Accessor> &accessor) {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mAccessors.find(connectionId) == mAccessors.end()) {
+ mAccessors.insert(std::make_pair(connectionId, accessor));
+ }
+}
+
+void ConnectionDeathRecipient::remove(int64_t connectionId) {
+ std::lock_guard<std::mutex> lock(mLock);
+ mAccessors.erase(connectionId);
+ auto it = mConnectionToCookie.find(connectionId);
+ if (it != mConnectionToCookie.end()) {
+ uint64_t cookie = it->second;
+ mConnectionToCookie.erase(it);
+ auto cit = mCookieToConnections.find(cookie);
+ if (cit != mCookieToConnections.end()) {
+ cit->second.erase(connectionId);
+ if (cit->second.size() == 0) {
+ mCookieToConnections.erase(cit);
+ }
+ }
+ }
+}
+
+void ConnectionDeathRecipient::addCookieToConnection(
+ uint64_t cookie,
+ int64_t connectionId) {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mAccessors.find(connectionId) == mAccessors.end()) {
+ return;
+ }
+ mConnectionToCookie.insert(std::make_pair(connectionId, cookie));
+ auto it = mCookieToConnections.find(cookie);
+ if (it != mCookieToConnections.end()) {
+ it->second.insert(connectionId);
+ } else {
+ mCookieToConnections.insert(std::make_pair(
+ cookie, std::set<int64_t>{connectionId}));
+ }
+}
+
+void ConnectionDeathRecipient::serviceDied(
+ uint64_t cookie,
+ const wp<::android::hidl::base::V1_0::IBase>& /* who */
+ ) {
+ std::map<int64_t, const wp<Accessor>> connectionsToClose;
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+
+ auto it = mCookieToConnections.find(cookie);
+ if (it != mCookieToConnections.end()) {
+ for (auto conIt = it->second.begin(); conIt != it->second.end(); ++conIt) {
+ auto accessorIt = mAccessors.find(*conIt);
+ if (accessorIt != mAccessors.end()) {
+ connectionsToClose.insert(std::make_pair(*conIt, accessorIt->second));
+ mAccessors.erase(accessorIt);
+ }
+ mConnectionToCookie.erase(*conIt);
+ }
+ mCookieToConnections.erase(it);
+ }
+ }
+
+ if (connectionsToClose.size() > 0) {
+ sp<Accessor> accessor;
+ for (auto it = connectionsToClose.begin(); it != connectionsToClose.end(); ++it) {
+ accessor = it->second.promote();
+
+ if (accessor) {
+ accessor->close(it->first);
+ ALOGD("connection %lld closed on death", (long long)it->first);
+ }
+ }
+ }
+}
+
+namespace {
+static sp<ConnectionDeathRecipient> sConnectionDeathRecipient =
+ new ConnectionDeathRecipient();
+}
+
+sp<ConnectionDeathRecipient> Accessor::getConnectionDeathRecipient() {
+ return sConnectionDeathRecipient;
+}
+
+// Methods from ::android::hardware::media::bufferpool::V1_0::IAccessor follow.
+Return<void> Accessor::connect(connect_cb _hidl_cb) {
+ sp<Connection> connection;
+ ConnectionId connectionId;
+ const QueueDescriptor* fmqDesc;
+
+ ResultStatus status = connect(&connection, &connectionId, &fmqDesc, false);
+ if (status == ResultStatus::OK) {
+ _hidl_cb(status, connection, connectionId, *fmqDesc);
+ } else {
+ _hidl_cb(status, nullptr, -1LL,
+ android::hardware::MQDescriptorSync<BufferStatusMessage>(
+ std::vector<android::hardware::GrantorDescriptor>(),
+ nullptr /* nhandle */, 0 /* size */));
+ }
+ return Void();
+}
+
+Accessor::Accessor(const std::shared_ptr<BufferPoolAllocator> &allocator)
+ : mImpl(new Impl(allocator)) {}
+
+Accessor::~Accessor() {
+}
+
+bool Accessor::isValid() {
+ return (bool)mImpl;
+}
+
+ResultStatus Accessor::allocate(
+ ConnectionId connectionId,
+ const std::vector<uint8_t> ¶ms,
+ BufferId *bufferId, const native_handle_t** handle) {
+ if (mImpl) {
+ return mImpl->allocate(connectionId, params, bufferId, handle);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::fetch(
+ ConnectionId connectionId, TransactionId transactionId,
+ BufferId bufferId, const native_handle_t** handle) {
+ if (mImpl) {
+ return mImpl->fetch(connectionId, transactionId, bufferId, handle);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::connect(
+ sp<Connection> *connection, ConnectionId *pConnectionId,
+ const QueueDescriptor** fmqDescPtr, bool local) {
+ if (mImpl) {
+ ResultStatus status = mImpl->connect(this, connection, pConnectionId, fmqDescPtr);
+ if (!local && status == ResultStatus::OK) {
+ sp<Accessor> accessor(this);
+ sConnectionDeathRecipient->add(*pConnectionId, accessor);
+ }
+ return status;
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::close(ConnectionId connectionId) {
+ if (mImpl) {
+ ResultStatus status = mImpl->close(connectionId);
+ sConnectionDeathRecipient->remove(connectionId);
+ return status;
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+void Accessor::cleanUp(bool clearCache) {
+ if (mImpl) {
+ mImpl->cleanUp(clearCache);
+ }
+}
+
+//IAccessor* HIDL_FETCH_IAccessor(const char* /* name */) {
+// return new Accessor();
+//}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/bufferpool/1.0/Accessor.h b/media/bufferpool/1.0/Accessor.h
new file mode 100644
index 0000000..2f86f7b
--- /dev/null
+++ b/media/bufferpool/1.0/Accessor.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H
+
+#include <android/hardware/media/bufferpool/1.0/IAccessor.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include "BufferStatus.h"
+
+#include <set>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct Accessor;
+struct Connection;
+
+/**
+ * Receives death notifications from remote connections.
+ * On death notifications, the connections are closed and used resources
+ * are released.
+ */
+struct ConnectionDeathRecipient : public hardware::hidl_death_recipient {
+ /**
+ * Registers a newly connected connection from remote processes.
+ */
+ void add(int64_t connectionId, const sp<Accessor> &accessor);
+
+ /**
+ * Removes a connection.
+ */
+ void remove(int64_t connectionId);
+
+ void addCookieToConnection(uint64_t cookie, int64_t connectionId);
+
+ virtual void serviceDied(
+ uint64_t /* cookie */,
+ const wp<::android::hidl::base::V1_0::IBase>& /* who */
+ ) override;
+
+private:
+ std::mutex mLock;
+ std::map<uint64_t, std::set<int64_t>> mCookieToConnections;
+ std::map<int64_t, uint64_t> mConnectionToCookie;
+ std::map<int64_t, const wp<Accessor>> mAccessors;
+};
+
+/**
+ * A buffer pool accessor which enables a buffer pool to communicate with buffer
+ * pool clients. 1:1 correspondense holds between a buffer pool and an accessor.
+ */
+struct Accessor : public IAccessor {
+ // Methods from ::android::hardware::media::bufferpool::V1_0::IAccessor follow.
+ Return<void> connect(connect_cb _hidl_cb) override;
+
+ /**
+ * Creates a buffer pool accessor which uses the specified allocator.
+ *
+ * @param allocator buffer allocator.
+ */
+ explicit Accessor(const std::shared_ptr<BufferPoolAllocator> &allocator);
+
+ /** Destructs a buffer pool accessor. */
+ ~Accessor();
+
+ /** Returns whether the accessor is valid. */
+ bool isValid();
+
+ /** Allocates a buffer from a buffer pool.
+ *
+ * @param connectionId the connection id of the client.
+ * @param params the allocation parameters.
+ * @param bufferId the id of the allocated buffer.
+ * @param handle the native handle of the allocated buffer.
+ *
+ * @return OK when a buffer is successfully allocated.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus allocate(
+ ConnectionId connectionId,
+ const std::vector<uint8_t>& params,
+ BufferId *bufferId,
+ const native_handle_t** handle);
+
+ /**
+ * Fetches a buffer for the specified transaction.
+ *
+ * @param connectionId the id of receiving connection(client).
+ * @param transactionId the id of the transfer transaction.
+ * @param bufferId the id of the buffer to be fetched.
+ * @param handle the native handle of the fetched buffer.
+ *
+ * @return OK when a buffer is successfully fetched.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus fetch(
+ ConnectionId connectionId,
+ TransactionId transactionId,
+ BufferId bufferId,
+ const native_handle_t** handle);
+
+ /**
+ * Makes a connection to the buffer pool. The buffer pool client uses the
+ * created connection in order to communicate with the buffer pool. An
+ * FMQ for buffer status message is also created for the client.
+ *
+ * @param connection created connection
+ * @param pConnectionId the id of the created connection
+ * @param fmqDescPtr FMQ descriptor for shared buffer status message
+ * queue between a buffer pool and the client.
+ * @param local true when a connection request comes from local process,
+ * false otherwise.
+ *
+ * @return OK when a connection is successfully made.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus connect(
+ sp<Connection> *connection, ConnectionId *pConnectionId,
+ const QueueDescriptor** fmqDescPtr, bool local);
+
+ /**
+ * Closes the specified connection to the client.
+ *
+ * @param connectionId the id of the connection.
+ *
+ * @return OK when the connection is closed.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus close(ConnectionId connectionId);
+
+ /**
+ * Processes pending buffer status messages and perfoms periodic cache
+ * cleaning.
+ *
+ * @param clearCache if clearCache is true, it frees all buffers waiting
+ * to be recycled.
+ */
+ void cleanUp(bool clearCache);
+
+ /**
+ * Gets a hidl_death_recipient for remote connection death.
+ */
+ static sp<ConnectionDeathRecipient> getConnectionDeathRecipient();
+
+private:
+ class Impl;
+ std::unique_ptr<Impl> mImpl;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H
diff --git a/media/bufferpool/1.0/AccessorImpl.cpp b/media/bufferpool/1.0/AccessorImpl.cpp
new file mode 100644
index 0000000..fa17f15
--- /dev/null
+++ b/media/bufferpool/1.0/AccessorImpl.cpp
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BufferPoolAccessor"
+//#define LOG_NDEBUG 0
+
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <utils/Log.h>
+#include "AccessorImpl.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+namespace {
+ static constexpr int64_t kCleanUpDurationUs = 500000; // TODO tune 0.5 sec
+ static constexpr int64_t kLogDurationUs = 5000000; // 5 secs
+
+ static constexpr size_t kMinAllocBytesForEviction = 1024*1024*15;
+ static constexpr size_t kMinBufferCountForEviction = 40;
+}
+
+// Buffer structure in bufferpool process
+struct InternalBuffer {
+ BufferId mId;
+ size_t mOwnerCount;
+ size_t mTransactionCount;
+ const std::shared_ptr<BufferPoolAllocation> mAllocation;
+ const size_t mAllocSize;
+ const std::vector<uint8_t> mConfig;
+
+ InternalBuffer(
+ BufferId id,
+ const std::shared_ptr<BufferPoolAllocation> &alloc,
+ const size_t allocSize,
+ const std::vector<uint8_t> &allocConfig)
+ : mId(id), mOwnerCount(0), mTransactionCount(0),
+ mAllocation(alloc), mAllocSize(allocSize), mConfig(allocConfig) {}
+
+ const native_handle_t *handle() {
+ return mAllocation->handle();
+ }
+};
+
+struct TransactionStatus {
+ TransactionId mId;
+ BufferId mBufferId;
+ ConnectionId mSender;
+ ConnectionId mReceiver;
+ BufferStatus mStatus;
+ int64_t mTimestampUs;
+ bool mSenderValidated;
+
+ TransactionStatus(const BufferStatusMessage &message, int64_t timestampUs) {
+ mId = message.transactionId;
+ mBufferId = message.bufferId;
+ mStatus = message.newStatus;
+ mTimestampUs = timestampUs;
+ if (mStatus == BufferStatus::TRANSFER_TO) {
+ mSender = message.connectionId;
+ mReceiver = message.targetConnectionId;
+ mSenderValidated = true;
+ } else {
+ mSender = -1LL;
+ mReceiver = message.connectionId;
+ mSenderValidated = false;
+ }
+ }
+};
+
+// Helper template methods for handling map of set.
+template<class T, class U>
+bool insert(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+ auto iter = mapOfSet->find(key);
+ if (iter == mapOfSet->end()) {
+ std::set<U> valueSet{value};
+ mapOfSet->insert(std::make_pair(key, valueSet));
+ return true;
+ } else if (iter->second.find(value) == iter->second.end()) {
+ iter->second.insert(value);
+ return true;
+ }
+ return false;
+}
+
+template<class T, class U>
+bool erase(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+ bool ret = false;
+ auto iter = mapOfSet->find(key);
+ if (iter != mapOfSet->end()) {
+ if (iter->second.erase(value) > 0) {
+ ret = true;
+ }
+ if (iter->second.size() == 0) {
+ mapOfSet->erase(iter);
+ }
+ }
+ return ret;
+}
+
+template<class T, class U>
+bool contains(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+ auto iter = mapOfSet->find(key);
+ if (iter != mapOfSet->end()) {
+ auto setIter = iter->second.find(value);
+ return setIter != iter->second.end();
+ }
+ return false;
+}
+
+int32_t Accessor::Impl::sPid = getpid();
+uint32_t Accessor::Impl::sSeqId = time(nullptr);
+
+Accessor::Impl::Impl(
+ const std::shared_ptr<BufferPoolAllocator> &allocator)
+ : mAllocator(allocator) {}
+
+Accessor::Impl::~Impl() {
+}
+
+ResultStatus Accessor::Impl::connect(
+ const sp<Accessor> &accessor, sp<Connection> *connection,
+ ConnectionId *pConnectionId, const QueueDescriptor** fmqDescPtr) {
+ sp<Connection> newConnection = new Connection();
+ ResultStatus status = ResultStatus::CRITICAL_ERROR;
+ {
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ if (newConnection) {
+ ConnectionId id = (int64_t)sPid << 32 | sSeqId;
+ status = mBufferPool.mObserver.open(id, fmqDescPtr);
+ if (status == ResultStatus::OK) {
+ newConnection->initialize(accessor, id);
+ *connection = newConnection;
+ *pConnectionId = id;
+ ++sSeqId;
+ }
+ }
+ mBufferPool.processStatusMessages();
+ mBufferPool.cleanUp();
+ }
+ return status;
+}
+
+ResultStatus Accessor::Impl::close(ConnectionId connectionId) {
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+ mBufferPool.handleClose(connectionId);
+ mBufferPool.mObserver.close(connectionId);
+ // Since close# will be called after all works are finished, it is OK to
+ // evict unused buffers.
+ mBufferPool.cleanUp(true);
+ return ResultStatus::OK;
+}
+
+ResultStatus Accessor::Impl::allocate(
+ ConnectionId connectionId, const std::vector<uint8_t>& params,
+ BufferId *bufferId, const native_handle_t** handle) {
+ std::unique_lock<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+ ResultStatus status = ResultStatus::OK;
+ if (!mBufferPool.getFreeBuffer(mAllocator, params, bufferId, handle)) {
+ lock.unlock();
+ std::shared_ptr<BufferPoolAllocation> alloc;
+ size_t allocSize;
+ status = mAllocator->allocate(params, &alloc, &allocSize);
+ lock.lock();
+ if (status == ResultStatus::OK) {
+ status = mBufferPool.addNewBuffer(alloc, allocSize, params, bufferId, handle);
+ }
+ ALOGV("create a buffer %d : %u %p",
+ status == ResultStatus::OK, *bufferId, *handle);
+ }
+ if (status == ResultStatus::OK) {
+ // TODO: handle ownBuffer failure
+ mBufferPool.handleOwnBuffer(connectionId, *bufferId);
+ }
+ mBufferPool.cleanUp();
+ return status;
+}
+
+ResultStatus Accessor::Impl::fetch(
+ ConnectionId connectionId, TransactionId transactionId,
+ BufferId bufferId, const native_handle_t** handle) {
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+ auto found = mBufferPool.mTransactions.find(transactionId);
+ if (found != mBufferPool.mTransactions.end() &&
+ contains(&mBufferPool.mPendingTransactions,
+ connectionId, transactionId)) {
+ if (found->second->mSenderValidated &&
+ found->second->mStatus == BufferStatus::TRANSFER_FROM &&
+ found->second->mBufferId == bufferId) {
+ found->second->mStatus = BufferStatus::TRANSFER_FETCH;
+ auto bufferIt = mBufferPool.mBuffers.find(bufferId);
+ if (bufferIt != mBufferPool.mBuffers.end()) {
+ mBufferPool.mStats.onBufferFetched();
+ *handle = bufferIt->second->handle();
+ return ResultStatus::OK;
+ }
+ }
+ }
+ mBufferPool.cleanUp();
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+void Accessor::Impl::cleanUp(bool clearCache) {
+ // transaction timeout, buffer cacheing TTL handling
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+ mBufferPool.cleanUp(clearCache);
+}
+
+Accessor::Impl::Impl::BufferPool::BufferPool()
+ : mTimestampUs(getTimestampNow()),
+ mLastCleanUpUs(mTimestampUs),
+ mLastLogUs(mTimestampUs),
+ mSeq(0) {}
+
+
+// Statistics helper
+template<typename T, typename S>
+int percentage(T base, S total) {
+ return int(total ? 0.5 + 100. * static_cast<S>(base) / total : 0);
+}
+
+Accessor::Impl::Impl::BufferPool::~BufferPool() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ ALOGD("Destruction - bufferpool %p "
+ "cached: %zu/%zuM, %zu/%d%% in use; "
+ "allocs: %zu, %d%% recycled; "
+ "transfers: %zu, %d%% unfetced",
+ this, mStats.mBuffersCached, mStats.mSizeCached >> 20,
+ mStats.mBuffersInUse, percentage(mStats.mBuffersInUse, mStats.mBuffersCached),
+ mStats.mTotalAllocations, percentage(mStats.mTotalRecycles, mStats.mTotalAllocations),
+ mStats.mTotalTransfers,
+ percentage(mStats.mTotalTransfers - mStats.mTotalFetches, mStats.mTotalTransfers));
+}
+
+bool Accessor::Impl::BufferPool::handleOwnBuffer(
+ ConnectionId connectionId, BufferId bufferId) {
+
+ bool added = insert(&mUsingBuffers, connectionId, bufferId);
+ if (added) {
+ auto iter = mBuffers.find(bufferId);
+ iter->second->mOwnerCount++;
+ }
+ insert(&mUsingConnections, bufferId, connectionId);
+ return added;
+}
+
+bool Accessor::Impl::BufferPool::handleReleaseBuffer(
+ ConnectionId connectionId, BufferId bufferId) {
+ bool deleted = erase(&mUsingBuffers, connectionId, bufferId);
+ if (deleted) {
+ auto iter = mBuffers.find(bufferId);
+ iter->second->mOwnerCount--;
+ if (iter->second->mOwnerCount == 0 &&
+ iter->second->mTransactionCount == 0) {
+ mStats.onBufferUnused(iter->second->mAllocSize);
+ mFreeBuffers.insert(bufferId);
+ }
+ }
+ erase(&mUsingConnections, bufferId, connectionId);
+ ALOGV("release buffer %u : %d", bufferId, deleted);
+ return deleted;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferTo(const BufferStatusMessage &message) {
+ auto completed = mCompletedTransactions.find(
+ message.transactionId);
+ if (completed != mCompletedTransactions.end()) {
+ // already completed
+ mCompletedTransactions.erase(completed);
+ return true;
+ }
+ // the buffer should exist and be owned.
+ auto bufferIter = mBuffers.find(message.bufferId);
+ if (bufferIter == mBuffers.end() ||
+ !contains(&mUsingBuffers, message.connectionId, message.bufferId)) {
+ return false;
+ }
+ auto found = mTransactions.find(message.transactionId);
+ if (found != mTransactions.end()) {
+ // transfer_from was received earlier.
+ found->second->mSender = message.connectionId;
+ found->second->mSenderValidated = true;
+ return true;
+ }
+ // TODO: verify there is target connection Id
+ mStats.onBufferSent();
+ mTransactions.insert(std::make_pair(
+ message.transactionId,
+ std::make_unique<TransactionStatus>(message, mTimestampUs)));
+ insert(&mPendingTransactions, message.targetConnectionId,
+ message.transactionId);
+ bufferIter->second->mTransactionCount++;
+ return true;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferFrom(const BufferStatusMessage &message) {
+ auto found = mTransactions.find(message.transactionId);
+ if (found == mTransactions.end()) {
+ // TODO: is it feasible to check ownership here?
+ mStats.onBufferSent();
+ mTransactions.insert(std::make_pair(
+ message.transactionId,
+ std::make_unique<TransactionStatus>(message, mTimestampUs)));
+ insert(&mPendingTransactions, message.connectionId,
+ message.transactionId);
+ auto bufferIter = mBuffers.find(message.bufferId);
+ bufferIter->second->mTransactionCount++;
+ } else {
+ if (message.connectionId == found->second->mReceiver) {
+ found->second->mStatus = BufferStatus::TRANSFER_FROM;
+ }
+ }
+ return true;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferResult(const BufferStatusMessage &message) {
+ auto found = mTransactions.find(message.transactionId);
+ if (found != mTransactions.end()) {
+ bool deleted = erase(&mPendingTransactions, message.connectionId,
+ message.transactionId);
+ if (deleted) {
+ if (!found->second->mSenderValidated) {
+ mCompletedTransactions.insert(message.transactionId);
+ }
+ auto bufferIter = mBuffers.find(message.bufferId);
+ if (message.newStatus == BufferStatus::TRANSFER_OK) {
+ handleOwnBuffer(message.connectionId, message.bufferId);
+ }
+ bufferIter->second->mTransactionCount--;
+ if (bufferIter->second->mOwnerCount == 0
+ && bufferIter->second->mTransactionCount == 0) {
+ mStats.onBufferUnused(bufferIter->second->mAllocSize);
+ mFreeBuffers.insert(message.bufferId);
+ }
+ mTransactions.erase(found);
+ }
+ ALOGV("transfer finished %llu %u - %d", (unsigned long long)message.transactionId,
+ message.bufferId, deleted);
+ return deleted;
+ }
+ ALOGV("transfer not found %llu %u", (unsigned long long)message.transactionId,
+ message.bufferId);
+ return false;
+}
+
+void Accessor::Impl::BufferPool::processStatusMessages() {
+ std::vector<BufferStatusMessage> messages;
+ mObserver.getBufferStatusChanges(messages);
+ mTimestampUs = getTimestampNow();
+ for (BufferStatusMessage& message: messages) {
+ bool ret = false;
+ switch (message.newStatus) {
+ case BufferStatus::NOT_USED:
+ ret = handleReleaseBuffer(
+ message.connectionId, message.bufferId);
+ break;
+ case BufferStatus::USED:
+ // not happening
+ break;
+ case BufferStatus::TRANSFER_TO:
+ ret = handleTransferTo(message);
+ break;
+ case BufferStatus::TRANSFER_FROM:
+ ret = handleTransferFrom(message);
+ break;
+ case BufferStatus::TRANSFER_TIMEOUT:
+ // TODO
+ break;
+ case BufferStatus::TRANSFER_LOST:
+ // TODO
+ break;
+ case BufferStatus::TRANSFER_FETCH:
+ // not happening
+ break;
+ case BufferStatus::TRANSFER_OK:
+ case BufferStatus::TRANSFER_ERROR:
+ ret = handleTransferResult(message);
+ break;
+ }
+ if (ret == false) {
+ ALOGW("buffer status message processing failure - message : %d connection : %lld",
+ message.newStatus, (long long)message.connectionId);
+ }
+ }
+ messages.clear();
+}
+
+bool Accessor::Impl::BufferPool::handleClose(ConnectionId connectionId) {
+ // Cleaning buffers
+ auto buffers = mUsingBuffers.find(connectionId);
+ if (buffers != mUsingBuffers.end()) {
+ for (const BufferId& bufferId : buffers->second) {
+ bool deleted = erase(&mUsingConnections, bufferId, connectionId);
+ if (deleted) {
+ auto bufferIter = mBuffers.find(bufferId);
+ bufferIter->second->mOwnerCount--;
+ if (bufferIter->second->mOwnerCount == 0 &&
+ bufferIter->second->mTransactionCount == 0) {
+ // TODO: handle freebuffer insert fail
+ mStats.onBufferUnused(bufferIter->second->mAllocSize);
+ mFreeBuffers.insert(bufferId);
+ }
+ }
+ }
+ mUsingBuffers.erase(buffers);
+ }
+
+ // Cleaning transactions
+ auto pending = mPendingTransactions.find(connectionId);
+ if (pending != mPendingTransactions.end()) {
+ for (const TransactionId& transactionId : pending->second) {
+ auto iter = mTransactions.find(transactionId);
+ if (iter != mTransactions.end()) {
+ if (!iter->second->mSenderValidated) {
+ mCompletedTransactions.insert(transactionId);
+ }
+ BufferId bufferId = iter->second->mBufferId;
+ auto bufferIter = mBuffers.find(bufferId);
+ bufferIter->second->mTransactionCount--;
+ if (bufferIter->second->mOwnerCount == 0 &&
+ bufferIter->second->mTransactionCount == 0) {
+ // TODO: handle freebuffer insert fail
+ mStats.onBufferUnused(bufferIter->second->mAllocSize);
+ mFreeBuffers.insert(bufferId);
+ }
+ mTransactions.erase(iter);
+ }
+ }
+ }
+ return true;
+}
+
+bool Accessor::Impl::BufferPool::getFreeBuffer(
+ const std::shared_ptr<BufferPoolAllocator> &allocator,
+ const std::vector<uint8_t> ¶ms, BufferId *pId,
+ const native_handle_t** handle) {
+ auto bufferIt = mFreeBuffers.begin();
+ for (;bufferIt != mFreeBuffers.end(); ++bufferIt) {
+ BufferId bufferId = *bufferIt;
+ if (allocator->compatible(params, mBuffers[bufferId]->mConfig)) {
+ break;
+ }
+ }
+ if (bufferIt != mFreeBuffers.end()) {
+ BufferId id = *bufferIt;
+ mFreeBuffers.erase(bufferIt);
+ mStats.onBufferRecycled(mBuffers[id]->mAllocSize);
+ *handle = mBuffers[id]->handle();
+ *pId = id;
+ ALOGV("recycle a buffer %u %p", id, *handle);
+ return true;
+ }
+ return false;
+}
+
+ResultStatus Accessor::Impl::BufferPool::addNewBuffer(
+ const std::shared_ptr<BufferPoolAllocation> &alloc,
+ const size_t allocSize,
+ const std::vector<uint8_t> ¶ms,
+ BufferId *pId,
+ const native_handle_t** handle) {
+
+ BufferId bufferId = mSeq++;
+ if (mSeq == Connection::SYNC_BUFFERID) {
+ mSeq = 0;
+ }
+ std::unique_ptr<InternalBuffer> buffer =
+ std::make_unique<InternalBuffer>(
+ bufferId, alloc, allocSize, params);
+ if (buffer) {
+ auto res = mBuffers.insert(std::make_pair(
+ bufferId, std::move(buffer)));
+ if (res.second) {
+ mStats.onBufferAllocated(allocSize);
+ *handle = alloc->handle();
+ *pId = bufferId;
+ return ResultStatus::OK;
+ }
+ }
+ return ResultStatus::NO_MEMORY;
+}
+
+void Accessor::Impl::BufferPool::cleanUp(bool clearCache) {
+ if (clearCache || mTimestampUs > mLastCleanUpUs + kCleanUpDurationUs) {
+ mLastCleanUpUs = mTimestampUs;
+ if (mTimestampUs > mLastLogUs + kLogDurationUs) {
+ mLastLogUs = mTimestampUs;
+ ALOGD("bufferpool %p : %zu(%zu size) total buffers - "
+ "%zu(%zu size) used buffers - %zu/%zu (recycle/alloc) - "
+ "%zu/%zu (fetch/transfer)",
+ this, mStats.mBuffersCached, mStats.mSizeCached,
+ mStats.mBuffersInUse, mStats.mSizeInUse,
+ mStats.mTotalRecycles, mStats.mTotalAllocations,
+ mStats.mTotalFetches, mStats.mTotalTransfers);
+ }
+ for (auto freeIt = mFreeBuffers.begin(); freeIt != mFreeBuffers.end();) {
+ if (!clearCache && mStats.mSizeCached < kMinAllocBytesForEviction
+ && mBuffers.size() < kMinBufferCountForEviction) {
+ break;
+ }
+ auto it = mBuffers.find(*freeIt);
+ if (it != mBuffers.end() &&
+ it->second->mOwnerCount == 0 && it->second->mTransactionCount == 0) {
+ mStats.onBufferEvicted(it->second->mAllocSize);
+ mBuffers.erase(it);
+ freeIt = mFreeBuffers.erase(freeIt);
+ } else {
+ ++freeIt;
+ ALOGW("bufferpool inconsistent!");
+ }
+ }
+ }
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/bufferpool/1.0/AccessorImpl.h b/media/bufferpool/1.0/AccessorImpl.h
new file mode 100644
index 0000000..c04dbf3
--- /dev/null
+++ b/media/bufferpool/1.0/AccessorImpl.h
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H
+
+#include <map>
+#include <set>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+struct InternalBuffer;
+struct TransactionStatus;
+
+/**
+ * An implementation of a buffer pool accessor(or a buffer pool implementation.) */
+class Accessor::Impl {
+public:
+ Impl(const std::shared_ptr<BufferPoolAllocator> &allocator);
+
+ ~Impl();
+
+ ResultStatus connect(
+ const sp<Accessor> &accessor, sp<Connection> *connection,
+ ConnectionId *pConnectionId, const QueueDescriptor** fmqDescPtr);
+
+ ResultStatus close(ConnectionId connectionId);
+
+ ResultStatus allocate(ConnectionId connectionId,
+ const std::vector<uint8_t>& params,
+ BufferId *bufferId,
+ const native_handle_t** handle);
+
+ ResultStatus fetch(ConnectionId connectionId,
+ TransactionId transactionId,
+ BufferId bufferId,
+ const native_handle_t** handle);
+
+ void cleanUp(bool clearCache);
+
+private:
+ // ConnectionId = pid : (timestamp_created + seqId)
+ // in order to guarantee uniqueness for each connection
+ static uint32_t sSeqId;
+ static int32_t sPid;
+
+ const std::shared_ptr<BufferPoolAllocator> mAllocator;
+
+ /**
+ * Buffer pool implementation.
+ *
+ * Handles buffer status messages. Handles buffer allocation/recycling.
+ * Handles buffer transfer between buffer pool clients.
+ */
+ struct BufferPool {
+ private:
+ std::mutex mMutex;
+ int64_t mTimestampUs;
+ int64_t mLastCleanUpUs;
+ int64_t mLastLogUs;
+ BufferId mSeq;
+ BufferStatusObserver mObserver;
+
+ std::map<ConnectionId, std::set<BufferId>> mUsingBuffers;
+ std::map<BufferId, std::set<ConnectionId>> mUsingConnections;
+
+ std::map<ConnectionId, std::set<TransactionId>> mPendingTransactions;
+ // Transactions completed before TRANSFER_TO message arrival.
+ // Fetch does not occur for the transactions.
+ // Only transaction id is kept for the transactions in short duration.
+ std::set<TransactionId> mCompletedTransactions;
+ // Currently active(pending) transations' status & information.
+ std::map<TransactionId, std::unique_ptr<TransactionStatus>>
+ mTransactions;
+
+ std::map<BufferId, std::unique_ptr<InternalBuffer>> mBuffers;
+ std::set<BufferId> mFreeBuffers;
+
+ /// Buffer pool statistics which tracks allocation and transfer statistics.
+ struct Stats {
+ /// Total size of allocations which are used or available to use.
+ /// (bytes or pixels)
+ size_t mSizeCached;
+ /// # of cached buffers which are used or available to use.
+ size_t mBuffersCached;
+ /// Total size of allocations which are currently used. (bytes or pixels)
+ size_t mSizeInUse;
+ /// # of currently used buffers
+ size_t mBuffersInUse;
+
+ /// # of allocations called on bufferpool. (# of fetched from BlockPool)
+ size_t mTotalAllocations;
+ /// # of allocations that were served from the cache.
+ /// (# of allocator alloc prevented)
+ size_t mTotalRecycles;
+ /// # of buffer transfers initiated.
+ size_t mTotalTransfers;
+ /// # of transfers that had to be fetched.
+ size_t mTotalFetches;
+
+ Stats()
+ : mSizeCached(0), mBuffersCached(0), mSizeInUse(0), mBuffersInUse(0),
+ mTotalAllocations(0), mTotalRecycles(0), mTotalTransfers(0), mTotalFetches(0) {}
+
+ /// A new buffer is allocated on an allocation request.
+ void onBufferAllocated(size_t allocSize) {
+ mSizeCached += allocSize;
+ mBuffersCached++;
+
+ mSizeInUse += allocSize;
+ mBuffersInUse++;
+
+ mTotalAllocations++;
+ }
+
+ /// A buffer is evicted and destroyed.
+ void onBufferEvicted(size_t allocSize) {
+ mSizeCached -= allocSize;
+ mBuffersCached--;
+ }
+
+ /// A buffer is recycled on an allocation request.
+ void onBufferRecycled(size_t allocSize) {
+ mSizeInUse += allocSize;
+ mBuffersInUse++;
+
+ mTotalAllocations++;
+ mTotalRecycles++;
+ }
+
+ /// A buffer is available to be recycled.
+ void onBufferUnused(size_t allocSize) {
+ mSizeInUse -= allocSize;
+ mBuffersInUse--;
+ }
+
+ /// A buffer transfer is initiated.
+ void onBufferSent() {
+ mTotalTransfers++;
+ }
+
+ /// A buffer fetch is invoked by a buffer transfer.
+ void onBufferFetched() {
+ mTotalFetches++;
+ }
+ } mStats;
+
+ public:
+ /** Creates a buffer pool. */
+ BufferPool();
+
+ /** Destroys a buffer pool. */
+ ~BufferPool();
+
+ /**
+ * Processes all pending buffer status messages, and returns the result.
+ * Each status message is handled by methods with 'handle' prefix.
+ */
+ void processStatusMessages();
+
+ /**
+ * Handles a buffer being owned by a connection.
+ *
+ * @param connectionId the id of the buffer owning connection.
+ * @param bufferId the id of the buffer.
+ *
+ * @return {@code true} when the buffer is owned,
+ * {@code false} otherwise.
+ */
+ bool handleOwnBuffer(ConnectionId connectionId, BufferId bufferId);
+
+ /**
+ * Handles a buffer being released by a connection.
+ *
+ * @param connectionId the id of the buffer owning connection.
+ * @param bufferId the id of the buffer.
+ *
+ * @return {@code true} when the buffer ownership is released,
+ * {@code false} otherwise.
+ */
+ bool handleReleaseBuffer(ConnectionId connectionId, BufferId bufferId);
+
+ /**
+ * Handles a transfer transaction start message from the sender.
+ *
+ * @param message a buffer status message for the transaction.
+ *
+ * @result {@code true} when transfer_to message is acknowledged,
+ * {@code false} otherwise.
+ */
+ bool handleTransferTo(const BufferStatusMessage &message);
+
+ /**
+ * Handles a transfer transaction being acked by the receiver.
+ *
+ * @param message a buffer status message for the transaction.
+ *
+ * @result {@code true} when transfer_from message is acknowledged,
+ * {@code false} otherwise.
+ */
+ bool handleTransferFrom(const BufferStatusMessage &message);
+
+ /**
+ * Handles a transfer transaction result message from the receiver.
+ *
+ * @param message a buffer status message for the transaction.
+ *
+ * @result {@code true} when the exisitng transaction is finished,
+ * {@code false} otherwise.
+ */
+ bool handleTransferResult(const BufferStatusMessage &message);
+
+ /**
+ * Handles a connection being closed, and returns the result. All the
+ * buffers and transactions owned by the connection will be cleaned up.
+ * The related FMQ will be cleaned up too.
+ *
+ * @param connectionId the id of the connection.
+ *
+ * @result {@code true} when the connection existed,
+ * {@code false} otherwise.
+ */
+ bool handleClose(ConnectionId connectionId);
+
+ /**
+ * Recycles a existing free buffer if it is possible.
+ *
+ * @param allocator the buffer allocator
+ * @param params the allocation parameters.
+ * @param pId the id of the recycled buffer.
+ * @param handle the native handle of the recycled buffer.
+ *
+ * @return {@code true} when a buffer is recycled, {@code false}
+ * otherwise.
+ */
+ bool getFreeBuffer(
+ const std::shared_ptr<BufferPoolAllocator> &allocator,
+ const std::vector<uint8_t> ¶ms,
+ BufferId *pId, const native_handle_t **handle);
+
+ /**
+ * Adds a newly allocated buffer to bufferpool.
+ *
+ * @param alloc the newly allocated buffer.
+ * @param allocSize the size of the newly allocated buffer.
+ * @param params the allocation parameters.
+ * @param pId the buffer id for the newly allocated buffer.
+ * @param handle the native handle for the newly allocated buffer.
+ *
+ * @return OK when an allocation is successfully allocated.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus addNewBuffer(
+ const std::shared_ptr<BufferPoolAllocation> &alloc,
+ const size_t allocSize,
+ const std::vector<uint8_t> ¶ms,
+ BufferId *pId,
+ const native_handle_t **handle);
+
+ /**
+ * Processes pending buffer status messages and performs periodic cache
+ * cleaning.
+ *
+ * @param clearCache if clearCache is true, it frees all buffers
+ * waiting to be recycled.
+ */
+ void cleanUp(bool clearCache = false);
+
+ friend class Accessor::Impl;
+ } mBufferPool;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace ufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H
diff --git a/media/bufferpool/1.0/Android.bp b/media/bufferpool/1.0/Android.bp
new file mode 100644
index 0000000..c7ea70f
--- /dev/null
+++ b/media/bufferpool/1.0/Android.bp
@@ -0,0 +1,29 @@
+cc_library {
+ name: "libstagefright_bufferpool@1.0",
+ vendor_available: true,
+ srcs: [
+ "Accessor.cpp",
+ "AccessorImpl.cpp",
+ "BufferPoolClient.cpp",
+ "BufferStatus.cpp",
+ "ClientManager.cpp",
+ "Connection.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "libhwbinder",
+ "libhidltransport",
+ "liblog",
+ "libutils",
+ "android.hardware.media.bufferpool@1.0",
+ ],
+ export_shared_lib_headers: [
+ "libfmq",
+ "android.hardware.media.bufferpool@1.0",
+ ],
+}
diff --git a/media/bufferpool/1.0/BufferPoolClient.cpp b/media/bufferpool/1.0/BufferPoolClient.cpp
new file mode 100644
index 0000000..41520ca
--- /dev/null
+++ b/media/bufferpool/1.0/BufferPoolClient.cpp
@@ -0,0 +1,708 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BufferPoolClient"
+//#define LOG_NDEBUG 0
+
+#include <thread>
+#include <utils/Log.h>
+#include "BufferPoolClient.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+static constexpr int64_t kReceiveTimeoutUs = 1000000; // 100ms
+static constexpr int kPostMaxRetry = 3;
+static constexpr int kCacheTtlUs = 1000000; // TODO: tune
+
+class BufferPoolClient::Impl
+ : public std::enable_shared_from_this<BufferPoolClient::Impl> {
+public:
+ explicit Impl(const sp<Accessor> &accessor);
+
+ explicit Impl(const sp<IAccessor> &accessor);
+
+ bool isValid() {
+ return mValid;
+ }
+
+ bool isLocal() {
+ return mValid && mLocal;
+ }
+
+ ConnectionId getConnectionId() {
+ return mConnectionId;
+ }
+
+ sp<IAccessor> &getAccessor() {
+ return mAccessor;
+ }
+
+ bool isActive(int64_t *lastTransactionUs, bool clearCache);
+
+ ResultStatus allocate(const std::vector<uint8_t> ¶ms,
+ native_handle_t **handle,
+ std::shared_ptr<BufferPoolData> *buffer);
+
+ ResultStatus receive(
+ TransactionId transactionId, BufferId bufferId,
+ int64_t timestampUs,
+ native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer);
+
+ void postBufferRelease(BufferId bufferId);
+
+ bool postSend(
+ BufferId bufferId, ConnectionId receiver,
+ TransactionId *transactionId, int64_t *timestampUs);
+private:
+
+ bool postReceive(
+ BufferId bufferId, TransactionId transactionId,
+ int64_t timestampUs);
+
+ bool postReceiveResult(
+ BufferId bufferId, TransactionId transactionId, bool result, bool *needsSync);
+
+ void trySyncFromRemote();
+
+ bool syncReleased();
+
+ void evictCaches(bool clearCache = false);
+
+ ResultStatus allocateBufferHandle(
+ const std::vector<uint8_t>& params, BufferId *bufferId,
+ native_handle_t **handle);
+
+ ResultStatus fetchBufferHandle(
+ TransactionId transactionId, BufferId bufferId,
+ native_handle_t **handle);
+
+ struct BlockPoolDataDtor;
+ struct ClientBuffer;
+
+ bool mLocal;
+ bool mValid;
+ sp<IAccessor> mAccessor;
+ sp<Connection> mLocalConnection;
+ sp<IConnection> mRemoteConnection;
+ uint32_t mSeqId;
+ ConnectionId mConnectionId;
+ int64_t mLastEvictCacheUs;
+
+ // CachedBuffers
+ struct BufferCache {
+ std::mutex mLock;
+ bool mCreating;
+ std::condition_variable mCreateCv;
+ std::map<BufferId, std::unique_ptr<ClientBuffer>> mBuffers;
+ int mActive;
+ int64_t mLastChangeUs;
+
+ BufferCache() : mCreating(false), mActive(0), mLastChangeUs(getTimestampNow()) {}
+
+ void incActive_l() {
+ ++mActive;
+ mLastChangeUs = getTimestampNow();
+ }
+
+ void decActive_l() {
+ --mActive;
+ mLastChangeUs = getTimestampNow();
+ }
+ } mCache;
+
+ // FMQ - release notifier
+ struct {
+ std::mutex mLock;
+ // TODO: use only one list?(using one list may dealy sending messages?)
+ std::list<BufferId> mReleasingIds;
+ std::list<BufferId> mReleasedIds;
+ std::unique_ptr<BufferStatusChannel> mStatusChannel;
+ } mReleasing;
+
+ // This lock is held during synchronization from remote side.
+ // In order to minimize remote calls and locking durtaion, this lock is held
+ // by best effort approach using try_lock().
+ std::mutex mRemoteSyncLock;
+};
+
+struct BufferPoolClient::Impl::BlockPoolDataDtor {
+ BlockPoolDataDtor(const std::shared_ptr<BufferPoolClient::Impl> &impl)
+ : mImpl(impl) {}
+
+ void operator()(BufferPoolData *buffer) {
+ BufferId id = buffer->mId;
+ delete buffer;
+
+ auto impl = mImpl.lock();
+ if (impl && impl->isValid()) {
+ impl->postBufferRelease(id);
+ }
+ }
+ const std::weak_ptr<BufferPoolClient::Impl> mImpl;
+};
+
+struct BufferPoolClient::Impl::ClientBuffer {
+private:
+ bool mInvalidated; // TODO: implement
+ int64_t mExpireUs;
+ bool mHasCache;
+ ConnectionId mConnectionId;
+ BufferId mId;
+ native_handle_t *mHandle;
+ std::weak_ptr<BufferPoolData> mCache;
+
+ void updateExpire() {
+ mExpireUs = getTimestampNow() + kCacheTtlUs;
+ }
+
+public:
+ ClientBuffer(
+ ConnectionId connectionId, BufferId id, native_handle_t *handle)
+ : mInvalidated(false), mHasCache(false),
+ mConnectionId(connectionId), mId(id), mHandle(handle) {
+ (void)mInvalidated;
+ mExpireUs = getTimestampNow() + kCacheTtlUs;
+ }
+
+ ~ClientBuffer() {
+ if (mHandle) {
+ native_handle_close(mHandle);
+ native_handle_delete(mHandle);
+ }
+ }
+
+ bool expire() const {
+ int64_t now = getTimestampNow();
+ return now >= mExpireUs;
+ }
+
+ bool hasCache() const {
+ return mHasCache;
+ }
+
+ std::shared_ptr<BufferPoolData> fetchCache(native_handle_t **pHandle) {
+ if (mHasCache) {
+ std::shared_ptr<BufferPoolData> cache = mCache.lock();
+ if (cache) {
+ *pHandle = mHandle;
+ }
+ return cache;
+ }
+ return nullptr;
+ }
+
+ std::shared_ptr<BufferPoolData> createCache(
+ const std::shared_ptr<BufferPoolClient::Impl> &impl,
+ native_handle_t **pHandle) {
+ if (!mHasCache) {
+ // Allocates a raw ptr in order to avoid sending #postBufferRelease
+ // from deleter, in case of native_handle_clone failure.
+ BufferPoolData *ptr = new BufferPoolData(mConnectionId, mId);
+ if (ptr) {
+ std::shared_ptr<BufferPoolData> cache(ptr, BlockPoolDataDtor(impl));
+ if (cache) {
+ mCache = cache;
+ mHasCache = true;
+ *pHandle = mHandle;
+ return cache;
+ }
+ }
+ if (ptr) {
+ delete ptr;
+ }
+ }
+ return nullptr;
+ }
+
+ bool onCacheRelease() {
+ if (mHasCache) {
+ // TODO: verify mCache is not valid;
+ updateExpire();
+ mHasCache = false;
+ return true;
+ }
+ return false;
+ }
+};
+
+BufferPoolClient::Impl::Impl(const sp<Accessor> &accessor)
+ : mLocal(true), mValid(false), mAccessor(accessor), mSeqId(0),
+ mLastEvictCacheUs(getTimestampNow()) {
+ const QueueDescriptor *fmqDesc;
+ ResultStatus status = accessor->connect(
+ &mLocalConnection, &mConnectionId, &fmqDesc, true);
+ if (status == ResultStatus::OK) {
+ mReleasing.mStatusChannel =
+ std::make_unique<BufferStatusChannel>(*fmqDesc);
+ mValid = mReleasing.mStatusChannel &&
+ mReleasing.mStatusChannel->isValid();
+ }
+}
+
+BufferPoolClient::Impl::Impl(const sp<IAccessor> &accessor)
+ : mLocal(false), mValid(false), mAccessor(accessor), mSeqId(0),
+ mLastEvictCacheUs(getTimestampNow()) {
+ bool valid = false;
+ sp<IConnection>& outConnection = mRemoteConnection;
+ ConnectionId& id = mConnectionId;
+ std::unique_ptr<BufferStatusChannel>& outChannel =
+ mReleasing.mStatusChannel;
+ Return<void> transResult = accessor->connect(
+ [&valid, &outConnection, &id, &outChannel]
+ (ResultStatus status, sp<IConnection> connection,
+ ConnectionId connectionId, const QueueDescriptor& desc) {
+ if (status == ResultStatus::OK) {
+ outConnection = connection;
+ id = connectionId;
+ outChannel = std::make_unique<BufferStatusChannel>(desc);
+ if (outChannel && outChannel->isValid()) {
+ valid = true;
+ }
+ }
+ });
+ mValid = transResult.isOk() && valid;
+}
+
+bool BufferPoolClient::Impl::isActive(int64_t *lastTransactionUs, bool clearCache) {
+ bool active = false;
+ {
+ std::lock_guard<std::mutex> lock(mCache.mLock);
+ syncReleased();
+ evictCaches(clearCache);
+ *lastTransactionUs = mCache.mLastChangeUs;
+ active = mCache.mActive > 0;
+ }
+ if (mValid && mLocal && mLocalConnection) {
+ mLocalConnection->cleanUp(clearCache);
+ return true;
+ }
+ return active;
+}
+
+ResultStatus BufferPoolClient::Impl::allocate(
+ const std::vector<uint8_t> ¶ms,
+ native_handle_t **pHandle,
+ std::shared_ptr<BufferPoolData> *buffer) {
+ if (!mLocal || !mLocalConnection || !mValid) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ BufferId bufferId;
+ native_handle_t *handle = nullptr;
+ buffer->reset();
+ ResultStatus status = allocateBufferHandle(params, &bufferId, &handle);
+ if (status == ResultStatus::OK) {
+ if (handle) {
+ std::unique_lock<std::mutex> lock(mCache.mLock);
+ syncReleased();
+ evictCaches();
+ auto cacheIt = mCache.mBuffers.find(bufferId);
+ if (cacheIt != mCache.mBuffers.end()) {
+ // TODO: verify it is recycled. (not having active ref)
+ mCache.mBuffers.erase(cacheIt);
+ }
+ auto clientBuffer = std::make_unique<ClientBuffer>(
+ mConnectionId, bufferId, handle);
+ if (clientBuffer) {
+ auto result = mCache.mBuffers.insert(std::make_pair(
+ bufferId, std::move(clientBuffer)));
+ if (result.second) {
+ *buffer = result.first->second->createCache(
+ shared_from_this(), pHandle);
+ if (*buffer) {
+ mCache.incActive_l();
+ }
+ }
+ }
+ }
+ if (!*buffer) {
+ ALOGV("client cache creation failure %d: %lld",
+ handle != nullptr, (long long)mConnectionId);
+ status = ResultStatus::NO_MEMORY;
+ postBufferRelease(bufferId);
+ }
+ }
+ return status;
+}
+
+ResultStatus BufferPoolClient::Impl::receive(
+ TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
+ native_handle_t **pHandle,
+ std::shared_ptr<BufferPoolData> *buffer) {
+ if (!mValid) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ if (timestampUs != 0) {
+ timestampUs += kReceiveTimeoutUs;
+ }
+ if (!postReceive(bufferId, transactionId, timestampUs)) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ ResultStatus status = ResultStatus::CRITICAL_ERROR;
+ buffer->reset();
+ while(1) {
+ std::unique_lock<std::mutex> lock(mCache.mLock);
+ syncReleased();
+ evictCaches();
+ auto cacheIt = mCache.mBuffers.find(bufferId);
+ if (cacheIt != mCache.mBuffers.end()) {
+ if (cacheIt->second->hasCache()) {
+ *buffer = cacheIt->second->fetchCache(pHandle);
+ if (!*buffer) {
+ // check transfer time_out
+ lock.unlock();
+ std::this_thread::yield();
+ continue;
+ }
+ ALOGV("client receive from reference %lld", (long long)mConnectionId);
+ break;
+ } else {
+ *buffer = cacheIt->second->createCache(shared_from_this(), pHandle);
+ if (*buffer) {
+ mCache.incActive_l();
+ }
+ ALOGV("client receive from cache %lld", (long long)mConnectionId);
+ break;
+ }
+ } else {
+ if (!mCache.mCreating) {
+ mCache.mCreating = true;
+ lock.unlock();
+ native_handle_t* handle = nullptr;
+ status = fetchBufferHandle(transactionId, bufferId, &handle);
+ lock.lock();
+ if (status == ResultStatus::OK) {
+ if (handle) {
+ auto clientBuffer = std::make_unique<ClientBuffer>(
+ mConnectionId, bufferId, handle);
+ if (clientBuffer) {
+ auto result = mCache.mBuffers.insert(
+ std::make_pair(bufferId, std::move(
+ clientBuffer)));
+ if (result.second) {
+ *buffer = result.first->second->createCache(
+ shared_from_this(), pHandle);
+ if (*buffer) {
+ mCache.incActive_l();
+ }
+ }
+ }
+ }
+ if (!*buffer) {
+ status = ResultStatus::NO_MEMORY;
+ }
+ }
+ mCache.mCreating = false;
+ lock.unlock();
+ mCache.mCreateCv.notify_all();
+ break;
+ }
+ mCache.mCreateCv.wait(lock);
+ }
+ }
+ bool needsSync = false;
+ bool posted = postReceiveResult(bufferId, transactionId,
+ *buffer ? true : false, &needsSync);
+ ALOGV("client receive %lld - %u : %s (%d)", (long long)mConnectionId, bufferId,
+ *buffer ? "ok" : "fail", posted);
+ if (mValid && mLocal && mLocalConnection) {
+ mLocalConnection->cleanUp(false);
+ }
+ if (needsSync && mRemoteConnection) {
+ trySyncFromRemote();
+ }
+ if (*buffer) {
+ if (!posted) {
+ buffer->reset();
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ return ResultStatus::OK;
+ }
+ return status;
+}
+
+
+void BufferPoolClient::Impl::postBufferRelease(BufferId bufferId) {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ mReleasing.mReleasingIds.push_back(bufferId);
+ mReleasing.mStatusChannel->postBufferRelease(
+ mConnectionId, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
+}
+
+// TODO: revise ad-hoc posting data structure
+bool BufferPoolClient::Impl::postSend(
+ BufferId bufferId, ConnectionId receiver,
+ TransactionId *transactionId, int64_t *timestampUs) {
+ bool ret = false;
+ bool needsSync = false;
+ {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ *timestampUs = getTimestampNow();
+ *transactionId = (mConnectionId << 32) | mSeqId++;
+ // TODO: retry, add timeout, target?
+ ret = mReleasing.mStatusChannel->postBufferStatusMessage(
+ *transactionId, bufferId, BufferStatus::TRANSFER_TO, mConnectionId,
+ receiver, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
+ needsSync = !mLocal && mReleasing.mStatusChannel->needsSync();
+ }
+ if (mValid && mLocal && mLocalConnection) {
+ mLocalConnection->cleanUp(false);
+ }
+ if (needsSync && mRemoteConnection) {
+ trySyncFromRemote();
+ }
+ return ret;
+}
+
+bool BufferPoolClient::Impl::postReceive(
+ BufferId bufferId, TransactionId transactionId, int64_t timestampUs) {
+ for (int i = 0; i < kPostMaxRetry; ++i) {
+ std::unique_lock<std::mutex> lock(mReleasing.mLock);
+ int64_t now = getTimestampNow();
+ if (timestampUs == 0 || now < timestampUs) {
+ bool result = mReleasing.mStatusChannel->postBufferStatusMessage(
+ transactionId, bufferId, BufferStatus::TRANSFER_FROM,
+ mConnectionId, -1, mReleasing.mReleasingIds,
+ mReleasing.mReleasedIds);
+ if (result) {
+ return true;
+ }
+ lock.unlock();
+ std::this_thread::yield();
+ } else {
+ mReleasing.mStatusChannel->postBufferStatusMessage(
+ transactionId, bufferId, BufferStatus::TRANSFER_TIMEOUT,
+ mConnectionId, -1, mReleasing.mReleasingIds,
+ mReleasing.mReleasedIds);
+ return false;
+ }
+ }
+ return false;
+}
+
+bool BufferPoolClient::Impl::postReceiveResult(
+ BufferId bufferId, TransactionId transactionId, bool result, bool *needsSync) {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ // TODO: retry, add timeout
+ bool ret = mReleasing.mStatusChannel->postBufferStatusMessage(
+ transactionId, bufferId,
+ result ? BufferStatus::TRANSFER_OK : BufferStatus::TRANSFER_ERROR,
+ mConnectionId, -1, mReleasing.mReleasingIds,
+ mReleasing.mReleasedIds);
+ *needsSync = !mLocal && mReleasing.mStatusChannel->needsSync();
+ return ret;
+}
+
+void BufferPoolClient::Impl::trySyncFromRemote() {
+ if (mRemoteSyncLock.try_lock()) {
+ bool needsSync = false;
+ {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ needsSync = mReleasing.mStatusChannel->needsSync();
+ }
+ if (needsSync) {
+ TransactionId transactionId = (mConnectionId << 32);
+ BufferId bufferId = Connection::SYNC_BUFFERID;
+ Return<void> transResult = mRemoteConnection->fetch(
+ transactionId, bufferId,
+ []
+ (ResultStatus outStatus, Buffer outBuffer) {
+ (void) outStatus;
+ (void) outBuffer;
+ });
+ }
+ mRemoteSyncLock.unlock();
+ }
+}
+
+// should have mCache.mLock
+bool BufferPoolClient::Impl::syncReleased() {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ if (mReleasing.mReleasingIds.size() > 0) {
+ mReleasing.mStatusChannel->postBufferRelease(
+ mConnectionId, mReleasing.mReleasingIds,
+ mReleasing.mReleasedIds);
+ }
+ if (mReleasing.mReleasedIds.size() > 0) {
+ for (BufferId& id: mReleasing.mReleasedIds) {
+ ALOGV("client release buffer %lld - %u", (long long)mConnectionId, id);
+ auto found = mCache.mBuffers.find(id);
+ if (found != mCache.mBuffers.end()) {
+ if (found->second->onCacheRelease()) {
+ mCache.decActive_l();
+ } else {
+ // should not happen!
+ ALOGW("client %lld cache release status inconsitent!",
+ (long long)mConnectionId);
+ }
+ } else {
+ // should not happen!
+ ALOGW("client %lld cache status inconsitent!", (long long)mConnectionId);
+ }
+ }
+ mReleasing.mReleasedIds.clear();
+ return true;
+ }
+ return false;
+}
+
+// should have mCache.mLock
+void BufferPoolClient::Impl::evictCaches(bool clearCache) {
+ int64_t now = getTimestampNow();
+ if (now >= mLastEvictCacheUs + kCacheTtlUs || clearCache) {
+ size_t evicted = 0;
+ for (auto it = mCache.mBuffers.begin(); it != mCache.mBuffers.end();) {
+ if (!it->second->hasCache() && (it->second->expire() || clearCache)) {
+ it = mCache.mBuffers.erase(it);
+ ++evicted;
+ } else {
+ ++it;
+ }
+ }
+ ALOGV("cache count %lld : total %zu, active %d, evicted %zu",
+ (long long)mConnectionId, mCache.mBuffers.size(), mCache.mActive, evicted);
+ mLastEvictCacheUs = now;
+ }
+}
+
+ResultStatus BufferPoolClient::Impl::allocateBufferHandle(
+ const std::vector<uint8_t>& params, BufferId *bufferId,
+ native_handle_t** handle) {
+ if (mLocalConnection) {
+ const native_handle_t* allocHandle = nullptr;
+ ResultStatus status = mLocalConnection->allocate(
+ params, bufferId, &allocHandle);
+ if (status == ResultStatus::OK) {
+ *handle = native_handle_clone(allocHandle);
+ }
+ ALOGV("client allocate result %lld %d : %u clone %p",
+ (long long)mConnectionId, status == ResultStatus::OK,
+ *handle ? *bufferId : 0 , *handle);
+ return status;
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::Impl::fetchBufferHandle(
+ TransactionId transactionId, BufferId bufferId,
+ native_handle_t **handle) {
+ sp<IConnection> connection;
+ if (mLocal) {
+ connection = mLocalConnection;
+ } else {
+ connection = mRemoteConnection;
+ }
+ ResultStatus status;
+ Return<void> transResult = connection->fetch(
+ transactionId, bufferId,
+ [&status, &handle]
+ (ResultStatus outStatus, Buffer outBuffer) {
+ status = outStatus;
+ if (status == ResultStatus::OK) {
+ *handle = native_handle_clone(
+ outBuffer.buffer.getNativeHandle());
+ }
+ });
+ return transResult.isOk() ? status : ResultStatus::CRITICAL_ERROR;
+}
+
+
+BufferPoolClient::BufferPoolClient(const sp<Accessor> &accessor) {
+ mImpl = std::make_shared<Impl>(accessor);
+}
+
+BufferPoolClient::BufferPoolClient(const sp<IAccessor> &accessor) {
+ mImpl = std::make_shared<Impl>(accessor);
+}
+
+BufferPoolClient::~BufferPoolClient() {
+ // TODO: how to handle orphaned buffers?
+}
+
+bool BufferPoolClient::isValid() {
+ return mImpl && mImpl->isValid();
+}
+
+bool BufferPoolClient::isLocal() {
+ return mImpl && mImpl->isLocal();
+}
+
+bool BufferPoolClient::isActive(int64_t *lastTransactionUs, bool clearCache) {
+ if (!isValid()) {
+ *lastTransactionUs = 0;
+ return false;
+ }
+ return mImpl->isActive(lastTransactionUs, clearCache);
+}
+
+ConnectionId BufferPoolClient::getConnectionId() {
+ if (isValid()) {
+ return mImpl->getConnectionId();
+ }
+ return -1;
+}
+
+ResultStatus BufferPoolClient::getAccessor(sp<IAccessor> *accessor) {
+ if (isValid()) {
+ *accessor = mImpl->getAccessor();
+ return ResultStatus::OK;
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::allocate(
+ const std::vector<uint8_t> ¶ms,
+ native_handle_t **handle,
+ std::shared_ptr<BufferPoolData> *buffer) {
+ if (isValid()) {
+ return mImpl->allocate(params, handle, buffer);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::receive(
+ TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
+ native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+ if (isValid()) {
+ return mImpl->receive(transactionId, bufferId, timestampUs, handle, buffer);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::postSend(
+ ConnectionId receiverId,
+ const std::shared_ptr<BufferPoolData> &buffer,
+ TransactionId *transactionId,
+ int64_t *timestampUs) {
+ if (isValid()) {
+ bool result = mImpl->postSend(
+ buffer->mId, receiverId, transactionId, timestampUs);
+ return result ? ResultStatus::OK : ResultStatus::CRITICAL_ERROR;
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/bufferpool/1.0/BufferPoolClient.h b/media/bufferpool/1.0/BufferPoolClient.h
new file mode 100644
index 0000000..577efed
--- /dev/null
+++ b/media/bufferpool/1.0/BufferPoolClient.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H
+
+#include <memory>
+#include <android/hardware/media/bufferpool/1.0/IAccessor.h>
+#include <android/hardware/media/bufferpool/1.0/IConnection.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <cutils/native_handle.h>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::media::bufferpool::V1_0::IAccessor;
+using ::android::hardware::media::bufferpool::V1_0::IConnection;
+using ::android::hardware::media::bufferpool::V1_0::ResultStatus;
+using ::android::sp;
+
+/**
+ * A buffer pool client for a buffer pool. For a specific buffer pool, at most
+ * one buffer pool client exists per process. This class will not be exposed
+ * outside. A buffer pool client will be used via ClientManager.
+ */
+class BufferPoolClient {
+public:
+ /**
+ * Creates a buffer pool client from a local buffer pool
+ * (via ClientManager#create).
+ */
+ explicit BufferPoolClient(const sp<Accessor> &accessor);
+
+ /**
+ * Creates a buffer pool client from a remote buffer pool
+ * (via ClientManager#registerSender).
+ * Note: A buffer pool client created with remote buffer pool cannot
+ * allocate a buffer.
+ */
+ explicit BufferPoolClient(const sp<IAccessor> &accessor);
+
+ /** Destructs a buffer pool client. */
+ ~BufferPoolClient();
+
+private:
+ bool isValid();
+
+ bool isLocal();
+
+ bool isActive(int64_t *lastTransactionUs, bool clearCache);
+
+ ConnectionId getConnectionId();
+
+ ResultStatus getAccessor(sp<IAccessor> *accessor);
+
+ ResultStatus allocate(const std::vector<uint8_t> ¶ms,
+ native_handle_t **handle,
+ std::shared_ptr<BufferPoolData> *buffer);
+
+ ResultStatus receive(TransactionId transactionId,
+ BufferId bufferId,
+ int64_t timestampUs,
+ native_handle_t **handle,
+ std::shared_ptr<BufferPoolData> *buffer);
+
+ ResultStatus postSend(ConnectionId receiver,
+ const std::shared_ptr<BufferPoolData> &buffer,
+ TransactionId *transactionId,
+ int64_t *timestampUs);
+
+ class Impl;
+ std::shared_ptr<Impl> mImpl;
+
+ friend struct ClientManager;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H
diff --git a/media/bufferpool/1.0/BufferStatus.cpp b/media/bufferpool/1.0/BufferStatus.cpp
new file mode 100644
index 0000000..169abce
--- /dev/null
+++ b/media/bufferpool/1.0/BufferStatus.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BufferPoolStatus"
+//#define LOG_NDEBUG 0
+
+#include <time.h>
+#include "BufferStatus.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+int64_t getTimestampNow() {
+ int64_t stamp;
+ struct timespec ts;
+ // TODO: CLOCK_MONOTONIC_COARSE?
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ stamp = ts.tv_nsec / 1000;
+ stamp += (ts.tv_sec * 1000000LL);
+ return stamp;
+}
+
+static constexpr int kNumElementsInQueue = 1024*16;
+static constexpr int kMinElementsToSyncInQueue = 128;
+
+ResultStatus BufferStatusObserver::open(
+ ConnectionId id, const QueueDescriptor** fmqDescPtr) {
+ if (mBufferStatusQueues.find(id) != mBufferStatusQueues.end()) {
+ // TODO: id collision log?
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ std::unique_ptr<BufferStatusQueue> queue =
+ std::make_unique<BufferStatusQueue>(kNumElementsInQueue);
+ if (!queue || queue->isValid() == false) {
+ *fmqDescPtr = nullptr;
+ return ResultStatus::NO_MEMORY;
+ } else {
+ *fmqDescPtr = queue->getDesc();
+ }
+ auto result = mBufferStatusQueues.insert(
+ std::make_pair(id, std::move(queue)));
+ if (!result.second) {
+ *fmqDescPtr = nullptr;
+ return ResultStatus::NO_MEMORY;
+ }
+ return ResultStatus::OK;
+}
+
+ResultStatus BufferStatusObserver::close(ConnectionId id) {
+ if (mBufferStatusQueues.find(id) == mBufferStatusQueues.end()) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ mBufferStatusQueues.erase(id);
+ return ResultStatus::OK;
+}
+
+void BufferStatusObserver::getBufferStatusChanges(std::vector<BufferStatusMessage> &messages) {
+ for (auto it = mBufferStatusQueues.begin(); it != mBufferStatusQueues.end(); ++it) {
+ BufferStatusMessage message;
+ size_t avail = it->second->availableToRead();
+ while (avail > 0) {
+ if (!it->second->read(&message, 1)) {
+ // Since avaliable # of reads are already confirmed,
+ // this should not happen.
+ // TODO: error handling (spurious client?)
+ ALOGW("FMQ message cannot be read from %lld", (long long)it->first);
+ return;
+ }
+ message.connectionId = it->first;
+ messages.push_back(message);
+ --avail;
+ }
+ }
+}
+
+BufferStatusChannel::BufferStatusChannel(
+ const QueueDescriptor &fmqDesc) {
+ std::unique_ptr<BufferStatusQueue> queue =
+ std::make_unique<BufferStatusQueue>(fmqDesc);
+ if (!queue || queue->isValid() == false) {
+ mValid = false;
+ return;
+ }
+ mValid = true;
+ mBufferStatusQueue = std::move(queue);
+}
+
+bool BufferStatusChannel::isValid() {
+ return mValid;
+}
+
+bool BufferStatusChannel::needsSync() {
+ if (mValid) {
+ size_t avail = mBufferStatusQueue->availableToWrite();
+ return avail + kMinElementsToSyncInQueue < kNumElementsInQueue;
+ }
+ return false;
+}
+
+void BufferStatusChannel::postBufferRelease(
+ ConnectionId connectionId,
+ std::list<BufferId> &pending, std::list<BufferId> &posted) {
+ if (mValid && pending.size() > 0) {
+ size_t avail = mBufferStatusQueue->availableToWrite();
+ avail = std::min(avail, pending.size());
+ BufferStatusMessage message;
+ for (size_t i = 0 ; i < avail; ++i) {
+ BufferId id = pending.front();
+ message.newStatus = BufferStatus::NOT_USED;
+ message.bufferId = id;
+ message.connectionId = connectionId;
+ if (!mBufferStatusQueue->write(&message, 1)) {
+ // Since avaliable # of writes are already confirmed,
+ // this should not happen.
+ // TODO: error handing?
+ ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId);
+ return;
+ }
+ pending.pop_front();
+ posted.push_back(id);
+ }
+ }
+}
+
+bool BufferStatusChannel::postBufferStatusMessage(
+ TransactionId transactionId, BufferId bufferId,
+ BufferStatus status, ConnectionId connectionId, ConnectionId targetId,
+ std::list<BufferId> &pending, std::list<BufferId> &posted) {
+ if (mValid) {
+ size_t avail = mBufferStatusQueue->availableToWrite();
+ size_t numPending = pending.size();
+ if (avail >= numPending + 1) {
+ BufferStatusMessage release, message;
+ for (size_t i = 0; i < numPending; ++i) {
+ BufferId id = pending.front();
+ release.newStatus = BufferStatus::NOT_USED;
+ release.bufferId = id;
+ release.connectionId = connectionId;
+ if (!mBufferStatusQueue->write(&release, 1)) {
+ // Since avaliable # of writes are already confirmed,
+ // this should not happen.
+ // TODO: error handling?
+ ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId);
+ return false;
+ }
+ pending.pop_front();
+ posted.push_back(id);
+ }
+ message.transactionId = transactionId;
+ message.bufferId = bufferId;
+ message.newStatus = status;
+ message.connectionId = connectionId;
+ message.targetConnectionId = targetId;
+ // TODO : timesatamp
+ message.timestampUs = 0;
+ if (!mBufferStatusQueue->write(&message, 1)) {
+ // Since avaliable # of writes are already confirmed,
+ // this should not happen.
+ ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId);
+ return false;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
diff --git a/media/bufferpool/1.0/BufferStatus.h b/media/bufferpool/1.0/BufferStatus.h
new file mode 100644
index 0000000..a18a921
--- /dev/null
+++ b/media/bufferpool/1.0/BufferStatus.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H
+
+#include <android/hardware/media/bufferpool/1.0/types.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <memory>
+#include <mutex>
+#include <vector>
+#include <list>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+/** Returns monotonic timestamp in Us since fixed point in time. */
+int64_t getTimestampNow();
+
+/**
+ * A collection of FMQ for a buffer pool. buffer ownership/status change
+ * messages are sent via the FMQs from the clients.
+ */
+class BufferStatusObserver {
+private:
+ std::map<ConnectionId, std::unique_ptr<BufferStatusQueue>>
+ mBufferStatusQueues;
+
+public:
+ /** Creates an FMQ for the specified connection(client).
+ *
+ * @param connectionId connection Id of the specified client.
+ * @param fmqDescPtr double ptr of created FMQ's descriptor.
+ *
+ * @return OK if FMQ is created successfully.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus open(ConnectionId id, const QueueDescriptor** fmqDescPtr);
+
+ /** Closes an FMQ for the specified connection(client).
+ *
+ * @param connectionId connection Id of the specified client.
+ *
+ * @return OK if the specified connection is closed successfully.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus close(ConnectionId id);
+
+ /** Retrieves all pending FMQ buffer status messages from clients.
+ *
+ * @param messages retrieved pending messages.
+ */
+ void getBufferStatusChanges(std::vector<BufferStatusMessage> &messages);
+};
+
+/**
+ * An FMQ for a buffer pool client. Buffer ownership/status change messages
+ * are sent via the fmq to the buffer pool.
+ */
+class BufferStatusChannel {
+private:
+ bool mValid;
+ std::unique_ptr<BufferStatusQueue> mBufferStatusQueue;
+
+public:
+ /**
+ * Connects to an FMQ from a descriptor of the created FMQ.
+ *
+ * @param fmqDesc Descriptor of the created FMQ.
+ */
+ BufferStatusChannel(const QueueDescriptor &fmqDesc);
+
+ /** Returns whether the FMQ is connected successfully. */
+ bool isValid();
+
+ /** Returns whether the FMQ needs to be synced from the buffer pool */
+ bool needsSync();
+
+ /**
+ * Posts a buffer release message to the buffer pool.
+ *
+ * @param connectionId connection Id of the client.
+ * @param pending currently pending buffer release messages.
+ * @param posted posted buffer release messages.
+ */
+ void postBufferRelease(
+ ConnectionId connectionId,
+ std::list<BufferId> &pending, std::list<BufferId> &posted);
+
+ /**
+ * Posts a buffer status message regarding the specified buffer
+ * transfer transaction.
+ *
+ * @param transactionId Id of the specified transaction.
+ * @param bufferId buffer Id of the specified transaction.
+ * @param status new status of the buffer.
+ * @param connectionId connection Id of the client.
+ * @param targetId connection Id of the receiver(only when the sender
+ * posts a status message).
+ * @param pending currently pending buffer release messages.
+ * @param posted posted buffer release messages.
+ *
+ * @return {@code true} when the specified message is posted,
+ * {@code false} otherwise.
+ */
+ bool postBufferStatusMessage(
+ TransactionId transactionId,
+ BufferId bufferId,
+ BufferStatus status,
+ ConnectionId connectionId,
+ ConnectionId targetId,
+ std::list<BufferId> &pending, std::list<BufferId> &posted);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H
diff --git a/media/bufferpool/1.0/ClientManager.cpp b/media/bufferpool/1.0/ClientManager.cpp
new file mode 100644
index 0000000..ecea0a4
--- /dev/null
+++ b/media/bufferpool/1.0/ClientManager.cpp
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "BufferPoolManager"
+//#define LOG_NDEBUG 0
+
+#include <bufferpool/ClientManager.h>
+#include <hidl/HidlTransportSupport.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <utils/Log.h>
+#include "BufferPoolClient.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+static constexpr int64_t kRegisterTimeoutUs = 500000; // 0.5 sec
+static constexpr int64_t kCleanUpDurationUs = 1000000; // TODO: 1 sec tune
+static constexpr int64_t kClientTimeoutUs = 5000000; // TODO: 5 secs tune
+
+/**
+ * The holder of the cookie of remote IClientManager.
+ * The cookie is process locally unique for each IClientManager.
+ * (The cookie is used to notify death of clients to bufferpool process.)
+ */
+class ClientManagerCookieHolder {
+public:
+ /**
+ * Creates a cookie holder for remote IClientManager(s).
+ */
+ ClientManagerCookieHolder();
+
+ /**
+ * Gets a cookie for a remote IClientManager.
+ *
+ * @param manager the specified remote IClientManager.
+ * @param added true when the specified remote IClientManager is added
+ * newly, false otherwise.
+ *
+ * @return the process locally unique cookie for the specified IClientManager.
+ */
+ uint64_t getCookie(const sp<IClientManager> &manager, bool *added);
+
+private:
+ uint64_t mSeqId;
+ std::mutex mLock;
+ std::list<std::pair<const wp<IClientManager>, uint64_t>> mManagers;
+};
+
+ClientManagerCookieHolder::ClientManagerCookieHolder() : mSeqId(0){}
+
+uint64_t ClientManagerCookieHolder::getCookie(
+ const sp<IClientManager> &manager,
+ bool *added) {
+ std::lock_guard<std::mutex> lock(mLock);
+ for (auto it = mManagers.begin(); it != mManagers.end();) {
+ const sp<IClientManager> key = it->first.promote();
+ if (key) {
+ if (interfacesEqual(key, manager)) {
+ *added = false;
+ return it->second;
+ }
+ ++it;
+ } else {
+ it = mManagers.erase(it);
+ }
+ }
+ uint64_t id = mSeqId++;
+ *added = true;
+ mManagers.push_back(std::make_pair(manager, id));
+ return id;
+}
+
+class ClientManager::Impl {
+public:
+ Impl();
+
+ // BnRegisterSender
+ ResultStatus registerSender(const sp<IAccessor> &accessor,
+ ConnectionId *pConnectionId);
+
+ // BpRegisterSender
+ ResultStatus registerSender(const sp<IClientManager> &receiver,
+ ConnectionId senderId,
+ ConnectionId *receiverId);
+
+ ResultStatus create(const std::shared_ptr<BufferPoolAllocator> &allocator,
+ ConnectionId *pConnectionId);
+
+ ResultStatus close(ConnectionId connectionId);
+
+ ResultStatus allocate(ConnectionId connectionId,
+ const std::vector<uint8_t> ¶ms,
+ native_handle_t **handle,
+ std::shared_ptr<BufferPoolData> *buffer);
+
+ ResultStatus receive(ConnectionId connectionId,
+ TransactionId transactionId,
+ BufferId bufferId,
+ int64_t timestampUs,
+ native_handle_t **handle,
+ std::shared_ptr<BufferPoolData> *buffer);
+
+ ResultStatus postSend(ConnectionId receiverId,
+ const std::shared_ptr<BufferPoolData> &buffer,
+ TransactionId *transactionId,
+ int64_t *timestampUs);
+
+ ResultStatus getAccessor(ConnectionId connectionId,
+ sp<IAccessor> *accessor);
+
+ void cleanUp(bool clearCache = false);
+
+private:
+ // In order to prevent deadlock between multiple locks,
+ // always lock ClientCache.lock before locking ActiveClients.lock.
+ struct ClientCache {
+ // This lock is held for brief duration.
+ // Blocking operation is not performed while holding the lock.
+ std::mutex mMutex;
+ std::list<std::pair<const wp<IAccessor>, const std::weak_ptr<BufferPoolClient>>>
+ mClients;
+ std::condition_variable mConnectCv;
+ bool mConnecting;
+ int64_t mLastCleanUpUs;
+
+ ClientCache() : mConnecting(false), mLastCleanUpUs(getTimestampNow()) {}
+ } mCache;
+
+ // Active clients which can be retrieved via ConnectionId
+ struct ActiveClients {
+ // This lock is held for brief duration.
+ // Blocking operation is not performed holding the lock.
+ std::mutex mMutex;
+ std::map<ConnectionId, const std::shared_ptr<BufferPoolClient>>
+ mClients;
+ } mActive;
+
+ ClientManagerCookieHolder mRemoteClientCookies;
+};
+
+ClientManager::Impl::Impl() {}
+
+ResultStatus ClientManager::Impl::registerSender(
+ const sp<IAccessor> &accessor, ConnectionId *pConnectionId) {
+ cleanUp();
+ int64_t timeoutUs = getTimestampNow() + kRegisterTimeoutUs;
+ do {
+ std::unique_lock<std::mutex> lock(mCache.mMutex);
+ for (auto it = mCache.mClients.begin(); it != mCache.mClients.end(); ++it) {
+ sp<IAccessor> sAccessor = it->first.promote();
+ if (sAccessor && interfacesEqual(sAccessor, accessor)) {
+ const std::shared_ptr<BufferPoolClient> client = it->second.lock();
+ if (client) {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ *pConnectionId = client->getConnectionId();
+ if (mActive.mClients.find(*pConnectionId) != mActive.mClients.end()) {
+ ALOGV("register existing connection %lld", (long long)*pConnectionId);
+ return ResultStatus::ALREADY_EXISTS;
+ }
+ }
+ mCache.mClients.erase(it);
+ break;
+ }
+ }
+ if (!mCache.mConnecting) {
+ mCache.mConnecting = true;
+ lock.unlock();
+ ResultStatus result = ResultStatus::OK;
+ const std::shared_ptr<BufferPoolClient> client =
+ std::make_shared<BufferPoolClient>(accessor);
+ lock.lock();
+ if (!client) {
+ result = ResultStatus::NO_MEMORY;
+ } else if (!client->isValid()) {
+ result = ResultStatus::CRITICAL_ERROR;
+ }
+ if (result == ResultStatus::OK) {
+ // TODO: handle insert fail. (malloc fail)
+ const std::weak_ptr<BufferPoolClient> wclient = client;
+ mCache.mClients.push_back(std::make_pair(accessor, wclient));
+ ConnectionId conId = client->getConnectionId();
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ mActive.mClients.insert(std::make_pair(conId, client));
+ }
+ *pConnectionId = conId;
+ ALOGV("register new connection %lld", (long long)*pConnectionId);
+ }
+ mCache.mConnecting = false;
+ lock.unlock();
+ mCache.mConnectCv.notify_all();
+ return result;
+ }
+ mCache.mConnectCv.wait_for(
+ lock, std::chrono::microseconds(kRegisterTimeoutUs));
+ } while (getTimestampNow() < timeoutUs);
+ // TODO: return timeout error
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::Impl::registerSender(
+ const sp<IClientManager> &receiver,
+ ConnectionId senderId,
+ ConnectionId *receiverId) {
+ sp<IAccessor> accessor;
+ bool local = false;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(senderId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ it->second->getAccessor(&accessor);
+ local = it->second->isLocal();
+ }
+ ResultStatus rs = ResultStatus::CRITICAL_ERROR;
+ if (accessor) {
+ Return<void> transResult = receiver->registerSender(
+ accessor,
+ [&rs, receiverId](
+ ResultStatus status,
+ int64_t connectionId) {
+ rs = status;
+ *receiverId = connectionId;
+ });
+ if (!transResult.isOk()) {
+ return ResultStatus::CRITICAL_ERROR;
+ } else if (local && rs == ResultStatus::OK) {
+ sp<ConnectionDeathRecipient> recipient = Accessor::getConnectionDeathRecipient();
+ if (recipient) {
+ ALOGV("client death recipient registered %lld", (long long)*receiverId);
+ bool added;
+ uint64_t cookie = mRemoteClientCookies.getCookie(receiver, &added);
+ recipient->addCookieToConnection(cookie, *receiverId);
+ if (added) {
+ Return<bool> transResult = receiver->linkToDeath(recipient, cookie);
+ }
+ }
+ }
+ }
+ return rs;
+}
+
+ResultStatus ClientManager::Impl::create(
+ const std::shared_ptr<BufferPoolAllocator> &allocator,
+ ConnectionId *pConnectionId) {
+ const sp<Accessor> accessor = new Accessor(allocator);
+ if (!accessor || !accessor->isValid()) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ std::shared_ptr<BufferPoolClient> client =
+ std::make_shared<BufferPoolClient>(accessor);
+ if (!client || !client->isValid()) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ // Since a new bufferpool is created, evict memories which are used by
+ // existing bufferpools and clients.
+ cleanUp(true);
+ {
+ // TODO: handle insert fail. (malloc fail)
+ std::lock_guard<std::mutex> lock(mCache.mMutex);
+ const std::weak_ptr<BufferPoolClient> wclient = client;
+ mCache.mClients.push_back(std::make_pair(accessor, wclient));
+ ConnectionId conId = client->getConnectionId();
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ mActive.mClients.insert(std::make_pair(conId, client));
+ }
+ *pConnectionId = conId;
+ ALOGV("create new connection %lld", (long long)*pConnectionId);
+ }
+ return ResultStatus::OK;
+}
+
+ResultStatus ClientManager::Impl::close(ConnectionId connectionId) {
+ std::lock_guard<std::mutex> lock1(mCache.mMutex);
+ std::lock_guard<std::mutex> lock2(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it != mActive.mClients.end()) {
+ sp<IAccessor> accessor;
+ it->second->getAccessor(&accessor);
+ mActive.mClients.erase(connectionId);
+ for (auto cit = mCache.mClients.begin(); cit != mCache.mClients.end();) {
+ // clean up dead client caches
+ sp<IAccessor> cAccessor = cit->first.promote();
+ if (!cAccessor || (accessor && interfacesEqual(cAccessor, accessor))) {
+ cit = mCache.mClients.erase(cit);
+ } else {
+ cit++;
+ }
+ }
+ return ResultStatus::OK;
+ }
+ return ResultStatus::NOT_FOUND;
+}
+
+ResultStatus ClientManager::Impl::allocate(
+ ConnectionId connectionId, const std::vector<uint8_t> ¶ms,
+ native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+ std::shared_ptr<BufferPoolClient> client;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ client = it->second;
+ }
+ return client->allocate(params, handle, buffer);
+}
+
+ResultStatus ClientManager::Impl::receive(
+ ConnectionId connectionId, TransactionId transactionId,
+ BufferId bufferId, int64_t timestampUs,
+ native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+ std::shared_ptr<BufferPoolClient> client;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ client = it->second;
+ }
+ return client->receive(transactionId, bufferId, timestampUs, handle, buffer);
+}
+
+ResultStatus ClientManager::Impl::postSend(
+ ConnectionId receiverId, const std::shared_ptr<BufferPoolData> &buffer,
+ TransactionId *transactionId, int64_t *timestampUs) {
+ ConnectionId connectionId = buffer->mConnectionId;
+ std::shared_ptr<BufferPoolClient> client;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ client = it->second;
+ }
+ return client->postSend(receiverId, buffer, transactionId, timestampUs);
+}
+
+ResultStatus ClientManager::Impl::getAccessor(
+ ConnectionId connectionId, sp<IAccessor> *accessor) {
+ std::shared_ptr<BufferPoolClient> client;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ client = it->second;
+ }
+ return client->getAccessor(accessor);
+}
+
+void ClientManager::Impl::cleanUp(bool clearCache) {
+ int64_t now = getTimestampNow();
+ int64_t lastTransactionUs;
+ std::lock_guard<std::mutex> lock1(mCache.mMutex);
+ if (clearCache || mCache.mLastCleanUpUs + kCleanUpDurationUs < now) {
+ std::lock_guard<std::mutex> lock2(mActive.mMutex);
+ int cleaned = 0;
+ for (auto it = mActive.mClients.begin(); it != mActive.mClients.end();) {
+ if (!it->second->isActive(&lastTransactionUs, clearCache)) {
+ if (lastTransactionUs + kClientTimeoutUs < now) {
+ sp<IAccessor> accessor;
+ it->second->getAccessor(&accessor);
+ it = mActive.mClients.erase(it);
+ ++cleaned;
+ continue;
+ }
+ }
+ ++it;
+ }
+ for (auto cit = mCache.mClients.begin(); cit != mCache.mClients.end();) {
+ // clean up dead client caches
+ sp<IAccessor> cAccessor = cit->first.promote();
+ if (!cAccessor) {
+ cit = mCache.mClients.erase(cit);
+ } else {
+ ++cit;
+ }
+ }
+ ALOGV("# of cleaned connections: %d", cleaned);
+ mCache.mLastCleanUpUs = now;
+ }
+}
+
+// Methods from ::android::hardware::media::bufferpool::V1_0::IClientManager follow.
+Return<void> ClientManager::registerSender(const sp<::android::hardware::media::bufferpool::V1_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) {
+ if (mImpl) {
+ ConnectionId connectionId = -1;
+ ResultStatus status = mImpl->registerSender(bufferPool, &connectionId);
+ _hidl_cb(status, connectionId);
+ } else {
+ _hidl_cb(ResultStatus::CRITICAL_ERROR, -1);
+ }
+ return Void();
+}
+
+// Methods for local use.
+sp<ClientManager> ClientManager::sInstance;
+std::mutex ClientManager::sInstanceLock;
+
+sp<ClientManager> ClientManager::getInstance() {
+ std::lock_guard<std::mutex> lock(sInstanceLock);
+ if (!sInstance) {
+ sInstance = new ClientManager();
+ }
+ return sInstance;
+}
+
+ClientManager::ClientManager() : mImpl(new Impl()) {}
+
+ClientManager::~ClientManager() {
+}
+
+ResultStatus ClientManager::create(
+ const std::shared_ptr<BufferPoolAllocator> &allocator,
+ ConnectionId *pConnectionId) {
+ if (mImpl) {
+ return mImpl->create(allocator, pConnectionId);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::registerSender(
+ const sp<IClientManager> &receiver,
+ ConnectionId senderId,
+ ConnectionId *receiverId) {
+ if (mImpl) {
+ return mImpl->registerSender(receiver, senderId, receiverId);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::close(ConnectionId connectionId) {
+ if (mImpl) {
+ return mImpl->close(connectionId);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::allocate(
+ ConnectionId connectionId, const std::vector<uint8_t> ¶ms,
+ native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+ if (mImpl) {
+ return mImpl->allocate(connectionId, params, handle, buffer);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::receive(
+ ConnectionId connectionId, TransactionId transactionId,
+ BufferId bufferId, int64_t timestampUs,
+ native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+ if (mImpl) {
+ return mImpl->receive(connectionId, transactionId, bufferId,
+ timestampUs, handle, buffer);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::postSend(
+ ConnectionId receiverId, const std::shared_ptr<BufferPoolData> &buffer,
+ TransactionId *transactionId, int64_t* timestampUs) {
+ if (mImpl && buffer) {
+ return mImpl->postSend(receiverId, buffer, transactionId, timestampUs);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+void ClientManager::cleanUp() {
+ if (mImpl) {
+ mImpl->cleanUp(true);
+ }
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/bufferpool/1.0/Connection.cpp b/media/bufferpool/1.0/Connection.cpp
new file mode 100644
index 0000000..e58f595
--- /dev/null
+++ b/media/bufferpool/1.0/Connection.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::bufferpool::V1_0::IConnection follow.
+Return<void> Connection::fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) {
+ ResultStatus status = ResultStatus::CRITICAL_ERROR;
+ if (mInitialized && mAccessor) {
+ if (bufferId != SYNC_BUFFERID) {
+ const native_handle_t *handle = nullptr;
+ status = mAccessor->fetch(
+ mConnectionId, transactionId, bufferId, &handle);
+ if (status == ResultStatus::OK) {
+ _hidl_cb(status, Buffer{bufferId, handle});
+ return Void();
+ }
+ } else {
+ mAccessor->cleanUp(false);
+ }
+ }
+ _hidl_cb(status, Buffer{0, nullptr});
+ return Void();
+}
+
+Connection::Connection() : mInitialized(false), mConnectionId(-1LL) {}
+
+Connection::~Connection() {
+ if (mInitialized && mAccessor) {
+ mAccessor->close(mConnectionId);
+ }
+}
+
+void Connection::initialize(
+ const sp<Accessor>& accessor, ConnectionId connectionId) {
+ if (!mInitialized) {
+ mAccessor = accessor;
+ mConnectionId = connectionId;
+ mInitialized = true;
+ }
+}
+
+ResultStatus Connection::allocate(
+ const std::vector<uint8_t> ¶ms, BufferId *bufferId,
+ const native_handle_t **handle) {
+ if (mInitialized && mAccessor) {
+ return mAccessor->allocate(mConnectionId, params, bufferId, handle);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+void Connection::cleanUp(bool clearCache) {
+ if (mInitialized && mAccessor) {
+ mAccessor->cleanUp(clearCache);
+ }
+}
+
+// Methods from ::android::hidl::base::V1_0::IBase follow.
+
+//IConnection* HIDL_FETCH_IConnection(const char* /* name */) {
+// return new Connection();
+//}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/bufferpool/1.0/Connection.h b/media/bufferpool/1.0/Connection.h
new file mode 100644
index 0000000..e19cb67
--- /dev/null
+++ b/media/bufferpool/1.0/Connection.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H
+
+#include <android/hardware/media/bufferpool/1.0/IConnection.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::media::bufferpool::V1_0::implementation::Accessor;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct Connection : public IConnection {
+ // Methods from ::android::hardware::media::bufferpool::V1_0::IConnection follow.
+ Return<void> fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) override;
+
+ /**
+ * Allocates a buffer using the specified parameters. Recycles a buffer if
+ * it is possible. The returned buffer can be transferred to other remote
+ * clients(Connection).
+ *
+ * @param params allocation parameters.
+ * @param bufferId Id of the allocated buffer.
+ * @param handle native handle of the allocated buffer.
+ *
+ * @return OK if a buffer is successfully allocated.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus allocate(const std::vector<uint8_t> ¶ms,
+ BufferId *bufferId, const native_handle_t **handle);
+
+ /**
+ * Processes pending buffer status messages and performs periodic cache cleaning
+ * from bufferpool.
+ *
+ * @param clearCache if clearCache is true, bufferpool frees all buffers
+ * waiting to be recycled.
+ */
+ void cleanUp(bool clearCache);
+
+ /** Destructs a connection. */
+ ~Connection();
+
+ /** Creates a connection. */
+ Connection();
+
+ /**
+ * Initializes with the specified buffer pool and the connection id.
+ * The connection id should be unique in the whole system.
+ *
+ * @param accessor the specified buffer pool.
+ * @param connectionId Id.
+ */
+ void initialize(const sp<Accessor> &accessor, ConnectionId connectionId);
+
+ enum : uint32_t {
+ SYNC_BUFFERID = UINT32_MAX,
+ };
+
+private:
+ bool mInitialized;
+ sp<Accessor> mAccessor;
+ ConnectionId mConnectionId;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H
diff --git a/media/bufferpool/1.0/include/bufferpool/BufferPoolTypes.h b/media/bufferpool/1.0/include/bufferpool/BufferPoolTypes.h
new file mode 100644
index 0000000..710f015
--- /dev/null
+++ b/media/bufferpool/1.0/include/bufferpool/BufferPoolTypes.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H
+
+#include <android/hardware/media/bufferpool/1.0/types.h>
+#include <cutils/native_handle.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+
+struct BufferPoolData {
+ // For local use, to specify a bufferpool (client connection) for buffers.
+ // Return value from connect#IAccessor(android.hardware.media.bufferpool@1.0).
+ int64_t mConnectionId;
+ // BufferId
+ uint32_t mId;
+
+ BufferPoolData() : mConnectionId(0), mId(0) {}
+
+ BufferPoolData(
+ int64_t connectionId, uint32_t id)
+ : mConnectionId(connectionId), mId(id) {}
+
+ ~BufferPoolData() {}
+};
+
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+
+typedef uint32_t BufferId;
+typedef uint64_t TransactionId;
+typedef int64_t ConnectionId;
+
+enum : ConnectionId {
+ INVALID_CONNECTIONID = 0,
+};
+
+typedef android::hardware::MessageQueue<BufferStatusMessage, kSynchronizedReadWrite> BufferStatusQueue;
+typedef BufferStatusQueue::Descriptor QueueDescriptor;
+
+/**
+ * Allocation wrapper class for buffer pool.
+ */
+struct BufferPoolAllocation {
+ const native_handle_t *mHandle;
+
+ const native_handle_t *handle() {
+ return mHandle;
+ }
+
+ BufferPoolAllocation(const native_handle_t *handle) : mHandle(handle) {}
+
+ ~BufferPoolAllocation() {};
+};
+
+/**
+ * Allocator wrapper class for buffer pool.
+ */
+class BufferPoolAllocator {
+public:
+
+ /**
+ * Allocate an allocation(buffer) for buffer pool.
+ *
+ * @param params allocation parameters
+ * @param alloc created allocation
+ * @param allocSize size of created allocation
+ *
+ * @return OK when an allocation is created successfully.
+ */
+ virtual ResultStatus allocate(
+ const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<BufferPoolAllocation> *alloc,
+ size_t *allocSize) = 0;
+
+ /**
+ * Returns whether allocation parameters of an old allocation are
+ * compatible with new allocation parameters.
+ */
+ virtual bool compatible(const std::vector<uint8_t> &newParams,
+ const std::vector<uint8_t> &oldParams) = 0;
+
+protected:
+ BufferPoolAllocator() = default;
+
+ virtual ~BufferPoolAllocator() = default;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H
diff --git a/media/bufferpool/1.0/include/bufferpool/ClientManager.h b/media/bufferpool/1.0/include/bufferpool/ClientManager.h
new file mode 100644
index 0000000..be5779f
--- /dev/null
+++ b/media/bufferpool/1.0/include/bufferpool/ClientManager.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H
+
+#include <android/hardware/media/bufferpool/1.0/IClientManager.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <memory>
+#include "BufferPoolTypes.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::media::bufferpool::V1_0::IAccessor;
+using ::android::hardware::media::bufferpool::V1_0::ResultStatus;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct ClientManager : public IClientManager {
+ // Methods from ::android::hardware::media::bufferpool::V1_0::IClientManager follow.
+ Return<void> registerSender(const sp<::android::hardware::media::bufferpool::V1_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) override;
+
+ /** Gets an instance. */
+ static sp<ClientManager> getInstance();
+
+ /**
+ * Creates a local connection with a newly created buffer pool.
+ *
+ * @param allocator for new buffer allocation.
+ * @param pConnectionId Id of the created connection. This is
+ * system-wide unique.
+ *
+ * @return OK when a buffer pool and a local connection is successfully
+ * created.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus create(const std::shared_ptr<BufferPoolAllocator> &allocator,
+ ConnectionId *pConnectionId);
+
+ /**
+ * Register a created connection as sender for remote process.
+ *
+ * @param receiver The remote receiving process.
+ * @param senderId A local connection which will send buffers to.
+ * @param receiverId Id of the created receiving connection on the receiver
+ * process.
+ *
+ * @return OK when the receiving connection is successfully created on the
+ * receiver process.
+ * NOT_FOUND when the sender connection was not found.
+ * ALREADY_EXISTS the receiving connection is already made.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus registerSender(const sp<IClientManager> &receiver,
+ ConnectionId senderId,
+ ConnectionId *receiverId);
+
+ /**
+ * Closes the specified connection.
+ *
+ * @param connectionId The id of the connection.
+ *
+ * @return OK when the connection is closed.
+ * NOT_FOUND when the specified connection was not found.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus close(ConnectionId connectionId);
+
+ /**
+ * Allocates a buffer from the specified connection.
+ *
+ * @param connectionId The id of the connection.
+ * @param params The allocation parameters.
+ * @param handle The native handle to the allocated buffer. handle
+ * should be cloned before use.
+ * @param buffer The allocated buffer.
+ *
+ * @return OK when a buffer was allocated successfully.
+ * NOT_FOUND when the specified connection was not found.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus allocate(ConnectionId connectionId,
+ const std::vector<uint8_t> ¶ms,
+ native_handle_t **handle,
+ std::shared_ptr<BufferPoolData> *buffer);
+
+ /**
+ * Receives a buffer for the transaction.
+ *
+ * @param connectionId The id of the receiving connection.
+ * @param transactionId The id for the transaction.
+ * @param bufferId The id for the buffer.
+ * @param timestampUs The timestamp of the buffer is being sent.
+ * @param handle The native handle to the allocated buffer. handle
+ * should be cloned before use.
+ * @param buffer The received buffer.
+ *
+ * @return OK when a buffer was received successfully.
+ * NOT_FOUND when the specified connection was not found.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus receive(ConnectionId connectionId,
+ TransactionId transactionId,
+ BufferId bufferId,
+ int64_t timestampUs,
+ native_handle_t **handle,
+ std::shared_ptr<BufferPoolData> *buffer);
+
+ /**
+ * Posts a buffer transfer transaction to the buffer pool. Sends a buffer
+ * to other remote clients(connection) after this call has been succeeded.
+ *
+ * @param receiverId The id of the receiving connection.
+ * @param buffer to transfer
+ * @param transactionId Id of the transfer transaction.
+ * @param timestampUs The timestamp of the buffer transaction is being
+ * posted.
+ *
+ * @return OK when a buffer transaction was posted successfully.
+ * NOT_FOUND when the sending connection was not found.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus postSend(ConnectionId receiverId,
+ const std::shared_ptr<BufferPoolData> &buffer,
+ TransactionId *transactionId,
+ int64_t *timestampUs);
+
+ /**
+ * Time out inactive lingering connections and close.
+ */
+ void cleanUp();
+
+ /** Destructs the manager of buffer pool clients. */
+ ~ClientManager();
+private:
+ static sp<ClientManager> sInstance;
+ static std::mutex sInstanceLock;
+
+ class Impl;
+ const std::unique_ptr<Impl> mImpl;
+
+ ClientManager();
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H
diff --git a/media/bufferpool/1.0/vts/Android.bp b/media/bufferpool/1.0/vts/Android.bp
new file mode 100644
index 0000000..ee5a757
--- /dev/null
+++ b/media/bufferpool/1.0/vts/Android.bp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+ name: "VtsVndkHidlBufferpoolV1_0TargetSingleTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "allocator.cpp",
+ "single.cpp",
+ ],
+ static_libs: [
+ "android.hardware.media.bufferpool@1.0",
+ "libion",
+ "libstagefright_bufferpool@1.0",
+ ],
+ shared_libs: [
+ "libfmq",
+ ],
+ compile_multilib: "both",
+}
+
+cc_test {
+ name: "VtsVndkHidlBufferpoolV1_0TargetMultiTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "allocator.cpp",
+ "multi.cpp",
+ ],
+ static_libs: [
+ "android.hardware.media.bufferpool@1.0",
+ "libion",
+ "libstagefright_bufferpool@1.0",
+ ],
+ shared_libs: [
+ "libfmq",
+ ],
+ compile_multilib: "both",
+}
diff --git a/media/bufferpool/1.0/vts/OWNERS b/media/bufferpool/1.0/vts/OWNERS
new file mode 100644
index 0000000..6733e0c
--- /dev/null
+++ b/media/bufferpool/1.0/vts/OWNERS
@@ -0,0 +1,9 @@
+# Media team
+lajos@google.com
+pawin@google.com
+taklee@google.com
+wonsik@google.com
+
+# VTS team
+yim@google.com
+zhuoyao@google.com
diff --git a/media/bufferpool/1.0/vts/allocator.cpp b/media/bufferpool/1.0/vts/allocator.cpp
new file mode 100644
index 0000000..843f7ea
--- /dev/null
+++ b/media/bufferpool/1.0/vts/allocator.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/ashmem.h>
+#include <sys/mman.h>
+#include "allocator.h"
+
+union Params {
+ struct {
+ uint32_t capacity;
+ } data;
+ uint8_t array[0];
+ Params() : data{0} {}
+ Params(uint32_t size)
+ : data{size} {}
+};
+
+
+namespace {
+
+struct HandleAshmem : public native_handle_t {
+ HandleAshmem(int ashmemFd, size_t size)
+ : native_handle_t(cHeader),
+ mFds{ ashmemFd },
+ mInts{ int (size & 0xFFFFFFFF), int((uint64_t(size) >> 32) & 0xFFFFFFFF), kMagic } {}
+
+ int ashmemFd() const { return mFds.mAshmem; }
+ size_t size() const {
+ return size_t(unsigned(mInts.mSizeLo))
+ | size_t(uint64_t(unsigned(mInts.mSizeHi)) << 32);
+ }
+
+ static bool isValid(const native_handle_t * const o);
+
+protected:
+ struct {
+ int mAshmem;
+ } mFds;
+ struct {
+ int mSizeLo;
+ int mSizeHi;
+ int mMagic;
+ } mInts;
+
+private:
+ enum {
+ kMagic = 'ahm\x00',
+ numFds = sizeof(mFds) / sizeof(int),
+ numInts = sizeof(mInts) / sizeof(int),
+ version = sizeof(native_handle_t)
+ };
+ const static native_handle_t cHeader;
+};
+
+const native_handle_t HandleAshmem::cHeader = {
+ HandleAshmem::version,
+ HandleAshmem::numFds,
+ HandleAshmem::numInts,
+ {}
+};
+
+bool HandleAshmem::isValid(const native_handle_t * const o) {
+ if (!o || memcmp(o, &cHeader, sizeof(cHeader))) {
+ return false;
+ }
+ const HandleAshmem *other = static_cast<const HandleAshmem*>(o);
+ return other->mInts.mMagic == kMagic;
+}
+
+class AllocationAshmem {
+private:
+ AllocationAshmem(int ashmemFd, size_t capacity, bool res)
+ : mHandle(ashmemFd, capacity),
+ mInit(res) {}
+
+public:
+ static AllocationAshmem *Alloc(size_t size) {
+ constexpr static const char *kAllocationTag = "bufferpool_test";
+ int ashmemFd = ashmem_create_region(kAllocationTag, size);
+ return new AllocationAshmem(ashmemFd, size, ashmemFd >= 0);
+ }
+
+ ~AllocationAshmem() {
+ if (mInit) {
+ native_handle_close(&mHandle);
+ }
+ }
+
+ const HandleAshmem *handle() {
+ return &mHandle;
+ }
+
+private:
+ HandleAshmem mHandle;
+ bool mInit;
+ // TODO: mapping and map fd
+};
+
+struct AllocationDtor {
+ AllocationDtor(const std::shared_ptr<AllocationAshmem> &alloc)
+ : mAlloc(alloc) {}
+
+ void operator()(BufferPoolAllocation *poolAlloc) { delete poolAlloc; }
+
+ const std::shared_ptr<AllocationAshmem> mAlloc;
+};
+
+}
+
+
+ResultStatus TestBufferPoolAllocator::allocate(
+ const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<BufferPoolAllocation> *alloc,
+ size_t *allocSize) {
+ Params ashmemParams;
+ memcpy(&ashmemParams, params.data(), std::min(sizeof(Params), params.size()));
+
+ std::shared_ptr<AllocationAshmem> ashmemAlloc =
+ std::shared_ptr<AllocationAshmem>(
+ AllocationAshmem::Alloc(ashmemParams.data.capacity));
+ if (ashmemAlloc) {
+ BufferPoolAllocation *ptr = new BufferPoolAllocation(ashmemAlloc->handle());
+ if (ptr) {
+ *alloc = std::shared_ptr<BufferPoolAllocation>(ptr, AllocationDtor(ashmemAlloc));
+ if (*alloc) {
+ *allocSize = ashmemParams.data.capacity;
+ return ResultStatus::OK;
+ }
+ delete ptr;
+ return ResultStatus::NO_MEMORY;
+ }
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+bool TestBufferPoolAllocator::compatible(const std::vector<uint8_t> &newParams,
+ const std::vector<uint8_t> &oldParams) {
+ size_t newSize = newParams.size();
+ size_t oldSize = oldParams.size();
+ if (newSize == oldSize) {
+ for (size_t i = 0; i < newSize; ++i) {
+ if (newParams[i] != oldParams[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+bool TestBufferPoolAllocator::Fill(const native_handle_t *handle, const unsigned char val) {
+ if (!HandleAshmem::isValid(handle)) {
+ return false;
+ }
+ const HandleAshmem *o = static_cast<const HandleAshmem*>(handle);
+ unsigned char *ptr = (unsigned char *)mmap(
+ NULL, o->size(), PROT_READ|PROT_WRITE, MAP_SHARED, o->ashmemFd(), 0);
+
+ if (ptr != MAP_FAILED) {
+ for (size_t i = 0; i < o->size(); ++i) {
+ ptr[i] = val;
+ }
+ munmap(ptr, o->size());
+ return true;
+ }
+ return false;
+}
+
+bool TestBufferPoolAllocator::Verify(const native_handle_t *handle, const unsigned char val) {
+ if (!HandleAshmem::isValid(handle)) {
+ return false;
+ }
+ const HandleAshmem *o = static_cast<const HandleAshmem*>(handle);
+ unsigned char *ptr = (unsigned char *)mmap(
+ NULL, o->size(), PROT_READ, MAP_SHARED, o->ashmemFd(), 0);
+
+ if (ptr != MAP_FAILED) {
+ bool res = true;
+ for (size_t i = 0; i < o->size(); ++i) {
+ if (ptr[i] != val) {
+ res = false;
+ break;
+ }
+ }
+ munmap(ptr, o->size());
+ return res;
+ }
+ return false;
+}
+
+void getTestAllocatorParams(std::vector<uint8_t> *params) {
+ constexpr static int kAllocationSize = 1024 * 10;
+ Params ashmemParams(kAllocationSize);
+
+ params->assign(ashmemParams.array, ashmemParams.array + sizeof(ashmemParams));
+}
diff --git a/media/bufferpool/1.0/vts/allocator.h b/media/bufferpool/1.0/vts/allocator.h
new file mode 100644
index 0000000..886e5f2
--- /dev/null
+++ b/media/bufferpool/1.0/vts/allocator.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VNDK_HIDL_BUFFERPOOL_V1_0_ALLOCATOR_H
+#define VNDK_HIDL_BUFFERPOOL_V1_0_ALLOCATOR_H
+
+#include <bufferpool/BufferPoolTypes.h>
+
+using android::hardware::media::bufferpool::V1_0::ResultStatus;
+using android::hardware::media::bufferpool::V1_0::implementation::
+ BufferPoolAllocation;
+using android::hardware::media::bufferpool::V1_0::implementation::
+ BufferPoolAllocator;
+
+// buffer allocator for the tests
+class TestBufferPoolAllocator : public BufferPoolAllocator {
+ public:
+ TestBufferPoolAllocator() {}
+
+ ~TestBufferPoolAllocator() override {}
+
+ ResultStatus allocate(const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<BufferPoolAllocation> *alloc,
+ size_t *allocSize) override;
+
+ bool compatible(const std::vector<uint8_t> &newParams,
+ const std::vector<uint8_t> &oldParams) override;
+
+ static bool Fill(const native_handle_t *handle, const unsigned char val);
+
+ static bool Verify(const native_handle_t *handle, const unsigned char val);
+
+};
+
+// retrieve buffer allocator paramters
+void getTestAllocatorParams(std::vector<uint8_t> *params);
+
+#endif // VNDK_HIDL_BUFFERPOOL_V1_0_ALLOCATOR_H
diff --git a/media/bufferpool/1.0/vts/multi.cpp b/media/bufferpool/1.0/vts/multi.cpp
new file mode 100644
index 0000000..1796819
--- /dev/null
+++ b/media/bufferpool/1.0/vts/multi.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "buffferpool_unit_test"
+
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+#include <binder/ProcessState.h>
+#include <bufferpool/ClientManager.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/LegacySupport.h>
+#include <hidl/Status.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <iostream>
+#include <memory>
+#include <vector>
+#include "allocator.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::hidl_handle;
+using android::hardware::media::bufferpool::V1_0::IClientManager;
+using android::hardware::media::bufferpool::V1_0::ResultStatus;
+using android::hardware::media::bufferpool::V1_0::implementation::BufferId;
+using android::hardware::media::bufferpool::V1_0::implementation::ClientManager;
+using android::hardware::media::bufferpool::V1_0::implementation::ConnectionId;
+using android::hardware::media::bufferpool::V1_0::implementation::TransactionId;
+using android::hardware::media::bufferpool::BufferPoolData;
+
+namespace {
+
+// communication message types between processes.
+enum PipeCommand : int32_t {
+ INIT_OK = 0,
+ INIT_ERROR,
+ SEND,
+ RECEIVE_OK,
+ RECEIVE_ERROR,
+};
+
+// communication message between processes.
+union PipeMessage {
+ struct {
+ int32_t command;
+ BufferId bufferId;
+ ConnectionId connectionId;
+ TransactionId transactionId;
+ int64_t timestampUs;
+ } data;
+ char array[0];
+};
+
+// media.bufferpool test setup
+class BufferpoolMultiTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ ResultStatus status;
+ mReceiverPid = -1;
+ mConnectionValid = false;
+
+ ASSERT_TRUE(pipe(mCommandPipeFds) == 0);
+ ASSERT_TRUE(pipe(mResultPipeFds) == 0);
+
+ mReceiverPid = fork();
+ ASSERT_TRUE(mReceiverPid >= 0);
+
+ if (mReceiverPid == 0) {
+ doReceiver();
+ // In order to ignore gtest behaviour, wait for being killed from
+ // tearDown
+ pause();
+ }
+
+ mManager = ClientManager::getInstance();
+ ASSERT_NE(mManager, nullptr);
+
+ mAllocator = std::make_shared<TestBufferPoolAllocator>();
+ ASSERT_TRUE((bool)mAllocator);
+
+ status = mManager->create(mAllocator, &mConnectionId);
+ ASSERT_TRUE(status == ResultStatus::OK);
+ mConnectionValid = true;
+ }
+
+ virtual void TearDown() override {
+ if (mReceiverPid > 0) {
+ kill(mReceiverPid, SIGKILL);
+ int wstatus;
+ wait(&wstatus);
+ }
+
+ if (mConnectionValid) {
+ mManager->close(mConnectionId);
+ }
+ }
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ android::sp<ClientManager> mManager;
+ std::shared_ptr<BufferPoolAllocator> mAllocator;
+ bool mConnectionValid;
+ ConnectionId mConnectionId;
+ pid_t mReceiverPid;
+ int mCommandPipeFds[2];
+ int mResultPipeFds[2];
+
+ bool sendMessage(int *pipes, const PipeMessage &message) {
+ int ret = write(pipes[1], message.array, sizeof(PipeMessage));
+ return ret == sizeof(PipeMessage);
+ }
+
+ bool receiveMessage(int *pipes, PipeMessage *message) {
+ int ret = read(pipes[0], message->array, sizeof(PipeMessage));
+ return ret == sizeof(PipeMessage);
+ }
+
+ void doReceiver() {
+ configureRpcThreadpool(1, false);
+ PipeMessage message;
+ mManager = ClientManager::getInstance();
+ if (!mManager) {
+ message.data.command = PipeCommand::INIT_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ android::status_t status = mManager->registerAsService();
+ if (status != android::OK) {
+ message.data.command = PipeCommand::INIT_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ message.data.command = PipeCommand::INIT_OK;
+ sendMessage(mResultPipeFds, message);
+
+ receiveMessage(mCommandPipeFds, &message);
+ {
+ native_handle_t *rhandle = nullptr;
+ std::shared_ptr<BufferPoolData> rbuffer;
+ ResultStatus status = mManager->receive(
+ message.data.connectionId, message.data.transactionId,
+ message.data.bufferId, message.data.timestampUs, &rhandle, &rbuffer);
+ mManager->close(message.data.connectionId);
+ if (status != ResultStatus::OK) {
+ if (!TestBufferPoolAllocator::Verify(rhandle, 0x77)) {
+ message.data.command = PipeCommand::RECEIVE_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ }
+ }
+ message.data.command = PipeCommand::RECEIVE_OK;
+ sendMessage(mResultPipeFds, message);
+ }
+};
+
+// Buffer transfer test between processes.
+TEST_F(BufferpoolMultiTest, TransferBuffer) {
+ ResultStatus status;
+ PipeMessage message;
+
+ ASSERT_TRUE(receiveMessage(mResultPipeFds, &message));
+
+ android::sp<IClientManager> receiver = IClientManager::getService();
+ ConnectionId receiverId;
+ ASSERT_TRUE((bool)receiver);
+
+ status = mManager->registerSender(receiver, mConnectionId, &receiverId);
+ ASSERT_TRUE(status == ResultStatus::OK);
+ {
+ native_handle_t *shandle = nullptr;
+ std::shared_ptr<BufferPoolData> sbuffer;
+ TransactionId transactionId;
+ int64_t postUs;
+ std::vector<uint8_t> vecParams;
+
+ getTestAllocatorParams(&vecParams);
+ status = mManager->allocate(mConnectionId, vecParams, &shandle, &sbuffer);
+ ASSERT_TRUE(status == ResultStatus::OK);
+
+ ASSERT_TRUE(TestBufferPoolAllocator::Fill(shandle, 0x77));
+
+ status = mManager->postSend(receiverId, sbuffer, &transactionId, &postUs);
+ ASSERT_TRUE(status == ResultStatus::OK);
+
+ message.data.command = PipeCommand::SEND;
+ message.data.bufferId = sbuffer->mId;
+ message.data.connectionId = receiverId;
+ message.data.transactionId = transactionId;
+ message.data.timestampUs = postUs;
+ sendMessage(mCommandPipeFds, message);
+ }
+ EXPECT_TRUE(receiveMessage(mResultPipeFds, &message));
+}
+
+} // anonymous namespace
+
+int main(int argc, char** argv) {
+ setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ return status;
+}
diff --git a/media/bufferpool/1.0/vts/single.cpp b/media/bufferpool/1.0/vts/single.cpp
new file mode 100644
index 0000000..f73eb62
--- /dev/null
+++ b/media/bufferpool/1.0/vts/single.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "buffferpool_unit_test"
+
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+#include <binder/ProcessState.h>
+#include <bufferpool/ClientManager.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/LegacySupport.h>
+#include <hidl/Status.h>
+#include <unistd.h>
+#include <iostream>
+#include <memory>
+#include <vector>
+#include "allocator.h"
+
+using android::hardware::hidl_handle;
+using android::hardware::media::bufferpool::V1_0::ResultStatus;
+using android::hardware::media::bufferpool::V1_0::implementation::BufferId;
+using android::hardware::media::bufferpool::V1_0::implementation::ClientManager;
+using android::hardware::media::bufferpool::V1_0::implementation::ConnectionId;
+using android::hardware::media::bufferpool::V1_0::implementation::TransactionId;
+using android::hardware::media::bufferpool::BufferPoolData;
+
+namespace {
+
+// Number of iteration for buffer allocation test.
+constexpr static int kNumAllocationTest = 3;
+
+// Number of iteration for buffer recycling test.
+constexpr static int kNumRecycleTest = 3;
+
+// media.bufferpool test setup
+class BufferpoolSingleTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ ResultStatus status;
+ mConnectionValid = false;
+
+ mManager = ClientManager::getInstance();
+ ASSERT_NE(mManager, nullptr);
+
+ mAllocator = std::make_shared<TestBufferPoolAllocator>();
+ ASSERT_TRUE((bool)mAllocator);
+
+ status = mManager->create(mAllocator, &mConnectionId);
+ ASSERT_TRUE(status == ResultStatus::OK);
+
+ mConnectionValid = true;
+
+ status = mManager->registerSender(mManager, mConnectionId, &mReceiverId);
+ ASSERT_TRUE(status == ResultStatus::ALREADY_EXISTS &&
+ mReceiverId == mConnectionId);
+ }
+
+ virtual void TearDown() override {
+ if (mConnectionValid) {
+ mManager->close(mConnectionId);
+ }
+ }
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ android::sp<ClientManager> mManager;
+ std::shared_ptr<BufferPoolAllocator> mAllocator;
+ bool mConnectionValid;
+ ConnectionId mConnectionId;
+ ConnectionId mReceiverId;
+
+};
+
+// Buffer allocation test.
+// Check whether each buffer allocation is done successfully with
+// unique buffer id.
+TEST_F(BufferpoolSingleTest, AllocateBuffer) {
+ ResultStatus status;
+ std::vector<uint8_t> vecParams;
+ getTestAllocatorParams(&vecParams);
+
+ std::shared_ptr<BufferPoolData> buffer[kNumAllocationTest];
+ native_handle_t *allocHandle = nullptr;
+ for (int i = 0; i < kNumAllocationTest; ++i) {
+ status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer[i]);
+ ASSERT_TRUE(status == ResultStatus::OK);
+ }
+ for (int i = 0; i < kNumAllocationTest; ++i) {
+ for (int j = i + 1; j < kNumAllocationTest; ++j) {
+ ASSERT_TRUE(buffer[i]->mId != buffer[j]->mId);
+ }
+ }
+ EXPECT_TRUE(kNumAllocationTest > 1);
+}
+
+// Buffer recycle test.
+// Check whether de-allocated buffers are recycled.
+TEST_F(BufferpoolSingleTest, RecycleBuffer) {
+ ResultStatus status;
+ std::vector<uint8_t> vecParams;
+ getTestAllocatorParams(&vecParams);
+
+ BufferId bid[kNumRecycleTest];
+ for (int i = 0; i < kNumRecycleTest; ++i) {
+ std::shared_ptr<BufferPoolData> buffer;
+ native_handle_t *allocHandle = nullptr;
+ status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer);
+ ASSERT_TRUE(status == ResultStatus::OK);
+ bid[i] = buffer->mId;
+ }
+ for (int i = 1; i < kNumRecycleTest; ++i) {
+ ASSERT_TRUE(bid[i - 1] == bid[i]);
+ }
+ EXPECT_TRUE(kNumRecycleTest > 1);
+}
+
+// Buffer transfer test.
+// Check whether buffer is transferred to another client successfully.
+TEST_F(BufferpoolSingleTest, TransferBuffer) {
+ ResultStatus status;
+ std::vector<uint8_t> vecParams;
+ getTestAllocatorParams(&vecParams);
+ std::shared_ptr<BufferPoolData> sbuffer, rbuffer;
+ native_handle_t *allocHandle = nullptr;
+ native_handle_t *recvHandle = nullptr;
+
+ TransactionId transactionId;
+ int64_t postUs;
+
+ status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &sbuffer);
+ ASSERT_TRUE(status == ResultStatus::OK);
+ ASSERT_TRUE(TestBufferPoolAllocator::Fill(allocHandle, 0x77));
+ status = mManager->postSend(mReceiverId, sbuffer, &transactionId, &postUs);
+ ASSERT_TRUE(status == ResultStatus::OK);
+ status = mManager->receive(mReceiverId, transactionId, sbuffer->mId, postUs,
+ &recvHandle, &rbuffer);
+ EXPECT_TRUE(status == ResultStatus::OK);
+ ASSERT_TRUE(TestBufferPoolAllocator::Verify(recvHandle, 0x77));
+}
+
+} // anonymous namespace
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ return status;
+}
diff --git a/media/bufferpool/2.0/Accessor.cpp b/media/bufferpool/2.0/Accessor.cpp
index f264501..57b4609 100644
--- a/media/bufferpool/2.0/Accessor.cpp
+++ b/media/bufferpool/2.0/Accessor.cpp
@@ -113,6 +113,10 @@
return sConnectionDeathRecipient;
}
+void Accessor::createInvalidator() {
+ Accessor::Impl::createInvalidator();
+}
+
// Methods from ::android::hardware::media::bufferpool::V2_0::IAccessor follow.
Return<void> Accessor::connect(
const sp<::android::hardware::media::bufferpool::V2_0::IObserver>& observer,
diff --git a/media/bufferpool/2.0/Accessor.h b/media/bufferpool/2.0/Accessor.h
index 4b5b17a..8d02519 100644
--- a/media/bufferpool/2.0/Accessor.h
+++ b/media/bufferpool/2.0/Accessor.h
@@ -185,6 +185,8 @@
*/
static sp<ConnectionDeathRecipient> getConnectionDeathRecipient();
+ static void createInvalidator();
+
private:
class Impl;
std::shared_ptr<Impl> mImpl;
diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp
index 4cc8abc..84fcca2 100644
--- a/media/bufferpool/2.0/AccessorImpl.cpp
+++ b/media/bufferpool/2.0/AccessorImpl.cpp
@@ -177,6 +177,7 @@
ResultStatus Accessor::Impl::close(ConnectionId connectionId) {
std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ ALOGV("connection close %lld: %u", (long long)connectionId, mBufferPool.mInvalidation.mId);
mBufferPool.processStatusMessages();
mBufferPool.handleClose(connectionId);
mBufferPool.mObserver.close(connectionId);
@@ -277,7 +278,7 @@
return int(total ? 0.5 + 100. * static_cast<S>(base) / total : 0);
}
-std::atomic<std::uint32_t> Accessor::Impl::BufferPool::Invalidation::sSeqId(0);
+std::atomic<std::uint32_t> Accessor::Impl::BufferPool::Invalidation::sInvSeqId(0);
Accessor::Impl::Impl::BufferPool::~BufferPool() {
std::lock_guard<std::mutex> lock(mMutex);
@@ -316,8 +317,7 @@
BufferId bufferId,
BufferInvalidationChannel &channel) {
for (auto it = mPendings.begin(); it != mPendings.end();) {
- if (it->invalidate(bufferId)) {
- it = mPendings.erase(it);
+ if (it->isInvalidated(bufferId)) {
uint32_t msgId = 0;
if (it->mNeedsAck) {
msgId = ++mInvalidationId;
@@ -327,7 +327,8 @@
}
}
channel.postInvalidation(msgId, it->mFrom, it->mTo);
- sInvalidator.addAccessor(mId, it->mImpl);
+ sInvalidator->addAccessor(mId, it->mImpl);
+ it = mPendings.erase(it);
continue;
}
++it;
@@ -350,10 +351,12 @@
msgId = ++mInvalidationId;
}
}
+ ALOGV("bufferpool invalidation requested and queued");
channel.postInvalidation(msgId, from, to);
- sInvalidator.addAccessor(mId, impl);
+ sInvalidator->addAccessor(mId, impl);
} else {
// TODO: sending hint message?
+ ALOGV("bufferpool invalidation requested and pending");
Pending pending(needsAck, from, to, left, impl);
mPendings.push_back(pending);
}
@@ -364,10 +367,14 @@
std::set<int> deads;
for (auto it = mAcks.begin(); it != mAcks.end(); ++it) {
if (it->second != mInvalidationId) {
- const sp<IObserver> observer = mObservers[it->first].promote();
+ const sp<IObserver> observer = mObservers[it->first];
if (observer) {
- observer->onMessage(it->first, mInvalidationId);
+ ALOGV("connection %lld call observer (%u: %u)",
+ (long long)it->first, it->second, mInvalidationId);
+ Return<void> transResult = observer->onMessage(it->first, mInvalidationId);
+ (void) transResult;
} else {
+ ALOGV("bufferpool observer died %lld", (long long)it->first);
deads.insert(it->first);
}
}
@@ -379,7 +386,7 @@
}
}
// All invalidation Ids are synced.
- sInvalidator.delAccessor(mId);
+ sInvalidator->delAccessor(mId);
}
bool Accessor::Impl::BufferPool::handleOwnBuffer(
@@ -542,6 +549,7 @@
break;
case BufferStatus::INVALIDATION_ACK:
mInvalidation.onAck(message.connectionId, message.bufferId);
+ ret = true;
break;
}
if (ret == false) {
@@ -727,6 +735,7 @@
BufferId to = mSeq;
mStartSeq = mSeq;
// TODO: needsAck params
+ ALOGV("buffer invalidation request bp:%u %u %u", mInvalidation.mId, from, to);
if (from != to) {
invalidate(true, from, to, impl);
}
@@ -791,6 +800,7 @@
notify = true;
}
mAccessors.insert(std::make_pair(accessorId, impl));
+ ALOGV("buffer invalidation added bp:%u %d", accessorId, notify);
}
lock.unlock();
if (notify) {
@@ -801,12 +811,19 @@
void Accessor::Impl::AccessorInvalidator::delAccessor(uint32_t accessorId) {
std::lock_guard<std::mutex> lock(mMutex);
mAccessors.erase(accessorId);
+ ALOGV("buffer invalidation deleted bp:%u", accessorId);
if (mAccessors.size() == 0) {
mReady = false;
}
}
-Accessor::Impl::AccessorInvalidator Accessor::Impl::sInvalidator;
+std::unique_ptr<Accessor::Impl::AccessorInvalidator> Accessor::Impl::sInvalidator;
+
+void Accessor::Impl::createInvalidator() {
+ if (!sInvalidator) {
+ sInvalidator = std::make_unique<Accessor::Impl::AccessorInvalidator>();
+ }
+}
} // namespace implementation
} // namespace V2_0
diff --git a/media/bufferpool/2.0/AccessorImpl.h b/media/bufferpool/2.0/AccessorImpl.h
index 6b03494..b3faa96 100644
--- a/media/bufferpool/2.0/AccessorImpl.h
+++ b/media/bufferpool/2.0/AccessorImpl.h
@@ -34,7 +34,7 @@
/**
* An implementation of a buffer pool accessor(or a buffer pool implementation.) */
-class Accessor::Impl
+class Accessor::Impl
: public std::enable_shared_from_this<Accessor::Impl> {
public:
Impl(const std::shared_ptr<BufferPoolAllocator> &allocator);
@@ -69,6 +69,8 @@
void handleInvalidateAck();
+ static void createInvalidator();
+
private:
// ConnectionId = pid : (timestamp_created + seqId)
// in order to guarantee uniqueness for each connection
@@ -111,7 +113,7 @@
std::set<BufferId> mFreeBuffers;
struct Invalidation {
- static std::atomic<std::uint32_t> sSeqId;
+ static std::atomic<std::uint32_t> sInvSeqId;
struct Pending {
bool mNeedsAck;
@@ -128,18 +130,18 @@
mImpl(impl)
{}
- bool invalidate(uint32_t bufferId) {
+ bool isInvalidated(uint32_t bufferId) {
return isBufferInRange(mFrom, mTo, bufferId) && --mLeft == 0;
}
};
std::list<Pending> mPendings;
std::map<ConnectionId, uint32_t> mAcks;
- std::map<ConnectionId, const wp<IObserver>> mObservers;
+ std::map<ConnectionId, const sp<IObserver>> mObservers;
uint32_t mInvalidationId;
uint32_t mId;
- Invalidation() : mInvalidationId(0), mId(sSeqId.fetch_add(1)) {}
+ Invalidation() : mInvalidationId(0), mId(sInvSeqId.fetch_add(1)) {}
void onConnect(ConnectionId conId, const sp<IObserver> &observer);
@@ -234,6 +236,8 @@
void invalidate(bool needsAck, BufferId from, BufferId to,
const std::shared_ptr<Accessor::Impl> &impl);
+ static void createInvalidator();
+
public:
/** Creates a buffer pool. */
BufferPool();
@@ -376,7 +380,7 @@
void delAccessor(uint32_t accessorId);
};
- static AccessorInvalidator sInvalidator;
+ static std::unique_ptr<AccessorInvalidator> sInvalidator;
static void invalidatorThread(
std::map<uint32_t, const std::weak_ptr<Accessor::Impl>> &accessors,
diff --git a/media/bufferpool/2.0/BufferPoolClient.cpp b/media/bufferpool/2.0/BufferPoolClient.cpp
index c80beff..5564a13 100644
--- a/media/bufferpool/2.0/BufferPoolClient.cpp
+++ b/media/bufferpool/2.0/BufferPoolClient.cpp
@@ -644,7 +644,7 @@
} else if (messageId != 0) {
// messages are drained.
if (isMessageLater(messageId, mReleasing.mInvalidateId)) {
- mReleasing.mInvalidateId = lastMsgId;
+ mReleasing.mInvalidateId = messageId;
mReleasing.mInvalidateAck = true;
}
}
@@ -653,6 +653,9 @@
mReleasing.mStatusChannel->postBufferInvalidateAck(
mConnectionId,
mReleasing.mInvalidateId, &mReleasing.mInvalidateAck);
+ ALOGV("client %lld invalidateion ack (%d) %u",
+ (long long)mConnectionId,
+ mReleasing.mInvalidateAck, mReleasing.mInvalidateId);
}
}
return cleared;
@@ -808,6 +811,7 @@
}
void BufferPoolClient::receiveInvalidation(uint32_t msgId) {
+ ALOGV("bufferpool client recv inv %u", msgId);
if (isValid()) {
mImpl->receiveInvalidation(msgId);
}
diff --git a/media/bufferpool/2.0/ClientManager.cpp b/media/bufferpool/2.0/ClientManager.cpp
index f8531d8..c31d313 100644
--- a/media/bufferpool/2.0/ClientManager.cpp
+++ b/media/bufferpool/2.0/ClientManager.cpp
@@ -24,6 +24,7 @@
#include <utils/Log.h>
#include "BufferPoolClient.h"
#include "Observer.h"
+#include "Accessor.h"
namespace android {
namespace hardware {
@@ -453,6 +454,7 @@
if (!sInstance) {
sInstance = new ClientManager();
}
+ Accessor::createInvalidator();
return sInstance;
}
diff --git a/media/extractors/mp3/MP3Extractor.cpp b/media/extractors/mp3/MP3Extractor.cpp
index 621fd03..0e1ffb4 100644
--- a/media/extractors/mp3/MP3Extractor.cpp
+++ b/media/extractors/mp3/MP3Extractor.cpp
@@ -546,7 +546,8 @@
buffer->release();
buffer = NULL;
- return (n < 0 ? AMEDIA_ERROR_UNKNOWN : AMEDIA_ERROR_END_OF_STREAM);
+ return ((n < 0 && n != ERROR_END_OF_STREAM) ?
+ AMEDIA_ERROR_UNKNOWN : AMEDIA_ERROR_END_OF_STREAM);
}
uint32_t header = U32_AT((const uint8_t *)buffer->data());
@@ -590,7 +591,8 @@
buffer->release();
buffer = NULL;
- return (n < 0 ? AMEDIA_ERROR_UNKNOWN : AMEDIA_ERROR_END_OF_STREAM);
+ return ((n < 0 && n != ERROR_END_OF_STREAM) ?
+ AMEDIA_ERROR_UNKNOWN : AMEDIA_ERROR_END_OF_STREAM);
}
buffer->set_range(0, frame_size);
diff --git a/media/extractors/mp4/Android.bp b/media/extractors/mp4/Android.bp
index 40b2c97..47dc718 100644
--- a/media/extractors/mp4/Android.bp
+++ b/media/extractors/mp4/Android.bp
@@ -16,6 +16,7 @@
shared_libs: [
"liblog",
"libmediaextractor",
+ "libmediandk"
],
static_libs: [
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index a61e60a..55a0c47 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -1477,7 +1477,7 @@
return mImageItemsValid ? mDisplayables.size() : 0;
}
-sp<MetaData> ItemTable::getImageMeta(const uint32_t imageIndex) {
+AMediaFormat *ItemTable::getImageMeta(const uint32_t imageIndex) {
if (!mImageItemsValid) {
return NULL;
}
@@ -1502,28 +1502,31 @@
}
}
- sp<MetaData> meta = new MetaData;
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
+ AMediaFormat *meta = AMediaFormat_new();
+ AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
if (image->itemId == mPrimaryItemId) {
- meta->setInt32(kKeyTrackIsDefault, 1);
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_DEFAULT, 1);
}
ALOGV("image[%u]: size %dx%d", imageIndex, image->width, image->height);
- meta->setInt32(kKeyWidth, image->width);
- meta->setInt32(kKeyHeight, image->height);
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_WIDTH, image->width);
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_HEIGHT, image->height);
if (image->rotation != 0) {
// Rotation angle in HEIF is CCW, convert to CW here to be
// consistent with the other media formats.
switch(image->rotation) {
- case 90: meta->setInt32(kKeyRotation, 270); break;
- case 180: meta->setInt32(kKeyRotation, 180); break;
- case 270: meta->setInt32(kKeyRotation, 90); break;
+ case 90:
+ case 180:
+ case 270:
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_ROTATION, 360 - image->rotation);
+ break;
default: break; // don't set if invalid
}
}
- meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
+ AMediaFormat_setInt32(meta,
+ AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 1.5);
if (!image->thumbnails.empty()) {
ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]);
@@ -1531,10 +1534,12 @@
const ImageItem &thumbnail = mItemIdToItemMap[thumbItemIndex];
if (thumbnail.hvcc != NULL) {
- meta->setInt32(kKeyThumbnailWidth, thumbnail.width);
- meta->setInt32(kKeyThumbnailHeight, thumbnail.height);
- meta->setData(kKeyThumbnailHVCC, kTypeHVCC,
- thumbnail.hvcc->data(), thumbnail.hvcc->size());
+ AMediaFormat_setInt32(meta,
+ AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH, thumbnail.width);
+ AMediaFormat_setInt32(meta,
+ AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT, thumbnail.height);
+ AMediaFormat_setBuffer(meta,
+ AMEDIAFORMAT_KEY_CSD_HEVC, thumbnail.hvcc->data(), thumbnail.hvcc->size());
ALOGV("image[%u]: thumbnail: size %dx%d, item index %zd",
imageIndex, thumbnail.width, thumbnail.height, thumbItemIndex);
} else {
@@ -1546,24 +1551,30 @@
}
if (image->isGrid()) {
- meta->setInt32(kKeyGridRows, image->rows);
- meta->setInt32(kKeyGridCols, image->columns);
-
+ AMediaFormat_setInt32(meta,
+ AMEDIAFORMAT_KEY_GRID_ROWS, image->rows);
+ AMediaFormat_setInt32(meta,
+ AMEDIAFORMAT_KEY_GRID_COLUMNS, image->columns);
// point image to the first tile for grid size and HVCC
image = &mItemIdToItemMap.editValueAt(tileItemIndex);
- meta->setInt32(kKeyTileWidth, image->width);
- meta->setInt32(kKeyTileHeight, image->height);
- meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
+ AMediaFormat_setInt32(meta,
+ AMEDIAFORMAT_KEY_TILE_WIDTH, image->width);
+ AMediaFormat_setInt32(meta,
+ AMEDIAFORMAT_KEY_TILE_HEIGHT, image->height);
+ AMediaFormat_setInt32(meta,
+ AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 1.5);
}
if (image->hvcc == NULL) {
ALOGE("%s: hvcc is missing for image[%u]!", __FUNCTION__, imageIndex);
return NULL;
}
- meta->setData(kKeyHVCC, kTypeHVCC, image->hvcc->data(), image->hvcc->size());
+ AMediaFormat_setBuffer(meta,
+ AMEDIAFORMAT_KEY_CSD_HEVC, image->hvcc->data(), image->hvcc->size());
if (image->icc != NULL) {
- meta->setData(kKeyIccProfile, 0, image->icc->data(), image->icc->size());
+ AMediaFormat_setBuffer(meta,
+ AMEDIAFORMAT_KEY_ICC_PROFILE, image->icc->data(), image->icc->size());
}
return meta;
}
diff --git a/media/extractors/mp4/ItemTable.h b/media/extractors/mp4/ItemTable.h
index 650b3f3..be81b59 100644
--- a/media/extractors/mp4/ItemTable.h
+++ b/media/extractors/mp4/ItemTable.h
@@ -19,6 +19,8 @@
#include <set>
+#include <media/NdkMediaFormat.h>
+
#include <media/stagefright/foundation/ADebug.h>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
@@ -51,7 +53,7 @@
bool isValid() { return mImageItemsValid; }
uint32_t countImages() const;
- sp<MetaData> getImageMeta(const uint32_t imageIndex);
+ AMediaFormat *getImageMeta(const uint32_t imageIndex);
status_t findImageItem(const uint32_t imageIndex, uint32_t *itemIndex);
status_t findThumbnailItem(const uint32_t imageIndex, uint32_t *itemIndex);
status_t getImageOffsetAndSize(
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 524d02f..b362b4a 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -56,6 +56,8 @@
#define UINT32_MAX (4294967295U)
#endif
+#define ALAC_SPECIFIC_INFO_SIZE (36)
+
namespace android {
enum {
@@ -67,11 +69,11 @@
kMaxAtomSize = 64 * 1024 * 1024,
};
-class MPEG4Source : public MediaTrackHelper {
+class MPEG4Source : public MediaTrackHelperV2 {
static const size_t kMaxPcmFrameSize = 8192;
public:
// Caller retains ownership of both "dataSource" and "sampleTable".
- MPEG4Source(MetaDataBase &format,
+ MPEG4Source(AMediaFormat *format,
DataSourceHelper *dataSource,
int32_t timeScale,
const sp<SampleTable> &sampleTable,
@@ -81,21 +83,22 @@
const sp<ItemTable> &itemTable);
virtual status_t init();
- virtual status_t start();
- virtual status_t stop();
+ virtual media_status_t start();
+ virtual media_status_t stop();
- virtual status_t getFormat(MetaDataBase &);
+ virtual media_status_t getFormat(AMediaFormat *);
- virtual status_t read(MediaBufferBase **buffer, const ReadOptions *options = NULL);
+ virtual media_status_t read(MediaBufferBase **buffer, const ReadOptions *options = NULL);
virtual bool supportNonblockingRead() { return true; }
- virtual status_t fragmentedRead(MediaBufferBase **buffer, const ReadOptions *options = NULL);
+ virtual media_status_t fragmentedRead(
+ MediaBufferBase **buffer, const ReadOptions *options = NULL);
virtual ~MPEG4Source();
private:
Mutex mLock;
- MetaDataBase &mFormat;
+ AMediaFormat *mFormat;
DataSourceHelper *mDataSource;
int32_t mTimescale;
sp<SampleTable> mSampleTable;
@@ -334,6 +337,8 @@
case FOURCC('t', 'w', 'o', 's'):
case FOURCC('s', 'o', 'w', 't'):
return MEDIA_MIMETYPE_AUDIO_RAW;
+ case FOURCC('a', 'l', 'a', 'c'):
+ return MEDIA_MIMETYPE_AUDIO_ALAC;
default:
ALOGW("Unknown fourcc: %c%c%c%c",
@@ -375,6 +380,7 @@
mFirstTrack(NULL),
mLastTrack(NULL) {
ALOGV("mime=%s, mPreferHeif=%d", mime, mPreferHeif);
+ mFileMetaData = AMediaFormat_new();
}
MPEG4Extractor::~MPEG4Extractor() {
@@ -382,6 +388,7 @@
while (track) {
Track *next = track->next;
+ AMediaFormat_delete(track->meta);
delete track;
track = next;
}
@@ -393,6 +400,7 @@
mPssh.clear();
delete mDataSource;
+ AMediaFormat_delete(mFileMetaData);
}
uint32_t MPEG4Extractor::flags() const {
@@ -401,13 +409,13 @@
(CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK) : 0);
}
-status_t MPEG4Extractor::getMetaData(MetaDataBase &meta) {
+media_status_t MPEG4Extractor::getMetaData(AMediaFormat *meta) {
status_t err;
if ((err = readMetaData()) != OK) {
- return UNKNOWN_ERROR;
+ return AMEDIA_ERROR_UNKNOWN;
}
- meta = mFileMetaData;
- return OK;
+ AMediaFormat_copy(meta, mFileMetaData);
+ return AMEDIA_OK;
}
size_t MPEG4Extractor::countTracks() {
@@ -428,18 +436,18 @@
return n;
}
-status_t MPEG4Extractor::getTrackMetaData(
- MetaDataBase &meta,
+media_status_t MPEG4Extractor::getTrackMetaData(
+ AMediaFormat *meta,
size_t index, uint32_t flags) {
status_t err;
if ((err = readMetaData()) != OK) {
- return UNKNOWN_ERROR;
+ return AMEDIA_ERROR_UNKNOWN;
}
Track *track = mFirstTrack;
while (index > 0) {
if (track == NULL) {
- return UNKNOWN_ERROR;
+ return AMEDIA_ERROR_UNKNOWN;
}
track = track->next;
@@ -447,15 +455,15 @@
}
if (track == NULL) {
- return UNKNOWN_ERROR;
+ return AMEDIA_ERROR_UNKNOWN;
}
[=] {
int64_t duration;
int32_t samplerate;
if (track->has_elst && mHeaderTimescale != 0 &&
- track->meta.findInt64(kKeyDuration, &duration) &&
- track->meta.findInt32(kKeySampleRate, &samplerate)) {
+ AMediaFormat_getInt64(track->meta, AMEDIAFORMAT_KEY_DURATION, &duration) &&
+ AMediaFormat_getInt32(track->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, &samplerate)) {
track->has_elst = false;
@@ -489,7 +497,7 @@
return;
}
ALOGV("delay = %" PRId64, delay);
- track->meta.setInt32(kKeyEncoderDelay, delay);
+ AMediaFormat_setInt32(track->meta, AMEDIAFORMAT_KEY_ENCODER_DELAY, delay);
int64_t scaled_duration;
// scaled_duration = duration * mHeaderTimescale;
@@ -509,8 +517,8 @@
ALOGV("segment_end = %" PRId64 ", padding = %" PRId64, segment_end, padding);
if (padding < 0) {
- // track duration from media header (which is what kKeyDuration is) might
- // be slightly shorter than the segment duration, which would make the
+ // track duration from media header (which is what AMEDIAFORMAT_KEY_DURATION is)
+ // might be slightly shorter than the segment duration, which would make the
// padding negative. Clamp to zero.
padding = 0;
}
@@ -529,7 +537,7 @@
return;
}
ALOGV("paddingsamples = %" PRId64, paddingsamples);
- track->meta.setInt32(kKeyEncoderPadding, paddingsamples);
+ AMediaFormat_setInt32(track->meta, AMEDIAFORMAT_KEY_ENCODER_PADDING, paddingsamples);
}
}();
@@ -538,7 +546,7 @@
track->includes_expensive_metadata = true;
const char *mime;
- CHECK(track->meta.findCString(kKeyMIMEType, &mime));
+ CHECK(AMediaFormat_getString(track->meta, AMEDIAFORMAT_KEY_MIME, &mime));
if (!strncasecmp("video/", mime, 6)) {
// MPEG2 tracks do not provide CSD, so read the stream header
if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG2)) {
@@ -551,17 +559,19 @@
}
uint8_t header[kMaxTrackHeaderSize];
if (mDataSource->readAt(offset, &header, size) == (ssize_t)size) {
- track->meta.setData(kKeyStreamHeader, 'mdat', header, size);
+ AMediaFormat_setBuffer(track->meta,
+ AMEDIAFORMAT_KEY_MPEG2_STREAM_HEADER, header, size);
}
}
}
if (mMoofOffset > 0) {
int64_t duration;
- if (track->meta.findInt64(kKeyDuration, &duration)) {
+ if (AMediaFormat_getInt64(track->meta,
+ AMEDIAFORMAT_KEY_DURATION, &duration)) {
// nothing fancy, just pick a frame near 1/4th of the duration
- track->meta.setInt64(
- kKeyThumbnailTime, duration / 4);
+ AMediaFormat_setInt64(track->meta,
+ AMEDIAFORMAT_KEY_THUMBNAIL_TIME, duration / 4);
}
} else {
uint32_t sampleIndex;
@@ -571,16 +581,16 @@
&& track->sampleTable->getMetaDataForSample(
sampleIndex, NULL /* offset */, NULL /* size */,
&sampleTime) == OK) {
- track->meta.setInt64(
- kKeyThumbnailTime,
- ((int64_t)sampleTime * 1000000) / track->timescale);
+ AMediaFormat_setInt64(track->meta,
+ AMEDIAFORMAT_KEY_THUMBNAIL_TIME,
+ ((int64_t)sampleTime * 1000000) / track->timescale);
}
}
}
}
- meta = track->meta;
- return OK;
+ AMediaFormat_copy(meta, track->meta);
+ return AMEDIA_OK;
}
status_t MPEG4Extractor::readMetaData() {
@@ -615,12 +625,14 @@
off64_t exifOffset;
size_t exifSize;
if (mItemTable->getExifOffsetAndSize(&exifOffset, &exifSize) == OK) {
- mFileMetaData.setInt64(kKeyExifOffset, (int64_t)exifOffset);
- mFileMetaData.setInt64(kKeyExifSize, (int64_t)exifSize);
+ AMediaFormat_setInt64(mFileMetaData,
+ AMEDIAFORMAT_KEY_EXIF_OFFSET, (int64_t)exifOffset);
+ AMediaFormat_setInt64(mFileMetaData,
+ AMEDIAFORMAT_KEY_EXIF_SIZE, (int64_t)exifSize);
}
for (uint32_t imageIndex = 0;
imageIndex < mItemTable->countImages(); imageIndex++) {
- sp<MetaData> meta = mItemTable->getImageMeta(imageIndex);
+ AMediaFormat *meta = mItemTable->getImageMeta(imageIndex);
if (meta == NULL) {
ALOGE("heif image %u has no meta!", imageIndex);
continue;
@@ -645,8 +657,8 @@
}
mLastTrack = track;
- track->meta = *(meta.get());
- track->meta.setInt32(kKeyTrackID, imageIndex);
+ track->meta = meta;
+ AMediaFormat_setInt32(track->meta, AMEDIAFORMAT_KEY_TRACK_ID, imageIndex);
track->includes_expensive_metadata = false;
track->skipTrack = false;
track->timescale = 1000000;
@@ -655,16 +667,18 @@
if (mInitCheck == OK) {
if (findTrackByMimePrefix("video/") != NULL) {
- mFileMetaData.setCString(
- kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG4);
+ AMediaFormat_setString(mFileMetaData,
+ AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_CONTAINER_MPEG4);
} else if (findTrackByMimePrefix("audio/") != NULL) {
- mFileMetaData.setCString(kKeyMIMEType, "audio/mp4");
+ AMediaFormat_setString(mFileMetaData,
+ AMEDIAFORMAT_KEY_MIME, "audio/mp4");
} else if (findTrackByMimePrefix(
MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC) != NULL) {
- mFileMetaData.setCString(
- kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_HEIF);
+ AMediaFormat_setString(mFileMetaData,
+ AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_CONTAINER_HEIF);
} else {
- mFileMetaData.setCString(kKeyMIMEType, "application/octet-stream");
+ AMediaFormat_setString(mFileMetaData,
+ AMEDIAFORMAT_KEY_MIME, "application/octet-stream");
}
} else {
mInitCheck = err;
@@ -689,7 +703,7 @@
memcpy(ptr + 20, mPssh[i].data, mPssh[i].datalen);
ptr += (20 + mPssh[i].datalen);
}
- mFileMetaData.setData(kKeyPssh, 'pssh', buf, psshsize);
+ AMediaFormat_setBuffer(mFileMetaData, AMEDIAFORMAT_KEY_PSSH, buf, psshsize);
free(buf);
}
@@ -940,7 +954,9 @@
track->includes_expensive_metadata = false;
track->skipTrack = false;
track->timescale = 0;
- track->meta.setCString(kKeyMIMEType, "application/octet-stream");
+ track->meta = AMediaFormat_new();
+ AMediaFormat_setString(track->meta,
+ AMEDIAFORMAT_KEY_MIME, "application/octet-stream");
track->has_elst = false;
track->subsample_encryption = false;
}
@@ -964,8 +980,10 @@
if (isTrack) {
int32_t trackId;
- // There must be exact one track header per track.
- if (!mLastTrack->meta.findInt32(kKeyTrackID, &trackId)) {
+ // There must be exactly one track header per track.
+
+ if (!AMediaFormat_getInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_TRACK_ID, &trackId)) {
mLastTrack->skipTrack = true;
}
@@ -1041,7 +1059,7 @@
}
}
if (mode != kCryptoModeUnencrypted) {
- mLastTrack->meta.setInt32(kKeyCryptoMode, mode);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CRYPTO_MODE, mode);
}
break;
}
@@ -1117,12 +1135,55 @@
return ERROR_MALFORMED;
}
- mLastTrack->meta.setCString(kKeyMIMEType, FourCC2MIME(original_fourcc));
+ AMediaFormat_setString(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_MIME, FourCC2MIME(original_fourcc));
uint32_t num_channels = 0;
uint32_t sample_rate = 0;
if (AdjustChannelsAndRate(original_fourcc, &num_channels, &sample_rate)) {
- mLastTrack->meta.setInt32(kKeyChannelCount, num_channels);
- mLastTrack->meta.setInt32(kKeySampleRate, sample_rate);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_CHANNEL_COUNT, num_channels);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_SAMPLE_RATE, sample_rate);
+ }
+
+ // If format type is 'alac', it is necessary to get the parameters
+ // from a alac atom spreading behind the frma atom.
+ // See 'external/alac/ALACMagicCookieDescription.txt'.
+ if (original_fourcc == FOURCC('a', 'l', 'a', 'c')) {
+ // Store ALAC magic cookie (decoder needs it).
+ uint8_t alacInfo[12];
+ data_offset = *offset;
+ if (mDataSource->readAt(
+ data_offset, alacInfo, sizeof(alacInfo)) < (ssize_t)sizeof(alacInfo)) {
+ return ERROR_IO;
+ }
+ uint32_t size = U32_AT(&alacInfo[0]);
+ if ((size != ALAC_SPECIFIC_INFO_SIZE) ||
+ (U32_AT(&alacInfo[4]) != FOURCC('a', 'l', 'a', 'c')) ||
+ (U32_AT(&alacInfo[8]) != 0)) {
+ return ERROR_MALFORMED;
+ }
+
+ data_offset += sizeof(alacInfo);
+ uint8_t cookie[size - sizeof(alacInfo)];
+ if (mDataSource->readAt(
+ data_offset, cookie, sizeof(cookie)) < (ssize_t)sizeof(cookie)) {
+ return ERROR_IO;
+ }
+
+ uint8_t bitsPerSample = cookie[5];
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, bitsPerSample);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_CHANNEL_COUNT, cookie[9]);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_SAMPLE_RATE, U32_AT(&cookie[20]));
+ AMediaFormat_setBuffer(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_CSD_0, cookie, sizeof(cookie));
+
+ // Add the size of ALAC Specific Info (36 bytes) and terminator
+ // atom (8 bytes).
+ *offset += (size + 8);
}
break;
}
@@ -1219,16 +1280,23 @@
}
int32_t tmpAlgorithmId;
- if (!mLastTrack->meta.findInt32(kKeyCryptoMode, &tmpAlgorithmId)) {
- mLastTrack->meta.setInt32(kKeyCryptoMode, defaultAlgorithmId);
+ if (!AMediaFormat_getInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_CRYPTO_MODE, &tmpAlgorithmId)) {
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_CRYPTO_MODE, defaultAlgorithmId);
}
- mLastTrack->meta.setInt32(kKeyCryptoDefaultIVSize, defaultIVSize);
- mLastTrack->meta.setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16);
- mLastTrack->meta.setInt32(kKeyEncryptedByteBlock, defaultEncryptedByteBlock);
- mLastTrack->meta.setInt32(kKeySkipByteBlock, defaultSkipByteBlock);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE, defaultIVSize);
+ AMediaFormat_setBuffer(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_CRYPTO_KEY, defaultKeyId, 16);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK, defaultEncryptedByteBlock);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK, defaultSkipByteBlock);
if (defaultConstantIv != NULL) {
- mLastTrack->meta.setData(kKeyCryptoIV, 'dciv', defaultConstantIv->data(), defaultConstantIv->size());
+ AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CRYPTO_IV,
+ defaultConstantIv->data(), defaultConstantIv->size());
}
break;
}
@@ -1377,8 +1445,8 @@
}
}
if (duration != 0 && mLastTrack->timescale != 0) {
- mLastTrack->meta.setInt64(
- kKeyDuration, (duration * 1000000) / mLastTrack->timescale);
+ AMediaFormat_setInt64(mLastTrack->meta, AMEDIAFORMAT_KEY_DURATION,
+ (duration * 1000000) / mLastTrack->timescale);
}
uint8_t lang[2];
@@ -1405,8 +1473,7 @@
lang_code[2] = (lang[1] & 0x1f) + 0x60;
lang_code[3] = '\0';
- mLastTrack->meta.setCString(
- kKeyMediaLanguage, lang_code);
+ AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_LANGUAGE, lang_code);
break;
}
@@ -1440,7 +1507,7 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- CHECK(mLastTrack->meta.findCString(kKeyMIMEType, &mime));
+ CHECK(AMediaFormat_getString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, &mime));
if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) &&
strcasecmp(mime, "application/octet-stream")) {
// For now we only support a single type of media per track.
@@ -1481,7 +1548,7 @@
}
String8 mimeFormat((const char *)(buffer.get()), chunk_data_size);
- mLastTrack->meta.setCString(kKeyMIMEType, mimeFormat.string());
+ AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, mimeFormat.string());
break;
}
@@ -1492,6 +1559,7 @@
case FOURCC('s', 'a', 'w', 'b'):
case FOURCC('t', 'w', 'o', 's'):
case FOURCC('s', 'o', 'w', 't'):
+ case FOURCC('a', 'l', 'a', 'c'):
{
if (mIsQT && chunk_type == FOURCC('m', 'p', '4', 'a')
&& depth >= 1 && mPath[depth - 1] == FOURCC('w', 'a', 'v', 'e')) {
@@ -1559,20 +1627,60 @@
if (chunk_type != FOURCC('e', 'n', 'c', 'a')) {
// if the chunk type is enca, we'll get the type from the frma box later
- mLastTrack->meta.setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
+ AMediaFormat_setString(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_MIME, FourCC2MIME(chunk_type));
AdjustChannelsAndRate(chunk_type, &num_channels, &sample_rate);
if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, FourCC2MIME(chunk_type))) {
- mLastTrack->meta.setInt32(kKeyBitsPerSample, sample_size);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, sample_size);
if (chunk_type == FOURCC('t', 'w', 'o', 's')) {
- mLastTrack->meta.setInt32(kKeyPcmBigEndian, 1);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN, 1);
}
}
}
ALOGV("*** coding='%s' %d channels, size %d, rate %d\n",
chunk, num_channels, sample_size, sample_rate);
- mLastTrack->meta.setInt32(kKeyChannelCount, num_channels);
- mLastTrack->meta.setInt32(kKeySampleRate, sample_rate);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, num_channels);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sample_rate);
+
+ if (chunk_type == FOURCC('a', 'l', 'a', 'c')) {
+
+ // See 'external/alac/ALACMagicCookieDescription.txt for the detail'.
+ // Store ALAC magic cookie (decoder needs it).
+ uint8_t alacInfo[12];
+ data_offset += sizeof(buffer);
+ if (mDataSource->readAt(
+ data_offset, alacInfo, sizeof(alacInfo)) < (ssize_t)sizeof(alacInfo)) {
+ return ERROR_IO;
+ }
+ uint32_t size = U32_AT(&alacInfo[0]);
+ if ((size != ALAC_SPECIFIC_INFO_SIZE) ||
+ (U32_AT(&alacInfo[4]) != FOURCC('a', 'l', 'a', 'c')) ||
+ (U32_AT(&alacInfo[8]) != 0)) {
+ return ERROR_MALFORMED;
+ }
+ data_offset += sizeof(alacInfo);
+ uint8_t cookie[size - sizeof(alacInfo)];
+ if (mDataSource->readAt(
+ data_offset, cookie, sizeof(cookie)) < (ssize_t)sizeof(cookie)) {
+ return ERROR_IO;
+ }
+
+ uint8_t bitsPerSample = cookie[5];
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, bitsPerSample);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_CHANNEL_COUNT, cookie[9]);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_SAMPLE_RATE, U32_AT(&cookie[20]));
+ AMediaFormat_setBuffer(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_CSD_0, cookie, sizeof(cookie));
+ data_offset += sizeof(cookie);
+ *offset = data_offset;
+ CHECK_EQ(*offset, stop_offset);
+ }
while (*offset < stop_offset) {
status_t err = parseChunk(offset, depth + 1);
@@ -1626,10 +1734,11 @@
if (chunk_type != FOURCC('e', 'n', 'c', 'v')) {
// if the chunk type is encv, we'll get the type from the frma box later
- mLastTrack->meta.setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
+ AMediaFormat_setString(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_MIME, FourCC2MIME(chunk_type));
}
- mLastTrack->meta.setInt32(kKeyWidth, width);
- mLastTrack->meta.setInt32(kKeyHeight, height);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_WIDTH, width);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_HEIGHT, height);
off64_t stop_offset = *offset + chunk_size;
*offset = data_offset + sizeof(buffer);
@@ -1717,12 +1826,15 @@
ALOGE("max sample size too big: %zu", max_size);
return ERROR_MALFORMED;
}
- mLastTrack->meta.setInt32(kKeyMaxInputSize, max_size + 10 * 2);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, max_size + 10 * 2);
} else {
// No size was specified. Pick a conservatively large size.
uint32_t width, height;
- if (!mLastTrack->meta.findInt32(kKeyWidth, (int32_t*)&width) ||
- !mLastTrack->meta.findInt32(kKeyHeight,(int32_t*) &height)) {
+ if (!AMediaFormat_getInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_WIDTH, (int32_t*)&width) ||
+ !AMediaFormat_getInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_HEIGHT,(int32_t*) &height)) {
ALOGE("No width or height, assuming worst case 1080p");
width = 1920;
height = 1080;
@@ -1737,7 +1849,7 @@
}
const char *mime;
- CHECK(mLastTrack->meta.findCString(kKeyMIMEType, &mime));
+ CHECK(AMediaFormat_getString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, &mime));
if (!strncmp(mime, "audio/", 6)) {
// for audio, use 128KB
max_size = 1024 * 128;
@@ -1754,26 +1866,28 @@
// HACK: allow 10% overhead
// TODO: read sample size from traf atom for fragmented MPEG4.
max_size += max_size / 10;
- mLastTrack->meta.setInt32(kKeyMaxInputSize, max_size);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, max_size);
}
// NOTE: setting another piece of metadata invalidates any pointers (such as the
// mimetype) previously obtained, so don't cache them.
const char *mime;
- CHECK(mLastTrack->meta.findCString(kKeyMIMEType, &mime));
+ CHECK(AMediaFormat_getString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, &mime));
// Calculate average frame rate.
if (!strncasecmp("video/", mime, 6)) {
size_t nSamples = mLastTrack->sampleTable->countSamples();
if (nSamples == 0) {
int32_t trackId;
- if (mLastTrack->meta.findInt32(kKeyTrackID, &trackId)) {
+ if (AMediaFormat_getInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_TRACK_ID, &trackId)) {
for (size_t i = 0; i < mTrex.size(); i++) {
Trex *t = &mTrex.editItemAt(i);
if (t->track_ID == (uint32_t) trackId) {
if (t->default_sample_duration > 0) {
int32_t frameRate =
mLastTrack->timescale / t->default_sample_duration;
- mLastTrack->meta.setInt32(kKeyFrameRate, frameRate);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_FRAME_RATE, frameRate);
}
break;
}
@@ -1781,15 +1895,18 @@
}
} else {
int64_t durationUs;
- if (mLastTrack->meta.findInt64(kKeyDuration, &durationUs)) {
+ if (AMediaFormat_getInt64(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_DURATION, &durationUs)) {
if (durationUs > 0) {
int32_t frameRate = (nSamples * 1000000LL +
(durationUs >> 1)) / durationUs;
- mLastTrack->meta.setInt32(kKeyFrameRate, frameRate);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_FRAME_RATE, frameRate);
}
}
ALOGV("setting frame count %zu", nSamples);
- mLastTrack->meta.setInt32(kKeyFrameCount, nSamples);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_FRAME_COUNT, nSamples);
}
}
@@ -1897,7 +2014,7 @@
if (buffer[len - 1] != '/') {
buffer[len] = '/';
}
- mFileMetaData.setCString(kKeyLocation, &buffer[0]);
+ AMediaFormat_setString(mFileMetaData, AMEDIAFORMAT_KEY_LOCATION, &buffer[0]);
break;
}
@@ -1927,8 +2044,8 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- mLastTrack->meta.setData(
- kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4);
+ AMediaFormat_setBuffer(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_ESDS, &buffer[4], chunk_data_size - 4);
if (mPath.size() >= 2
&& mPath[mPath.size() - 2] == FOURCC('m', 'p', '4', 'a')) {
@@ -1952,7 +2069,8 @@
uint8_t objectTypeIndication;
if (esds.getObjectTypeIndication(&objectTypeIndication) == OK) {
if (objectTypeIndication >= 0x60 && objectTypeIndication <= 0x65) {
- mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG2);
+ AMediaFormat_setString(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_MPEG2);
}
}
}
@@ -1979,10 +2097,12 @@
uint32_t maxBitrate = U32_AT(&buffer[4]);
uint32_t avgBitrate = U32_AT(&buffer[8]);
if (maxBitrate > 0 && maxBitrate < INT32_MAX) {
- mLastTrack->meta.setInt32(kKeyMaxBitRate, (int32_t)maxBitrate);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_MAX_BIT_RATE, (int32_t)maxBitrate);
}
if (avgBitrate > 0 && avgBitrate < INT32_MAX) {
- mLastTrack->meta.setInt32(kKeyBitRate, (int32_t)avgBitrate);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)avgBitrate);
}
break;
}
@@ -2006,8 +2126,8 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- mLastTrack->meta.setData(
- kKeyAVCC, kTypeAVCC, buffer.get(), chunk_data_size);
+ AMediaFormat_setBuffer(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_CSD_AVC, buffer.get(), chunk_data_size);
break;
}
@@ -2028,8 +2148,8 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- mLastTrack->meta.setData(
- kKeyHVCC, kTypeHVCC, buffer.get(), chunk_data_size);
+ AMediaFormat_setBuffer(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_CSD_HEVC, buffer.get(), chunk_data_size);
*offset += chunk_size;
break;
@@ -2064,7 +2184,8 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- mLastTrack->meta.setData(kKeyD263, kTypeD263, buffer, chunk_data_size);
+ AMediaFormat_setBuffer(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_D263, buffer, chunk_data_size);
break;
}
@@ -2192,15 +2313,15 @@
duration = d32;
}
if (duration != 0 && mHeaderTimescale != 0 && duration < UINT64_MAX / 1000000) {
- mFileMetaData.setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
+ AMediaFormat_setInt64(mFileMetaData,
+ AMEDIAFORMAT_KEY_DURATION, duration * 1000000 / mHeaderTimescale);
}
String8 s;
if (convertTimeToDate(creationTime, &s)) {
- mFileMetaData.setCString(kKeyDate, s.string());
+ AMediaFormat_setString(mFileMetaData, AMEDIAFORMAT_KEY_DATE, s.string());
}
-
break;
}
@@ -2242,7 +2363,8 @@
}
if (duration != 0 && mHeaderTimescale != 0) {
- mFileMetaData.setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
+ AMediaFormat_setInt64(mFileMetaData,
+ AMEDIAFORMAT_KEY_DURATION, duration * 1000000 / mHeaderTimescale);
}
break;
@@ -2276,7 +2398,8 @@
// for a practical reason as various MPEG4 containers use it.
if (type == FOURCC('t', 'e', 'x', 't') || type == FOURCC('s', 'b', 't', 'l')) {
if (mLastTrack != NULL) {
- mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP);
+ AMediaFormat_setString(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_TEXT_3GPP);
}
}
@@ -2320,11 +2443,10 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- uint32_t type;
- const void *data;
+ void *data;
size_t size = 0;
- if (!mLastTrack->meta.findData(
- kKeyTextFormatData, &type, &data, &size)) {
+ if (!AMediaFormat_getBuffer(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA, &data, &size)) {
size = 0;
}
@@ -2351,8 +2473,8 @@
return ERROR_IO;
}
- mLastTrack->meta.setData(
- kKeyTextFormatData, 0, buffer, size + chunk_size);
+ AMediaFormat_setBuffer(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA, buffer, size + chunk_size);
delete[] buffer;
@@ -2384,8 +2506,8 @@
return ERROR_MALFORMED;
}
- mFileMetaData.setData(
- kKeyAlbumArt, MetaData::TYPE_NONE,
+ AMediaFormat_setBuffer(mFileMetaData,
+ AMEDIAFORMAT_KEY_ALBUMART,
buffer.get() + kSkipBytesOfDataBox, chunk_data_size - kSkipBytesOfDataBox);
break;
@@ -2620,9 +2742,9 @@
return ERROR_MALFORMED;
}
- mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC4);
- mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
- mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
+ AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_AC4);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, channelCount);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sampleRate);
return OK;
}
@@ -2781,9 +2903,9 @@
}
}
}
- mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_EAC3);
- mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
- mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
+ AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_EAC3);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, channelCount);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sampleRate);
delete[] chunk;
return OK;
@@ -2853,9 +2975,9 @@
unsigned lfeon = br.getBits(1);
unsigned channelCount = channelCountTable[acmod] + lfeon;
- mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC3);
- mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
- mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
+ AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_AC3);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, channelCount);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sampleRate);
return OK;
}
@@ -2975,8 +3097,9 @@
return ERROR_MALFORMED;
int64_t metaDuration;
- if (!mLastTrack->meta.findInt64(kKeyDuration, &metaDuration) || metaDuration == 0) {
- mLastTrack->meta.setInt64(kKeyDuration, sidxDuration);
+ if (!AMediaFormat_getInt64(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_DURATION, &metaDuration) || metaDuration == 0) {
+ AMediaFormat_setInt64(mLastTrack->meta, AMEDIAFORMAT_KEY_DURATION, sidxDuration);
}
return OK;
}
@@ -3074,7 +3197,7 @@
return ERROR_MALFORMED;
}
if (!strcasecmp(mMetaKeyMap[index].c_str(), "com.android.capture.fps")) {
- mFileMetaData.setFloat(kKeyCaptureFramerate, *(float *)&val);
+ AMediaFormat_setFloat(mFileMetaData, AMEDIAFORMAT_KEY_CAPTURE_RATE, *(float *)&val);
}
} else if (dataType == 67 && dataSize >= 4) {
// BE signed int32
@@ -3083,7 +3206,8 @@
return ERROR_MALFORMED;
}
if (!strcasecmp(mMetaKeyMap[index].c_str(), "com.android.video.temporal_layers_count")) {
- mFileMetaData.setInt32(kKeyTemporalLayerCount, val);
+ AMediaFormat_setInt32(mFileMetaData,
+ AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT, val);
}
} else {
// add more keys if needed
@@ -3137,7 +3261,7 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- mLastTrack->meta.setInt32(kKeyTrackID, id);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_TRACK_ID, id);
size_t matrixOffset = dynSize + 16;
int32_t a00 = U32_AT(&buffer[matrixOffset]);
@@ -3173,15 +3297,15 @@
}
if (rotationDegrees != 0) {
- mLastTrack->meta.setInt32(kKeyRotation, rotationDegrees);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_ROTATION, rotationDegrees);
}
// Handle presentation display size, which could be different
- // from the image size indicated by kKeyWidth and kKeyHeight.
+ // from the image size indicated by AMEDIAFORMAT_KEY_WIDTH and AMEDIAFORMAT_KEY_HEIGHT.
uint32_t width = U32_AT(&buffer[dynSize + 52]);
uint32_t height = U32_AT(&buffer[dynSize + 56]);
- mLastTrack->meta.setInt32(kKeyDisplayWidth, width >> 16);
- mLastTrack->meta.setInt32(kKeyDisplayHeight, height >> 16);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_DISPLAY_WIDTH, width >> 16);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_DISPLAY_HEIGHT, height >> 16);
return OK;
}
@@ -3209,54 +3333,50 @@
uint32_t flags = U32_AT(buffer);
- uint32_t metadataKey = 0;
+ const char *metadataKey = nullptr;
char chunk[5];
MakeFourCCString(mPath[4], chunk);
ALOGV("meta: %s @ %lld", chunk, (long long)offset);
switch ((int32_t)mPath[4]) {
case FOURCC(0xa9, 'a', 'l', 'b'):
{
- metadataKey = kKeyAlbum;
+ metadataKey = "album";
break;
}
case FOURCC(0xa9, 'A', 'R', 'T'):
{
- metadataKey = kKeyArtist;
+ metadataKey = "artist";
break;
}
case FOURCC('a', 'A', 'R', 'T'):
{
- metadataKey = kKeyAlbumArtist;
+ metadataKey = "albumartist";
break;
}
case FOURCC(0xa9, 'd', 'a', 'y'):
{
- metadataKey = kKeyYear;
+ metadataKey = "year";
break;
}
case FOURCC(0xa9, 'n', 'a', 'm'):
{
- metadataKey = kKeyTitle;
+ metadataKey = "title";
break;
}
case FOURCC(0xa9, 'w', 'r', 't'):
{
- metadataKey = kKeyWriter;
+ metadataKey = "writer";
break;
}
case FOURCC('c', 'o', 'v', 'r'):
{
- metadataKey = kKeyAlbumArt;
+ metadataKey = "albumart";
break;
}
case FOURCC('g', 'n', 'r', 'e'):
- {
- metadataKey = kKeyGenre;
- break;
- }
case FOURCC(0xa9, 'g', 'e', 'n'):
{
- metadataKey = kKeyGenre;
+ metadataKey = "genre";
break;
}
case FOURCC('c', 'p', 'i', 'l'):
@@ -3266,7 +3386,7 @@
sprintf(tmp, "%d",
(int)buffer[size - 1]);
- mFileMetaData.setCString(kKeyCompilation, tmp);
+ AMediaFormat_setString(mFileMetaData, AMEDIAFORMAT_KEY_COMPILATION, tmp);
}
break;
}
@@ -3278,7 +3398,7 @@
uint16_t* pTotalTracks = (uint16_t*)&buffer[12];
sprintf(tmp, "%d/%d", ntohs(*pTrack), ntohs(*pTotalTracks));
- mFileMetaData.setCString(kKeyCDTrackNumber, tmp);
+ AMediaFormat_setString(mFileMetaData, AMEDIAFORMAT_KEY_CDTRACKNUMBER, tmp);
}
break;
}
@@ -3290,7 +3410,7 @@
uint16_t* pTotalDiscs = (uint16_t*)&buffer[12];
sprintf(tmp, "%d/%d", ntohs(*pDisc), ntohs(*pTotalDiscs));
- mFileMetaData.setCString(kKeyDiscNumber, tmp);
+ AMediaFormat_setString(mFileMetaData, AMEDIAFORMAT_KEY_DISCNUMBER, tmp);
}
break;
}
@@ -3334,8 +3454,10 @@
return ERROR_MALFORMED;
}
- mLastTrack->meta.setInt32(kKeyEncoderDelay, delay);
- mLastTrack->meta.setInt32(kKeyEncoderPadding, padding);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_ENCODER_DELAY, delay);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_ENCODER_PADDING, padding);
}
}
@@ -3350,12 +3472,14 @@
break;
}
- if (size >= 8 && metadataKey && !mFileMetaData.hasData(metadataKey)) {
- if (metadataKey == kKeyAlbumArt) {
- mFileMetaData.setData(
- kKeyAlbumArt, MetaData::TYPE_NONE,
+ void *tmpData;
+ size_t tmpDataSize;
+ if (size >= 8 && metadataKey &&
+ !AMediaFormat_getBuffer(mFileMetaData, metadataKey, &tmpData, &tmpDataSize)) {
+ if (!strcmp(metadataKey, "albumart")) {
+ AMediaFormat_setBuffer(mFileMetaData, metadataKey,
buffer + 8, size - 8);
- } else if (metadataKey == kKeyGenre) {
+ } else if (!strcmp(metadataKey, "genre")) {
if (flags == 0) {
// uint8_t genre code, iTunes genre codes are
// the standard id3 codes, except they start
@@ -3369,18 +3493,18 @@
char genre[10];
sprintf(genre, "%d", genrecode);
- mFileMetaData.setCString(metadataKey, genre);
+ AMediaFormat_setString(mFileMetaData, metadataKey, genre);
} else if (flags == 1) {
// custom genre string
buffer[size] = '\0';
- mFileMetaData.setCString(
+ AMediaFormat_setString(mFileMetaData,
metadataKey, (const char *)buffer + 8);
}
} else {
buffer[size] = '\0';
- mFileMetaData.setCString(
+ AMediaFormat_setString(mFileMetaData,
metadataKey, (const char *)buffer + 8);
}
}
@@ -3410,21 +3534,31 @@
int32_t type = U32_AT(&buffer[0]);
if ((type == FOURCC('n', 'c', 'l', 'x') && size >= 11)
|| (type == FOURCC('n', 'c', 'l', 'c') && size >= 10)) {
- int32_t primaries = U16_AT(&buffer[4]);
- int32_t transfer = U16_AT(&buffer[6]);
- int32_t coeffs = U16_AT(&buffer[8]);
- bool fullRange = (type == FOURCC('n', 'c', 'l', 'x')) && (buffer[10] & 128);
-
- ColorAspects aspects;
- ColorUtils::convertIsoColorAspectsToCodecAspects(
- primaries, transfer, coeffs, fullRange, aspects);
-
// only store the first color specification
- if (!mLastTrack->meta.hasData(kKeyColorPrimaries)) {
- mLastTrack->meta.setInt32(kKeyColorPrimaries, aspects.mPrimaries);
- mLastTrack->meta.setInt32(kKeyTransferFunction, aspects.mTransfer);
- mLastTrack->meta.setInt32(kKeyColorMatrix, aspects.mMatrixCoeffs);
- mLastTrack->meta.setInt32(kKeyColorRange, aspects.mRange);
+ int32_t existingColor;
+ if (!AMediaFormat_getInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_COLOR_RANGE, &existingColor)) {
+ int32_t primaries = U16_AT(&buffer[4]);
+ int32_t isotransfer = U16_AT(&buffer[6]);
+ int32_t coeffs = U16_AT(&buffer[8]);
+ bool fullRange = (type == FOURCC('n', 'c', 'l', 'x')) && (buffer[10] & 128);
+
+ int32_t range = 0;
+ int32_t standard = 0;
+ int32_t transfer = 0;
+ ColorUtils::convertIsoColorAspectsToPlatformAspects(
+ primaries, isotransfer, coeffs, fullRange,
+ &range, &standard, &transfer);
+
+ if (range != 0) {
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_COLOR_RANGE, range);
+ }
+ if (standard != 0) {
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_COLOR_STANDARD, standard);
+ }
+ if (transfer != 0) {
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_COLOR_TRANSFER, transfer);
+ }
}
}
@@ -3451,26 +3585,26 @@
return ERROR_IO;
}
- uint32_t metadataKey = 0;
+ const char *metadataKey = nullptr;
switch (mPath[depth]) {
case FOURCC('t', 'i', 't', 'l'):
{
- metadataKey = kKeyTitle;
+ metadataKey = "title";
break;
}
case FOURCC('p', 'e', 'r', 'f'):
{
- metadataKey = kKeyArtist;
+ metadataKey = "artist";
break;
}
case FOURCC('a', 'u', 't', 'h'):
{
- metadataKey = kKeyWriter;
+ metadataKey = "writer";
break;
}
case FOURCC('g', 'n', 'r', 'e'):
{
- metadataKey = kKeyGenre;
+ metadataKey = "genre";
break;
}
case FOURCC('a', 'l', 'b', 'm'):
@@ -3479,10 +3613,10 @@
char tmp[4];
sprintf(tmp, "%u", buffer[size - 1]);
- mFileMetaData.setCString(kKeyCDTrackNumber, tmp);
+ AMediaFormat_setString(mFileMetaData, AMEDIAFORMAT_KEY_CDTRACKNUMBER, tmp);
}
- metadataKey = kKeyAlbum;
+ metadataKey = "album";
break;
}
case FOURCC('y', 'r', 'r', 'c'):
@@ -3500,7 +3634,7 @@
if (year < 10000) {
sprintf(tmp, "%u", year);
- mFileMetaData.setCString(kKeyYear, tmp);
+ AMediaFormat_setString(mFileMetaData, "year", tmp);
}
break;
}
@@ -3509,7 +3643,7 @@
break;
}
- if (metadataKey > 0) {
+ if (metadataKey) {
bool isUTF8 = true; // Common case
char16_t *framedata = NULL;
int len16 = 0; // Number of UTF-16 characters
@@ -3545,11 +3679,11 @@
if (isUTF8) {
buffer[size] = 0;
- mFileMetaData.setCString(metadataKey, (const char *)buffer + 6);
+ AMediaFormat_setString(mFileMetaData, metadataKey, (const char *)buffer + 6);
} else {
// Convert from UTF-16 string to UTF-8 string.
String8 tmpUTF8str(framedata, len16);
- mFileMetaData.setCString(metadataKey, tmpUTF8str.string());
+ AMediaFormat_setString(mFileMetaData, metadataKey, tmpUTF8str.string());
}
}
@@ -3564,27 +3698,29 @@
if (id3.isValid()) {
struct Map {
- int key;
+ const char *key;
const char *tag1;
const char *tag2;
};
static const Map kMap[] = {
- { kKeyAlbum, "TALB", "TAL" },
- { kKeyArtist, "TPE1", "TP1" },
- { kKeyAlbumArtist, "TPE2", "TP2" },
- { kKeyComposer, "TCOM", "TCM" },
- { kKeyGenre, "TCON", "TCO" },
- { kKeyTitle, "TIT2", "TT2" },
- { kKeyYear, "TYE", "TYER" },
- { kKeyAuthor, "TXT", "TEXT" },
- { kKeyCDTrackNumber, "TRK", "TRCK" },
- { kKeyDiscNumber, "TPA", "TPOS" },
- { kKeyCompilation, "TCP", "TCMP" },
+ { AMEDIAFORMAT_KEY_ALBUM, "TALB", "TAL" },
+ { AMEDIAFORMAT_KEY_ARTIST, "TPE1", "TP1" },
+ { AMEDIAFORMAT_KEY_ALBUMARTIST, "TPE2", "TP2" },
+ { AMEDIAFORMAT_KEY_COMPOSER, "TCOM", "TCM" },
+ { AMEDIAFORMAT_KEY_GENRE, "TCON", "TCO" },
+ { AMEDIAFORMAT_KEY_TITLE, "TIT2", "TT2" },
+ { AMEDIAFORMAT_KEY_YEAR, "TYE", "TYER" },
+ { AMEDIAFORMAT_KEY_AUTHOR, "TXT", "TEXT" },
+ { AMEDIAFORMAT_KEY_CDTRACKNUMBER, "TRK", "TRCK" },
+ { AMEDIAFORMAT_KEY_DISCNUMBER, "TPA", "TPOS" },
+ { AMEDIAFORMAT_KEY_COMPILATION, "TCP", "TCMP" },
};
static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
+ void *tmpData;
+ size_t tmpDataSize;
for (size_t i = 0; i < kNumMapEntries; ++i) {
- if (!mFileMetaData.hasData(kMap[i].key)) {
+ if (!AMediaFormat_getBuffer(mFileMetaData, kMap[i].key, &tmpData, &tmpDataSize)) {
ID3::Iterator *it = new ID3::Iterator(id3, kMap[i].tag1);
if (it->done()) {
delete it;
@@ -3600,7 +3736,7 @@
it->getString(&s);
delete it;
- mFileMetaData.setCString(kMap[i].key, s);
+ AMediaFormat_setString(mFileMetaData, kMap[i].key, s);
}
}
@@ -3609,12 +3745,12 @@
const void *data = id3.getAlbumArt(&dataSize, &mime);
if (data) {
- mFileMetaData.setData(kKeyAlbumArt, MetaData::TYPE_NONE, data, dataSize);
+ AMediaFormat_setBuffer(mFileMetaData, AMEDIAFORMAT_KEY_ALBUMART, data, dataSize);
}
}
}
-MediaTrackHelper *MPEG4Extractor::getTrack(size_t index) {
+MediaTrackHelperV2 *MPEG4Extractor::getTrack(size_t index) {
status_t err;
if ((err = readMetaData()) != OK) {
return NULL;
@@ -3637,7 +3773,7 @@
Trex *trex = NULL;
int32_t trackId;
- if (track->meta.findInt32(kKeyTrackID, &trackId)) {
+ if (AMediaFormat_getInt32(track->meta, AMEDIAFORMAT_KEY_TRACK_ID, &trackId)) {
for (size_t i = 0; i < mTrex.size(); i++) {
Trex *t = &mTrex.editItemAt(i);
if (t->track_ID == (uint32_t) trackId) {
@@ -3653,16 +3789,15 @@
ALOGV("getTrack called, pssh: %zu", mPssh.size());
const char *mime;
- if (!track->meta.findCString(kKeyMIMEType, &mime)) {
+ if (!AMediaFormat_getString(track->meta, AMEDIAFORMAT_KEY_MIME, &mime)) {
return NULL;
}
sp<ItemTable> itemTable;
if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
- uint32_t type;
- const void *data;
+ void *data;
size_t size;
- if (!track->meta.findData(kKeyAVCC, &type, &data, &size)) {
+ if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_AVC, &data, &size)) {
return NULL;
}
@@ -3673,10 +3808,9 @@
}
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)
|| !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) {
- uint32_t type;
- const void *data;
+ void *data;
size_t size;
- if (!track->meta.findData(kKeyHVCC, &type, &data, &size)) {
+ if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_HEVC, &data, &size)) {
return NULL;
}
@@ -3703,26 +3837,22 @@
// static
status_t MPEG4Extractor::verifyTrack(Track *track) {
const char *mime;
- CHECK(track->meta.findCString(kKeyMIMEType, &mime));
+ CHECK(AMediaFormat_getString(track->meta, AMEDIAFORMAT_KEY_MIME, &mime));
- uint32_t type;
- const void *data;
+ void *data;
size_t size;
if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
- if (!track->meta.findData(kKeyAVCC, &type, &data, &size)
- || type != kTypeAVCC) {
+ if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_AVC, &data, &size)) {
return ERROR_MALFORMED;
}
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) {
- if (!track->meta.findData(kKeyHVCC, &type, &data, &size)
- || type != kTypeHVCC) {
+ if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_HEVC, &data, &size)) {
return ERROR_MALFORMED;
}
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
|| !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG2)
|| !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
- if (!track->meta.findData(kKeyESDS, &type, &data, &size)
- || type != kTypeESDS) {
+ if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_ESDS, &data, &size)) {
return ERROR_MALFORMED;
}
}
@@ -3807,7 +3937,7 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_QCELP);
+ AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_QCELP);
return OK;
}
@@ -3824,10 +3954,12 @@
uint32_t avgBitrate = 0;
esds.getBitRate(&maxBitrate, &avgBitrate);
if (maxBitrate > 0 && maxBitrate < INT32_MAX) {
- mLastTrack->meta.setInt32(kKeyMaxBitRate, (int32_t)maxBitrate);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_MAX_BIT_RATE, (int32_t)maxBitrate);
}
if (avgBitrate > 0 && avgBitrate < INT32_MAX) {
- mLastTrack->meta.setInt32(kKeyBitRate, (int32_t)avgBitrate);
+ AMediaFormat_setInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)avgBitrate);
}
}
@@ -3871,7 +4003,7 @@
return ERROR_MALFORMED;
//keep AOT type
- mLastTrack->meta.setInt32(kKeyAACAOT, objectType);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_AAC_PROFILE, objectType);
uint32_t freqIndex = br.getBits(4);
@@ -3892,7 +4024,7 @@
sampleRate = kSamplingRate[freqIndex];
}
- if (objectType == AOT_SBR || objectType == AOT_PS) {//SBR specific config per 14496-3 table 1.13
+ if (objectType == AOT_SBR || objectType == AOT_PS) {//SBR specific config per 14496-3 tbl 1.13
if (br.numBitsLeft() < 4) return ERROR_MALFORMED;
uint32_t extFreqIndex = br.getBits(4);
int32_t extSampleRate __unused;
@@ -3909,7 +4041,7 @@
extSampleRate = kSamplingRate[extFreqIndex];
}
//TODO: save the extension sampling rate value in meta data =>
- // mLastTrack->meta.setInt32(kKeyExtSampleRate, extSampleRate);
+ // AMediaFormat_setInt32(mLastTrack->meta, kKeyExtSampleRate, extSampleRate);
}
switch (numChannels) {
@@ -4062,24 +4194,25 @@
return ERROR_MALFORMED;
int32_t prevSampleRate;
- CHECK(mLastTrack->meta.findInt32(kKeySampleRate, &prevSampleRate));
+ CHECK(AMediaFormat_getInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, &prevSampleRate));
if (prevSampleRate != sampleRate) {
ALOGV("mpeg4 audio sample rate different from previous setting. "
"was: %d, now: %d", prevSampleRate, sampleRate);
}
- mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sampleRate);
int32_t prevChannelCount;
- CHECK(mLastTrack->meta.findInt32(kKeyChannelCount, &prevChannelCount));
+ CHECK(AMediaFormat_getInt32(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_CHANNEL_COUNT, &prevChannelCount));
if (prevChannelCount != numChannels) {
ALOGV("mpeg4 audio channel count different from previous setting. "
"was: %d, now: %d", prevChannelCount, numChannels);
}
- mLastTrack->meta.setInt32(kKeyChannelCount, numChannels);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, numChannels);
return OK;
}
@@ -4087,7 +4220,7 @@
////////////////////////////////////////////////////////////////////////////////
MPEG4Source::MPEG4Source(
- MetaDataBase &format,
+ AMediaFormat *format,
DataSourceHelper *dataSource,
int32_t timeScale,
const sp<SampleTable> &sampleTable,
@@ -4127,23 +4260,27 @@
memset(&mTrackFragmentHeaderInfo, 0, sizeof(mTrackFragmentHeaderInfo));
- mFormat.findInt32(kKeyCryptoMode, &mCryptoMode);
+ AMediaFormat_getInt32(mFormat,
+ AMEDIAFORMAT_KEY_CRYPTO_MODE, &mCryptoMode);
mDefaultIVSize = 0;
- mFormat.findInt32(kKeyCryptoDefaultIVSize, &mDefaultIVSize);
- uint32_t keytype;
- const void *key;
+ AMediaFormat_getInt32(mFormat,
+ AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE, &mDefaultIVSize);
+ void *key;
size_t keysize;
- if (mFormat.findData(kKeyCryptoKey, &keytype, &key, &keysize)) {
+ if (AMediaFormat_getBuffer(mFormat,
+ AMEDIAFORMAT_KEY_CRYPTO_KEY, &key, &keysize)) {
CHECK(keysize <= 16);
memset(mCryptoKey, 0, 16);
memcpy(mCryptoKey, key, keysize);
}
- mFormat.findInt32(kKeyEncryptedByteBlock, &mDefaultEncryptedByteBlock);
- mFormat.findInt32(kKeySkipByteBlock, &mDefaultSkipByteBlock);
+ AMediaFormat_getInt32(mFormat,
+ AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK, &mDefaultEncryptedByteBlock);
+ AMediaFormat_getInt32(mFormat,
+ AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK, &mDefaultSkipByteBlock);
const char *mime;
- bool success = mFormat.findCString(kKeyMIMEType, &mime);
+ bool success = AMediaFormat_getString(mFormat, AMEDIAFORMAT_KEY_MIME, &mime);
CHECK(success);
mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
@@ -4152,10 +4289,9 @@
mIsAC4 = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC4);
if (mIsAVC) {
- uint32_t type;
- const void *data;
+ void *data;
size_t size;
- CHECK(format.findData(kKeyAVCC, &type, &data, &size));
+ CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_AVC, &data, &size));
const uint8_t *ptr = (const uint8_t *)data;
@@ -4165,10 +4301,9 @@
// The number of bytes used to encode the length of a NAL unit.
mNALLengthSize = 1 + (ptr[4] & 3);
} else if (mIsHEVC) {
- uint32_t type;
- const void *data;
+ void *data;
size_t size;
- CHECK(format.findData(kKeyHVCC, &type, &data, &size));
+ CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_HEVC, &data, &size));
const uint8_t *ptr = (const uint8_t *)data;
@@ -4183,23 +4318,25 @@
if (mIsPcm) {
int32_t numChannels = 0;
int32_t bitsPerSample = 0;
- CHECK(mFormat.findInt32(kKeyBitsPerSample, &bitsPerSample));
- CHECK(mFormat.findInt32(kKeyChannelCount, &numChannels));
+ CHECK(AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, &bitsPerSample));
+ CHECK(AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &numChannels));
int32_t bytesPerSample = bitsPerSample >> 3;
int32_t pcmSampleSize = bytesPerSample * numChannels;
size_t maxSampleSize;
status_t err = mSampleTable->getMaxSampleSize(&maxSampleSize);
- if (err != OK || maxSampleSize != static_cast<size_t>(pcmSampleSize) || bitsPerSample != 16) {
+ if (err != OK || maxSampleSize != static_cast<size_t>(pcmSampleSize)
+ || bitsPerSample != 16) {
// Not supported
mIsPcm = false;
} else {
- mFormat.setInt32(kKeyMaxInputSize, pcmSampleSize * kMaxPcmFrameSize);
+ AMediaFormat_setInt32(mFormat,
+ AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, pcmSampleSize * kMaxPcmFrameSize);
}
}
- CHECK(format.findInt32(kKeyTrackID, &mTrackId));
+ CHECK(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_TRACK_ID, &mTrackId));
}
@@ -4219,13 +4356,13 @@
free(mCurrentSampleInfoOffsets);
}
-status_t MPEG4Source::start() {
+media_status_t MPEG4Source::start() {
Mutex::Autolock autoLock(mLock);
CHECK(!mStarted);
int32_t tmp;
- CHECK(mFormat.findInt32(kKeyMaxInputSize, &tmp));
+ CHECK(AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, &tmp));
size_t max_size = tmp;
// A somewhat arbitrary limit that should be sufficient for 8k video frames
@@ -4233,11 +4370,11 @@
const size_t kMaxBufferSize = 64 * 1024 * 1024;
if (max_size > kMaxBufferSize) {
ALOGE("bogus max input size: %zu > %zu", max_size, kMaxBufferSize);
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
if (max_size == 0) {
ALOGE("zero max input size");
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
// Allow up to kMaxBuffers, but not if the total exceeds kMaxBufferSize.
@@ -4250,15 +4387,15 @@
// file probably specified a bad max size
delete mGroup;
mGroup = NULL;
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
mStarted = true;
- return OK;
+ return AMEDIA_OK;
}
-status_t MPEG4Source::stop() {
+media_status_t MPEG4Source::stop() {
Mutex::Autolock autoLock(mLock);
CHECK(mStarted);
@@ -4277,7 +4414,7 @@
mStarted = false;
mCurrentSampleIndex = 0;
- return OK;
+ return AMEDIA_OK;
}
status_t MPEG4Source::parseChunk(off64_t *offset) {
@@ -4395,7 +4532,8 @@
}
case FOURCC('s', 'a', 'i', 'o'): {
status_t err;
- if ((err = parseSampleAuxiliaryInformationOffsets(data_offset, chunk_data_size)) != OK) {
+ if ((err = parseSampleAuxiliaryInformationOffsets(data_offset, chunk_data_size))
+ != OK) {
return err;
}
*offset += chunk_size;
@@ -4526,7 +4664,8 @@
if (entrycount > mCurrentSampleInfoOffsetsAllocSize) {
uint64_t *newPtr = (uint64_t *)realloc(mCurrentSampleInfoOffsets, entrycount * 8);
if (newPtr == NULL) {
- ALOGE("failed to realloc %u -> %u", mCurrentSampleInfoOffsetsAllocSize, entrycount * 8);
+ ALOGE("failed to realloc %u -> %u",
+ mCurrentSampleInfoOffsetsAllocSize, entrycount * 8);
return NO_MEMORY;
}
mCurrentSampleInfoOffsets = newPtr;
@@ -4565,10 +4704,11 @@
return parseClearEncryptedSizes(drmoffset, false, 0);
}
-status_t MPEG4Source::parseClearEncryptedSizes(off64_t offset, bool isSubsampleEncryption, uint32_t flags) {
+status_t MPEG4Source::parseClearEncryptedSizes(
+ off64_t offset, bool isSubsampleEncryption, uint32_t flags) {
- int ivlength;
- CHECK(mFormat.findInt32(kKeyCryptoDefaultIVSize, &ivlength));
+ int32_t ivlength;
+ CHECK(AMediaFormat_getInt32(mFormat, "crypto-defaultivsize", &ivlength));
// only 0, 8 and 16 byte initialization vectors are supported
if (ivlength != 0 && ivlength != 8 && ivlength != 16) {
@@ -4912,10 +5052,10 @@
return OK;
}
-status_t MPEG4Source::getFormat(MetaDataBase &meta) {
+media_status_t MPEG4Source::getFormat(AMediaFormat *meta) {
Mutex::Autolock autoLock(mLock);
- meta = mFormat;
- return OK;
+ AMediaFormat_copy(meta, mFormat);
+ return AMEDIA_OK;
}
size_t MPEG4Source::parseNALSize(const uint8_t *data) const {
@@ -4966,7 +5106,7 @@
return 0;
}
-status_t MPEG4Source::read(
+media_status_t MPEG4Source::read(
MediaBufferBase **out, const ReadOptions *options) {
Mutex::Autolock autoLock(mLock);
@@ -4974,7 +5114,7 @@
if (options != nullptr && options->getNonBlocking() && !mGroup->has_buffers()) {
*out = nullptr;
- return WOULD_BLOCK;
+ return AMEDIA_ERROR_WOULD_BLOCK;
}
if (mFirstMoofOffset > 0) {
@@ -4992,8 +5132,8 @@
CHECK(mSampleTable == NULL);
CHECK(mItemTable != NULL);
int32_t imageIndex;
- if (!mFormat.findInt32(kKeyTrackID, &imageIndex)) {
- return ERROR_MALFORMED;
+ if (!AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_TRACK_ID, &imageIndex)) {
+ return AMEDIA_ERROR_MALFORMED;
}
status_t err;
@@ -5003,7 +5143,7 @@
err = mItemTable->findThumbnailItem(imageIndex, &mCurrentSampleIndex);
}
if (err != OK) {
- return err;
+ return AMEDIA_ERROR_UNKNOWN;
}
} else {
uint32_t findFlags = 0;
@@ -5058,10 +5198,10 @@
// this all the way to the MediaPlayer would cause abnormal
// termination. Legacy behaviour appears to be to behave as if
// we had seeked to the end of stream, ending normally.
- err = ERROR_END_OF_STREAM;
+ return AMEDIA_ERROR_END_OF_STREAM;
}
ALOGV("end of stream");
- return err;
+ return AMEDIA_ERROR_UNKNOWN;
}
if (mode == ReadOptions::SEEK_CLOSEST
@@ -5115,20 +5255,23 @@
}
if (err != OK) {
- return err;
+ if (err == ERROR_END_OF_STREAM) {
+ return AMEDIA_ERROR_END_OF_STREAM;
+ }
+ return AMEDIA_ERROR_UNKNOWN;
}
err = mGroup->acquire_buffer(&mBuffer);
if (err != OK) {
CHECK(mBuffer == NULL);
- return err;
+ return AMEDIA_ERROR_UNKNOWN;
}
if (size > mBuffer->size()) {
ALOGE("buffer too small: %zu > %zu", size, mBuffer->size());
mBuffer->release();
mBuffer = NULL;
- return ERROR_BUFFER_TOO_SMALL;
+ return AMEDIA_ERROR_UNKNOWN; // ERROR_BUFFER_TOO_SMALL
}
}
@@ -5154,7 +5297,7 @@
mBuffer->release();
mBuffer = NULL;
- return ERROR_IO;
+ return AMEDIA_ERROR_IO;
}
mBuffer->meta_data().clear();
@@ -5162,7 +5305,8 @@
mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1);
int32_t byteOrder;
- mFormat.findInt32(kKeyPcmBigEndian, &byteOrder);
+ AMediaFormat_getInt32(mFormat,
+ AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN, &byteOrder);
if (byteOrder == 1) {
// Big-endian -> little-endian
@@ -5184,7 +5328,7 @@
mBuffer->release();
mBuffer = NULL;
- return ERROR_IO;
+ return AMEDIA_ERROR_IO;
}
CHECK(mBuffer != NULL);
@@ -5212,14 +5356,14 @@
*out = mBuffer;
mBuffer = NULL;
- return OK;
+ return AMEDIA_OK;
}
if (mIsAC4) {
mBuffer->release();
mBuffer = NULL;
- return ERROR_IO;
+ return AMEDIA_ERROR_IO;
}
// Each NAL unit is split up into its constituent fragments and
@@ -5240,7 +5384,7 @@
mBuffer->release();
mBuffer = NULL;
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
MediaBufferBase *clone = mBuffer->clone();
@@ -5259,7 +5403,7 @@
*out = clone;
- return OK;
+ return AMEDIA_OK;
} else if (mIsAC4) {
CHECK(mBuffer != NULL);
// Make sure there is enough space to write the sync header and the raw frame
@@ -5267,7 +5411,7 @@
mBuffer->release();
mBuffer = NULL;
- return ERROR_IO;
+ return AMEDIA_ERROR_IO;
}
uint8_t *dstData = (uint8_t *)mBuffer->data();
@@ -5287,7 +5431,7 @@
mBuffer->release();
mBuffer = NULL;
- return ERROR_IO;
+ return AMEDIA_ERROR_IO;
}
mBuffer->set_range(0, dstOffset + size);
@@ -5311,7 +5455,7 @@
*out = mBuffer;
mBuffer = NULL;
- return OK;
+ return AMEDIA_OK;
} else {
// Whole NAL units are returned but each fragment is prefixed by
// the start code (0x00 00 00 01).
@@ -5322,7 +5466,7 @@
mBuffer->release();
mBuffer = NULL;
- return ERROR_IO;
+ return AMEDIA_ERROR_IO;
}
uint8_t *dstData = (uint8_t *)mBuffer->data();
@@ -5342,7 +5486,7 @@
ALOGE("Video is malformed");
mBuffer->release();
mBuffer = NULL;
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
if (nalLength == 0) {
@@ -5356,7 +5500,7 @@
android_errorWriteLog(0x534e4554, "27208621");
mBuffer->release();
mBuffer = NULL;
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
dstData[dstOffset++] = 0;
@@ -5403,11 +5547,11 @@
*out = mBuffer;
mBuffer = NULL;
- return OK;
+ return AMEDIA_OK;
}
}
-status_t MPEG4Source::fragmentedRead(
+media_status_t MPEG4Source::fragmentedRead(
MediaBufferBase **out, const ReadOptions *options) {
ALOGV("MPEG4Source::fragmentedRead");
@@ -5449,7 +5593,7 @@
mCurrentSampleIndex = 0;
status_t err = parseChunk(&totalOffset);
if (err != OK) {
- return err;
+ return AMEDIA_ERROR_UNKNOWN;
}
mCurrentTime = totalTime * mTimescale / 1000000ll;
} else {
@@ -5461,7 +5605,7 @@
off64_t tmp = mCurrentMoofOffset;
status_t err = parseChunk(&tmp);
if (err != OK) {
- return err;
+ return AMEDIA_ERROR_UNKNOWN;
}
mCurrentTime = 0;
}
@@ -5489,7 +5633,7 @@
if (mCurrentSampleIndex >= mCurrentSamples.size()) {
// move to next fragment if there is one
if (mNextMoofOffset <= mCurrentMoofOffset) {
- return ERROR_END_OF_STREAM;
+ return AMEDIA_ERROR_END_OF_STREAM;
}
off64_t nextMoof = mNextMoofOffset;
mCurrentMoofOffset = nextMoof;
@@ -5497,10 +5641,10 @@
mCurrentSampleIndex = 0;
status_t err = parseChunk(&nextMoof);
if (err != OK) {
- return err;
+ return AMEDIA_ERROR_UNKNOWN;
}
if (mCurrentSampleIndex >= mCurrentSamples.size()) {
- return ERROR_END_OF_STREAM;
+ return AMEDIA_ERROR_END_OF_STREAM;
}
}
@@ -5509,20 +5653,20 @@
size = smpl->size;
cts = mCurrentTime + smpl->compositionOffset;
mCurrentTime += smpl->duration;
- isSyncSample = (mCurrentSampleIndex == 0); // XXX
+ isSyncSample = (mCurrentSampleIndex == 0);
status_t err = mGroup->acquire_buffer(&mBuffer);
if (err != OK) {
CHECK(mBuffer == NULL);
ALOGV("acquire_buffer returned %d", err);
- return err;
+ return AMEDIA_ERROR_UNKNOWN;
}
if (size > mBuffer->size()) {
ALOGE("buffer too small: %zu > %zu", size, mBuffer->size());
mBuffer->release();
mBuffer = NULL;
- return ERROR_BUFFER_TOO_SMALL;
+ return AMEDIA_ERROR_UNKNOWN;
}
}
@@ -5541,12 +5685,11 @@
bufmeta.setInt32(kKeyEncryptedByteBlock, mDefaultEncryptedByteBlock);
bufmeta.setInt32(kKeySkipByteBlock, mDefaultSkipByteBlock);
- uint32_t type = 0;
- const void *iv = NULL;
+ void *iv = NULL;
size_t ivlength = 0;
- if (!mFormat.findData(
- kKeyCryptoIV, &type, &iv, &ivlength)) {
- iv = smpl->iv;
+ if (!AMediaFormat_getBuffer(mFormat,
+ "crypto-iv", &iv, &ivlength)) {
+ iv = (void *) smpl->iv;
ivlength = 16; // use 16 or the actual size?
}
bufmeta.setData(kKeyCryptoIV, 0, iv, ivlength);
@@ -5560,7 +5703,7 @@
mBuffer = NULL;
ALOGE("fragmentedRead ERROR_MALFORMED size %zu", size);
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
ssize_t num_bytes_read =
@@ -5571,7 +5714,7 @@
mBuffer = NULL;
ALOGE("i/o error");
- return ERROR_IO;
+ return AMEDIA_ERROR_IO;
}
CHECK(mBuffer != NULL);
@@ -5609,7 +5752,7 @@
*out = mBuffer;
mBuffer = NULL;
- return OK;
+ return AMEDIA_OK;
}
// Each NAL unit is split up into its constituent fragments and
@@ -5631,7 +5774,7 @@
mBuffer->release();
mBuffer = NULL;
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
MediaBufferBase *clone = mBuffer->clone();
@@ -5650,7 +5793,7 @@
*out = clone;
- return OK;
+ return AMEDIA_OK;
} else {
ALOGV("whole NAL");
// Whole NAL units are returned but each fragment is prefixed by
@@ -5659,7 +5802,7 @@
void *data = NULL;
bool isMalFormed = false;
int32_t max_size;
- if (!mFormat.findInt32(kKeyMaxInputSize, &max_size)
+ if (!AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, &max_size)
|| !isInRange((size_t)0u, (size_t)max_size, size)) {
isMalFormed = true;
} else {
@@ -5672,7 +5815,7 @@
mBuffer->release();
mBuffer = NULL;
}
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
num_bytes_read = mDataSource->readAt(offset, data, size);
@@ -5681,7 +5824,7 @@
mBuffer = NULL;
ALOGE("i/o error");
- return ERROR_IO;
+ return AMEDIA_ERROR_IO;
}
uint8_t *dstData = (uint8_t *)mBuffer->data();
@@ -5703,7 +5846,7 @@
ALOGE("Video is malformed; nalLength %zu", nalLength);
mBuffer->release();
mBuffer = NULL;
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
if (nalLength == 0) {
@@ -5717,7 +5860,7 @@
android_errorWriteLog(0x534e4554, "26365349");
mBuffer->release();
mBuffer = NULL;
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
dstData[dstOffset++] = 0;
@@ -5751,17 +5894,17 @@
*out = mBuffer;
mBuffer = NULL;
- return OK;
+ return AMEDIA_OK;
}
- return OK;
+ return AMEDIA_OK;
}
MPEG4Extractor::Track *MPEG4Extractor::findTrackByMimePrefix(
const char *mimePrefix) {
for (Track *track = mFirstTrack; track != NULL; track = track->next) {
const char *mime;
- if (track->meta.findCString(kKeyMIMEType, &mime)
+ if (AMediaFormat_getString(track->meta, AMEDIAFORMAT_KEY_MIME, &mime)
&& !strncasecmp(mime, mimePrefix, strlen(mimePrefix))) {
return track;
}
@@ -5881,7 +6024,8 @@
char chunkstring[5];
MakeFourCCString(chunkType, chunkstring);
- ALOGV("saw chunk type %s, size %" PRIu64 " @ %lld", chunkstring, chunkSize, (long long)offset);
+ ALOGV("saw chunk type %s, size %" PRIu64 " @ %lld",
+ chunkstring, chunkSize, (long long)offset);
switch (chunkType) {
case FOURCC('f', 't', 'y', 'p'):
{
@@ -5942,11 +6086,11 @@
return true;
}
-static CMediaExtractor* CreateExtractor(CDataSource *source, void *) {
- return wrap(new MPEG4Extractor(new DataSourceHelper(source)));
+static CMediaExtractorV2* CreateExtractor(CDataSource *source, void *) {
+ return wrapV2(new MPEG4Extractor(new DataSourceHelper(source)));
}
-static CreatorFunc Sniff(
+static CreatorFuncV2 Sniff(
CDataSource *source, float *confidence, void **,
FreeMetaFunc *) {
DataSourceHelper helper(source);
@@ -5967,11 +6111,11 @@
__attribute__ ((visibility ("default")))
ExtractorDef GETEXTRACTORDEF() {
return {
- EXTRACTORDEF_VERSION,
+ EXTRACTORDEF_VERSION_CURRENT,
UUID("27575c67-4417-4c54-8d3d-8e626985a164"),
- 1, // version
+ 2, // version
"MP4 Extractor",
- { Sniff }
+ { .v2 = Sniff }
};
}
diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h
index 9b8de20..56b641d 100644
--- a/media/extractors/mp4/MPEG4Extractor.h
+++ b/media/extractors/mp4/MPEG4Extractor.h
@@ -22,7 +22,7 @@
#include <media/MediaExtractorPluginApi.h>
#include <media/MediaExtractorPluginHelper.h>
-#include <media/stagefright/MetaDataBase.h>
+#include <media/NdkMediaFormat.h>
#include <media/stagefright/foundation/AString.h>
#include <utils/KeyedVector.h>
#include <utils/List.h>
@@ -53,15 +53,15 @@
uint32_t default_sample_flags;
};
-class MPEG4Extractor : public MediaExtractorPluginHelper {
+class MPEG4Extractor : public MediaExtractorPluginHelperV2 {
public:
explicit MPEG4Extractor(DataSourceHelper *source, const char *mime = NULL);
virtual size_t countTracks();
- virtual MediaTrackHelper *getTrack(size_t index);
- virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags);
+ virtual MediaTrackHelperV2 *getTrack(size_t index);
+ virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags);
- virtual status_t getMetaData(MetaDataBase& meta);
+ virtual media_status_t getMetaData(AMediaFormat *meta);
virtual uint32_t flags() const;
virtual const char * name() { return "MPEG4Extractor"; }
@@ -77,7 +77,7 @@
};
struct Track {
Track *next;
- MetaDataBase meta;
+ AMediaFormat *meta;
uint32_t timescale;
sp<SampleTable> sampleTable;
bool includes_expensive_metadata;
@@ -107,7 +107,7 @@
Track *mFirstTrack, *mLastTrack;
- MetaDataBase mFileMetaData;
+ AMediaFormat *mFileMetaData;
Vector<uint32_t> mPath;
String8 mLastCommentMean;
diff --git a/media/img_utils/include/img_utils/DngUtils.h b/media/img_utils/include/img_utils/DngUtils.h
index de8f120..8819f87 100644
--- a/media/img_utils/include/img_utils/DngUtils.h
+++ b/media/img_utils/include/img_utils/DngUtils.h
@@ -49,6 +49,7 @@
CFA_RGGB,
CFA_BGGR,
CFA_GBRG,
+ CFA_NONE,
};
OpcodeListBuilder();
@@ -89,7 +90,6 @@
CfaLayout cfa,
const float* lensShadingMap);
-
/**
* Add a GainMap opcode with the given fields. The mapGains array
* must have mapPointsV * mapPointsH * mapPlanes elements.
@@ -197,6 +197,33 @@
status_t addOpcodePreamble(uint32_t opcodeId);
+ private:
+ /**
+ * Add Bayer GainMap opcode(s) for the given metadata parameters.
+ * CFA layout must match the layout of the shading map passed into the
+ * lensShadingMap parameter.
+ *
+ * Returns OK on success, or a negative error code.
+ */
+ status_t addBayerGainMapsForMetadata(uint32_t lsmWidth,
+ uint32_t lsmHeight,
+ uint32_t activeAreaWidth,
+ uint32_t activeAreaHeight,
+ CfaLayout cfa,
+ const float* lensShadingMap);
+
+ /**
+ * Add Bayer GainMap opcode(s) for the given metadata parameters.
+ * CFA layout must match the layout of the shading map passed into the
+ * lensShadingMap parameter.
+ *
+ * Returns OK on success, or a negative error code.
+ */
+ status_t addMonochromeGainMapsForMetadata(uint32_t lsmWidth,
+ uint32_t lsmHeight,
+ uint32_t activeAreaWidth,
+ uint32_t activeAreaHeight,
+ const float* lensShadingMap);
};
} /*namespace img_utils*/
diff --git a/media/img_utils/src/DngUtils.cpp b/media/img_utils/src/DngUtils.cpp
index 9ac7e2a..9304f53 100644
--- a/media/img_utils/src/DngUtils.cpp
+++ b/media/img_utils/src/DngUtils.cpp
@@ -60,34 +60,36 @@
uint32_t activeAreaRight,
CfaLayout cfa,
const float* lensShadingMap) {
+ status_t err = OK;
uint32_t activeAreaWidth = activeAreaRight - activeAreaLeft;
uint32_t activeAreaHeight = activeAreaBottom - activeAreaTop;
- double spacingV = 1.0 / std::max(1u, lsmHeight - 1);
- double spacingH = 1.0 / std::max(1u, lsmWidth - 1);
- std::vector<float> redMapVector(lsmWidth * lsmHeight);
- float *redMap = redMapVector.data();
-
- std::vector<float> greenEvenMapVector(lsmWidth * lsmHeight);
- float *greenEvenMap = greenEvenMapVector.data();
-
- std::vector<float> greenOddMapVector(lsmWidth * lsmHeight);
- float *greenOddMap = greenOddMapVector.data();
-
- std::vector<float> blueMapVector(lsmWidth * lsmHeight);
- float *blueMap = blueMapVector.data();
-
- size_t lsmMapSize = lsmWidth * lsmHeight * 4;
-
- // Split lens shading map channels into separate arrays
- size_t j = 0;
- for (size_t i = 0; i < lsmMapSize; i += 4, ++j) {
- redMap[j] = lensShadingMap[i + LSM_R_IND];
- greenEvenMap[j] = lensShadingMap[i + LSM_GE_IND];
- greenOddMap[j] = lensShadingMap[i + LSM_GO_IND];
- blueMap[j] = lensShadingMap[i + LSM_B_IND];
+ switch (cfa) {
+ case CFA_RGGB:
+ case CFA_GRBG:
+ case CFA_GBRG:
+ case CFA_BGGR:
+ err = addBayerGainMapsForMetadata(lsmWidth, lsmHeight, activeAreaWidth,
+ activeAreaHeight, cfa, lensShadingMap);
+ break;
+ case CFA_NONE:
+ err = addMonochromeGainMapsForMetadata(lsmWidth, lsmHeight, activeAreaWidth,
+ activeAreaHeight, lensShadingMap);
+ break;
+ default:
+ ALOGE("%s: Unknown CFA layout %d", __FUNCTION__, cfa);
+ err = BAD_VALUE;
+ break;
}
+ return err;
+}
+status_t OpcodeListBuilder::addBayerGainMapsForMetadata(uint32_t lsmWidth,
+ uint32_t lsmHeight,
+ uint32_t activeAreaWidth,
+ uint32_t activeAreaHeight,
+ CfaLayout cfa,
+ const float* lensShadingMap) {
uint32_t redTop = 0;
uint32_t redLeft = 0;
uint32_t greenEvenTop = 0;
@@ -143,6 +145,32 @@
return BAD_VALUE;
}
+ std::vector<float> redMapVector(lsmWidth * lsmHeight);
+ float *redMap = redMapVector.data();
+
+ std::vector<float> greenEvenMapVector(lsmWidth * lsmHeight);
+ float *greenEvenMap = greenEvenMapVector.data();
+
+ std::vector<float> greenOddMapVector(lsmWidth * lsmHeight);
+ float *greenOddMap = greenOddMapVector.data();
+
+ std::vector<float> blueMapVector(lsmWidth * lsmHeight);
+ float *blueMap = blueMapVector.data();
+
+ double spacingV = 1.0 / std::max(1u, lsmHeight - 1);
+ double spacingH = 1.0 / std::max(1u, lsmWidth - 1);
+
+ size_t lsmMapSize = lsmWidth * lsmHeight * 4;
+
+ // Split lens shading map channels into separate arrays
+ size_t j = 0;
+ for (size_t i = 0; i < lsmMapSize; i += 4, ++j) {
+ redMap[j] = lensShadingMap[i + LSM_R_IND];
+ greenEvenMap[j] = lensShadingMap[i + LSM_GE_IND];
+ greenOddMap[j] = lensShadingMap[i + LSM_GO_IND];
+ blueMap[j] = lensShadingMap[i + LSM_B_IND];
+ }
+
status_t err = addGainMap(/*top*/redTop,
/*left*/redLeft,
/*bottom*/activeAreaHeight - 1,
@@ -216,6 +244,46 @@
return err;
}
+status_t OpcodeListBuilder::addMonochromeGainMapsForMetadata(uint32_t lsmWidth,
+ uint32_t lsmHeight,
+ uint32_t activeAreaWidth,
+ uint32_t activeAreaHeight,
+ const float* lensShadingMap) {
+ std::vector<float> mapVector(lsmWidth * lsmHeight);
+ float *map = mapVector.data();
+
+ double spacingV = 1.0 / std::max(1u, lsmHeight - 1);
+ double spacingH = 1.0 / std::max(1u, lsmWidth - 1);
+
+ size_t lsmMapSize = lsmWidth * lsmHeight * 4;
+
+ // Split lens shading map channels into separate arrays
+ size_t j = 0;
+ for (size_t i = 0; i < lsmMapSize; i += 4, ++j) {
+ map[j] = lensShadingMap[i];
+ }
+
+ status_t err = addGainMap(/*top*/0,
+ /*left*/0,
+ /*bottom*/activeAreaHeight - 1,
+ /*right*/activeAreaWidth - 1,
+ /*plane*/0,
+ /*planes*/1,
+ /*rowPitch*/1,
+ /*colPitch*/1,
+ /*mapPointsV*/lsmHeight,
+ /*mapPointsH*/lsmWidth,
+ /*mapSpacingV*/spacingV,
+ /*mapSpacingH*/spacingH,
+ /*mapOriginV*/0,
+ /*mapOriginH*/0,
+ /*mapPlanes*/1,
+ /*mapGains*/map);
+ if (err != OK) return err;
+
+ return err;
+}
+
status_t OpcodeListBuilder::addGainMap(uint32_t top,
uint32_t left,
uint32_t bottom,
diff --git a/media/libaaudio/examples/input_monitor/Android.bp b/media/libaaudio/examples/input_monitor/Android.bp
index d8c5843..5d399b5 100644
--- a/media/libaaudio/examples/input_monitor/Android.bp
+++ b/media/libaaudio/examples/input_monitor/Android.bp
@@ -5,6 +5,7 @@
cflags: ["-Wall", "-Werror"],
shared_libs: ["libaaudio"],
header_libs: ["libaaudio_example_utils"],
+ pack_relocations: false,
}
cc_test {
@@ -14,4 +15,5 @@
cflags: ["-Wall", "-Werror"],
shared_libs: ["libaaudio"],
header_libs: ["libaaudio_example_utils"],
+ pack_relocations: false,
}
diff --git a/media/libaaudio/examples/loopback/Android.bp b/media/libaaudio/examples/loopback/Android.bp
index 5b7d956..53e5020 100644
--- a/media/libaaudio/examples/loopback/Android.bp
+++ b/media/libaaudio/examples/loopback/Android.bp
@@ -9,4 +9,5 @@
"libaudioutils",
],
header_libs: ["libaaudio_example_utils"],
+ pack_relocations: false,
}
diff --git a/media/libaaudio/examples/write_sine/Android.bp b/media/libaaudio/examples/write_sine/Android.bp
index aa25e67..cc80861 100644
--- a/media/libaaudio/examples/write_sine/Android.bp
+++ b/media/libaaudio/examples/write_sine/Android.bp
@@ -4,6 +4,7 @@
cflags: ["-Wall", "-Werror"],
shared_libs: ["libaaudio"],
header_libs: ["libaaudio_example_utils"],
+ pack_relocations: false,
}
cc_test {
@@ -12,4 +13,5 @@
cflags: ["-Wall", "-Werror"],
shared_libs: ["libaaudio"],
header_libs: ["libaaudio_example_utils"],
+ pack_relocations: false,
}
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index f267f76..3302982 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -23,6 +23,8 @@
#include <android/hardware/drm/1.0/IDrmPluginListener.h>
#include <android/hardware/drm/1.1/IDrmFactory.h>
#include <android/hardware/drm/1.1/IDrmPlugin.h>
+#include <android/hardware/drm/1.2/IDrmFactory.h>
+#include <android/hardware/drm/1.2/IDrmPlugin.h>
#include <media/MediaAnalyticsItem.h>
#include <mediadrm/DrmMetrics.h>
@@ -36,6 +38,7 @@
using drm::V1_0::IDrmPlugin;
using drm::V1_0::IDrmPluginListener;
using drm::V1_0::KeyStatus;
+using drm::V1_2::OfflineLicenseState;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
@@ -113,6 +116,11 @@
virtual status_t getSecurityLevel(Vector<uint8_t> const &sessionId,
DrmPlugin::SecurityLevel *level) const;
+ virtual status_t getOfflineLicenseKeySetIds(List<Vector<uint8_t>> &keySetIds) const;
+ virtual status_t removeOfflineLicense(Vector<uint8_t> const &keySetId);
+ virtual status_t getOfflineLicenseState(Vector<uint8_t> const &keySetId,
+ DrmPlugin::OfflineLicenseState *licenseState) const;
+
virtual status_t getPropertyString(String8 const &name, String8 &value ) const;
virtual status_t getPropertyByteArray(String8 const &name,
Vector<uint8_t> &value ) const;
@@ -182,6 +190,7 @@
const Vector<sp<IDrmFactory>> mFactories;
sp<IDrmPlugin> mPlugin;
sp<drm::V1_1::IDrmPlugin> mPluginV1_1;
+ sp<drm::V1_2::IDrmPlugin> mPluginV1_2;
String8 mAppPackageName;
// Mutable to allow modification within GetPropertyByteArray.
diff --git a/media/libmedia/include/media/IDrm.h b/media/libmedia/include/media/IDrm.h
index 8e9eb3a..49166c6 100644
--- a/media/libmedia/include/media/IDrm.h
+++ b/media/libmedia/include/media/IDrm.h
@@ -75,8 +75,8 @@
Vector<uint8_t> &certificate,
Vector<uint8_t> &wrappedKey) = 0;
- virtual status_t getSecureStops(List<Vector<uint8_t> > &secureStops) = 0;
- virtual status_t getSecureStopIds(List<Vector<uint8_t> > &secureStopIds) = 0;
+ virtual status_t getSecureStops(List<Vector<uint8_t>> &secureStops) = 0;
+ virtual status_t getSecureStopIds(List<Vector<uint8_t>> &secureStopIds) = 0;
virtual status_t getSecureStop(Vector<uint8_t> const &ssid, Vector<uint8_t> &secureStop) = 0;
virtual status_t releaseSecureStops(Vector<uint8_t> const &ssRelease) = 0;
@@ -91,6 +91,11 @@
virtual status_t getSecurityLevel(Vector<uint8_t> const &sessionId,
DrmPlugin::SecurityLevel *level) const = 0;
+ virtual status_t getOfflineLicenseKeySetIds(List<Vector<uint8_t>> &keySetIds) const = 0;
+ virtual status_t removeOfflineLicense(Vector<uint8_t> const &keySetId) = 0;
+ virtual status_t getOfflineLicenseState(Vector<uint8_t> const &keySetId,
+ DrmPlugin::OfflineLicenseState *licenseState) const = 0;
+
virtual status_t getPropertyString(String8 const &name, String8 &value) const = 0;
virtual status_t getPropertyByteArray(String8 const &name,
Vector<uint8_t> &value) const = 0;
diff --git a/media/libmedia/include/media/JAudioAttributes.h b/media/libmedia/include/media/JAudioAttributes.h
index fb11435..ea0aaa3 100644
--- a/media/libmedia/include/media/JAudioAttributes.h
+++ b/media/libmedia/include/media/JAudioAttributes.h
@@ -26,8 +26,7 @@
public:
/* Creates a Java AudioAttributes object. */
static jobject createAudioAttributesObj(JNIEnv *env,
- const audio_attributes_t* pAttributes,
- audio_stream_type_t streamType) {
+ const audio_attributes_t* pAttributes) {
jclass jBuilderCls = env->FindClass("android/media/AudioAttributes$Builder");
jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
@@ -58,11 +57,6 @@
// TODO: Handle the 'tags' (char[] to HashSet<String>).
// How to parse the char[]? Is there any example of it?
// Also, the addTags() method is hidden.
- } else {
- // Call AudioAttributes.Builder.setLegacyStreamType().build()
- jmethodID jSetLegacyStreamType = env->GetMethodID(jBuilderCls, "setLegacyStreamType",
- "(I)Landroid/media/AudioAttributes$Builder;");
- jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetLegacyStreamType, streamType);
}
jmethodID jBuild = env->GetMethodID(jBuilderCls, "build",
diff --git a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
index 28b8c2b..eea7cfc 100644
--- a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
+++ b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
@@ -221,6 +221,9 @@
kKeyExifSize = 'exsz', // int64_t, Exif data size
kKeyIsExif = 'exif', // bool (int32_t) buffer contains exif data block
kKeyPcmBigEndian = 'pcmb', // bool (int32_t)
+
+ // Key for ALAC Magic Cookie
+ kKeyAlacMagicCookie = 'almc', // raw data
};
enum {
diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp
index 75d1df0..2109ad1 100644
--- a/media/libmediaplayer2/Android.bp
+++ b/media/libmediaplayer2/Android.bp
@@ -59,6 +59,7 @@
"libstagefright_player2",
"libstagefright_rtsp",
"libstagefright_timedtext2",
+ "libmedia2_jni_core",
],
export_include_dirs: [
diff --git a/media/libmediaplayer2/JAudioTrack.cpp b/media/libmediaplayer2/JAudioTrack.cpp
index 778ae1b..cb139c9 100644
--- a/media/libmediaplayer2/JAudioTrack.cpp
+++ b/media/libmediaplayer2/JAudioTrack.cpp
@@ -28,7 +28,6 @@
// TODO: Store Java class/methodID as a member variable in the class.
// TODO: Add NULL && Exception checks after every JNI call.
JAudioTrack::JAudioTrack( // < Usages of the arguments are below >
- audio_stream_type_t streamType, // AudioAudioAttributes
uint32_t sampleRate, // AudioFormat && bufferSizeInBytes
audio_format_t format, // AudioFormat && bufferSizeInBytes
audio_channel_mask_t channelMask, // AudioFormat && bufferSizeInBytes
@@ -36,12 +35,14 @@
void* user, // Offload
size_t frameCount, // bufferSizeInBytes
audio_session_t sessionId, // AudioTrack
- const audio_attributes_t* pAttributes, // AudioAttributes
+ const jobject attributes, // AudioAttributes
float maxRequiredSpeed) { // bufferSizeInBytes
JNIEnv *env = JavaVMHelper::getJNIEnv();
+
jclass jAudioTrackCls = env->FindClass("android/media/AudioTrack");
- mAudioTrackCls = (jclass) env->NewGlobalRef(jAudioTrackCls);
+ mAudioTrackCls = reinterpret_cast<jclass>(env->NewGlobalRef(jAudioTrackCls));
+ env->DeleteLocalRef(jAudioTrackCls);
maxRequiredSpeed = std::min(std::max(maxRequiredSpeed, 1.0f), AUDIO_TIMESTRETCH_SPEED_MAX);
@@ -64,10 +65,17 @@
jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
+ if (attributes != NULL) {
+ mAudioAttributesObj = new JObjectHolder(attributes);
+ } else {
+ mAudioAttributesObj = new JObjectHolder(
+ JAudioAttributes::createAudioAttributesObj(env, NULL));
+ }
+
jmethodID jSetAudioAttributes = env->GetMethodID(jBuilderCls, "setAudioAttributes",
"(Landroid/media/AudioAttributes;)Landroid/media/AudioTrack$Builder;");
- jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetAudioAttributes,
- JAudioAttributes::createAudioAttributesObj(env, pAttributes, streamType));
+ jBuilderObj = env->CallObjectMethod(jBuilderObj,
+ jSetAudioAttributes, mAudioAttributesObj->getJObject());
jmethodID jSetAudioFormat = env->GetMethodID(jBuilderCls, "setAudioFormat",
"(Landroid/media/AudioFormat;)Landroid/media/AudioTrack$Builder;");
@@ -100,7 +108,9 @@
}
jmethodID jBuild = env->GetMethodID(jBuilderCls, "build", "()Landroid/media/AudioTrack;");
- mAudioTrackObj = env->CallObjectMethod(jBuilderObj, jBuild);
+ jobject jAudioTrackObj = env->CallObjectMethod(jBuilderObj, jBuild);
+ mAudioTrackObj = reinterpret_cast<jobject>(env->NewGlobalRef(jAudioTrackObj));
+ env->DeleteLocalRef(jBuilderObj);
if (cbf != NULL) {
// Set offload mode callback
@@ -118,6 +128,7 @@
JAudioTrack::~JAudioTrack() {
JNIEnv *env = JavaVMHelper::getJNIEnv();
env->DeleteGlobalRef(mAudioTrackCls);
+ env->DeleteGlobalRef(mAudioTrackObj);
}
size_t JAudioTrack::frameCount() {
@@ -151,21 +162,21 @@
return NO_ERROR;
}
-bool JAudioTrack::getTimestamp(AudioTimestamp& timestamp) {
+status_t JAudioTrack::getTimestamp(AudioTimestamp& timestamp) {
JNIEnv *env = JavaVMHelper::getJNIEnv();
jclass jAudioTimeStampCls = env->FindClass("android/media/AudioTimestamp");
jobject jAudioTimeStampObj = env->AllocObject(jAudioTimeStampCls);
- jfieldID jFramePosition = env->GetFieldID(jAudioTimeStampCls, "framePosition", "L");
- jfieldID jNanoTime = env->GetFieldID(jAudioTimeStampCls, "nanoTime", "L");
+ jfieldID jFramePosition = env->GetFieldID(jAudioTimeStampCls, "framePosition", "J");
+ jfieldID jNanoTime = env->GetFieldID(jAudioTimeStampCls, "nanoTime", "J");
jmethodID jGetTimestamp = env->GetMethodID(mAudioTrackCls,
- "getTimestamp", "(Landroid/media/AudioTimestamp)B");
+ "getTimestamp", "(Landroid/media/AudioTimestamp;)Z");
bool success = env->CallBooleanMethod(mAudioTrackObj, jGetTimestamp, jAudioTimeStampObj);
if (!success) {
- return false;
+ return NO_INIT;
}
long long framePosition = env->GetLongField(jAudioTimeStampObj, jFramePosition);
@@ -178,7 +189,7 @@
timestamp.mTime = ts;
timestamp.mPosition = (uint32_t) framePosition;
- return true;
+ return NO_ERROR;
}
status_t JAudioTrack::getTimestamp(ExtendedTimestamp *timestamp __unused) {
@@ -423,6 +434,35 @@
return audioFormatToNative(javaFormat);
}
+size_t JAudioTrack::frameSize() {
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
+
+ // TODO: Calculated here implementing the logic in AudioTrack.java
+ // wait for AudioTrack.java exposing this parameter (i.e. getFrameSizeInBtytes())
+ jmethodID jGetAudioFormat = env->GetMethodID(mAudioTrackCls, "getAudioFormat", "()I");
+ int javaFormat = env->CallIntMethod(mAudioTrackObj, jGetAudioFormat);
+
+ jclass jAudioFormatCls = env->FindClass("android/media/AudioFormat");
+ jmethodID jIsEncodingLinearFrames = env->GetStaticMethodID(
+ jAudioFormatCls, "isEncodingLinearFrames", "(I)Z");
+ jboolean javaIsEncodingLinearFrames = env->CallStaticBooleanMethod(
+ jAudioFormatCls, jIsEncodingLinearFrames, javaFormat);
+
+ if (javaIsEncodingLinearFrames == false) {
+ return 1;
+ }
+
+ jmethodID jGetBytesPerSample = env->GetStaticMethodID(jAudioFormatCls,
+ "getBytesPerSample", "(I)I");
+ int javaBytesPerSample = env->CallStaticIntMethod(jAudioFormatCls,
+ jGetBytesPerSample, javaFormat);
+
+ jmethodID jGetChannelCount = env->GetMethodID(mAudioTrackCls, "getChannelCount", "()I");
+ int javaChannelCount = env->CallIntMethod(mAudioTrackObj, jGetChannelCount);
+
+ return javaChannelCount * javaBytesPerSample;
+}
+
status_t JAudioTrack::dump(int fd, const Vector<String16>& args __unused) const
{
String8 result;
@@ -432,10 +472,6 @@
// TODO: Remove logs that includes unavailable information from below.
// result.appendFormat(" status(%d), state(%d), session Id(%d), flags(%#x)\n",
// mStatus, mState, mSessionId, mFlags);
-// result.appendFormat(" stream type(%d), left - right volume(%f, %f)\n",
-// (mStreamType == AUDIO_STREAM_DEFAULT) ?
-// audio_attributes_to_stream_type(&mAttributes) : mStreamType,
-// mVolume[AUDIO_INTERLEAVE_LEFT], mVolume[AUDIO_INTERLEAVE_RIGHT]);
// result.appendFormat(" format(%#x), channel mask(%#x), channel count(%u)\n",
// format(), mChannelMask, channelCount());
// result.appendFormat(" sample rate(%u), original sample rate(%u), speed(%f)\n",
@@ -453,19 +489,11 @@
return NO_ERROR;
}
-audio_port_handle_t JAudioTrack::getRoutedDeviceId() {
+jobject JAudioTrack::getRoutedDevice() {
JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jGetRoutedDevice = env->GetMethodID(mAudioTrackCls, "getRoutedDevice",
"()Landroid/media/AudioDeviceInfo;");
- jobject jAudioDeviceInfoObj = env->CallObjectMethod(mAudioTrackObj, jGetRoutedDevice);
- if (env->IsSameObject(jAudioDeviceInfoObj, NULL)) {
- return AUDIO_PORT_HANDLE_NONE;
- }
-
- jclass jAudioDeviceInfoCls = env->FindClass("Landroid/media/AudioDeviceInfo");
- jmethodID jGetId = env->GetMethodID(jAudioDeviceInfoCls, "getId", "()I");
- jint routedDeviceId = env->CallIntMethod(jAudioDeviceInfoObj, jGetId);
- return routedDeviceId;
+ return env->CallObjectMethod(mAudioTrackObj, jGetRoutedDevice);
}
audio_session_t JAudioTrack::getAudioSessionId() {
@@ -475,16 +503,27 @@
return (audio_session_t) sessionId;
}
-status_t JAudioTrack::setOutputDevice(audio_port_handle_t deviceId) {
+status_t JAudioTrack::setPreferredDevice(jobject device) {
JNIEnv *env = JavaVMHelper::getJNIEnv();
- jclass jMP2ImplCls = env->FindClass("android/media/MediaPlayer2Impl");
- jmethodID jSetAudioOutputDeviceById = env->GetMethodID(
- jMP2ImplCls, "setAudioOutputDeviceById", "(Landroid/media/AudioTrack;I)Z");
- jboolean result = env->CallStaticBooleanMethod(
- jMP2ImplCls, jSetAudioOutputDeviceById, mAudioTrackObj, deviceId);
+ jmethodID jSetPreferredDeviceId = env->GetMethodID(mAudioTrackCls, "setPreferredDevice",
+ "(Landroid/media/AudioDeviceInfo;)Z");
+ jboolean result = env->CallBooleanMethod(mAudioTrackObj, jSetPreferredDeviceId, device);
return result == true ? NO_ERROR : BAD_VALUE;
}
+audio_stream_type_t JAudioTrack::getAudioStreamType() {
+ if (mAudioAttributesObj == NULL) {
+ return AUDIO_STREAM_DEFAULT;
+ }
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
+ jclass jAudioAttributesCls = env->FindClass("android/media/AudioAttributes");
+ jmethodID jGetVolumeControlStream = env->GetMethodID(jAudioAttributesCls,
+ "getVolumeControlStream", "()I");
+ int javaAudioStreamType = env->CallIntMethod(
+ mAudioAttributesObj->getJObject(), jGetVolumeControlStream);
+ return (audio_stream_type_t)javaAudioStreamType;
+}
+
status_t JAudioTrack::pendingDuration(int32_t *msec) {
if (msec == nullptr) {
return BAD_VALUE;
@@ -526,18 +565,85 @@
return NO_ERROR;
}
-status_t JAudioTrack::addAudioDeviceCallback(
- const sp<AudioSystem::AudioDeviceCallback>& callback __unused) {
- // TODO: Implement this after appropriate Java AudioTrack method is available.
+status_t JAudioTrack::addAudioDeviceCallback(jobject listener, jobject handler) {
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
+ jmethodID jAddOnRoutingChangedListener = env->GetMethodID(mAudioTrackCls,
+ "addOnRoutingChangedListener",
+ "(Landroid/media/AudioRouting$OnRoutingChangedListener;Landroid/os/Handler;)V");
+ env->CallVoidMethod(mAudioTrackObj, jAddOnRoutingChangedListener, listener, handler);
return NO_ERROR;
}
-status_t JAudioTrack::removeAudioDeviceCallback(
- const sp<AudioSystem::AudioDeviceCallback>& callback __unused) {
- // TODO: Implement this after appropriate Java AudioTrack method is available.
+status_t JAudioTrack::removeAudioDeviceCallback(jobject listener) {
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
+ jmethodID jRemoveOnRoutingChangedListener = env->GetMethodID(mAudioTrackCls,
+ "removeOnRoutingChangedListener",
+ "(Landroid/media/AudioRouting$OnRoutingChangedListener;)V");
+ env->CallVoidMethod(mAudioTrackObj, jRemoveOnRoutingChangedListener, listener);
return NO_ERROR;
}
+void JAudioTrack::registerRoutingDelegates(
+ std::vector<std::pair<jobject, jobject>>& routingDelegates) {
+ for (std::vector<std::pair<jobject, jobject>>::iterator it = routingDelegates.begin();
+ it != routingDelegates.end(); it++) {
+ addAudioDeviceCallback(it->second, getHandler(it->second));
+ }
+}
+
+/////////////////////////////////////////////////////////////
+/// Static methods begin ///
+/////////////////////////////////////////////////////////////
+jobject JAudioTrack::getListener(const jobject routingDelegateObj) {
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
+ jclass jRoutingDelegateCls = env->FindClass("android/media/RoutingDelegate");
+ jmethodID jGetListener = env->GetMethodID(jRoutingDelegateCls,
+ "getListener", "()Landroid/media/AudioRouting$OnRoutingChangedListener;");
+ return env->CallObjectMethod(routingDelegateObj, jGetListener);
+}
+
+jobject JAudioTrack::getHandler(const jobject routingDelegateObj) {
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
+ jclass jRoutingDelegateCls = env->FindClass("android/media/RoutingDelegate");
+ jmethodID jGetHandler = env->GetMethodID(jRoutingDelegateCls,
+ "getHandler", "()Landroid/os/Handler;");
+ return env->CallObjectMethod(routingDelegateObj, jGetHandler);
+}
+
+jobject JAudioTrack::addGlobalRef(const jobject obj) {
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
+ return reinterpret_cast<jobject>(env->NewGlobalRef(obj));
+}
+
+status_t JAudioTrack::removeGlobalRef(const jobject obj) {
+ if (obj == NULL) {
+ return BAD_VALUE;
+ }
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
+ env->DeleteGlobalRef(obj);
+ return NO_ERROR;
+}
+
+jobject JAudioTrack::findByKey(std::vector<std::pair<jobject, jobject>>& mp, const jobject key) {
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
+ for (std::vector<std::pair<jobject, jobject>>::iterator it = mp.begin(); it != mp.end(); it++) {
+ if (env->IsSameObject(it->first, key)) {
+ return it->second;
+ }
+ }
+ return nullptr;
+}
+
+void JAudioTrack::eraseByKey(std::vector<std::pair<jobject, jobject>>& mp, const jobject key) {
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
+ for (std::vector<std::pair<jobject, jobject>>::iterator it = mp.begin(); it != mp.end(); it++) {
+ if (env->IsSameObject(it->first, key)) {
+ mp.erase(it);
+ return;
+ }
+ }
+}
+
/////////////////////////////////////////////////////////////
/// Private method begins ///
/////////////////////////////////////////////////////////////
diff --git a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp
index 6b27ca7..7457d84 100644
--- a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp
+++ b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp
@@ -23,7 +23,6 @@
#include <utils/Log.h>
#include <media/AudioPolicyHelper.h>
-#include <media/AudioTrack.h>
#include <media/stagefright/foundation/ADebug.h>
namespace {
@@ -44,29 +43,27 @@
String8 result;
result.append(" MediaPlayer2AudioOutput\n");
- snprintf(buffer, 255, " stream type(%d), volume(%f)\n",
- mStreamType, mVolume);
+ snprintf(buffer, 255, " volume(%f)\n", mVolume);
result.append(buffer);
snprintf(buffer, 255, " msec per frame(%f), latency (%d)\n",
- mMsecsPerFrame, (mTrack != 0) ? mTrack->latency() : -1);
+ mMsecsPerFrame, (mJAudioTrack != nullptr) ? mJAudioTrack->latency() : -1);
result.append(buffer);
snprintf(buffer, 255, " aux effect id(%d), send level (%f)\n",
mAuxEffectId, mSendLevel);
result.append(buffer);
::write(fd, result.string(), result.size());
- if (mTrack != 0) {
- mTrack->dump(fd, args);
+ if (mJAudioTrack != nullptr) {
+ mJAudioTrack->dump(fd, args);
}
return NO_ERROR;
}
MediaPlayer2AudioOutput::MediaPlayer2AudioOutput(audio_session_t sessionId, uid_t uid, int pid,
- const audio_attributes_t* attr, const sp<AudioSystem::AudioDeviceCallback>& deviceCallback)
- : mCallback(NULL),
- mCallbackCookie(NULL),
- mCallbackData(NULL),
- mStreamType(AUDIO_STREAM_MUSIC),
+ const jobject attributes)
+ : mCallback(nullptr),
+ mCallbackCookie(nullptr),
+ mCallbackData(nullptr),
mVolume(1.0),
mPlaybackRate(AUDIO_PLAYBACK_RATE_DEFAULT),
mSampleRateHz(0),
@@ -77,28 +74,22 @@
mPid(pid),
mSendLevel(0.0),
mAuxEffectId(0),
- mFlags(AUDIO_OUTPUT_FLAG_NONE),
- mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
- mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
- mDeviceCallbackEnabled(false),
- mDeviceCallback(deviceCallback) {
+ mFlags(AUDIO_OUTPUT_FLAG_NONE) {
ALOGV("MediaPlayer2AudioOutput(%d)", sessionId);
- if (attr != NULL) {
- mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
- if (mAttributes != NULL) {
- memcpy(mAttributes, attr, sizeof(audio_attributes_t));
- mStreamType = audio_attributes_to_stream_type(attr);
- }
- } else {
- mAttributes = NULL;
+
+ if (attributes != nullptr) {
+ mAttributes = new JObjectHolder(attributes);
}
setMinBufferCount();
+ mRoutingDelegates.clear();
}
MediaPlayer2AudioOutput::~MediaPlayer2AudioOutput() {
+ for (auto routingDelegate : mRoutingDelegates) {
+ JAudioTrack::removeGlobalRef(routingDelegate.second);
+ }
close();
- free(mAttributes);
delete mCallbackData;
}
@@ -125,31 +116,31 @@
ssize_t MediaPlayer2AudioOutput::bufferSize() const {
Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
+ if (mJAudioTrack == nullptr) {
return NO_INIT;
}
- return mTrack->frameCount() * mFrameSize;
+ return mJAudioTrack->frameCount() * mFrameSize;
}
ssize_t MediaPlayer2AudioOutput::frameCount() const {
Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
+ if (mJAudioTrack == nullptr) {
return NO_INIT;
}
- return mTrack->frameCount();
+ return mJAudioTrack->frameCount();
}
ssize_t MediaPlayer2AudioOutput::channelCount() const {
Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
+ if (mJAudioTrack == nullptr) {
return NO_INIT;
}
- return mTrack->channelCount();
+ return mJAudioTrack->channelCount();
}
ssize_t MediaPlayer2AudioOutput::frameSize() const {
Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
+ if (mJAudioTrack == nullptr) {
return NO_INIT;
}
return mFrameSize;
@@ -157,10 +148,10 @@
uint32_t MediaPlayer2AudioOutput::latency () const {
Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
+ if (mJAudioTrack == nullptr) {
return 0;
}
- return mTrack->latency();
+ return mJAudioTrack->latency();
}
float MediaPlayer2AudioOutput::msecsPerFrame() const {
@@ -170,18 +161,18 @@
status_t MediaPlayer2AudioOutput::getPosition(uint32_t *position) const {
Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
+ if (mJAudioTrack == nullptr) {
return NO_INIT;
}
- return mTrack->getPosition(position);
+ return mJAudioTrack->getPosition(position);
}
status_t MediaPlayer2AudioOutput::getTimestamp(AudioTimestamp &ts) const {
Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
+ if (mJAudioTrack == nullptr) {
return NO_INIT;
}
- return mTrack->getTimestamp(ts);
+ return mJAudioTrack->getTimestamp(ts);
}
// TODO: Remove unnecessary calls to getPlayedOutDurationUs()
@@ -194,7 +185,7 @@
// Calculate duration of played samples if played at normal rate (i.e., 1.0).
int64_t MediaPlayer2AudioOutput::getPlayedOutDurationUs(int64_t nowUs) const {
Mutex::Autolock lock(mLock);
- if (mTrack == 0 || mSampleRateHz == 0) {
+ if (mJAudioTrack == nullptr || mSampleRateHz == 0) {
return 0;
}
@@ -202,22 +193,18 @@
int64_t numFramesPlayedAtUs;
AudioTimestamp ts;
- status_t res = mTrack->getTimestamp(ts);
+ status_t res = mJAudioTrack->getTimestamp(ts);
+
if (res == OK) { // case 1: mixing audio tracks and offloaded tracks.
numFramesPlayed = ts.mPosition;
numFramesPlayedAtUs = ts.mTime.tv_sec * 1000000LL + ts.mTime.tv_nsec / 1000;
//ALOGD("getTimestamp: OK %d %lld", numFramesPlayed, (long long)numFramesPlayedAtUs);
- } else if (res == WOULD_BLOCK) { // case 2: transitory state on start of a new track
+ } else { // case 2: transitory state on start of a new track
+ // case 3: transitory at new track or audio fast tracks.
numFramesPlayed = 0;
numFramesPlayedAtUs = nowUs;
//ALOGD("getTimestamp: WOULD_BLOCK %d %lld",
// numFramesPlayed, (long long)numFramesPlayedAtUs);
- } else { // case 3: transitory at new track or audio fast tracks.
- res = mTrack->getPosition(&numFramesPlayed);
- CHECK_EQ(res, (status_t)OK);
- numFramesPlayedAtUs = nowUs;
- numFramesPlayedAtUs += 1000LL * mTrack->latency() / 2; /* XXX */
- //ALOGD("getPosition: %u %lld", numFramesPlayed, (long long)numFramesPlayedAtUs);
}
// CHECK_EQ(numFramesPlayed & (1 << 31), 0); // can't be negative until 12.4 hrs, test
@@ -243,57 +230,33 @@
status_t MediaPlayer2AudioOutput::getFramesWritten(uint32_t *frameswritten) const {
Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
+ if (mJAudioTrack == nullptr) {
return NO_INIT;
}
ExtendedTimestamp ets;
- status_t status = mTrack->getTimestamp(&ets);
+ status_t status = mJAudioTrack->getTimestamp(&ets);
if (status == OK || status == WOULD_BLOCK) {
*frameswritten = (uint32_t)ets.mPosition[ExtendedTimestamp::LOCATION_CLIENT];
}
return status;
}
-status_t MediaPlayer2AudioOutput::setParameters(const String8& keyValuePairs) {
+void MediaPlayer2AudioOutput::setAudioAttributes(const jobject attributes) {
Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
- return NO_INIT;
- }
- return mTrack->setParameters(keyValuePairs);
+ mAttributes = (attributes == nullptr) ? nullptr : new JObjectHolder(attributes);
}
-String8 MediaPlayer2AudioOutput::getParameters(const String8& keys) {
+audio_stream_type_t MediaPlayer2AudioOutput::getAudioStreamType() const {
+ ALOGV("getAudioStreamType");
Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
- return String8::empty();
+ if (mJAudioTrack == nullptr) {
+ return AUDIO_STREAM_DEFAULT;
}
- return mTrack->getParameters(keys);
-}
-
-void MediaPlayer2AudioOutput::setAudioAttributes(const audio_attributes_t * attributes) {
- Mutex::Autolock lock(mLock);
- if (attributes == NULL) {
- free(mAttributes);
- mAttributes = NULL;
- } else {
- if (mAttributes == NULL) {
- mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
- }
- memcpy(mAttributes, attributes, sizeof(audio_attributes_t));
- mStreamType = audio_attributes_to_stream_type(attributes);
- }
-}
-
-void MediaPlayer2AudioOutput::setAudioStreamType(audio_stream_type_t streamType) {
- Mutex::Autolock lock(mLock);
- // do not allow direct stream type modification if attributes have been set
- if (mAttributes == NULL) {
- mStreamType = streamType;
- }
+ return mJAudioTrack->getAudioStreamType();
}
void MediaPlayer2AudioOutput::close_l() {
- mTrack.clear();
+ mJAudioTrack.clear();
}
status_t MediaPlayer2AudioOutput::open(
@@ -302,7 +265,6 @@
AudioCallback cb, void *cookie,
audio_output_flags_t flags,
const audio_offload_info_t *offloadInfo,
- bool doNotReconnect,
uint32_t suggestedFrameCount) {
ALOGV("open(%u, %d, 0x%x, 0x%x, %d 0x%x)", sampleRate, channelCount, channelMask,
format, mSessionId, flags);
@@ -310,7 +272,7 @@
// offloading is only supported in callback mode for now.
// offloadInfo must be present if offload flag is set
if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) &&
- ((cb == NULL) || (offloadInfo == NULL))) {
+ ((cb == nullptr) || (offloadInfo == nullptr))) {
return BAD_VALUE;
}
@@ -330,32 +292,23 @@
mCallback = cb;
mCallbackCookie = cookie;
- sp<AudioTrack> t;
- CallbackData *newcbd = NULL;
+ sp<JAudioTrack> jT;
+ CallbackData *newcbd = nullptr;
- ALOGV("creating new AudioTrack");
+ ALOGV("creating new JAudioTrack");
- if (mCallback != NULL) {
+ if (mCallback != nullptr) {
newcbd = new CallbackData(this);
- t = new AudioTrack(
- mStreamType,
- sampleRate,
- format,
- channelMask,
- frameCount,
- flags,
- CallbackWrapper,
- newcbd,
- 0, // notification frames
- mSessionId,
- AudioTrack::TRANSFER_CALLBACK,
- offloadInfo,
- mUid,
- mPid,
- mAttributes,
- doNotReconnect,
- 1.0f, // default value for maxRequiredSpeed
- mSelectedDeviceId);
+ jT = new JAudioTrack(
+ sampleRate,
+ format,
+ channelMask,
+ CallbackWrapper,
+ newcbd,
+ frameCount,
+ mSessionId,
+ mAttributes != nullptr ? mAttributes->getJObject() : nullptr,
+ 1.0f); // default value for maxRequiredSpeed
} else {
// TODO: Due to buffer memory concerns, we use a max target playback speed
// based on mPlaybackRate at the time of open (instead of kMaxRequiredSpeed),
@@ -365,73 +318,62 @@
ALOGW_IF(targetSpeed != mPlaybackRate.mSpeed,
"track target speed:%f clamped from playback speed:%f",
targetSpeed, mPlaybackRate.mSpeed);
- t = new AudioTrack(
- mStreamType,
- sampleRate,
- format,
- channelMask,
- frameCount,
- flags,
- NULL, // callback
- NULL, // user data
- 0, // notification frames
- mSessionId,
- AudioTrack::TRANSFER_DEFAULT,
- NULL, // offload info
- mUid,
- mPid,
- mAttributes,
- doNotReconnect,
- targetSpeed,
- mSelectedDeviceId);
+ jT = new JAudioTrack(
+ sampleRate,
+ format,
+ channelMask,
+ nullptr,
+ nullptr,
+ frameCount,
+ mSessionId,
+ mAttributes != nullptr ? mAttributes->getJObject() : nullptr,
+ targetSpeed);
}
- if ((t == 0) || (t->initCheck() != NO_ERROR)) {
+ if (jT == 0) {
ALOGE("Unable to create audio track");
delete newcbd;
// t goes out of scope, so reference count drops to zero
return NO_INIT;
- } else {
- // successful AudioTrack initialization implies a legacy stream type was generated
- // from the audio attributes
- mStreamType = t->streamType();
}
- CHECK((t != NULL) && ((mCallback == NULL) || (newcbd != NULL)));
+ CHECK((jT != nullptr) && ((mCallback == nullptr) || (newcbd != nullptr)));
mCallbackData = newcbd;
ALOGV("setVolume");
- t->setVolume(mVolume);
+ jT->setVolume(mVolume);
mSampleRateHz = sampleRate;
mFlags = flags;
mMsecsPerFrame = 1E3f / (mPlaybackRate.mSpeed * sampleRate);
- mFrameSize = t->frameSize();
- mTrack = t;
+ mFrameSize = jT->frameSize();
+ mJAudioTrack = jT;
return updateTrack_l();
}
status_t MediaPlayer2AudioOutput::updateTrack_l() {
- if (mTrack == NULL) {
+ if (mJAudioTrack == nullptr) {
return NO_ERROR;
}
status_t res = NO_ERROR;
// Note some output devices may give us a direct track even though we don't specify it.
// Example: Line application b/17459982.
- if ((mTrack->getFlags()
+ if ((mJAudioTrack->getFlags()
& (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT)) == 0) {
- res = mTrack->setPlaybackRate(mPlaybackRate);
+ res = mJAudioTrack->setPlaybackRate(mPlaybackRate);
if (res == NO_ERROR) {
- mTrack->setAuxEffectSendLevel(mSendLevel);
- res = mTrack->attachAuxEffect(mAuxEffectId);
+ mJAudioTrack->setAuxEffectSendLevel(mSendLevel);
+ res = mJAudioTrack->attachAuxEffect(mAuxEffectId);
}
}
- mTrack->setOutputDevice(mSelectedDeviceId);
- if (mDeviceCallbackEnabled) {
- mTrack->addAudioDeviceCallback(mDeviceCallback.promote());
+ if (mPreferredDevice != nullptr) {
+ mJAudioTrack->setPreferredDevice(mPreferredDevice->getJObject());
}
+
+ mJAudioTrack->registerRoutingDelegates(mRoutingDelegates);
+
ALOGV("updateTrack_l() DONE status %d", res);
return res;
}
@@ -439,13 +381,13 @@
status_t MediaPlayer2AudioOutput::start() {
ALOGV("start");
Mutex::Autolock lock(mLock);
- if (mCallbackData != NULL) {
+ if (mCallbackData != nullptr) {
mCallbackData->endTrackSwitch();
}
- if (mTrack != 0) {
- mTrack->setVolume(mVolume);
- mTrack->setAuxEffectSendLevel(mSendLevel);
- status_t status = mTrack->start();
+ if (mJAudioTrack != nullptr) {
+ mJAudioTrack->setVolume(mVolume);
+ mJAudioTrack->setAuxEffectSendLevel(mSendLevel);
+ status_t status = mJAudioTrack->start();
return status;
}
return NO_INIT;
@@ -453,11 +395,11 @@
ssize_t MediaPlayer2AudioOutput::write(const void* buffer, size_t size, bool blocking) {
Mutex::Autolock lock(mLock);
- LOG_ALWAYS_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
+ LOG_ALWAYS_FATAL_IF(mCallback != nullptr, "Don't call write if supplying a callback.");
//ALOGV("write(%p, %u)", buffer, size);
- if (mTrack != 0) {
- return mTrack->write(buffer, size, blocking);
+ if (mJAudioTrack != nullptr) {
+ return mJAudioTrack->write(buffer, size, blocking);
}
return NO_INIT;
}
@@ -465,34 +407,34 @@
void MediaPlayer2AudioOutput::stop() {
ALOGV("stop");
Mutex::Autolock lock(mLock);
- if (mTrack != 0) {
- mTrack->stop();
+ if (mJAudioTrack != nullptr) {
+ mJAudioTrack->stop();
}
}
void MediaPlayer2AudioOutput::flush() {
ALOGV("flush");
Mutex::Autolock lock(mLock);
- if (mTrack != 0) {
- mTrack->flush();
+ if (mJAudioTrack != nullptr) {
+ mJAudioTrack->flush();
}
}
void MediaPlayer2AudioOutput::pause() {
ALOGV("pause");
Mutex::Autolock lock(mLock);
- if (mTrack != 0) {
- mTrack->pause();
+ if (mJAudioTrack != nullptr) {
+ mJAudioTrack->pause();
}
}
void MediaPlayer2AudioOutput::close() {
ALOGV("close");
- sp<AudioTrack> track;
+ sp<JAudioTrack> track;
{
Mutex::Autolock lock(mLock);
- track = mTrack;
- close_l(); // clears mTrack
+ track = mJAudioTrack;
+ close_l(); // clears mJAudioTrack
}
// destruction of the track occurs outside of mutex.
}
@@ -501,8 +443,8 @@
ALOGV("setVolume(%f)", volume);
Mutex::Autolock lock(mLock);
mVolume = volume;
- if (mTrack != 0) {
- mTrack->setVolume(volume);
+ if (mJAudioTrack != nullptr) {
+ mJAudioTrack->setVolume(volume);
}
}
@@ -510,12 +452,12 @@
ALOGV("setPlaybackRate(%f %f %d %d)",
rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
+ if (mJAudioTrack == 0) {
// remember rate so that we can set it when the track is opened
mPlaybackRate = rate;
return OK;
}
- status_t res = mTrack->setPlaybackRate(rate);
+ status_t res = mJAudioTrack->setPlaybackRate(rate);
if (res != NO_ERROR) {
return res;
}
@@ -531,10 +473,10 @@
status_t MediaPlayer2AudioOutput::getPlaybackRate(AudioPlaybackRate *rate) {
ALOGV("getPlaybackRate");
Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
+ if (mJAudioTrack == 0) {
return NO_INIT;
}
- *rate = mTrack->getPlaybackRate();
+ *rate = mJAudioTrack->getPlaybackRate();
return NO_ERROR;
}
@@ -542,8 +484,8 @@
ALOGV("setAuxEffectSendLevel(%f)", level);
Mutex::Autolock lock(mLock);
mSendLevel = level;
- if (mTrack != 0) {
- return mTrack->setAuxEffectSendLevel(level);
+ if (mJAudioTrack != nullptr) {
+ return mJAudioTrack->setAuxEffectSendLevel(level);
}
return NO_ERROR;
}
@@ -552,44 +494,59 @@
ALOGV("attachAuxEffect(%d)", effectId);
Mutex::Autolock lock(mLock);
mAuxEffectId = effectId;
- if (mTrack != 0) {
- return mTrack->attachAuxEffect(effectId);
+ if (mJAudioTrack != nullptr) {
+ return mJAudioTrack->attachAuxEffect(effectId);
}
return NO_ERROR;
}
-status_t MediaPlayer2AudioOutput::setOutputDevice(audio_port_handle_t deviceId) {
- ALOGV("setOutputDevice(%d)", deviceId);
+status_t MediaPlayer2AudioOutput::setPreferredDevice(jobject device) {
+ ALOGV("setPreferredDevice");
Mutex::Autolock lock(mLock);
- mSelectedDeviceId = deviceId;
- if (mTrack != 0) {
- return mTrack->setOutputDevice(deviceId);
+ status_t ret = NO_ERROR;
+ if (mJAudioTrack != nullptr) {
+ ret = mJAudioTrack->setPreferredDevice(device);
+ }
+ if (ret == NO_ERROR) {
+ mPreferredDevice = new JObjectHolder(device);
+ }
+ return ret;
+}
+
+jobject MediaPlayer2AudioOutput::getRoutedDevice() {
+ ALOGV("getRoutedDevice");
+ Mutex::Autolock lock(mLock);
+ if (mJAudioTrack != nullptr) {
+ return mJAudioTrack->getRoutedDevice();
+ }
+ return nullptr;
+}
+
+status_t MediaPlayer2AudioOutput::addAudioDeviceCallback(jobject jRoutingDelegate) {
+ ALOGV("addAudioDeviceCallback");
+ Mutex::Autolock lock(mLock);
+ jobject listener = JAudioTrack::getListener(jRoutingDelegate);
+ if (mJAudioTrack != nullptr &&
+ JAudioTrack::findByKey(mRoutingDelegates, listener) == nullptr) {
+ jobject handler = JAudioTrack::getHandler(jRoutingDelegate);
+ jobject routingDelegate = JAudioTrack::addGlobalRef(jRoutingDelegate);
+ mRoutingDelegates.push_back(std::pair<jobject, jobject>(listener, routingDelegate));
+ return mJAudioTrack->addAudioDeviceCallback(routingDelegate, handler);
}
return NO_ERROR;
}
-status_t MediaPlayer2AudioOutput::getRoutedDeviceId(audio_port_handle_t* deviceId) {
- ALOGV("getRoutedDeviceId");
+status_t MediaPlayer2AudioOutput::removeAudioDeviceCallback(jobject listener) {
+ ALOGV("removeAudioDeviceCallback");
Mutex::Autolock lock(mLock);
- if (mTrack != 0) {
- mRoutedDeviceId = mTrack->getRoutedDeviceId();
- }
- *deviceId = mRoutedDeviceId;
- return NO_ERROR;
-}
-
-status_t MediaPlayer2AudioOutput::enableAudioDeviceCallback(bool enabled) {
- ALOGV("enableAudioDeviceCallback, %d", enabled);
- Mutex::Autolock lock(mLock);
- mDeviceCallbackEnabled = enabled;
- if (mTrack != 0) {
- status_t status;
- if (enabled) {
- status = mTrack->addAudioDeviceCallback(mDeviceCallback.promote());
- } else {
- status = mTrack->removeAudioDeviceCallback(mDeviceCallback.promote());
+ jobject routingDelegate = nullptr;
+ if (mJAudioTrack != nullptr &&
+ (routingDelegate = JAudioTrack::findByKey(mRoutingDelegates, listener)) != nullptr) {
+ mJAudioTrack->removeAudioDeviceCallback(routingDelegate);
+ JAudioTrack::eraseByKey(mRoutingDelegates, listener);
+ if (JAudioTrack::removeGlobalRef(routingDelegate) != NO_ERROR) {
+ return BAD_VALUE;
}
- return status;
}
return NO_ERROR;
}
@@ -602,21 +559,21 @@
// lock to ensure we aren't caught in the middle of a track switch.
data->lock();
MediaPlayer2AudioOutput *me = data->getOutput();
- AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
- if (me == NULL) {
+ JAudioTrack::Buffer *buffer = (JAudioTrack::Buffer *)info;
+ if (me == nullptr) {
// no output set, likely because the track was scheduled to be reused
// by another player, but the format turned out to be incompatible.
data->unlock();
- if (buffer != NULL) {
- buffer->size = 0;
+ if (buffer != nullptr) {
+ buffer->mSize = 0;
}
return;
}
switch(event) {
- case AudioTrack::EVENT_MORE_DATA: {
+ case JAudioTrack::EVENT_MORE_DATA: {
size_t actualSize = (*me->mCallback)(
- me, buffer->raw, buffer->size, me->mCallbackCookie,
+ me, buffer->mData, buffer->mSize, me->mCallbackCookie,
CB_EVENT_FILL_BUFFER);
// Log when no data is returned from the callback.
@@ -628,25 +585,25 @@
// This is a benign busy-wait, with the next data request generated 10 ms or more later;
// nevertheless for power reasons, we don't want to see too many of these.
- ALOGV_IF(actualSize == 0 && buffer->size > 0, "callbackwrapper: empty buffer returned");
+ ALOGV_IF(actualSize == 0 && buffer->mSize > 0, "callbackwrapper: empty buffer returned");
- buffer->size = actualSize;
+ buffer->mSize = actualSize;
} break;
- case AudioTrack::EVENT_STREAM_END:
+ case JAudioTrack::EVENT_STREAM_END:
// currently only occurs for offloaded callbacks
ALOGV("callbackwrapper: deliver EVENT_STREAM_END");
- (*me->mCallback)(me, NULL /* buffer */, 0 /* size */,
+ (*me->mCallback)(me, nullptr /* buffer */, 0 /* size */,
me->mCallbackCookie, CB_EVENT_STREAM_END);
break;
- case AudioTrack::EVENT_NEW_IAUDIOTRACK :
+ case JAudioTrack::EVENT_NEW_IAUDIOTRACK :
ALOGV("callbackwrapper: deliver EVENT_TEAR_DOWN");
- (*me->mCallback)(me, NULL /* buffer */, 0 /* size */,
+ (*me->mCallback)(me, nullptr /* buffer */, 0 /* size */,
me->mCallbackCookie, CB_EVENT_TEAR_DOWN);
break;
- case AudioTrack::EVENT_UNDERRUN:
+ case JAudioTrack::EVENT_UNDERRUN:
// This occurs when there is no data available, typically
// when there is a failure to supply data to the AudioTrack. It can also
// occur in non-offloaded mode when the audio device comes out of standby.
@@ -666,29 +623,31 @@
data->unlock();
}
-audio_session_t MediaPlayer2AudioOutput::getSessionId() const
-{
+audio_session_t MediaPlayer2AudioOutput::getSessionId() const {
Mutex::Autolock lock(mLock);
return mSessionId;
}
-uint32_t MediaPlayer2AudioOutput::getSampleRate() const
-{
+void MediaPlayer2AudioOutput::setSessionId(const audio_session_t id) {
Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
- return 0;
- }
- return mTrack->getSampleRate();
+ mSessionId = id;
}
-int64_t MediaPlayer2AudioOutput::getBufferDurationInUs() const
-{
+uint32_t MediaPlayer2AudioOutput::getSampleRate() const {
Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
+ if (mJAudioTrack == 0) {
+ return 0;
+ }
+ return mJAudioTrack->getSampleRate();
+}
+
+int64_t MediaPlayer2AudioOutput::getBufferDurationInUs() const {
+ Mutex::Autolock lock(mLock);
+ if (mJAudioTrack == 0) {
return 0;
}
int64_t duration;
- if (mTrack->getBufferDurationInUs(&duration) != OK) {
+ if (mJAudioTrack->getBufferDurationInUs(&duration) != OK) {
return 0;
}
return duration;
diff --git a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
index 301825b..13cf85a 100644
--- a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
+++ b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
@@ -17,19 +17,21 @@
#ifndef ANDROID_JAUDIOTRACK_H
#define ANDROID_JAUDIOTRACK_H
+#include <vector>
+#include <utility>
#include <jni.h>
#include <media/AudioResamplerPublic.h>
#include <media/AudioSystem.h>
#include <media/VolumeShaper.h>
#include <system/audio.h>
#include <utils/Errors.h>
-
+#include <mediaplayer2/JObjectHolder.h>
#include <media/AudioTimestamp.h> // It has dependency on audio.h/Errors.h, but doesn't
// include them in it. Therefore it is included here at last.
namespace android {
-class JAudioTrack {
+class JAudioTrack : public RefBase {
public:
/* Events used by AudioTrack callback function (callback_t).
@@ -37,6 +39,8 @@
*/
enum event_type {
EVENT_MORE_DATA = 0, // Request to write more data to buffer.
+ EVENT_UNDERRUN = 1, // Buffer underrun occurred. This will not occur for
+ // static tracks.
EVENT_NEW_IAUDIOTRACK = 6, // IAudioTrack was re-created, either due to re-routing and
// voluntary invalidation by mediaserver, or mediaserver crash.
EVENT_STREAM_END = 7, // Sent after all the buffers queued in AF and HW are played
@@ -104,15 +108,14 @@
*
* TODO: Revive removed arguments after offload mode is supported.
*/
- JAudioTrack(audio_stream_type_t streamType,
- uint32_t sampleRate,
+ JAudioTrack(uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
callback_t cbf,
void* user,
size_t frameCount = 0,
audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
- const audio_attributes_t* pAttributes = NULL,
+ const jobject pAttributes = NULL,
float maxRequiredSpeed = 1.0f);
/*
@@ -158,10 +161,10 @@
* 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 true if timestamp is valid.
- * The timestamp parameter is undefined on return, if false is returned.
+ * Returns NO_ERROR if timestamp is valid.
+ * NO_INIT if finds error, and timestamp parameter will be undefined on return.
*/
- bool getTimestamp(AudioTimestamp& timestamp);
+ status_t getTimestamp(AudioTimestamp& timestamp);
// TODO: This doc is just copied from AudioTrack.h. Revise it after implemenation.
/* Return the extended timestamp, with additional timebase info and improved drain behavior.
@@ -324,37 +327,43 @@
audio_format_t format();
+ size_t frameSize();
+
/*
* Dumps the state of an audio track.
* Not a general-purpose API; intended only for use by media player service to dump its tracks.
*/
status_t dump(int fd, const Vector<String16>& args) const;
- /* Returns the ID of the audio device actually used by the output to which this AudioTrack is
- * attached. When the AudioTrack is inactive, it will return AUDIO_PORT_HANDLE_NONE.
+ /* Returns the AudioDeviceInfo used by the output to which this AudioTrack is
+ * attached.
*/
- audio_port_handle_t getRoutedDeviceId();
+ jobject getRoutedDevice();
/* Returns the ID of the audio session this AudioTrack belongs to. */
audio_session_t getAudioSessionId();
- /* Selects the audio device to use for output of this AudioTrack. A value of
- * AUDIO_PORT_HANDLE_NONE indicates default routing.
+ /* Sets the preferred audio device to use for output of this AudioTrack.
*
* Parameters:
- * The device ID of the selected device (as returned by the AudioDevicesManager API).
+ * Device: an AudioDeviceInfo object.
*
* Returned value:
* - NO_ERROR: successful operation
- * - BAD_VALUE: failed to find the valid output device with given device Id.
+ * - BAD_VALUE: failed to set the device
*/
- status_t setOutputDevice(audio_port_handle_t deviceId);
+ status_t setPreferredDevice(jobject device);
// TODO: Add AUDIO_OUTPUT_FLAG_DIRECT when it is possible to check.
// TODO: Add AUDIO_FLAG_HW_AV_SYNC when it is possible to check.
/* Returns the flags */
audio_output_flags_t getFlags() const { return mFlags; }
+ /* We don't keep stream type here,
+ * instead, we keep attributes and call getVolumeControlStream() to get stream type
+ */
+ audio_stream_type_t getAudioStreamType();
+
/* Obtain the pending duration in milliseconds for playback of pure PCM data remaining in
* AudioTrack.
*
@@ -369,33 +378,75 @@
* Replaces any previously installed callback.
*
* Parameters:
- *
- * callback: The callback interface
+ * Listener: the listener to receive notification of rerouting events.
+ * Handler: the handler to handler the rerouting events.
*
* Returns NO_ERROR if successful.
- * INVALID_OPERATION if the same callback is already installed.
- * NO_INIT or PREMISSION_DENIED if AudioFlinger service is not reachable
- * BAD_VALUE if the callback is NULL
+ * (TODO) INVALID_OPERATION if the same callback is already installed.
+ * (TODO) NO_INIT or PREMISSION_DENIED if AudioFlinger service is not reachable
+ * (TODO) BAD_VALUE if the callback is NULL
*/
- status_t addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
+ status_t addAudioDeviceCallback(jobject listener, jobject rd);
/* Removes an AudioDeviceCallback.
*
* Parameters:
- *
- * callback: The callback interface
+ * Listener: the listener to receive notification of rerouting events.
*
* Returns NO_ERROR if successful.
- * INVALID_OPERATION if the callback is not installed
- * BAD_VALUE if the callback is NULL
+ * (TODO) INVALID_OPERATION if the callback is not installed
+ * (TODO) BAD_VALUE if the callback is NULL
*/
- status_t removeAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
+ status_t removeAudioDeviceCallback(jobject listener);
+
+ /* Register all backed-up routing delegates.
+ *
+ * Parameters:
+ * routingDelegates: backed-up routing delegates
+ *
+ */
+ void registerRoutingDelegates(std::vector<std::pair<jobject, jobject>>& routingDelegates);
+
+ /* get listener from RoutingDelegate object
+ */
+ static jobject getListener(const jobject routingDelegateObj);
+
+ /* get handler from RoutingDelegate object
+ */
+ static jobject getHandler(const jobject routingDelegateObj);
+
+ /* convert local reference to global reference.
+ */
+ static jobject addGlobalRef(const jobject obj);
+
+ /* erase global reference.
+ *
+ * Returns NO_ERROR if succeeds
+ * BAD_VALUE if obj is NULL
+ */
+ static status_t removeGlobalRef(const jobject obj);
+
+ /*
+ * Parameters:
+ * map and key
+ *
+ * Returns value if key is in the map
+ * nullptr if key is not in the map
+ */
+ static jobject findByKey(std::vector<std::pair<jobject, jobject>>& mp, const jobject key);
+
+ /*
+ * Parameters:
+ * map and key
+ */
+ static void eraseByKey(std::vector<std::pair<jobject, jobject>>& mp, const jobject key);
private:
audio_output_flags_t mFlags;
jclass mAudioTrackCls;
jobject mAudioTrackObj;
+ sp<JObjectHolder> mAudioAttributesObj;
/* Creates a Java VolumeShaper.Configuration object from VolumeShaper::Configuration */
jobject createVolumeShaperConfigurationObj(
diff --git a/media/libmediaplayer2/include/mediaplayer2/JObjectHolder.h b/media/libmediaplayer2/include/mediaplayer2/JObjectHolder.h
new file mode 100644
index 0000000..93d8b40
--- /dev/null
+++ b/media/libmediaplayer2/include/mediaplayer2/JObjectHolder.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef JOBJECT_HOLDER_H_
+
+#define JOBJECT_HOLDER_H_
+
+#include "jni.h"
+#include <mediaplayer2/JavaVMHelper.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+// Helper class for managing global reference of jobject.
+struct JObjectHolder : public RefBase {
+ JObjectHolder(jobject obj) {
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
+ mJObject = reinterpret_cast<jobject>(env->NewGlobalRef(obj));
+ }
+
+ virtual ~JObjectHolder() {
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
+ env->DeleteGlobalRef(mJObject);
+ }
+
+ jobject getJObject() { return mJObject; }
+
+private:
+ jobject mJObject;
+};
+
+} //" android
+
+#endif // JOBJECT_HOLDER_H_
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h
index fe1005b..fc020ca 100644
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h
@@ -19,10 +19,16 @@
#define ANDROID_MEDIAPLAYER2AUDIOOUTPUT_H
#include <mediaplayer2/MediaPlayer2Interface.h>
+#include <mediaplayer2/JAudioTrack.h>
+#include <mediaplayer2/JObjectHolder.h>
+#include <vector>
+#include <utility>
#include <utils/String16.h>
#include <utils/Vector.h>
+#include "jni.h"
+
namespace android {
class AudioTrack;
@@ -35,12 +41,11 @@
MediaPlayer2AudioOutput(audio_session_t sessionId,
uid_t uid,
int pid,
- const audio_attributes_t * attr,
- const sp<AudioSystem::AudioDeviceCallback>& deviceCallback);
+ const jobject attributes);
virtual ~MediaPlayer2AudioOutput();
virtual bool ready() const {
- return mTrack != 0;
+ return mJAudioTrack != nullptr;
}
virtual ssize_t bufferSize() const;
virtual ssize_t frameCount() const;
@@ -53,6 +58,7 @@
virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const;
virtual status_t getFramesWritten(uint32_t *frameswritten) const;
virtual audio_session_t getSessionId() const;
+ virtual void setSessionId(const audio_session_t id);
virtual uint32_t getSampleRate() const;
virtual int64_t getBufferDurationInUs() const;
@@ -62,7 +68,6 @@
AudioCallback cb, void *cookie,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
const audio_offload_info_t *offloadInfo = NULL,
- bool doNotReconnect = false,
uint32_t suggestedFrameCount = 0);
virtual status_t start();
@@ -71,11 +76,8 @@
virtual void flush();
virtual void pause();
virtual void close();
- void setAudioStreamType(audio_stream_type_t streamType);
- virtual audio_stream_type_t getAudioStreamType() const {
- return mStreamType;
- }
- void setAudioAttributes(const audio_attributes_t * attributes);
+ void setAudioAttributes(const jobject attributes);
+ virtual audio_stream_type_t getAudioStreamType() const;
void setVolume(float volume);
virtual status_t setPlaybackRate(const AudioPlaybackRate& rate);
@@ -92,13 +94,11 @@
// TODO: return correct value.
//return mNextOutput == NULL;
}
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys);
-
// AudioRouting
- virtual status_t setOutputDevice(audio_port_handle_t deviceId);
- virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
- virtual status_t enableAudioDeviceCallback(bool enabled);
+ virtual status_t setPreferredDevice(jobject device);
+ virtual jobject getRoutedDevice();
+ virtual status_t addAudioDeviceCallback(jobject routingDelegate);
+ virtual status_t removeAudioDeviceCallback(jobject listener);
private:
static void setMinBufferCount();
@@ -107,12 +107,11 @@
void close_l();
status_t updateTrack_l();
- sp<AudioTrack> mTrack;
+ sp<JAudioTrack> mJAudioTrack;
AudioCallback mCallback;
void * mCallbackCookie;
CallbackData * mCallbackData;
- audio_stream_type_t mStreamType;
- audio_attributes_t * mAttributes;
+ sp<JObjectHolder> mAttributes;
float mVolume;
AudioPlaybackRate mPlaybackRate;
uint32_t mSampleRateHz; // sample rate of the content, as set in open()
@@ -124,11 +123,9 @@
float mSendLevel;
int mAuxEffectId;
audio_output_flags_t mFlags;
- audio_port_handle_t mSelectedDeviceId;
- audio_port_handle_t mRoutedDeviceId;
- bool mDeviceCallbackEnabled;
- wp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
+ sp<JObjectHolder> mPreferredDevice;
mutable Mutex mLock;
+ std::vector<std::pair<jobject, jobject>> mRoutingDelegates; // <listener, routingDelegate>
// static variables below not protected by mutex
static bool mIsOnEmulator;
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
index 846441e..07a7946 100644
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
@@ -23,6 +23,7 @@
#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/RefBase.h>
+#include <jni.h>
#include <media/AVSyncSettings.h>
#include <media/AudioResamplerPublic.h>
@@ -33,6 +34,7 @@
#include <media/stagefright/foundation/AHandler.h>
#include <mediaplayer2/MediaPlayer2Types.h>
+#include "jni.h"
#include "mediaplayer2.pb.h"
using android::media::MediaPlayer2Proto::PlayerMessage;
@@ -106,7 +108,6 @@
void *cookie = NULL,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
const audio_offload_info_t *offloadInfo = NULL,
- bool doNotReconnect = false,
uint32_t suggestedFrameCount = 0) = 0;
virtual status_t start() = 0;
@@ -142,9 +143,10 @@
}
// AudioRouting
- virtual status_t setOutputDevice(audio_port_handle_t deviceId);
- virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
- virtual status_t enableAudioDeviceCallback(bool enabled);
+ virtual status_t setPreferredDevice(jobject device);
+ virtual jobject getRoutedDevice();
+ virtual status_t addAudioDeviceCallback(jobject routingDelegate);
+ virtual status_t removeAudioDeviceCallback(jobject listener);
};
MediaPlayer2Interface() : mListener(NULL) { }
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h
index 10e07ea..2430289 100644
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h
@@ -42,7 +42,6 @@
MEDIA2_SUBTITLE_DATA = 201,
MEDIA2_META_DATA = 202,
MEDIA2_DRM_INFO = 210,
- MEDIA2_AUDIO_ROUTING_CHANGED = 10000,
};
// Generic error codes for the media player framework. Errors are fatal, the
diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
index 4f73ad3..d881813 100644
--- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
+++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
@@ -24,7 +24,9 @@
#include <media/mediaplayer_common.h>
#include <mediaplayer2/MediaPlayer2Interface.h>
#include <mediaplayer2/MediaPlayer2Types.h>
+#include <mediaplayer2/JObjectHolder.h>
+#include <jni.h>
#include <utils/Errors.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
@@ -32,6 +34,8 @@
#include <utils/Vector.h>
#include <system/audio-base.h>
+#include "jni.h"
+
namespace android {
struct ANativeWindowWrapper;
@@ -96,16 +100,18 @@
audio_session_t getAudioSessionId();
status_t setAuxEffectSendLevel(float level);
status_t attachAuxEffect(int effectId);
- status_t setParameter(int key, const Parcel& request);
+ status_t setAudioAttributes(const jobject attributes);
+ jobject getAudioAttributes();
status_t getParameter(int key, Parcel* reply);
// Modular DRM
status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId);
status_t releaseDrm();
// AudioRouting
- status_t setOutputDevice(audio_port_handle_t deviceId);
- audio_port_handle_t getRoutedDeviceId();
- status_t enableAudioDeviceCallback(bool enabled);
+ status_t setPreferredDevice(jobject device);
+ jobject getRoutedDevice();
+ status_t addAudioDeviceCallback(jobject routingDelegate);
+ status_t removeAudioDeviceCallback(jobject listener);
status_t dump(int fd, const Vector<String16>& args);
@@ -116,14 +122,14 @@
// Disconnect from the currently connected ANativeWindow.
void disconnectNativeWindow_l();
- status_t setAudioAttributes_l(const Parcel &request);
+ status_t setAudioAttributes_l(const jobject attributes);
void clear_l();
status_t seekTo_l(int64_t msec, MediaPlayer2SeekMode mode);
status_t prepareAsync_l();
status_t getDuration_l(int64_t *msec);
status_t reset_l();
- status_t checkStateForKeySet_l(int key);
+ status_t checkState_l();
pid_t mPid;
uid_t mUid;
@@ -140,15 +146,13 @@
int64_t mSeekPosition;
MediaPlayer2SeekMode mSeekMode;
audio_stream_type_t mStreamType;
- Parcel* mAudioAttributesParcel;
bool mLoop;
float mVolume;
int mVideoWidth;
int mVideoHeight;
audio_session_t mAudioSessionId;
- audio_attributes_t * mAudioAttributes;
+ sp<JObjectHolder> mAudioAttributes;
float mSendLevel;
-
sp<ANativeWindowWrapper> mConnectedWindow;
};
diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp
index 480a630..04a6f68 100644
--- a/media/libmediaplayer2/mediaplayer2.cpp
+++ b/media/libmediaplayer2/mediaplayer2.cpp
@@ -53,78 +53,6 @@
const int kDumpLockRetries = 50;
const int kDumpLockSleepUs = 20000;
-// marshalling tag indicating flattened utf16 tags
-// keep in sync with frameworks/base/media/java/android/media/AudioAttributes.java
-const int32_t kAudioAttributesMarshallTagFlattenTags = 1;
-
-// Audio attributes format in a parcel:
-//
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | usage |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | content_type |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | source |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | flags |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | kAudioAttributesMarshallTagFlattenTags | // ignore tags if not found
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | flattened tags in UTF16 |
-// | ... |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-//
-// @param p Parcel that contains audio attributes.
-// @param[out] attributes On exit points to an initialized audio_attributes_t structure
-// @param[out] status On exit contains the status code to be returned.
-void unmarshallAudioAttributes(const Parcel& parcel, audio_attributes_t *attributes) {
- attributes->usage = (audio_usage_t) parcel.readInt32();
- attributes->content_type = (audio_content_type_t) parcel.readInt32();
- attributes->source = (audio_source_t) parcel.readInt32();
- attributes->flags = (audio_flags_mask_t) parcel.readInt32();
- const bool hasFlattenedTag = (parcel.readInt32() == kAudioAttributesMarshallTagFlattenTags);
- if (hasFlattenedTag) {
- // the tags are UTF16, convert to UTF8
- String16 tags = parcel.readString16();
- ssize_t realTagSize = utf16_to_utf8_length(tags.string(), tags.size());
- if (realTagSize <= 0) {
- strcpy(attributes->tags, "");
- } else {
- // copy the flattened string into the attributes as the destination for the conversion:
- // copying array size -1, array for tags was calloc'd, no need to NULL-terminate it
- size_t tagSize = realTagSize > AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 ?
- AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 : realTagSize;
- utf16_to_utf8(tags.string(), tagSize, attributes->tags,
- sizeof(attributes->tags) / sizeof(attributes->tags[0]));
- }
- } else {
- ALOGE("unmarshallAudioAttributes() received unflattened tags, ignoring tag values");
- strcpy(attributes->tags, "");
- }
-}
-
-class AudioDeviceUpdatedNotifier: public AudioSystem::AudioDeviceCallback {
-public:
- AudioDeviceUpdatedNotifier(const sp<MediaPlayer2Interface>& listener)
- : mListener(listener) { }
-
- ~AudioDeviceUpdatedNotifier() { }
-
- virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
- audio_port_handle_t deviceId) override {
- sp<MediaPlayer2Interface> listener = mListener.promote();
- if (listener != NULL) {
- listener->sendEvent(0, MEDIA2_AUDIO_ROUTING_CHANGED, audioIo, deviceId);
- } else {
- ALOGW("listener for process %d death is gone", MEDIA2_AUDIO_ROUTING_CHANGED);
- }
- }
-
-private:
- wp<MediaPlayer2Interface> mListener;
-};
-
class proxyListener : public MediaPlayer2InterfaceListener {
public:
proxyListener(const wp<MediaPlayer2> &player)
@@ -314,7 +242,7 @@
mLockThreadId = 0;
mListener = NULL;
mStreamType = AUDIO_STREAM_MUSIC;
- mAudioAttributesParcel = NULL;
+ mAudioAttributes = NULL;
mCurrentPosition = -1;
mCurrentSeekMode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC;
mSeekPosition = -1;
@@ -331,21 +259,14 @@
mPid = IPCThreadState::self()->getCallingPid();
mUid = IPCThreadState::self()->getCallingUid();
- mAudioAttributes = NULL;
+ mAudioOutput = new MediaPlayer2AudioOutput(mAudioSessionId, mUid, mPid, NULL /*attributes*/);
}
MediaPlayer2::~MediaPlayer2() {
ALOGV("destructor");
- if (mAudioAttributesParcel != NULL) {
- delete mAudioAttributesParcel;
- mAudioAttributesParcel = NULL;
- }
AudioSystem::releaseAudioSessionId(mAudioSessionId, -1);
disconnect();
removePlayer(this);
- if (mAudioAttributes != NULL) {
- free(mAudioAttributes);
- }
}
bool MediaPlayer2::init() {
@@ -434,8 +355,6 @@
clear_l();
player->setListener(new proxyListener(this));
- mAudioOutput = new MediaPlayer2AudioOutput(mAudioSessionId, mUid,
- mPid, mAudioAttributes, new AudioDeviceUpdatedNotifier(player));
player->setAudioSink(mAudioOutput);
err = player->setDataSource(dsd);
@@ -585,22 +504,9 @@
return mPlayer->setBufferingSettings(buffering);
}
-status_t MediaPlayer2::setAudioAttributes_l(const Parcel &parcel) {
- if (mAudioAttributes != NULL) {
- free(mAudioAttributes);
- }
- mAudioAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
- if (mAudioAttributes == NULL) {
- return NO_MEMORY;
- }
- unmarshallAudioAttributes(parcel, mAudioAttributes);
-
- ALOGV("setAudioAttributes_l() usage=%d content=%d flags=0x%x tags=%s",
- mAudioAttributes->usage, mAudioAttributes->content_type, mAudioAttributes->flags,
- mAudioAttributes->tags);
-
- if (mAudioOutput != 0) {
- mAudioOutput->setAudioAttributes(mAudioAttributes);
+status_t MediaPlayer2::setAudioAttributes_l(const jobject attributes) {
+ if (mAudioOutput != NULL) {
+ mAudioOutput->setAudioAttributes(attributes);
}
return NO_ERROR;
}
@@ -609,13 +515,11 @@
ALOGV("prepareAsync");
Mutex::Autolock _l(mLock);
if ((mPlayer != 0) && (mCurrentState & MEDIA_PLAYER2_INITIALIZED)) {
- if (mAudioAttributesParcel != NULL) {
- status_t err = setAudioAttributes_l(*mAudioAttributesParcel);
+ if (mAudioAttributes != NULL) {
+ status_t err = setAudioAttributes_l(mAudioAttributes->getJObject());
if (err != OK) {
return err;
}
- } else if (mAudioOutput != 0) {
- mAudioOutput->setAudioStreamType(mStreamType);
}
mCurrentState = MEDIA_PLAYER2_PREPARING;
return mPlayer->prepareAsync();
@@ -1002,6 +906,9 @@
AudioSystem::releaseAudioSessionId(mAudioSessionId, -1);
mAudioSessionId = sessionId;
}
+ if (mAudioOutput != NULL && mAudioSessionId != mAudioOutput->getSessionId()) {
+ mAudioOutput->setSessionId(sessionId);
+ }
return NO_ERROR;
}
@@ -1034,67 +941,37 @@
}
// always call with lock held
-status_t MediaPlayer2::checkStateForKeySet_l(int key) {
- switch(key) {
- case MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES:
- if (mCurrentState & ( MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_STARTED |
- MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_PLAYBACK_COMPLETE) ) {
- // Can't change the audio attributes after prepare
- ALOGE("trying to set audio attributes called in state %d", mCurrentState);
- return INVALID_OPERATION;
- }
- break;
- default:
- // parameter doesn't require player state check
- break;
+status_t MediaPlayer2::checkState_l() {
+ if (mCurrentState & ( MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_STARTED |
+ MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_PLAYBACK_COMPLETE) ) {
+ // Can't change the audio attributes after prepare
+ ALOGE("trying to set audio attributes called in state %d", mCurrentState);
+ return INVALID_OPERATION;
}
return OK;
}
-status_t MediaPlayer2::setParameter(int key, const Parcel& request) {
- ALOGV("MediaPlayer2::setParameter(%d)", key);
+status_t MediaPlayer2::setAudioAttributes(const jobject attributes) {
+ ALOGV("MediaPlayer2::setAudioAttributes");
status_t status = INVALID_OPERATION;
Mutex::Autolock _l(mLock);
- if (checkStateForKeySet_l(key) != OK) {
+ if (checkState_l() != OK) {
return status;
}
- switch (key) {
- case MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES:
- // save the marshalled audio attributes
- if (mAudioAttributesParcel != NULL) {
- delete mAudioAttributesParcel;
- }
- mAudioAttributesParcel = new Parcel();
- mAudioAttributesParcel->appendFrom(&request, 0, request.dataSize());
- status = setAudioAttributes_l(request);
- if (status != OK) {
- return status;
- }
- break;
- default:
- ALOGV_IF(mPlayer == NULL, "setParameter: no active player");
- break;
- }
-
- if (mPlayer != NULL) {
- status = mPlayer->setParameter(key, request);
- }
+ mAudioAttributes = new JObjectHolder(attributes);
+ status = setAudioAttributes_l(attributes);
return status;
}
+jobject MediaPlayer2::getAudioAttributes() {
+ ALOGV("MediaPlayer2::getAudioAttributes)");
+ Mutex::Autolock _l(mLock);
+ return mAudioAttributes != NULL ? mAudioAttributes->getJObject() : NULL;
+}
+
status_t MediaPlayer2::getParameter(int key, Parcel *reply) {
ALOGV("MediaPlayer2::getParameter(%d)", key);
Mutex::Autolock _l(mLock);
- if (key == MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES) {
- if (reply == NULL) {
- return BAD_VALUE;
- }
- if (mAudioAttributesParcel != NULL) {
- reply->appendFrom(mAudioAttributesParcel, 0, mAudioAttributesParcel->dataSize());
- }
- return OK;
- }
-
if (mPlayer == NULL) {
ALOGV("getParameter: no active player");
return INVALID_OPERATION;
@@ -1283,36 +1160,40 @@
return status;
}
-status_t MediaPlayer2::setOutputDevice(audio_port_handle_t deviceId) {
+status_t MediaPlayer2::setPreferredDevice(jobject device) {
Mutex::Autolock _l(mLock);
if (mAudioOutput == NULL) {
- ALOGV("setOutputDevice: audio sink not init");
+ ALOGV("setPreferredDevice: audio sink not init");
return NO_INIT;
}
- return mAudioOutput->setOutputDevice(deviceId);
+ return mAudioOutput->setPreferredDevice(device);
}
-audio_port_handle_t MediaPlayer2::getRoutedDeviceId() {
+jobject MediaPlayer2::getRoutedDevice() {
Mutex::Autolock _l(mLock);
if (mAudioOutput == NULL) {
- ALOGV("getRoutedDeviceId: audio sink not init");
- return AUDIO_PORT_HANDLE_NONE;
+ ALOGV("getRoutedDevice: audio sink not init");
+ return nullptr;
}
- audio_port_handle_t deviceId;
- status_t status = mAudioOutput->getRoutedDeviceId(&deviceId);
- if (status != NO_ERROR) {
- return AUDIO_PORT_HANDLE_NONE;
- }
- return deviceId;
+ return mAudioOutput->getRoutedDevice();
}
-status_t MediaPlayer2::enableAudioDeviceCallback(bool enabled) {
+status_t MediaPlayer2::addAudioDeviceCallback(jobject routingDelegate) {
Mutex::Autolock _l(mLock);
if (mAudioOutput == NULL) {
ALOGV("addAudioDeviceCallback: player not init");
return NO_INIT;
}
- return mAudioOutput->enableAudioDeviceCallback(enabled);
+ return mAudioOutput->addAudioDeviceCallback(routingDelegate);
+}
+
+status_t MediaPlayer2::removeAudioDeviceCallback(jobject listener) {
+ Mutex::Autolock _l(mLock);
+ if (mAudioOutput == NULL) {
+ ALOGV("addAudioDeviceCallback: player not init");
+ return NO_INIT;
+ }
+ return mAudioOutput->removeAudioDeviceCallback(listener);
}
status_t MediaPlayer2::dump(int fd, const Vector<String16>& args) {
diff --git a/media/libmediaplayer2/nuplayer2/Android.bp b/media/libmediaplayer2/nuplayer2/Android.bp
index c3c94b6..93c218e 100644
--- a/media/libmediaplayer2/nuplayer2/Android.bp
+++ b/media/libmediaplayer2/nuplayer2/Android.bp
@@ -1,6 +1,7 @@
cc_library_static {
srcs: [
+ "JMediaPlayer2Utils.cpp",
"JWakeLock.cpp",
"GenericSource2.cpp",
"HTTPLiveSource2.cpp",
@@ -29,6 +30,7 @@
"frameworks/av/media/libstagefright/rtsp",
"frameworks/av/media/libstagefright/timedtext",
"frameworks/av/media/ndk",
+ "frameworks/base/core/jni",
],
cflags: [
@@ -57,6 +59,7 @@
static_libs: [
"libmedia_helper",
"libmediaplayer2-protos",
+ "libmedia2_jni_core",
],
name: "libstagefright_nuplayer2",
diff --git a/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.cpp b/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.cpp
new file mode 100644
index 0000000..bbd22bc
--- /dev/null
+++ b/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "JMediaPlayer2Utils"
+
+#include "JMediaPlayer2Utils.h"
+#include <mediaplayer2/JavaVMHelper.h>
+
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/Utils.h>
+#include <nativehelper/JNIHelp.h>
+#include <utils/Log.h>
+
+#include "log/log.h"
+
+namespace android {
+
+static const int64_t kOffloadMinDurationSec = 60;
+
+// static
+bool JMediaPlayer2Utils::isOffloadedAudioPlaybackSupported(
+ const sp<MetaData>& meta, bool hasVideo, bool isStreaming, audio_stream_type_t streamType)
+{
+ if (hasVideo || streamType != AUDIO_STREAM_MUSIC) {
+ return false;
+ }
+
+ audio_offload_info_t info = AUDIO_INFO_INITIALIZER;
+ if (OK != getAudioOffloadInfo(meta, hasVideo, isStreaming, streamType, &info)) {
+ return false;
+ }
+
+ if (info.duration_us < kOffloadMinDurationSec * 1000000) {
+ return false;
+ }
+
+ int32_t audioFormat = audioFormatFromNative(info.format);
+ int32_t channelMask = outChannelMaskFromNative(info.channel_mask);
+ if (audioFormat == ENCODING_INVALID || channelMask == CHANNEL_INVALID) {
+ return false;
+ }
+
+ JNIEnv* env = JavaVMHelper::getJNIEnv();
+ jclass jMP2UtilsCls = env->FindClass("android/media/MediaPlayer2Utils");
+ jmethodID jSetAudioOutputDeviceById = env->GetStaticMethodID(
+ jMP2UtilsCls, "isOffloadedAudioPlaybackSupported", "(III)Z");
+ jboolean result = env->CallStaticBooleanMethod(
+ jMP2UtilsCls, jSetAudioOutputDeviceById, audioFormat, info.sample_rate, channelMask);
+ return result;
+}
+
+} // namespace android
diff --git a/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.h b/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.h
new file mode 100644
index 0000000..fcbd43c
--- /dev/null
+++ b/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _J_MEDIAPLAYER2_UTILS2_H_
+#define _J_MEDIAPLAYER2_UTILS2_H_
+
+#include <media/stagefright/MetaData.h>
+
+#include "jni.h"
+#include "android_media_AudioFormat.h"
+
+namespace android {
+
+struct JMediaPlayer2Utils {
+ static bool isOffloadedAudioPlaybackSupported(
+ const sp<MetaData>& meta, bool hasVideo, bool isStreaming,
+ audio_stream_type_t streamType);
+};
+
+} // namespace android
+
+#endif // _J_MEDIAPLAYER2_UTILS2_H_
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
index bc17d13..fef9fae 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
@@ -24,6 +24,7 @@
#include "NuPlayer2.h"
#include "HTTPLiveSource2.h"
+#include "JMediaPlayer2Utils.h"
#include "NuPlayer2CCDecoder.h"
#include "NuPlayer2Decoder.h"
#include "NuPlayer2DecoderBase.h"
@@ -643,6 +644,7 @@
}
void NuPlayer2::onMessageReceived(const sp<AMessage> &msg) {
+
switch (msg->what()) {
case kWhatSetDataSource:
{
@@ -1698,7 +1700,8 @@
}
mOffloadAudio =
- canOffloadStream(audioMeta, hasVideo, mCurrentSourceInfo.mSource->isStreaming(), streamType)
+ JMediaPlayer2Utils::isOffloadedAudioPlaybackSupported(
+ audioMeta, hasVideo, mCurrentSourceInfo.mSource->isStreaming(), streamType)
&& (mPlaybackSettings.mSpeed == 1.f && mPlaybackSettings.mPitch == 1.f);
// Modular DRM: Disabling audio offload if the source is protected
@@ -1717,7 +1720,7 @@
mRenderer = new Renderer(mAudioSink, mMediaClock, notify, flags);
mRendererLooper = new ALooper;
mRendererLooper->setName("NuPlayerRenderer");
- mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
+ mRendererLooper->start(false, true, ANDROID_PRIORITY_AUDIO);
mRendererLooper->registerHandler(mRenderer);
status_t err = mRenderer->setPlaybackSettings(mPlaybackSettings);
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp
index 652cc89..7db78c1 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp
@@ -1848,6 +1848,7 @@
bool isStreaming) {
ALOGV("openAudioSink: offloadOnly(%d) offloadingAudio(%d)",
offloadOnly, offloadingAudio());
+
bool audioSinkChanged = false;
int32_t numChannels;
@@ -1989,13 +1990,6 @@
const uint32_t frameCount =
(unsigned long long)sampleRate * getAudioSinkPcmMsSetting() / 1000;
- // The doNotReconnect means AudioSink will signal back and let NuPlayer2 to re-construct
- // AudioSink. We don't want this when there's video because it will cause a video seek to
- // the previous I frame. But we do want this when there's only audio because it will give
- // NuPlayer2 a chance to switch from non-offload mode to offload mode.
- // So we only set doNotReconnect when there's no video.
- const bool doNotReconnect = !hasVideo;
-
// We should always be able to set our playback settings if the sink is closed.
LOG_ALWAYS_FATAL_IF(mAudioSink->setPlaybackRate(mPlaybackSettings) != OK,
"onOpenAudioSink: can't set playback rate on closed sink");
@@ -2008,7 +2002,6 @@
mUseAudioCallback ? this : NULL,
(audio_output_flags_t)pcmFlags,
NULL,
- doNotReconnect,
frameCount);
if (err != OK) {
ALOGW("openAudioSink: non offloaded open failed status: %d", err);
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 8cd6eda..f3b69d6 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -1547,6 +1547,16 @@
notifyBufferingUpdate(100);
}
+ if (mPreparing) {
+ notifyPreparedAndCleanup(finalStatus);
+ mPreparing = false;
+ } else if (mSentPauseOnBuffering) {
+ sendCacheStats();
+ mSentPauseOnBuffering = false;
+ sp<AMessage> notify = dupNotify();
+ notify->setInt32("what", kWhatResumeOnBufferingEnd);
+ notify->post();
+ }
return;
}
diff --git a/media/libmediaplayerservice/tests/Android.bp b/media/libmediaplayerservice/tests/Android.bp
index e86b68a..4749a8b 100644
--- a/media/libmediaplayerservice/tests/Android.bp
+++ b/media/libmediaplayerservice/tests/Android.bp
@@ -11,6 +11,7 @@
"libutils",
"android.hardware.drm@1.0",
"android.hardware.drm@1.1",
+ "android.hardware.drm@1.2",
],
compile_multilib: "32",
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 96993e9..a024754 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -569,7 +569,7 @@
}
-std::vector<std::pair<const char *, uint32_t>> tagMappings {
+static std::vector<std::pair<const char *, uint32_t>> stringMappings {
{
{ "album", kKeyAlbum },
{ "albumartist", kKeyAlbumArtist },
@@ -581,47 +581,108 @@
{ "date", kKeyDate },
{ "discnum", kKeyDiscNumber },
{ "genre", kKeyGenre },
+ { "location", kKeyLocation },
{ "lyricist", kKeyWriter },
{ "title", kKeyTitle },
{ "year", kKeyYear },
}
};
-void convertMessageToMetaDataTags(const sp<AMessage> &msg, sp<MetaData> &meta) {
- for (auto elem : tagMappings) {
+static std::vector<std::pair<const char *, uint32_t>> int64Mappings {
+ {
+ { "exif-offset", kKeyExifOffset },
+ { "exif-size", kKeyExifSize },
+ }
+};
+
+static std::vector<std::pair<const char *, uint32_t>> int32Mappings {
+ {
+ { "loop", kKeyAutoLoop },
+ { "time-scale", kKeyTimeScale },
+ { "crypto-mode", kKeyCryptoMode },
+ { "crypto-default-iv-size", kKeyCryptoDefaultIVSize },
+ { "crypto-encrypted-byte-block", kKeyEncryptedByteBlock },
+ { "crypto-skip-byte-block", kKeySkipByteBlock },
+ { "max-bitrate", kKeyMaxBitRate },
+ { "pcm-big-endian", kKeyPcmBigEndian },
+ { "temporal-layer-count", kKeyTemporalLayerCount },
+ { "thumbnail-width", kKeyThumbnailWidth },
+ { "thumbnail-height", kKeyThumbnailHeight },
+ }
+};
+
+static std::vector<std::pair<const char *, uint32_t>> bufferMappings {
+ {
+ { "albumart", kKeyAlbumArt },
+ { "pssh", kKeyPssh },
+ { "crypto-iv", kKeyCryptoIV },
+ { "crypto-key", kKeyCryptoKey },
+ { "icc-profile", kKeyIccProfile },
+ { "text-format-data", kKeyTextFormatData },
+ }
+};
+
+void convertMessageToMetaDataFromMappings(const sp<AMessage> &msg, sp<MetaData> &meta) {
+ for (auto elem : stringMappings) {
AString value;
if (msg->findString(elem.first, &value)) {
meta->setCString(elem.second, value.c_str());
}
}
- sp<ABuffer> buf;
- if (msg->findBuffer("albumart", &buf)) {
- meta->setData(kKeyAlbumArt, MetaDataBase::Type::TYPE_NONE, buf->data(), buf->size());
+
+ for (auto elem : int64Mappings) {
+ int64_t value;
+ if (msg->findInt64(elem.first, &value)) {
+ meta->setInt64(elem.second, value);
+ }
}
- int32_t loop;
- if (msg->findInt32("loop", &loop)) {
- meta->setInt32(kKeyAutoLoop, loop);
+ for (auto elem : int32Mappings) {
+ int32_t value;
+ if (msg->findInt32(elem.first, &value)) {
+ meta->setInt32(elem.second, value);
+ }
+ }
+
+ for (auto elem : bufferMappings) {
+ sp<ABuffer> value;
+ if (msg->findBuffer(elem.first, &value)) {
+ meta->setData(elem.second,
+ MetaDataBase::Type::TYPE_NONE, value->data(), value->size());
+ }
}
}
-void convertMetaDataToMessageTags(const MetaDataBase *meta, sp<AMessage> format) {
- for (auto elem : tagMappings) {
+void convertMetaDataToMessageFromMappings(const MetaDataBase *meta, sp<AMessage> format) {
+ for (auto elem : stringMappings) {
const char *value;
if (meta->findCString(elem.second, &value)) {
format->setString(elem.first, value, strlen(value));
}
}
- uint32_t type;
- const void* data;
- size_t size;
- if (meta->findData(kKeyAlbumArt, &type, &data, &size)) {
- sp<ABuffer> buf = ABuffer::CreateAsCopy(data, size);
- format->setBuffer("albumart", buf);
+
+ for (auto elem : int64Mappings) {
+ int64_t value;
+ if (meta->findInt64(elem.second, &value)) {
+ format->setInt64(elem.first, value);
+ }
}
- int32_t loop;
- if (meta->findInt32(kKeyAutoLoop, &loop)) {
- format->setInt32("loop", loop);
+
+ for (auto elem : int32Mappings) {
+ int32_t value;
+ if (meta->findInt32(elem.second, &value)) {
+ format->setInt32(elem.first, value);
+ }
+ }
+
+ for (auto elem : bufferMappings) {
+ uint32_t type;
+ const void* data;
+ size_t size;
+ if (meta->findData(elem.second, &type, &data, &size)) {
+ sp<ABuffer> buf = ABuffer::CreateAsCopy(data, size);
+ format->setBuffer(elem.first, buf);
+ }
}
}
@@ -648,7 +709,7 @@
sp<AMessage> msg = new AMessage;
msg->setString("mime", mime);
- convertMetaDataToMessageTags(meta, msg);
+ convertMetaDataToMessageFromMappings(meta, msg);
uint32_t type;
const void *data;
@@ -1117,7 +1178,7 @@
msg->setInt32("max-bitrate", (int32_t)maxBitrate);
}
}
- } else if (meta->findData(kTypeD263, &type, &data, &size)) {
+ } else if (meta->findData(kKeyD263, &type, &data, &size)) {
const uint8_t *ptr = (const uint8_t *)data;
parseH263ProfileLevelFromD263(ptr, size, msg);
} else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
@@ -1204,13 +1265,17 @@
msg->setBuffer("csd-0", buffer);
parseVp9ProfileLevelFromCsd(buffer, msg);
- }
-
- // TODO expose "crypto-key"/kKeyCryptoKey through public api
- if (meta->findData(kKeyCryptoKey, &type, &data, &size)) {
+ } else if (meta->findData(kKeyAlacMagicCookie, &type, &data, &size)) {
+ ALOGV("convertMetaDataToMessage found kKeyAlacMagicCookie of size %zu\n", size);
sp<ABuffer> buffer = new (std::nothrow) ABuffer(size);
- msg->setBuffer("crypto-key", buffer);
+ if (buffer.get() == NULL || buffer->base() == NULL) {
+ return NO_MEMORY;
+ }
memcpy(buffer->data(), data, size);
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ msg->setBuffer("csd-0", buffer);
}
*format = msg;
@@ -1411,7 +1476,7 @@
ALOGW("did not find mime type");
}
- convertMessageToMetaDataTags(msg, meta);
+ convertMessageToMetaDataFromMappings(msg, meta);
int64_t durationUs;
if (msg->findInt64("durationUs", &durationUs)) {
@@ -1444,7 +1509,7 @@
meta->setInt32(kKeyWidth, width);
meta->setInt32(kKeyHeight, height);
} else {
- ALOGW("did not find width and/or height");
+ ALOGV("did not find width and/or height");
}
int32_t sarWidth, sarHeight;
@@ -1600,7 +1665,7 @@
// The written ESDS is actually for an audio stream, but it's enough
// for transporting the CSD to muxers.
reassembleESDS(csd0, esds.data());
- meta->setData(kKeyESDS, kKeyESDS, esds.data(), esds.size());
+ meta->setData(kKeyESDS, kTypeESDS, esds.data(), esds.size());
} else if (mime == MEDIA_MIMETYPE_VIDEO_HEVC ||
mime == MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC) {
std::vector<uint8_t> hvcc(csd0size + 1024);
@@ -1621,17 +1686,20 @@
if (msg->findBuffer("csd-1", &csd1)) {
meta->setData(kKeyVorbisBooks, 0, csd1->data(), csd1->size());
}
+ } else if (mime == MEDIA_MIMETYPE_AUDIO_ALAC) {
+ meta->setData(kKeyAlacMagicCookie, 0, csd0->data(), csd0->size());
}
} else if (mime == MEDIA_MIMETYPE_VIDEO_AVC && msg->findBuffer("csd-avc", &csd0)) {
meta->setData(kKeyAVCC, kTypeAVCC, csd0->data(), csd0->size());
} else if ((mime == MEDIA_MIMETYPE_VIDEO_HEVC || mime == MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)
&& msg->findBuffer("csd-hevc", &csd0)) {
meta->setData(kKeyHVCC, kTypeHVCC, csd0->data(), csd0->size());
- }
-
- int32_t timeScale;
- if (msg->findInt32("time-scale", &timeScale)) {
- meta->setInt32(kKeyTimeScale, timeScale);
+ } else if (msg->findBuffer("esds", &csd0)) {
+ meta->setData(kKeyESDS, kTypeESDS, csd0->data(), csd0->size());
+ } else if (msg->findBuffer("mpeg2-stream-header", &csd0)) {
+ meta->setData(kKeyStreamHeader, 'mdat', csd0->data(), csd0->size());
+ } else if (msg->findBuffer("d263", &csd0)) {
+ meta->setData(kKeyD263, kTypeD263, csd0->data(), csd0->size());
}
// XXX TODO add whatever other keys there are
@@ -1710,6 +1778,7 @@
{ MEDIA_MIMETYPE_AUDIO_EAC3, AUDIO_FORMAT_E_AC3},
{ MEDIA_MIMETYPE_AUDIO_AC4, AUDIO_FORMAT_AC4},
{ MEDIA_MIMETYPE_AUDIO_FLAC, AUDIO_FORMAT_FLAC},
+ { MEDIA_MIMETYPE_AUDIO_ALAC, AUDIO_FORMAT_ALAC },
{ 0, AUDIO_FORMAT_INVALID }
};
@@ -1761,46 +1830,46 @@
return;
}
-bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo,
- bool isStreaming, audio_stream_type_t streamType)
+status_t getAudioOffloadInfo(const sp<MetaData>& meta, bool hasVideo,
+ bool isStreaming, audio_stream_type_t streamType, audio_offload_info_t *info)
{
const char *mime;
if (meta == NULL) {
- return false;
+ return BAD_VALUE;
}
CHECK(meta->findCString(kKeyMIMEType, &mime));
- audio_offload_info_t info = AUDIO_INFO_INITIALIZER;
+ (*info) = AUDIO_INFO_INITIALIZER;
- info.format = AUDIO_FORMAT_INVALID;
- if (mapMimeToAudioFormat(info.format, mime) != OK) {
+ info->format = AUDIO_FORMAT_INVALID;
+ if (mapMimeToAudioFormat(info->format, mime) != OK) {
ALOGE(" Couldn't map mime type \"%s\" to a valid AudioSystem::audio_format !", mime);
- return false;
+ return BAD_VALUE;
} else {
- ALOGV("Mime type \"%s\" mapped to audio_format %d", mime, info.format);
+ ALOGV("Mime type \"%s\" mapped to audio_format %d", mime, info->format);
}
- if (AUDIO_FORMAT_INVALID == info.format) {
+ if (AUDIO_FORMAT_INVALID == info->format) {
// can't offload if we don't know what the source format is
ALOGE("mime type \"%s\" not a known audio format", mime);
- return false;
+ return BAD_VALUE;
}
// Redefine aac format according to its profile
// Offloading depends on audio DSP capabilities.
int32_t aacaot = -1;
if (meta->findInt32(kKeyAACAOT, &aacaot)) {
- mapAACProfileToAudioFormat(info.format,(OMX_AUDIO_AACPROFILETYPE) aacaot);
+ mapAACProfileToAudioFormat(info->format,(OMX_AUDIO_AACPROFILETYPE) aacaot);
}
int32_t srate = -1;
if (!meta->findInt32(kKeySampleRate, &srate)) {
ALOGV("track of type '%s' does not publish sample rate", mime);
}
- info.sample_rate = srate;
+ info->sample_rate = srate;
int32_t cmask = 0;
- if (!meta->findInt32(kKeyChannelMask, &cmask)) {
+ if (!meta->findInt32(kKeyChannelMask, &cmask) || cmask == CHANNEL_MASK_USE_CHANNEL_ORDER) {
ALOGV("track of type '%s' does not publish channel mask", mime);
// Try a channel count instead
@@ -1811,25 +1880,34 @@
cmask = audio_channel_out_mask_from_count(channelCount);
}
}
- info.channel_mask = cmask;
+ info->channel_mask = cmask;
int64_t duration = 0;
if (!meta->findInt64(kKeyDuration, &duration)) {
ALOGV("track of type '%s' does not publish duration", mime);
}
- info.duration_us = duration;
+ info->duration_us = duration;
int32_t brate = -1;
if (!meta->findInt32(kKeyBitRate, &brate)) {
ALOGV("track of type '%s' does not publish bitrate", mime);
}
- info.bit_rate = brate;
+ info->bit_rate = brate;
- info.stream_type = streamType;
- info.has_video = hasVideo;
- info.is_streaming = isStreaming;
+ info->stream_type = streamType;
+ info->has_video = hasVideo;
+ info->is_streaming = isStreaming;
+ return OK;
+}
+bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo,
+ bool isStreaming, audio_stream_type_t streamType)
+{
+ audio_offload_info_t info = AUDIO_INFO_INITIALIZER;
+ if (OK != getAudioOffloadInfo(meta, hasVideo, isStreaming, streamType, &info)) {
+ return false;
+ }
// Check if offload is possible for given format, stream type, sample rate,
// bit rate, duration, video and streaming
return AudioSystem::isOffloadSupported(info);
diff --git a/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder_basic_op_cequivalent.h b/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder_basic_op_cequivalent.h
index 3c7590c..7a86ec2 100644
--- a/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder_basic_op_cequivalent.h
+++ b/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder_basic_op_cequivalent.h
@@ -467,7 +467,12 @@
__inline int32 fxp_mac_16by16(int16 var1, int16 var2, int32 L_add)
{
- L_add += (int32)var1 * var2;
+ int32 l_orig = L_add;
+ if (__builtin_add_overflow( (int32)var1 * var2, l_orig, &L_add)) {
+ // needs saturation
+ if (l_orig > 0) L_add = MAX_32;
+ else L_add = MIN_32;
+ }
return L_add;
}
diff --git a/media/libstagefright/foundation/MediaDefs.cpp b/media/libstagefright/foundation/MediaDefs.cpp
index 28bb10a..f93ae65 100644
--- a/media/libstagefright/foundation/MediaDefs.cpp
+++ b/media/libstagefright/foundation/MediaDefs.cpp
@@ -52,6 +52,7 @@
const char *MEDIA_MIMETYPE_AUDIO_EAC3 = "audio/eac3";
const char *MEDIA_MIMETYPE_AUDIO_AC4 = "audio/ac4";
const char *MEDIA_MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
+const char *MEDIA_MIMETYPE_AUDIO_ALAC = "audio/alac";
const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mp4";
const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/x-wav";
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h
index b165bcb..523378e 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h
@@ -54,6 +54,7 @@
extern const char *MEDIA_MIMETYPE_AUDIO_EAC3;
extern const char *MEDIA_MIMETYPE_AUDIO_AC4;
extern const char *MEDIA_MIMETYPE_AUDIO_SCRAMBLED;
+extern const char *MEDIA_MIMETYPE_AUDIO_ALAC;
extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;
extern const char *MEDIA_MIMETYPE_CONTAINER_WAV;
diff --git a/media/libstagefright/include/media/stagefright/Utils.h b/media/libstagefright/include/media/stagefright/Utils.h
index 46a419d..e8e0a11 100644
--- a/media/libstagefright/include/media/stagefright/Utils.h
+++ b/media/libstagefright/include/media/stagefright/Utils.h
@@ -52,6 +52,10 @@
// Send information from MetaData to the HAL via AudioSink
status_t sendMetaDataToHal(sp<MediaPlayerBase::AudioSink>& sink, const sp<MetaData>& meta);
+// Return |audio_offload_info_t| filled from given metadata
+status_t getAudioOffloadInfo(const sp<MetaData>& meta, bool hasVideo,
+ bool isStreaming, audio_stream_type_t streamType, audio_offload_info_t *info);
+
// Check whether the stream defined by meta can be offloaded to hardware
bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo,
bool isStreaming, audio_stream_type_t streamType);
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index 47b0780..1adecb9 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -68,6 +68,7 @@
case AIMAGE_FORMAT_RAW12:
case AIMAGE_FORMAT_DEPTH16:
case AIMAGE_FORMAT_DEPTH_POINT_CLOUD:
+ case AIMAGE_FORMAT_Y8:
return true;
case AIMAGE_FORMAT_PRIVATE:
// For private format, cpu usage is prohibited.
@@ -94,6 +95,7 @@
case AIMAGE_FORMAT_RAW12:
case AIMAGE_FORMAT_DEPTH16:
case AIMAGE_FORMAT_DEPTH_POINT_CLOUD:
+ case AIMAGE_FORMAT_Y8:
return 1;
case AIMAGE_FORMAT_PRIVATE:
return 0;
diff --git a/media/ndk/NdkImageReaderPriv.h b/media/ndk/NdkImageReaderPriv.h
index a9b54a8..d9ddfd9 100644
--- a/media/ndk/NdkImageReaderPriv.h
+++ b/media/ndk/NdkImageReaderPriv.h
@@ -170,9 +170,7 @@
};
// Retrieves HGraphicBufferProducer corresponding to the native_handle_t
-// provided. This method also deletes the HalToken corresponding to the
-// native_handle_t. Thus, if it is used twice in succession, the second call
-// returns nullptr;
+// provided (this native handle MUST have been obtained by AImageReader_getWindowNativeHandle()).
sp<HGraphicBufferProducer> AImageReader_getHGBPFromHandle(const native_handle_t *handle);
#endif // _NDK_IMAGE_READER_PRIV_H
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index 6537ad1..b282ed8 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -288,13 +288,19 @@
EXPORT const char* AMEDIAFORMAT_KEY_COMPILATION = "compilation";
EXPORT const char* AMEDIAFORMAT_KEY_COMPLEXITY = "complexity";
EXPORT const char* AMEDIAFORMAT_KEY_COMPOSER = "composer";
+EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE = "crypto-default-iv-size";
+EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK = "crypto-encrypted-byte-block";
+EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_IV = "crypto-iv";
EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_KEY = "crypto-key";
+EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_MODE = "crypto-mode";
+EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK = "crypto-skip-byte-block";
EXPORT const char* AMEDIAFORMAT_KEY_CSD = "csd";
EXPORT const char* AMEDIAFORMAT_KEY_CSD_0 = "csd-0";
EXPORT const char* AMEDIAFORMAT_KEY_CSD_1 = "csd-1";
EXPORT const char* AMEDIAFORMAT_KEY_CSD_2 = "csd-2";
EXPORT const char* AMEDIAFORMAT_KEY_CSD_AVC = "csd-avc";
EXPORT const char* AMEDIAFORMAT_KEY_CSD_HEVC = "csd-hevc";
+EXPORT const char* AMEDIAFORMAT_KEY_D263 = "d263";
EXPORT const char* AMEDIAFORMAT_KEY_DATE = "date";
EXPORT const char* AMEDIAFORMAT_KEY_DISCNUMBER = "discnum";
EXPORT const char* AMEDIAFORMAT_KEY_DISPLAY_CROP = "crop";
@@ -303,13 +309,18 @@
EXPORT const char* AMEDIAFORMAT_KEY_DURATION = "durationUs";
EXPORT const char* AMEDIAFORMAT_KEY_ENCODER_DELAY = "encoder-delay";
EXPORT const char* AMEDIAFORMAT_KEY_ENCODER_PADDING = "encoder-padding";
+EXPORT const char* AMEDIAFORMAT_KEY_ESDS = "esds";
+EXPORT const char* AMEDIAFORMAT_KEY_EXIF_OFFSET = "exif-offset";
+EXPORT const char* AMEDIAFORMAT_KEY_EXIF_SIZE = "exif-size";
EXPORT const char* AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
+EXPORT const char* AMEDIAFORMAT_KEY_FRAME_COUNT = "frame-count";
EXPORT const char* AMEDIAFORMAT_KEY_FRAME_RATE = "frame-rate";
EXPORT const char* AMEDIAFORMAT_KEY_GENRE = "genre";
EXPORT const char* AMEDIAFORMAT_KEY_GRID_COLUMNS = "grid-cols";
EXPORT const char* AMEDIAFORMAT_KEY_GRID_ROWS = "grid-rows";
EXPORT const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO = "hdr-static-info";
EXPORT const char* AMEDIAFORMAT_KEY_HEIGHT = "height";
+EXPORT const char* AMEDIAFORMAT_KEY_ICC_PROFILE = "icc-profile";
EXPORT const char* AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
EXPORT const char* AMEDIAFORMAT_KEY_IS_ADTS = "is-adts";
EXPORT const char* AMEDIAFORMAT_KEY_IS_AUTOSELECT = "is-autoselect";
@@ -319,17 +330,22 @@
EXPORT const char* AMEDIAFORMAT_KEY_LANGUAGE = "language";
EXPORT const char* AMEDIAFORMAT_KEY_LATENCY = "latency";
EXPORT const char* AMEDIAFORMAT_KEY_LEVEL = "level";
+EXPORT const char* AMEDIAFORMAT_KEY_LOCATION = "location";
EXPORT const char* AMEDIAFORMAT_KEY_LOOP = "loop";
EXPORT const char* AMEDIAFORMAT_KEY_LYRICIST = "lyricist";
+EXPORT const char* AMEDIAFORMAT_KEY_MAX_BIT_RATE = "max-bitrate";
EXPORT const char* AMEDIAFORMAT_KEY_MAX_HEIGHT = "max-height";
EXPORT const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE = "max-input-size";
EXPORT const char* AMEDIAFORMAT_KEY_MAX_WIDTH = "max-width";
EXPORT const char* AMEDIAFORMAT_KEY_MIME = "mime";
EXPORT const char* AMEDIAFORMAT_KEY_MPEG_USER_DATA = "mpeg-user-data";
+EXPORT const char* AMEDIAFORMAT_KEY_MPEG2_STREAM_HEADER = "mpeg2-stream-header";
EXPORT const char* AMEDIAFORMAT_KEY_OPERATING_RATE = "operating-rate";
EXPORT const char* AMEDIAFORMAT_KEY_PCM_ENCODING = "pcm-encoding";
EXPORT const char* AMEDIAFORMAT_KEY_PRIORITY = "priority";
EXPORT const char* AMEDIAFORMAT_KEY_PROFILE = "profile";
+EXPORT const char* AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN = "pcm-big-endian";
+EXPORT const char* AMEDIAFORMAT_KEY_PSSH = "pssh";
EXPORT const char* AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP = "push-blank-buffers-on-shutdown";
EXPORT const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after";
EXPORT const char* AMEDIAFORMAT_KEY_ROTATION = "rotation-degrees";
@@ -339,9 +355,13 @@
EXPORT const char* AMEDIAFORMAT_KEY_SEI = "sei";
EXPORT const char* AMEDIAFORMAT_KEY_SLICE_HEIGHT = "slice-height";
EXPORT const char* AMEDIAFORMAT_KEY_STRIDE = "stride";
+EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT = "temporal-layer-count";
EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID = "temporal-layer-id";
EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYERING = "ts-schema";
+EXPORT const char* AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA = "text-format-data";
+EXPORT const char* AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT = "thumbnail-height";
EXPORT const char* AMEDIAFORMAT_KEY_THUMBNAIL_TIME = "thumbnail-time";
+EXPORT const char* AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH = "thumbnail-width";
EXPORT const char* AMEDIAFORMAT_KEY_TILE_HEIGHT = "tile-height";
EXPORT const char* AMEDIAFORMAT_KEY_TILE_WIDTH = "tile-width";
EXPORT const char* AMEDIAFORMAT_KEY_TIME_US = "timeUs";
diff --git a/media/ndk/include/media/NdkImage.h b/media/ndk/include/media/NdkImage.h
index f936118..15b340c 100644
--- a/media/ndk/include/media/NdkImage.h
+++ b/media/ndk/include/media/NdkImage.h
@@ -499,7 +499,34 @@
* <p>When an {@link AImage} of this format is obtained from an {@link AImageReader} or
* {@link AImage_getNumberOfPlanes()} method will return zero.</p>
*/
- AIMAGE_FORMAT_PRIVATE = 0x22
+ AIMAGE_FORMAT_PRIVATE = 0x22,
+
+ /**
+ * Android Y8 format.
+ *
+ * <p>Y8 is a planar format comprised of a WxH Y plane only, with each pixel
+ * being represented by 8 bits.</p>
+ *
+ * <p>This format assumes
+ * <ul>
+ * <li>an even width</li>
+ * <li>an even height</li>
+ * <li>a horizontal stride multiple of 16 pixels</li>
+ * </ul>
+ * </p>
+ *
+ * <pre> size = stride * height </pre>
+ *
+ * <p>For example, the {@link AImage} object can provide data
+ * in this format from a {@link ACameraDevice} (if supported) through a
+ * {@link AImageReader} object. The number of planes returned by
+ * {@link AImage_getNumberOfPlanes} will always be 1. The pixel stride returned by
+ * {@link AImage_getPlanePixelStride} will always be 1, and the
+ * {@link AImage_getPlaneRowStride} described the vertical neighboring pixel distance
+ * (in bytes) between adjacent rows.</p>
+ *
+ */
+ AIMAGE_FORMAT_Y8 = 0x20203859
};
/**
diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h
index 3a3268c..89cfd5e 100644
--- a/media/ndk/include/media/NdkMediaFormat.h
+++ b/media/ndk/include/media/NdkMediaFormat.h
@@ -188,20 +188,40 @@
extern const char* AMEDIAFORMAT_KEY_COLOR_TRANSFER __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_COMPILATION __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_COMPOSER __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_CRYPTO_IV __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_CRYPTO_KEY __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_CRYPTO_MODE __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_CSD_AVC __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_CSD_HEVC __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_D263 __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_DATE __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_DISCNUMBER __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_ENCODER_DELAY __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_ENCODER_PADDING __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_ESDS __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_EXIF_OFFSET __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_EXIF_SIZE __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_FRAME_COUNT __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_GENRE __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_ICC_PROFILE __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_LOCATION __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_LOOP __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_LYRICIST __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_MAX_BIT_RATE __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_MPEG2_STREAM_HEADER __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_PSSH __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_SAR_HEIGHT __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_SAR_WIDTH __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_THUMBNAIL_TIME __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_TITLE __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_YEAR __INTRODUCED_IN(29);
diff --git a/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java b/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java
index c9869c0..061bc5b 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.graphics.Rect;
import android.media.MediaPlayer2;
+import android.media.VideoSize;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
@@ -149,8 +150,8 @@
// TODO: Investigate the way to move onMeasure() code into FrameLayout.
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int videoWidth = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoWidth();
- int videoHeight = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoHeight();
+ int videoWidth = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoSize().getWidth();
+ int videoHeight = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoSize().getHeight();
if (DEBUG) {
Log.d(TAG, "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
+ MeasureSpec.toString(heightMeasureSpec) + ")");
diff --git a/packages/MediaComponents/src/com/android/widget/VideoTextureView.java b/packages/MediaComponents/src/com/android/widget/VideoTextureView.java
index 40fb046..c2c1ca6 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoTextureView.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoTextureView.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.media.MediaPlayer2;
+import android.media.VideoSize;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Surface;
@@ -160,8 +161,8 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int videoWidth = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoWidth();
- int videoHeight = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoHeight();
+ int videoWidth = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoSize().getWidth();
+ int videoHeight = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoSize().getHeight();
if (DEBUG) {
Log.d(TAG, "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
+ MeasureSpec.toString(heightMeasureSpec) + ")");
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index 802f86f..17507cd 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -39,6 +39,7 @@
import android.media.SessionToken2;
import android.media.SubtitleData;
import android.media.TimedText;
+import android.media.VideoSize;
import android.media.session.MediaController;
import android.media.session.MediaController.PlaybackInfo;
import android.media.session.MediaSession;
@@ -406,7 +407,7 @@
return;
}
mSpeed = speed;
- if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
+ if (mMediaPlayer != null && mMediaPlayer.getState() == MediaPlayer2.PLAYER_STATE_PLAYING) {
applySpeed();
}
updatePlaybackState();
@@ -1110,12 +1111,13 @@
@Override
public void onVideoSizeChanged(
- MediaPlayer2 mp, DataSourceDesc dsd, int width, int height) {
+ MediaPlayer2 mp, DataSourceDesc dsd, VideoSize size) {
if (DEBUG) {
- Log.d(TAG, "onVideoSizeChanged(): size: " + width + "/" + height);
+ Log.d(TAG, "onVideoSizeChanged(): size: " + size.getWidth() + "/"
+ + size.getHeight());
}
- mVideoWidth = mp.getVideoWidth();
- mVideoHeight = mp.getVideoHeight();
+ mVideoWidth = mp.getVideoSize().getWidth();
+ mVideoHeight = mp.getVideoSize().getHeight();
if (DEBUG) {
Log.d(TAG, "onVideoSizeChanged(): mVideoSize:" + mVideoWidth + "/"
+ mVideoHeight);
@@ -1193,8 +1195,8 @@
if (mMediaControlView != null) {
mMediaControlView.setEnabled(true);
}
- int videoWidth = mp.getVideoWidth();
- int videoHeight = mp.getVideoHeight();
+ int videoWidth = mp.getVideoSize().getWidth();
+ int videoHeight = mp.getVideoSize().getHeight();
// mSeekWhenPrepared may be changed after seekTo() call
long seekToPosition = mSeekWhenPrepared;
@@ -1339,7 +1341,7 @@
if (isRemotePlayback()) {
mRoutePlayer.onPause();
mCurrentState = STATE_PAUSED;
- } else if (mMediaPlayer.isPlaying()) {
+ } else if (mMediaPlayer.getState() == MediaPlayer2.PLAYER_STATE_PLAYING) {
mMediaPlayer.pause();
mCurrentState = STATE_PAUSED;
updatePlaybackState();
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 52a8fa8..5555acf 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3220,13 +3220,16 @@
}
if (status == OK) {
// verify downstream latency (we assume a max reasonable
- // latency of 1 second).
- if (latencyMs >= 0. && latencyMs <= 1000.) {
+ // latency of 5 seconds).
+ const double minLatency = 0., maxLatency = 5000.;
+ if (latencyMs >= minLatency && latencyMs <= maxLatency) {
ALOGV("new downstream latency %lf ms", latencyMs);
- downstreamLatencyStatMs.add(latencyMs);
} else {
ALOGD("out of range downstream latency %lf ms", latencyMs);
+ if (latencyMs < minLatency) latencyMs = minLatency;
+ else if (latencyMs > maxLatency) latencyMs = maxLatency;
}
+ downstreamLatencyStatMs.add(latencyMs);
}
mAudioFlinger->mLock.unlock();
}
diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp
index a1163ed..9f2515e 100644
--- a/services/camera/libcameraservice/Android.bp
+++ b/services/camera/libcameraservice/Android.bp
@@ -53,6 +53,11 @@
"device3/DistortionMapper.cpp",
"gui/RingBufferConsumer.cpp",
"utils/CameraThreadState.cpp",
+ "hidl/AidlCameraDeviceCallbacks.cpp",
+ "hidl/AidlCameraServiceListener.cpp",
+ "hidl/Convert.cpp",
+ "hidl/HidlCameraDeviceUser.cpp",
+ "hidl/HidlCameraService.cpp",
"utils/CameraTraces.cpp",
"utils/AutoConditionLock.cpp",
"utils/TagMonitor.cpp",
@@ -67,6 +72,7 @@
"libbinder",
"libcutils",
"libmedia",
+ "libmediandk",
"libmediautils",
"libcamera_client",
"libcamera_metadata",
@@ -78,6 +84,10 @@
"libhidltransport",
"libjpeg",
"libmemunreachable",
+ "libstagefright_foundation",
+ "android.frameworks.cameraservice.common@2.0",
+ "android.frameworks.cameraservice.service@2.0",
+ "android.frameworks.cameraservice.device@2.0",
"android.hardware.camera.common@1.0",
"android.hardware.camera.provider@2.4",
"android.hardware.camera.device@1.0",
@@ -96,6 +106,7 @@
include_dirs: [
"system/media/private/camera/include",
"frameworks/native/include/media/openmax",
+ "frameworks/av/media/ndk",
],
export_include_dirs: ["."],
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index e69ce1f..4dacd02 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -47,6 +47,8 @@
#include <cutils/misc.h>
#include <gui/Surface.h>
#include <hardware/hardware.h>
+#include "hidl/HidlCameraService.h"
+#include <hidl/HidlTransportSupport.h>
#include <memunreachable/memunreachable.h>
#include <media/AudioSystem.h>
#include <media/IMediaHTTPService.h>
@@ -78,6 +80,7 @@
namespace android {
using binder::Status;
+using frameworks::cameraservice::service::V2_0::implementation::HidlCameraService;
using hardware::ICamera;
using hardware::ICameraClient;
using hardware::ICameraServiceProxy;
@@ -142,6 +145,11 @@
mUidPolicy = new UidPolicy(this);
mUidPolicy->registerSelf();
+ sp<HidlCameraService> hcs = HidlCameraService::getInstance(this);
+ if (hcs->registerAsService() != android::OK) {
+ ALOGE("%s: Failed to register default android.frameworks.cameraservice.service@1.0",
+ __FUNCTION__);
+ }
}
status_t CameraService::enumerateProviders() {
@@ -209,6 +217,14 @@
proxyBinder->pingForUserUpdate();
}
+void CameraService::broadcastTorchModeStatus(const String8& cameraId, TorchModeStatus status) {
+ Mutex::Autolock lock(mStatusListenerLock);
+
+ for (auto& i : mListenerList) {
+ i->onTorchStatusChanged(mapToInterface(status), String16{cameraId});
+ }
+}
+
CameraService::~CameraService() {
VendorTagDescriptor::clearGlobalVendorTagDescriptor();
mUidPolicy->unregisterSelf();
@@ -247,6 +263,8 @@
if (mFlashlight->hasFlashUnit(id)) {
Mutex::Autolock al(mTorchStatusMutex);
mTorchStatusMap.add(id, TorchModeStatus::AVAILABLE_OFF);
+
+ broadcastTorchModeStatus(id, TorchModeStatus::AVAILABLE_OFF);
}
updateCameraNumAndIds();
@@ -399,12 +417,7 @@
}
}
- {
- Mutex::Autolock lock(mStatusListenerLock);
- for (auto& i : mListenerList) {
- i->onTorchStatusChanged(mapToInterface(newStatus), String16{cameraId});
- }
- }
+ broadcastTorchModeStatus(cameraId, newStatus);
}
Status CameraService::getNumberOfCameras(int32_t type, int32_t* numCameras) {
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 80d9ef4..064863f 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -857,6 +857,8 @@
static sp<hardware::ICameraServiceProxy> getCameraServiceProxy();
static void pingCameraServiceProxy();
+ void broadcastTorchModeStatus(const String8& cameraId,
+ hardware::camera::common::V1_0::TorchModeStatus status);
};
} // namespace android
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 7b1454d..18addb5 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -69,7 +69,7 @@
res = buildQuirks();
if (res != OK) return res;
- const Size MAX_PREVIEW_SIZE = { MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT };
+ Size maxPreviewSize = { MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT };
// Treat the H.264 max size as the max supported video size.
MediaProfiles *videoEncoderProfiles = MediaProfiles::getInstance();
Vector<video_encoder> encoders = videoEncoderProfiles->getVideoEncoders();
@@ -90,11 +90,16 @@
}
}
// This is just an upper bound and may not be an actually valid video size
- const Size VIDEO_SIZE_UPPER_BOUND = {maxVideoWidth, maxVideoHeight};
+ Size videoSizeUpperBound = {maxVideoWidth, maxVideoHeight};
- res = getFilteredSizes(MAX_PREVIEW_SIZE, &availablePreviewSizes);
+ if (fastInfo.supportsPreferredConfigs) {
+ maxPreviewSize = getMaxSize(getPreferredPreviewSizes());
+ videoSizeUpperBound = getMaxSize(getPreferredVideoSizes());
+ }
+
+ res = getFilteredSizes(maxPreviewSize, &availablePreviewSizes);
if (res != OK) return res;
- res = getFilteredSizes(VIDEO_SIZE_UPPER_BOUND, &availableVideoSizes);
+ res = getFilteredSizes(videoSizeUpperBound, &availableVideoSizes);
if (res != OK) return res;
// Select initial preview and video size that's under the initial bound and
@@ -296,9 +301,13 @@
Vector<Size> availableJpegSizes = getAvailableJpegSizes();
if (!availableJpegSizes.size()) return NO_INIT;
- // TODO: Pick maximum
pictureWidth = availableJpegSizes[0].width;
pictureHeight = availableJpegSizes[0].height;
+ if (fastInfo.supportsPreferredConfigs) {
+ Size suggestedJpegSize = getMaxSize(getPreferredJpegSizes());
+ pictureWidth = suggestedJpegSize.width;
+ pictureHeight = suggestedJpegSize.height;
+ }
pictureWidthLastSet = pictureWidth;
pictureHeightLastSet = pictureHeight;
pictureSizeOverriden = false;
@@ -1011,6 +1020,9 @@
arrayHeight = activeArraySize.data.i32[3];
} else return NO_INIT;
+ fastInfo.supportsPreferredConfigs =
+ info->exists(ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS);
+
// We'll set the target FPS range for still captures to be as wide
// as possible to give the HAL maximum latitude for exposure selection
camera_metadata_ro_entry_t availableFpsRanges =
@@ -1022,8 +1034,11 @@
// Get supported preview fps ranges, up to default maximum.
Vector<Size> supportedPreviewSizes;
Vector<FpsRange> supportedPreviewFpsRanges;
- const Size PREVIEW_SIZE_BOUND = { MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT };
- status_t res = getFilteredSizes(PREVIEW_SIZE_BOUND, &supportedPreviewSizes);
+ Size previewSizeBound = { MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT };
+ if (fastInfo.supportsPreferredConfigs) {
+ previewSizeBound = getMaxSize(getPreferredPreviewSizes());
+ }
+ status_t res = getFilteredSizes(previewSizeBound, &supportedPreviewSizes);
if (res != OK) return res;
for (size_t i=0; i < availableFpsRanges.count; i += 2) {
if (!isFpsSupported(supportedPreviewSizes,
@@ -3107,6 +3122,67 @@
return jpegSizes;
}
+Vector<Parameters::StreamConfiguration> Parameters::getPreferredStreamConfigurations(
+ int32_t usecaseId) const {
+ const size_t STREAM_CONFIGURATION_SIZE = 5;
+ const size_t STREAM_WIDTH_OFFSET = 0;
+ const size_t STREAM_HEIGHT_OFFSET = 1;
+ const size_t STREAM_FORMAT_OFFSET = 2;
+ const size_t STREAM_IS_INPUT_OFFSET = 3;
+ const size_t STREAM_USECASE_BITMAP_OFFSET = 4;
+ Vector<StreamConfiguration> scs;
+
+ if (fastInfo.supportsPreferredConfigs) {
+ camera_metadata_ro_entry_t availableStreamConfigs = staticInfo(
+ ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS);
+ for (size_t i = 0; i < availableStreamConfigs.count; i+= STREAM_CONFIGURATION_SIZE) {
+ int32_t width = availableStreamConfigs.data.i32[i + STREAM_WIDTH_OFFSET];
+ int32_t height = availableStreamConfigs.data.i32[i + STREAM_HEIGHT_OFFSET];
+ int32_t format = availableStreamConfigs.data.i32[i + STREAM_FORMAT_OFFSET];
+ int32_t isInput = availableStreamConfigs.data.i32[i + STREAM_IS_INPUT_OFFSET];
+ int32_t supportedUsecases =
+ availableStreamConfigs.data.i32[i + STREAM_USECASE_BITMAP_OFFSET];
+ if (supportedUsecases & (1 << usecaseId)) {
+ StreamConfiguration sc = {format, width, height, isInput};
+ scs.add(sc);
+ }
+ }
+ }
+
+ return scs;
+}
+
+Vector<Parameters::Size> Parameters::getPreferredFilteredSizes(int32_t usecaseId,
+ int32_t format) const {
+ Vector<Parameters::Size> sizes;
+ Vector<StreamConfiguration> scs = getPreferredStreamConfigurations(usecaseId);
+ for (const auto &it : scs) {
+ if (it.format == format) {
+ sizes.add({it.width, it.height});
+ }
+ }
+
+ return sizes;
+}
+
+Vector<Parameters::Size> Parameters::getPreferredJpegSizes() const {
+ return getPreferredFilteredSizes(
+ ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_SNAPSHOT,
+ HAL_PIXEL_FORMAT_BLOB);
+}
+
+Vector<Parameters::Size> Parameters::getPreferredPreviewSizes() const {
+ return getPreferredFilteredSizes(
+ ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_PREVIEW,
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
+}
+
+Vector<Parameters::Size> Parameters::getPreferredVideoSizes() const {
+ return getPreferredFilteredSizes(
+ ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_RECORD,
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
+}
+
Parameters::CropRegion Parameters::calculateCropRegion(bool previewOnly) const {
float zoomLeft, zoomTop, zoomWidth, zoomHeight;
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index e008648..3a709c9 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -248,6 +248,7 @@
bool useFlexibleYuv;
Size maxJpegSize;
Size maxZslSize;
+ bool supportsPreferredConfigs;
} fastInfo;
// Quirks information; these are short-lived flags to enable workarounds for
@@ -418,6 +419,9 @@
// returns an empty Vector if device HAL version does support it
Vector<StreamConfiguration> getStreamConfigurations();
+ // Helper function to extract the suggested stream configurations
+ Vector<StreamConfiguration> getPreferredStreamConfigurations(int32_t usecaseId) const;
+
// Helper function to get minimum frame duration for a jpeg size
// return -1 if input jpeg size cannot be found in supported size list
int64_t getJpegStreamMinFrameDurationNs(Parameters::Size size);
@@ -439,6 +443,15 @@
// The maximum size is defined by comparing width first, when width ties comparing height.
Size getMaxSize(const Vector<Size>& sizes);
+ // Helper function to filter and sort suggested sizes
+ Vector<Parameters::Size> getPreferredFilteredSizes(int32_t usecaseId, int32_t format) const;
+ // Helper function to get the suggested jpeg sizes
+ Vector<Size> getPreferredJpegSizes() const;
+ // Helper function to get the suggested preview sizes
+ Vector<Size> getPreferredPreviewSizes() const;
+ // Helper function to get the suggested video sizes
+ Vector<Size> getPreferredVideoSizes() const;
+
int mDeviceVersion;
uint8_t mDefaultSceneMode;
};
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index a94e886..2542ab2 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -425,6 +425,102 @@
}
}
+status_t CameraProviderManager::ProviderInfo::DeviceInfo3::fixupMonochromeTags() {
+ status_t res = OK;
+ auto& c = mCameraCharacteristics;
+
+ // Override static metadata for MONOCHROME camera with older device version
+ if (mVersion.get_major() == 3 && mVersion.get_minor() < 5) {
+ camera_metadata_entry cap = c.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+ for (size_t i = 0; i < cap.count; i++) {
+ if (cap.data.u8[i] == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME) {
+ // ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
+ uint8_t cfa = ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO;
+ res = c.update(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT, &cfa, 1);
+ if (res != OK) {
+ ALOGE("%s: Failed to update COLOR_FILTER_ARRANGEMENT: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ // ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS
+ const std::vector<uint32_t> sKeys = {
+ ANDROID_SENSOR_REFERENCE_ILLUMINANT1,
+ ANDROID_SENSOR_REFERENCE_ILLUMINANT2,
+ ANDROID_SENSOR_CALIBRATION_TRANSFORM1,
+ ANDROID_SENSOR_CALIBRATION_TRANSFORM2,
+ ANDROID_SENSOR_COLOR_TRANSFORM1,
+ ANDROID_SENSOR_COLOR_TRANSFORM2,
+ ANDROID_SENSOR_FORWARD_MATRIX1,
+ ANDROID_SENSOR_FORWARD_MATRIX2,
+ };
+ res = removeAvailableKeys(c, sKeys,
+ ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
+ if (res != OK) {
+ ALOGE("%s: Failed to update REQUEST_AVAILABLE_CHARACTERISTICS_KEYS: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ // ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS
+ const std::vector<uint32_t> reqKeys = {
+ ANDROID_COLOR_CORRECTION_MODE,
+ ANDROID_COLOR_CORRECTION_TRANSFORM,
+ ANDROID_COLOR_CORRECTION_GAINS,
+ };
+ res = removeAvailableKeys(c, reqKeys, ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS);
+ if (res != OK) {
+ ALOGE("%s: Failed to update REQUEST_AVAILABLE_REQUEST_KEYS: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ // ANDROID_REQUEST_AVAILABLE_RESULT_KEYS
+ const std::vector<uint32_t> resKeys = {
+ ANDROID_SENSOR_GREEN_SPLIT,
+ ANDROID_SENSOR_NEUTRAL_COLOR_POINT,
+ ANDROID_COLOR_CORRECTION_MODE,
+ ANDROID_COLOR_CORRECTION_TRANSFORM,
+ ANDROID_COLOR_CORRECTION_GAINS,
+ };
+ res = removeAvailableKeys(c, resKeys, ANDROID_REQUEST_AVAILABLE_RESULT_KEYS);
+ if (res != OK) {
+ ALOGE("%s: Failed to update REQUEST_AVAILABLE_RESULT_KEYS: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ // ANDROID_SENSOR_BLACK_LEVEL_PATTERN
+ camera_metadata_entry blEntry = c.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
+ for (size_t j = 1; j < blEntry.count; j++) {
+ blEntry.data.i32[j] = blEntry.data.i32[0];
+ }
+ }
+ }
+ }
+ return res;
+}
+
+status_t CameraProviderManager::ProviderInfo::DeviceInfo3::removeAvailableKeys(
+ CameraMetadata& c, const std::vector<uint32_t>& keys, uint32_t keyTag) {
+ status_t res = OK;
+
+ camera_metadata_entry keysEntry = c.find(keyTag);
+ if (keysEntry.count == 0) {
+ ALOGE("%s: Failed to find tag %u: %s (%d)", __FUNCTION__, keyTag, strerror(-res), res);
+ return res;
+ }
+ std::vector<int32_t> vKeys;
+ vKeys.reserve(keysEntry.count);
+ for (size_t i = 0; i < keysEntry.count; i++) {
+ if (std::find(keys.begin(), keys.end(), keysEntry.data.i32[i]) == keys.end()) {
+ vKeys.push_back(keysEntry.data.i32[i]);
+ }
+ }
+ res = c.update(keyTag, vKeys.data(), vKeys.size());
+ return res;
+}
+
bool CameraProviderManager::isLogicalCamera(const std::string& id,
std::vector<std::string>* physicalCameraIds) {
std::lock_guard<std::mutex> lock(mInterfaceMutex);
@@ -1131,6 +1227,12 @@
__FUNCTION__, mId.c_str(), CameraProviderManager::statusToString(status), status);
return;
}
+ status_t res = fixupMonochromeTags();
+ if (OK != res) {
+ ALOGE("%s: Unable to fix up monochrome tags based for older HAL version: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return;
+ }
camera_metadata_entry flashAvailable =
mCameraCharacteristics.find(ANDROID_FLASH_INFO_AVAILABLE);
if (flashAvailable.count == 1 &&
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 9016747..c506d35 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -385,6 +385,9 @@
CameraMetadata mCameraCharacteristics;
std::unordered_map<std::string, CameraMetadata> mPhysicalCameraCharacteristics;
void queryPhysicalCameraIds();
+ status_t fixupMonochromeTags();
+ status_t removeAvailableKeys(CameraMetadata& c, const std::vector<uint32_t>& keys,
+ uint32_t keyTag);
};
private:
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 53aee7e..58471b9 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -79,11 +79,10 @@
mNextReprocessShutterFrameNumber(0),
mListener(NULL),
mVendorTagId(CAMERA_METADATA_INVALID_VENDOR_ID),
- mLastTemplateId(-1)
+ mLastTemplateId(-1),
+ mNeedFixupMonochromeTags(false)
{
ATRACE_CALL();
- camera3_callback_ops::notify = &sNotify;
- camera3_callback_ops::process_capture_result = &sProcessCaptureResult;
ALOGV("%s: Created device for camera %s", __FUNCTION__, mId.string());
}
@@ -190,6 +189,28 @@
mTagMonitor.parseTagsToMonitor(String8(monitorTags));
}
+ // Metadata tags needs fixup for monochrome camera device version less
+ // than 3.5.
+ hardware::hidl_version maxVersion{0,0};
+ res = manager->getHighestSupportedVersion(mId.string(), &maxVersion);
+ if (res != OK) {
+ ALOGE("%s: Error in getting camera device version id: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ int deviceVersion = HARDWARE_DEVICE_API_VERSION(
+ maxVersion.get_major(), maxVersion.get_minor());
+
+ bool isMonochrome = false;
+ camera_metadata_entry_t entry = mDeviceInfo.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+ for (size_t i = 0; i < entry.count; i++) {
+ uint8_t capability = entry.data.u8[i];
+ if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME) {
+ isMonochrome = true;
+ }
+ }
+ mNeedFixupMonochromeTags = (isMonochrome && deviceVersion < CAMERA_DEVICE_API_VERSION_3_5);
+
return initializeCommonLocked();
}
@@ -218,8 +239,28 @@
if (sessionKeysEntry.count > 0) {
sessionParamKeys.insertArrayAt(sessionKeysEntry.data.i32, 0, sessionKeysEntry.count);
}
+
+ camera_metadata_entry bufMgrMode =
+ mDeviceInfo.find(ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION);
+ if (bufMgrMode.count > 0) {
+ mUseHalBufManager = (bufMgrMode.data.u8[0] ==
+ ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
+ }
+
+ if (mUseHalBufManager) {
+ res = mRequestBufferSM.initialize(mStatusTracker);
+ if (res != OK) {
+ SET_ERR_L("Unable to start request buffer state machine: %s (%d)",
+ strerror(-res), res);
+ mInterface->close();
+ mStatusTracker.clear();
+ return res;
+ }
+ }
+
/** Start up request queue thread */
- mRequestThread = new RequestThread(this, mStatusTracker, mInterface, sessionParamKeys);
+ mRequestThread = new RequestThread(
+ this, mStatusTracker, mInterface, sessionParamKeys, mUseHalBufManager);
res = mRequestThread->run(String8::format("C3Dev-%s-ReqQueue", mId.string()).string());
if (res != OK) {
SET_ERR_L("Unable to start request queue thread: %s (%d)",
@@ -271,7 +312,6 @@
return res;
}
}
-
return OK;
}
@@ -919,6 +959,221 @@
return res;
}
+hardware::Return<void> Camera3Device::requestStreamBuffers(
+ const hardware::hidl_vec<hardware::camera::device::V3_5::BufferRequest>& bufReqs,
+ requestStreamBuffers_cb _hidl_cb) {
+ using hardware::camera::device::V3_5::BufferRequestStatus;
+ using hardware::camera::device::V3_5::StreamBufferRet;
+ using hardware::camera::device::V3_5::StreamBufferRequestError;
+
+ std::lock_guard<std::mutex> lock(mRequestBufferInterfaceLock);
+
+ hardware::hidl_vec<StreamBufferRet> bufRets;
+ if (!mUseHalBufManager) {
+ ALOGE("%s: Camera %s does not support HAL buffer management",
+ __FUNCTION__, mId.string());
+ _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
+ return hardware::Void();
+ }
+
+ SortedVector<int32_t> streamIds;
+ ssize_t sz = streamIds.setCapacity(bufReqs.size());
+ if (sz < 0 || static_cast<size_t>(sz) != bufReqs.size()) {
+ ALOGE("%s: failed to allocate memory for %zu buffer requests",
+ __FUNCTION__, bufReqs.size());
+ _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
+ return hardware::Void();
+ }
+
+ if (bufReqs.size() > mOutputStreams.size()) {
+ ALOGE("%s: too many buffer requests (%zu > # of output streams %zu)",
+ __FUNCTION__, bufReqs.size(), mOutputStreams.size());
+ _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
+ return hardware::Void();
+ }
+
+ // Check for repeated streamId
+ for (const auto& bufReq : bufReqs) {
+ if (streamIds.indexOf(bufReq.streamId) != NAME_NOT_FOUND) {
+ ALOGE("%s: Stream %d appear multiple times in buffer requests",
+ __FUNCTION__, bufReq.streamId);
+ _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
+ return hardware::Void();
+ }
+ streamIds.add(bufReq.streamId);
+ }
+
+ if (!mRequestBufferSM.startRequestBuffer()) {
+ ALOGE("%s: request buffer disallowed while camera service is configuring",
+ __FUNCTION__);
+ _hidl_cb(BufferRequestStatus::FAILED_CONFIGURING, bufRets);
+ return hardware::Void();
+ }
+
+ bufRets.resize(bufReqs.size());
+
+ bool allReqsSucceeds = true;
+ bool oneReqSucceeds = false;
+ for (size_t i = 0; i < bufReqs.size(); i++) {
+ const auto& bufReq = bufReqs[i];
+ auto& bufRet = bufRets[i];
+ int32_t streamId = bufReq.streamId;
+ sp<Camera3OutputStreamInterface> outputStream = mOutputStreams.get(streamId);
+ if (outputStream == nullptr) {
+ ALOGE("%s: Output stream id %d not found!", __FUNCTION__, streamId);
+ hardware::hidl_vec<StreamBufferRet> emptyBufRets;
+ _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, emptyBufRets);
+ mRequestBufferSM.endRequestBuffer();
+ return hardware::Void();
+ }
+
+ bufRet.streamId = streamId;
+ uint32_t numBuffersRequested = bufReq.numBuffersRequested;
+ size_t totalHandout = outputStream->getOutstandingBuffersCount() + numBuffersRequested;
+ if (totalHandout > outputStream->asHalStream()->max_buffers) {
+ // Not able to allocate enough buffer. Exit early for this stream
+ bufRet.val.error(StreamBufferRequestError::MAX_BUFFER_EXCEEDED);
+ allReqsSucceeds = false;
+ continue;
+ }
+
+ hardware::hidl_vec<StreamBuffer> tmpRetBuffers(numBuffersRequested);
+ bool currentReqSucceeds = true;
+ std::vector<camera3_stream_buffer_t> streamBuffers(numBuffersRequested);
+ size_t numAllocatedBuffers = 0;
+ size_t numPushedInflightBuffers = 0;
+ for (size_t b = 0; b < numBuffersRequested; b++) {
+ camera3_stream_buffer_t& sb = streamBuffers[b];
+ // Since this method can run concurrently with request thread
+ // We need to update the wait duration everytime we call getbuffer
+ nsecs_t waitDuration = kBaseGetBufferWait + getExpectedInFlightDuration();
+ status_t res = outputStream->getBuffer(&sb, waitDuration);
+ if (res != OK) {
+ ALOGE("%s: Can't get output buffer for stream %d: %s (%d)",
+ __FUNCTION__, streamId, strerror(-res), res);
+ if (res == NO_INIT || res == DEAD_OBJECT) {
+ bufRet.val.error(StreamBufferRequestError::STREAM_DISCONNECTED);
+ } else if (res == TIMED_OUT || res == NO_MEMORY) {
+ bufRet.val.error(StreamBufferRequestError::NO_BUFFER_AVAILABLE);
+ } else {
+ bufRet.val.error(StreamBufferRequestError::UNKNOWN_ERROR);
+ }
+ currentReqSucceeds = false;
+ break;
+ }
+ numAllocatedBuffers++;
+
+ buffer_handle_t *buffer = sb.buffer;
+ auto pair = mInterface->getBufferId(*buffer, streamId);
+ bool isNewBuffer = pair.first;
+ uint64_t bufferId = pair.second;
+ StreamBuffer& hBuf = tmpRetBuffers[b];
+
+ hBuf.streamId = streamId;
+ hBuf.bufferId = bufferId;
+ hBuf.buffer = (isNewBuffer) ? *buffer : nullptr;
+ hBuf.status = BufferStatus::OK;
+ hBuf.releaseFence = nullptr;
+
+ native_handle_t *acquireFence = nullptr;
+ if (sb.acquire_fence != -1) {
+ acquireFence = native_handle_create(1,0);
+ acquireFence->data[0] = sb.acquire_fence;
+ }
+ hBuf.acquireFence.setTo(acquireFence, /*shouldOwn*/true);
+ hBuf.releaseFence = nullptr;
+
+ res = mInterface->pushInflightRequestBuffer(bufferId, buffer);
+ if (res != OK) {
+ ALOGE("%s: Can't get register request buffers for stream %d: %s (%d)",
+ __FUNCTION__, streamId, strerror(-res), res);
+ bufRet.val.error(StreamBufferRequestError::UNKNOWN_ERROR);
+ currentReqSucceeds = false;
+ break;
+ }
+ numPushedInflightBuffers++;
+ }
+ if (currentReqSucceeds) {
+ bufRet.val.buffers(std::move(tmpRetBuffers));
+ oneReqSucceeds = true;
+ } else {
+ allReqsSucceeds = false;
+ for (size_t b = 0; b < numPushedInflightBuffers; b++) {
+ StreamBuffer& hBuf = tmpRetBuffers[b];
+ buffer_handle_t* buffer;
+ status_t res = mInterface->popInflightRequestBuffer(hBuf.bufferId, &buffer);
+ if (res != OK) {
+ SET_ERR("%s: popInflightRequestBuffer failed for stream %d: %s (%d)",
+ __FUNCTION__, streamId, strerror(-res), res);
+ }
+ }
+ for (size_t b = 0; b < numAllocatedBuffers; b++) {
+ camera3_stream_buffer_t& sb = streamBuffers[b];
+ sb.acquire_fence = -1;
+ sb.status = CAMERA3_BUFFER_STATUS_ERROR;
+ }
+ returnOutputBuffers(streamBuffers.data(), numAllocatedBuffers, 0);
+ }
+ }
+
+ _hidl_cb(allReqsSucceeds ? BufferRequestStatus::OK :
+ oneReqSucceeds ? BufferRequestStatus::FAILED_PARTIAL :
+ BufferRequestStatus::FAILED_UNKNOWN,
+ bufRets);
+ mRequestBufferSM.endRequestBuffer();
+ return hardware::Void();
+}
+
+hardware::Return<void> Camera3Device::returnStreamBuffers(
+ const hardware::hidl_vec<hardware::camera::device::V3_2::StreamBuffer>& buffers) {
+ if (!mUseHalBufManager) {
+ ALOGE("%s: Camera %s does not support HAL buffer managerment",
+ __FUNCTION__, mId.string());
+ return hardware::Void();
+ }
+
+ for (const auto& buf : buffers) {
+ if (buf.bufferId == HalInterface::BUFFER_ID_NO_BUFFER) {
+ ALOGE("%s: cannot return a buffer without bufferId", __FUNCTION__);
+ continue;
+ }
+
+ buffer_handle_t* buffer;
+ status_t res = mInterface->popInflightRequestBuffer(buf.bufferId, &buffer);
+
+ if (res != OK) {
+ ALOGE("%s: cannot find in-flight buffer %" PRIu64 " for stream %d",
+ __FUNCTION__, buf.bufferId, buf.streamId);
+ continue;
+ }
+
+ camera3_stream_buffer_t streamBuffer;
+ streamBuffer.buffer = buffer;
+ streamBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
+ streamBuffer.acquire_fence = -1;
+ streamBuffer.release_fence = -1;
+
+ if (buf.releaseFence == nullptr) {
+ streamBuffer.release_fence = -1;
+ } else if (buf.releaseFence->numFds == 1) {
+ streamBuffer.release_fence = dup(buf.releaseFence->data[0]);
+ } else {
+ ALOGE("%s: Invalid release fence, fd count is %d, not 1",
+ __FUNCTION__, buf.releaseFence->numFds);
+ continue;
+ }
+
+ sp<Camera3StreamInterface> stream = mOutputStreams.get(buf.streamId);
+ if (stream == nullptr) {
+ ALOGE("%s: Output stream id %d not found!", __FUNCTION__, buf.streamId);
+ continue;
+ }
+ streamBuffer.stream = stream->asHalStream();
+ returnOutputBuffers(&streamBuffer, /*size*/1, /*timestamp*/ 0);
+ }
+ return hardware::Void();
+}
+
hardware::Return<void> Camera3Device::processCaptureResult_3_4(
const hardware::hidl_vec<
hardware::camera::device::V3_4::CaptureResult>& results) {
@@ -1067,21 +1322,32 @@
auto& bDst = outputBuffers[i];
const StreamBuffer &bSrc = result.outputBuffers[i];
- ssize_t idx = mOutputStreams.indexOfKey(bSrc.streamId);
- if (idx == NAME_NOT_FOUND) {
+ sp<Camera3StreamInterface> stream = mOutputStreams.get(bSrc.streamId);
+ if (stream == nullptr) {
ALOGE("%s: Frame %d: Buffer %zu: Invalid output stream id %d",
__FUNCTION__, result.frameNumber, i, bSrc.streamId);
return;
}
- bDst.stream = mOutputStreams.valueAt(idx)->asHalStream();
+ bDst.stream = stream->asHalStream();
buffer_handle_t *buffer;
- res = mInterface->popInflightBuffer(result.frameNumber, bSrc.streamId, &buffer);
+ if (mUseHalBufManager) {
+ if (bSrc.bufferId == HalInterface::BUFFER_ID_NO_BUFFER) {
+ ALOGE("%s: Frame %d: Buffer %zu: No bufferId for stream %d",
+ __FUNCTION__, result.frameNumber, i, bSrc.streamId);
+ return;
+ }
+ res = mInterface->popInflightRequestBuffer(bSrc.bufferId, &buffer);
+ } else {
+ res = mInterface->popInflightBuffer(result.frameNumber, bSrc.streamId, &buffer);
+ }
+
if (res != OK) {
ALOGE("%s: Frame %d: Buffer %zu: No in-flight buffer for stream %d",
__FUNCTION__, result.frameNumber, i, bSrc.streamId);
return;
}
+
bDst.buffer = buffer;
bDst.status = mapHidlBufferStatus(bSrc.status);
bDst.acquire_fence = -1;
@@ -1163,13 +1429,13 @@
m.type = CAMERA3_MSG_ERROR;
m.message.error.frame_number = msg.msg.error.frameNumber;
if (msg.msg.error.errorStreamId >= 0) {
- ssize_t idx = mOutputStreams.indexOfKey(msg.msg.error.errorStreamId);
- if (idx == NAME_NOT_FOUND) {
- ALOGE("%s: Frame %d: Invalid error stream id %d",
- __FUNCTION__, m.message.error.frame_number, msg.msg.error.errorStreamId);
+ sp<Camera3StreamInterface> stream = mOutputStreams.get(msg.msg.error.errorStreamId);
+ if (stream == nullptr) {
+ ALOGE("%s: Frame %d: Invalid error stream id %d", __FUNCTION__,
+ m.message.error.frame_number, msg.msg.error.errorStreamId);
return;
}
- m.message.error.error_stream = mOutputStreams.valueAt(idx)->asHalStream();
+ m.message.error.error_stream = stream->asHalStream();
} else {
m.message.error.error_stream = nullptr;
}
@@ -1350,6 +1616,56 @@
return OK;
}
+status_t Camera3Device::StreamSet::add(
+ int streamId, sp<camera3::Camera3OutputStreamInterface> stream) {
+ if (stream == nullptr) {
+ ALOGE("%s: cannot add null stream", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ std::lock_guard<std::mutex> lock(mLock);
+ return mData.add(streamId, stream);
+}
+
+ssize_t Camera3Device::StreamSet::remove(int streamId) {
+ std::lock_guard<std::mutex> lock(mLock);
+ return mData.removeItem(streamId);
+}
+
+sp<camera3::Camera3OutputStreamInterface>
+Camera3Device::StreamSet::get(int streamId) {
+ std::lock_guard<std::mutex> lock(mLock);
+ ssize_t idx = mData.indexOfKey(streamId);
+ if (idx == NAME_NOT_FOUND) {
+ return nullptr;
+ }
+ return mData.editValueAt(idx);
+}
+
+sp<camera3::Camera3OutputStreamInterface>
+Camera3Device::StreamSet::operator[] (size_t index) {
+ std::lock_guard<std::mutex> lock(mLock);
+ return mData.editValueAt(index);
+}
+
+size_t Camera3Device::StreamSet::size() const {
+ std::lock_guard<std::mutex> lock(mLock);
+ return mData.size();
+}
+
+void Camera3Device::StreamSet::clear() {
+ std::lock_guard<std::mutex> lock(mLock);
+ return mData.clear();
+}
+
+std::vector<int> Camera3Device::StreamSet::getStreamIds() {
+ std::lock_guard<std::mutex> lock(mLock);
+ std::vector<int> streamIds(mData.size());
+ for (size_t i = 0; i < mData.size(); i++) {
+ streamIds[i] = mData.keyAt(i);
+ }
+ return streamIds;
+}
+
status_t Camera3Device::createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
@@ -1456,7 +1772,8 @@
} else if (isShared) {
newStream = new Camera3SharedOutputStream(mNextStreamId, consumers,
width, height, format, consumerUsage, dataSpace, rotation,
- mTimestampOffset, physicalCameraId, streamSetId);
+ mTimestampOffset, physicalCameraId, streamSetId,
+ mUseHalBufManager);
} else if (consumers.size() == 0 && hasDeferredConsumer) {
newStream = new Camera3OutputStream(mNextStreamId,
width, height, format, consumerUsage, dataSpace, rotation,
@@ -1533,20 +1850,20 @@
return INVALID_OPERATION;
}
- ssize_t idx = mOutputStreams.indexOfKey(id);
- if (idx == NAME_NOT_FOUND) {
+ sp<Camera3StreamInterface> stream = mOutputStreams.get(id);
+ if (stream == nullptr) {
CLOGE("Stream %d is unknown", id);
- return idx;
+ return BAD_VALUE;
}
- streamInfo->width = mOutputStreams[idx]->getWidth();
- streamInfo->height = mOutputStreams[idx]->getHeight();
- streamInfo->format = mOutputStreams[idx]->getFormat();
- streamInfo->dataSpace = mOutputStreams[idx]->getDataSpace();
- streamInfo->formatOverridden = mOutputStreams[idx]->isFormatOverridden();
- streamInfo->originalFormat = mOutputStreams[idx]->getOriginalFormat();
- streamInfo->dataSpaceOverridden = mOutputStreams[idx]->isDataSpaceOverridden();
- streamInfo->originalDataSpace = mOutputStreams[idx]->getOriginalDataSpace();
+ streamInfo->width = stream->getWidth();
+ streamInfo->height = stream->getHeight();
+ streamInfo->format = stream->getFormat();
+ streamInfo->dataSpace = stream->getDataSpace();
+ streamInfo->formatOverridden = stream->isFormatOverridden();
+ streamInfo->originalFormat = stream->getOriginalFormat();
+ streamInfo->dataSpaceOverridden = stream->isDataSpaceOverridden();
+ streamInfo->originalDataSpace = stream->getOriginalDataSpace();
return OK;
}
@@ -1573,14 +1890,12 @@
return INVALID_OPERATION;
}
- ssize_t idx = mOutputStreams.indexOfKey(id);
- if (idx == NAME_NOT_FOUND) {
- CLOGE("Stream %d does not exist",
- id);
+ sp<Camera3OutputStreamInterface> stream = mOutputStreams.get(id);
+ if (stream == nullptr) {
+ CLOGE("Stream %d does not exist", id);
return BAD_VALUE;
}
-
- return mOutputStreams.editValueAt(idx)->setTransform(transform);
+ return stream->setTransform(transform);
}
status_t Camera3Device::deleteStream(int id) {
@@ -1605,21 +1920,21 @@
}
sp<Camera3StreamInterface> deletedStream;
- ssize_t outputStreamIdx = mOutputStreams.indexOfKey(id);
+ sp<Camera3StreamInterface> stream = mOutputStreams.get(id);
if (mInputStream != NULL && id == mInputStream->getId()) {
deletedStream = mInputStream;
mInputStream.clear();
} else {
- if (outputStreamIdx == NAME_NOT_FOUND) {
+ if (stream == nullptr) {
CLOGE("Stream %d does not exist", id);
return BAD_VALUE;
}
}
// Delete output stream or the output part of a bi-directional stream.
- if (outputStreamIdx != NAME_NOT_FOUND) {
- deletedStream = mOutputStreams.editValueAt(outputStreamIdx);
- mOutputStreams.removeItem(id);
+ if (stream != nullptr) {
+ deletedStream = stream;
+ mOutputStreams.remove(id);
}
// Free up the stream endpoint so that it can be used by some other stream
@@ -1856,6 +2171,15 @@
mStatusWaiters++;
+ // Notify HAL to start draining. We need to notify the HalInterface layer
+ // even when the device is already IDLE, so HalInterface can reject incoming
+ // requestStreamBuffers call.
+ if (!active && mUseHalBufManager) {
+ auto streamIds = mOutputStreams.getStreamIds();
+ mRequestThread->signalPipelineDrain(streamIds);
+ mRequestBufferSM.onWaitUntilIdle();
+ }
+
bool stateSeen = false;
do {
if (active == (mStatus == STATUS_ACTIVE)) {
@@ -2038,15 +2362,12 @@
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
- sp<Camera3StreamInterface> stream;
- ssize_t outputStreamIdx = mOutputStreams.indexOfKey(streamId);
- if (outputStreamIdx == NAME_NOT_FOUND) {
+ sp<Camera3StreamInterface> stream = mOutputStreams.get(streamId);
+ if (stream == nullptr) {
CLOGE("Stream %d does not exist", streamId);
return BAD_VALUE;
}
- stream = mOutputStreams.editValueAt(outputStreamIdx);
-
if (stream->isUnpreparable() || stream->hasOutstandingBuffers() ) {
CLOGE("Stream %d has already been a request target", streamId);
return BAD_VALUE;
@@ -2066,15 +2387,12 @@
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
- sp<Camera3StreamInterface> stream;
- ssize_t outputStreamIdx = mOutputStreams.indexOfKey(streamId);
- if (outputStreamIdx == NAME_NOT_FOUND) {
+ sp<Camera3StreamInterface> stream = mOutputStreams.get(streamId);
+ if (stream == nullptr) {
CLOGE("Stream %d does not exist", streamId);
return BAD_VALUE;
}
- stream = mOutputStreams.editValueAt(outputStreamIdx);
-
if (stream->hasOutstandingBuffers() || mRequestThread->isStreamPending(stream)) {
CLOGE("Stream %d is a target of a in-progress request", streamId);
return BAD_VALUE;
@@ -2090,14 +2408,11 @@
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
- sp<Camera3StreamInterface> stream;
- ssize_t outputStreamIdx = mOutputStreams.indexOfKey(streamId);
- if (outputStreamIdx == NAME_NOT_FOUND) {
+ sp<Camera3StreamInterface> stream = mOutputStreams.get(streamId);
+ if (stream == nullptr) {
CLOGE("Stream %d does not exist", streamId);
return BAD_VALUE;
}
-
- stream = mOutputStreams.editValueAt(outputStreamIdx);
stream->addBufferListener(listener);
return OK;
@@ -2156,12 +2471,11 @@
return BAD_VALUE;
}
- ssize_t idx = mOutputStreams.indexOfKey(streamId);
- if (idx == NAME_NOT_FOUND) {
+ sp<Camera3OutputStreamInterface> stream = mOutputStreams.get(streamId);
+ if (stream == nullptr) {
CLOGE("Stream %d is unknown", streamId);
- return idx;
+ return BAD_VALUE;
}
- sp<Camera3OutputStreamInterface> stream = mOutputStreams[idx];
status_t res = stream->setConsumers(consumers);
if (res != OK) {
CLOGE("Stream %d set consumer failed (error %d %s) ", streamId, res, strerror(-res));
@@ -2206,10 +2520,10 @@
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
- ssize_t idx = mOutputStreams.indexOfKey(streamId);
- if (idx == NAME_NOT_FOUND) {
+ sp<Camera3OutputStreamInterface> stream = mOutputStreams.get(streamId);
+ if (stream == nullptr) {
CLOGE("Stream %d is unknown", streamId);
- return idx;
+ return BAD_VALUE;
}
for (const auto &it : removedSurfaceIds) {
@@ -2219,7 +2533,6 @@
}
}
- sp<Camera3OutputStreamInterface> stream = mOutputStreams[idx];
status_t res = stream->updateStream(newSurfaces, outputInfo, removedSurfaceIds, outputMap);
if (res != OK) {
CLOGE("Stream %d failed to update stream (error %d %s) ",
@@ -2238,13 +2551,11 @@
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
- int idx = mOutputStreams.indexOfKey(streamId);
- if (idx == NAME_NOT_FOUND) {
+ sp<Camera3OutputStreamInterface> stream = mOutputStreams.get(streamId);
+ if (stream == nullptr) {
ALOGE("%s: Stream %d is not found.", __FUNCTION__, streamId);
return BAD_VALUE;
}
-
- sp<Camera3OutputStreamInterface> stream = mOutputStreams.editValueAt(idx);
return stream->dropBuffers(dropping);
}
@@ -2298,15 +2609,12 @@
}
for (size_t i = 0; i < streams.count; i++) {
- int idx = mOutputStreams.indexOfKey(streams.data.i32[i]);
- if (idx == NAME_NOT_FOUND) {
+ sp<Camera3OutputStreamInterface> stream = mOutputStreams.get(streams.data.i32[i]);
+ if (stream == nullptr) {
CLOGE("Request references unknown stream %d",
- streams.data.u8[i]);
+ streams.data.i32[i]);
return NULL;
}
- sp<Camera3OutputStreamInterface> stream =
- mOutputStreams.editValueAt(idx);
-
// It is illegal to include a deferred consumer output stream into a request
auto iter = surfaceMap.find(streams.data.i32[i]);
if (iter != surfaceMap.end()) {
@@ -2367,7 +2675,7 @@
}
for (size_t i = 0; i < mOutputStreams.size(); i++) {
- sp<Camera3OutputStreamInterface> outputStream = mOutputStreams.editValueAt(i);
+ sp<Camera3OutputStreamInterface> outputStream = mOutputStreams[i];
if (outputStream->isConfiguring()) {
res = outputStream->cancelConfiguration();
if (res != OK) {
@@ -2502,7 +2810,7 @@
}
camera3_stream_t *outputStream;
- outputStream = mOutputStreams.editValueAt(i)->startConfiguration();
+ outputStream = mOutputStreams[i]->startConfiguration();
if (outputStream == NULL) {
CLOGE("Can't start output stream configuration");
cancelStreamsConfigurationLocked();
@@ -2560,8 +2868,7 @@
}
for (size_t i = 0; i < mOutputStreams.size(); i++) {
- sp<Camera3OutputStreamInterface> outputStream =
- mOutputStreams.editValueAt(i);
+ sp<Camera3OutputStreamInterface> outputStream = mOutputStreams[i];
if (outputStream->isConfiguring() && !outputStream->isConsumerConfigurationDeferred()) {
res = outputStream->finishConfiguration();
if (res != OK) {
@@ -2624,6 +2931,10 @@
return rc;
}
+ if (mDummyStreamId == NO_STREAM) {
+ mRequestBufferSM.onStreamsConfigured();
+ }
+
return OK;
}
@@ -2668,15 +2979,12 @@
// Ok, have a dummy stream and there's at least one other output stream,
// so remove the dummy
- sp<Camera3StreamInterface> deletedStream;
- ssize_t outputStreamIdx = mOutputStreams.indexOfKey(mDummyStreamId);
- if (outputStreamIdx == NAME_NOT_FOUND) {
+ sp<Camera3StreamInterface> deletedStream = mOutputStreams.get(mDummyStreamId);
+ if (deletedStream == nullptr) {
SET_ERR_L("Dummy stream %d does not appear to exist", mDummyStreamId);
return INVALID_OPERATION;
}
-
- deletedStream = mOutputStreams.editValueAt(outputStreamIdx);
- mOutputStreams.removeItemsAt(outputStreamIdx);
+ mOutputStreams.remove(mDummyStreamId);
// Free up the stream endpoint so that it can be used by some other stream
res = deletedStream->disconnect();
@@ -2751,13 +3059,14 @@
int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
bool hasAppCallback, nsecs_t maxExpectedDuration,
std::set<String8>& physicalCameraIds, bool isStillCapture,
- bool isZslCapture) {
+ bool isZslCapture, const SurfaceMap& outputSurfaces) {
ATRACE_CALL();
Mutex::Autolock l(mInFlightLock);
ssize_t res;
res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput,
- hasAppCallback, maxExpectedDuration, physicalCameraIds, isStillCapture, isZslCapture));
+ hasAppCallback, maxExpectedDuration, physicalCameraIds, isStillCapture, isZslCapture,
+ outputSurfaces));
if (res < 0) return res;
if (mInFlightMap.size() == 1) {
@@ -2775,18 +3084,55 @@
void Camera3Device::returnOutputBuffers(
const camera3_stream_buffer_t *outputBuffers, size_t numBuffers,
- nsecs_t timestamp, bool timestampIncreasing) {
+ nsecs_t timestamp, bool timestampIncreasing,
+ const SurfaceMap& outputSurfaces,
+ const CaptureResultExtras &inResultExtras) {
for (size_t i = 0; i < numBuffers; i++)
{
- Camera3Stream *stream = Camera3Stream::cast(outputBuffers[i].stream);
- status_t res = stream->returnBuffer(outputBuffers[i], timestamp, timestampIncreasing);
+ Camera3StreamInterface *stream = Camera3Stream::cast(outputBuffers[i].stream);
+ int streamId = stream->getId();
+ const auto& it = outputSurfaces.find(streamId);
+ status_t res = OK;
+ if (it != outputSurfaces.end()) {
+ res = stream->returnBuffer(
+ outputBuffers[i], timestamp, timestampIncreasing, it->second);
+ } else {
+ res = stream->returnBuffer(
+ outputBuffers[i], timestamp, timestampIncreasing);
+ }
+
// Note: stream may be deallocated at this point, if this buffer was
// the last reference to it.
if (res != OK) {
ALOGE("Can't return buffer to its stream: %s (%d)",
strerror(-res), res);
}
+
+ // Long processing consumers can cause returnBuffer timeout for shared stream
+ // If that happens, cancel the buffer and send a buffer error to client
+ if (it != outputSurfaces.end() && res == TIMED_OUT &&
+ outputBuffers[i].status == CAMERA3_BUFFER_STATUS_OK) {
+ // cancel the buffer
+ camera3_stream_buffer_t sb = outputBuffers[i];
+ sb.status = CAMERA3_BUFFER_STATUS_ERROR;
+ stream->returnBuffer(sb, /*timestamp*/0, timestampIncreasing);
+
+ // notify client buffer error
+ sp<NotificationListener> listener;
+ {
+ Mutex::Autolock l(mOutputLock);
+ listener = mListener.promote();
+ }
+
+ if (listener != nullptr) {
+ CaptureResultExtras extras = inResultExtras;
+ extras.errorStreamId = streamId;
+ listener->notifyError(
+ hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER,
+ extras);
+ }
+ }
}
}
@@ -2797,6 +3143,7 @@
// Indicate idle inFlightMap to the status tracker
if (mInFlightMap.size() == 0) {
+ mRequestBufferSM.onInflightMapEmpty();
// Hold a separate dedicated tracker lock to prevent race with disconnect and also
// avoid a deadlock during reprocess requests.
Mutex::Autolock l(mTrackerLock);
@@ -2844,7 +3191,8 @@
assert(request.requestStatus != OK ||
request.pendingOutputBuffers.size() == 0);
returnOutputBuffers(request.pendingOutputBuffers.array(),
- request.pendingOutputBuffers.size(), 0);
+ request.pendingOutputBuffers.size(), 0, /*timestampIncreasing*/true,
+ request.outputSurfaces, request.resultExtras);
removeInFlightMapEntryLocked(idx);
ALOGVV("%s: removed frame %d from InFlightMap", __FUNCTION__, frameNumber);
@@ -2868,7 +3216,9 @@
for (size_t idx = 0; idx < mInFlightMap.size(); idx++) {
const InFlightRequest &request = mInFlightMap.valueAt(idx);
returnOutputBuffers(request.pendingOutputBuffers.array(),
- request.pendingOutputBuffers.size(), 0);
+ request.pendingOutputBuffers.size(), 0,
+ /*timestampIncreasing*/true, request.outputSurfaces,
+ request.resultExtras);
}
mInFlightMap.clear();
mExpectedInflightDuration = 0;
@@ -2943,12 +3293,12 @@
frameNumber, streamId, strerror(-res), res);
}
} else {
- ssize_t idx = mOutputStreams.indexOfKey(streamId);
- if (idx == NAME_NOT_FOUND) {
+ sp<Camera3StreamInterface> stream = mOutputStreams.get(streamId);
+ if (stream == nullptr) {
ALOGE("%s: Output stream id %d not found!", __FUNCTION__, streamId);
continue;
}
- streamBuffer.stream = mOutputStreams.valueAt(idx)->asHalStream();
+ streamBuffer.stream = stream->asHalStream();
returnOutputBuffers(&streamBuffer, /*size*/1, /*timestamp*/ 0);
}
}
@@ -2996,6 +3346,13 @@
captureResult.mResultExtras = resultExtras;
captureResult.mMetadata = partialResult;
+ // Fix up result metadata for monochrome camera.
+ status_t res = fixupMonochromeTags(mDeviceInfo, captureResult.mMetadata);
+ if (res != OK) {
+ SET_ERR("Failed to override result metadata: %s (%d)", strerror(-res), res);
+ return;
+ }
+
insertResultLocked(&captureResult, frameNumber);
}
@@ -3067,6 +3424,21 @@
frameNumber, strerror(res), res);
return;
}
+ // Fix up result metadata for monochrome camera.
+ res = fixupMonochromeTags(mDeviceInfo, captureResult.mMetadata);
+ if (res != OK) {
+ SET_ERR("Failed to override result metadata: %s (%d)", strerror(-res), res);
+ return;
+ }
+ for (auto& physicalMetadata : captureResult.mPhysicalMetadatas) {
+ String8 cameraId8(physicalMetadata.mPhysicalCameraId);
+ res = fixupMonochromeTags(mPhysicalDeviceInfoMap.at(cameraId8.c_str()),
+ physicalMetadata.mPhysicalCameraMetadata);
+ if (res != OK) {
+ SET_ERR("Failed to override result metadata: %s (%d)", strerror(-res), res);
+ return;
+ }
+ }
mTagMonitor.monitorMetadata(TagMonitor::RESULT,
frameNumber, timestamp.data.i64[0], captureResult.mMetadata);
@@ -3226,7 +3598,8 @@
} else {
bool timestampIncreasing = !(request.zslCapture || request.hasInputBuffer);
returnOutputBuffers(result->output_buffers,
- result->num_output_buffers, shutterTimestamp, timestampIncreasing);
+ result->num_output_buffers, shutterTimestamp, timestampIncreasing,
+ request.outputSurfaces, request.resultExtras);
}
if (result->result != NULL && !isPartialResult) {
@@ -3239,7 +3612,7 @@
if (shutterTimestamp == 0) {
request.pendingMetadata = result->result;
request.collectedPartialResult = collectedPartialResult;
- } else if (request.hasCallback) {
+ } else if (request.hasCallback) {
CameraMetadata metadata;
metadata = result->result;
sendCaptureResult(metadata, request.resultExtras,
@@ -3359,6 +3732,9 @@
// In case of missing result check whether the buffers
// returned. If they returned, then remove inflight
// request.
+ // TODO: should we call this for ERROR_CAMERA_REQUEST as well?
+ // otherwise we are depending on HAL to send the buffers back after
+ // calling notifyError. Not sure if that's in the spec.
removeInFlightRequestIfReadyLocked(idx);
}
} else {
@@ -3434,7 +3810,8 @@
}
bool timestampIncreasing = !(r.zslCapture || r.hasInputBuffer);
returnOutputBuffers(r.pendingOutputBuffers.array(),
- r.pendingOutputBuffers.size(), r.shutterTimestamp, timestampIncreasing);
+ r.pendingOutputBuffers.size(), r.shutterTimestamp, timestampIncreasing,
+ r.outputSurfaces, r.resultExtras);
r.pendingOutputBuffers.clear();
removeInFlightRequestIfReadyLocked(idx);
@@ -3475,6 +3852,10 @@
mRequestMetadataQueue(queue) {
// Check with hardware service manager if we can downcast these interfaces
// Somewhat expensive, so cache the results at startup
+ auto castResult_3_5 = device::V3_5::ICameraDeviceSession::castFrom(mHidlSession);
+ if (castResult_3_5.isOk()) {
+ mHidlSession_3_5 = castResult_3_5;
+ }
auto castResult_3_4 = device::V3_4::ICameraDeviceSession::castFrom(mHidlSession);
if (castResult_3_4.isOk()) {
mHidlSession_3_4 = castResult_3_4;
@@ -3651,26 +4032,49 @@
// Invoke configureStreams
device::V3_3::HalStreamConfiguration finalConfiguration;
+ device::V3_4::HalStreamConfiguration finalConfiguration3_4;
common::V1_0::Status status;
- // See if we have v3.4 or v3.3 HAL
- if (mHidlSession_3_4 != nullptr) {
- // We do; use v3.4 for the call
- ALOGV("%s: v3.4 device found", __FUNCTION__);
- device::V3_4::HalStreamConfiguration finalConfiguration3_4;
- auto err = mHidlSession_3_4->configureStreams_3_4(requestedConfiguration3_4,
- [&status, &finalConfiguration3_4]
+ auto configStream34Cb = [&status, &finalConfiguration3_4]
(common::V1_0::Status s, const device::V3_4::HalStreamConfiguration& halConfiguration) {
finalConfiguration3_4 = halConfiguration;
status = s;
- });
- if (!err.isOk()) {
- ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
- return DEAD_OBJECT;
+ };
+
+ auto postprocConfigStream34 = [&finalConfiguration, &finalConfiguration3_4]
+ (hardware::Return<void>& err) -> status_t {
+ if (!err.isOk()) {
+ ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+ return DEAD_OBJECT;
+ }
+ finalConfiguration.streams.resize(finalConfiguration3_4.streams.size());
+ for (size_t i = 0; i < finalConfiguration3_4.streams.size(); i++) {
+ finalConfiguration.streams[i] = finalConfiguration3_4.streams[i].v3_3;
+ }
+ return OK;
+ };
+
+ // See if we have v3.4 or v3.3 HAL
+ if (mHidlSession_3_5 != nullptr) {
+ ALOGV("%s: v3.5 device found", __FUNCTION__);
+ device::V3_5::StreamConfiguration requestedConfiguration3_5;
+ requestedConfiguration3_5.v3_4 = requestedConfiguration3_4;
+ requestedConfiguration3_5.streamConfigCounter = mNextStreamConfigCounter++;
+ auto err = mHidlSession_3_5->configureStreams_3_5(
+ requestedConfiguration3_5, configStream34Cb);
+ res = postprocConfigStream34(err);
+ if (res != OK) {
+ return res;
}
- finalConfiguration.streams.resize(finalConfiguration3_4.streams.size());
- for (size_t i = 0; i < finalConfiguration3_4.streams.size(); i++) {
- finalConfiguration.streams[i] = finalConfiguration3_4.streams[i].v3_3;
+ } else if (mHidlSession_3_4 != nullptr) {
+ // We do; use v3.4 for the call
+ ALOGV("%s: v3.4 device found", __FUNCTION__);
+ device::V3_4::HalStreamConfiguration finalConfiguration3_4;
+ auto err = mHidlSession_3_4->configureStreams_3_4(
+ requestedConfiguration3_4, configStream34Cb);
+ res = postprocConfigStream34(err);
+ if (res != OK) {
+ return res;
}
} else if (mHidlSession_3_3 != nullptr) {
// We do; use v3.3 for the call
@@ -4041,6 +4445,20 @@
return res;
}
+void Camera3Device::HalInterface::signalPipelineDrain(const std::vector<int>& streamIds) {
+ ATRACE_NAME("CameraHal::signalPipelineDrain");
+ if (!valid() || mHidlSession_3_5 == nullptr) {
+ ALOGE("%s called on invalid camera!", __FUNCTION__);
+ return;
+ }
+
+ auto err = mHidlSession_3_5->signalStreamFlush(streamIds, mNextStreamConfigCounter);
+ if (!err.isOk()) {
+ ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+ return;
+ }
+}
+
void Camera3Device::HalInterface::getInflightBufferKeys(
std::vector<std::pair<int32_t, int32_t>>* out) {
std::lock_guard<std::mutex> lock(mInflightLock);
@@ -4081,6 +4499,33 @@
return OK;
}
+status_t Camera3Device::HalInterface::pushInflightRequestBuffer(
+ uint64_t bufferId, buffer_handle_t* buf) {
+ std::lock_guard<std::mutex> lock(mRequestedBuffersLock);
+ auto pair = mRequestedBuffers.insert({bufferId, buf});
+ if (!pair.second) {
+ ALOGE("%s: bufId %" PRIu64 " is already inflight!",
+ __FUNCTION__, bufferId);
+ return BAD_VALUE;
+ }
+ return OK;
+}
+
+// Find and pop a buffer_handle_t based on bufferId
+status_t Camera3Device::HalInterface::popInflightRequestBuffer(
+ uint64_t bufferId, /*out*/ buffer_handle_t **buffer) {
+ std::lock_guard<std::mutex> lock(mRequestedBuffersLock);
+ auto it = mRequestedBuffers.find(bufferId);
+ if (it == mRequestedBuffers.end()) {
+ ALOGE("%s: bufId %" PRIu64 " is not inflight!",
+ __FUNCTION__, bufferId);
+ return BAD_VALUE;
+ }
+ *buffer = it->second;
+ mRequestedBuffers.erase(it);
+ return OK;
+}
+
std::pair<bool, uint64_t> Camera3Device::HalInterface::getBufferId(
const buffer_handle_t& buf, int streamId) {
std::lock_guard<std::mutex> lock(mBufferIdMapLock);
@@ -4129,7 +4574,8 @@
Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
sp<StatusTracker> statusTracker,
- sp<HalInterface> interface, const Vector<int32_t>& sessionParamKeys) :
+ sp<HalInterface> interface, const Vector<int32_t>& sessionParamKeys,
+ bool useHalBufManager) :
Thread(/*canCallJava*/false),
mParent(parent),
mStatusTracker(statusTracker),
@@ -4139,6 +4585,7 @@
mReconfigured(false),
mDoPause(false),
mPaused(true),
+ mNotifyPipelineDrain(false),
mFrameNumber(0),
mLatestRequestId(NAME_NOT_FOUND),
mCurrentAfTriggerId(0),
@@ -4149,7 +4596,8 @@
mConstrainedMode(false),
mRequestLatency(kRequestLatencyBinSize),
mSessionParamKeys(sessionParamKeys),
- mLatestSessionParams(sessionParamKeys.size()) {
+ mLatestSessionParams(sessionParamKeys.size()),
+ mUseHalBufManager(useHalBufManager) {
mStatusId = statusTracker->addComponent();
}
@@ -4764,6 +5212,13 @@
nsecs_t tRequestEnd = systemTime(SYSTEM_TIME_MONOTONIC);
mRequestLatency.add(tRequestStart, tRequestEnd);
+ if (submitRequestSuccess) {
+ sp<Camera3Device> parent = mParent.promote();
+ if (parent != nullptr) {
+ parent->mRequestBufferSM.onRequestSubmitted();
+ }
+ }
+
if (useFlushLock) {
mFlushLock.unlock();
}
@@ -4908,8 +5363,11 @@
}
nsecs_t waitDuration = kBaseGetBufferWait + parent->getExpectedInFlightDuration();
+ SurfaceMap uniqueSurfaceIdMap;
for (size_t j = 0; j < captureRequest->mOutputStreams.size(); j++) {
- sp<Camera3OutputStreamInterface> outputStream = captureRequest->mOutputStreams.editItemAt(j);
+ sp<Camera3OutputStreamInterface> outputStream =
+ captureRequest->mOutputStreams.editItemAt(j);
+ int streamId = outputStream->getId();
// Prepare video buffers for high speed recording on the first video request.
if (mPrepareVideoStream && outputStream->isVideoStream()) {
@@ -4928,16 +5386,41 @@
}
}
- res = outputStream->getBuffer(&outputBuffers->editItemAt(j),
- waitDuration,
- captureRequest->mOutputSurfaces[outputStream->getId()]);
- if (res != OK) {
- // Can't get output buffer from gralloc queue - this could be due to
- // abandoned queue or other consumer misbehavior, so not a fatal
- // error
- ALOGE("RequestThread: Can't get output buffer, skipping request:"
- " %s (%d)", strerror(-res), res);
- return TIMED_OUT;
+ std::vector<size_t> uniqueSurfaceIds;
+ res = outputStream->getUniqueSurfaceIds(
+ captureRequest->mOutputSurfaces[streamId],
+ &uniqueSurfaceIds);
+ // INVALID_OPERATION is normal output for streams not supporting surfaceIds
+ if (res != OK && res != INVALID_OPERATION) {
+ ALOGE("%s: failed to query stream %d unique surface IDs",
+ __FUNCTION__, streamId);
+ return res;
+ }
+ if (res == OK) {
+ uniqueSurfaceIdMap.insert({streamId, std::move(uniqueSurfaceIds)});
+ }
+
+ if (mUseHalBufManager) {
+ // HAL will request buffer through requestStreamBuffer API
+ camera3_stream_buffer_t& buffer = outputBuffers->editItemAt(j);
+ buffer.stream = outputStream->asHalStream();
+ buffer.buffer = nullptr;
+ buffer.status = CAMERA3_BUFFER_STATUS_OK;
+ buffer.acquire_fence = -1;
+ buffer.release_fence = -1;
+ } else {
+ res = outputStream->getBuffer(&outputBuffers->editItemAt(j),
+ waitDuration,
+ captureRequest->mOutputSurfaces[streamId]);
+ if (res != OK) {
+ // Can't get output buffer from gralloc queue - this could be due to
+ // abandoned queue or other consumer misbehavior, so not a fatal
+ // error
+ ALOGE("RequestThread: Can't get output buffer, skipping request:"
+ " %s (%d)", strerror(-res), res);
+
+ return TIMED_OUT;
+ }
}
String8 physicalCameraId = outputStream->getPhysicalCameraId();
@@ -4982,7 +5465,9 @@
/*hasInput*/halRequest->input_buffer != NULL,
hasCallback,
calculateMaxExpectedDuration(halRequest->settings),
- requestedPhysicalCameras, isStillCapture, isZslCapture);
+ requestedPhysicalCameras, isStillCapture, isZslCapture,
+ (mUseHalBufManager) ? uniqueSurfaceIdMap :
+ SurfaceMap{});
ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
", burstId = %" PRId32 ".",
__FUNCTION__,
@@ -5058,7 +5543,7 @@
if (s.first == streamId) {
const auto &it = std::find(s.second.begin(), s.second.end(), surfaceId);
if (it != s.second.end()) {
- return true;
+ return true;
}
}
}
@@ -5069,7 +5554,7 @@
if (s.first == streamId) {
const auto &it = std::find(s.second.begin(), s.second.end(), surfaceId);
if (it != s.second.end()) {
- return true;
+ return true;
}
}
}
@@ -5078,6 +5563,22 @@
return false;
}
+void Camera3Device::RequestThread::signalPipelineDrain(const std::vector<int>& streamIds) {
+ if (!mUseHalBufManager) {
+ ALOGE("%s called for camera device not supporting HAL buffer management", __FUNCTION__);
+ return;
+ }
+
+ Mutex::Autolock pl(mPauseLock);
+ if (mPaused) {
+ mInterface->signalPipelineDrain(streamIds);
+ return;
+ }
+ // If request thread is still busy, wait until paused then notify HAL
+ mNotifyPipelineDrain = true;
+ mStreamIdsToBeDrained = streamIds;
+}
+
nsecs_t Camera3Device::getExpectedInFlightDuration() {
ATRACE_CALL();
Mutex::Autolock al(mInFlightLock);
@@ -5255,6 +5756,15 @@
if (statusTracker != 0) {
statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
}
+ if (mNotifyPipelineDrain) {
+ mInterface->signalPipelineDrain(mStreamIdsToBeDrained);
+ mNotifyPipelineDrain = false;
+ mStreamIdsToBeDrained.clear();
+ }
+ sp<Camera3Device> parent = mParent.promote();
+ if (parent != nullptr) {
+ parent->mRequestBufferSM.onRequestThreadPaused();
+ }
}
// Stop waiting for now and let thread management happen
return NULL;
@@ -5339,6 +5849,15 @@
if (statusTracker != 0) {
statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
}
+ if (mNotifyPipelineDrain) {
+ mInterface->signalPipelineDrain(mStreamIdsToBeDrained);
+ mNotifyPipelineDrain = false;
+ mStreamIdsToBeDrained.clear();
+ }
+ sp<Camera3Device> parent = mParent.promote();
+ if (parent != nullptr) {
+ parent->mRequestBufferSM.onRequestThreadPaused();
+ }
}
res = mDoPauseSignal.waitRelative(mPauseLock, kRequestTimeout);
@@ -5794,23 +6313,176 @@
return true;
}
-/**
- * Static callback forwarding methods from HAL to instance
- */
+status_t Camera3Device::RequestBufferStateMachine::initialize(
+ sp<camera3::StatusTracker> statusTracker) {
+ if (statusTracker == nullptr) {
+ ALOGE("%s: statusTracker is null", __FUNCTION__);
+ return BAD_VALUE;
+ }
-void Camera3Device::sProcessCaptureResult(const camera3_callback_ops *cb,
- const camera3_capture_result *result) {
- Camera3Device *d =
- const_cast<Camera3Device*>(static_cast<const Camera3Device*>(cb));
-
- d->processCaptureResult(result);
+ std::lock_guard<std::mutex> lock(mLock);
+ mStatusTracker = statusTracker;
+ mRequestBufferStatusId = statusTracker->addComponent();
+ return OK;
}
-void Camera3Device::sNotify(const camera3_callback_ops *cb,
- const camera3_notify_msg *msg) {
- Camera3Device *d =
- const_cast<Camera3Device*>(static_cast<const Camera3Device*>(cb));
- d->notify(msg);
+bool Camera3Device::RequestBufferStateMachine::startRequestBuffer() {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mStatus == RB_STATUS_READY) {
+ mRequestBufferOngoing = true;
+ return true;
+ }
+ return false;
+}
+
+void Camera3Device::RequestBufferStateMachine::endRequestBuffer() {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (!mRequestBufferOngoing) {
+ ALOGE("%s called without a successful startRequestBuffer call first!", __FUNCTION__);
+ return;
+ }
+ mRequestBufferOngoing = false;
+ if (mStatus == RB_STATUS_PENDING_STOP) {
+ checkSwitchToStopLocked();
+ }
+}
+
+void Camera3Device::RequestBufferStateMachine::onStreamsConfigured() {
+ std::lock_guard<std::mutex> lock(mLock);
+ RequestBufferState oldStatus = mStatus;
+ mStatus = RB_STATUS_READY;
+ if (oldStatus != RB_STATUS_READY) {
+ notifyTrackerLocked(/*active*/true);
+ }
+ return;
+}
+
+void Camera3Device::RequestBufferStateMachine::onRequestSubmitted() {
+ std::lock_guard<std::mutex> lock(mLock);
+ mRequestThreadPaused = false;
+ mInflightMapEmpty = false;
+ if (mStatus == RB_STATUS_STOPPED) {
+ mStatus = RB_STATUS_READY;
+ notifyTrackerLocked(/*active*/true);
+ }
+ return;
+}
+
+void Camera3Device::RequestBufferStateMachine::onRequestThreadPaused() {
+ std::lock_guard<std::mutex> lock(mLock);
+ mRequestThreadPaused = true;
+ if (mStatus == RB_STATUS_PENDING_STOP) {
+ checkSwitchToStopLocked();
+ }
+ return;
+}
+
+void Camera3Device::RequestBufferStateMachine::onInflightMapEmpty() {
+ std::lock_guard<std::mutex> lock(mLock);
+ mInflightMapEmpty = true;
+ if (mStatus == RB_STATUS_PENDING_STOP) {
+ checkSwitchToStopLocked();
+ }
+ return;
+}
+
+void Camera3Device::RequestBufferStateMachine::onWaitUntilIdle() {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (!checkSwitchToStopLocked()) {
+ mStatus = RB_STATUS_PENDING_STOP;
+ }
+ return;
+}
+
+void Camera3Device::RequestBufferStateMachine::notifyTrackerLocked(bool active) {
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != nullptr) {
+ if (active) {
+ statusTracker->markComponentActive(mRequestBufferStatusId);
+ } else {
+ statusTracker->markComponentIdle(mRequestBufferStatusId, Fence::NO_FENCE);
+ }
+ }
+}
+
+bool Camera3Device::RequestBufferStateMachine::checkSwitchToStopLocked() {
+ if (mInflightMapEmpty && mRequestThreadPaused && !mRequestBufferOngoing) {
+ mStatus = RB_STATUS_STOPPED;
+ notifyTrackerLocked(/*active*/false);
+ return true;
+ }
+ return false;
+}
+
+status_t Camera3Device::fixupMonochromeTags(const CameraMetadata& deviceInfo,
+ CameraMetadata& resultMetadata) {
+ status_t res = OK;
+ if (!mNeedFixupMonochromeTags) {
+ return res;
+ }
+
+ // Remove tags that are not applicable to monochrome camera.
+ int32_t tagsToRemove[] = {
+ ANDROID_SENSOR_GREEN_SPLIT,
+ ANDROID_SENSOR_NEUTRAL_COLOR_POINT,
+ ANDROID_COLOR_CORRECTION_MODE,
+ ANDROID_COLOR_CORRECTION_TRANSFORM,
+ ANDROID_COLOR_CORRECTION_GAINS,
+ };
+ for (auto tag : tagsToRemove) {
+ res = resultMetadata.erase(tag);
+ if (res != OK) {
+ ALOGE("%s: Failed to remove tag %d for monochrome camera", __FUNCTION__, tag);
+ return res;
+ }
+ }
+
+ // ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL
+ camera_metadata_entry blEntry = resultMetadata.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL);
+ for (size_t i = 1; i < blEntry.count; i++) {
+ blEntry.data.f[i] = blEntry.data.f[0];
+ }
+
+ // ANDROID_SENSOR_NOISE_PROFILE
+ camera_metadata_entry npEntry = resultMetadata.find(ANDROID_SENSOR_NOISE_PROFILE);
+ if (npEntry.count > 0 && npEntry.count % 2 == 0) {
+ double np[] = {npEntry.data.d[0], npEntry.data.d[1]};
+ res = resultMetadata.update(ANDROID_SENSOR_NOISE_PROFILE, np, 2);
+ if (res != OK) {
+ ALOGE("%s: Failed to update SENSOR_NOISE_PROFILE: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ }
+
+ // ANDROID_STATISTICS_LENS_SHADING_MAP
+ camera_metadata_ro_entry lsSizeEntry = deviceInfo.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
+ camera_metadata_entry lsEntry = resultMetadata.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
+ if (lsSizeEntry.count == 2 && lsEntry.count > 0
+ && (int32_t)lsEntry.count == 4 * lsSizeEntry.data.i32[0] * lsSizeEntry.data.i32[1]) {
+ for (int32_t i = 0; i < lsSizeEntry.data.i32[0] * lsSizeEntry.data.i32[1]; i++) {
+ lsEntry.data.f[4*i+1] = lsEntry.data.f[4*i];
+ lsEntry.data.f[4*i+2] = lsEntry.data.f[4*i];
+ lsEntry.data.f[4*i+3] = lsEntry.data.f[4*i];
+ }
+ }
+
+ // ANDROID_TONEMAP_CURVE_BLUE
+ // ANDROID_TONEMAP_CURVE_GREEN
+ // ANDROID_TONEMAP_CURVE_RED
+ camera_metadata_entry tcbEntry = resultMetadata.find(ANDROID_TONEMAP_CURVE_BLUE);
+ camera_metadata_entry tcgEntry = resultMetadata.find(ANDROID_TONEMAP_CURVE_GREEN);
+ camera_metadata_entry tcrEntry = resultMetadata.find(ANDROID_TONEMAP_CURVE_RED);
+ if (tcbEntry.count > 0
+ && tcbEntry.count == tcgEntry.count
+ && tcbEntry.count == tcrEntry.count) {
+ for (size_t i = 0; i < tcbEntry.count; i++) {
+ tcbEntry.data.f[i] = tcrEntry.data.f[i];
+ tcgEntry.data.f[i] = tcrEntry.data.f[i];
+ }
+ }
+
+ return res;
}
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 5e749b6..35b9d6d 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -33,10 +33,11 @@
#include <android/hardware/camera/device/3.2/ICameraDeviceSession.h>
#include <android/hardware/camera/device/3.3/ICameraDeviceSession.h>
#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
+#include <android/hardware/camera/device/3.5/ICameraDeviceSession.h>
#include <android/hardware/camera/device/3.2/ICameraDeviceCallback.h>
#include <android/hardware/camera/device/3.4/ICameraDeviceCallback.h>
+#include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
#include <fmq/MessageQueue.h>
-#include <hardware/camera3.h>
#include <camera/CaptureResult.h>
@@ -50,20 +51,6 @@
using android::camera3::OutputStreamInfo;
-/**
- * Function pointer types with C calling convention to
- * use for HAL callback functions.
- */
-extern "C" {
- typedef void (callbacks_process_capture_result_t)(
- const struct camera3_callback_ops *,
- const camera3_capture_result_t *);
-
- typedef void (callbacks_notify_t)(
- const struct camera3_callback_ops *,
- const camera3_notify_msg_t *);
-}
-
namespace android {
namespace camera3 {
@@ -80,8 +67,7 @@
*/
class Camera3Device :
public CameraDeviceBase,
- virtual public hardware::camera::device::V3_4::ICameraDeviceCallback,
- private camera3_callback_ops {
+ virtual public hardware::camera::device::V3_5::ICameraDeviceCallback {
public:
explicit Camera3Device(const String8& id);
@@ -299,14 +285,27 @@
status_t dump(int fd);
status_t close();
+ void signalPipelineDrain(const std::vector<int>& streamIds);
+
+ // method to extract buffer's unique ID
+ // return pair of (newlySeenBuffer?, bufferId)
+ std::pair<bool, uint64_t> getBufferId(const buffer_handle_t& buf, int streamId);
+
// Find a buffer_handle_t based on frame number and stream ID
status_t popInflightBuffer(int32_t frameNumber, int32_t streamId,
/*out*/ buffer_handle_t **buffer);
+ // Register a bufId/buffer_handle_t to inflight request buffer
+ status_t pushInflightRequestBuffer(uint64_t bufferId, buffer_handle_t* buf);
+
+ // Find a buffer_handle_t based on bufferId
+ status_t popInflightRequestBuffer(uint64_t bufferId, /*out*/ buffer_handle_t **buffer);
+
// Get a vector of (frameNumber, streamId) pair of currently inflight
// buffers
void getInflightBufferKeys(std::vector<std::pair<int32_t, int32_t>>* out);
+ static const uint64_t BUFFER_ID_NO_BUFFER = 0;
private:
// Always valid
sp<hardware::camera::device::V3_2::ICameraDeviceSession> mHidlSession;
@@ -314,6 +313,8 @@
sp<hardware::camera::device::V3_3::ICameraDeviceSession> mHidlSession_3_3;
// Valid if ICameraDeviceSession is @3.4 or newer
sp<hardware::camera::device::V3_4::ICameraDeviceSession> mHidlSession_3_4;
+ // Valid if ICameraDeviceSession is @3.5 or newer
+ sp<hardware::camera::device::V3_5::ICameraDeviceSession> mHidlSession_3_5;
std::shared_ptr<RequestMetadataQueue> mRequestMetadataQueue;
@@ -365,19 +366,16 @@
// stream ID -> per stream buffer ID map
std::unordered_map<int, BufferIdMap> mBufferIdMaps;
uint64_t mNextBufferId = 1; // 0 means no buffer
- static const uint64_t BUFFER_ID_NO_BUFFER = 0;
-
- // method to extract buffer's unique ID
- // TODO: we should switch to use gralloc mapper's getBackingStore API
- // once we ran in binderized gralloc mode, but before that is ready,
- // we need to rely on the conventional buffer queue behavior where
- // buffer_handle_t's FD won't change.
- // return pair of (newlySeenBuffer?, bufferId)
- std::pair<bool, uint64_t> getBufferId(const buffer_handle_t& buf, int streamId);
virtual void onBufferFreed(int streamId, const native_handle_t* handle) override;
std::vector<std::pair<int, uint64_t>> mFreedBuffers;
+
+ // Buffers given to HAL through requestStreamBuffer API
+ std::mutex mRequestedBuffersLock;
+ std::unordered_map<uint64_t, buffer_handle_t*> mRequestedBuffers;
+
+ uint32_t mNextStreamConfigCounter = 1;
};
sp<HalInterface> mInterface;
@@ -412,9 +410,22 @@
// Tracking cause of fatal errors when in STATUS_ERROR
String8 mErrorCause;
- // Mapping of stream IDs to stream instances
- typedef KeyedVector<int, sp<camera3::Camera3OutputStreamInterface> >
- StreamSet;
+ // Synchronized mapping of stream IDs to stream instances
+ class StreamSet {
+ public:
+ status_t add(int streamId, sp<camera3::Camera3OutputStreamInterface>);
+ ssize_t remove(int streamId);
+ sp<camera3::Camera3OutputStreamInterface> get(int streamId);
+ // get by (underlying) vector index
+ sp<camera3::Camera3OutputStreamInterface> operator[] (size_t index);
+ size_t size() const;
+ std::vector<int> getStreamIds();
+ void clear();
+
+ private:
+ mutable std::mutex mLock;
+ KeyedVector<int, sp<camera3::Camera3OutputStreamInterface>> mData;
+ };
StreamSet mOutputStreams;
sp<camera3::Camera3Stream> mInputStream;
@@ -483,8 +494,9 @@
/**
- * Implementation of android::hardware::camera::device::V3_4::ICameraDeviceCallback
+ * Implementation of android::hardware::camera::device::V3_5::ICameraDeviceCallback
*/
+
hardware::Return<void> processCaptureResult_3_4(
const hardware::hidl_vec<
hardware::camera::device::V3_4::CaptureResult>& results) override;
@@ -495,6 +507,15 @@
const hardware::hidl_vec<
hardware::camera::device::V3_2::NotifyMsg>& msgs) override;
+ hardware::Return<void> requestStreamBuffers(
+ const hardware::hidl_vec<
+ hardware::camera::device::V3_5::BufferRequest>& bufReqs,
+ requestStreamBuffers_cb _hidl_cb) override;
+
+ hardware::Return<void> returnStreamBuffers(
+ const hardware::hidl_vec<
+ hardware::camera::device::V3_2::StreamBuffer>& buffers) override;
+
// Handle one capture result. Assume that mProcessCaptureResultLock is held.
void processOneCaptureResultLocked(
const hardware::camera::device::V3_2::CaptureResult& result,
@@ -702,7 +723,9 @@
RequestThread(wp<Camera3Device> parent,
sp<camera3::StatusTracker> statusTracker,
- sp<HalInterface> interface, const Vector<int32_t>& sessionParamKeys);
+ sp<HalInterface> interface,
+ const Vector<int32_t>& sessionParamKeys,
+ bool useHalBufManager);
~RequestThread();
void setNotificationListener(wp<NotificationListener> listener);
@@ -790,6 +813,8 @@
mRequestLatency.dump(fd, name);
}
+ void signalPipelineDrain(const std::vector<int>& streamIds);
+
protected:
virtual bool threadLoop();
@@ -899,12 +924,13 @@
bool mReconfigured;
- // Used by waitIfPaused, waitForNextRequest, and waitUntilPaused
+ // Used by waitIfPaused, waitForNextRequest, waitUntilPaused, and signalPipelineDrain
Mutex mPauseLock;
bool mDoPause;
Condition mDoPauseSignal;
bool mPaused;
- Condition mPausedSignal;
+ bool mNotifyPipelineDrain;
+ std::vector<int> mStreamIdsToBeDrained;
sp<CaptureRequest> mPrevRequest;
int32_t mPrevTriggers;
@@ -937,6 +963,8 @@
Vector<int32_t> mSessionParamKeys;
CameraMetadata mLatestSessionParams;
+
+ const bool mUseHalBufManager;
};
sp<RequestThread> mRequestThread;
@@ -1003,6 +1031,9 @@
// Indicates a ZSL capture request
bool zslCapture;
+ // What shared surfaces an output should go to
+ SurfaceMap outputSurfaces;
+
// Default constructor needed by KeyedVector
InFlightRequest() :
shutterTimestamp(0),
@@ -1020,8 +1051,9 @@
InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput,
bool hasAppCallback, nsecs_t maxDuration,
- const std::set<String8>& physicalCameraIdSet, bool isStillCapture,
- bool isZslCapture) :
+ const std::set<String8>& physicalCameraIdSet, bool isStillCapture,
+ bool isZslCapture,
+ const SurfaceMap& outSurfaces = SurfaceMap{}) :
shutterTimestamp(0),
sensorTimestamp(0),
requestStatus(OK),
@@ -1034,7 +1066,8 @@
skipResultMetadata(false),
physicalCameraIds(physicalCameraIdSet),
stillCapture(isStillCapture),
- zslCapture(isZslCapture) {
+ zslCapture(isZslCapture),
+ outputSurfaces(outSurfaces) {
}
};
@@ -1051,7 +1084,8 @@
status_t registerInFlight(uint32_t frameNumber,
int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
bool callback, nsecs_t maxExpectedDuration, std::set<String8>& physicalCameraIds,
- bool isStillCapture, bool isZslCapture);
+ bool isStillCapture, bool isZslCapture,
+ const SurfaceMap& outputSurfaces);
/**
* Returns the maximum expected time it'll take for all currently in-flight
@@ -1161,7 +1195,11 @@
// helper function to return the output buffers to the streams.
void returnOutputBuffers(const camera3_stream_buffer_t *outputBuffers,
- size_t numBuffers, nsecs_t timestamp, bool timestampIncreasing = true);
+ size_t numBuffers, nsecs_t timestamp, bool timestampIncreasing = true,
+ // The following arguments are only meant for surface sharing use case
+ const SurfaceMap& outputSurfaces = SurfaceMap{},
+ // Used to send buffer error callback when failing to return buffer
+ const CaptureResultExtras &resultExtras = CaptureResultExtras{});
// Send a partial capture result.
void sendPartialCaptureResult(const camera_metadata_t * partialResult,
@@ -1217,16 +1255,88 @@
// Cached last requested template id
int mLastTemplateId;
- /**
- * Static callback forwarding methods from HAL to instance
- */
- static callbacks_process_capture_result_t sProcessCaptureResult;
-
- static callbacks_notify_t sNotify;
-
// Synchronizes access to status tracker between inflight updates and disconnect.
// b/79972865
Mutex mTrackerLock;
+
+ // Whether HAL request buffers through requestStreamBuffers API
+ bool mUseHalBufManager = false;
+
+ // Lock to ensure requestStreamBuffers() callbacks are serialized
+ std::mutex mRequestBufferInterfaceLock;
+
+ // The state machine to control when requestStreamBuffers should allow
+ // HAL to request buffers.
+ enum RequestBufferState {
+ /**
+ * This is the initial state.
+ * requestStreamBuffers call will return FAILED_CONFIGURING in this state.
+ * Will switch to RB_STATUS_READY after a successful configureStreams or
+ * processCaptureRequest call.
+ */
+ RB_STATUS_STOPPED,
+
+ /**
+ * requestStreamBuffers call will proceed in this state.
+ * When device is asked to stay idle via waitUntilStateThenRelock() call:
+ * - Switch to RB_STATUS_STOPPED if there is no inflight requests and
+ * request thread is paused.
+ * - Switch to RB_STATUS_PENDING_STOP otherwise
+ */
+ RB_STATUS_READY,
+
+ /**
+ * requestStreamBuffers call will proceed in this state.
+ * Switch to RB_STATUS_STOPPED when all inflight requests are fulfilled
+ * and request thread is paused
+ */
+ RB_STATUS_PENDING_STOP,
+ };
+
+ class RequestBufferStateMachine {
+ public:
+ status_t initialize(sp<camera3::StatusTracker> statusTracker);
+
+ // Return if the state machine currently allows for requestBuffers
+ // If the state allows for it, mRequestBufferOngoing will be set to true
+ // and caller must call endRequestBuffer() later to unset the flag
+ bool startRequestBuffer();
+ void endRequestBuffer();
+
+ // Events triggered by application API call
+ void onStreamsConfigured();
+ void onWaitUntilIdle();
+
+ // Events usually triggered by hwBinder processCaptureResult callback thread
+ // But can also be triggered on request thread for failed request, or on
+ // hwbinder notify callback thread for shutter/error callbacks
+ void onInflightMapEmpty();
+
+ // Events triggered by RequestThread
+ void onRequestSubmitted();
+ void onRequestThreadPaused();
+
+ private:
+ void notifyTrackerLocked(bool active);
+
+ // Switch to STOPPED state and return true if all conditions allows for it.
+ // Otherwise do nothing and return false.
+ bool checkSwitchToStopLocked();
+
+ std::mutex mLock;
+ RequestBufferState mStatus = RB_STATUS_STOPPED;
+
+ bool mRequestThreadPaused = true;
+ bool mInflightMapEmpty = true;
+ bool mRequestBufferOngoing = false;
+
+ wp<camera3::StatusTracker> mStatusTracker;
+ int mRequestBufferStatusId;
+ } mRequestBufferSM;
+
+ // Fix up result metadata for monochrome camera.
+ bool mNeedFixupMonochromeTags;
+ status_t fixupMonochromeTags(const CameraMetadata& deviceInfo, CameraMetadata& resultMetadata);
}; // class Camera3Device
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index fb1ff77..b637160 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -48,7 +48,7 @@
status_t Camera3DummyStream::returnBufferLocked(
const camera3_stream_buffer &,
- nsecs_t) {
+ nsecs_t, const std::vector<size_t>&) {
ATRACE_CALL();
ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", __FUNCTION__, mId);
return INVALID_OPERATION;
@@ -58,6 +58,7 @@
const camera3_stream_buffer &,
nsecs_t,
bool,
+ const std::vector<size_t>&,
/*out*/
sp<Fence>*) {
ATRACE_CALL();
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
index 4627548..4b67ea5 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h
@@ -87,6 +87,9 @@
*/
virtual ssize_t getSurfaceId(const sp<Surface> &/*surface*/) { return 0; }
+ virtual status_t getUniqueSurfaceIds(const std::vector<size_t>&,
+ /*out*/std::vector<size_t>*) { return INVALID_OPERATION; };
+
/**
* Update the stream output surfaces.
*/
@@ -104,6 +107,7 @@
const camera3_stream_buffer &buffer,
nsecs_t timestamp,
bool output,
+ const std::vector<size_t>& surface_ids,
/*out*/
sp<Fence> *releaseFenceOut);
@@ -128,7 +132,7 @@
const std::vector<size_t>& surface_ids = std::vector<size_t>());
virtual status_t returnBufferLocked(
const camera3_stream_buffer &buffer,
- nsecs_t timestamp);
+ nsecs_t timestamp, const std::vector<size_t>& surface_ids);
virtual status_t configureQueueLocked();
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index 3c1e43d..2e909a0 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -116,7 +116,7 @@
return mTotalBufferCount;
}
-size_t Camera3IOStreamBase::getHandoutOutputBufferCountLocked() {
+size_t Camera3IOStreamBase::getHandoutOutputBufferCountLocked() const {
return mHandoutOutputBufferCount;
}
@@ -219,7 +219,8 @@
status_t Camera3IOStreamBase::returnAnyBufferLocked(
const camera3_stream_buffer &buffer,
nsecs_t timestamp,
- bool output) {
+ bool output,
+ const std::vector<size_t>& surface_ids) {
status_t res;
// returnBuffer may be called from a raw pointer, not a sp<>, and we'll be
@@ -235,7 +236,7 @@
}
sp<Fence> releaseFence;
- res = returnBufferCheckedLocked(buffer, timestamp, output,
+ res = returnBufferCheckedLocked(buffer, timestamp, output, surface_ids,
&releaseFence);
// Res may be an error, but we still want to decrement our owned count
// to enable clean shutdown. So we'll just return the error but otherwise
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index 0a31d44..750f64d 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -66,12 +66,14 @@
status_t returnAnyBufferLocked(
const camera3_stream_buffer &buffer,
nsecs_t timestamp,
- bool output);
+ bool output,
+ const std::vector<size_t>& surface_ids = std::vector<size_t>());
virtual status_t returnBufferCheckedLocked(
const camera3_stream_buffer &buffer,
nsecs_t timestamp,
bool output,
+ const std::vector<size_t>& surface_ids,
/*out*/
sp<Fence> *releaseFenceOut) = 0;
@@ -82,7 +84,7 @@
virtual size_t getBufferCountLocked();
- virtual size_t getHandoutOutputBufferCountLocked();
+ virtual size_t getHandoutOutputBufferCountLocked() const;
virtual size_t getHandoutInputBufferCountLocked();
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index 017d7be..fc83684 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -98,6 +98,7 @@
const camera3_stream_buffer &buffer,
nsecs_t timestamp,
bool output,
+ const std::vector<size_t>&,
/*out*/
sp<Fence> *releaseFenceOut) {
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h
index 0732464..97a627a 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.h
@@ -62,6 +62,7 @@
const camera3_stream_buffer &buffer,
nsecs_t timestamp,
bool output,
+ const std::vector<size_t>& surface_ids,
/*out*/
sp<Fence> *releaseFenceOut);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index ecbcf76..219cc24 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -187,16 +187,17 @@
}
status_t Camera3OutputStream::queueBufferToConsumer(sp<ANativeWindow>& consumer,
- ANativeWindowBuffer* buffer, int anwReleaseFence) {
+ ANativeWindowBuffer* buffer, int anwReleaseFence,
+ const std::vector<size_t>&) {
return consumer->queueBuffer(consumer.get(), buffer, anwReleaseFence);
}
status_t Camera3OutputStream::returnBufferLocked(
const camera3_stream_buffer &buffer,
- nsecs_t timestamp) {
+ nsecs_t timestamp, const std::vector<size_t>& surface_ids) {
ATRACE_CALL();
- status_t res = returnAnyBufferLocked(buffer, timestamp, /*output*/true);
+ status_t res = returnAnyBufferLocked(buffer, timestamp, /*output*/true, surface_ids);
if (res != OK) {
return res;
@@ -212,6 +213,7 @@
const camera3_stream_buffer &buffer,
nsecs_t timestamp,
bool output,
+ const std::vector<size_t>& surface_ids,
/*out*/
sp<Fence> *releaseFenceOut) {
@@ -281,7 +283,7 @@
return res;
}
- res = queueBufferToConsumer(currentConsumer, anwBuffer, anwReleaseFence);
+ res = queueBufferToConsumer(currentConsumer, anwBuffer, anwReleaseFence, surface_ids);
if (res != OK) {
ALOGE("%s: Stream %d: Error queueing buffer to native window: "
"%s (%d)", __FUNCTION__, mId, strerror(-res), res);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 6f36f92..410905d 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -190,6 +190,9 @@
*/
virtual ssize_t getSurfaceId(const sp<Surface> &/*surface*/) { return 0; }
+ virtual status_t getUniqueSurfaceIds(const std::vector<size_t>&,
+ /*out*/std::vector<size_t>*) { return INVALID_OPERATION; };
+
/**
* Update the stream output surfaces.
*/
@@ -213,6 +216,7 @@
const camera3_stream_buffer &buffer,
nsecs_t timestamp,
bool output,
+ const std::vector<size_t>& surface_ids,
/*out*/
sp<Fence> *releaseFenceOut);
@@ -285,10 +289,11 @@
virtual status_t returnBufferLocked(
const camera3_stream_buffer &buffer,
- nsecs_t timestamp);
+ nsecs_t timestamp, const std::vector<size_t>& surface_ids);
virtual status_t queueBufferToConsumer(sp<ANativeWindow>& consumer,
- ANativeWindowBuffer* buffer, int anwReleaseFence);
+ ANativeWindowBuffer* buffer, int anwReleaseFence,
+ const std::vector<size_t>& surface_ids);
virtual status_t configureQueueLocked();
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
index a711a6d..2bde949 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
@@ -67,6 +67,18 @@
virtual ssize_t getSurfaceId(const sp<Surface> &surface) = 0;
/**
+ * Query the unique surface IDs of current surfaceIds.
+ * When passing unique surface IDs in returnBuffer(), if the
+ * surfaceId has been removed from the stream, the output corresponding to
+ * the unique surface ID will be ignored and not delivered to client.
+ *
+ * Return INVALID_OPERATION if and only if the stream does not support
+ * surface sharing.
+ */
+ virtual status_t getUniqueSurfaceIds(const std::vector<size_t>& surfaceIds,
+ /*out*/std::vector<size_t>* outUniqueIds) = 0;
+
+ /**
* Update the stream output surfaces.
*/
virtual status_t updateStream(const std::vector<sp<Surface>> &outputSurfaces,
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
index fb3ce4c..e3b74d7 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#define LOG_TAG "Camera3-SharedOuStrm"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
#include "Camera3SharedOutputStream.h"
namespace android {
@@ -28,16 +32,17 @@
uint64_t consumerUsage, android_dataspace dataSpace,
camera3_stream_rotation_t rotation,
nsecs_t timestampOffset, const String8& physicalCameraId,
- int setId) :
+ int setId, bool useHalBufManager) :
Camera3OutputStream(id, CAMERA3_STREAM_OUTPUT, width, height,
format, dataSpace, rotation, physicalCameraId,
- consumerUsage, timestampOffset, setId) {
+ consumerUsage, timestampOffset, setId),
+ mUseHalBufManager(useHalBufManager) {
size_t consumerCount = std::min(surfaces.size(), kMaxOutputs);
if (surfaces.size() > consumerCount) {
ALOGE("%s: Trying to add more consumers than the maximum ", __func__);
}
for (size_t i = 0; i < consumerCount; i++) {
- mSurfaces[i] = surfaces[i];
+ mSurfaceUniqueIds[i] = std::make_pair(surfaces[i], mNextUniqueSurfaceId++);
}
}
@@ -48,15 +53,15 @@
status_t Camera3SharedOutputStream::connectStreamSplitterLocked() {
status_t res = OK;
- mStreamSplitter = new Camera3StreamSplitter();
+ mStreamSplitter = new Camera3StreamSplitter(mUseHalBufManager);
uint64_t usage;
getEndpointUsage(&usage);
std::unordered_map<size_t, sp<Surface>> initialSurfaces;
for (size_t i = 0; i < kMaxOutputs; i++) {
- if (mSurfaces[i] != nullptr) {
- initialSurfaces.emplace(i, mSurfaces[i]);
+ if (mSurfaceUniqueIds[i].first != nullptr) {
+ initialSurfaces.emplace(i, mSurfaceUniqueIds[i].first);
}
}
@@ -71,6 +76,31 @@
return res;
}
+status_t Camera3SharedOutputStream::attachBufferToSplitterLocked(
+ ANativeWindowBuffer* anb,
+ const std::vector<size_t>& surface_ids) {
+ status_t res = OK;
+
+ // Attach the buffer to the splitter output queues. This could block if
+ // the output queue doesn't have any empty slot. So unlock during the course
+ // of attachBufferToOutputs.
+ sp<Camera3StreamSplitter> splitter = mStreamSplitter;
+ mLock.unlock();
+ res = splitter->attachBufferToOutputs(anb, surface_ids);
+ mLock.lock();
+ if (res != OK) {
+ ALOGE("%s: Stream %d: Cannot attach stream splitter buffer to outputs: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ // Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is STATE_PREPARING,
+ // let prepareNextBuffer handle the error.)
+ if (res == NO_INIT && mState == STATE_CONFIGURED) {
+ mState = STATE_ABANDONED;
+ }
+ }
+ return res;
+}
+
+
status_t Camera3SharedOutputStream::notifyBufferReleased(ANativeWindowBuffer *anwBuffer) {
Mutex::Autolock l(mLock);
status_t res = OK;
@@ -89,7 +119,7 @@
return true;
}
- return (mSurfaces[surface_id] == nullptr);
+ return (mSurfaceUniqueIds[surface_id].first == nullptr);
}
status_t Camera3SharedOutputStream::setConsumers(const std::vector<sp<Surface>>& surfaces) {
@@ -112,7 +142,7 @@
return NO_MEMORY;
}
- mSurfaces[id] = surface;
+ mSurfaceUniqueIds[id] = std::make_pair(surface, mNextUniqueSurfaceId++);
// Only call addOutput if the splitter has been connected.
if (mStreamSplitter != nullptr) {
@@ -128,7 +158,7 @@
}
status_t Camera3SharedOutputStream::getBufferLocked(camera3_stream_buffer *buffer,
- const std::vector<size_t>& surface_ids) {
+ const std::vector<size_t>& surfaceIds) {
ANativeWindowBuffer* anb;
int fenceFd = -1;
@@ -138,23 +168,11 @@
return res;
}
- // Attach the buffer to the splitter output queues. This could block if
- // the output queue doesn't have any empty slot. So unlock during the course
- // of attachBufferToOutputs.
- sp<Camera3StreamSplitter> splitter = mStreamSplitter;
- mLock.unlock();
- res = splitter->attachBufferToOutputs(anb, surface_ids);
- mLock.lock();
- if (res != OK) {
- ALOGE("%s: Stream %d: Cannot attach stream splitter buffer to outputs: %s (%d)",
- __FUNCTION__, mId, strerror(-res), res);
- // Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is STATE_PREPARING,
- // let prepareNextBuffer handle the error.)
- if (res == NO_INIT && mState == STATE_CONFIGURED) {
- mState = STATE_ABANDONED;
+ if (!mUseHalBufManager) {
+ res = attachBufferToSplitterLocked(anb, surfaceIds);
+ if (res != OK) {
+ return res;
}
-
- return res;
}
/**
@@ -168,8 +186,38 @@
}
status_t Camera3SharedOutputStream::queueBufferToConsumer(sp<ANativeWindow>& consumer,
- ANativeWindowBuffer* buffer, int anwReleaseFence) {
- status_t res = consumer->queueBuffer(consumer.get(), buffer, anwReleaseFence);
+ ANativeWindowBuffer* buffer, int anwReleaseFence,
+ const std::vector<size_t>& uniqueSurfaceIds) {
+ status_t res = OK;
+ if (mUseHalBufManager) {
+ if (uniqueSurfaceIds.size() == 0) {
+ ALOGE("%s: uniqueSurfaceIds must not be empty!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ Mutex::Autolock l(mLock);
+ std::vector<size_t> surfaceIds;
+ for (const auto& uniqueId : uniqueSurfaceIds) {
+ bool uniqueIdFound = false;
+ for (size_t i = 0; i < kMaxOutputs; i++) {
+ if (mSurfaceUniqueIds[i].second == uniqueId) {
+ surfaceIds.push_back(i);
+ uniqueIdFound = true;
+ break;
+ }
+ }
+ if (!uniqueIdFound) {
+ ALOGV("%s: unknown unique surface ID %zu for stream %d: "
+ "output might have been removed.",
+ __FUNCTION__, uniqueId, mId);
+ }
+ }
+ res = attachBufferToSplitterLocked(buffer, surfaceIds);
+ if (res != OK) {
+ return res;
+ }
+ }
+
+ res = consumer->queueBuffer(consumer.get(), buffer, anwReleaseFence);
// After queuing buffer to the internal consumer queue, check whether the buffer is
// successfully queued to the output queues.
@@ -228,8 +276,8 @@
*usage = getPresetConsumerUsage();
for (size_t id = 0; id < kMaxOutputs; id++) {
- if (mSurfaces[id] != nullptr) {
- res = getEndpointUsageForSurface(&u, mSurfaces[id]);
+ if (mSurfaceUniqueIds[id].first != nullptr) {
+ res = getEndpointUsageForSurface(&u, mSurfaceUniqueIds[id].first);
*usage |= u;
}
}
@@ -245,7 +293,7 @@
ssize_t Camera3SharedOutputStream::getNextSurfaceIdLocked() {
ssize_t id = -1;
for (size_t i = 0; i < kMaxOutputs; i++) {
- if (mSurfaces[i] == nullptr) {
+ if (mSurfaceUniqueIds[i].first == nullptr) {
id = i;
break;
}
@@ -258,7 +306,7 @@
Mutex::Autolock l(mLock);
ssize_t id = -1;
for (size_t i = 0; i < kMaxOutputs; i++) {
- if (mSurfaces[i] == surface) {
+ if (mSurfaceUniqueIds[i].first == surface) {
id = i;
break;
}
@@ -267,6 +315,26 @@
return id;
}
+status_t Camera3SharedOutputStream::getUniqueSurfaceIds(
+ const std::vector<size_t>& surfaceIds,
+ /*out*/std::vector<size_t>* outUniqueIds) {
+ Mutex::Autolock l(mLock);
+ if (outUniqueIds == nullptr || surfaceIds.size() > kMaxOutputs) {
+ return BAD_VALUE;
+ }
+
+ outUniqueIds->clear();
+ outUniqueIds->reserve(surfaceIds.size());
+
+ for (const auto& surfaceId : surfaceIds) {
+ if (surfaceId >= kMaxOutputs) {
+ return BAD_VALUE;
+ }
+ outUniqueIds->push_back(mSurfaceUniqueIds[surfaceId].second);
+ }
+ return OK;
+}
+
status_t Camera3SharedOutputStream::revertPartialUpdateLocked(
const KeyedVector<sp<Surface>, size_t> &removedSurfaces,
const KeyedVector<sp<Surface>, size_t> &attachedSurfaces) {
@@ -280,7 +348,7 @@
return UNKNOWN_ERROR;
}
}
- mSurfaces[index] = nullptr;
+ mSurfaceUniqueIds[index] = std::make_pair(nullptr, mNextUniqueSurfaceId++);
}
for (size_t i = 0; i < removedSurfaces.size(); i++) {
@@ -291,7 +359,8 @@
return UNKNOWN_ERROR;
}
}
- mSurfaces[index] = removedSurfaces.keyAt(i);
+ mSurfaceUniqueIds[index] = std::make_pair(
+ removedSurfaces.keyAt(i), mNextUniqueSurfaceId++);
}
return ret;
@@ -343,8 +412,8 @@
}
}
- mSurfaces[it] = nullptr;
- removedSurfaces.add(mSurfaces[it], it);
+ removedSurfaces.add(mSurfaceUniqueIds[it].first, it);
+ mSurfaceUniqueIds[it] = std::make_pair(nullptr, mNextUniqueSurfaceId++);
}
//Next add the new outputs
@@ -369,7 +438,7 @@
return ret;
}
}
- mSurfaces[surfaceId] = it;
+ mSurfaceUniqueIds[surfaceId] = std::make_pair(it, mNextUniqueSurfaceId++);
outputMap->add(it, surfaceId);
}
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
index 02b1c09..b5e37c2 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_SERVERS_CAMERA3_SHARED_OUTPUT_STREAM_H
#define ANDROID_SERVERS_CAMERA3_SHARED_OUTPUT_STREAM_H
+#include <array>
#include "Camera3StreamSplitter.h"
#include "Camera3OutputStream.h"
@@ -37,7 +38,8 @@
uint64_t consumerUsage, android_dataspace dataSpace,
camera3_stream_rotation_t rotation, nsecs_t timestampOffset,
const String8& physicalCameraId,
- int setId = CAMERA3_STREAM_SET_ID_INVALID);
+ int setId = CAMERA3_STREAM_SET_ID_INVALID,
+ bool useHalBufManager = false);
virtual ~Camera3SharedOutputStream();
@@ -49,6 +51,15 @@
virtual ssize_t getSurfaceId(const sp<Surface> &surface);
+ /**
+ * Query the unique surface IDs of current surfaceIds.
+ * When passing unique surface IDs in returnBuffer(), if the
+ * surfaceId has been removed from the stream, the output corresponding to
+ * the unique surface ID will be ignored and not delivered to client.
+ */
+ virtual status_t getUniqueSurfaceIds(const std::vector<size_t>& surfaceIds,
+ /*out*/std::vector<size_t>* outUniqueIds) override;
+
virtual status_t updateStream(const std::vector<sp<Surface>> &outputSurfaces,
const std::vector<OutputStreamInfo> &outputInfo,
const std::vector<size_t> &removedSurfaceIds,
@@ -58,8 +69,17 @@
static const size_t kMaxOutputs = 4;
- // Map surfaceId -> output surfaces
- sp<Surface> mSurfaces[kMaxOutputs];
+ // Whether HAL is in control for buffer management. Surface sharing behavior
+ // depends on this flag.
+ const bool mUseHalBufManager;
+
+ // Pair of an output Surface and its unique ID
+ typedef std::pair<sp<Surface>, size_t> SurfaceUniqueId;
+
+ // Map surfaceId -> (output surface, unique surface ID)
+ std::array<SurfaceUniqueId, kMaxOutputs> mSurfaceUniqueIds;
+
+ size_t mNextUniqueSurfaceId = 0;
ssize_t getNextSurfaceIdLocked();
@@ -78,13 +98,24 @@
status_t connectStreamSplitterLocked();
/**
+ * Attach the output buffer to stream splitter.
+ * When camera service is doing buffer management, this method will be called
+ * before the buffer is handed out to HAL in request thread.
+ * When HAL is doing buffer management, this method will be called when
+ * the buffer is returned from HAL in hwbinder callback thread.
+ */
+ status_t attachBufferToSplitterLocked(ANativeWindowBuffer* anb,
+ const std::vector<size_t>& surface_ids);
+
+ /**
* Internal Camera3Stream interface
*/
virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
const std::vector<size_t>& surface_ids);
virtual status_t queueBufferToConsumer(sp<ANativeWindow>& consumer,
- ANativeWindowBuffer* buffer, int anwReleaseFence);
+ ANativeWindowBuffer* buffer, int anwReleaseFence,
+ const std::vector<size_t>& uniqueSurfaceIds);
virtual status_t configureQueueLocked();
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index ee989e1..24d1c1b 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -655,7 +655,8 @@
}
status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer,
- nsecs_t timestamp, bool timestampIncreasing) {
+ nsecs_t timestamp, bool timestampIncreasing,
+ const std::vector<size_t>& surface_ids) {
ATRACE_CALL();
Mutex::Autolock l(mLock);
@@ -684,7 +685,7 @@
*
* Do this for getBuffer as well.
*/
- status_t res = returnBufferLocked(b, timestamp);
+ status_t res = returnBufferLocked(b, timestamp, surface_ids);
if (res == OK) {
fireBufferListenersLocked(b, /*acquired*/false, /*output*/true);
}
@@ -795,6 +796,12 @@
return hasOutstandingBuffersLocked();
}
+size_t Camera3Stream::getOutstandingBuffersCount() const {
+ ATRACE_CALL();
+ Mutex::Autolock l(mLock);
+ return getHandoutOutputBufferCountLocked();
+}
+
status_t Camera3Stream::setStatusTracker(sp<StatusTracker> statusTracker) {
Mutex::Autolock l(mLock);
sp<StatusTracker> oldTracker = mStatusTracker.promote();
@@ -837,7 +844,7 @@
return INVALID_OPERATION;
}
status_t Camera3Stream::returnBufferLocked(const camera3_stream_buffer &,
- nsecs_t) {
+ nsecs_t, const std::vector<size_t>&) {
ALOGE("%s: This type of stream does not support output", __FUNCTION__);
return INVALID_OPERATION;
}
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 1c67fb2..ddba9f6 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -322,11 +322,17 @@
/**
* Return a buffer to the stream after use by the HAL.
*
+ * Multiple surfaces could share the same HAL stream, but a request may
+ * be only for a subset of surfaces. In this case, the
+ * Camera3StreamInterface object needs the surface ID information to attach
+ * buffers for those surfaces.
+ *
* This method may only be called for buffers provided by getBuffer().
* For bidirectional streams, this method applies to the output-side buffers
*/
status_t returnBuffer(const camera3_stream_buffer &buffer,
- nsecs_t timestamp, bool timestampIncreasing);
+ nsecs_t timestamp, bool timestampIncreasing,
+ const std::vector<size_t>& surface_ids = std::vector<size_t>());
/**
* Fill in the camera3_stream_buffer with the next valid buffer for this
@@ -360,6 +366,11 @@
*/
bool hasOutstandingBuffers() const;
+ /**
+ * Get number of buffers currently handed out to HAL
+ */
+ size_t getOutstandingBuffersCount() const;
+
enum {
TIMEOUT_NEVER = -1
};
@@ -473,7 +484,8 @@
virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
const std::vector<size_t>& surface_ids = std::vector<size_t>());
virtual status_t returnBufferLocked(const camera3_stream_buffer &buffer,
- nsecs_t timestamp);
+ nsecs_t timestamp,
+ const std::vector<size_t>& surface_ids = std::vector<size_t>());
virtual status_t getInputBufferLocked(camera3_stream_buffer *buffer);
virtual status_t returnInputBufferLocked(
const camera3_stream_buffer &buffer);
@@ -495,7 +507,7 @@
virtual size_t getBufferCountLocked() = 0;
// Get handout output buffer count.
- virtual size_t getHandoutOutputBufferCountLocked() = 0;
+ virtual size_t getHandoutOutputBufferCountLocked() const = 0;
// Get handout input buffer count.
virtual size_t getHandoutInputBufferCountLocked() = 0;
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 5758ac8..a84720b 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -248,11 +248,18 @@
/**
* Return a buffer to the stream after use by the HAL.
*
+ * Multiple surfaces could share the same HAL stream, but a request may
+ * be only for a subset of surfaces. In this case, the
+ * Camera3StreamInterface object needs the surface ID information to attach
+ * buffers for those surfaces. For the case of single surface for a HAL
+ * stream, surface_ids parameter has no effect.
+ *
* This method may only be called for buffers provided by getBuffer().
* For bidirectional streams, this method applies to the output-side buffers
*/
virtual status_t returnBuffer(const camera3_stream_buffer &buffer,
- nsecs_t timestamp, bool timestampIncreasing = true) = 0;
+ nsecs_t timestamp, bool timestampIncreasing = true,
+ const std::vector<size_t>& surface_ids = std::vector<size_t>()) = 0;
/**
* Fill in the camera3_stream_buffer with the next valid buffer for this
@@ -289,6 +296,11 @@
*/
virtual bool hasOutstandingBuffers() const = 0;
+ /**
+ * Get number of buffers currently handed out to HAL
+ */
+ virtual size_t getOutstandingBuffersCount() const = 0;
+
enum {
TIMEOUT_NEVER = -1
};
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
index 6d08842..2b5debf 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -152,6 +152,8 @@
SP_LOGV("%s: Disconnected", __FUNCTION__);
}
+Camera3StreamSplitter::Camera3StreamSplitter(bool useHalBufManager) :
+ mUseHalBufManager(useHalBufManager) {}
Camera3StreamSplitter::~Camera3StreamSplitter() {
disconnect();
@@ -241,7 +243,9 @@
uint64_t usage = 0;
res = native_window_get_consumer_usage(static_cast<ANativeWindow*>(outputQueue.get()), &usage);
if (!(usage & (GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE))) {
- outputQueue->setDequeueTimeout(kDequeueBufferTimeout);
+ nsecs_t timeout = mUseHalBufManager ?
+ kHalBufMgrDequeueBufferTimeout : kNormalDequeueBufferTimeout;
+ outputQueue->setDequeueTimeout(timeout);
}
res = gbp->allowAllocation(false);
@@ -436,8 +440,9 @@
res = gbp->attachBuffer(&slot, gb);
mMutex.lock();
if (res != OK) {
- SP_LOGE("%s: Cannot acquireBuffer from GraphicBufferProducer %p: %s (%d)",
+ SP_LOGE("%s: Cannot attachBuffer from GraphicBufferProducer %p: %s (%d)",
__FUNCTION__, gbp.get(), strerror(-res), res);
+ // TODO: might need to detach/cleanup the already attached buffers before return?
return res;
}
if ((slot < 0) || (slot > BufferQueue::NUM_BUFFER_SLOTS)) {
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
index 1eaf2bd..960f7aa 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
@@ -49,7 +49,7 @@
public:
// Constructor
- Camera3StreamSplitter() = default;
+ Camera3StreamSplitter(bool useHalBufManager = false);
// Connect to the stream splitter by creating buffer queue and connecting it
// with output surfaces.
@@ -226,7 +226,10 @@
android::PixelFormat mFormat = android::PIXEL_FORMAT_NONE;
uint64_t mProducerUsage = 0;
- static const nsecs_t kDequeueBufferTimeout = s2ns(1); // 1 sec
+ // The attachBuffer call will happen on different thread according to mUseHalBufManager and have
+ // different timing constraint.
+ static const nsecs_t kNormalDequeueBufferTimeout = s2ns(1); // 1 sec
+ static const nsecs_t kHalBufMgrDequeueBufferTimeout = ms2ns(1); // 1 msec
Mutex mMutex;
@@ -273,6 +276,8 @@
size_t mAcquiredInputBuffers;
String8 mConsumerName;
+
+ const bool mUseHalBufManager;
};
} // namespace android
diff --git a/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.cpp b/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.cpp
new file mode 100644
index 0000000..e5e5024
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <hardware/camera.h>
+
+#include <hidl/AidlCameraDeviceCallbacks.h>
+#include <hidl/Convert.h>
+
+namespace android {
+namespace frameworks {
+namespace cameraservice {
+namespace device {
+namespace V2_0 {
+namespace implementation {
+
+using hardware::hidl_vec;
+using HCaptureResultExtras = android::frameworks::cameraservice::device::V2_0::CaptureResultExtras;
+using HPhysicalCaptureResultInfo = android::frameworks::cameraservice::device::V2_0::PhysicalCaptureResultInfo;
+using HCameraMetadata = android::frameworks::cameraservice::device::V2_0::CameraMetadata;
+
+const char *H2BCameraDeviceCallbacks::kResultKey = "CaptureResult";
+
+H2BCameraDeviceCallbacks::H2BCameraDeviceCallbacks(const sp<HalInterface>& base) : CBase(base) { }
+
+bool H2BCameraDeviceCallbacks::initializeLooper() {
+ mCbLooper = new ALooper;
+ mCbLooper->setName("cs-looper");
+ status_t err = mCbLooper->start(/*runOnCallingThread*/ false, /*canCallJava*/ false,
+ PRIORITY_DEFAULT);
+ if (err !=OK) {
+ ALOGE("Unable to start camera device callback looper");
+ return false;
+ }
+ mHandler = new CallbackHandler(this);
+ mCbLooper->registerHandler(mHandler);
+ return true;
+}
+
+H2BCameraDeviceCallbacks::~H2BCameraDeviceCallbacks() {
+ if (mCbLooper != nullptr) {
+ if (mHandler != nullptr) {
+ mCbLooper->unregisterHandler(mHandler->id());
+ }
+ mCbLooper->stop();
+ }
+ mCbLooper.clear();
+ mHandler.clear();
+}
+
+binder::Status H2BCameraDeviceCallbacks::onDeviceError(
+ int32_t errorCode, const CaptureResultExtras& resultExtras) {
+ using hardware::cameraservice::utils::conversion::convertToHidl;
+ HCaptureResultExtras hCaptureResultExtras = convertToHidl(resultExtras);
+ auto ret = mBase->onDeviceError(convertToHidl(errorCode), hCaptureResultExtras);
+ if (!ret.isOk()) {
+ ALOGE("%s OnDeviceError callback failed due to %s",__FUNCTION__,
+ ret.description().c_str());
+ }
+ return binder::Status::ok();
+}
+
+binder::Status H2BCameraDeviceCallbacks::onDeviceIdle() {
+ auto ret = mBase->onDeviceIdle();
+ if (!ret.isOk()) {
+ ALOGE("%s OnDeviceIdle callback failed due to %s",__FUNCTION__,
+ ret.description().c_str());
+ }
+ return binder::Status::ok();
+}
+
+binder::Status H2BCameraDeviceCallbacks::onCaptureStarted(
+ const CaptureResultExtras& resultExtras, int64_t timestamp) {
+ using hardware::cameraservice::utils::conversion::convertToHidl;
+ HCaptureResultExtras hCaptureResultExtras = convertToHidl(resultExtras);
+ auto ret = mBase->onCaptureStarted(hCaptureResultExtras, timestamp);
+ if (!ret.isOk()) {
+ ALOGE("%s OnCaptureCallback failed due to %s",__FUNCTION__,
+ ret.description().c_str());
+ }
+ return binder::Status::ok();
+}
+
+void H2BCameraDeviceCallbacks::convertResultMetadataToHidl(const camera_metadata_t *rawMetadata,
+ FmqSizeOrMetadata *hResultMetadata) {
+ // First try writing to fmq.
+ size_t metadata_size = get_camera_metadata_size(rawMetadata);
+ if ((metadata_size > 0) &&
+ (mCaptureResultMetadataQueue->availableToWrite() > 0)) {
+ if (mCaptureResultMetadataQueue->write((uint8_t *)rawMetadata, metadata_size)) {
+ hResultMetadata->fmqMetadataSize(metadata_size);
+ } else {
+ ALOGW("%s Couldn't use fmq, falling back to hwbinder", __FUNCTION__);
+ HCameraMetadata metadata;
+ hardware::cameraservice::utils::conversion::convertToHidl(rawMetadata, &metadata);
+ hResultMetadata->metadata(std::move(metadata));
+ }
+ }
+}
+
+void H2BCameraDeviceCallbacks::CallbackHandler::onMessageReceived(const sp<AMessage> &msg) {
+ sp<RefBase> obj = nullptr;
+ sp<ResultWrapper> resultWrapper = nullptr;
+ bool found = false;
+ switch (msg->what()) {
+ case kWhatResultReceived:
+ found = msg->findObject(kResultKey, &obj);
+ if (!found || obj == nullptr) {
+ ALOGE("Cannot find result object in callback message");
+ return;
+ }
+ resultWrapper = static_cast<ResultWrapper *>(obj.get());
+ processResultMessage(resultWrapper);
+ break;
+ default:
+ ALOGE("Unknown callback sent");
+ break;
+ }
+ return;
+}
+
+void H2BCameraDeviceCallbacks::CallbackHandler::processResultMessage(
+ sp<ResultWrapper> &resultWrapper) {
+ sp<H2BCameraDeviceCallbacks> converter = mConverter.promote();
+ if (converter == nullptr) {
+ ALOGE("Callback wrapper has died, result callback cannot be made");
+ return;
+ }
+ CameraMetadataNative &result = resultWrapper->mResult;
+ auto resultExtras = resultWrapper->mResultExtras;
+ auto &physicalCaptureResultInfos = resultWrapper->mPhysicalCaptureResultInfos;
+ HCaptureResultExtras hResultExtras =
+ hardware::cameraservice::utils::conversion::convertToHidl(resultExtras);
+ hidl_vec<HPhysicalCaptureResultInfo> hPhysicalCaptureResultInfos =
+ hardware::cameraservice::utils::conversion::convertToHidl(
+ physicalCaptureResultInfos, converter->mCaptureResultMetadataQueue);
+
+ // Convert Metadata into HCameraMetadata;
+ FmqSizeOrMetadata hResult;
+ const camera_metadata_t *rawMetadata = result.getAndLock();
+ converter->convertResultMetadataToHidl(rawMetadata, &hResult);
+ result.unlock(rawMetadata);
+ auto ret = converter->mBase->onResultReceived(hResult, hResultExtras,
+ hPhysicalCaptureResultInfos);
+ if (!ret.isOk()) {
+ ALOGE("%s OnResultReceived callback failed due to %s",__FUNCTION__,
+ ret.description().c_str());
+ }
+}
+
+binder::Status H2BCameraDeviceCallbacks::onResultReceived(
+ const CameraMetadataNative& result,
+ const CaptureResultExtras& resultExtras,
+ const ::std::vector<PhysicalCaptureResultInfo>& physicalCaptureResultInfos) {
+ // Wrap CameraMetadata, resultExtras and physicalCaptureResultInfos in on
+ // sp<RefBase>-able structure and post it.
+ sp<ResultWrapper> resultWrapper = new ResultWrapper(const_cast<CameraMetadataNative &>(result),
+ resultExtras, physicalCaptureResultInfos);
+ sp<AMessage> msg = new AMessage(kWhatResultReceived, mHandler);
+ msg->setObject(kResultKey, resultWrapper);
+ msg->post();
+ return binder::Status::ok();
+}
+
+binder::Status H2BCameraDeviceCallbacks::onPrepared(int32_t streamId) {
+ // not implemented
+ // To silence Wunused-parameter.
+ (void) streamId;
+ return binder::Status::ok();
+}
+
+binder::Status H2BCameraDeviceCallbacks::onRepeatingRequestError(
+ int64_t lastFrameNumber,
+ int32_t repeatingRequestId) {
+ auto ret =
+ mBase->onRepeatingRequestError(lastFrameNumber, repeatingRequestId);
+ if (!ret.isOk()) {
+ ALOGE("%s OnRepeatingRequestEror callback failed due to %s",__FUNCTION__,
+ ret.description().c_str());
+ }
+ return binder::Status::ok();
+}
+
+binder::Status H2BCameraDeviceCallbacks::onRequestQueueEmpty() {
+ // not implemented
+ return binder::Status::ok();
+}
+
+} // implementation
+} // V2_0
+} // device
+} // cameraservice
+} // frameworks
+} // android
diff --git a/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.h b/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.h
new file mode 100644
index 0000000..dbf520a
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_AIDL_CAMERADEVICECALLBACKS_H
+#define ANDROID_FRAMEWORKS_AIDL_CAMERADEVICECALLBACKS_H
+
+#include <mutex>
+#include <thread>
+
+#include <android/frameworks/cameraservice/common/2.0/types.h>
+#include <android/frameworks/cameraservice/service/2.0/types.h>
+#include <android/frameworks/cameraservice/device/2.0/ICameraDeviceCallback.h>
+#include <android/frameworks/cameraservice/device/2.0/types.h>
+#include <android/hardware/camera2/BnCameraDeviceCallbacks.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <CameraService.h>
+#include <hidl/CameraHybridInterface.h>
+
+namespace android {
+namespace frameworks {
+namespace cameraservice {
+namespace device {
+namespace V2_0 {
+namespace implementation {
+
+using camerahybrid::H2BConverter;
+using HCameraDeviceCallback = cameraservice::device::V2_0::ICameraDeviceCallback;
+using HPhysicalCaptureResultInfo = cameraservice::device::V2_0::PhysicalCaptureResultInfo;
+using android::frameworks::cameraservice::device::V2_0::FmqSizeOrMetadata;
+
+using hardware::camera2::BnCameraDeviceCallbacks;
+using hardware::camera2::ICameraDeviceCallbacks;
+using hardware::camera2::impl::CaptureResultExtras;
+using hardware::camera2::impl::CameraMetadataNative;
+using hardware::camera2::impl::PhysicalCaptureResultInfo;
+using hardware::kSynchronizedReadWrite;
+using hardware::MessageQueue;
+using CaptureResultMetadataQueue = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+struct H2BCameraDeviceCallbacks :
+ public H2BConverter<HCameraDeviceCallback, ICameraDeviceCallbacks, BnCameraDeviceCallbacks> {
+ H2BCameraDeviceCallbacks(const sp<HalInterface>& base);
+
+ ~H2BCameraDeviceCallbacks();
+
+ bool initializeLooper();
+
+ virtual binder::Status onDeviceError(int32_t errorCode,
+ const CaptureResultExtras& resultExtras) override;
+
+ virtual binder::Status onDeviceIdle() override;
+
+ virtual binder::Status onCaptureStarted(const CaptureResultExtras& resultExtras,
+ int64_t timestamp) override;
+
+ virtual binder::Status onResultReceived(
+ const CameraMetadataNative& result, const CaptureResultExtras& resultExtras,
+ const std::vector<PhysicalCaptureResultInfo>& physicalCaptureResultInfos) override;
+
+ virtual binder::Status onPrepared(int32_t streamId) override;
+
+ virtual binder::Status onRepeatingRequestError(int64_t lastFrameNumber,
+ int32_t repeatingRequestId) override;
+
+ virtual binder::Status onRequestQueueEmpty() override;
+
+ void setCaptureResultMetadataQueue(std::shared_ptr<CaptureResultMetadataQueue> metadataQueue) {
+ mCaptureResultMetadataQueue = metadataQueue;
+ }
+
+ private:
+ // Wrapper struct so that parameters to onResultReceived callback may be
+ // sent through an AMessage.
+ struct ResultWrapper : public RefBase {
+ CameraMetadataNative mResult;
+ CaptureResultExtras mResultExtras;
+ std::vector<PhysicalCaptureResultInfo> mPhysicalCaptureResultInfos;
+ ResultWrapper(CameraMetadataNative &result,
+ const CaptureResultExtras resultExtras,
+ const std::vector<PhysicalCaptureResultInfo> &physicalCaptureResultInfos) :
+ // TODO: make this std::movable
+ mResult(result), mResultExtras(resultExtras), mPhysicalCaptureResultInfos(physicalCaptureResultInfos) { }
+ };
+
+ struct CallbackHandler : public AHandler {
+ public:
+ void onMessageReceived(const sp<AMessage> &msg) override;
+ CallbackHandler(H2BCameraDeviceCallbacks *converter) : mConverter(converter) { }
+ private:
+ void processResultMessage(sp<ResultWrapper> &resultWrapper);
+ wp<H2BCameraDeviceCallbacks> mConverter = nullptr;
+ Mutex mMetadataQueueLock;
+ };
+
+ void convertResultMetadataToHidl(const camera_metadata *rawMetadata,
+ FmqSizeOrMetadata *resultMetadata);
+ enum {
+ kWhatResultReceived,
+ };
+
+ static const char *kResultKey;
+
+ std::shared_ptr<CaptureResultMetadataQueue> mCaptureResultMetadataQueue = nullptr;
+ sp<CallbackHandler> mHandler = nullptr;
+ sp<ALooper> mCbLooper = nullptr;
+};
+
+} // implementation
+} // V2_0
+} // device
+} // cameraservice
+} // frameworks
+} // android
+#endif // ANDROID_FRAMEWORKS_AIDL_CAMERADEVICECALLBACKS_H
diff --git a/services/camera/libcameraservice/hidl/AidlCameraServiceListener.cpp b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.cpp
new file mode 100644
index 0000000..110ef8e
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hidl/AidlCameraServiceListener.h>
+#include <hidl/Convert.h>
+
+namespace android {
+namespace frameworks {
+namespace cameraservice {
+namespace service {
+namespace V2_0 {
+namespace implementation {
+
+using hardware::cameraservice::utils::conversion::convertToHidlCameraDeviceStatus;
+
+binder::Status H2BCameraServiceListener::onStatusChanged(
+ int32_t status, const ::android::String16& cameraId) {
+ HCameraDeviceStatus hCameraDeviceStatus = convertToHidlCameraDeviceStatus(status);
+ CameraStatusAndId cameraStatusAndId;
+ cameraStatusAndId.deviceStatus = hCameraDeviceStatus;
+ cameraStatusAndId.cameraId = String8(cameraId).string();
+ auto ret = mBase->onStatusChanged(cameraStatusAndId);
+ if (!ret.isOk()) {
+ ALOGE("%s OnStatusChanged callback failed due to %s",__FUNCTION__,
+ ret.description().c_str());
+ }
+ return binder::Status::ok();
+}
+
+::android::binder::Status H2BCameraServiceListener::onTorchStatusChanged(
+ int32_t, const ::android::String16&) {
+ // We don't implement onTorchStatusChanged
+ return binder::Status::ok();
+}
+
+} // implementation
+} // V2_0
+} // common
+} // cameraservice
+} // frameworks
+} // android
diff --git a/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h
new file mode 100644
index 0000000..ca9143d
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <mutex>
+#include <thread>
+
+#include <android/frameworks/cameraservice/common/2.0/types.h>
+#include <android/frameworks/cameraservice/service/2.0/ICameraServiceListener.h>
+#include <android/frameworks/cameraservice/device/2.0/types.h>
+#include <android/hardware/BnCameraServiceListener.h>
+#include <android/hardware/BpCameraServiceListener.h>
+
+#include <hidl/Status.h>
+#include <hidl/CameraHybridInterface.h>
+
+namespace android {
+namespace frameworks {
+namespace cameraservice {
+namespace service {
+namespace V2_0 {
+namespace implementation {
+
+using hardware::BnCameraServiceListener;
+using hardware::BpCameraServiceListener;
+using camerahybrid::H2BConverter;
+using HCameraDeviceStatus = frameworks::cameraservice::service::V2_0::CameraDeviceStatus;
+typedef frameworks::cameraservice::service::V2_0::ICameraServiceListener HCameraServiceListener;
+
+struct H2BCameraServiceListener :
+ public H2BConverter<HCameraServiceListener, ICameraServiceListener, BnCameraServiceListener> {
+ H2BCameraServiceListener(const sp<HalInterface>& base) : CBase(base) { }
+
+ ~H2BCameraServiceListener() { }
+
+ virtual ::android::binder::Status onStatusChanged(int32_t status,
+ const ::android::String16& cameraId) override;
+
+ virtual ::android::binder::Status onTorchStatusChanged(
+ int32_t status, const ::android::String16& cameraId) override;
+};
+
+} // implementation
+} // V2_0
+} // service
+} // cameraservice
+} // frameworks
+} // android
diff --git a/services/camera/libcameraservice/hidl/CameraHybridInterface.h b/services/camera/libcameraservice/hidl/CameraHybridInterface.h
new file mode 100644
index 0000000..baf0112
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/CameraHybridInterface.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CAMERA_HYBRIDINTERFACE_H
+#define ANDROID_CAMERA_HYBRIDINTERFACE_H
+
+#include <vector>
+#include <mutex>
+
+#include <binder/Parcel.h>
+#include <hidl/HidlSupport.h>
+
+namespace android {
+namespace camerahybrid {
+typedef ::android::hidl::base::V1_0::IBase HInterface;
+
+template <
+ typename HINTERFACE,
+ typename INTERFACE,
+ typename BNINTERFACE >
+class H2BConverter : public BNINTERFACE {
+public:
+ typedef H2BConverter<HINTERFACE, INTERFACE, BNINTERFACE> CBase; // Converter Base
+ typedef INTERFACE BaseInterface;
+ typedef HINTERFACE HalInterface;
+
+ H2BConverter(const sp<HalInterface>& base) : mBase(base) {}
+ virtual sp<HalInterface> getHalInterface() { return mBase; }
+ virtual status_t linkToDeath(
+ const sp<IBinder::DeathRecipient>& recipient,
+ void* cookie = nullptr,
+ uint32_t flags = 0);
+ virtual status_t unlinkToDeath(
+ const wp<IBinder::DeathRecipient>& recipient,
+ void* cookie = nullptr,
+ uint32_t flags = 0,
+ wp<IBinder::DeathRecipient>* outRecipient = nullptr);
+
+protected:
+ sp<HalInterface> mBase;
+ struct Obituary : public hardware::hidl_death_recipient {
+ wp<IBinder::DeathRecipient> recipient;
+ void* cookie;
+ uint32_t flags;
+ wp<IBinder> who;
+ Obituary(
+ const wp<IBinder::DeathRecipient>& r,
+ void* c, uint32_t f,
+ const wp<IBinder>& w) :
+ recipient(r), cookie(c), flags(f), who(w) {
+ }
+ Obituary(const Obituary& o) :
+ recipient(o.recipient),
+ cookie(o.cookie),
+ flags(o.flags),
+ who(o.who) {
+ }
+ Obituary& operator=(const Obituary& o) {
+ recipient = o.recipient;
+ cookie = o.cookie;
+ flags = o.flags;
+ who = o.who;
+ return *this;
+ }
+ void serviceDied(uint64_t, const wp<HInterface>&) override {
+ sp<IBinder::DeathRecipient> dr = recipient.promote();
+ if (dr != nullptr) {
+ dr->binderDied(who);
+ }
+ }
+ };
+ std::mutex mObituariesLock;
+ std::vector<sp<Obituary> > mObituaries;
+};
+
+template <
+ typename HINTERFACE,
+ typename INTERFACE,
+ typename BNINTERFACE>
+status_t H2BConverter<HINTERFACE, INTERFACE, BNINTERFACE>::
+ linkToDeath(
+ const sp<IBinder::DeathRecipient>& recipient,
+ void* cookie, uint32_t flags) {
+ LOG_ALWAYS_FATAL_IF(recipient == nullptr,
+ "linkToDeath(): recipient must be non-nullptr");
+ {
+ std::lock_guard<std::mutex> lock(mObituariesLock);
+ mObituaries.push_back(new Obituary(recipient, cookie, flags, this));
+ if (!mBase->linkToDeath(mObituaries.back(), 0)) {
+ return DEAD_OBJECT;
+ }
+ }
+ return NO_ERROR;
+}
+
+template <
+ typename HINTERFACE,
+ typename INTERFACE,
+ typename BNINTERFACE>
+status_t H2BConverter<HINTERFACE, INTERFACE, BNINTERFACE>::
+ unlinkToDeath(
+ const wp<IBinder::DeathRecipient>& recipient,
+ void* cookie, uint32_t flags,
+ wp<IBinder::DeathRecipient>* outRecipient) {
+ std::lock_guard<std::mutex> lock(mObituariesLock);
+ for (auto i = mObituaries.begin(); i != mObituaries.end(); ++i) {
+ if ((flags = (*i)->flags) && (
+ (recipient == (*i)->recipient) ||
+ ((recipient == nullptr) && (cookie == (*i)->cookie)))) {
+ if (outRecipient != nullptr) {
+ *outRecipient = (*i)->recipient;
+ }
+ bool success = mBase->unlinkToDeath(*i);
+ mObituaries.erase(i);
+ return success ? NO_ERROR : DEAD_OBJECT;
+ }
+ }
+ return NAME_NOT_FOUND;
+}
+
+} // namespace camerahybrid
+} // namespace android
+
+#endif // ANDROID_CAMERA_HYBRIDINTERFACE_H
+
diff --git a/services/camera/libcameraservice/hidl/Convert.cpp b/services/camera/libcameraservice/hidl/Convert.cpp
new file mode 100644
index 0000000..22e879e
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/Convert.cpp
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hidl/Convert.h>
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+#include <NdkImageReaderPriv.h>
+
+namespace android {
+namespace hardware {
+namespace cameraservice {
+namespace utils {
+namespace conversion {
+
+using hardware::graphics::bufferqueue::V1_0::utils::H2BGraphicBufferProducer;
+
+// Note: existing data in dst will be gone. Caller still owns the memory of src
+void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst) {
+ if (src == nullptr) {
+ ALOGW("%s:attempt to convert empty metadata to Hidl", __FUNCTION__);
+ return;
+ }
+ size_t size = get_camera_metadata_size(src);
+ dst->setToExternal((uint8_t *) src, size);
+ return;
+}
+
+int32_t convertFromHidl(HStreamConfigurationMode streamConfigurationMode) {
+ switch (streamConfigurationMode) {
+ case HStreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE:
+ return camera2::ICameraDeviceUser::CONSTRAINED_HIGH_SPEED_MODE;
+ case HStreamConfigurationMode::NORMAL_MODE:
+ return camera2::ICameraDeviceUser::NORMAL_MODE;
+ default:
+ // TODO: Fix this
+ return camera2::ICameraDeviceUser::VENDOR_MODE_START;
+ }
+}
+
+int32_t convertFromHidl(HTemplateId templateId) {
+ switch(templateId) {
+ case HTemplateId::PREVIEW:
+ return camera2::ICameraDeviceUser::TEMPLATE_PREVIEW;
+ case HTemplateId::STILL_CAPTURE:
+ return camera2::ICameraDeviceUser::TEMPLATE_STILL_CAPTURE;
+ case HTemplateId::RECORD:
+ return camera2::ICameraDeviceUser::TEMPLATE_RECORD;
+ case HTemplateId::VIDEO_SNAPSHOT:
+ return camera2::ICameraDeviceUser::TEMPLATE_VIDEO_SNAPSHOT;
+ case HTemplateId::ZERO_SHUTTER_LAG:
+ return camera2::ICameraDeviceUser::TEMPLATE_ZERO_SHUTTER_LAG;
+ case HTemplateId::MANUAL:
+ return camera2::ICameraDeviceUser::TEMPLATE_MANUAL;
+ }
+}
+
+int convertFromHidl(HOutputConfiguration::Rotation rotation) {
+ switch(rotation) {
+ case HOutputConfiguration::Rotation::R0:
+ return 0;
+ case HOutputConfiguration::Rotation::R90:
+ return 1;
+ case HOutputConfiguration::Rotation::R180:
+ return 2;
+ case HOutputConfiguration::Rotation::R270:
+ return 3;
+ }
+}
+
+hardware::camera2::params::OutputConfiguration convertFromHidl(
+ const HOutputConfiguration &hOutputConfiguration) {
+ std::vector<sp<IGraphicBufferProducer>> iGBPs;
+ auto &windowHandles = hOutputConfiguration.windowHandles;
+ iGBPs.reserve(windowHandles.size());
+ for (auto &handle : windowHandles) {
+ iGBPs.push_back(new H2BGraphicBufferProducer(AImageReader_getHGBPFromHandle(handle)));
+ }
+ hardware::camera2::params::OutputConfiguration outputConfiguration(
+ iGBPs, convertFromHidl(hOutputConfiguration.rotation),
+ hOutputConfiguration.windowGroupId, OutputConfiguration::SURFACE_TYPE_UNKNOWN, 0, 0,
+ (windowHandles.size() > 1));
+ return outputConfiguration;
+}
+
+// The camera metadata here is cloned. Since we're reading metadata over
+// hwbinder we would need to clone it in order to avoid aligment issues.
+bool convertFromHidl(const HCameraMetadata &src, CameraMetadata *dst) {
+ const camera_metadata_t *buffer = reinterpret_cast<const camera_metadata_t*>(src.data());
+ size_t expectedSize = src.size();
+ int res = validate_camera_metadata_structure(buffer, &expectedSize);
+ if (res == OK || res == CAMERA_METADATA_VALIDATION_SHIFTED) {
+ *dst = buffer;
+ } else {
+ ALOGE("%s: Malformed camera metadata received from HAL", __FUNCTION__);
+ return false;
+ }
+ return true;
+}
+
+HCameraDeviceStatus convertToHidlCameraDeviceStatus(int32_t status) {
+ HCameraDeviceStatus deviceStatus = HCameraDeviceStatus::STATUS_UNKNOWN;
+ switch(status) {
+ case hardware::ICameraServiceListener::STATUS_NOT_PRESENT:
+ deviceStatus = HCameraDeviceStatus::STATUS_NOT_PRESENT;
+ break;
+ case hardware::ICameraServiceListener::STATUS_PRESENT:
+ deviceStatus = HCameraDeviceStatus::STATUS_PRESENT;
+ break;
+ case hardware::ICameraServiceListener::STATUS_ENUMERATING:
+ deviceStatus = HCameraDeviceStatus::STATUS_ENUMERATING;
+ break;
+ case hardware::ICameraServiceListener::STATUS_NOT_AVAILABLE:
+ deviceStatus = HCameraDeviceStatus::STATUS_NOT_AVAILABLE;
+ break;
+ default:
+ break;
+ }
+ return deviceStatus;
+}
+
+HCaptureResultExtras convertToHidl(const CaptureResultExtras &captureResultExtras) {
+ HCaptureResultExtras hCaptureResultExtras;
+ hCaptureResultExtras.burstId = captureResultExtras.burstId;
+ hCaptureResultExtras.frameNumber = captureResultExtras.frameNumber;
+ hCaptureResultExtras.partialResultCount = captureResultExtras.partialResultCount;
+ hCaptureResultExtras.errorStreamId = captureResultExtras.errorStreamId;
+ return hCaptureResultExtras;
+}
+
+HErrorCode convertToHidl(int32_t errorCode) {
+ switch(errorCode) {
+ case camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED:
+ return HErrorCode::CAMERA_DISCONNECTED;
+ case camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE :
+ return HErrorCode::CAMERA_DEVICE;
+ case camera2::ICameraDeviceCallbacks::ERROR_CAMERA_SERVICE:
+ return HErrorCode::CAMERA_SERVICE;
+ case camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST:
+ return HErrorCode::CAMERA_REQUEST;
+ case camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT:
+ return HErrorCode::CAMERA_RESULT;
+ case camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER:
+ return HErrorCode::CAMERA_BUFFER;
+ case camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DISABLED:
+ return HErrorCode::CAMERA_DISABLED;
+ case camera2::ICameraDeviceCallbacks::ERROR_CAMERA_INVALID_ERROR:
+ return HErrorCode::CAMERA_INVALID_ERROR;
+ default:
+ return HErrorCode::CAMERA_UNKNOWN_ERROR;
+ }
+}
+
+void convertToHidl(const std::vector<hardware::CameraStatus> &src,
+ hidl_vec<HCameraStatusAndId>* dst) {
+ dst->resize(src.size());
+ size_t i = 0;
+ for (auto &statusAndId : src) {
+ auto &a = (*dst)[i++];
+ a.cameraId = statusAndId.cameraId.c_str();
+ a.deviceStatus = convertToHidlCameraDeviceStatus(statusAndId.status);
+ }
+ return;
+}
+
+void convertToHidl(
+ const hardware::camera2::utils::SubmitInfo &submitInfo,
+ frameworks::cameraservice::device::V2_0::SubmitInfo *hSubmitInfo) {
+ hSubmitInfo->requestId = submitInfo.mRequestId;
+ hSubmitInfo->lastFrameNumber = submitInfo.mLastFrameNumber;
+}
+
+HStatus B2HStatus(const binder::Status &bStatus) {
+ HStatus status = HStatus::NO_ERROR;
+ if (bStatus.isOk()) {
+ // NO Error here
+ return status;
+ }
+ switch(bStatus.serviceSpecificErrorCode()) {
+ case hardware::ICameraService::ERROR_DISCONNECTED:
+ status = HStatus::DISCONNECTED;
+ break;
+ case hardware::ICameraService::ERROR_CAMERA_IN_USE:
+ status = HStatus::CAMERA_IN_USE;
+ break;
+ case hardware::ICameraService::ERROR_MAX_CAMERAS_IN_USE:
+ status = HStatus::MAX_CAMERAS_IN_USE;
+ break;
+ case hardware::ICameraService::ERROR_ILLEGAL_ARGUMENT:
+ status = HStatus::ILLEGAL_ARGUMENT;
+ break;
+ case hardware::ICameraService::ERROR_DEPRECATED_HAL:
+ // Should not reach here since we filtered legacy HALs earlier
+ status = HStatus::DEPRECATED_HAL;
+ break;
+ case hardware::ICameraService::ERROR_DISABLED:
+ status = HStatus::DISABLED;
+ break;
+ case hardware::ICameraService::ERROR_PERMISSION_DENIED:
+ status = HStatus::PERMISSION_DENIED;
+ break;
+ case hardware::ICameraService::ERROR_INVALID_OPERATION:
+ status = HStatus::INVALID_OPERATION;
+ break;
+ default:
+ status = HStatus::UNKNOWN_ERROR;
+ break;
+ }
+ return status;
+}
+
+HPhysicalCaptureResultInfo convertToHidl(
+ const PhysicalCaptureResultInfo &physicalCaptureResultInfo,
+ std::shared_ptr<CaptureResultMetadataQueue> &captureResultMetadataQueue) {
+ HPhysicalCaptureResultInfo hPhysicalCaptureResultInfo;
+ hPhysicalCaptureResultInfo.physicalCameraId =
+ String8(physicalCaptureResultInfo.mPhysicalCameraId).string();
+ const camera_metadata_t *rawMetadata =
+ physicalCaptureResultInfo.mPhysicalCameraMetadata.getAndLock();
+ // Try using fmq at first.
+ size_t metadata_size = get_camera_metadata_size(rawMetadata);
+ if ((metadata_size > 0) && (captureResultMetadataQueue->availableToWrite() > 0)) {
+ if (captureResultMetadataQueue->write((uint8_t *)rawMetadata, metadata_size)) {
+ hPhysicalCaptureResultInfo.physicalCameraMetadata.fmqMetadataSize(metadata_size);
+ } else {
+ ALOGW("%s Couldn't use fmq, falling back to hwbinder", __FUNCTION__);
+ HCameraMetadata metadata;
+ convertToHidl(rawMetadata, &metadata);
+ hPhysicalCaptureResultInfo.physicalCameraMetadata.metadata(std::move(metadata));
+ }
+ }
+ physicalCaptureResultInfo.mPhysicalCameraMetadata.unlock(rawMetadata);
+ return hPhysicalCaptureResultInfo;
+}
+
+hidl_vec<HPhysicalCaptureResultInfo> convertToHidl(
+ const std::vector<PhysicalCaptureResultInfo> &physicalCaptureResultInfos,
+ std::shared_ptr<CaptureResultMetadataQueue> &captureResultMetadataQueue) {
+ hidl_vec<HPhysicalCaptureResultInfo> hPhysicalCaptureResultInfos;
+ hPhysicalCaptureResultInfos.resize(physicalCaptureResultInfos.size());
+ size_t i = 0;
+ for (auto &physicalCaptureResultInfo : physicalCaptureResultInfos) {
+ hPhysicalCaptureResultInfos[i++] = convertToHidl(physicalCaptureResultInfo,
+ captureResultMetadataQueue);
+ }
+ return hPhysicalCaptureResultInfos;
+}
+
+} //conversion
+} // utils
+} //cameraservice
+} // hardware
+} // android
diff --git a/services/camera/libcameraservice/hidl/Convert.h b/services/camera/libcameraservice/hidl/Convert.h
new file mode 100644
index 0000000..82937a3
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/Convert.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CAMERASERVER_CONVERT_HIDL
+#define CAMERASERVER_CONVERT_HIDL
+
+#include <vector>
+
+#include <android/frameworks/cameraservice/service/2.0/ICameraService.h>
+#include <android/frameworks/cameraservice/device/2.0/ICameraDeviceUser.h>
+#include <android/frameworks/cameraservice/common/2.0/types.h>
+#include <android/frameworks/cameraservice/service/2.0/types.h>
+#include <android/frameworks/cameraservice/device/2.0/types.h>
+#include <android/hardware/camera/common/1.0/types.h>
+#include <android/hardware/camera2/ICameraDeviceUser.h>
+#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
+#include <android/hardware/ICameraService.h>
+#include <fmq/MessageQueue.h>
+#include <hardware/camera.h>
+#include <hidl/MQDescriptor.h>
+
+namespace android {
+namespace hardware {
+namespace cameraservice {
+namespace utils {
+namespace conversion {
+
+using hardware::camera2::impl::CaptureResultExtras;
+using hardware::camera2::impl::PhysicalCaptureResultInfo;
+
+using CaptureResultMetadataQueue = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using HCameraMetadata = frameworks::cameraservice::service::V2_0::CameraMetadata;
+using HCameraDeviceStatus = frameworks::cameraservice::service::V2_0::CameraDeviceStatus;
+using HCameraStatusAndId = frameworks::cameraservice::service::V2_0::CameraStatusAndId;
+using HCameraDeviceUser = frameworks::cameraservice::device::V2_0::ICameraDeviceUser;
+using HCaptureResultExtras = frameworks::cameraservice::device::V2_0::CaptureResultExtras;
+using HCaptureRequest = frameworks::cameraservice::device::V2_0::CaptureRequest;
+using HErrorCode = frameworks::cameraservice::device::V2_0::ErrorCode;
+using HGraphicBufferProducer = hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer;
+using HOutputConfiguration = frameworks::cameraservice::device::V2_0::OutputConfiguration;
+using HPhysicalCameraSettings = frameworks::cameraservice::device::V2_0::PhysicalCameraSettings;
+using HPhysicalCaptureResultInfo = frameworks::cameraservice::device::V2_0::PhysicalCaptureResultInfo;
+using HSubmitInfo = frameworks::cameraservice::device::V2_0::SubmitInfo;
+using HStatus = frameworks::cameraservice::common::V2_0::Status;
+using HStreamConfigurationMode = frameworks::cameraservice::device::V2_0::StreamConfigurationMode;
+using HTemplateId = frameworks::cameraservice::device::V2_0::TemplateId;
+
+// Note: existing data in dst will be gone. Caller still owns the memory of src
+void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst);
+
+int32_t convertFromHidl(HStreamConfigurationMode streamConfigurationMode);
+
+int32_t convertFromHidl(HTemplateId templateId);
+
+bool convertFromHidl(const HCameraMetadata &src, CameraMetadata *dst);
+
+hardware::camera2::params::OutputConfiguration convertFromHidl(
+ const HOutputConfiguration &hOutputConfiguration);
+
+HCameraDeviceStatus convertToHidlCameraDeviceStatus(int32_t status);
+
+void convertToHidl(const std::vector<hardware::CameraStatus> &src,
+ hidl_vec<HCameraStatusAndId>* dst);
+
+void convertToHidl(const hardware::camera2::utils::SubmitInfo &submitInfo,
+ HSubmitInfo *hSubmitInfo);
+
+HErrorCode convertToHidl(int32_t errorCode);
+
+HCaptureResultExtras convertToHidl(const CaptureResultExtras &captureResultExtras);
+
+hidl_vec<HPhysicalCaptureResultInfo> convertToHidl(
+ const std::vector<PhysicalCaptureResultInfo> &physicalCaptureResultInfos,
+ std::shared_ptr<CaptureResultMetadataQueue> &captureResultMetadataQueue);
+
+HStatus B2HStatus(const binder::Status &bStatus);
+
+} // conversion
+} // utils
+} // cameraservice
+} // hardware
+} //android
+
+#endif //CAMERASERVER_CONVERT_TO_HIDL
diff --git a/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp
new file mode 100644
index 0000000..d22ba5a
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/hardware/camera/device/3.2/types.h>
+#include <cutils/properties.h>
+#include <gui/Surface.h>
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+
+#include <hidl/AidlCameraDeviceCallbacks.h>
+#include <hidl/Convert.h>
+#include <hidl/HidlCameraDeviceUser.h>
+#include <android/hardware/camera/device/3.2/types.h>
+
+namespace android {
+namespace frameworks {
+namespace cameraservice {
+namespace device {
+namespace V2_0 {
+namespace implementation {
+
+using hardware::cameraservice::utils::conversion::convertToHidl;
+using hardware::cameraservice::utils::conversion::convertFromHidl;
+using hardware::cameraservice::utils::conversion::B2HStatus;
+
+using hardware::graphics::bufferqueue::V1_0::utils::H2BGraphicBufferProducer;
+using hardware::hidl_vec;
+using hardware::Return;
+using hardware::Void;
+using HSubmitInfo = device::V2_0::SubmitInfo;
+using hardware::camera2::params::OutputConfiguration;
+
+static constexpr int32_t CAMERA_REQUEST_METADATA_QUEUE_SIZE = 1 << 20 /* 1 MB */;
+static constexpr int32_t CAMERA_RESULT_METADATA_QUEUE_SIZE = 1 << 20 /* 1 MB */;
+
+Return<void> HidlCameraDeviceUser::disconnect() {
+ mDeviceRemote->disconnect();
+ return Void();
+}
+
+HidlCameraDeviceUser::HidlCameraDeviceUser(
+ const sp<hardware::camera2::ICameraDeviceUser> &deviceRemote)
+ : mDeviceRemote(deviceRemote) {
+ mInitSuccess = initDevice();
+}
+
+bool HidlCameraDeviceUser::initDevice() {
+ // TODO: Get request and result metadata queue size from a system property.
+ int32_t reqFMQSize = CAMERA_REQUEST_METADATA_QUEUE_SIZE;
+
+ mCaptureRequestMetadataQueue =
+ std::make_unique<CaptureRequestMetadataQueue>(static_cast<size_t>(reqFMQSize),
+ false /* non blocking */);
+ if (!mCaptureRequestMetadataQueue->isValid()) {
+ ALOGE("%s: invalid request fmq", __FUNCTION__);
+ return false;
+ }
+
+ int32_t resFMQSize = CAMERA_RESULT_METADATA_QUEUE_SIZE;
+ mCaptureResultMetadataQueue =
+ std::make_shared<CaptureResultMetadataQueue>(static_cast<size_t>(resFMQSize),
+ false /* non blocking */);
+ if (!mCaptureResultMetadataQueue->isValid()) {
+ ALOGE("%s: invalid result fmq", __FUNCTION__);
+ return false;
+ }
+ return true;
+}
+
+Return<void> HidlCameraDeviceUser::getCaptureRequestMetadataQueue(
+ getCaptureRequestMetadataQueue_cb _hidl_cb) {
+ if (mInitSuccess) {
+ _hidl_cb(*mCaptureRequestMetadataQueue->getDesc());
+ }
+ return Void();
+}
+
+Return<void> HidlCameraDeviceUser::getCaptureResultMetadataQueue(
+ getCaptureResultMetadataQueue_cb _hidl_cb) {
+ if (mInitSuccess) {
+ _hidl_cb(*mCaptureResultMetadataQueue->getDesc());
+ }
+ return Void();
+}
+
+/**
+ * To be used only by submitRequestList implementation, since it requires
+ * clients to call this method serially, incase fmq is used to send metadata.
+ */
+bool HidlCameraDeviceUser::copyPhysicalCameraSettings(
+ const hidl_vec<HPhysicalCameraSettings> &hPhysicalCameraSettings,
+ std::vector<CaptureRequest::PhysicalCameraSettings> *physicalCameraSettings) {
+ bool converted = false;
+ for (auto &e : hPhysicalCameraSettings) {
+ physicalCameraSettings->emplace_back();
+ CaptureRequest::PhysicalCameraSettings &physicalCameraSetting =
+ physicalCameraSettings->back();
+ physicalCameraSetting.id = e.id.c_str();
+
+ // Read the settings either from the fmq or straightaway from the
+ // request. We don't need any synchronization, since submitRequestList
+ // is guaranteed to be called serially by the client if it decides to
+ // use fmq.
+ if (e.settings.getDiscriminator() ==
+ FmqSizeOrMetadata::hidl_discriminator::fmqMetadataSize) {
+ /**
+ * Get settings from the fmq.
+ */
+ HCameraMetadata settingsFmq;
+ settingsFmq.resize(e.settings.fmqMetadataSize());
+ bool read = mCaptureRequestMetadataQueue->read(settingsFmq.data(),
+ e.settings.fmqMetadataSize());
+ if (!read) {
+ ALOGE("%s capture request settings could't be read from fmq size",
+ __FUNCTION__);
+ converted = false;
+ } else {
+ converted = convertFromHidl(settingsFmq, &physicalCameraSetting.settings);
+ }
+ } else {
+ /**
+ * The settings metadata is contained in request settings field.
+ */
+ converted =
+ convertFromHidl(e.settings.metadata(),
+ &physicalCameraSetting.settings);
+ }
+ if (!converted) {
+ ALOGE("%s: Unable to convert physicalCameraSettings from HIDL to AIDL.", __FUNCTION__);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool HidlCameraDeviceUser::convertRequestFromHidl(const HCaptureRequest &hRequest,
+ CaptureRequest *request) {
+ // No reprocessing support.
+ request->mIsReprocess = false;
+ for (const auto &streamAndWindowId : hRequest.streamAndWindowIds) {
+ request->mStreamIdxList.push_back(streamAndWindowId.streamId);
+ request->mSurfaceIdxList.push_back(streamAndWindowId.windowId);
+ }
+ return copyPhysicalCameraSettings(hRequest.physicalCameraSettings,
+ &(request->mPhysicalCameraSettings));
+}
+
+Return<void> HidlCameraDeviceUser::submitRequestList(const hidl_vec<HCaptureRequest>& hRequestList,
+ bool streaming,
+ submitRequestList_cb _hidl_cb) {
+ hardware::camera2::utils::SubmitInfo submitInfo;
+ HSubmitInfo hSubmitInfo;
+ /**
+ * Create AIDL CaptureRequest from requestList and graphicBufferProducers.
+ */
+ std::vector<hardware::camera2::CaptureRequest> requests;
+ for (auto &hRequest : hRequestList) {
+ requests.emplace_back();
+ auto &request = requests.back();
+ if (!convertRequestFromHidl(hRequest, &request)) {
+ _hidl_cb(HStatus::ILLEGAL_ARGUMENT, hSubmitInfo);
+ return Void();
+ }
+ }
+ mDeviceRemote->submitRequestList(requests, streaming, &submitInfo);
+ mRequestId = submitInfo.mRequestId;
+ convertToHidl(submitInfo, &hSubmitInfo);
+ _hidl_cb(HStatus::NO_ERROR, hSubmitInfo);
+ return Void();
+}
+
+Return<void> HidlCameraDeviceUser::cancelRepeatingRequest(cancelRepeatingRequest_cb _hidl_cb) {
+ int64_t lastFrameNumber = 0;
+ binder::Status ret = mDeviceRemote->cancelRequest(mRequestId, &lastFrameNumber);
+ _hidl_cb(B2HStatus(ret), lastFrameNumber);
+ return Void();
+}
+
+Return<HStatus> HidlCameraDeviceUser::beginConfigure() {
+ binder::Status ret = mDeviceRemote->beginConfigure();
+ return B2HStatus(ret);
+}
+
+Return<HStatus> HidlCameraDeviceUser::endConfigure(StreamConfigurationMode operatingMode,
+ const hidl_vec<uint8_t>& sessionParams) {
+ android::CameraMetadata cameraMetadata;
+ if (!convertFromHidl(sessionParams, &cameraMetadata)) {
+ return HStatus::ILLEGAL_ARGUMENT;
+ }
+
+ binder::Status ret = mDeviceRemote->endConfigure(convertFromHidl(operatingMode),
+ cameraMetadata);
+ return B2HStatus(ret);
+}
+
+Return<HStatus> HidlCameraDeviceUser::deleteStream(int32_t streamId) {
+ binder::Status ret = mDeviceRemote->deleteStream(streamId);
+ return B2HStatus(ret);
+}
+
+Return<void> HidlCameraDeviceUser::createStream(const HOutputConfiguration& hOutputConfiguration,
+ createStream_cb hidl_cb_) {
+ OutputConfiguration outputConfiguration =
+ convertFromHidl(hOutputConfiguration);
+ int32_t newStreamId = 0;
+ binder::Status ret = mDeviceRemote->createStream(outputConfiguration, &newStreamId);
+ HStatus status = B2HStatus(ret);
+ hidl_cb_(status, newStreamId);
+ return Void();
+}
+
+Return<void> HidlCameraDeviceUser::createDefaultRequest(TemplateId templateId,
+ createDefaultRequest_cb _hidl_cb) {
+ android::CameraMetadata cameraMetadata;
+ binder::Status ret = mDeviceRemote->createDefaultRequest(convertFromHidl(templateId),
+ &cameraMetadata);
+ HStatus hStatus = B2HStatus(ret);
+ HCameraMetadata hidlMetadata;
+ const camera_metadata_t *rawMetadata = cameraMetadata.getAndLock();
+ convertToHidl(rawMetadata, &hidlMetadata);
+ _hidl_cb(hStatus, hidlMetadata);
+ cameraMetadata.unlock(rawMetadata);
+ return Void();
+}
+
+Return<HStatus> HidlCameraDeviceUser::waitUntilIdle() {
+ binder::Status ret = mDeviceRemote->waitUntilIdle();
+ return B2HStatus(ret);
+}
+
+Return<void> HidlCameraDeviceUser::flush(flush_cb _hidl_cb) {
+ int64_t lastFrameNumber = 0;
+ binder::Status ret = mDeviceRemote->flush(&lastFrameNumber);
+ _hidl_cb(B2HStatus(ret),lastFrameNumber);
+ return Void();
+}
+
+Return<HStatus> HidlCameraDeviceUser::updateOutputConfiguration(
+ int32_t streamId,
+ const HOutputConfiguration& hOutputConfiguration) {
+ OutputConfiguration outputConfiguration = convertFromHidl(hOutputConfiguration);
+ binder::Status ret = mDeviceRemote->updateOutputConfiguration(streamId, outputConfiguration);
+ return B2HStatus(ret);
+}
+
+} // implementation
+} // V2_0
+} // device
+} // cameraservice
+} // frameworks
+} // android
diff --git a/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.h b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.h
new file mode 100644
index 0000000..be8f1d6
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_CAMERADEVICEUSER_V2_0_CAMERADEVICEUSER_H
+#define ANDROID_FRAMEWORKS_CAMERADEVICEUSER_V2_0_CAMERADEVICEUSER_H
+
+#include <mutex>
+#include <memory>
+#include <thread>
+
+#include <android/frameworks/cameraservice/common/2.0/types.h>
+#include <android/frameworks/cameraservice/service/2.0/types.h>
+#include <android/frameworks/cameraservice/device/2.0/ICameraDeviceUser.h>
+#include <android/frameworks/cameraservice/device/2.0/types.h>
+#include <android/hardware/camera2/ICameraDeviceCallbacks.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <CameraService.h>
+
+namespace android {
+namespace frameworks {
+namespace cameraservice {
+namespace device {
+namespace V2_0 {
+namespace implementation {
+
+using frameworks::cameraservice::device::V2_0::StreamConfigurationMode;
+using hardware::camera2::CaptureRequest;
+using hardware::hidl_vec;
+using hardware::kSynchronizedReadWrite;
+using hardware::MessageQueue;
+using hardware::MQDescriptorSync;
+using hardware::Return;
+using CaptureResultMetadataQueue = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using CaptureRequestMetadataQueue = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using TemplateId = frameworks::cameraservice::device::V2_0::TemplateId;
+
+using HCameraDeviceUser = device::V2_0::ICameraDeviceUser;
+using HCameraMetadata = cameraservice::service::V2_0::CameraMetadata;
+using HCaptureRequest = device::V2_0::CaptureRequest;
+using HOutputConfiguration = frameworks::cameraservice::device::V2_0::OutputConfiguration;
+using HPhysicalCameraSettings = frameworks::cameraservice::device::V2_0::PhysicalCameraSettings;
+using HStatus = frameworks::cameraservice::common::V2_0::Status;
+
+static constexpr int32_t REQUEST_ID_NONE = -1;
+
+struct HidlCameraDeviceUser final : public HCameraDeviceUser {
+ HidlCameraDeviceUser(const sp<hardware::camera2::ICameraDeviceUser> &deviceRemote);
+
+ ~HidlCameraDeviceUser() { }
+
+ virtual Return<void> disconnect() override;
+
+ virtual Return<void> getCaptureRequestMetadataQueue(
+ getCaptureRequestMetadataQueue_cb _hidl_cb) override;
+
+ virtual Return<void> getCaptureResultMetadataQueue(
+ getCaptureResultMetadataQueue_cb _hidl_cb) override;
+
+ virtual Return<void> submitRequestList(const hidl_vec<HCaptureRequest>& requestList,
+ bool streaming, submitRequestList_cb _hidl_cb) override;
+
+ virtual Return<void> cancelRepeatingRequest(cancelRepeatingRequest_cb _hidl_cb) override;
+
+ virtual Return<HStatus> beginConfigure() override;
+
+ virtual Return<HStatus> endConfigure(StreamConfigurationMode operatingMode,
+ const hidl_vec<uint8_t>& sessionParams);
+
+ virtual Return<HStatus> deleteStream(int32_t streamId) override;
+
+ virtual Return<void> createStream(const HOutputConfiguration& outputConfiguration,
+ createStream_cb _hidl_cb) override;
+
+ Return<void> createDefaultRequest(TemplateId templateId,
+ createDefaultRequest_cb _hidl_cb) override;
+
+ virtual Return<HStatus> waitUntilIdle() override;
+
+ virtual Return<void> flush(flush_cb _hidl_cb) override;
+
+ virtual Return<HStatus> updateOutputConfiguration(
+ int32_t streamId, const HOutputConfiguration& outputConfiguration) override;
+
+ bool initStatus() { return mInitSuccess; }
+
+ std::shared_ptr<CaptureResultMetadataQueue> getCaptureResultMetadataQueue() {
+ return mCaptureResultMetadataQueue;
+ }
+
+ private:
+ bool initDevice();
+
+ bool convertRequestFromHidl(const HCaptureRequest &hRequest, CaptureRequest *request);
+
+ bool copyPhysicalCameraSettings(
+ const hidl_vec<HPhysicalCameraSettings> &hPhysicalCameraSettings,
+ std::vector<CaptureRequest::PhysicalCameraSettings> *physicalCameraSettings);
+
+ const sp<hardware::camera2::ICameraDeviceUser> mDeviceRemote;
+ std::unique_ptr<CaptureRequestMetadataQueue> mCaptureRequestMetadataQueue = nullptr;
+ std::shared_ptr<CaptureResultMetadataQueue> mCaptureResultMetadataQueue = nullptr;
+ bool mInitSuccess = false;
+ int32_t mRequestId = REQUEST_ID_NONE;
+};
+
+} // implementation
+} // V2_0
+} // device
+} // cameraservice
+} // frameworks
+} // android
+#endif // ANDROID_FRAMEOWORKS_CAMERADEVICEUSER_V2_0_CAMERADEVICEUSER_H
diff --git a/services/camera/libcameraservice/hidl/HidlCameraService.cpp b/services/camera/libcameraservice/hidl/HidlCameraService.cpp
new file mode 100644
index 0000000..31bdf6d
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/HidlCameraService.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hidl/Convert.h>
+
+#include <hidl/HidlCameraService.h>
+
+#include <hidl/HidlCameraDeviceUser.h>
+#include <hidl/AidlCameraDeviceCallbacks.h>
+#include <hidl/AidlCameraServiceListener.h>
+
+#include <hidl/HidlTransportSupport.h>
+
+namespace android {
+namespace frameworks {
+namespace cameraservice {
+namespace service {
+namespace V2_0 {
+namespace implementation {
+
+using frameworks::cameraservice::service::V2_0::implementation::HidlCameraService;
+using hardware::hidl_vec;
+using hardware::cameraservice::utils::conversion::convertToHidl;
+using hardware::cameraservice::utils::conversion::B2HStatus;
+using hardware::Void;
+
+using device::V2_0::implementation::H2BCameraDeviceCallbacks;
+using device::V2_0::implementation::HidlCameraDeviceUser;
+using service::V2_0::implementation::H2BCameraServiceListener;
+using HCameraMetadataType = android::frameworks::cameraservice::common::V2_0::CameraMetadataType;
+using HVendorTag = android::frameworks::cameraservice::common::V2_0::VendorTag;
+using HVendorTagSection = android::frameworks::cameraservice::common::V2_0::VendorTagSection;
+
+sp<HidlCameraService> gHidlCameraService;
+
+sp<HidlCameraService> HidlCameraService::getInstance(android::CameraService *cs) {
+ gHidlCameraService = new HidlCameraService(cs);
+ return gHidlCameraService;
+}
+
+Return<void>
+HidlCameraService::getCameraCharacteristics(const hidl_string& cameraId,
+ getCameraCharacteristics_cb _hidl_cb) {
+ android::CameraMetadata cameraMetadata;
+ HStatus status = HStatus::NO_ERROR;
+ binder::Status serviceRet =
+ mAidlICameraService->getCameraCharacteristics(String16(cameraId.c_str()), &cameraMetadata);
+ HCameraMetadata hidlMetadata;
+ if (!serviceRet.isOk()) {
+ switch(serviceRet.serviceSpecificErrorCode()) {
+ // No ERROR_CAMERA_DISCONNECTED since we're in the same process.
+ case hardware::ICameraService::ERROR_ILLEGAL_ARGUMENT:
+ ALOGE("%s: Camera ID %s does not exist!", __FUNCTION__, cameraId.c_str());
+ status = HStatus::ILLEGAL_ARGUMENT;
+ break;
+ default:
+ ALOGE("Get camera characteristics from camera service failed: %s",
+ serviceRet.toString8().string());
+ status = B2HStatus(serviceRet);
+ }
+ _hidl_cb(status, hidlMetadata);
+ return Void();
+ }
+ const camera_metadata_t *rawMetadata = cameraMetadata.getAndLock();
+ convertToHidl(rawMetadata, &hidlMetadata);
+ _hidl_cb(status, hidlMetadata);
+ cameraMetadata.unlock(rawMetadata);
+ return Void();
+}
+
+Return<void> HidlCameraService::connectDevice(const sp<HCameraDeviceCallback>& hCallback,
+ const hidl_string& cameraId,
+ connectDevice_cb _hidl_cb) {
+ // Here, we first get ICameraDeviceUser from mAidlICameraService, then save
+ // that interface in the newly created HidlCameraDeviceUser impl class.
+ if (mAidlICameraService == nullptr) {
+ _hidl_cb(HStatus::UNKNOWN_ERROR, nullptr);
+ return Void();
+ }
+ sp<hardware::camera2::ICameraDeviceUser> deviceRemote = nullptr;
+ // Create a hardware::camera2::ICameraDeviceCallback object which internally
+ // calls callback functions passed through hCallback.
+ sp<H2BCameraDeviceCallbacks> hybridCallbacks = new H2BCameraDeviceCallbacks(hCallback);
+ if (!hybridCallbacks->initializeLooper()) {
+ ALOGE("Unable to handle callbacks on device, cannot connect");
+ _hidl_cb(HStatus::UNKNOWN_ERROR, nullptr);
+ return Void();
+ }
+ sp<hardware::camera2::ICameraDeviceCallbacks> callbacks = hybridCallbacks;
+ binder::Status serviceRet = mAidlICameraService->connectDevice(
+ callbacks, String16(cameraId.c_str()), String16(""),
+ hardware::ICameraService::USE_CALLING_UID, /*out*/&deviceRemote);
+ HStatus status = HStatus::NO_ERROR;
+ if (!serviceRet.isOk()) {
+ ALOGE("%s: Unable to connect to camera device", __FUNCTION__);
+ status = B2HStatus(serviceRet);
+ _hidl_cb(status, nullptr);
+ return Void();
+ }
+ // Now we create a HidlCameraDeviceUser class, store the deviceRemote in it,
+ // and return that back. All calls on that interface will be forwarded to
+ // the AIDL interface.
+ sp<HidlCameraDeviceUser> hDeviceRemote = new HidlCameraDeviceUser(deviceRemote);
+ if (!hDeviceRemote->initStatus()) {
+ ALOGE("%s: Unable to initialize camera device HIDL wrapper", __FUNCTION__);
+ _hidl_cb(HStatus::UNKNOWN_ERROR, nullptr);
+ return Void();
+ }
+ hybridCallbacks->setCaptureResultMetadataQueue(hDeviceRemote->getCaptureResultMetadataQueue());
+ _hidl_cb(status, hDeviceRemote);
+ return Void();
+}
+
+void HidlCameraService::addToListenerCacheLocked(sp<HCameraServiceListener> hListener,
+ sp<hardware::ICameraServiceListener> csListener) {
+ mListeners.emplace_back(std::make_pair(hListener, csListener));
+}
+
+sp<hardware::ICameraServiceListener>
+HidlCameraService::searchListenerCacheLocked(sp<HCameraServiceListener> hListener,
+ bool shouldRemove) {
+ // Go through the mListeners list and compare the listener with the HIDL
+ // listener registered.
+ auto it = mListeners.begin();
+ sp<ICameraServiceListener> csListener = nullptr;
+ for (;it != mListeners.end(); it++) {
+ if (hardware::interfacesEqual(it->first, hListener)) {
+ break;
+ }
+ }
+ if (it != mListeners.end()) {
+ csListener = it->second;
+ if (shouldRemove) {
+ mListeners.erase(it);
+ }
+ }
+ return csListener;
+}
+
+Return<void> HidlCameraService::addListener(const sp<HCameraServiceListener>& hCsListener,
+ addListener_cb _hidl_cb) {
+ if (mAidlICameraService == nullptr) {
+ _hidl_cb(HStatus::UNKNOWN_ERROR, {});
+ return Void();
+ }
+ if (hCsListener == nullptr) {
+ ALOGE("%s listener must not be NULL", __FUNCTION__);
+ _hidl_cb(HStatus::ILLEGAL_ARGUMENT, {});
+ return Void();
+ }
+ sp<hardware::ICameraServiceListener> csListener = nullptr;
+ // Check the cache for previously registered callbacks
+ {
+ Mutex::Autolock l(mListenerListLock);
+ csListener = searchListenerCacheLocked(hCsListener);
+ if (csListener == nullptr) {
+ // Wrap an hCsListener with AidlCameraServiceListener and pass it to
+ // CameraService.
+ csListener = new H2BCameraServiceListener(hCsListener);
+ // Add to cache
+ addToListenerCacheLocked(hCsListener, csListener);
+ } else {
+ ALOGE("%s: Trying to add a listener %p already registered",
+ __FUNCTION__, hCsListener.get());
+ _hidl_cb(HStatus::ILLEGAL_ARGUMENT, {});
+ return Void();
+ }
+ }
+ std::vector<hardware::CameraStatus> cameraStatusAndIds{};
+ binder::Status serviceRet = mAidlICameraService->addListener(csListener, &cameraStatusAndIds);
+ HStatus status = HStatus::NO_ERROR;
+ if (!serviceRet.isOk()) {
+ ALOGE("%s: Unable to add camera device status listener", __FUNCTION__);
+ status = B2HStatus(serviceRet);
+ _hidl_cb(status, {});
+ return Void();
+ }
+ hidl_vec<HCameraStatusAndId> hCameraStatusAndIds;
+ //Convert cameraStatusAndIds to HIDL and call callback
+ convertToHidl(cameraStatusAndIds, &hCameraStatusAndIds);
+ _hidl_cb(status, hCameraStatusAndIds);
+ return Void();
+}
+
+Return<HStatus> HidlCameraService::removeListener(const sp<HCameraServiceListener>& hCsListener) {
+ if (hCsListener == nullptr) {
+ ALOGE("%s listener must not be NULL", __FUNCTION__);
+ return HStatus::ILLEGAL_ARGUMENT;
+ }
+ sp<ICameraServiceListener> csListener = nullptr;
+ {
+ Mutex::Autolock l(mListenerListLock);
+ csListener = searchListenerCacheLocked(hCsListener, /*removeIfFound*/true);
+ }
+ if (csListener != nullptr) {
+ mAidlICameraService->removeListener(csListener);
+ } else {
+ ALOGE("%s Removing unregistered listener %p", __FUNCTION__, hCsListener.get());
+ return HStatus::ILLEGAL_ARGUMENT;
+ }
+ return HStatus::NO_ERROR;
+}
+
+Return<void> HidlCameraService::getCameraVendorTagSections(getCameraVendorTagSections_cb _hidl_cb) {
+ hidl_vec<HVendorTagSection> hVendorTagSections;
+ // TODO: Could this be just created on the stack since we don't set it to
+ // global cache or anything ?
+ HStatus hStatus = HStatus::NO_ERROR;
+ sp<VendorTagDescriptor> desc = new VendorTagDescriptor();
+ binder::Status serviceRet = mAidlICameraService->getCameraVendorTagDescriptor(desc.get());
+
+ if (!serviceRet.isOk()) {
+ ALOGE("%s: Failed to get VendorTagDescriptor", __FUNCTION__);
+ _hidl_cb(B2HStatus(serviceRet), hVendorTagSections);
+ return Void();
+ }
+
+ const SortedVector<String8>* sectionNames = desc->getAllSectionNames();
+ size_t numSections = sectionNames->size();
+ std::vector<std::vector<HVendorTag>> tagsBySection(numSections);
+ int tagCount = desc->getTagCount();
+ std::vector<uint32_t> tags(tagCount);
+ desc->getTagArray(tags.data());
+ for (int i = 0; i < tagCount; i++) {
+ HVendorTag vt;
+ vt.tagId = tags[i];
+ vt.tagName = desc->getTagName(tags[i]);
+ vt.tagType = (HCameraMetadataType) desc->getTagType(tags[i]);
+ ssize_t sectionIdx = desc->getSectionIndex(tags[i]);
+ tagsBySection[sectionIdx].push_back(vt);
+ }
+ hVendorTagSections.resize(numSections);
+ for (size_t s = 0; s < numSections; s++) {
+ hVendorTagSections[s].sectionName = (*sectionNames)[s].string();
+ hVendorTagSections[s].tags = tagsBySection[s];
+ }
+ _hidl_cb(hStatus, hVendorTagSections);
+ return Void();
+}
+
+} // implementation
+} // V2_0
+} // service
+} // cameraservice
+} // frameworks
+} // android
+
diff --git a/services/camera/libcameraservice/hidl/HidlCameraService.h b/services/camera/libcameraservice/hidl/HidlCameraService.h
new file mode 100644
index 0000000..eead0bc
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/HidlCameraService.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_CAMERASERVICE_V2_0_CAMERASERVICE_H
+#define ANDROID_FRAMEWORKS_CAMERASERVICE_V2_0_CAMERASERVICE_H
+
+#include <mutex>
+#include <thread>
+
+#include <android/frameworks/cameraservice/common/2.0/types.h>
+#include <android/frameworks/cameraservice/service/2.0/ICameraService.h>
+#include <android/frameworks/cameraservice/service/2.0/types.h>
+#include <android/frameworks/cameraservice/device/2.0/types.h>
+
+#include <hidl/Status.h>
+
+#include <CameraService.h>
+
+namespace android {
+namespace frameworks {
+namespace cameraservice {
+namespace service {
+namespace V2_0 {
+namespace implementation {
+
+using hardware::hidl_string;
+using hardware::ICameraServiceListener;
+using hardware::Return;
+
+using HCameraDeviceCallback = frameworks::cameraservice::device::V2_0::ICameraDeviceCallback;
+using HCameraMetadata = frameworks::cameraservice::service::V2_0::CameraMetadata;
+using HCameraService = frameworks::cameraservice::service::V2_0::ICameraService;
+using HCameraServiceListener = frameworks::cameraservice::service::V2_0::ICameraServiceListener;
+using HStatus = frameworks::cameraservice::common::V2_0::Status;
+using HCameraStatusAndId = frameworks::cameraservice::service::V2_0::CameraStatusAndId;
+
+struct HidlCameraService final : public HCameraService {
+
+ ~HidlCameraService() { };
+
+ // Methods from ::android::frameworks::cameraservice::service::V2.0::ICameraService follow.
+
+ Return<void> connectDevice(const sp<HCameraDeviceCallback>& callback,
+ const hidl_string& cameraId, connectDevice_cb _hidl_cb) override;
+
+ Return<void> addListener(const sp<HCameraServiceListener>& listener,
+ addListener_cb _hidl_cb) override;
+
+ Return<HStatus> removeListener(const sp<HCameraServiceListener>& listener) override;
+
+ Return<void> getCameraCharacteristics(const hidl_string& cameraId,
+ getCameraCharacteristics_cb _hidl_cb) override;
+
+ Return<void> getCameraVendorTagSections(getCameraVendorTagSections_cb _hidl_cb) override;
+
+ // This method should only be called by the cameraservers main thread to
+ // instantiate the hidl cameraserver.
+ static sp<HidlCameraService> getInstance(android::CameraService *cs);
+
+private:
+ HidlCameraService(android::CameraService *cs) : mAidlICameraService(cs) { };
+
+ sp<hardware::ICameraServiceListener> searchListenerCacheLocked(
+ sp<HCameraServiceListener> listener, /*removeIfFound*/ bool shouldRemove = false);
+
+ void addToListenerCacheLocked(sp<HCameraServiceListener> hListener,
+ sp<hardware::ICameraServiceListener> csListener);
+
+ android::CameraService *const mAidlICameraService = nullptr;
+
+ Mutex mListenerListLock;
+
+ using HIListeners =
+ std::pair<sp<HCameraServiceListener>, sp<ICameraServiceListener>>;
+ std::list<HIListeners> mListeners;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace service
+} // namespace cameraservice
+} // namespace frameworks
+} // namespace android
+
+#endif // ANDROID_FRAMEWORKS_CAMERASERVICE_V2_0_CAMERASERVICE_H
diff --git a/services/mediadrm/Android.mk b/services/mediadrm/Android.mk
index e870965..227a29d 100644
--- a/services/mediadrm/Android.mk
+++ b/services/mediadrm/Android.mk
@@ -29,7 +29,8 @@
libhidlmemory \
libhidltransport \
android.hardware.drm@1.0 \
- android.hardware.drm@1.1
+ android.hardware.drm@1.1 \
+ android.hardware.drm@1.2
LOCAL_CFLAGS += -Wall -Wextra -Werror
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
index d70e27b..e6c676c 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
@@ -40,4 +40,7 @@
pread64: 1
mremap: 1
+# Required by Sanitizers
+sched_yield: 1
+
@include /system/etc/seccomp_policy/crash_dump.arm64.policy
diff --git a/services/soundtrigger/SoundTriggerHalHidl.cpp b/services/soundtrigger/SoundTriggerHalHidl.cpp
index 0f9aa15..1d37a8e 100644
--- a/services/soundtrigger/SoundTriggerHalHidl.cpp
+++ b/services/soundtrigger/SoundTriggerHalHidl.cpp
@@ -356,8 +356,7 @@
return hidlReturn;
}
-int SoundTriggerHalHidl::getModelState(sound_model_handle_t handle,
- struct sound_trigger_recognition_event** event)
+int SoundTriggerHalHidl::getModelState(sound_model_handle_t handle)
{
sp<ISoundTriggerHw> soundtrigger = getService();
if (soundtrigger == 0) {
@@ -377,24 +376,13 @@
}
int ret = NO_ERROR;
- Return<void> hidlReturn;
+ Return<int32_t> hidlReturn(0);
{
AutoMutex lock(mHalLock);
- hidlReturn = soundtrigger_2_2->getModelState(
- model->mHalHandle,
- [&](int r, const V2_0_ISoundTriggerHwCallback::RecognitionEvent& halEvent) {
- ret = r;
- if (ret != 0) {
- ALOGE("getModelState returned error code %d", ret);
- } else {
- *event = convertRecognitionEventFromHal(&halEvent);
- }
- });
+ hidlReturn = soundtrigger_2_2->getModelState(model->mHalHandle);
}
if (!hidlReturn.isOk()) {
ALOGE("getModelState error %s", hidlReturn.description().c_str());
- free(*event);
- *event = nullptr;
ret = FAILED_TRANSACTION;
}
return ret;
diff --git a/services/soundtrigger/SoundTriggerHalHidl.h b/services/soundtrigger/SoundTriggerHalHidl.h
index 3f4bec3..fb9e39e 100644
--- a/services/soundtrigger/SoundTriggerHalHidl.h
+++ b/services/soundtrigger/SoundTriggerHalHidl.h
@@ -96,12 +96,12 @@
virtual int stopAllRecognitions();
/* Get the current state of a given model.
- * Returns 0 or an error code. If successful it also sets indicated the event pointer
- * and expectes that the caller will free the memory.
+ * Returns 0 or an error code. If successful the state will be returned asynchronously
+ * via a recognition event in the callback method that was registered in the
+ * startRecognition() method.
* Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_2 or above.
*/
- virtual int getModelState(sound_model_handle_t handle,
- struct sound_trigger_recognition_event** event);
+ virtual int getModelState(sound_model_handle_t handle);
// ISoundTriggerHwCallback
virtual ::android::hardware::Return<void> recognitionCallback(
diff --git a/services/soundtrigger/SoundTriggerHalInterface.h b/services/soundtrigger/SoundTriggerHalInterface.h
index 076ca23..0183ece 100644
--- a/services/soundtrigger/SoundTriggerHalInterface.h
+++ b/services/soundtrigger/SoundTriggerHalInterface.h
@@ -72,12 +72,12 @@
virtual int stopAllRecognitions() = 0;
/* Get the current state of a given model.
- * Returns 0 or an error code. If successful it also sets indicated the event pointer
- * and expectes that the caller will free the memory.
+ * Returns 0 or an error code. If successful the state will be returned asynchronously
+ * via a recognition event in the callback method that was registered in the
+ * startRecognition() method.
* Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_2 or above.
*/
- virtual int getModelState(sound_model_handle_t handle,
- struct sound_trigger_recognition_event** event) = 0;
+ virtual int getModelState(sound_model_handle_t handle) = 0;
protected:
SoundTriggerHalInterface() {}
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
index 79e9e88..7915068 100644
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ b/services/soundtrigger/SoundTriggerHwService.cpp
@@ -717,8 +717,7 @@
return NO_ERROR;
}
-status_t SoundTriggerHwService::Module::getModelState(sound_model_handle_t handle,
- sp<IMemory>& eventMemory)
+status_t SoundTriggerHwService::Module::getModelState(sound_model_handle_t handle)
{
ALOGV("getModelState() model handle %d", handle);
if (mHalInterface == 0) {
@@ -734,21 +733,7 @@
return INVALID_OPERATION;
}
- if (model->mType != SOUND_MODEL_TYPE_GENERIC) {
- return BAD_VALUE;
- }
-
- struct sound_trigger_recognition_event* event = nullptr;
- status_t status = mHalInterface->getModelState(handle, &event);
- if (status == NO_ERROR) {
- sp<SoundTriggerHwService> service;
- service = mService.promote();
- if (service != 0) {
- eventMemory = service->prepareRecognitionEvent(event);
- }
- free(event);
- }
- return status;
+ return mHalInterface->getModelState(handle);
}
void SoundTriggerHwService::Module::onCallbackEvent(const sp<CallbackEvent>& event)
@@ -784,7 +769,10 @@
}
recognitionEvent->capture_session = model->mCaptureSession;
- model->mState = Model::STATE_IDLE;
+ // Don't reset the model state if this recognition event is a get-state response
+ if (recognitionEvent->status != RECOGNITION_STATUS_GET_STATE_RESPONSE) {
+ model->mState = Model::STATE_IDLE;
+ }
clients.add(model->mModuleClient);
}
} break;
@@ -1052,8 +1040,7 @@
return module->stopRecognition(handle);
}
-status_t SoundTriggerHwService::ModuleClient::getModelState(sound_model_handle_t handle,
- sp<IMemory>& eventMemory)
+status_t SoundTriggerHwService::ModuleClient::getModelState(sound_model_handle_t handle)
{
ALOGV("getModelState() model handle %d", handle);
if (!captureHotwordAllowed(IPCThreadState::self()->getCallingPid(),
@@ -1065,7 +1052,7 @@
if (module == 0) {
return NO_INIT;
}
- return module->getModelState(handle, eventMemory);
+ return module->getModelState(handle);
}
void SoundTriggerHwService::ModuleClient::setCaptureState_l(bool active)
diff --git a/services/soundtrigger/SoundTriggerHwService.h b/services/soundtrigger/SoundTriggerHwService.h
index c222cd9..4258ec0 100644
--- a/services/soundtrigger/SoundTriggerHwService.h
+++ b/services/soundtrigger/SoundTriggerHwService.h
@@ -122,8 +122,7 @@
virtual status_t startRecognition(sound_model_handle_t handle,
const sp<IMemory>& dataMemory);
virtual status_t stopRecognition(sound_model_handle_t handle);
- virtual status_t getModelState(sound_model_handle_t handle,
- sp<IMemory>& eventMemory);
+ virtual status_t getModelState(sound_model_handle_t handle);
sp<SoundTriggerHalInterface> halInterface() const { return mHalInterface; }
struct sound_trigger_module_descriptor descriptor() { return mDescriptor; }
@@ -171,8 +170,7 @@
virtual status_t startRecognition(sound_model_handle_t handle,
const sp<IMemory>& dataMemory);
virtual status_t stopRecognition(sound_model_handle_t handle);
- virtual status_t getModelState(sound_model_handle_t handle,
- sp<IMemory>& eventMemory);
+ virtual status_t getModelState(sound_model_handle_t handle);
virtual status_t dump(int fd, const Vector<String16>& args);
diff --git a/soundtrigger/ISoundTrigger.cpp b/soundtrigger/ISoundTrigger.cpp
index 32882f1..f5b4b59 100644
--- a/soundtrigger/ISoundTrigger.cpp
+++ b/soundtrigger/ISoundTrigger.cpp
@@ -114,8 +114,7 @@
return status;
}
- virtual status_t getModelState(sound_model_handle_t handle,
- sp<IMemory>& eventMemory)
+ virtual status_t getModelState(sound_model_handle_t handle)
{
Parcel data, reply;
data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor());
@@ -123,9 +122,6 @@
status_t status = remote()->transact(GET_MODEL_STATE, data, &reply);
if (status == NO_ERROR) {
status = (status_t)reply.readInt32();
- if (status == NO_ERROR) {
- eventMemory = interface_cast<IMemory>(reply.readStrongBinder());
- }
}
return status;
}
@@ -192,14 +188,7 @@
status_t status = UNKNOWN_ERROR;
status_t ret = data.read(&handle, sizeof(sound_model_handle_t));
if (ret == NO_ERROR) {
- sp<IMemory> eventMemory;
- status = getModelState(handle, eventMemory);
- if (eventMemory != NULL) {
- ret = reply->writeStrongBinder(
- IInterface::asBinder(eventMemory));
- } else {
- ret = NO_MEMORY;
- }
+ status = getModelState(handle);
}
reply->writeInt32(status);
return ret;
diff --git a/soundtrigger/SoundTrigger.cpp b/soundtrigger/SoundTrigger.cpp
index bb0650f..d1eb367 100644
--- a/soundtrigger/SoundTrigger.cpp
+++ b/soundtrigger/SoundTrigger.cpp
@@ -188,14 +188,13 @@
return mISoundTrigger->stopRecognition(handle);
}
-status_t SoundTrigger::getModelState(sound_model_handle_t handle,
- sp<IMemory>& eventMemory)
+status_t SoundTrigger::getModelState(sound_model_handle_t handle)
{
Mutex::Autolock _l(mLock);
if (mISoundTrigger == 0) {
return NO_INIT;
}
- return mISoundTrigger->getModelState(handle, eventMemory);
+ return mISoundTrigger->getModelState(handle);
}
// BpSoundTriggerClient