Merge "screenrecord: Disambiguate ALooper"
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 97cf6bf..e84eeef 100644
--- a/camera/ndk/Android.bp
+++ b/camera/ndk/Android.bp
@@ -30,3 +30,43 @@
srcs: ["include/camera/**/*.h"],
license: "NOTICE",
}
+
+cc_library_shared {
+ name: "libcamera2ndk",
+ srcs: [
+ "NdkCameraManager.cpp",
+ "NdkCameraMetadata.cpp",
+ "NdkCameraDevice.cpp",
+ "NdkCaptureRequest.cpp",
+ "NdkCameraCaptureSession.cpp",
+ "impl/ACameraManager.cpp",
+ "impl/ACameraMetadata.cpp",
+ "impl/ACameraDevice.cpp",
+ "impl/ACameraCaptureSession.cpp",
+ ],
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libgui",
+ "libutils",
+ "libandroid_runtime",
+ "libcamera_client",
+ "libstagefright_foundation",
+ "libcutils",
+ "libcamera_metadata",
+ "libmediandk",
+ "libnativewindow",
+ ],
+ cflags: [
+ "-fvisibility=hidden",
+ "-DEXPORT=__attribute__ ((visibility (\"default\")))",
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ 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 f5ff69d..0000000
--- a/camera/ndk/Android.mk
+++ /dev/null
@@ -1,56 +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.
-#
-
-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_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/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/mediadrm/Crypto.h b/include/mediadrm/Crypto.h
deleted file mode 120000
index 9af6495..0000000
--- a/include/mediadrm/Crypto.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libmedia/include/media/Crypto.h
\ No newline at end of file
diff --git a/include/mediadrm/Drm.h b/include/mediadrm/Drm.h
deleted file mode 120000
index ac60003..0000000
--- a/include/mediadrm/Drm.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libmedia/include/media/Drm.h
\ No newline at end of file
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 3eaea7c..57b4609 100644
--- a/media/bufferpool/2.0/Accessor.cpp
+++ b/media/bufferpool/2.0/Accessor.cpp
@@ -113,23 +113,26 @@
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,
connect_cb _hidl_cb) {
- (void)observer;
sp<Connection> connection;
ConnectionId connectionId;
+ uint32_t msgId;
const StatusDescriptor* fmqDesc;
+ const InvalidationDescriptor* invDesc;
- ResultStatus status = connect(&connection, &connectionId, &fmqDesc, false);
+ ResultStatus status = connect(
+ observer, false, &connection, &connectionId, &msgId, &fmqDesc, &invDesc);
if (status == ResultStatus::OK) {
- _hidl_cb(status, connection, connectionId, *fmqDesc,
- android::hardware::MQDescriptorUnsync<BufferInvalidationMessage>(
- std::vector<android::hardware::GrantorDescriptor>(),
- nullptr /* nhandle */, 0 /* size */));
+ _hidl_cb(status, connection, connectionId, msgId, *fmqDesc, *invDesc);
} else {
- _hidl_cb(status, nullptr, -1LL,
+ _hidl_cb(status, nullptr, -1LL, 0,
android::hardware::MQDescriptorSync<BufferStatusMessage>(
std::vector<android::hardware::GrantorDescriptor>(),
nullptr /* nhandle */, 0 /* size */),
@@ -147,7 +150,15 @@
}
bool Accessor::isValid() {
- return (bool)mImpl;
+ return (bool)mImpl && mImpl->isValid();
+}
+
+ResultStatus Accessor::flush() {
+ if (mImpl) {
+ mImpl->flush();
+ return ResultStatus::OK;
+ }
+ return ResultStatus::CRITICAL_ERROR;
}
ResultStatus Accessor::allocate(
@@ -170,10 +181,15 @@
}
ResultStatus Accessor::connect(
+ const sp<IObserver> &observer, bool local,
sp<Connection> *connection, ConnectionId *pConnectionId,
- const StatusDescriptor** fmqDescPtr, bool local) {
+ uint32_t *pMsgId,
+ const StatusDescriptor** statusDescPtr,
+ const InvalidationDescriptor** invDescPtr) {
if (mImpl) {
- ResultStatus status = mImpl->connect(this, connection, pConnectionId, fmqDescPtr);
+ ResultStatus status = mImpl->connect(
+ this, observer, connection, pConnectionId, pMsgId,
+ statusDescPtr, invDescPtr);
if (!local && status == ResultStatus::OK) {
sp<Accessor> accessor(this);
sConnectionDeathRecipient->add(*pConnectionId, accessor);
diff --git a/media/bufferpool/2.0/Accessor.h b/media/bufferpool/2.0/Accessor.h
index a718da1..8d02519 100644
--- a/media/bufferpool/2.0/Accessor.h
+++ b/media/bufferpool/2.0/Accessor.h
@@ -95,6 +95,9 @@
/** Returns whether the accessor is valid. */
bool isValid();
+ /** Invalidates all buffers which are owned by bufferpool */
+ ResultStatus flush();
+
/** Allocates a buffer from a buffer pool.
*
* @param connectionId the connection id of the client.
@@ -135,20 +138,28 @@
* 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 observer client observer for buffer invalidation
* @param local true when a connection request comes from local process,
* false otherwise.
+ * @param connection created connection
+ * @param pConnectionId the id of the created connection
+ * @param pMsgId the id of the recent buffer pool message
+ * @param statusDescPtr FMQ descriptor for shared buffer status message
+ * queue between a buffer pool and the client.
+ * @param invDescPtr FMQ descriptor for buffer invalidation message
+ * queue from a buffer pool to the client.
*
* @return OK when a connection is successfully made.
* NO_MEMORY when there is no memory.
* CRITICAL_ERROR otherwise.
*/
ResultStatus connect(
+ const sp<IObserver>& observer,
+ bool local,
sp<Connection> *connection, ConnectionId *pConnectionId,
- const StatusDescriptor** fmqDescPtr, bool local);
+ uint32_t *pMsgId,
+ const StatusDescriptor** statusDescPtr,
+ const InvalidationDescriptor** invDescPtr);
/**
* Closes the specified connection to the client.
@@ -174,9 +185,11 @@
*/
static sp<ConnectionDeathRecipient> getConnectionDeathRecipient();
+ static void createInvalidator();
+
private:
class Impl;
- std::unique_ptr<Impl> mImpl;
+ std::shared_ptr<Impl> mImpl;
};
} // namespace implementation
diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp
index 0ba6600..84fcca2 100644
--- a/media/bufferpool/2.0/AccessorImpl.cpp
+++ b/media/bufferpool/2.0/AccessorImpl.cpp
@@ -21,6 +21,7 @@
#include <time.h>
#include <unistd.h>
#include <utils/Log.h>
+#include <thread>
#include "AccessorImpl.h"
#include "Connection.h"
@@ -47,6 +48,7 @@
const std::shared_ptr<BufferPoolAllocation> mAllocation;
const size_t mAllocSize;
const std::vector<uint8_t> mConfig;
+ bool mInvalidated;
InternalBuffer(
BufferId id,
@@ -54,11 +56,16 @@
const size_t allocSize,
const std::vector<uint8_t> &allocConfig)
: mId(id), mOwnerCount(0), mTransactionCount(0),
- mAllocation(alloc), mAllocSize(allocSize), mConfig(allocConfig) {}
+ mAllocation(alloc), mAllocSize(allocSize), mConfig(allocConfig),
+ mInvalidated(false) {}
const native_handle_t *handle() {
return mAllocation->handle();
}
+
+ void invalidate() {
+ mInvalidated = true;
+ }
};
struct TransactionStatus {
@@ -138,21 +145,29 @@
}
ResultStatus Accessor::Impl::connect(
- const sp<Accessor> &accessor, sp<Connection> *connection,
- ConnectionId *pConnectionId, const StatusDescriptor** fmqDescPtr) {
+ const sp<Accessor> &accessor, const sp<IObserver> &observer,
+ sp<Connection> *connection,
+ ConnectionId *pConnectionId,
+ uint32_t *pMsgId,
+ const StatusDescriptor** statusDescPtr,
+ const InvalidationDescriptor** invDescPtr) {
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);
+ status = mBufferPool.mObserver.open(id, statusDescPtr);
if (status == ResultStatus::OK) {
newConnection->initialize(accessor, id);
*connection = newConnection;
*pConnectionId = id;
+ *pMsgId = mBufferPool.mInvalidation.mInvalidationId;
+ mBufferPool.mInvalidationChannel.getDesc(invDescPtr);
+ mBufferPool.mInvalidation.onConnect(id, observer);
++sSeqId;
}
+
}
mBufferPool.processStatusMessages();
mBufferPool.cleanUp();
@@ -162,9 +177,11 @@
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);
+ mBufferPool.mInvalidation.onClose(connectionId);
// Since close# will be called after all works are finished, it is OK to
// evict unused buffers.
mBufferPool.cleanUp(true);
@@ -229,11 +246,30 @@
mBufferPool.cleanUp(clearCache);
}
+void Accessor::Impl::flush() {
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+ mBufferPool.flush(shared_from_this());
+}
+
+void Accessor::Impl::handleInvalidateAck() {
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+ mBufferPool.mInvalidation.onHandleAck();
+}
+
+bool Accessor::Impl::isValid() {
+ return mBufferPool.isValid();
+}
+
Accessor::Impl::Impl::BufferPool::BufferPool()
: mTimestampUs(getTimestampNow()),
mLastCleanUpUs(mTimestampUs),
mLastLogUs(mTimestampUs),
- mSeq(0) {}
+ mSeq(0),
+ mStartSeq(0) {
+ mValid = mInvalidationChannel.isValid();
+}
// Statistics helper
@@ -242,6 +278,8 @@
return int(total ? 0.5 + 100. * static_cast<S>(base) / total : 0);
}
+std::atomic<std::uint32_t> Accessor::Impl::BufferPool::Invalidation::sInvSeqId(0);
+
Accessor::Impl::Impl::BufferPool::~BufferPool() {
std::lock_guard<std::mutex> lock(mMutex);
ALOGD("Destruction - bufferpool %p "
@@ -255,6 +293,102 @@
percentage(mStats.mTotalTransfers - mStats.mTotalFetches, mStats.mTotalTransfers));
}
+void Accessor::Impl::BufferPool::Invalidation::onConnect(
+ ConnectionId conId, const sp<IObserver>& observer) {
+ mAcks[conId] = mInvalidationId; // starts from current invalidationId
+ mObservers.insert(std::make_pair(conId, observer));
+}
+
+void Accessor::Impl::BufferPool::Invalidation::onClose(ConnectionId conId) {
+ mAcks.erase(conId);
+ mObservers.erase(conId);
+}
+
+void Accessor::Impl::BufferPool::Invalidation::onAck(
+ ConnectionId conId,
+ uint32_t msgId) {
+ auto it = mAcks.find(conId);
+ if (it == mAcks.end() || isMessageLater(msgId, it->second)) {
+ mAcks[conId] = msgId;
+ }
+}
+
+void Accessor::Impl::BufferPool::Invalidation::onBufferInvalidated(
+ BufferId bufferId,
+ BufferInvalidationChannel &channel) {
+ for (auto it = mPendings.begin(); it != mPendings.end();) {
+ if (it->isInvalidated(bufferId)) {
+ uint32_t msgId = 0;
+ if (it->mNeedsAck) {
+ msgId = ++mInvalidationId;
+ if (msgId == 0) {
+ // wrap happens
+ msgId = ++mInvalidationId;
+ }
+ }
+ channel.postInvalidation(msgId, it->mFrom, it->mTo);
+ sInvalidator->addAccessor(mId, it->mImpl);
+ it = mPendings.erase(it);
+ continue;
+ }
+ ++it;
+ }
+}
+
+void Accessor::Impl::BufferPool::Invalidation::onInvalidationRequest(
+ bool needsAck,
+ uint32_t from,
+ uint32_t to,
+ size_t left,
+ BufferInvalidationChannel &channel,
+ const std::shared_ptr<Accessor::Impl> &impl) {
+ if (left == 0) {
+ uint32_t msgId = 0;
+ if (needsAck) {
+ msgId = ++mInvalidationId;
+ if (msgId == 0) {
+ // wrap happens
+ msgId = ++mInvalidationId;
+ }
+ }
+ ALOGV("bufferpool invalidation requested and queued");
+ channel.postInvalidation(msgId, from, to);
+ 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);
+ }
+}
+
+void Accessor::Impl::BufferPool::Invalidation::onHandleAck() {
+ if (mInvalidationId != 0) {
+ std::set<int> deads;
+ for (auto it = mAcks.begin(); it != mAcks.end(); ++it) {
+ if (it->second != mInvalidationId) {
+ const sp<IObserver> observer = mObservers[it->first];
+ if (observer) {
+ 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);
+ }
+ }
+ }
+ if (deads.size() > 0) {
+ for (auto it = deads.begin(); it != deads.end(); ++it) {
+ onClose(*it);
+ }
+ }
+ }
+ // All invalidation Ids are synced.
+ sInvalidator->delAccessor(mId);
+}
+
bool Accessor::Impl::BufferPool::handleOwnBuffer(
ConnectionId connectionId, BufferId bufferId) {
@@ -275,8 +409,15 @@
iter->second->mOwnerCount--;
if (iter->second->mOwnerCount == 0 &&
iter->second->mTransactionCount == 0) {
- mStats.onBufferUnused(iter->second->mAllocSize);
- mFreeBuffers.insert(bufferId);
+ if (!iter->second->mInvalidated) {
+ mStats.onBufferUnused(iter->second->mAllocSize);
+ mFreeBuffers.insert(bufferId);
+ } else {
+ mStats.onBufferUnused(iter->second->mAllocSize);
+ mStats.onBufferEvicted(iter->second->mAllocSize);
+ mBuffers.erase(iter);
+ mInvalidation.onBufferInvalidated(bufferId, mInvalidationChannel);
+ }
}
}
erase(&mUsingConnections, bufferId, connectionId);
@@ -352,8 +493,15 @@
bufferIter->second->mTransactionCount--;
if (bufferIter->second->mOwnerCount == 0
&& bufferIter->second->mTransactionCount == 0) {
- mStats.onBufferUnused(bufferIter->second->mAllocSize);
- mFreeBuffers.insert(message.bufferId);
+ if (!bufferIter->second->mInvalidated) {
+ mStats.onBufferUnused(bufferIter->second->mAllocSize);
+ mFreeBuffers.insert(message.bufferId);
+ } else {
+ mStats.onBufferUnused(bufferIter->second->mAllocSize);
+ mStats.onBufferEvicted(bufferIter->second->mAllocSize);
+ mBuffers.erase(bufferIter);
+ mInvalidation.onBufferInvalidated(message.bufferId, mInvalidationChannel);
+ }
}
mTransactions.erase(found);
}
@@ -400,7 +548,8 @@
ret = handleTransferResult(message);
break;
case BufferStatus::INVALIDATION_ACK:
- // TODO
+ mInvalidation.onAck(message.connectionId, message.bufferId);
+ ret = true;
break;
}
if (ret == false) {
@@ -423,8 +572,15 @@
if (bufferIter->second->mOwnerCount == 0 &&
bufferIter->second->mTransactionCount == 0) {
// TODO: handle freebuffer insert fail
- mStats.onBufferUnused(bufferIter->second->mAllocSize);
- mFreeBuffers.insert(bufferId);
+ if (!bufferIter->second->mInvalidated) {
+ mStats.onBufferUnused(bufferIter->second->mAllocSize);
+ mFreeBuffers.insert(bufferId);
+ } else {
+ mStats.onBufferUnused(bufferIter->second->mAllocSize);
+ mStats.onBufferEvicted(bufferIter->second->mAllocSize);
+ mBuffers.erase(bufferIter);
+ mInvalidation.onBufferInvalidated(bufferId, mInvalidationChannel);
+ }
}
}
}
@@ -446,8 +602,15 @@
if (bufferIter->second->mOwnerCount == 0 &&
bufferIter->second->mTransactionCount == 0) {
// TODO: handle freebuffer insert fail
- mStats.onBufferUnused(bufferIter->second->mAllocSize);
- mFreeBuffers.insert(bufferId);
+ if (!bufferIter->second->mInvalidated) {
+ mStats.onBufferUnused(bufferIter->second->mAllocSize);
+ mFreeBuffers.insert(bufferId);
+ } else {
+ mStats.onBufferUnused(bufferIter->second->mAllocSize);
+ mStats.onBufferEvicted(bufferIter->second->mAllocSize);
+ mBuffers.erase(bufferIter);
+ mInvalidation.onBufferInvalidated(bufferId, mInvalidationChannel);
+ }
}
mTransactions.erase(iter);
}
@@ -538,6 +701,130 @@
}
}
+void Accessor::Impl::BufferPool::invalidate(
+ bool needsAck, BufferId from, BufferId to,
+ const std::shared_ptr<Accessor::Impl> &impl) {
+ for (auto freeIt = mFreeBuffers.begin(); freeIt != mFreeBuffers.end();) {
+ if (isBufferInRange(from, to, *freeIt)) {
+ 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);
+ continue;
+ } else {
+ ALOGW("bufferpool inconsistent!");
+ }
+ }
+ ++freeIt;
+ }
+
+ size_t left = 0;
+ for (auto it = mBuffers.begin(); it != mBuffers.end(); ++it) {
+ if (isBufferInRange(from, to, it->first)) {
+ it->second->invalidate();
+ ++left;
+ }
+ }
+ mInvalidation.onInvalidationRequest(needsAck, from, to, left, mInvalidationChannel, impl);
+}
+
+void Accessor::Impl::BufferPool::flush(const std::shared_ptr<Accessor::Impl> &impl) {
+ BufferId from = mStartSeq;
+ 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);
+ }
+}
+
+void Accessor::Impl::invalidatorThread(
+ std::map<uint32_t, const std::weak_ptr<Accessor::Impl>> &accessors,
+ std::mutex &mutex,
+ std::condition_variable &cv,
+ bool &ready) {
+ while(true) {
+ std::map<uint32_t, const std::weak_ptr<Accessor::Impl>> copied;
+ {
+ std::unique_lock<std::mutex> lock(mutex);
+ if (!ready) {
+ cv.wait(lock);
+ }
+ copied.insert(accessors.begin(), accessors.end());
+ }
+ std::list<ConnectionId> erased;
+ for (auto it = copied.begin(); it != copied.end(); ++it) {
+ const std::shared_ptr<Accessor::Impl> impl = it->second.lock();
+ if (!impl) {
+ erased.push_back(it->first);
+ } else {
+ impl->handleInvalidateAck();
+ }
+ }
+ {
+ std::unique_lock<std::mutex> lock(mutex);
+ for (auto it = erased.begin(); it != erased.end(); ++it) {
+ accessors.erase(*it);
+ }
+ if (accessors.size() == 0) {
+ ready = false;
+ } else {
+ // prevent draining cpu.
+ lock.unlock();
+ std::this_thread::yield();
+ }
+ }
+ }
+}
+
+Accessor::Impl::AccessorInvalidator::AccessorInvalidator() : mReady(false) {
+ std::thread invalidator(
+ invalidatorThread,
+ std::ref(mAccessors),
+ std::ref(mMutex),
+ std::ref(mCv),
+ std::ref(mReady));
+ invalidator.detach();
+}
+
+void Accessor::Impl::AccessorInvalidator::addAccessor(
+ uint32_t accessorId, const std::weak_ptr<Accessor::Impl> &impl) {
+ bool notify = false;
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (mAccessors.find(accessorId) == mAccessors.end()) {
+ if (!mReady) {
+ mReady = true;
+ notify = true;
+ }
+ mAccessors.insert(std::make_pair(accessorId, impl));
+ ALOGV("buffer invalidation added bp:%u %d", accessorId, notify);
+ }
+ lock.unlock();
+ if (notify) {
+ mCv.notify_one();
+ }
+}
+
+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;
+ }
+}
+
+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
} // namespace bufferpool
diff --git a/media/bufferpool/2.0/AccessorImpl.h b/media/bufferpool/2.0/AccessorImpl.h
index 1d33880..b3faa96 100644
--- a/media/bufferpool/2.0/AccessorImpl.h
+++ b/media/bufferpool/2.0/AccessorImpl.h
@@ -19,6 +19,7 @@
#include <map>
#include <set>
+#include <condition_variable>
#include "Accessor.h"
namespace android {
@@ -33,15 +34,20 @@
/**
* 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);
~Impl();
ResultStatus connect(
- const sp<Accessor> &accessor, sp<Connection> *connection,
- ConnectionId *pConnectionId, const StatusDescriptor** fmqDescPtr);
+ const sp<Accessor> &accessor, const sp<IObserver> &observer,
+ sp<Connection> *connection,
+ ConnectionId *pConnectionId,
+ uint32_t *pMsgId,
+ const StatusDescriptor** statusDescPtr,
+ const InvalidationDescriptor** invDescPtr);
ResultStatus close(ConnectionId connectionId);
@@ -55,8 +61,16 @@
BufferId bufferId,
const native_handle_t** handle);
+ void flush();
+
void cleanUp(bool clearCache);
+ bool isValid();
+
+ void handleInvalidateAck();
+
+ static void createInvalidator();
+
private:
// ConnectionId = pid : (timestamp_created + seqId)
// in order to guarantee uniqueness for each connection
@@ -78,7 +92,10 @@
int64_t mLastCleanUpUs;
int64_t mLastLogUs;
BufferId mSeq;
+ BufferId mStartSeq;
+ bool mValid;
BufferStatusObserver mObserver;
+ BufferInvalidationChannel mInvalidationChannel;
std::map<ConnectionId, std::set<BufferId>> mUsingBuffers;
std::map<BufferId, std::set<ConnectionId>> mUsingConnections;
@@ -95,6 +112,54 @@
std::map<BufferId, std::unique_ptr<InternalBuffer>> mBuffers;
std::set<BufferId> mFreeBuffers;
+ struct Invalidation {
+ static std::atomic<std::uint32_t> sInvSeqId;
+
+ struct Pending {
+ bool mNeedsAck;
+ uint32_t mFrom;
+ uint32_t mTo;
+ size_t mLeft;
+ const std::weak_ptr<Accessor::Impl> mImpl;
+ Pending(bool needsAck, uint32_t from, uint32_t to, size_t left,
+ const std::shared_ptr<Accessor::Impl> &impl)
+ : mNeedsAck(needsAck),
+ mFrom(from),
+ mTo(to),
+ mLeft(left),
+ mImpl(impl)
+ {}
+
+ 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 sp<IObserver>> mObservers;
+ uint32_t mInvalidationId;
+ uint32_t mId;
+
+ Invalidation() : mInvalidationId(0), mId(sInvSeqId.fetch_add(1)) {}
+
+ void onConnect(ConnectionId conId, const sp<IObserver> &observer);
+
+ void onClose(ConnectionId conId);
+
+ void onAck(ConnectionId conId, uint32_t msgId);
+
+ void onBufferInvalidated(
+ BufferId bufferId,
+ BufferInvalidationChannel &channel);
+
+ void onInvalidationRequest(
+ bool needsAck, uint32_t from, uint32_t to, size_t left,
+ BufferInvalidationChannel &channel,
+ const std::shared_ptr<Accessor::Impl> &impl);
+
+ void onHandleAck();
+ } mInvalidation;
/// Buffer pool statistics which tracks allocation and transfer statistics.
struct Stats {
/// Total size of allocations which are used or available to use.
@@ -164,6 +229,15 @@
}
} mStats;
+ bool isValid() {
+ return mValid;
+ }
+
+ void invalidate(bool needsAck, BufferId from, BufferId to,
+ const std::shared_ptr<Accessor::Impl> &impl);
+
+ static void createInvalidator();
+
public:
/** Creates a buffer pool. */
BufferPool();
@@ -286,8 +360,33 @@
*/
void cleanUp(bool clearCache = false);
+ /**
+ * Processes pending buffer status messages and invalidate all current
+ * free buffers. Active buffers are invalidated after being inactive.
+ */
+ void flush(const std::shared_ptr<Accessor::Impl> &impl);
+
friend class Accessor::Impl;
} mBufferPool;
+
+ struct AccessorInvalidator {
+ std::map<uint32_t, const std::weak_ptr<Accessor::Impl>> mAccessors;
+ std::mutex mMutex;
+ std::condition_variable mCv;
+ bool mReady;
+
+ AccessorInvalidator();
+ void addAccessor(uint32_t accessorId, const std::weak_ptr<Accessor::Impl> &impl);
+ void delAccessor(uint32_t accessorId);
+ };
+
+ static std::unique_ptr<AccessorInvalidator> sInvalidator;
+
+ static void invalidatorThread(
+ std::map<uint32_t, const std::weak_ptr<Accessor::Impl>> &accessors,
+ std::mutex &mutex,
+ std::condition_variable &cv,
+ bool &ready);
};
} // namespace implementation
diff --git a/media/bufferpool/2.0/Android.bp b/media/bufferpool/2.0/Android.bp
index 413125a..cd4e06e 100644
--- a/media/bufferpool/2.0/Android.bp
+++ b/media/bufferpool/2.0/Android.bp
@@ -8,6 +8,7 @@
"BufferStatus.cpp",
"ClientManager.cpp",
"Connection.cpp",
+ "Observer.cpp",
],
export_include_dirs: [
"include",
diff --git a/media/bufferpool/2.0/BufferPoolClient.cpp b/media/bufferpool/2.0/BufferPoolClient.cpp
index 0f763f7..5564a13 100644
--- a/media/bufferpool/2.0/BufferPoolClient.cpp
+++ b/media/bufferpool/2.0/BufferPoolClient.cpp
@@ -36,9 +36,9 @@
class BufferPoolClient::Impl
: public std::enable_shared_from_this<BufferPoolClient::Impl> {
public:
- explicit Impl(const sp<Accessor> &accessor);
+ explicit Impl(const sp<Accessor> &accessor, const sp<IObserver> &observer);
- explicit Impl(const sp<IAccessor> &accessor);
+ explicit Impl(const sp<IAccessor> &accessor, const sp<IObserver> &observer);
bool isValid() {
return mValid;
@@ -58,6 +58,10 @@
bool isActive(int64_t *lastTransactionUs, bool clearCache);
+ void receiveInvalidation(uint32_t msgID);
+
+ ResultStatus flush();
+
ResultStatus allocate(const std::vector<uint8_t> ¶ms,
native_handle_t **handle,
std::shared_ptr<BufferPoolData> *buffer);
@@ -83,10 +87,14 @@
void trySyncFromRemote();
- bool syncReleased();
+ bool syncReleased(uint32_t msgId = 0);
void evictCaches(bool clearCache = false);
+ void invalidateBuffer(BufferId id);
+
+ void invalidateRange(BufferId from, BufferId to);
+
ResultStatus allocateBufferHandle(
const std::vector<uint8_t>& params, BufferId *bufferId,
native_handle_t **handle);
@@ -106,6 +114,7 @@
uint32_t mSeqId;
ConnectionId mConnectionId;
int64_t mLastEvictCacheUs;
+ std::unique_ptr<BufferInvalidationListener> mInvalidationListener;
// CachedBuffers
struct BufferCache {
@@ -130,12 +139,16 @@
} mCache;
// FMQ - release notifier
- struct {
+ struct ReleaseCache {
std::mutex mLock;
// TODO: use only one list?(using one list may dealy sending messages?)
std::list<BufferId> mReleasingIds;
std::list<BufferId> mReleasedIds;
+ uint32_t mInvalidateId; // TODO: invalidation ACK to bufferpool
+ bool mInvalidateAck;
std::unique_ptr<BufferStatusChannel> mStatusChannel;
+
+ ReleaseCache() : mInvalidateId(0), mInvalidateAck(true) {}
} mReleasing;
// This lock is held during synchronization from remote side.
@@ -162,7 +175,6 @@
struct BufferPoolClient::Impl::ClientBuffer {
private:
- bool mInvalidated; // TODO: implement
int64_t mExpireUs;
bool mHasCache;
ConnectionId mConnectionId;
@@ -177,9 +189,8 @@
public:
ClientBuffer(
ConnectionId connectionId, BufferId id, native_handle_t *handle)
- : mInvalidated(false), mHasCache(false),
- mConnectionId(connectionId), mId(id), mHandle(handle) {
- (void)mInvalidated;
+ : mHasCache(false), mConnectionId(connectionId),
+ mId(id), mHandle(handle) {
mExpireUs = getTimestampNow() + kCacheTtlUs;
}
@@ -190,6 +201,10 @@
}
}
+ BufferId id() const {
+ return mId;
+ }
+
bool expire() const {
int64_t now = getTimestampNow();
return now >= mExpireUs;
@@ -244,41 +259,53 @@
}
};
-BufferPoolClient::Impl::Impl(const sp<Accessor> &accessor)
+BufferPoolClient::Impl::Impl(const sp<Accessor> &accessor, const sp<IObserver> &observer)
: mLocal(true), mValid(false), mAccessor(accessor), mSeqId(0),
mLastEvictCacheUs(getTimestampNow()) {
- const StatusDescriptor *fmqDesc;
+ const StatusDescriptor *statusDesc;
+ const InvalidationDescriptor *invDesc;
ResultStatus status = accessor->connect(
- &mLocalConnection, &mConnectionId, &fmqDesc, true);
+ observer, true,
+ &mLocalConnection, &mConnectionId, &mReleasing.mInvalidateId,
+ &statusDesc, &invDesc);
if (status == ResultStatus::OK) {
mReleasing.mStatusChannel =
- std::make_unique<BufferStatusChannel>(*fmqDesc);
+ std::make_unique<BufferStatusChannel>(*statusDesc);
+ mInvalidationListener =
+ std::make_unique<BufferInvalidationListener>(*invDesc);
mValid = mReleasing.mStatusChannel &&
- mReleasing.mStatusChannel->isValid();
+ mReleasing.mStatusChannel->isValid() &&
+ mInvalidationListener &&
+ mInvalidationListener->isValid();
}
}
-BufferPoolClient::Impl::Impl(const sp<IAccessor> &accessor)
+BufferPoolClient::Impl::Impl(const sp<IAccessor> &accessor, const sp<IObserver> &observer)
: mLocal(false), mValid(false), mAccessor(accessor), mSeqId(0),
mLastEvictCacheUs(getTimestampNow()) {
bool valid = false;
- sp<IObserver> observer; // TODO
sp<IConnection>& outConnection = mRemoteConnection;
ConnectionId& id = mConnectionId;
+ uint32_t& outMsgId = mReleasing.mInvalidateId;
std::unique_ptr<BufferStatusChannel>& outChannel =
mReleasing.mStatusChannel;
+ std::unique_ptr<BufferInvalidationListener>& outObserver =
+ mInvalidationListener;
Return<void> transResult = accessor->connect(
observer,
- [&valid, &outConnection, &id, &outChannel]
+ [&valid, &outConnection, &id, &outMsgId, &outChannel, &outObserver]
(ResultStatus status, sp<IConnection> connection,
- ConnectionId connectionId, const StatusDescriptor& desc,
+ ConnectionId connectionId, uint32_t msgId,
+ const StatusDescriptor& statusDesc,
const InvalidationDescriptor& invDesc) {
- (void) invDesc;
if (status == ResultStatus::OK) {
outConnection = connection;
id = connectionId;
- outChannel = std::make_unique<BufferStatusChannel>(desc);
- if (outChannel && outChannel->isValid()) {
+ outMsgId = msgId;
+ outChannel = std::make_unique<BufferStatusChannel>(statusDesc);
+ outObserver = std::make_unique<BufferInvalidationListener>(invDesc);
+ if (outChannel && outChannel->isValid() &&
+ outObserver && outObserver->isValid()) {
valid = true;
}
}
@@ -302,6 +329,24 @@
return active;
}
+void BufferPoolClient::Impl::receiveInvalidation(uint32_t messageId) {
+ std::lock_guard<std::mutex> lock(mCache.mLock);
+ syncReleased(messageId);
+ // TODO: evict cache required?
+}
+
+ResultStatus BufferPoolClient::Impl::flush() {
+ if (!mLocal || !mLocalConnection || !mValid) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ {
+ std::unique_lock<std::mutex> lock(mCache.mLock);
+ syncReleased();
+ evictCaches();
+ return mLocalConnection->flush();
+ }
+}
+
ResultStatus BufferPoolClient::Impl::allocate(
const std::vector<uint8_t> ¶ms,
native_handle_t **pHandle,
@@ -455,6 +500,11 @@
bool BufferPoolClient::Impl::postSend(
BufferId bufferId, ConnectionId receiver,
TransactionId *transactionId, int64_t *timestampUs) {
+ {
+ // TODO: don't need to call syncReleased every time
+ std::lock_guard<std::mutex> lock(mCache.mLock);
+ syncReleased();
+ }
bool ret = false;
bool needsSync = false;
{
@@ -538,34 +588,77 @@
}
// 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();
+bool BufferPoolClient::Impl::syncReleased(uint32_t messageId) {
+ bool cleared = false;
+ {
+ 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 release status inconsitent!",
- (long long)mConnectionId);
+ ALOGW("client %lld cache status inconsitent!", (long long)mConnectionId);
}
+ }
+ mReleasing.mReleasedIds.clear();
+ cleared = true;
+ }
+ }
+ std::vector<BufferInvalidationMessage> invalidations;
+ mInvalidationListener->getInvalidations(invalidations);
+ uint32_t lastMsgId = 0;
+ if (invalidations.size() > 0) {
+ for (auto it = invalidations.begin(); it != invalidations.end(); ++it) {
+ if (it->messageId != 0) {
+ lastMsgId = it->messageId;
+ }
+ if (it->fromBufferId == it->toBufferId) {
+ // TODO: handle fromBufferId = UINT32_MAX
+ invalidateBuffer(it->fromBufferId);
} else {
- // should not happen!
- ALOGW("client %lld cache status inconsitent!", (long long)mConnectionId);
+ invalidateRange(it->fromBufferId, it->toBufferId);
}
}
- mReleasing.mReleasedIds.clear();
- return true;
}
- return false;
+ {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ if (lastMsgId != 0) {
+ if (isMessageLater(lastMsgId, mReleasing.mInvalidateId)) {
+ mReleasing.mInvalidateId = lastMsgId;
+ mReleasing.mInvalidateAck = false;
+ }
+ } else if (messageId != 0) {
+ // messages are drained.
+ if (isMessageLater(messageId, mReleasing.mInvalidateId)) {
+ mReleasing.mInvalidateId = messageId;
+ mReleasing.mInvalidateAck = true;
+ }
+ }
+ if (!mReleasing.mInvalidateAck) {
+ // post ACK
+ mReleasing.mStatusChannel->postBufferInvalidateAck(
+ mConnectionId,
+ mReleasing.mInvalidateId, &mReleasing.mInvalidateAck);
+ ALOGV("client %lld invalidateion ack (%d) %u",
+ (long long)mConnectionId,
+ mReleasing.mInvalidateAck, mReleasing.mInvalidateId);
+ }
+ }
+ return cleared;
}
// should have mCache.mLock
@@ -587,6 +680,49 @@
}
}
+// should have mCache.mLock
+void BufferPoolClient::Impl::invalidateBuffer(BufferId id) {
+ for (auto it = mCache.mBuffers.begin(); it != mCache.mBuffers.end(); ++it) {
+ if (id == it->second->id()) {
+ if (!it->second->hasCache()) {
+ mCache.mBuffers.erase(it);
+ ALOGV("cache invalidated %lld : buffer %u",
+ (long long)mConnectionId, id);
+ } else {
+ ALOGW("Inconsitent invalidation %lld : activer buffer!! %u",
+ (long long)mConnectionId, (unsigned int)id);
+ }
+ break;
+ }
+ }
+}
+
+// should have mCache.mLock
+void BufferPoolClient::Impl::invalidateRange(BufferId from, BufferId to) {
+ size_t invalidated = 0;
+ for (auto it = mCache.mBuffers.begin(); it != mCache.mBuffers.end();) {
+ if (!it->second->hasCache()) {
+ BufferId bid = it->second->id();
+ if (from < to) {
+ if (from <= bid && bid < to) {
+ ++invalidated;
+ it = mCache.mBuffers.erase(it);
+ continue;
+ }
+ } else {
+ if (from <= bid || bid < to) {
+ ++invalidated;
+ it = mCache.mBuffers.erase(it);
+ continue;
+ }
+ }
+ }
+ ++it;
+ }
+ ALOGV("cache invalidated %lld : # of invalidated %zu",
+ (long long)mConnectionId, invalidated);
+}
+
ResultStatus BufferPoolClient::Impl::allocateBufferHandle(
const std::vector<uint8_t>& params, BufferId *bufferId,
native_handle_t** handle) {
@@ -629,12 +765,14 @@
}
-BufferPoolClient::BufferPoolClient(const sp<Accessor> &accessor) {
- mImpl = std::make_shared<Impl>(accessor);
+BufferPoolClient::BufferPoolClient(const sp<Accessor> &accessor,
+ const sp<IObserver> &observer) {
+ mImpl = std::make_shared<Impl>(accessor, observer);
}
-BufferPoolClient::BufferPoolClient(const sp<IAccessor> &accessor) {
- mImpl = std::make_shared<Impl>(accessor);
+BufferPoolClient::BufferPoolClient(const sp<IAccessor> &accessor,
+ const sp<IObserver> &observer) {
+ mImpl = std::make_shared<Impl>(accessor, observer);
}
BufferPoolClient::~BufferPoolClient() {
@@ -672,6 +810,20 @@
return ResultStatus::CRITICAL_ERROR;
}
+void BufferPoolClient::receiveInvalidation(uint32_t msgId) {
+ ALOGV("bufferpool client recv inv %u", msgId);
+ if (isValid()) {
+ mImpl->receiveInvalidation(msgId);
+ }
+}
+
+ResultStatus BufferPoolClient::flush() {
+ if (isValid()) {
+ return mImpl->flush();
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
ResultStatus BufferPoolClient::allocate(
const std::vector<uint8_t> ¶ms,
native_handle_t **handle,
diff --git a/media/bufferpool/2.0/BufferPoolClient.h b/media/bufferpool/2.0/BufferPoolClient.h
index 1889ea3..e8d9ae6 100644
--- a/media/bufferpool/2.0/BufferPoolClient.h
+++ b/media/bufferpool/2.0/BufferPoolClient.h
@@ -49,7 +49,8 @@
* Creates a buffer pool client from a local buffer pool
* (via ClientManager#create).
*/
- explicit BufferPoolClient(const sp<Accessor> &accessor);
+ explicit BufferPoolClient(const sp<Accessor> &accessor,
+ const sp<IObserver> &observer);
/**
* Creates a buffer pool client from a remote buffer pool
@@ -57,7 +58,8 @@
* Note: A buffer pool client created with remote buffer pool cannot
* allocate a buffer.
*/
- explicit BufferPoolClient(const sp<IAccessor> &accessor);
+ explicit BufferPoolClient(const sp<IAccessor> &accessor,
+ const sp<IObserver> &observer);
/** Destructs a buffer pool client. */
~BufferPoolClient();
@@ -73,6 +75,10 @@
ResultStatus getAccessor(sp<IAccessor> *accessor);
+ void receiveInvalidation(uint32_t msgId);
+
+ ResultStatus flush();
+
ResultStatus allocate(const std::vector<uint8_t> ¶ms,
native_handle_t **handle,
std::shared_ptr<BufferPoolData> *buffer);
@@ -92,6 +98,7 @@
std::shared_ptr<Impl> mImpl;
friend struct ClientManager;
+ friend struct Observer;
};
} // namespace implementation
diff --git a/media/bufferpool/2.0/BufferStatus.cpp b/media/bufferpool/2.0/BufferStatus.cpp
index 0d3f5a3..6937260 100644
--- a/media/bufferpool/2.0/BufferStatus.cpp
+++ b/media/bufferpool/2.0/BufferStatus.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "BufferPoolStatus"
//#define LOG_NDEBUG 0
+#include <thread>
#include <time.h>
#include "BufferStatus.h"
@@ -37,6 +38,18 @@
return stamp;
}
+bool isMessageLater(uint32_t curMsgId, uint32_t prevMsgId) {
+ return curMsgId != prevMsgId && curMsgId - prevMsgId < prevMsgId - curMsgId;
+}
+
+bool isBufferInRange(BufferId from, BufferId to, BufferId bufferId) {
+ if (from < to) {
+ return from <= bufferId && bufferId < to;
+ } else { // wrap happens
+ return from <= bufferId || bufferId < to;
+ }
+}
+
static constexpr int kNumElementsInQueue = 1024*16;
static constexpr int kMinElementsToSyncInQueue = 128;
@@ -139,6 +152,29 @@
}
}
+void BufferStatusChannel::postBufferInvalidateAck(
+ ConnectionId connectionId,
+ uint32_t invalidateId,
+ bool *invalidated) {
+ if (mValid && !*invalidated) {
+ size_t avail = mBufferStatusQueue->availableToWrite();
+ if (avail > 0) {
+ BufferStatusMessage message;
+ message.newStatus = BufferStatus::INVALIDATION_ACK;
+ message.bufferId = invalidateId;
+ 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;
+ }
+ *invalidated = true;
+ }
+ }
+}
+
bool BufferStatusChannel::postBufferStatusMessage(
TransactionId transactionId, BufferId bufferId,
BufferStatus status, ConnectionId connectionId, ConnectionId targetId,
@@ -182,6 +218,83 @@
return false;
}
+BufferInvalidationListener::BufferInvalidationListener(
+ const InvalidationDescriptor &fmqDesc) {
+ std::unique_ptr<BufferInvalidationQueue> queue =
+ std::make_unique<BufferInvalidationQueue>(fmqDesc);
+ if (!queue || queue->isValid() == false) {
+ mValid = false;
+ return;
+ }
+ mValid = true;
+ mBufferInvalidationQueue = std::move(queue);
+ // drain previous messages
+ size_t avail = std::min(
+ mBufferInvalidationQueue->availableToRead(), (size_t) kNumElementsInQueue);
+ std::vector<BufferInvalidationMessage> temp(avail);
+ if (avail > 0) {
+ mBufferInvalidationQueue->read(temp.data(), avail);
+ }
+}
+
+void BufferInvalidationListener::getInvalidations(
+ std::vector<BufferInvalidationMessage> &messages) {
+ // Try twice in case of overflow.
+ // TODO: handling overflow though it may not happen.
+ for (int i = 0; i < 2; ++i) {
+ size_t avail = std::min(
+ mBufferInvalidationQueue->availableToRead(), (size_t) kNumElementsInQueue);
+ if (avail > 0) {
+ std::vector<BufferInvalidationMessage> temp(avail);
+ if (mBufferInvalidationQueue->read(temp.data(), avail)) {
+ messages.reserve(messages.size() + avail);
+ for (auto it = temp.begin(); it != temp.end(); ++it) {
+ messages.push_back(*it);
+ }
+ break;
+ }
+ } else {
+ return;
+ }
+ }
+}
+
+bool BufferInvalidationListener::isValid() {
+ return mValid;
+}
+
+BufferInvalidationChannel::BufferInvalidationChannel()
+ : mValid(true),
+ mBufferInvalidationQueue(
+ std::make_unique<BufferInvalidationQueue>(kNumElementsInQueue, true)) {
+ if (!mBufferInvalidationQueue || mBufferInvalidationQueue->isValid() == false) {
+ mValid = false;
+ }
+}
+
+bool BufferInvalidationChannel::isValid() {
+ return mValid;
+}
+
+void BufferInvalidationChannel::getDesc(const InvalidationDescriptor **fmqDescPtr) {
+ if (mValid) {
+ *fmqDescPtr = mBufferInvalidationQueue->getDesc();
+ } else {
+ *fmqDescPtr = nullptr;
+ }
+}
+
+void BufferInvalidationChannel::postInvalidation(
+ uint32_t msgId, BufferId fromId, BufferId toId) {
+ BufferInvalidationMessage message;
+
+ message.messageId = msgId;
+ message.fromBufferId = fromId;
+ message.toBufferId = toId;
+ // TODO: handle failure (it does not happen normally.)
+ mBufferInvalidationQueue->write(&message);
+}
+
} // namespace implementation
} // namespace V2_0
} // namespace bufferpool
diff --git a/media/bufferpool/2.0/BufferStatus.h b/media/bufferpool/2.0/BufferStatus.h
index 777a320..fa65838 100644
--- a/media/bufferpool/2.0/BufferStatus.h
+++ b/media/bufferpool/2.0/BufferStatus.h
@@ -37,9 +37,13 @@
/** Returns monotonic timestamp in Us since fixed point in time. */
int64_t getTimestampNow();
+bool isMessageLater(uint32_t curMsgId, uint32_t prevMsgId);
+
+bool isBufferInRange(BufferId from, BufferId to, BufferId bufferId);
+
/**
- * A collection of FMQ for a buffer pool. buffer ownership/status change
- * messages are sent via the FMQs from the clients.
+ * A collection of buffer status message FMQ for a buffer pool. buffer
+ * ownership/status change messages are sent via the FMQs from the clients.
*/
class BufferStatusObserver {
private:
@@ -47,7 +51,8 @@
mBufferStatusQueues;
public:
- /** Creates an FMQ for the specified connection(client).
+ /** Creates a buffer status message FMQ for the specified
+ * connection(client).
*
* @param connectionId connection Id of the specified client.
* @param fmqDescPtr double ptr of created FMQ's descriptor.
@@ -58,7 +63,8 @@
*/
ResultStatus open(ConnectionId id, const StatusDescriptor** fmqDescPtr);
- /** Closes an FMQ for the specified connection(client).
+ /** Closes a buffer status message FMQ for the specified
+ * connection(client).
*
* @param connectionId connection Id of the specified client.
*
@@ -75,8 +81,8 @@
};
/**
- * An FMQ for a buffer pool client. Buffer ownership/status change messages
- * are sent via the fmq to the buffer pool.
+ * A buffer status message FMQ for a buffer pool client. Buffer ownership/status
+ * change messages are sent via the fmq to the buffer pool.
*/
class BufferStatusChannel {
private:
@@ -85,7 +91,8 @@
public:
/**
- * Connects to an FMQ from a descriptor of the created FMQ.
+ * Connects to a buffer status message FMQ from a descriptor of
+ * the created FMQ.
*
* @param fmqDesc Descriptor of the created FMQ.
*/
@@ -131,6 +138,86 @@
ConnectionId connectionId,
ConnectionId targetId,
std::list<BufferId> &pending, std::list<BufferId> &posted);
+
+ /**
+ * Posts a buffer invaliadation messge to the buffer pool.
+ *
+ * @param connectionId connection Id of the client.
+ * @param invalidateId invalidation ack to the buffer pool.
+ * if invalidation id is zero, the ack will not be
+ * posted.
+ * @param invalidated sets {@code true} only when the invalidation ack is
+ * posted.
+ */
+ void postBufferInvalidateAck(
+ ConnectionId connectionId,
+ uint32_t invalidateId,
+ bool *invalidated);
+};
+
+/**
+ * A buffer invalidation FMQ for a buffer pool client. Buffer invalidation
+ * messages are received via the fmq from the buffer pool. Buffer invalidation
+ * messages are handled as soon as possible.
+ */
+class BufferInvalidationListener {
+private:
+ bool mValid;
+ std::unique_ptr<BufferInvalidationQueue> mBufferInvalidationQueue;
+
+public:
+ /**
+ * Connects to a buffer invalidation FMQ from a descriptor of the created FMQ.
+ *
+ * @param fmqDesc Descriptor of the created FMQ.
+ */
+ BufferInvalidationListener(const InvalidationDescriptor &fmqDesc);
+
+ /** Retrieves all pending buffer invalidation messages from the buffer pool.
+ *
+ * @param messages retrieved pending messages.
+ */
+ void getInvalidations(std::vector<BufferInvalidationMessage> &messages);
+
+ /** Returns whether the FMQ is connected succesfully. */
+ bool isValid();
+};
+
+/**
+ * A buffer invalidation FMQ for a buffer pool. A buffer pool will send buffer
+ * invalidation messages to the clients via the FMQ. The FMQ is shared among
+ * buffer pool clients.
+ */
+class BufferInvalidationChannel {
+private:
+ bool mValid;
+ std::unique_ptr<BufferInvalidationQueue> mBufferInvalidationQueue;
+
+public:
+ /**
+ * Creates a buffer invalidation FMQ for a buffer pool.
+ */
+ BufferInvalidationChannel();
+
+ /** Returns whether the FMQ is connected succesfully. */
+ bool isValid();
+
+ /**
+ * Retrieves the descriptor of a buffer invalidation FMQ. the descriptor may
+ * be passed to the client for buffer invalidation handling.
+ *
+ * @param fmqDescPtr double ptr of created FMQ's descriptor.
+ */
+ void getDesc(const InvalidationDescriptor **fmqDescPtr);
+
+ /** Posts a buffer invalidation for invalidated buffers.
+ *
+ * @param msgId Invalidation message id which is used when clients send
+ * acks back via BufferStatusMessage
+ * @param fromId The start bufferid of the invalidated buffers(inclusive)
+ * @param toId The end bufferId of the invalidated buffers(inclusive)
+ */
+ void postInvalidation(uint32_t msgId, BufferId fromId, BufferId toId);
};
} // namespace implementation
diff --git a/media/bufferpool/2.0/ClientManager.cpp b/media/bufferpool/2.0/ClientManager.cpp
index eeaf093..c31d313 100644
--- a/media/bufferpool/2.0/ClientManager.cpp
+++ b/media/bufferpool/2.0/ClientManager.cpp
@@ -23,6 +23,8 @@
#include <unistd.h>
#include <utils/Log.h>
#include "BufferPoolClient.h"
+#include "Observer.h"
+#include "Accessor.h"
namespace android {
namespace hardware {
@@ -106,6 +108,8 @@
ResultStatus close(ConnectionId connectionId);
+ ResultStatus flush(ConnectionId connectionId);
+
ResultStatus allocate(ConnectionId connectionId,
const std::vector<uint8_t> ¶ms,
native_handle_t **handle,
@@ -153,10 +157,13 @@
mClients;
} mActive;
+ sp<Observer> mObserver;
+
ClientManagerCookieHolder mRemoteClientCookies;
};
-ClientManager::Impl::Impl() {}
+ClientManager::Impl::Impl()
+ : mObserver(new Observer()) {}
ResultStatus ClientManager::Impl::registerSender(
const sp<IAccessor> &accessor, ConnectionId *pConnectionId) {
@@ -185,7 +192,7 @@
lock.unlock();
ResultStatus result = ResultStatus::OK;
const std::shared_ptr<BufferPoolClient> client =
- std::make_shared<BufferPoolClient>(accessor);
+ std::make_shared<BufferPoolClient>(accessor, mObserver);
lock.lock();
if (!client) {
result = ResultStatus::NO_MEMORY;
@@ -197,6 +204,7 @@
const std::weak_ptr<BufferPoolClient> wclient = client;
mCache.mClients.push_back(std::make_pair(accessor, wclient));
ConnectionId conId = client->getConnectionId();
+ mObserver->addClient(conId, wclient);
{
std::lock_guard<std::mutex> lock(mActive.mMutex);
mActive.mClients.insert(std::make_pair(conId, client));
@@ -266,8 +274,9 @@
if (!accessor || !accessor->isValid()) {
return ResultStatus::CRITICAL_ERROR;
}
+ // TODO: observer is local. use direct call instead of hidl call.
std::shared_ptr<BufferPoolClient> client =
- std::make_shared<BufferPoolClient>(accessor);
+ std::make_shared<BufferPoolClient>(accessor, mObserver);
if (!client || !client->isValid()) {
return ResultStatus::CRITICAL_ERROR;
}
@@ -280,6 +289,7 @@
const std::weak_ptr<BufferPoolClient> wclient = client;
mCache.mClients.push_back(std::make_pair(accessor, wclient));
ConnectionId conId = client->getConnectionId();
+ mObserver->addClient(conId, wclient);
{
std::lock_guard<std::mutex> lock(mActive.mMutex);
mActive.mClients.insert(std::make_pair(conId, client));
@@ -291,12 +301,13 @@
}
ResultStatus ClientManager::Impl::close(ConnectionId connectionId) {
- std::lock_guard<std::mutex> lock1(mCache.mMutex);
- std::lock_guard<std::mutex> lock2(mActive.mMutex);
+ std::unique_lock<std::mutex> lock1(mCache.mMutex);
+ std::unique_lock<std::mutex> lock2(mActive.mMutex);
auto it = mActive.mClients.find(connectionId);
if (it != mActive.mClients.end()) {
sp<IAccessor> accessor;
it->second->getAccessor(&accessor);
+ std::shared_ptr<BufferPoolClient> closing = it->second;
mActive.mClients.erase(connectionId);
for (auto cit = mCache.mClients.begin(); cit != mCache.mClients.end();) {
// clean up dead client caches
@@ -307,11 +318,27 @@
cit++;
}
}
+ lock2.unlock();
+ lock1.unlock();
+ closing->flush();
return ResultStatus::OK;
}
return ResultStatus::NOT_FOUND;
}
+ResultStatus ClientManager::Impl::flush(ConnectionId connectionId) {
+ 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->flush();
+}
+
ResultStatus ClientManager::Impl::allocate(
ConnectionId connectionId, const std::vector<uint8_t> ¶ms,
native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
@@ -427,6 +454,7 @@
if (!sInstance) {
sInstance = new ClientManager();
}
+ Accessor::createInvalidator();
return sInstance;
}
@@ -461,6 +489,13 @@
return ResultStatus::CRITICAL_ERROR;
}
+ResultStatus ClientManager::flush(ConnectionId connectionId) {
+ if (mImpl) {
+ return mImpl->flush(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) {
diff --git a/media/bufferpool/2.0/Connection.cpp b/media/bufferpool/2.0/Connection.cpp
index cd837a1..6bd0e79 100644
--- a/media/bufferpool/2.0/Connection.cpp
+++ b/media/bufferpool/2.0/Connection.cpp
@@ -60,6 +60,13 @@
}
}
+ResultStatus Connection::flush() {
+ if (mInitialized && mAccessor) {
+ return mAccessor->flush();
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
ResultStatus Connection::allocate(
const std::vector<uint8_t> ¶ms, BufferId *bufferId,
const native_handle_t **handle) {
diff --git a/media/bufferpool/2.0/Connection.h b/media/bufferpool/2.0/Connection.h
index e2b47f1..8507749 100644
--- a/media/bufferpool/2.0/Connection.h
+++ b/media/bufferpool/2.0/Connection.h
@@ -44,6 +44,11 @@
Return<void> fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) override;
/**
+ * Invalidates all buffers which are active and/or are ready to be recycled.
+ */
+ ResultStatus flush();
+
+ /**
* 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).
diff --git a/media/bufferpool/2.0/Observer.cpp b/media/bufferpool/2.0/Observer.cpp
new file mode 100644
index 0000000..5b23160
--- /dev/null
+++ b/media/bufferpool/2.0/Observer.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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 "Observer.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+Observer::Observer() {
+}
+
+Observer::~Observer() {
+}
+
+// Methods from ::android::hardware::media::bufferpool::V2_0::IObserver follow.
+Return<void> Observer::onMessage(int64_t connectionId, uint32_t msgId) {
+ std::unique_lock<std::mutex> lock(mLock);
+ auto it = mClients.find(connectionId);
+ if (it != mClients.end()) {
+ const std::shared_ptr<BufferPoolClient> client = it->second.lock();
+ if (!client) {
+ mClients.erase(it);
+ } else {
+ lock.unlock();
+ client->receiveInvalidation(msgId);
+ }
+ }
+ return Void();
+}
+
+void Observer::addClient(ConnectionId connectionId,
+ const std::weak_ptr<BufferPoolClient> &wclient) {
+ std::lock_guard<std::mutex> lock(mLock);
+ for (auto it = mClients.begin(); it != mClients.end();) {
+ if (!it->second.lock() || it->first == connectionId) {
+ it = mClients.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ mClients.insert(std::make_pair(connectionId, wclient));
+
+}
+
+void Observer::delClient(ConnectionId connectionId) {
+ std::lock_guard<std::mutex> lock(mLock);
+ mClients.erase(connectionId);
+}
+
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/bufferpool/2.0/Observer.h b/media/bufferpool/2.0/Observer.h
new file mode 100644
index 0000000..42bd7c1
--- /dev/null
+++ b/media/bufferpool/2.0/Observer.h
@@ -0,0 +1,67 @@
+/*
+ * 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_V2_0_OBSERVER_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_OBSERVER_H
+
+#include <android/hardware/media/bufferpool/2.0/IObserver.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include "BufferPoolClient.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_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 Observer : public IObserver {
+ // Methods from ::android::hardware::media::bufferpool::V2_0::IObserver follow.
+ Return<void> onMessage(int64_t connectionId, uint32_t msgId) override;
+
+ ~Observer();
+
+ void addClient(ConnectionId connectionId,
+ const std::weak_ptr<BufferPoolClient> &wclient);
+
+ void delClient(ConnectionId connectionId);
+
+private:
+ Observer();
+
+ friend struct ClientManager;
+
+ std::mutex mLock;
+ std::map<ConnectionId, const std::weak_ptr<BufferPoolClient>> mClients;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_OBSERVER_H
diff --git a/media/bufferpool/2.0/include/bufferpool/ClientManager.h b/media/bufferpool/2.0/include/bufferpool/ClientManager.h
index cfc3bc3..953c304 100644
--- a/media/bufferpool/2.0/include/bufferpool/ClientManager.h
+++ b/media/bufferpool/2.0/include/bufferpool/ClientManager.h
@@ -92,6 +92,18 @@
ResultStatus close(ConnectionId connectionId);
/**
+ * Evicts cached allocations. If it's local connection, release the
+ * previous allocations and do not recycle current active allocations.
+ *
+ * @param connectionId The id of the connection.
+ *
+ * @return OK when the connection is resetted.
+ * NOT_FOUND when the specified connection was not found.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus flush(ConnectionId connectionId);
+
+ /**
* Allocates a buffer from the specified connection.
*
* @param connectionId The id of the connection.
diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp
index fcfdaff..be6d9fd 100644
--- a/media/extractors/flac/FLACExtractor.cpp
+++ b/media/extractors/flac/FLACExtractor.cpp
@@ -622,6 +622,8 @@
AMediaFormat_setInt32(mTrackMetadata,
AMEDIAFORMAT_KEY_SAMPLE_RATE, getSampleRate());
AMediaFormat_setInt32(mTrackMetadata,
+ AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, getBitsPerSample());
+ AMediaFormat_setInt32(mTrackMetadata,
AMEDIAFORMAT_KEY_PCM_ENCODING, kAudioEncodingPcm16bit);
// sample rate is non-zero, so division by zero not possible
AMediaFormat_setInt64(mTrackMetadata,
diff --git a/media/extractors/mkv/Android.bp b/media/extractors/mkv/Android.bp
index 681fd35..ae108ec 100644
--- a/media/extractors/mkv/Android.bp
+++ b/media/extractors/mkv/Android.bp
@@ -12,6 +12,7 @@
shared_libs: [
"liblog",
"libmediaextractor",
+ "libmediandk",
],
static_libs: [
diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp
index e15cbdb..1fdac05 100644
--- a/media/extractors/mkv/MatroskaExtractor.cpp
+++ b/media/extractors/mkv/MatroskaExtractor.cpp
@@ -127,15 +127,15 @@
BlockIterator &operator=(const BlockIterator &);
};
-struct MatroskaSource : public MediaTrackHelper {
+struct MatroskaSource : public MediaTrackHelperV2 {
MatroskaSource(MatroskaExtractor *extractor, size_t index);
- 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(
+ virtual media_status_t read(
MediaBufferBase **buffer, const ReadOptions *options);
protected:
@@ -161,7 +161,7 @@
status_t advance();
status_t setWebmBlockCryptoInfo(MediaBufferBase *mbuf);
- status_t readBlock();
+ media_status_t readBlock();
void clearPendingFrames();
MatroskaSource(const MatroskaSource &);
@@ -226,43 +226,30 @@
index),
mNALSizeLen(-1) {
MatroskaExtractor::TrackInfo &trackInfo = mExtractor->mTracks.editItemAt(index);
- MetaDataBase &meta = trackInfo.mMeta;
+ AMediaFormat *meta = trackInfo.mMeta;
const char *mime;
- CHECK(meta.findCString(kKeyMIMEType, &mime));
+ CHECK(AMediaFormat_getString(meta, AMEDIAFORMAT_KEY_MIME, &mime));
mIsAudio = !strncasecmp("audio/", mime, 6);
if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
mType = AVC;
- uint32_t dummy;
- const uint8_t *avcc;
- size_t avccSize;
int32_t nalSizeLen = trackInfo.mNalLengthSize;
- if (nalSizeLen >= 0) {
- if (nalSizeLen <= 4) {
- mNALSizeLen = nalSizeLen;
- }
- } else if (meta.findData(kKeyAVCC, &dummy, (const void **)&avcc, &avccSize)
- && avccSize >= 5u) {
- mNALSizeLen = 1 + (avcc[4] & 3);
- ALOGV("mNALSizeLen = %zd", mNALSizeLen);
+ if (nalSizeLen >= 0 && nalSizeLen <= 4) {
+ mNALSizeLen = nalSizeLen;
} else {
- ALOGE("No mNALSizeLen");
+ ALOGE("No AVC mNALSizeLen");
}
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) {
mType = HEVC;
- uint32_t dummy;
- const uint8_t *hvcc;
- size_t hvccSize;
- if (meta.findData(kKeyHVCC, &dummy, (const void **)&hvcc, &hvccSize)
- && hvccSize >= 22u) {
- mNALSizeLen = 1 + (hvcc[14+7] & 3);
- ALOGV("mNALSizeLen = %zu", mNALSizeLen);
+ int32_t nalSizeLen = trackInfo.mNalLengthSize;
+ if (nalSizeLen >= 0 && nalSizeLen <= 4) {
+ mNALSizeLen = nalSizeLen;
} else {
- ALOGE("No mNALSizeLen");
+ ALOGE("No HEVC mNALSizeLen");
}
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
mType = AAC;
@@ -273,25 +260,24 @@
clearPendingFrames();
}
-status_t MatroskaSource::start() {
+media_status_t MatroskaSource::start() {
if (mType == AVC && mNALSizeLen < 0) {
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
mBlockIter.reset();
- return OK;
+ return AMEDIA_OK;
}
-status_t MatroskaSource::stop() {
+media_status_t MatroskaSource::stop() {
clearPendingFrames();
- return OK;
+ return AMEDIA_OK;
}
-status_t MatroskaSource::getFormat(MetaDataBase &meta) {
- meta = mExtractor->mTracks.itemAt(mTrackIndex).mMeta;
- return OK;
+media_status_t MatroskaSource::getFormat(AMediaFormat *meta) {
+ return AMediaFormat_copy(meta, mExtractor->mTracks.itemAt(mTrackIndex).mMeta);
}
////////////////////////////////////////////////////////////////////////////////
@@ -608,11 +594,11 @@
MetaDataBase &meta = mbuf->meta_data();
if (encrypted) {
uint8_t ctrCounter[16] = { 0 };
- uint32_t type;
const uint8_t *keyId;
size_t keyIdSize;
- const MetaDataBase &trackMeta = mExtractor->mTracks.itemAt(mTrackIndex).mMeta;
- CHECK(trackMeta.findData(kKeyCryptoKey, &type, (const void **)&keyId, &keyIdSize));
+ AMediaFormat *trackMeta = mExtractor->mTracks.itemAt(mTrackIndex).mMeta;
+ AMediaFormat_getBuffer(trackMeta, AMEDIAFORMAT_KEY_CRYPTO_KEY,
+ (void**)&keyId, &keyIdSize);
meta.setData(kKeyCryptoKey, 0, keyId, keyIdSize);
memcpy(ctrCounter, data + 1, 8);
meta.setData(kKeyCryptoIV, 0, ctrCounter, 16);
@@ -715,11 +701,11 @@
return OK;
}
-status_t MatroskaSource::readBlock() {
+media_status_t MatroskaSource::readBlock() {
CHECK(mPendingFrames.empty());
if (mBlockIter.eos()) {
- return ERROR_END_OF_STREAM;
+ return AMEDIA_ERROR_END_OF_STREAM;
}
const mkvparser::Block *block = mBlockIter.block();
@@ -731,7 +717,7 @@
const mkvparser::Block::Frame &frame = block->GetFrame(i);
size_t len = frame.len;
if (SIZE_MAX - len < trackInfo->mHeaderLen) {
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
len += trackInfo->mHeaderLen;
@@ -756,7 +742,7 @@
mBlockIter.advance();
mbuf->release();
- return err;
+ return AMEDIA_ERROR_UNKNOWN;
}
mPendingFrames.push_back(mbuf);
@@ -764,10 +750,10 @@
mBlockIter.advance();
- return OK;
+ return AMEDIA_OK;
}
-status_t MatroskaSource::read(
+media_status_t MatroskaSource::read(
MediaBufferBase **out, const ReadOptions *options) {
*out = NULL;
@@ -777,7 +763,7 @@
ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
if (mode == ReadOptions::SEEK_FRAME_INDEX) {
- return ERROR_UNSUPPORTED;
+ return AMEDIA_ERROR_UNSUPPORTED;
}
if (!mExtractor->isLiveStreaming()) {
@@ -795,7 +781,7 @@
}
while (mPendingFrames.empty()) {
- status_t err = readBlock();
+ media_status_t err = readBlock();
if (err != OK) {
clearPendingFrames();
@@ -815,7 +801,7 @@
*out = frame;
- return OK;
+ return AMEDIA_OK;
}
// Each input frame contains one or more NAL fragments, each fragment
@@ -854,7 +840,7 @@
frame->release();
frame = NULL;
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
} else if (srcOffset + mNALSizeLen + NALsize > srcSize) {
break;
}
@@ -882,7 +868,7 @@
frame->release();
frame = NULL;
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
if (pass == 0) {
@@ -920,7 +906,7 @@
*out = buffer;
- return OK;
+ return AMEDIA_OK;
}
////////////////////////////////////////////////////////////////////////////////
@@ -1006,7 +992,7 @@
return mTracks.size();
}
-MediaTrackHelper *MatroskaExtractor::getTrack(size_t index) {
+MediaTrackHelperV2 *MatroskaExtractor::getTrack(size_t index) {
if (index >= mTracks.size()) {
return NULL;
}
@@ -1014,11 +1000,11 @@
return new MatroskaSource(this, index);
}
-status_t MatroskaExtractor::getTrackMetaData(
- MetaDataBase &meta,
+media_status_t MatroskaExtractor::getTrackMetaData(
+ AMediaFormat *meta,
size_t index, uint32_t flags) {
if (index >= mTracks.size()) {
- return UNKNOWN_ERROR;
+ return AMEDIA_ERROR_UNKNOWN;
}
if ((flags & kIncludeExtensiveMetaData) && !mExtractedThumbnails
@@ -1027,8 +1013,7 @@
mExtractedThumbnails = true;
}
- meta = mTracks.itemAt(index).mMeta;
- return OK;
+ return AMediaFormat_copy(meta, mTracks.itemAt(index).mMeta);
}
bool MatroskaExtractor::isLiveStreaming() const {
@@ -1063,7 +1048,7 @@
}
static void addESDSFromCodecPrivate(
- MetaDataBase &meta,
+ AMediaFormat *meta,
bool isAudio, const void *priv, size_t privSize) {
int privSizeBytesRequired = bytesForSize(privSize);
@@ -1091,14 +1076,14 @@
storeSize(esds, idx, privSize);
memcpy(esds + idx, priv, privSize);
- meta.setData(kKeyESDS, 0, esds, esdsSize);
+ AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CSD_0, priv, privSize);
delete[] esds;
esds = NULL;
}
status_t addVorbisCodecInfo(
- MetaDataBase &meta,
+ AMediaFormat *meta,
const void *_codecPrivate, size_t codecPrivateSize) {
// hexdump(_codecPrivate, codecPrivateSize);
@@ -1156,7 +1141,8 @@
if (codecPrivate[offset] != 0x01) {
return ERROR_MALFORMED;
}
- meta.setData(kKeyVorbisInfo, 0, &codecPrivate[offset], len1);
+ // formerly kKeyVorbisInfo
+ AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CSD_0, &codecPrivate[offset], len1);
offset += len1;
if (codecPrivate[offset] != 0x03) {
@@ -1168,19 +1154,20 @@
return ERROR_MALFORMED;
}
- meta.setData(
- kKeyVorbisBooks, 0, &codecPrivate[offset],
- codecPrivateSize - offset);
+ // formerly kKeyVorbisBooks
+ AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CSD_1,
+ &codecPrivate[offset], codecPrivateSize - offset);
return OK;
}
static status_t addFlacMetadata(
- MetaDataBase &meta,
+ AMediaFormat *meta,
const void *codecPrivate, size_t codecPrivateSize) {
// hexdump(codecPrivate, codecPrivateSize);
- meta.setData(kKeyFlacMetadata, 0, codecPrivate, codecPrivateSize);
+ // formerly kKeyFlacMetadata
+ AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CSD_0, codecPrivate, codecPrivateSize);
int32_t maxInputSize = 64 << 10;
FLACDecoder *flacDecoder = FLACDecoder::Create();
@@ -1202,7 +1189,7 @@
* streamInfo.max_blocksize * streamInfo.channels;
}
}
- meta.setInt32(kKeyMaxInputSize, maxInputSize);
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, maxInputSize);
delete flacDecoder;
return OK;
@@ -1253,7 +1240,7 @@
}
void MatroskaExtractor::getColorInformation(
- const mkvparser::VideoTrack *vtrack, MetaDataBase &meta) {
+ const mkvparser::VideoTrack *vtrack, AMediaFormat *meta) {
const mkvparser::Colour *color = vtrack->GetColour();
if (color == NULL) {
return;
@@ -1262,7 +1249,7 @@
// Color Aspects
{
int32_t primaries = 2; // ISO unspecified
- int32_t transfer = 2; // ISO unspecified
+ int32_t isotransfer = 2; // ISO unspecified
int32_t coeffs = 2; // ISO unspecified
bool fullRange = false; // default
bool rangeSpecified = false;
@@ -1271,7 +1258,7 @@
primaries = color->primaries;
}
if (isValidInt32ColourValue(color->transfer_characteristics)) {
- transfer = color->transfer_characteristics;
+ isotransfer = color->transfer_characteristics;
}
if (isValidInt32ColourValue(color->matrix_coefficients)) {
coeffs = color->matrix_coefficients;
@@ -1284,14 +1271,21 @@
rangeSpecified = true;
}
- ColorAspects aspects;
- ColorUtils::convertIsoColorAspectsToCodecAspects(
- primaries, transfer, coeffs, fullRange, aspects);
- meta.setInt32(kKeyColorPrimaries, aspects.mPrimaries);
- meta.setInt32(kKeyTransferFunction, aspects.mTransfer);
- meta.setInt32(kKeyColorMatrix, aspects.mMatrixCoeffs);
- meta.setInt32(
- kKeyColorRange, rangeSpecified ? aspects.mRange : ColorAspects::RangeUnspecified);
+ 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(meta, AMEDIAFORMAT_KEY_COLOR_RANGE, range);
+ }
+ if (standard != 0) {
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_COLOR_STANDARD, standard);
+ }
+ if (transfer != 0) {
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_COLOR_TRANSFER, transfer);
+ }
}
// HDR Static Info
@@ -1335,13 +1329,13 @@
// Only advertise static info if at least one of the groups have been specified.
if (memcmp(&info, &nullInfo, sizeof(info)) != 0) {
info.mID = HDRStaticInfo::kType1;
- meta.setData(kKeyHdrStaticInfo, 'hdrS', &info, sizeof(info));
+ ColorUtils::setHDRStaticInfoIntoAMediaFormat(info, meta);
}
}
}
status_t MatroskaExtractor::initTrackInfo(
- const mkvparser::Track *track, MetaDataBase &meta, TrackInfo *trackInfo) {
+ const mkvparser::Track *track, AMediaFormat *meta, TrackInfo *trackInfo) {
trackInfo->mTrackNum = track->GetNumber();
trackInfo->mMeta = meta;
trackInfo->mExtractor = this;
@@ -1355,7 +1349,8 @@
for(size_t j = 0; j < encoding->GetEncryptionCount(); j++) {
const mkvparser::ContentEncoding::ContentEncryption *encryption;
encryption = encoding->GetEncryptionByIndex(j);
- trackInfo->mMeta.setData(kKeyCryptoKey, 0, encryption->key_id, encryption->key_id_len);
+ AMediaFormat_setBuffer(trackInfo->mMeta,
+ AMEDIAFORMAT_KEY_CRYPTO_KEY, encryption->key_id, encryption->key_id_len);
trackInfo->mEncrypted = true;
break;
}
@@ -1404,9 +1399,10 @@
enum { VIDEO_TRACK = 1, AUDIO_TRACK = 2 };
- MetaDataBase meta;
+ AMediaFormat *meta = AMediaFormat_new();
status_t err = OK;
+ int32_t nalSize = -1;
switch (track->GetType()) {
case VIDEO_TRACK:
@@ -1415,20 +1411,28 @@
static_cast<const mkvparser::VideoTrack *>(track);
if (!strcmp("V_MPEG4/ISO/AVC", codecID)) {
- meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
- meta.setData(kKeyAVCC, 0, codecPrivate, codecPrivateSize);
+ AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AVC);
+ AMediaFormat_setBuffer(meta,
+ AMEDIAFORMAT_KEY_CSD_AVC, codecPrivate, codecPrivateSize);
+ if (codecPrivateSize > 4) {
+ nalSize = 1 + (codecPrivate[4] & 3);
+ }
} else if (!strcmp("V_MPEGH/ISO/HEVC", codecID)) {
- meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_HEVC);
+ AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_HEVC);
if (codecPrivateSize > 0) {
- meta.setData(kKeyHVCC, kTypeHVCC, codecPrivate, codecPrivateSize);
+ AMediaFormat_setBuffer(meta,
+ AMEDIAFORMAT_KEY_CSD_HEVC, codecPrivate, codecPrivateSize);
+ if (codecPrivateSize > 14 + 7) {
+ nalSize = 1 + (codecPrivate[14 + 7] & 3);
+ }
} else {
ALOGW("HEVC is detected, but does not have configuration.");
continue;
}
} else if (!strcmp("V_MPEG4/ISO/ASP", codecID)) {
if (codecPrivateSize > 0) {
- meta.setCString(
- kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+ AMediaFormat_setString(meta,
+ AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_MPEG4);
addESDSFromCodecPrivate(
meta, false, codecPrivate, codecPrivateSize);
} else {
@@ -1437,15 +1441,14 @@
continue;
}
} else if (!strcmp("V_VP8", codecID)) {
- meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VP8);
+ AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_VP8);
} else if (!strcmp("V_VP9", codecID)) {
- meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VP9);
+ AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_VP9);
if (codecPrivateSize > 0) {
// 'csd-0' for VP9 is the Blob of Codec Private data as
// specified in http://www.webmproject.org/vp9/profiles/.
- meta.setData(
- kKeyVp9CodecPrivate, 0, codecPrivate,
- codecPrivateSize);
+ AMediaFormat_setBuffer(meta,
+ AMEDIAFORMAT_KEY_CSD_0, codecPrivate, codecPrivateSize);
}
} else {
ALOGW("%s is not supported.", codecID);
@@ -1462,8 +1465,8 @@
ALOGW("track height exceeds int32_t, %lld", height);
continue;
}
- meta.setInt32(kKeyWidth, (int32_t)width);
- meta.setInt32(kKeyHeight, (int32_t)height);
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_WIDTH, (int32_t)width);
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_HEIGHT, (int32_t)height);
// setting display width/height is optional
const long long displayUnit = vtrack->GetDisplayUnit();
@@ -1473,8 +1476,10 @@
&& displayHeight > 0 && displayHeight <= INT32_MAX) {
switch (displayUnit) {
case 0: // pixels
- meta.setInt32(kKeyDisplayWidth, (int32_t)displayWidth);
- meta.setInt32(kKeyDisplayHeight, (int32_t)displayHeight);
+ AMediaFormat_setInt32(meta,
+ AMEDIAFORMAT_KEY_DISPLAY_WIDTH, (int32_t)displayWidth);
+ AMediaFormat_setInt32(meta,
+ AMEDIAFORMAT_KEY_DISPLAY_HEIGHT, (int32_t)displayHeight);
break;
case 1: // centimeters
case 2: // inches
@@ -1488,8 +1493,10 @@
const long long computedHeight =
std::max(height, width * displayHeight / displayWidth);
if (computedWidth <= INT32_MAX && computedHeight <= INT32_MAX) {
- meta.setInt32(kKeyDisplayWidth, (int32_t)computedWidth);
- meta.setInt32(kKeyDisplayHeight, (int32_t)computedHeight);
+ AMediaFormat_setInt32(meta,
+ AMEDIAFORMAT_KEY_DISPLAY_WIDTH, (int32_t)computedWidth);
+ AMediaFormat_setInt32(meta,
+ AMEDIAFORMAT_KEY_DISPLAY_HEIGHT, (int32_t)computedHeight);
}
break;
}
@@ -1509,34 +1516,39 @@
static_cast<const mkvparser::AudioTrack *>(track);
if (!strcmp("A_AAC", codecID)) {
- meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+ AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_AAC);
CHECK(codecPrivateSize >= 2);
addESDSFromCodecPrivate(
meta, true, codecPrivate, codecPrivateSize);
} else if (!strcmp("A_VORBIS", codecID)) {
- meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS);
+ AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_VORBIS);
err = addVorbisCodecInfo(
meta, codecPrivate, codecPrivateSize);
} else if (!strcmp("A_OPUS", codecID)) {
- meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_OPUS);
- meta.setData(kKeyOpusHeader, 0, codecPrivate, codecPrivateSize);
- meta.setInt64(kKeyOpusCodecDelay, track->GetCodecDelay());
- meta.setInt64(kKeyOpusSeekPreRoll, track->GetSeekPreRoll());
+ AMediaFormat_setString(meta,
+ AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_OPUS);
+ AMediaFormat_setBuffer(meta,
+ AMEDIAFORMAT_KEY_CSD_0, codecPrivate, codecPrivateSize);
+ int64_t codecDelay = track->GetCodecDelay();
+ AMediaFormat_setBuffer(meta,
+ AMEDIAFORMAT_KEY_CSD_1, &codecDelay, sizeof(codecDelay));
mSeekPreRollNs = track->GetSeekPreRoll();
+ AMediaFormat_setBuffer(meta,
+ AMEDIAFORMAT_KEY_CSD_2, &mSeekPreRollNs, sizeof(mSeekPreRollNs));
} else if (!strcmp("A_MPEG/L3", codecID)) {
- meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+ AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_MPEG);
} else if (!strcmp("A_FLAC", codecID)) {
- meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_FLAC);
+ AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_FLAC);
err = addFlacMetadata(meta, codecPrivate, codecPrivateSize);
} else {
ALOGW("%s is not supported.", codecID);
continue;
}
- meta.setInt32(kKeySampleRate, atrack->GetSamplingRate());
- meta.setInt32(kKeyChannelCount, atrack->GetChannels());
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, atrack->GetSamplingRate());
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, atrack->GetChannels());
break;
}
@@ -1549,7 +1561,7 @@
char lang[4];
strncpy(lang, language, 3);
lang[3] = '\0';
- meta.setCString(kKeyMediaLanguage, lang);
+ AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_LANGUAGE, lang);
}
if (err != OK) {
@@ -1558,12 +1570,13 @@
}
long long durationNs = mSegment->GetDuration();
- meta.setInt64(kKeyDuration, (durationNs + 500) / 1000);
+ AMediaFormat_setInt64(meta, AMEDIAFORMAT_KEY_DURATION, (durationNs + 500) / 1000);
mTracks.push();
size_t n = mTracks.size() - 1;
TrackInfo *trackInfo = &mTracks.editItemAt(n);
initTrackInfo(track, meta, trackInfo);
+ trackInfo->mNalLengthSize = nalSize;
if (!strcmp("V_MPEG4/ISO/AVC", codecID) && codecPrivateSize == 0) {
// Attempt to recover from AVC track without codec private data
@@ -1580,7 +1593,7 @@
TrackInfo *info = &mTracks.editItemAt(i);
const char *mime;
- CHECK(info->mMeta.findCString(kKeyMIMEType, &mime));
+ CHECK(AMediaFormat_getString(info->mMeta, AMEDIAFORMAT_KEY_MIME, &mime));
if (strncasecmp(mime, "video/", 6)) {
continue;
@@ -1606,16 +1619,16 @@
}
iter.advance();
}
- info->mMeta.setInt64(kKeyThumbnailTime, thumbnailTimeUs);
+ AMediaFormat_setInt64(info->mMeta,
+ AMEDIAFORMAT_KEY_THUMBNAIL_TIME, thumbnailTimeUs);
}
}
-status_t MatroskaExtractor::getMetaData(MetaDataBase &meta) {
- meta.setCString(
- kKeyMIMEType,
- mIsWebm ? "video/webm" : MEDIA_MIMETYPE_CONTAINER_MATROSKA);
+media_status_t MatroskaExtractor::getMetaData(AMediaFormat *meta) {
+ AMediaFormat_setString(meta,
+ AMEDIAFORMAT_KEY_MIME, mIsWebm ? "video/webm" : MEDIA_MIMETYPE_CONTAINER_MATROSKA);
- return OK;
+ return AMEDIA_OK;
}
uint32_t MatroskaExtractor::flags() const {
@@ -1647,22 +1660,22 @@
__attribute__ ((visibility ("default")))
ExtractorDef GETEXTRACTORDEF() {
return {
- EXTRACTORDEF_VERSION,
+ EXTRACTORDEF_VERSION_CURRENT,
UUID("abbedd92-38c4-4904-a4c1-b3f45f899980"),
1,
"Matroska Extractor",
{
- [](
+ .v2 = [](
CDataSource *source,
float *confidence,
void **,
- FreeMetaFunc *) -> CreatorFunc {
+ FreeMetaFunc *) -> CreatorFuncV2 {
DataSourceHelper helper(source);
if (SniffMatroska(&helper, confidence)) {
return [](
CDataSource *source,
- void *) -> CMediaExtractor* {
- return wrap(new MatroskaExtractor(new DataSourceHelper(source)));};
+ void *) -> CMediaExtractorV2* {
+ return wrapV2(new MatroskaExtractor(new DataSourceHelper(source)));};
}
return NULL;
}
diff --git a/media/extractors/mkv/MatroskaExtractor.h b/media/extractors/mkv/MatroskaExtractor.h
index 03fdeee..2fa8881 100644
--- a/media/extractors/mkv/MatroskaExtractor.h
+++ b/media/extractors/mkv/MatroskaExtractor.h
@@ -22,7 +22,7 @@
#include <media/MediaExtractorPluginApi.h>
#include <media/MediaExtractorPluginHelper.h>
-#include <media/stagefright/MetaDataBase.h>
+#include <media/NdkMediaFormat.h>
#include <utils/Vector.h>
#include <utils/threads.h>
@@ -35,16 +35,16 @@
struct DataSourceBaseReader;
struct MatroskaSource;
-struct MatroskaExtractor : public MediaExtractorPluginHelper {
+struct MatroskaExtractor : public MediaExtractorPluginHelperV2 {
explicit MatroskaExtractor(DataSourceHelper *source);
virtual size_t countTracks();
- virtual MediaTrackHelper *getTrack(size_t index);
+ virtual MediaTrackHelperV2 *getTrack(size_t index);
- virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags);
+ 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;
@@ -58,9 +58,17 @@
friend struct BlockIterator;
struct TrackInfo {
+ TrackInfo() {
+ mMeta = NULL;
+ }
+ ~TrackInfo() {
+ if (mMeta) {
+ AMediaFormat_delete(mMeta);
+ }
+ }
unsigned long mTrackNum;
bool mEncrypted;
- MetaDataBase mMeta;
+ AMediaFormat *mMeta;
const MatroskaExtractor *mExtractor;
Vector<const mkvparser::CuePoint*> mCuePoints;
@@ -89,13 +97,13 @@
status_t synthesizeAVCC(TrackInfo *trackInfo, size_t index);
status_t initTrackInfo(
const mkvparser::Track *track,
- MetaDataBase &meta,
+ AMediaFormat *meta,
TrackInfo *trackInfo);
void addTracks();
void findThumbnails();
void getColorInformation(
const mkvparser::VideoTrack *vtrack,
- MetaDataBase &meta);
+ AMediaFormat *meta);
bool isLiveStreaming() const;
MatroskaExtractor(const MatroskaExtractor &);
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 eec0392..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;
@@ -150,6 +153,8 @@
status_t parseSampleAuxiliaryInformationOffsets(off64_t offset, off64_t size);
status_t parseClearEncryptedSizes(off64_t offset, bool isSubsampleEncryption, uint32_t flags);
status_t parseSampleEncryption(off64_t offset);
+ // returns -1 for invalid layer ID
+ int32_t parseHEVCLayerId(const uint8_t *data, size_t size);
struct TrackFragmentHeaderInfo {
enum Flags {
@@ -332,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",
@@ -373,6 +380,7 @@
mFirstTrack(NULL),
mLastTrack(NULL) {
ALOGV("mime=%s, mPreferHeif=%d", mime, mPreferHeif);
+ mFileMetaData = AMediaFormat_new();
}
MPEG4Extractor::~MPEG4Extractor() {
@@ -380,6 +388,7 @@
while (track) {
Track *next = track->next;
+ AMediaFormat_delete(track->meta);
delete track;
track = next;
}
@@ -391,6 +400,7 @@
mPssh.clear();
delete mDataSource;
+ AMediaFormat_delete(mFileMetaData);
}
uint32_t MPEG4Extractor::flags() const {
@@ -399,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() {
@@ -426,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;
@@ -445,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;
@@ -487,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;
@@ -507,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;
}
@@ -527,7 +537,7 @@
return;
}
ALOGV("paddingsamples = %" PRId64, paddingsamples);
- track->meta.setInt32(kKeyEncoderPadding, paddingsamples);
+ AMediaFormat_setInt32(track->meta, AMEDIAFORMAT_KEY_ENCODER_PADDING, paddingsamples);
}
}();
@@ -536,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)) {
@@ -549,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;
@@ -569,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() {
@@ -613,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;
@@ -643,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;
@@ -653,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;
@@ -687,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);
}
@@ -938,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;
}
@@ -962,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;
}
@@ -1039,7 +1059,7 @@
}
}
if (mode != kCryptoModeUnencrypted) {
- mLastTrack->meta.setInt32(kKeyCryptoMode, mode);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CRYPTO_MODE, mode);
}
break;
}
@@ -1115,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;
}
@@ -1217,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;
}
@@ -1375,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];
@@ -1403,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;
}
@@ -1438,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.
@@ -1479,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;
}
@@ -1490,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')) {
@@ -1557,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);
@@ -1624,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);
@@ -1715,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;
@@ -1735,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;
@@ -1752,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;
}
@@ -1779,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);
}
}
@@ -1895,7 +2014,7 @@
if (buffer[len - 1] != '/') {
buffer[len] = '/';
}
- mFileMetaData.setCString(kKeyLocation, &buffer[0]);
+ AMediaFormat_setString(mFileMetaData, AMEDIAFORMAT_KEY_LOCATION, &buffer[0]);
break;
}
@@ -1925,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')) {
@@ -1950,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);
}
}
}
@@ -1977,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;
}
@@ -2004,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;
}
@@ -2026,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;
@@ -2062,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;
}
@@ -2190,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;
}
@@ -2240,7 +2363,8 @@
}
if (duration != 0 && mHeaderTimescale != 0) {
- mFileMetaData.setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
+ AMediaFormat_setInt64(mFileMetaData,
+ AMEDIAFORMAT_KEY_DURATION, duration * 1000000 / mHeaderTimescale);
}
break;
@@ -2274,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);
}
}
@@ -2318,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;
}
@@ -2349,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;
@@ -2382,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;
@@ -2618,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;
}
@@ -2779,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;
@@ -2851,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;
}
@@ -2973,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;
}
@@ -3072,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
@@ -3081,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
@@ -3135,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]);
@@ -3171,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;
}
@@ -3207,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'):
@@ -3264,7 +3386,7 @@
sprintf(tmp, "%d",
(int)buffer[size - 1]);
- mFileMetaData.setCString(kKeyCompilation, tmp);
+ AMediaFormat_setString(mFileMetaData, AMEDIAFORMAT_KEY_COMPILATION, tmp);
}
break;
}
@@ -3276,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;
}
@@ -3288,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;
}
@@ -3332,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);
}
}
@@ -3348,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
@@ -3367,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);
}
}
@@ -3408,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);
+ }
}
}
@@ -3449,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'):
@@ -3477,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'):
@@ -3498,7 +3634,7 @@
if (year < 10000) {
sprintf(tmp, "%u", year);
- mFileMetaData.setCString(kKeyYear, tmp);
+ AMediaFormat_setString(mFileMetaData, "year", tmp);
}
break;
}
@@ -3507,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
@@ -3543,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());
}
}
@@ -3562,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;
@@ -3598,7 +3736,7 @@
it->getString(&s);
delete it;
- mFileMetaData.setCString(kMap[i].key, s);
+ AMediaFormat_setString(mFileMetaData, kMap[i].key, s);
}
}
@@ -3607,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;
@@ -3635,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) {
@@ -3651,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;
}
@@ -3671,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;
}
@@ -3701,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;
}
}
@@ -3805,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;
}
@@ -3822,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);
}
}
@@ -3869,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);
@@ -3890,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;
@@ -3907,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) {
@@ -4060,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;
}
@@ -4085,7 +4220,7 @@
////////////////////////////////////////////////////////////////////////////////
MPEG4Source::MPEG4Source(
- MetaDataBase &format,
+ AMediaFormat *format,
DataSourceHelper *dataSource,
int32_t timeScale,
const sp<SampleTable> &sampleTable,
@@ -4125,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);
@@ -4150,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;
@@ -4163,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;
@@ -4181,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));
}
@@ -4217,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
@@ -4231,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.
@@ -4248,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);
@@ -4275,7 +4414,7 @@
mStarted = false;
mCurrentSampleIndex = 0;
- return OK;
+ return AMEDIA_OK;
}
status_t MPEG4Source::parseChunk(off64_t *offset) {
@@ -4393,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;
@@ -4524,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;
@@ -4563,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) {
@@ -4910,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 {
@@ -4935,7 +5077,36 @@
return 0;
}
-status_t MPEG4Source::read(
+int32_t MPEG4Source::parseHEVCLayerId(const uint8_t *data, size_t size) {
+ CHECK(data != nullptr && size >= (mNALLengthSize + 2));
+
+ // HEVC NAL-header (16-bit)
+ // 1 6 6 3
+ // |-|uuuuuu|------|iii|
+ // ^ ^
+ // NAL_type layer_id + 1
+ //
+ // Layer-id is non-zero only for Temporal Sub-layer Access pictures (TSA)
+ enum {
+ TSA_N = 2,
+ TSA_R = 3,
+ STSA_N = 4,
+ STSA_R = 5,
+ };
+
+ data += mNALLengthSize;
+ uint16_t nalHeader = data[0] << 8 | data[1];
+
+ uint16_t nalType = (nalHeader >> 9) & 0x3Fu;
+ if (nalType == TSA_N || nalType == TSA_R || nalType == STSA_N || nalType == STSA_R) {
+ int32_t layerIdPlusOne = nalHeader & 0x7u;
+ ALOGD_IF(layerIdPlusOne == 0, "got layerId 0 for TSA picture");
+ return layerIdPlusOne - 1;
+ }
+ return 0;
+}
+
+media_status_t MPEG4Source::read(
MediaBufferBase **out, const ReadOptions *options) {
Mutex::Autolock autoLock(mLock);
@@ -4943,7 +5114,7 @@
if (options != nullptr && options->getNonBlocking() && !mGroup->has_buffers()) {
*out = nullptr;
- return WOULD_BLOCK;
+ return AMEDIA_ERROR_WOULD_BLOCK;
}
if (mFirstMoofOffset > 0) {
@@ -4961,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;
@@ -4972,7 +5143,7 @@
err = mItemTable->findThumbnailItem(imageIndex, &mCurrentSampleIndex);
}
if (err != OK) {
- return err;
+ return AMEDIA_ERROR_UNKNOWN;
}
} else {
uint32_t findFlags = 0;
@@ -5027,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
@@ -5084,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
}
}
@@ -5123,7 +5297,7 @@
mBuffer->release();
mBuffer = NULL;
- return ERROR_IO;
+ return AMEDIA_ERROR_IO;
}
mBuffer->meta_data().clear();
@@ -5131,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
@@ -5153,7 +5328,7 @@
mBuffer->release();
mBuffer = NULL;
- return ERROR_IO;
+ return AMEDIA_ERROR_IO;
}
CHECK(mBuffer != NULL);
@@ -5181,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
@@ -5209,7 +5384,7 @@
mBuffer->release();
mBuffer = NULL;
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
MediaBufferBase *clone = mBuffer->clone();
@@ -5228,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
@@ -5236,7 +5411,7 @@
mBuffer->release();
mBuffer = NULL;
- return ERROR_IO;
+ return AMEDIA_ERROR_IO;
}
uint8_t *dstData = (uint8_t *)mBuffer->data();
@@ -5256,7 +5431,7 @@
mBuffer->release();
mBuffer = NULL;
- return ERROR_IO;
+ return AMEDIA_ERROR_IO;
}
mBuffer->set_range(0, dstOffset + size);
@@ -5280,78 +5455,65 @@
*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).
ssize_t num_bytes_read = 0;
- int32_t drm = 0;
- bool usesDRM = (mFormat.findInt32(kKeyIsDRM, &drm) && drm != 0);
- if (usesDRM) {
- num_bytes_read =
- mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size);
- } else {
- num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);
- }
+ num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
- return ERROR_IO;
+ return AMEDIA_ERROR_IO;
}
- if (usesDRM) {
- CHECK(mBuffer != NULL);
- mBuffer->set_range(0, size);
+ uint8_t *dstData = (uint8_t *)mBuffer->data();
+ size_t srcOffset = 0;
+ size_t dstOffset = 0;
- } else {
- uint8_t *dstData = (uint8_t *)mBuffer->data();
- size_t srcOffset = 0;
- size_t dstOffset = 0;
-
- while (srcOffset < size) {
- bool isMalFormed = !isInRange((size_t)0u, size, srcOffset, mNALLengthSize);
- size_t nalLength = 0;
- if (!isMalFormed) {
- nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
- srcOffset += mNALLengthSize;
- isMalFormed = !isInRange((size_t)0u, size, srcOffset, nalLength);
- }
-
- if (isMalFormed) {
- ALOGE("Video is malformed");
- mBuffer->release();
- mBuffer = NULL;
- return ERROR_MALFORMED;
- }
-
- if (nalLength == 0) {
- continue;
- }
-
- if (dstOffset > SIZE_MAX - 4 ||
- dstOffset + 4 > SIZE_MAX - nalLength ||
- dstOffset + 4 + nalLength > mBuffer->size()) {
- ALOGE("b/27208621 : %zu %zu", dstOffset, mBuffer->size());
- android_errorWriteLog(0x534e4554, "27208621");
- mBuffer->release();
- mBuffer = NULL;
- return ERROR_MALFORMED;
- }
-
- dstData[dstOffset++] = 0;
- dstData[dstOffset++] = 0;
- dstData[dstOffset++] = 0;
- dstData[dstOffset++] = 1;
- memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
- srcOffset += nalLength;
- dstOffset += nalLength;
+ while (srcOffset < size) {
+ bool isMalFormed = !isInRange((size_t)0u, size, srcOffset, mNALLengthSize);
+ size_t nalLength = 0;
+ if (!isMalFormed) {
+ nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
+ srcOffset += mNALLengthSize;
+ isMalFormed = !isInRange((size_t)0u, size, srcOffset, nalLength);
}
- CHECK_EQ(srcOffset, size);
- CHECK(mBuffer != NULL);
- mBuffer->set_range(0, dstOffset);
+
+ if (isMalFormed) {
+ ALOGE("Video is malformed");
+ mBuffer->release();
+ mBuffer = NULL;
+ return AMEDIA_ERROR_MALFORMED;
+ }
+
+ if (nalLength == 0) {
+ continue;
+ }
+
+ if (dstOffset > SIZE_MAX - 4 ||
+ dstOffset + 4 > SIZE_MAX - nalLength ||
+ dstOffset + 4 + nalLength > mBuffer->size()) {
+ ALOGE("b/27208621 : %zu %zu", dstOffset, mBuffer->size());
+ android_errorWriteLog(0x534e4554, "27208621");
+ mBuffer->release();
+ mBuffer = NULL;
+ return AMEDIA_ERROR_MALFORMED;
+ }
+
+ dstData[dstOffset++] = 0;
+ dstData[dstOffset++] = 0;
+ dstData[dstOffset++] = 0;
+ dstData[dstOffset++] = 1;
+ memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
+ srcOffset += nalLength;
+ dstOffset += nalLength;
}
+ CHECK_EQ(srcOffset, size);
+ CHECK(mBuffer != NULL);
+ mBuffer->set_range(0, dstOffset);
mBuffer->meta_data().clear();
mBuffer->meta_data().setInt64(
@@ -5368,6 +5530,12 @@
uint32_t layerId = FindAVCLayerId(
(const uint8_t *)mBuffer->data(), mBuffer->range_length());
mBuffer->meta_data().setInt32(kKeyTemporalLayerId, layerId);
+ } else if (mIsHEVC) {
+ int32_t layerId = parseHEVCLayerId(
+ (const uint8_t *)mBuffer->data(), mBuffer->range_length());
+ if (layerId >= 0) {
+ mBuffer->meta_data().setInt32(kKeyTemporalLayerId, layerId);
+ }
}
if (isSyncSample) {
@@ -5379,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");
@@ -5425,7 +5593,7 @@
mCurrentSampleIndex = 0;
status_t err = parseChunk(&totalOffset);
if (err != OK) {
- return err;
+ return AMEDIA_ERROR_UNKNOWN;
}
mCurrentTime = totalTime * mTimescale / 1000000ll;
} else {
@@ -5437,7 +5605,7 @@
off64_t tmp = mCurrentMoofOffset;
status_t err = parseChunk(&tmp);
if (err != OK) {
- return err;
+ return AMEDIA_ERROR_UNKNOWN;
}
mCurrentTime = 0;
}
@@ -5465,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;
@@ -5473,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;
}
}
@@ -5485,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;
}
}
@@ -5517,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);
@@ -5536,7 +5703,7 @@
mBuffer = NULL;
ALOGE("fragmentedRead ERROR_MALFORMED size %zu", size);
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
ssize_t num_bytes_read =
@@ -5547,7 +5714,7 @@
mBuffer = NULL;
ALOGE("i/o error");
- return ERROR_IO;
+ return AMEDIA_ERROR_IO;
}
CHECK(mBuffer != NULL);
@@ -5566,6 +5733,12 @@
uint32_t layerId = FindAVCLayerId(
(const uint8_t *)mBuffer->data(), mBuffer->range_length());
mBuffer->meta_data().setInt32(kKeyTemporalLayerId, layerId);
+ } else if (mIsHEVC) {
+ int32_t layerId = parseHEVCLayerId(
+ (const uint8_t *)mBuffer->data(), mBuffer->range_length());
+ if (layerId >= 0) {
+ mBuffer->meta_data().setInt32(kKeyTemporalLayerId, layerId);
+ }
}
if (isSyncSample) {
@@ -5579,7 +5752,7 @@
*out = mBuffer;
mBuffer = NULL;
- return OK;
+ return AMEDIA_OK;
}
// Each NAL unit is split up into its constituent fragments and
@@ -5601,7 +5774,7 @@
mBuffer->release();
mBuffer = NULL;
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
MediaBufferBase *clone = mBuffer->clone();
@@ -5620,30 +5793,20 @@
*out = clone;
- return OK;
+ return AMEDIA_OK;
} else {
ALOGV("whole NAL");
// Whole NAL units are returned but each fragment is prefixed by
// the start code (0x00 00 00 01).
ssize_t num_bytes_read = 0;
- int32_t drm = 0;
- bool usesDRM = (mFormat.findInt32(kKeyIsDRM, &drm) && drm != 0);
void *data = NULL;
bool isMalFormed = false;
- if (usesDRM) {
- if (mBuffer == NULL || !isInRange((size_t)0u, mBuffer->size(), size)) {
- isMalFormed = true;
- } else {
- data = mBuffer->data();
- }
+ int32_t 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 {
- int32_t max_size;
- if (!mFormat.findInt32(kKeyMaxInputSize, &max_size)
- || !isInRange((size_t)0u, (size_t)max_size, size)) {
- isMalFormed = true;
- } else {
- data = mSrcBuffer;
- }
+ data = mSrcBuffer;
}
if (isMalFormed || data == NULL) {
@@ -5652,7 +5815,7 @@
mBuffer->release();
mBuffer = NULL;
}
- return ERROR_MALFORMED;
+ return AMEDIA_ERROR_MALFORMED;
}
num_bytes_read = mDataSource->readAt(offset, data, size);
@@ -5661,62 +5824,56 @@
mBuffer = NULL;
ALOGE("i/o error");
- return ERROR_IO;
+ return AMEDIA_ERROR_IO;
}
- if (usesDRM) {
- CHECK(mBuffer != NULL);
- mBuffer->set_range(0, size);
+ uint8_t *dstData = (uint8_t *)mBuffer->data();
+ size_t srcOffset = 0;
+ size_t dstOffset = 0;
- } else {
- uint8_t *dstData = (uint8_t *)mBuffer->data();
- size_t srcOffset = 0;
- size_t dstOffset = 0;
-
- while (srcOffset < size) {
- isMalFormed = !isInRange((size_t)0u, size, srcOffset, mNALLengthSize);
- size_t nalLength = 0;
- if (!isMalFormed) {
- nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
- srcOffset += mNALLengthSize;
- isMalFormed = !isInRange((size_t)0u, size, srcOffset, nalLength)
- || !isInRange((size_t)0u, mBuffer->size(), dstOffset, (size_t)4u)
- || !isInRange((size_t)0u, mBuffer->size(), dstOffset + 4, nalLength);
- }
-
- if (isMalFormed) {
- ALOGE("Video is malformed; nalLength %zu", nalLength);
- mBuffer->release();
- mBuffer = NULL;
- return ERROR_MALFORMED;
- }
-
- if (nalLength == 0) {
- continue;
- }
-
- if (dstOffset > SIZE_MAX - 4 ||
- dstOffset + 4 > SIZE_MAX - nalLength ||
- dstOffset + 4 + nalLength > mBuffer->size()) {
- ALOGE("b/26365349 : %zu %zu", dstOffset, mBuffer->size());
- android_errorWriteLog(0x534e4554, "26365349");
- mBuffer->release();
- mBuffer = NULL;
- return ERROR_MALFORMED;
- }
-
- dstData[dstOffset++] = 0;
- dstData[dstOffset++] = 0;
- dstData[dstOffset++] = 0;
- dstData[dstOffset++] = 1;
- memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
- srcOffset += nalLength;
- dstOffset += nalLength;
+ while (srcOffset < size) {
+ isMalFormed = !isInRange((size_t)0u, size, srcOffset, mNALLengthSize);
+ size_t nalLength = 0;
+ if (!isMalFormed) {
+ nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
+ srcOffset += mNALLengthSize;
+ isMalFormed = !isInRange((size_t)0u, size, srcOffset, nalLength)
+ || !isInRange((size_t)0u, mBuffer->size(), dstOffset, (size_t)4u)
+ || !isInRange((size_t)0u, mBuffer->size(), dstOffset + 4, nalLength);
}
- CHECK_EQ(srcOffset, size);
- CHECK(mBuffer != NULL);
- mBuffer->set_range(0, dstOffset);
+
+ if (isMalFormed) {
+ ALOGE("Video is malformed; nalLength %zu", nalLength);
+ mBuffer->release();
+ mBuffer = NULL;
+ return AMEDIA_ERROR_MALFORMED;
+ }
+
+ if (nalLength == 0) {
+ continue;
+ }
+
+ if (dstOffset > SIZE_MAX - 4 ||
+ dstOffset + 4 > SIZE_MAX - nalLength ||
+ dstOffset + 4 + nalLength > mBuffer->size()) {
+ ALOGE("b/26365349 : %zu %zu", dstOffset, mBuffer->size());
+ android_errorWriteLog(0x534e4554, "26365349");
+ mBuffer->release();
+ mBuffer = NULL;
+ return AMEDIA_ERROR_MALFORMED;
+ }
+
+ dstData[dstOffset++] = 0;
+ dstData[dstOffset++] = 0;
+ dstData[dstOffset++] = 0;
+ dstData[dstOffset++] = 1;
+ memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
+ srcOffset += nalLength;
+ dstOffset += nalLength;
}
+ CHECK_EQ(srcOffset, size);
+ CHECK(mBuffer != NULL);
+ mBuffer->set_range(0, dstOffset);
mBuffer->meta_data().setInt64(
kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
@@ -5737,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;
}
@@ -5867,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'):
{
@@ -5928,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);
@@ -5953,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/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp
index 2ae9b5a..a52ccb1 100644
--- a/media/extractors/ogg/OggExtractor.cpp
+++ b/media/extractors/ogg/OggExtractor.cpp
@@ -987,11 +987,10 @@
AMediaFormat_setBuffer(mMeta, AMEDIAFORMAT_KEY_CSD_0, data, size);
AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE, kOpusSampleRate);
AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, mChannelCount);
- // are these actually used anywhere?
- // (they are kKeyOpusSeekPreRoll and kKeyOpusCodecDelay respectively)
- AMediaFormat_setInt64(mMeta, AMEDIAFORMAT_KEY_CSD_2, kOpusSeekPreRollUs * 1000 /* = 80 ms*/);
- AMediaFormat_setInt64(mMeta, AMEDIAFORMAT_KEY_CSD_1,
- mCodecDelay /* sample/s */ * 1000000000ll / kOpusSampleRate);
+ int64_t codecdelay = mCodecDelay /* sample/s */ * 1000000000ll / kOpusSampleRate;
+ AMediaFormat_setBuffer(mMeta, AMEDIAFORMAT_KEY_CSD_1, &codecdelay, sizeof(codecdelay));
+ int64_t preroll = kOpusSeekPreRollUs * 1000 /* = 80 ms*/;
+ AMediaFormat_setBuffer(mMeta, AMEDIAFORMAT_KEY_CSD_2, &preroll, sizeof(preroll));
return AMEDIA_OK;
}
diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp
index 9a329f1..ddc7031 100644
--- a/media/extractors/wav/WAVExtractor.cpp
+++ b/media/extractors/wav/WAVExtractor.cpp
@@ -311,6 +311,7 @@
AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, mNumChannels);
AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_CHANNEL_MASK, mChannelMask);
AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE, mSampleRate);
+ AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, mBitsPerSample);
AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_PCM_ENCODING,
kAudioEncodingPcm16bit);
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/loopback/src/LoopbackAnalyzer.h b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
index ef9a753..64deb60 100644
--- a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
+++ b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
@@ -163,8 +163,7 @@
const float *needle, int needleSize,
LatencyReport *report) {
const double threshold = 0.1;
- printf("measureLatencyFromEchos: haystackSize = %d, needleSize = %d\n",
- haystackSize, needleSize);
+ // printf("%s: haystackSize = %d, needleSize = %d\n", __func__, haystackSize, needleSize);
// Find first peak
int first = (int) (findFirstMatch(haystack,
@@ -181,8 +180,6 @@
needle,
needleSize,
threshold) + 0.5);
-
- printf("measureLatencyFromEchos: first = %d, again at %d\n", first, again);
first = again;
// Allocate results array
@@ -199,7 +196,7 @@
// Add higher harmonics mapped onto lower harmonics.
// This reinforces the "fundamental" echo.
- const int numEchoes = 10;
+ const int numEchoes = 8;
for (int partial = 1; partial < numEchoes; partial++) {
for (int i = 0; i < numCorrelations; i++) {
harmonicSums[i / partial] += correlations[i] / partial;
@@ -216,7 +213,7 @@
maxCorrelation = harmonicSums[i];
sumOfPeaks += maxCorrelation;
peakIndex = i;
- printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex);
+ // printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex);
}
}
@@ -476,9 +473,13 @@
void report() override {
printf("EchoAnalyzer ---------------\n");
- printf(LOOPBACK_RESULT_TAG "measured.gain = %f\n", mMeasuredLoopGain);
- printf(LOOPBACK_RESULT_TAG "echo.gain = %f\n", mEchoGain);
- printf(LOOPBACK_RESULT_TAG "test.state = %d\n", mState);
+ printf(LOOPBACK_RESULT_TAG "measured.gain = %8f\n", mMeasuredLoopGain);
+ printf(LOOPBACK_RESULT_TAG "echo.gain = %8f\n", mEchoGain);
+ printf(LOOPBACK_RESULT_TAG "test.state = %8d\n", mState);
+ printf(LOOPBACK_RESULT_TAG "test.state.name = %8s\n", convertStateToText(mState));
+ if (mState == STATE_WAITING_FOR_SILENCE) {
+ printf("WARNING - Stuck waiting for silence. Input may be too noisy!\n");
+ }
if (mMeasuredLoopGain >= 0.9999) {
printf(" ERROR - clipping, turn down volume slightly\n");
} else {
@@ -491,9 +492,12 @@
printf(" ERROR - confidence too low = %f\n", mLatencyReport.confidence);
} else {
double latencyMillis = 1000.0 * mLatencyReport.latencyInFrames / getSampleRate();
- printf(LOOPBACK_RESULT_TAG "latency.frames = %8.2f\n", mLatencyReport.latencyInFrames);
- printf(LOOPBACK_RESULT_TAG "latency.msec = %8.2f\n", latencyMillis);
- printf(LOOPBACK_RESULT_TAG "latency.confidence = %8.6f\n", mLatencyReport.confidence);
+ printf(LOOPBACK_RESULT_TAG "latency.frames = %8.2f\n",
+ mLatencyReport.latencyInFrames);
+ printf(LOOPBACK_RESULT_TAG "latency.msec = %8.2f\n",
+ latencyMillis);
+ printf(LOOPBACK_RESULT_TAG "latency.confidence = %8.6f\n",
+ mLatencyReport.confidence);
}
}
}
@@ -527,7 +531,7 @@
int numWritten;
int numSamples;
- echo_state_t nextState = mState;
+ echo_state nextState = mState;
switch (mState) {
case STATE_INITIAL_SILENCE:
@@ -553,6 +557,7 @@
// mLoopCounter, peak);
mDownCounter = 8;
mMeasuredLoopGain = peak; // assumes original pulse amplitude is one
+ mSilenceThreshold = peak * 0.1; // scale silence to measured pulse
// Calculate gain that will give us a nice decaying echo.
mEchoGain = mDesiredEchoGain / mMeasuredLoopGain;
if (mEchoGain > MAX_ECHO_GAIN) {
@@ -638,7 +643,7 @@
private:
- enum echo_state_t {
+ enum echo_state {
STATE_INITIAL_SILENCE,
STATE_MEASURING_GAIN,
STATE_WAITING_FOR_SILENCE,
@@ -648,6 +653,35 @@
STATE_FAILED
};
+ const char *convertStateToText(echo_state state) {
+ const char *result = "Unknown";
+ switch(state) {
+ case STATE_INITIAL_SILENCE:
+ result = "INIT";
+ break;
+ case STATE_MEASURING_GAIN:
+ result = "GAIN";
+ break;
+ case STATE_WAITING_FOR_SILENCE:
+ result = "SILENCE";
+ break;
+ case STATE_SENDING_PULSE:
+ result = "PULSE";
+ break;
+ case STATE_GATHERING_ECHOS:
+ result = "ECHOS";
+ break;
+ case STATE_DONE:
+ result = "DONE";
+ break;
+ case STATE_FAILED:
+ result = "FAILED";
+ break;
+ }
+ return result;
+ }
+
+
int32_t mDownCounter = 500;
int32_t mLoopCounter = 0;
int32_t mSampleIndex = 0;
@@ -656,7 +690,7 @@
float mMeasuredLoopGain = 0.0f;
float mDesiredEchoGain = 0.95f;
float mEchoGain = 1.0f;
- echo_state_t mState = STATE_INITIAL_SILENCE;
+ echo_state mState = STATE_INITIAL_SILENCE;
AudioRecording mAudioRecording; // contains only the input after the gain detection burst
LatencyReport mLatencyReport;
@@ -680,21 +714,32 @@
void report() override {
printf("SineAnalyzer ------------------\n");
- printf(LOOPBACK_RESULT_TAG "peak.amplitude = %7.5f\n", mPeakAmplitude);
- printf(LOOPBACK_RESULT_TAG "sine.magnitude = %7.5f\n", mMagnitude);
- printf(LOOPBACK_RESULT_TAG "phase.offset = %7.5f\n", mPhaseOffset);
- printf(LOOPBACK_RESULT_TAG "ref.phase = %7.5f\n", mPhase);
- printf(LOOPBACK_RESULT_TAG "frames.accumulated = %6d\n", mFramesAccumulated);
- printf(LOOPBACK_RESULT_TAG "sine.period = %6d\n", mSinePeriod);
- printf(LOOPBACK_RESULT_TAG "test.state = %6d\n", mState);
- printf(LOOPBACK_RESULT_TAG "frame.count = %6d\n", mFrameCounter);
+ printf(LOOPBACK_RESULT_TAG "peak.amplitude = %8f\n", mPeakAmplitude);
+ printf(LOOPBACK_RESULT_TAG "sine.magnitude = %8f\n", mMagnitude);
+ printf(LOOPBACK_RESULT_TAG "peak.noise = %8f\n", mPeakNoise);
+ printf(LOOPBACK_RESULT_TAG "rms.noise = %8f\n", mRootMeanSquareNoise);
+ float amplitudeRatio = mMagnitude / mPeakNoise;
+ float signalToNoise = amplitudeRatio * amplitudeRatio;
+ printf(LOOPBACK_RESULT_TAG "signal.to.noise = %8.2f\n", signalToNoise);
+ float signalToNoiseDB = 10.0 * log(signalToNoise);
+ printf(LOOPBACK_RESULT_TAG "signal.to.noise.db = %8.2f\n", signalToNoiseDB);
+ if (signalToNoiseDB < MIN_SNRATIO_DB) {
+ printf("WARNING - signal to noise ratio is too low! < %d dB\n", MIN_SNRATIO_DB);
+ }
+ printf(LOOPBACK_RESULT_TAG "phase.offset = %8.5f\n", mPhaseOffset);
+ printf(LOOPBACK_RESULT_TAG "ref.phase = %8.5f\n", mPhase);
+ printf(LOOPBACK_RESULT_TAG "frames.accumulated = %8d\n", mFramesAccumulated);
+ printf(LOOPBACK_RESULT_TAG "sine.period = %8d\n", mSinePeriod);
+ printf(LOOPBACK_RESULT_TAG "test.state = %8d\n", mState);
+ printf(LOOPBACK_RESULT_TAG "frame.count = %8d\n", mFrameCounter);
// Did we ever get a lock?
bool gotLock = (mState == STATE_LOCKED) || (mGlitchCount > 0);
if (!gotLock) {
printf("ERROR - failed to lock on reference sine tone\n");
} else {
// Only print if meaningful.
- printf(LOOPBACK_RESULT_TAG "glitch.count = %6d\n", mGlitchCount);
+ printf(LOOPBACK_RESULT_TAG "glitch.count = %8d\n", mGlitchCount);
+ printf(LOOPBACK_RESULT_TAG "max.glitch = %8f\n", mMaxGlitchDelta);
}
}
@@ -732,15 +777,48 @@
}
for (int i = 0; i < numFrames; i++) {
+ bool sineEnabled = true;
float sample = inputData[i * inputChannelCount];
float sinOut = sinf(mPhase);
switch (mState) {
case STATE_IDLE:
- case STATE_IMMUNE:
- case STATE_WAITING_FOR_SIGNAL:
+ sineEnabled = false;
+ mDownCounter--;
+ if (mDownCounter <= 0) {
+ mState = STATE_MEASURE_NOISE;
+ mDownCounter = NOISE_FRAME_COUNT;
+ }
break;
+ case STATE_MEASURE_NOISE:
+ sineEnabled = false;
+ mPeakNoise = std::max(abs(sample), mPeakNoise);
+ mNoiseSumSquared += sample * sample;
+ mDownCounter--;
+ if (mDownCounter <= 0) {
+ mState = STATE_WAITING_FOR_SIGNAL;
+ mRootMeanSquareNoise = sqrt(mNoiseSumSquared / NOISE_FRAME_COUNT);
+ mTolerance = std::max(MIN_TOLERANCE, mPeakNoise * 2.0f);
+ mPhase = 0.0; // prevent spike at start
+ }
+ break;
+
+ case STATE_IMMUNE:
+ mDownCounter--;
+ if (mDownCounter <= 0) {
+ mState = STATE_WAITING_FOR_SIGNAL;
+ }
+ break;
+
+ case STATE_WAITING_FOR_SIGNAL:
+ if (peak > mThreshold) {
+ mState = STATE_WAITING_FOR_LOCK;
+ //printf("%5d: switch to STATE_WAITING_FOR_LOCK\n", mFrameCounter);
+ resetAccumulator();
+ }
+ break;
+
case STATE_WAITING_FOR_LOCK:
mSinAccumulator += sample * sinOut;
mCosAccumulator += sample * cosf(mPhase);
@@ -766,13 +844,14 @@
// printf(" predicted = %f, actual = %f\n", predicted, sample);
float diff = predicted - sample;
- if (fabs(diff) > mTolerance) {
+ float absDiff = fabs(diff);
+ mMaxGlitchDelta = std::max(mMaxGlitchDelta, absDiff);
+ if (absDiff > mTolerance) {
mGlitchCount++;
//printf("%5d: Got a glitch # %d, predicted = %f, actual = %f\n",
// mFrameCounter, mGlitchCount, predicted, sample);
mState = STATE_IMMUNE;
- //printf("%5d: switch to STATE_IMMUNE\n", mFrameCounter);
- mDownCounter = mSinePeriod; // Set duration of IMMUNE state.
+ mDownCounter = mSinePeriod * PERIODS_IMMUNE;
}
// Track incoming signal and slowly adjust magnitude to account
@@ -792,44 +871,23 @@
} break;
}
+ float output = 0.0f;
// Output sine wave so we can measure it.
- outputData[i * outputChannelCount] = (sinOut * mOutputAmplitude)
- + (mWhiteNoise.nextRandomDouble() * mNoiseAmplitude);
- // printf("%5d: sin(%f) = %f, %f\n", i, mPhase, sinOut, mPhaseIncrement);
-
- // advance and wrap phase
- mPhase += mPhaseIncrement;
- if (mPhase > M_PI) {
- mPhase -= (2.0 * M_PI);
+ if (sineEnabled) {
+ output = (sinOut * mOutputAmplitude)
+ + (mWhiteNoise.nextRandomDouble() * mNoiseAmplitude);
+ // printf("%5d: sin(%f) = %f, %f\n", i, mPhase, sinOut, mPhaseIncrement);
+ // advance and wrap phase
+ mPhase += mPhaseIncrement;
+ if (mPhase > M_PI) {
+ mPhase -= (2.0 * M_PI);
+ }
}
+ outputData[i * outputChannelCount] = output;
+
mFrameCounter++;
}
-
- // Do these once per buffer.
- switch (mState) {
- case STATE_IDLE:
- mState = STATE_IMMUNE; // so we can tell when
- break;
- case STATE_IMMUNE:
- mDownCounter -= numFrames;
- if (mDownCounter <= 0) {
- mState = STATE_WAITING_FOR_SIGNAL;
- //printf("%5d: switch to STATE_WAITING_FOR_SIGNAL\n", mFrameCounter);
- }
- break;
- case STATE_WAITING_FOR_SIGNAL:
- if (peak > mThreshold) {
- mState = STATE_WAITING_FOR_LOCK;
- //printf("%5d: switch to STATE_WAITING_FOR_LOCK\n", mFrameCounter);
- resetAccumulator();
- }
- break;
- case STATE_WAITING_FOR_LOCK:
- case STATE_LOCKED:
- break;
- }
-
}
void resetAccumulator() {
@@ -840,18 +898,24 @@
void reset() override {
mGlitchCount = 0;
- mState = STATE_IMMUNE;
- mDownCounter = IMMUNE_FRAME_COUNT;
+ mState = STATE_IDLE;
+ mDownCounter = IDLE_FRAME_COUNT;
mPhaseIncrement = 2.0 * M_PI / mSinePeriod;
printf("phaseInc = %f for period %d\n", mPhaseIncrement, mSinePeriod);
resetAccumulator();
mProcessCount = 0;
+ mPeakNoise = 0.0f;
+ mNoiseSumSquared = 0.0;
+ mRootMeanSquareNoise = 0.0;
+ mPhase = 0.0f;
+ mMaxGlitchDelta = 0.0;
}
private:
enum sine_state_t {
STATE_IDLE,
+ STATE_MEASURE_NOISE,
STATE_IMMUNE,
STATE_WAITING_FOR_SIGNAL,
STATE_WAITING_FOR_LOCK,
@@ -859,10 +923,16 @@
};
enum constants {
- IMMUNE_FRAME_COUNT = 48 * 500,
- PERIODS_NEEDED_FOR_LOCK = 8
+ // Arbitrary durations, assuming 48000 Hz
+ IDLE_FRAME_COUNT = 48 * 100,
+ NOISE_FRAME_COUNT = 48 * 600,
+ PERIODS_NEEDED_FOR_LOCK = 8,
+ PERIODS_IMMUNE = 2,
+ MIN_SNRATIO_DB = 65
};
+ static constexpr float MIN_TOLERANCE = 0.01;
+
int mSinePeriod = 79;
double mPhaseIncrement = 0.0;
double mPhase = 0.0;
@@ -870,17 +940,23 @@
double mPreviousPhaseOffset = 0.0;
double mMagnitude = 0.0;
double mThreshold = 0.005;
- double mTolerance = 0.01;
+ double mTolerance = MIN_TOLERANCE;
int32_t mFramesAccumulated = 0;
int32_t mProcessCount = 0;
double mSinAccumulator = 0.0;
double mCosAccumulator = 0.0;
+ float mMaxGlitchDelta = 0.0f;
int32_t mGlitchCount = 0;
double mPeakAmplitude = 0.0;
- int mDownCounter = IMMUNE_FRAME_COUNT;
+ int mDownCounter = IDLE_FRAME_COUNT;
int32_t mFrameCounter = 0;
float mOutputAmplitude = 0.75;
+ // measure background noise
+ float mPeakNoise = 0.0f;
+ double mNoiseSumSquared = 0.0;
+ double mRootMeanSquareNoise = 0.0;
+
PseudoRandom mWhiteNoise;
float mNoiseAmplitude = 0.00; // Used to experiment with warbling caused by DRC.
diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp
index 84f9c22..124efd8 100644
--- a/media/libaaudio/examples/loopback/src/loopback.cpp
+++ b/media/libaaudio/examples/loopback/src/loopback.cpp
@@ -37,13 +37,12 @@
// Tag for machine readable results as property = value pairs
#define RESULT_TAG "RESULT: "
-#define NUM_SECONDS 5
-#define PERIOD_MILLIS 1000
-#define NUM_INPUT_CHANNELS 1
#define FILENAME_ALL "/data/loopback_all.wav"
#define FILENAME_ECHOS "/data/loopback_echos.wav"
-#define APP_VERSION "0.2.04"
+#define APP_VERSION "0.3.00"
+constexpr int kLogPeriodMillis = 1000;
+constexpr int kNumInputChannels = 1;
constexpr int kNumCallbacksToDrain = 20;
constexpr int kNumCallbacksToDiscard = 20;
@@ -336,7 +335,7 @@
aaudio_result_t result = AAUDIO_OK;
aaudio_sharing_mode_t requestedInputSharingMode = AAUDIO_SHARING_MODE_SHARED;
- int requestedInputChannelCount = NUM_INPUT_CHANNELS;
+ int requestedInputChannelCount = kNumInputChannels;
aaudio_format_t requestedInputFormat = AAUDIO_FORMAT_UNSPECIFIED;
int32_t requestedInputCapacity = AAUDIO_UNSPECIFIED;
aaudio_performance_mode_t inputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
@@ -459,7 +458,9 @@
argParser.setPerformanceMode(inputPerformanceLevel);
argParser.setChannelCount(requestedInputChannelCount);
argParser.setSharingMode(requestedInputSharingMode);
- // Warning! If you change input capacity then you may not get a FAST track on Legacy path.
+ if (requestedInputCapacity != AAUDIO_UNSPECIFIED) {
+ printf("Warning! If you set input capacity then maybe no FAST track on Legacy path!\n");
+ }
argParser.setBufferCapacity(requestedInputCapacity);
result = recorder.open(argParser);
@@ -510,15 +511,11 @@
// Start OUTPUT first so INPUT does not overflow.
result = player.start();
if (result != AAUDIO_OK) {
- printf("ERROR - AAudioStream_requestStart(output) returned %d = %s\n",
- result, AAudio_convertResultToText(result));
goto finish;
}
result = recorder.start();
if (result != AAUDIO_OK) {
- printf("ERROR - AAudioStream_requestStart(input) returned %d = %s\n",
- result, AAudio_convertResultToText(result));
goto finish;
}
@@ -561,7 +558,7 @@
AAudioStream_getXRunCount(outputStream)
);
}
- int32_t periodMillis = (timeMillis < 2000) ? PERIOD_MILLIS / 4 : PERIOD_MILLIS;
+ int32_t periodMillis = (timeMillis < 2000) ? kLogPeriodMillis / 4 : kLogPeriodMillis;
usleep(periodMillis * 1000);
timeMillis += periodMillis;
}
@@ -586,12 +583,12 @@
if (loopbackData.inputError == AAUDIO_OK) {
if (testMode == TEST_SINE_MAGNITUDE) {
printAudioGraph(loopbackData.audioRecording, 200);
+ // Print again so we don't have to scroll past waveform.
+ printf("OUTPUT Stream ----------------------------------------\n");
+ argParser.compareWithStream(outputStream);
+ printf("INPUT Stream ----------------------------------------\n");
+ argParser.compareWithStream(inputStream);
}
- // Print again so we don't have to scroll past waveform.
- printf("OUTPUT Stream ----------------------------------------\n");
- argParser.compareWithStream(outputStream);
- printf("INPUT Stream ----------------------------------------\n");
- argParser.compareWithStream(inputStream);
loopbackData.loopbackProcessor->report();
}
@@ -600,21 +597,21 @@
int32_t framesRead = AAudioStream_getFramesRead(inputStream);
int32_t framesWritten = AAudioStream_getFramesWritten(inputStream);
printf("Callback Results ---------------------------------------- INPUT\n");
- printf(" input overruns = %d\n", AAudioStream_getXRunCount(inputStream));
+ printf(" input overruns = %8d\n", AAudioStream_getXRunCount(inputStream));
printf(" framesWritten = %8d\n", framesWritten);
printf(" framesRead = %8d\n", framesRead);
printf(" myFramesRead = %8d\n", (int) loopbackData.framesReadTotal);
printf(" written - read = %8d\n", (int) (framesWritten - framesRead));
printf(" insufficient # = %8d\n", (int) loopbackData.insufficientReadCount);
if (loopbackData.insufficientReadCount > 0) {
- printf(" insufficient frames = %8d\n", (int) loopbackData.insufficientReadFrames);
+ printf(" insuffic. frames = %8d\n", (int) loopbackData.insufficientReadFrames);
}
}
{
int32_t framesRead = AAudioStream_getFramesRead(outputStream);
int32_t framesWritten = AAudioStream_getFramesWritten(outputStream);
printf("Callback Results ---------------------------------------- OUTPUT\n");
- printf(" output underruns = %d\n", AAudioStream_getXRunCount(outputStream));
+ printf(" output underruns = %8d\n", AAudioStream_getXRunCount(outputStream));
printf(" myFramesWritten = %8d\n", (int) loopbackData.framesWrittenTotal);
printf(" framesWritten = %8d\n", framesWritten);
printf(" framesRead = %8d\n", framesRead);
@@ -659,4 +656,3 @@
return EXIT_SUCCESS;
}
}
-
diff --git a/media/libaaudio/examples/utils/AAudioSimplePlayer.h b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
index 54b77ba..1645986 100644
--- a/media/libaaudio/examples/utils/AAudioSimplePlayer.h
+++ b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
@@ -193,7 +193,7 @@
aaudio_result_t start() {
aaudio_result_t result = AAudioStream_requestStart(mStream);
if (result != AAUDIO_OK) {
- printf("ERROR - AAudioStream_requestStart() returned %d %s\n",
+ printf("ERROR - AAudioStream_requestStart(output) returned %d %s\n",
result, AAudio_convertResultToText(result));
}
return result;
@@ -203,7 +203,7 @@
aaudio_result_t stop() {
aaudio_result_t result = AAudioStream_requestStop(mStream);
if (result != AAUDIO_OK) {
- printf("ERROR - AAudioStream_requestStop() returned %d %s\n",
+ printf("ERROR - AAudioStream_requestStop(output) returned %d %s\n",
result, AAudio_convertResultToText(result));
}
int32_t xRunCount = AAudioStream_getXRunCount(mStream);
@@ -215,7 +215,7 @@
aaudio_result_t pause() {
aaudio_result_t result = AAudioStream_requestPause(mStream);
if (result != AAUDIO_OK) {
- printf("ERROR - AAudioStream_requestPause() returned %d %s\n",
+ printf("ERROR - AAudioStream_requestPause(output) returned %d %s\n",
result, AAudio_convertResultToText(result));
}
int32_t xRunCount = AAudioStream_getXRunCount(mStream);
@@ -223,11 +223,27 @@
return result;
}
+ aaudio_result_t waitUntilPaused() {
+ aaudio_result_t result = AAUDIO_OK;
+ aaudio_stream_state_t currentState = AAudioStream_getState(mStream);
+ aaudio_stream_state_t inputState = AAUDIO_STREAM_STATE_PAUSING;
+ while (result == AAUDIO_OK && currentState == AAUDIO_STREAM_STATE_PAUSING) {
+ result = AAudioStream_waitForStateChange(mStream, inputState,
+ ¤tState, NANOS_PER_SECOND);
+ inputState = currentState;
+ }
+ if (result != AAUDIO_OK) {
+ return result;
+ }
+ return (currentState == AAUDIO_STREAM_STATE_PAUSED)
+ ? AAUDIO_OK : AAUDIO_ERROR_INVALID_STATE;
+ }
+
// Flush the stream. AAudio will stop calling your callback function.
aaudio_result_t flush() {
aaudio_result_t result = AAudioStream_requestFlush(mStream);
if (result != AAUDIO_OK) {
- printf("ERROR - AAudioStream_requestFlush() returned %d %s\n",
+ printf("ERROR - AAudioStream_requestFlush(output) returned %d %s\n",
result, AAudio_convertResultToText(result));
}
return result;
diff --git a/media/libaaudio/examples/utils/AAudioSimpleRecorder.h b/media/libaaudio/examples/utils/AAudioSimpleRecorder.h
index 869fad0..246e2d7 100644
--- a/media/libaaudio/examples/utils/AAudioSimpleRecorder.h
+++ b/media/libaaudio/examples/utils/AAudioSimpleRecorder.h
@@ -201,8 +201,10 @@
aaudio_result_t start() {
aaudio_result_t result = AAudioStream_requestStart(mStream);
if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - AAudioStream_requestStart() returned %d %s\n",
+ fprintf(stderr, "ERROR - AAudioStream_requestStart(input) returned %d %s\n",
result, AAudio_convertResultToText(result));
+ fprintf(stderr, " Did you remember to enter: adb root ????\n");
+
}
return result;
}
@@ -211,8 +213,9 @@
aaudio_result_t stop() {
aaudio_result_t result = AAudioStream_requestStop(mStream);
if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - AAudioStream_requestStop() returned %d %s\n",
+ fprintf(stderr, "ERROR - AAudioStream_requestStop(input) returned %d %s\n",
result, AAudio_convertResultToText(result));
+
}
return result;
}
@@ -221,7 +224,7 @@
aaudio_result_t pause() {
aaudio_result_t result = AAudioStream_requestPause(mStream);
if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - AAudioStream_requestPause() returned %d %s\n",
+ fprintf(stderr, "ERROR - AAudioStream_requestPause(input) returned %d %s\n",
result, AAudio_convertResultToText(result));
}
return result;
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/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
index e33e9f8..7a48153 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -30,6 +30,8 @@
#include "AAudioSimplePlayer.h"
#include "AAudioArgsParser.h"
+#define APP_VERSION "0.1.5"
+
/**
* Open stream, play some sine waves, then close the stream.
*
@@ -109,13 +111,13 @@
startedAtNanos = getNanoseconds(CLOCK_MONOTONIC);
for (int second = 0; second < durationSeconds; second++) {
// Sleep a while. Wake up early if there is an error, for example a DISCONNECT.
- long ret = myData.waker.wait(AAUDIO_OK, NANOS_PER_SECOND);
+ myData.waker.wait(AAUDIO_OK, NANOS_PER_SECOND);
int64_t millis =
(getNanoseconds(CLOCK_MONOTONIC) - startedAtNanos) / NANOS_PER_MILLISECOND;
result = myData.waker.get();
- printf("wait() returns %ld, aaudio_result = %d, at %6d millis"
+ printf(" waker result = %d, at %6d millis"
", second = %3d, framesWritten = %8d, underruns = %d\n",
- ret, result, (int) millis,
+ result, (int) millis,
second,
(int) AAudioStream_getFramesWritten(player.getStream()),
(int) AAudioStream_getXRunCount(player.getStream()));
@@ -138,6 +140,10 @@
if (result != AAUDIO_OK) {
goto error;
}
+ result = player.waitUntilPaused();
+ if (result != AAUDIO_OK) {
+ goto error;
+ }
result = player.flush();
}
if (result != AAUDIO_OK) {
@@ -219,7 +225,7 @@
// in a buffer if we hang or crash.
setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
- printf("%s - Play a sine sweep using an AAudio callback V0.1.4\n", argv[0]);
+ printf("%s - Play a sine sweep using an AAudio callback V%s\n", argv[0], APP_VERSION);
for (int i = 1; i < argc; i++) {
const char *arg = argv[i];
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index 8dc31d0..e272f2a 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -146,14 +146,14 @@
AAUDIO_API void AAudioStreamBuilder_setChannelCount(AAudioStreamBuilder* builder,
int32_t channelCount)
{
- AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
- streamBuilder->setSamplesPerFrame(channelCount);
+ AAudioStreamBuilder_setSamplesPerFrame(builder, channelCount);
}
AAUDIO_API void AAudioStreamBuilder_setSamplesPerFrame(AAudioStreamBuilder* builder,
- int32_t channelCount)
+ int32_t samplesPerFrame)
{
- AAudioStreamBuilder_setChannelCount(builder, channelCount);
+ AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
+ streamBuilder->setSamplesPerFrame(samplesPerFrame);
}
AAUDIO_API void AAudioStreamBuilder_setDirection(AAudioStreamBuilder* builder,
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index c86d4ce..8b35a85 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -399,9 +399,10 @@
}
break;
case TRANSFER_CALLBACK:
+ case TRANSFER_SYNC_NOTIF_CALLBACK:
if (cbf == NULL || sharedBuffer != 0) {
- ALOGE("%s(): Transfer type TRANSFER_CALLBACK but cbf == NULL || sharedBuffer != 0",
- __func__);
+ ALOGE("%s(): Transfer type %s but cbf == NULL || sharedBuffer != 0",
+ convertTransferToText(transferType), __func__);
status = BAD_VALUE;
goto exit;
}
@@ -1406,6 +1407,7 @@
MEDIA_CASE_ENUM(TRANSFER_OBTAIN);
MEDIA_CASE_ENUM(TRANSFER_SYNC);
MEDIA_CASE_ENUM(TRANSFER_SHARED);
+ MEDIA_CASE_ENUM(TRANSFER_SYNC_NOTIF_CALLBACK);
default:
return "UNRECOGNIZED";
}
@@ -1438,7 +1440,8 @@
// use case 3: obtain/release mode
(mTransfer == TRANSFER_OBTAIN) ||
// use case 4: synchronous write
- ((mTransfer == TRANSFER_SYNC) && mThreadCanCallJava);
+ ((mTransfer == TRANSFER_SYNC || mTransfer == TRANSFER_SYNC_NOTIF_CALLBACK)
+ && mThreadCanCallJava);
bool fastAllowed = sharedBuffer || transferAllowed;
if (!fastAllowed) {
@@ -1795,7 +1798,7 @@
ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking)
{
- if (mTransfer != TRANSFER_SYNC) {
+ if (mTransfer != TRANSFER_SYNC && mTransfer != TRANSFER_SYNC_NOTIF_CALLBACK) {
return INVALID_OPERATION;
}
@@ -1846,7 +1849,17 @@
if (written > 0) {
mFramesWritten += written / mFrameSize;
+
+ if (mTransfer == TRANSFER_SYNC_NOTIF_CALLBACK) {
+ const sp<AudioTrackThread> t = mAudioTrackThread;
+ if (t != 0) {
+ // causes wake up of the playback thread, that will callback the client for
+ // more data (with EVENT_CAN_WRITE_MORE_DATA) in processAudioBuffer()
+ t->wake();
+ }
+ }
}
+
return written;
}
@@ -2100,8 +2113,8 @@
if (ns < 0) ns = 0;
}
- // If not supplying data by EVENT_MORE_DATA, then we're done
- if (mTransfer != TRANSFER_CALLBACK) {
+ // If not supplying data by EVENT_MORE_DATA or EVENT_CAN_WRITE_MORE_DATA, then we're done
+ if (mTransfer != TRANSFER_CALLBACK && mTransfer != TRANSFER_SYNC_NOTIF_CALLBACK) {
return ns;
}
@@ -2163,7 +2176,13 @@
}
size_t reqSize = audioBuffer.size;
- mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);
+ if (mTransfer == TRANSFER_SYNC_NOTIF_CALLBACK) {
+ // when notifying client it can write more data, pass the total size that can be
+ // written in the next write() call, since it's not passed through the callback
+ audioBuffer.size += nonContig;
+ }
+ mCbf(mTransfer == TRANSFER_CALLBACK ? EVENT_MORE_DATA : EVENT_CAN_WRITE_MORE_DATA,
+ mUserData, &audioBuffer);
size_t writtenSize = audioBuffer.size;
// Sanity check on returned size
@@ -2174,6 +2193,14 @@
}
if (writtenSize == 0) {
+ if (mTransfer == TRANSFER_SYNC_NOTIF_CALLBACK) {
+ // The callback EVENT_CAN_WRITE_MORE_DATA was processed in the JNI of
+ // android.media.AudioTrack. The JNI is not using the callback to provide data,
+ // it only signals to the Java client that it can provide more data, which
+ // this track is read to accept now.
+ // The playback thread will be awaken at the next ::write()
+ return NS_WHENEVER;
+ }
// The callback is done filling buffers
// Keep this thread going to handle timed events and
// still try to get more data in intervals of WAIT_PERIOD_MS
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index c5105af..4b84fd1 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -74,6 +74,8 @@
// in the mapping from frame position to presentation time.
// See AudioTimestamp for the information included with event.
#endif
+ EVENT_CAN_WRITE_MORE_DATA = 9,// Notification that more data can be given by write()
+ // This event only occurs for TRANSFER_SYNC_NOTIF_CALLBACK.
};
/* Client should declare a Buffer and pass the address to obtainBuffer()
@@ -153,6 +155,7 @@
TRANSFER_OBTAIN, // call obtainBuffer() and releaseBuffer()
TRANSFER_SYNC, // synchronous write()
TRANSFER_SHARED, // shared memory
+ TRANSFER_SYNC_NOTIF_CALLBACK, // synchronous write(), notif EVENT_CAN_WRITE_MORE_DATA
};
/* Constructs an uninitialized AudioTrack. No connection with
@@ -295,6 +298,8 @@
* Parameters not listed in the AudioTrack constructors above:
*
* threadCanCallJava: Whether callbacks are made from an attached thread and thus can call JNI.
+ * Only set to true when AudioTrack object is used for a java android.media.AudioTrack
+ * in its JNI code.
*
* Internal state post condition:
* (mStreamType == AUDIO_STREAM_DEFAULT) implies this AudioTrack has valid attributes
diff --git a/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk
index 3534149..35e2f3d 100644
--- a/media/libeffects/visualizer/Android.mk
+++ b/media/libeffects/visualizer/Android.mk
@@ -9,6 +9,7 @@
LOCAL_CFLAGS+= -O2 -fvisibility=hidden
LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -DBUILD_FLOAT -DSUPPORT_MC
LOCAL_SHARED_LIBRARIES := \
libcutils \
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index e2ccfb7..00bc371 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -32,8 +32,6 @@
#include <audio_effects/effect_visualizer.h>
#include <audio_utils/primitives.h>
-#define BUILD_FLOAT
-
#ifdef BUILD_FLOAT
static constexpr audio_format_t kProcessFormat = AUDIO_FORMAT_PCM_FLOAT;
@@ -157,7 +155,12 @@
if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate) return -EINVAL;
if (pConfig->inputCfg.channels != pConfig->outputCfg.channels) return -EINVAL;
if (pConfig->inputCfg.format != pConfig->outputCfg.format) return -EINVAL;
- if (pConfig->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL;
+ const uint32_t channelCount = audio_channel_count_from_out_mask(pConfig->inputCfg.channels);
+#ifdef SUPPORT_MC
+ if (channelCount < 1 || channelCount > FCC_8) return -EINVAL;
+#else
+ if (channelCount != FCC_2) return -EINVAL;
+#endif
if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE &&
pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL;
if (pConfig->inputCfg.format != kProcessFormat) return -EINVAL;
@@ -356,7 +359,7 @@
// store the measurement
pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mPeakU16 = (uint16_t)maxSample;
pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mRmsSquared =
- rmsSqAcc / (inBuffer->frameCount * pContext->mChannelCount);
+ rmsSqAcc / sampleLen;
pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mIsValid = true;
if (++pContext->mMeasurementBufferIdx >= pContext->mMeasurementWindowSizeInBuffers) {
pContext->mMeasurementBufferIdx = 0;
@@ -375,12 +378,17 @@
#ifdef BUILD_FLOAT
float maxSample = 0.f;
- for (size_t inIdx = 0; inIdx < sampleLen; ++inIdx) {
- maxSample = fmax(maxSample, fabs(inBuffer->f32[inIdx]));
+ for (size_t inIdx = 0; inIdx < sampleLen; ) {
+ // we reconstruct the actual summed value to ensure proper normalization
+ // for multichannel outputs (channels > 2 may often be 0).
+ float smp = 0.f;
+ for (int i = 0; i < pContext->mChannelCount; ++i) {
+ smp += inBuffer->f32[inIdx++];
+ }
+ maxSample = fmax(maxSample, fabs(smp));
}
if (maxSample > 0.f) {
- constexpr float halfish = 127.f / 256.f;
- fscale = halfish / maxSample;
+ fscale = 127.f / maxSample;
int exp; // unused
const float significand = frexp(fscale, &exp);
if (significand == 0.5f) {
@@ -412,7 +420,8 @@
} else {
assert(pContext->mScalingMode == VISUALIZER_SCALING_MODE_AS_PLAYED);
#ifdef BUILD_FLOAT
- fscale = 0.5f; // default divide by 2 to account for sum of L + R.
+ // Note: if channels are uncorrelated, 1/sqrt(N) could be used at the risk of clipping.
+ fscale = 1.f / pContext->mChannelCount; // account for summing all the channels together.
#else
shift = 9;
#endif // BUILD_FLOAT
@@ -422,17 +431,19 @@
uint32_t inIdx;
uint8_t *buf = pContext->mCaptureBuf;
for (inIdx = 0, captIdx = pContext->mCaptureIdx;
- inIdx < inBuffer->frameCount;
- inIdx++, captIdx++) {
- if (captIdx >= CAPTURE_BUF_SIZE) {
- // wrap around
- captIdx = 0;
- }
+ inIdx < sampleLen;
+ captIdx++) {
+ if (captIdx >= CAPTURE_BUF_SIZE) captIdx = 0; // wrap
+
#ifdef BUILD_FLOAT
- const float smp = (inBuffer->f32[2 * inIdx] + inBuffer->f32[2 * inIdx + 1]) * fscale;
- buf[captIdx] = clamp8_from_float(smp);
+ float smp = 0.f;
+ for (uint32_t i = 0; i < pContext->mChannelCount; ++i) {
+ smp += inBuffer->f32[inIdx++];
+ }
+ buf[captIdx] = clamp8_from_float(smp * fscale);
#else
- const int32_t smp = (inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1]) >> shift;
+ const int32_t smp = (inBuffer->s16[inIdx] + inBuffer->s16[inIdx + 1]) >> shift;
+ inIdx += FCC_2; // integer supports stereo only.
buf[captIdx] = ((uint8_t)smp)^0x80;
#endif // BUILD_FLOAT
}
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 25d28ff..3d9e62e 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -291,6 +291,7 @@
"libmediaextractor",
"libmediandk",
"libnativewindow",
+ "libmediandk_utils",
"libstagefright_foundation",
"libui",
"libutils",
@@ -305,6 +306,10 @@
"media_plugin_headers",
],
+ include_dirs: [
+ "frameworks/av/media/ndk",
+ ],
+
static_libs: [
"libstagefright_rtsp",
"libstagefright_timedtext",
diff --git a/media/libmedia/MediaUtils.cpp b/media/libmedia/MediaUtils.cpp
index 320c7a9..bcc7ebf 100644
--- a/media/libmedia/MediaUtils.cpp
+++ b/media/libmedia/MediaUtils.cpp
@@ -25,6 +25,7 @@
#include "MediaUtils.h"
extern "C" size_t __cfi_shadow_size();
+extern "C" void __scudo_set_rss_limit(size_t, int) __attribute__((weak));
namespace android {
@@ -65,6 +66,14 @@
maxMem = propVal;
}
+ // If 64-bit Scudo is in use, enforce the hard RSS limit (in MB).
+ if (maxMem != SIZE_MAX && sizeof(void *) == 8 &&
+ &__scudo_set_rss_limit != 0) {
+ __scudo_set_rss_limit(maxMem >> 20, 1);
+ ALOGV("Scudo hard RSS limit set to %zu MB", maxMem >> 20);
+ return;
+ }
+
// Increase by the size of the CFI shadow mapping. Most of the shadow is not
// backed with physical pages, and it is possible for the result to be
// higher than total physical memory. This is fine for RLIMIT_AS.
diff --git a/media/libmedia/NdkWrapper.cpp b/media/libmedia/NdkWrapper.cpp
index 9e09c7e..eed96e7 100644
--- a/media/libmedia/NdkWrapper.cpp
+++ b/media/libmedia/NdkWrapper.cpp
@@ -31,6 +31,8 @@
#include <media/stagefright/foundation/AMessage.h>
#include <utils/Errors.h>
+#include "NdkMediaDataSourceCallbacksPriv.h"
+
namespace android {
static const size_t kAESBlockSize = 16; // AES_BLOCK_SIZE
@@ -1244,8 +1246,14 @@
return new AMediaCodecCryptoInfoWrapper(AMediaExtractor_getSampleCryptoInfo(mAMediaExtractor));
}
+AMediaDataSourceWrapper::AMediaDataSourceWrapper(const sp<DataSource> &dataSource)
+ : mDataSource(dataSource),
+ mAMediaDataSource(convertDataSourceToAMediaDataSource(dataSource)) {
+}
+
AMediaDataSourceWrapper::AMediaDataSourceWrapper(AMediaDataSource *aDataSource)
- : mAMediaDataSource(aDataSource) {
+ : mDataSource(NULL),
+ mAMediaDataSource(aDataSource) {
}
AMediaDataSourceWrapper::~AMediaDataSourceWrapper() {
diff --git a/media/libmedia/include/media/Crypto.h b/media/libmedia/include/media/Crypto.h
deleted file mode 100644
index b68413d..0000000
--- a/media/libmedia/include/media/Crypto.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2012 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 CRYPTO_H_
-
-#define CRYPTO_H_
-
-#include <media/ICrypto.h>
-#include <utils/threads.h>
-#include <utils/KeyedVector.h>
-
-#include "SharedLibrary.h"
-
-namespace android {
-
-struct CryptoFactory;
-struct CryptoPlugin;
-
-struct Crypto : public BnCrypto {
- Crypto();
- virtual ~Crypto();
-
- virtual status_t initCheck() const;
-
- virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]);
-
- virtual status_t createPlugin(
- const uint8_t uuid[16], const void *data, size_t size);
-
- virtual status_t destroyPlugin();
-
- virtual bool requiresSecureDecoderComponent(
- const char *mime) const;
-
- virtual void notifyResolution(uint32_t width, uint32_t height);
-
- virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId);
-
- virtual ssize_t decrypt(const uint8_t key[16], const uint8_t iv[16],
- CryptoPlugin::Mode mode, const CryptoPlugin::Pattern &pattern,
- const sp<IMemory> &source, size_t offset,
- const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
- const DestinationBuffer &destination, AString *errorDetailMsg);
-
- virtual void setHeap(const sp<IMemoryHeap>&) {}
- virtual void unsetHeap(const sp<IMemoryHeap>&) {}
-
-private:
- mutable Mutex mLock;
-
- status_t mInitCheck;
- sp<SharedLibrary> mLibrary;
- CryptoFactory *mFactory;
- CryptoPlugin *mPlugin;
-
- static KeyedVector<Vector<uint8_t>, String8> mUUIDToLibraryPathMap;
- static KeyedVector<String8, wp<SharedLibrary> > mLibraryPathToOpenLibraryMap;
- static Mutex mMapLock;
-
- void findFactoryForScheme(const uint8_t uuid[16]);
- bool loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]);
- void closeFactory();
-
- DISALLOW_EVIL_CONSTRUCTORS(Crypto);
-};
-
-} // namespace android
-
-#endif // CRYPTO_H_
diff --git a/media/libmedia/include/media/DataSourceDesc.h b/media/libmedia/include/media/DataSourceDesc.h
index c190261..4336767 100644
--- a/media/libmedia/include/media/DataSourceDesc.h
+++ b/media/libmedia/include/media/DataSourceDesc.h
@@ -30,6 +30,11 @@
// A binder interface for implementing a stagefright DataSource remotely.
struct DataSourceDesc : public RefBase {
public:
+ // intentionally less than INT64_MAX
+ // keep consistent with JAVA code
+ static const int64_t kMaxTimeMs = 0x7ffffffffffffffll / 1000;
+ static const int64_t kMaxTimeUs = kMaxTimeMs * 1000;
+
enum {
/* No data source has been set yet */
TYPE_NONE = 0,
diff --git a/media/libmedia/include/media/Drm.h b/media/libmedia/include/media/Drm.h
deleted file mode 100644
index fc869cc..0000000
--- a/media/libmedia/include/media/Drm.h
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef DRM_H_
-
-#define DRM_H_
-
-#include "SharedLibrary.h"
-
-#include <media/IDrm.h>
-#include <media/IDrmClient.h>
-#include <utils/threads.h>
-
-namespace android {
-
-class DrmFactory;
-class DrmPlugin;
-struct DrmSessionClientInterface;
-
-struct Drm : public BnDrm,
- public IBinder::DeathRecipient,
- public DrmPluginListener {
- Drm();
- virtual ~Drm();
-
- virtual status_t initCheck() const;
-
- virtual bool isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType);
-
- virtual status_t createPlugin(const uint8_t uuid[16], const String8 &appPackageName);
-
- virtual status_t destroyPlugin();
-
- virtual status_t openSession(Vector<uint8_t> &sessionId);
-
- virtual status_t closeSession(Vector<uint8_t> const &sessionId);
-
- virtual status_t
- getKeyRequest(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &initData,
- String8 const &mimeType, DrmPlugin::KeyType keyType,
- KeyedVector<String8, String8> const &optionalParameters,
- Vector<uint8_t> &request, String8 &defaultUrl,
- DrmPlugin::KeyRequestType *keyRequestType);
-
- virtual status_t provideKeyResponse(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &response,
- Vector<uint8_t> &keySetId);
-
- virtual status_t removeKeys(Vector<uint8_t> const &keySetId);
-
- virtual status_t restoreKeys(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &keySetId);
-
- virtual status_t queryKeyStatus(Vector<uint8_t> const &sessionId,
- KeyedVector<String8, String8> &infoMap) const;
-
- virtual status_t getProvisionRequest(String8 const &certType,
- String8 const &certAuthority,
- Vector<uint8_t> &request,
- String8 &defaulUrl);
-
- virtual status_t provideProvisionResponse(Vector<uint8_t> const &response,
- Vector<uint8_t> &certificate,
- Vector<uint8_t> &wrappedKey);
-
- virtual status_t getSecureStops(List<Vector<uint8_t> > &secureStops);
- virtual status_t getSecureStop(Vector<uint8_t> const &ssid, Vector<uint8_t> &secureStop);
-
- virtual status_t releaseSecureStops(Vector<uint8_t> const &ssRelease);
- virtual status_t releaseAllSecureStops();
-
- virtual status_t getPropertyString(String8 const &name, String8 &value ) const;
- virtual status_t getPropertyByteArray(String8 const &name,
- Vector<uint8_t> &value ) const;
- virtual status_t setPropertyString(String8 const &name, String8 const &value ) const;
- virtual status_t setPropertyByteArray(String8 const &name,
- Vector<uint8_t> const &value ) const;
-
- virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
- String8 const &algorithm);
-
- virtual status_t setMacAlgorithm(Vector<uint8_t> const &sessionId,
- String8 const &algorithm);
-
- virtual status_t encrypt(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &keyId,
- Vector<uint8_t> const &input,
- Vector<uint8_t> const &iv,
- Vector<uint8_t> &output);
-
- virtual status_t decrypt(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &keyId,
- Vector<uint8_t> const &input,
- Vector<uint8_t> const &iv,
- Vector<uint8_t> &output);
-
- virtual status_t sign(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &keyId,
- Vector<uint8_t> const &message,
- Vector<uint8_t> &signature);
-
- virtual status_t verify(Vector<uint8_t> const &sessionId,
- Vector<uint8_t> const &keyId,
- Vector<uint8_t> const &message,
- Vector<uint8_t> const &signature,
- bool &match);
-
- virtual status_t signRSA(Vector<uint8_t> const &sessionId,
- String8 const &algorithm,
- Vector<uint8_t> const &message,
- Vector<uint8_t> const &wrappedKey,
- Vector<uint8_t> &signature);
-
- virtual status_t setListener(const sp<IDrmClient>& listener);
-
- virtual void sendEvent(DrmPlugin::EventType eventType, int extra,
- Vector<uint8_t> const *sessionId,
- Vector<uint8_t> const *data);
-
- virtual void sendExpirationUpdate(Vector<uint8_t> const *sessionId,
- int64_t expiryTimeInMS);
-
- virtual void sendKeysChange(Vector<uint8_t> const *sessionId,
- Vector<DrmPlugin::KeyStatus> const *keyStatusList,
- bool hasNewUsableKey);
-
- virtual void binderDied(const wp<IBinder> &the_late_who);
-
-private:
- static Mutex mLock;
-
- status_t mInitCheck;
-
- sp<DrmSessionClientInterface> mDrmSessionClient;
-
- sp<IDrmClient> mListener;
- mutable Mutex mEventLock;
- mutable Mutex mNotifyLock;
-
- sp<SharedLibrary> mLibrary;
- DrmFactory *mFactory;
- DrmPlugin *mPlugin;
-
- static KeyedVector<Vector<uint8_t>, String8> mUUIDToLibraryPathMap;
- static KeyedVector<String8, wp<SharedLibrary> > mLibraryPathToOpenLibraryMap;
- static Mutex mMapLock;
-
- void findFactoryForScheme(const uint8_t uuid[16]);
- bool loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]);
- void closeFactory();
- void writeByteArray(Parcel &obj, Vector<uint8_t> const *array);
-
- DISALLOW_EVIL_CONSTRUCTORS(Drm);
-};
-
-} // namespace android
-
-#endif // CRYPTO_H_
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/libmedia/include/media/NdkWrapper.h b/media/libmedia/include/media/NdkWrapper.h
index b3b0688..8a417f6 100644
--- a/media/libmedia/include/media/NdkWrapper.h
+++ b/media/libmedia/include/media/NdkWrapper.h
@@ -280,13 +280,10 @@
struct AMediaDataSourceWrapper : public RefBase {
+ AMediaDataSourceWrapper(const sp<DataSource>&);
AMediaDataSourceWrapper(AMediaDataSource*);
- AMediaDataSourceWrapper(int fd, int64_t offset, int64_t length);
AMediaDataSource *getAMediaDataSource();
- int getFd() { return mFd; }
- int64_t getOffset() { return mOffset; }
- int64_t getLength() { return mLength; }
void close();
@@ -294,10 +291,8 @@
virtual ~AMediaDataSourceWrapper();
private:
+ sp<DataSource> mDataSource;
AMediaDataSource *mAMediaDataSource;
- int mFd;
- int64_t mOffset;
- int64_t mLength;
DISALLOW_EVIL_CONSTRUCTORS(AMediaDataSourceWrapper);
};
diff --git a/media/libmedia/include/media/mediametadataretriever.h b/media/libmedia/include/media/mediametadataretriever.h
index cdef637..d29e97d 100644
--- a/media/libmedia/include/media/mediametadataretriever.h
+++ b/media/libmedia/include/media/mediametadataretriever.h
@@ -68,6 +68,11 @@
METADATA_KEY_VIDEO_FRAME_COUNT = 32,
METADATA_KEY_EXIF_OFFSET = 33,
METADATA_KEY_EXIF_LENGTH = 34,
+ METADATA_KEY_COLOR_STANDARD = 35,
+ METADATA_KEY_COLOR_TRANSFER = 36,
+ METADATA_KEY_COLOR_RANGE = 37,
+ METADATA_KEY_SAMPLERATE = 38,
+ METADATA_KEY_BITS_PER_SAMPLE = 39,
// Add more here...
};
diff --git a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
index 0a99974..eea7cfc 100644
--- a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
+++ b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
@@ -89,7 +89,6 @@
kKeyMaxHeight = 'maxH',
kKeyThumbnailTime = 'thbT', // int64_t (usecs)
kKeyTrackID = 'trID',
- kKeyIsDRM = 'idrm', // int32_t (bool)
kKeyEncoderDelay = 'encd', // int32_t (frames)
kKeyEncoderPadding = 'encp', // int32_t (frames)
@@ -222,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 3b155c5..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() {
@@ -404,12 +325,12 @@
return BAD_VALUE;
}
// Microsecond is used in NuPlayer2.
- if (dsd->mStartPositionMs > INT64_MAX / 1000) {
- dsd->mStartPositionMs = INT64_MAX / 1000;
+ if (dsd->mStartPositionMs > DataSourceDesc::kMaxTimeMs) {
+ dsd->mStartPositionMs = DataSourceDesc::kMaxTimeMs;
ALOGW("setDataSource, start poistion clamped to %lld ms", (long long)dsd->mStartPositionMs);
}
- if (dsd->mEndPositionMs > INT64_MAX / 1000) {
- dsd->mEndPositionMs = INT64_MAX / 1000;
+ if (dsd->mEndPositionMs > DataSourceDesc::kMaxTimeMs) {
+ dsd->mEndPositionMs = DataSourceDesc::kMaxTimeMs;
ALOGW("setDataSource, end poistion clamped to %lld ms", (long long)dsd->mStartPositionMs);
}
ALOGV("setDataSource type(%d), srcId(%lld)", dsd->mType, (long long)dsd->mId);
@@ -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/GenericSource2.cpp b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
index 6056ad9..1860b0c 100644
--- a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
+++ b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
@@ -34,7 +34,6 @@
#include <media/stagefright/MetaData.h>
#include <media/stagefright/NdkUtils.h>
#include <media/stagefright/Utils.h>
-#include "NdkMediaDataSourceCallbacksPriv.h"
namespace android {
@@ -79,6 +78,7 @@
void NuPlayer2::GenericSource2::resetDataSource() {
ALOGV("resetDataSource");
+ mDisconnected = false;
mUri.clear();
mUriHeaders.clear();
if (mFd >= 0) {
@@ -136,8 +136,7 @@
ALOGV("setDataSource (source: %p)", source.get());
resetDataSource();
- AMediaDataSource *aSource = convertDataSourceToAMediaDataSource(source);
- mDataSourceWrapper = new AMediaDataSourceWrapper(aSource);
+ mDataSourceWrapper = new AMediaDataSourceWrapper(source);
return OK;
}
@@ -196,7 +195,11 @@
}
sp<AMediaExtractorWrapper> trackExtractor = new AMediaExtractorWrapper(AMediaExtractor_new());
- trackExtractor->setDataSource(mDataSourceWrapper->getAMediaDataSource());
+ if (aSourceWrapper != NULL) {
+ trackExtractor->setDataSource(aSourceWrapper->getAMediaDataSource());
+ } else {
+ trackExtractor->setDataSource(fd, mOffset, mLength);
+ }
const char *mime;
sp<MetaData> meta = convertMediaFormatWrapperToMetaData(trackFormat);
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 7b9ff30..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);
@@ -1753,8 +1756,13 @@
}
void NuPlayer2::addEndTimeMonitor() {
- sp<AMessage> msg = new AMessage(kWhatEOSMonitor, this);
++mEOSMonitorGeneration;
+
+ if (mCurrentSourceInfo.mEndTimeUs == DataSourceDesc::kMaxTimeUs) {
+ return;
+ }
+
+ sp<AMessage> msg = new AMessage(kWhatEOSMonitor, this);
msg->setInt32("generation", mEOSMonitorGeneration);
mMediaClock->addTimer(msg, mCurrentSourceInfo.mEndTimeUs);
}
@@ -3216,7 +3224,7 @@
mSrcId(0),
mSourceFlags(0),
mStartTimeUs(0),
- mEndTimeUs(INT64_MAX) {
+ mEndTimeUs(DataSourceDesc::kMaxTimeUs) {
}
NuPlayer2::SourceInfo & NuPlayer2::SourceInfo::operator=(const NuPlayer2::SourceInfo &other) {
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/Android.bp b/media/libmediaplayerservice/Android.bp
index a37973b..09b19d7 100644
--- a/media/libmediaplayerservice/Android.bp
+++ b/media/libmediaplayerservice/Android.bp
@@ -29,6 +29,7 @@
"libmediametrics",
"libmediautils",
"libmemunreachable",
+ "libnetd_client",
"libpowermanager",
"libstagefright",
"libstagefright_foundation",
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 30c0b1c..e3ae02e 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1996,7 +1996,7 @@
mTotalPausedDurationUs += resumeStartTimeUs - mPauseStartTimeUs - 30000;
}
double timeOffset = -mTotalPausedDurationUs;
- if (mCaptureFpsEnable) {
+ if (mCaptureFpsEnable && (mVideoSource == VIDEO_SOURCE_CAMERA)) {
timeOffset *= mCaptureFps / mFrameRate;
}
sp<MetaData> meta = new MetaData;
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/Android.bp b/media/libstagefright/Android.bp
index 3208d16..d96d358 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -239,52 +239,29 @@
name: "libstagefright_player2",
srcs: [
- "CallbackDataSource.cpp",
- "CallbackMediaSource.cpp",
- "ClearDataSourceFactory.cpp",
"ClearFileSource.cpp",
"DataURISource.cpp",
"HTTPBase.cpp",
"HevcUtils.cpp",
- "InterfaceUtils.cpp",
"MediaClock.cpp",
- "MediaExtractor.cpp",
- "MediaExtractorFactory.cpp",
"NdkUtils.cpp",
- "NuCachedSource2.cpp",
- "RemoteMediaExtractor.cpp",
- "RemoteMediaSource.cpp",
"Utils.cpp",
"VideoFrameScheduler.cpp",
"http/ClearMediaHTTP.cpp",
],
shared_libs: [
- "libbinder",
- "libcutils",
"libgui",
"liblog",
- "libaudioclient",
- "libmediaextractor",
- "libmediametrics",
- "libmediautils",
"libnetd_client",
- "libui",
"libutils",
- "libmedia_helper",
"libstagefright_foundation",
- "libziparchive",
],
static_libs: [
- "libstagefright_esds",
"libmedia_player2_util",
],
- header_libs:[
- "media_plugin_headers",
- ],
-
export_include_dirs: [
"include",
],
diff --git a/media/libstagefright/ClearDataSourceFactory.cpp b/media/libstagefright/ClearDataSourceFactory.cpp
deleted file mode 100644
index 5d23fda..0000000
--- a/media/libstagefright/ClearDataSourceFactory.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-//#define LOG_NDEBUG 0
-#define LOG_TAG "ClearDataSourceFactory"
-
-#include "include/HTTPBase.h"
-#include "include/NuCachedSource2.h"
-
-#include <media/MediaHTTPConnection.h>
-#include <media/MediaHTTPService.h>
-#include <media/stagefright/ClearFileSource.h>
-#include <media/stagefright/ClearMediaHTTP.h>
-#include <media/stagefright/ClearDataSourceFactory.h>
-#include <media/stagefright/DataURISource.h>
-#include <utils/String8.h>
-
-namespace android {
-
-// static
-sp<DataSource> ClearDataSourceFactory::CreateFromURI(
- const sp<MediaHTTPService> &httpService,
- const char *uri,
- const KeyedVector<String8, String8> *headers,
- String8 *contentType,
- HTTPBase *httpSource) {
- if (contentType != NULL) {
- *contentType = "";
- }
-
- sp<DataSource> source;
- if (!strncasecmp("file://", uri, 7)) {
- source = new ClearFileSource(uri + 7);
- } else if (!strncasecmp("http://", uri, 7) || !strncasecmp("https://", uri, 8)) {
- if (httpService == NULL) {
- ALOGE("Invalid http service!");
- return NULL;
- }
-
- if (httpSource == NULL) {
- sp<MediaHTTPConnection> conn = httpService->makeHTTPConnection();
- if (conn == NULL) {
- ALOGE("Failed to make http connection from http service!");
- return NULL;
- }
- httpSource = new ClearMediaHTTP(conn);
- }
-
- String8 cacheConfig;
- bool disconnectAtHighwatermark = false;
- KeyedVector<String8, String8> nonCacheSpecificHeaders;
- if (headers != NULL) {
- nonCacheSpecificHeaders = *headers;
- NuCachedSource2::RemoveCacheSpecificHeaders(
- &nonCacheSpecificHeaders,
- &cacheConfig,
- &disconnectAtHighwatermark);
- }
-
- if (httpSource->connect(uri, &nonCacheSpecificHeaders) != OK) {
- ALOGE("Failed to connect http source!");
- return NULL;
- }
-
- if (contentType != NULL) {
- *contentType = httpSource->getMIMEType();
- }
-
- source = NuCachedSource2::Create(
- httpSource,
- cacheConfig.isEmpty() ? NULL : cacheConfig.string(),
- disconnectAtHighwatermark);
- } else if (!strncasecmp("data:", uri, 5)) {
- source = DataURISource::Create(uri);
- } else {
- // Assume it's a filename.
- source = new ClearFileSource(uri);
- }
-
- if (source == NULL || source->initCheck() != OK) {
- return NULL;
- }
-
- return source;
-}
-
-sp<DataSource> ClearDataSourceFactory::CreateFromFd(int fd, int64_t offset, int64_t length) {
- sp<ClearFileSource> source = new ClearFileSource(fd, offset, length);
- return source->initCheck() != OK ? nullptr : source;
-}
-
-sp<DataSource> ClearDataSourceFactory::CreateMediaHTTP(const sp<MediaHTTPService> &httpService) {
- if (httpService == NULL) {
- return NULL;
- }
-
- sp<MediaHTTPConnection> conn = httpService->makeHTTPConnection();
- if (conn == NULL) {
- return NULL;
- } else {
- return new ClearMediaHTTP(conn);
- }
-}
-
-} // namespace android
diff --git a/media/libstagefright/HTTPBase.cpp b/media/libstagefright/HTTPBase.cpp
index 0f24329..03e0d12 100644
--- a/media/libstagefright/HTTPBase.cpp
+++ b/media/libstagefright/HTTPBase.cpp
@@ -114,30 +114,4 @@
mMaxBandwidthHistoryItems = numHistoryItems;
}
-// static
-void HTTPBase::RegisterSocketUserTag(int sockfd, uid_t uid, uint32_t kTag) {
- int res = qtaguid_tagSocket(sockfd, kTag, uid);
- if (res != 0) {
- ALOGE("Failed tagging socket %d for uid %d (My UID=%d)", sockfd, uid, geteuid());
- }
-}
-
-// static
-void HTTPBase::UnRegisterSocketUserTag(int sockfd) {
- int res = qtaguid_untagSocket(sockfd);
- if (res != 0) {
- ALOGE("Failed untagging socket %d (My UID=%d)", sockfd, geteuid());
- }
-}
-
-// static
-void HTTPBase::RegisterSocketUserMark(int sockfd, uid_t uid) {
- setNetworkForUser(uid, sockfd);
-}
-
-// static
-void HTTPBase::UnRegisterSocketUserMark(int sockfd) {
- RegisterSocketUserMark(sockfd, geteuid());
-}
-
} // namespace android
diff --git a/media/libstagefright/MediaTrack.cpp b/media/libstagefright/MediaTrack.cpp
index 5ad5295..9fbb20e 100644
--- a/media/libstagefright/MediaTrack.cpp
+++ b/media/libstagefright/MediaTrack.cpp
@@ -135,13 +135,13 @@
uint32_t opts = 0;
- if (options->getNonBlocking()) {
+ if (options && options->getNonBlocking()) {
opts |= CMediaTrackReadOptions::NONBLOCKING;
}
int64_t seekPosition = 0;
MediaTrack::ReadOptions::SeekMode seekMode;
- if (options->getSeekTo(&seekPosition, &seekMode)) {
+ if (options && options->getSeekTo(&seekPosition, &seekMode)) {
opts |= SEEK;
opts |= (uint32_t) seekMode;
}
diff --git a/media/libstagefright/MetaDataUtils.cpp b/media/libstagefright/MetaDataUtils.cpp
index 2cf79c3..a3259fd 100644
--- a/media/libstagefright/MetaDataUtils.cpp
+++ b/media/libstagefright/MetaDataUtils.cpp
@@ -56,6 +56,32 @@
return true;
}
+bool MakeAVCCodecSpecificData(AMediaFormat *meta, const uint8_t *data, size_t size) {
+ if (meta == nullptr || data == nullptr || size == 0) {
+ return false;
+ }
+
+ int32_t width;
+ int32_t height;
+ int32_t sarWidth;
+ int32_t sarHeight;
+ sp<ABuffer> accessUnit = new ABuffer((void*)data, size);
+ sp<ABuffer> csd = MakeAVCCodecSpecificData(accessUnit, &width, &height, &sarWidth, &sarHeight);
+ if (csd == nullptr) {
+ return false;
+ }
+ AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AVC);
+
+ AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CSD_AVC, csd->data(), csd->size());
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_WIDTH, width);
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_HEIGHT, height);
+ if (sarWidth > 0 && sarHeight > 0) {
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_SAR_WIDTH, sarWidth);
+ AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_SAR_HEIGHT, sarHeight);
+ }
+ return true;
+}
+
bool MakeAACCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size) {
if (data == nullptr || size < 7) {
return false;
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index e010b3e..610b961 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -132,6 +132,9 @@
{ "date", METADATA_KEY_DATE },
{ "width", METADATA_KEY_VIDEO_WIDTH },
{ "height", METADATA_KEY_VIDEO_HEIGHT },
+ { "colorstandard", METADATA_KEY_COLOR_STANDARD },
+ { "colortransfer", METADATA_KEY_COLOR_TRANSFER },
+ { "colorrange", METADATA_KEY_COLOR_RANGE },
};
static const size_t kNumEntries = sizeof(kKeyMap) / sizeof(kKeyMap[0]);
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 231d540..f34d54c 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -36,6 +36,7 @@
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MediaExtractorFactory.h>
#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
#include <media/CharacterEncodingDetector.h>
namespace android {
@@ -275,12 +276,6 @@
return NO_INIT;
}
- int32_t drm = 0;
- if (fileMeta->findInt32(kKeyIsDRM, &drm) && drm != 0) {
- ALOGE("frame grab not allowed.");
- return ERROR_DRM_UNKNOWN;
- }
-
size_t n = mExtractor->countTracks();
size_t i;
for (i = 0; i < n; ++i) {
@@ -403,6 +398,25 @@
return mMetaData.valueAt(index).string();
}
+void StagefrightMetadataRetriever::parseColorAspects(const sp<MetaData>& meta) {
+ sp<AMessage> format = new AMessage();
+ if (convertMetaDataToMessage(meta, &format) != OK) {
+ return;
+ }
+
+ int32_t standard, transfer, range;
+ if (format->findInt32("color-standard", &standard)
+ && format->findInt32("color-transfer", &transfer)
+ && format->findInt32("color-range", &range)) {
+ ALOGV("found color aspects : standard=%d, transfer=%d, range=%d",
+ standard, transfer, range);
+
+ mMetaData.add(METADATA_KEY_COLOR_STANDARD, String8::format("%d", standard));
+ mMetaData.add(METADATA_KEY_COLOR_TRANSFER, String8::format("%d", transfer));
+ mMetaData.add(METADATA_KEY_COLOR_RANGE, String8::format("%d", range));
+ }
+}
+
void StagefrightMetadataRetriever::parseMetaData() {
sp<MetaData> meta = mExtractor->getMetaData();
@@ -531,6 +545,19 @@
if (!trackMeta->findInt32(kKeyBitRate, &audioBitrate)) {
audioBitrate = -1;
}
+
+ int32_t bitsPerSample = -1;
+ int32_t sampleRate = -1;
+ trackMeta->findInt32(kKeyBitsPerSample, &bitsPerSample);
+ trackMeta->findInt32(kKeySampleRate, &sampleRate);
+ if (bitsPerSample >= 0) {
+ sprintf(tmp, "%d", bitsPerSample);
+ mMetaData.add(METADATA_KEY_BITS_PER_SAMPLE, String8(tmp));
+ }
+ if (sampleRate >= 0) {
+ sprintf(tmp, "%d", sampleRate);
+ mMetaData.add(METADATA_KEY_SAMPLERATE, String8(tmp));
+ }
} else if (!hasVideo && !strncasecmp("video/", mime, 6)) {
hasVideo = true;
@@ -542,6 +569,8 @@
if (!trackMeta->findInt32(kKeyFrameCount, &videoFrameCount)) {
videoFrameCount = 0;
}
+
+ parseColorAspects(trackMeta);
} else if (!strncasecmp("image/", mime, 6)) {
int32_t isPrimary;
if (trackMeta->findInt32(
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 456e2e3..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;
@@ -787,6 +848,11 @@
msg->setInt32("channel-count", numChannels);
msg->setInt32("sample-rate", sampleRate);
+ int32_t bitsPerSample;
+ if (meta->findInt32(kKeyBitsPerSample, &bitsPerSample)) {
+ msg->setInt32("bits-per-sample", bitsPerSample);
+ }
+
int32_t channelMask;
if (meta->findInt32(kKeyChannelMask, &channelMask)) {
msg->setInt32("channel-mask", channelMask);
@@ -1014,6 +1080,56 @@
msg->setInt32("android._is-hdr", (info & hvcc.kInfoIsHdr) != 0);
}
+ uint32_t isoPrimaries, isoTransfer, isoMatrix, isoRange;
+ if (hvcc.findParam32(kColourPrimaries, &isoPrimaries)
+ && hvcc.findParam32(kTransferCharacteristics, &isoTransfer)
+ && hvcc.findParam32(kMatrixCoeffs, &isoMatrix)
+ && hvcc.findParam32(kVideoFullRangeFlag, &isoRange)) {
+ ALOGV("found iso color aspects : primaris=%d, transfer=%d, matrix=%d, range=%d",
+ isoPrimaries, isoTransfer, isoMatrix, isoRange);
+
+ ColorAspects aspects;
+ ColorUtils::convertIsoColorAspectsToCodecAspects(
+ isoPrimaries, isoTransfer, isoMatrix, isoRange, aspects);
+
+ if (aspects.mPrimaries == ColorAspects::PrimariesUnspecified) {
+ int32_t primaries;
+ if (meta->findInt32(kKeyColorPrimaries, &primaries)) {
+ ALOGV("unspecified primaries found, replaced to %d", primaries);
+ aspects.mPrimaries = static_cast<ColorAspects::Primaries>(primaries);
+ }
+ }
+ if (aspects.mTransfer == ColorAspects::TransferUnspecified) {
+ int32_t transferFunction;
+ if (meta->findInt32(kKeyTransferFunction, &transferFunction)) {
+ ALOGV("unspecified transfer found, replaced to %d", transferFunction);
+ aspects.mTransfer = static_cast<ColorAspects::Transfer>(transferFunction);
+ }
+ }
+ if (aspects.mMatrixCoeffs == ColorAspects::MatrixUnspecified) {
+ int32_t colorMatrix;
+ if (meta->findInt32(kKeyColorMatrix, &colorMatrix)) {
+ ALOGV("unspecified matrix found, replaced to %d", colorMatrix);
+ aspects.mMatrixCoeffs = static_cast<ColorAspects::MatrixCoeffs>(colorMatrix);
+ }
+ }
+ if (aspects.mRange == ColorAspects::RangeUnspecified) {
+ int32_t range;
+ if (meta->findInt32(kKeyColorRange, &range)) {
+ ALOGV("unspecified range found, replaced to %d", range);
+ aspects.mRange = static_cast<ColorAspects::Range>(range);
+ }
+ }
+
+ int32_t standard, transfer, range;
+ if (ColorUtils::convertCodecColorAspectsToPlatformAspects(
+ aspects, &range, &standard, &transfer) == OK) {
+ msg->setInt32("color-standard", standard);
+ msg->setInt32("color-transfer", transfer);
+ msg->setInt32("color-range", range);
+ }
+ }
+
parseHevcProfileLevelFromHvcc((const uint8_t *)data, dataSize, msg);
} else if (meta->findData(kKeyESDS, &type, &data, &size)) {
ESDS esds((const char *)data, size);
@@ -1062,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)) {
@@ -1149,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;
@@ -1356,7 +1476,7 @@
ALOGW("did not find mime type");
}
- convertMessageToMetaDataTags(msg, meta);
+ convertMessageToMetaDataFromMappings(msg, meta);
int64_t durationUs;
if (msg->findInt64("durationUs", &durationUs)) {
@@ -1389,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;
@@ -1476,6 +1596,10 @@
if (msg->findInt32("sample-rate", &sampleRate)) {
meta->setInt32(kKeySampleRate, sampleRate);
}
+ int32_t bitsPerSample;
+ if (msg->findInt32("bits-per-sample", &bitsPerSample)) {
+ meta->setInt32(kKeyBitsPerSample, bitsPerSample);
+ }
int32_t channelMask;
if (msg->findInt32("channel-mask", &channelMask)) {
meta->setInt32(kKeyChannelMask, channelMask);
@@ -1534,19 +1658,19 @@
if (msg->findBuffer("csd-1", &csd1)) {
std::vector<char> avcc(csd0size + csd1->size() + 1024);
size_t outsize = reassembleAVCC(csd0, csd1, avcc.data());
- meta->setData(kKeyAVCC, kKeyAVCC, avcc.data(), outsize);
+ meta->setData(kKeyAVCC, kTypeAVCC, avcc.data(), outsize);
}
} else if (mime == MEDIA_MIMETYPE_AUDIO_AAC || mime == MEDIA_MIMETYPE_VIDEO_MPEG4) {
std::vector<char> esds(csd0size + 31);
// 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);
size_t outsize = reassembleHVCC(csd0, hvcc.data(), hvcc.size(), 4);
- meta->setData(kKeyHVCC, kKeyHVCC, hvcc.data(), outsize);
+ meta->setData(kKeyHVCC, kTypeHVCC, hvcc.data(), outsize);
} else if (mime == MEDIA_MIMETYPE_VIDEO_VP9) {
meta->setData(kKeyVp9CodecPrivate, 0, csd0->data(), csd0->size());
} else if (mime == MEDIA_MIMETYPE_AUDIO_OPUS) {
@@ -1562,12 +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());
}
- }
-
- int32_t timeScale;
- if (msg->findInt32("time-scale", &timeScale)) {
- meta->setInt32(kKeyTimeScale, timeScale);
+ } 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());
+ } 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
@@ -1646,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 }
};
@@ -1697,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
@@ -1747,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/amrnb/common/src/l_abs.cpp b/media/libstagefright/codecs/amrnb/common/src/l_abs.cpp
index fd1c90d..7e0ae99 100644
--- a/media/libstagefright/codecs/amrnb/common/src/l_abs.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/l_abs.cpp
@@ -176,7 +176,7 @@
/*----------------------------------------------------------------------------
; FUNCTION CODE
----------------------------------------------------------------------------*/
-Word32 L_abs(register Word32 L_var1)
+Word32 L_abs(Word32 L_var1)
{
/*----------------------------------------------------------------------------
; Define all local variables
diff --git a/media/libstagefright/codecs/amrnb/common/src/l_shr_r.cpp b/media/libstagefright/codecs/amrnb/common/src/l_shr_r.cpp
index f609a73..47e1ee8 100644
--- a/media/libstagefright/codecs/amrnb/common/src/l_shr_r.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/l_shr_r.cpp
@@ -190,7 +190,7 @@
/*----------------------------------------------------------------------------
; FUNCTION CODE
----------------------------------------------------------------------------*/
-Word32 L_shr_r(register Word32 L_var1, register Word16 var2, Flag *pOverflow)
+Word32 L_shr_r(Word32 L_var1, Word16 var2, Flag *pOverflow)
{
Word32 result;
diff --git a/media/libstagefright/codecs/amrnb/common/src/negate.cpp b/media/libstagefright/codecs/amrnb/common/src/negate.cpp
index be58d2b..aa36422 100644
--- a/media/libstagefright/codecs/amrnb/common/src/negate.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/negate.cpp
@@ -161,7 +161,7 @@
/*----------------------------------------------------------------------------
; FUNCTION CODE
----------------------------------------------------------------------------*/
-Word16 negate(register Word16 var1)
+Word16 negate(Word16 var1)
{
/*----------------------------------------------------------------------------
; Define all local variables
diff --git a/media/libstagefright/codecs/amrnb/common/src/round.cpp b/media/libstagefright/codecs/amrnb/common/src/round.cpp
index 71d1702..633a8c9 100644
--- a/media/libstagefright/codecs/amrnb/common/src/round.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/round.cpp
@@ -184,7 +184,7 @@
/*----------------------------------------------------------------------------
; FUNCTION CODE
----------------------------------------------------------------------------*/
-Word16 pv_round(register Word32 L_var1, Flag *pOverflow)
+Word16 pv_round(Word32 L_var1, Flag *pOverflow)
{
Word16 result;
diff --git a/media/libstagefright/codecs/amrnb/common/src/shr_r.cpp b/media/libstagefright/codecs/amrnb/common/src/shr_r.cpp
index 6656f93..cdcc246 100644
--- a/media/libstagefright/codecs/amrnb/common/src/shr_r.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/shr_r.cpp
@@ -193,7 +193,7 @@
/*----------------------------------------------------------------------------
; FUNCTION CODE
----------------------------------------------------------------------------*/
-Word16 shr_r(register Word16 var1, register Word16 var2, Flag *pOverflow)
+Word16 shr_r(Word16 var1, Word16 var2, Flag *pOverflow)
{
/*----------------------------------------------------------------------------
; Define all local variables
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/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
index d534f64..ce8d458 100644
--- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
+++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
@@ -357,7 +357,10 @@
int32_t numPageSamples = 0;
if (inHeader) {
- if (mInputBufferCount < 2) {
+ // Assume the very first 2 buffers are always codec config (in this case mState is NULL)
+ // After flush, handle CSD
+ if (mInputBufferCount < 2 &&
+ (mState == NULL || (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG))) {
const uint8_t *data = inHeader->pBuffer + inHeader->nOffset;
size_t size = inHeader->nFilledLen;
@@ -380,7 +383,24 @@
makeBitReader((const uint8_t *)data + 7, size - 7, &buf, &ref, &bits);
- if (mInputBufferCount == 0) {
+ // Assume very first frame is identification header - or reset identification
+ // header after flush, but allow only specifying setup header after flush if
+ // identification header was already set up.
+ if (mInputBufferCount == 0 &&
+ (mVi == NULL || data[0] == 1 /* identification header */)) {
+ // remove any prior state
+ if (mVi != NULL) {
+ // also clear mState as it may refer to the old mVi
+ if (mState != NULL) {
+ vorbis_dsp_clear(mState);
+ delete mState;
+ mState = NULL;
+ }
+ vorbis_info_clear(mVi);
+ delete mVi;
+ mVi = NULL;
+ }
+
CHECK(mVi == NULL);
mVi = new vorbis_info;
vorbis_info_init(mVi);
@@ -392,8 +412,15 @@
return;
}
} else {
+ // remove any prior state
+ if (mState != NULL) {
+ vorbis_dsp_clear(mState);
+ delete mState;
+ mState = NULL;
+ }
+
int ret = _vorbis_unpack_books(mVi, &bits);
- if (ret != 0) {
+ if (ret != 0 || mState != NULL) {
notify(OMX_EventError, OMX_ErrorUndefined, ret, NULL);
mSignalledError = true;
return;
@@ -409,6 +436,7 @@
notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
mOutputPortSettingsChange = AWAITING_DISABLED;
}
+ mInputBufferCount = 1;
}
if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
@@ -550,19 +578,10 @@
mInputBufferCount = 0;
mNumFramesOutput = 0;
- if (mState != NULL) {
- vorbis_dsp_clear(mState);
- delete mState;
- mState = NULL;
- }
- if (mVi != NULL) {
- vorbis_info_clear(mVi);
- delete mVi;
- mVi = NULL;
- }
mSawInputEos = false;
mSignalledOutputEos = false;
mNumFramesLeftOnPage = -1;
+ vorbis_dsp_restart(mState);
}
}
diff --git a/media/libstagefright/foundation/ALooper.cpp b/media/libstagefright/foundation/ALooper.cpp
index 9921636..768cbd6 100644
--- a/media/libstagefright/foundation/ALooper.cpp
+++ b/media/libstagefright/foundation/ALooper.cpp
@@ -170,7 +170,9 @@
int64_t whenUs;
if (delayUs > 0) {
- whenUs = GetNowUs() + delayUs;
+ int64_t nowUs = GetNowUs();
+ whenUs = (delayUs > INT64_MAX - nowUs ? INT64_MAX : nowUs + delayUs);
+
} else {
whenUs = GetNowUs();
}
@@ -208,6 +210,9 @@
if (whenUs > nowUs) {
int64_t delayUs = whenUs - nowUs;
+ if (delayUs > INT64_MAX / 1000) {
+ delayUs = INT64_MAX / 1000;
+ }
mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);
return true;
diff --git a/media/libstagefright/foundation/ColorUtils.cpp b/media/libstagefright/foundation/ColorUtils.cpp
index c4eaa27..070e325 100644
--- a/media/libstagefright/foundation/ColorUtils.cpp
+++ b/media/libstagefright/foundation/ColorUtils.cpp
@@ -23,6 +23,7 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALookup.h>
#include <media/stagefright/foundation/ColorUtils.h>
+#include <media/NdkMediaFormatPriv.h>
namespace android {
@@ -342,6 +343,14 @@
aspects.mRange = fullRange ? ColorAspects::RangeFull : ColorAspects::RangeLimited;
}
+void ColorUtils::convertIsoColorAspectsToPlatformAspects(
+ int32_t primaries, int32_t intransfer, int32_t coeffs, bool fullRange,
+ int32_t *range, int32_t *standard, int32_t *outtransfer) {
+ ColorAspects aspects;
+ convertIsoColorAspectsToCodecAspects(primaries, intransfer, coeffs, fullRange, aspects);
+ convertCodecColorAspectsToPlatformAspects(aspects, range, standard, outtransfer);
+}
+
// static
ColorAspects ColorUtils::unpackToColorAspects(uint32_t packed) {
ColorAspects aspects;
@@ -684,6 +693,13 @@
transfer, asString((ColorTransfer)transfer));
}
+
+// static
+void ColorUtils::setHDRStaticInfoIntoAMediaFormat(
+ const HDRStaticInfo &info, AMediaFormat *format) {
+ setHDRStaticInfoIntoFormat(info, format->mFormat);
+}
+
// static
void ColorUtils::setHDRStaticInfoIntoFormat(
const HDRStaticInfo &info, sp<AMessage> &format) {
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/ColorUtils.h b/media/libstagefright/foundation/include/media/stagefright/foundation/ColorUtils.h
index d6c768d..cd0af2b 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/ColorUtils.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/ColorUtils.h
@@ -27,6 +27,8 @@
#include <media/hardware/VideoAPI.h>
#include <system/graphics.h>
+struct AMediaFormat;
+
namespace android {
struct ColorUtils {
@@ -135,6 +137,9 @@
static void convertIsoColorAspectsToCodecAspects(
int32_t primaries, int32_t transfer, int32_t coeffs, bool fullRange,
ColorAspects &aspects);
+ static void convertIsoColorAspectsToPlatformAspects(
+ int32_t primaries, int32_t isotransfer, int32_t coeffs, bool fullRange,
+ int32_t *range, int32_t *standard, int32_t *transfer);
// unpack a uint32_t to a full ColorAspects struct
static ColorAspects unpackToColorAspects(uint32_t packed);
@@ -180,6 +185,8 @@
// writes |info| into format.
static void setHDRStaticInfoIntoFormat(const HDRStaticInfo &info, sp<AMessage> &format);
+ // writes |info| into format.
+ static void setHDRStaticInfoIntoAMediaFormat(const HDRStaticInfo &info, AMediaFormat *format);
};
inline static const char *asString(android::ColorUtils::ColorStandard i, const char *def = "??") {
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/HTTPBase.h b/media/libstagefright/include/HTTPBase.h
index a924197..8b20187 100644
--- a/media/libstagefright/include/HTTPBase.h
+++ b/media/libstagefright/include/HTTPBase.h
@@ -51,12 +51,6 @@
virtual void setBandwidthHistorySize(size_t numHistoryItems);
- static void RegisterSocketUserTag(int sockfd, uid_t uid, uint32_t kTag);
- static void UnRegisterSocketUserTag(int sockfd);
-
- static void RegisterSocketUserMark(int sockfd, uid_t uid);
- static void UnRegisterSocketUserMark(int sockfd);
-
virtual String8 toString() {
return mName;
}
diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h
index a7090ad..c50677a 100644
--- a/media/libstagefright/include/StagefrightMetadataRetriever.h
+++ b/media/libstagefright/include/StagefrightMetadataRetriever.h
@@ -65,6 +65,7 @@
sp<ImageDecoder> mImageDecoder;
int mLastImageIndex;
void parseMetaData();
+ void parseColorAspects(const sp<MetaData>& meta);
// Delete album art and clear metadata.
void clearMetadata();
diff --git a/media/libstagefright/include/media/stagefright/ClearDataSourceFactory.h b/media/libstagefright/include/media/stagefright/ClearDataSourceFactory.h
deleted file mode 100644
index 12bcdd3..0000000
--- a/media/libstagefright/include/media/stagefright/ClearDataSourceFactory.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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 DATA_SOURCE_FACTORY2_H_
-
-#define DATA_SOURCE_FACTORY2_H_
-
-#include <sys/types.h>
-#include <utils/RefBase.h>
-
-namespace android {
-
-struct MediaHTTPService;
-class String8;
-struct HTTPBase;
-
-class ClearDataSourceFactory {
-public:
- static sp<DataSource> CreateFromURI(
- const sp<MediaHTTPService> &httpService,
- const char *uri,
- const KeyedVector<String8, String8> *headers = NULL,
- String8 *contentType = NULL,
- HTTPBase *httpSource = NULL);
-
- static sp<DataSource> CreateMediaHTTP(const sp<MediaHTTPService> &httpService);
- static sp<DataSource> CreateFromFd(int fd, int64_t offset, int64_t length);
-};
-
-} // namespace android
-
-#endif // DATA_SOURCE_FACTORY2_H_
diff --git a/media/libstagefright/include/media/stagefright/MetaDataUtils.h b/media/libstagefright/include/media/stagefright/MetaDataUtils.h
index e87c7f9..dcaf27f 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataUtils.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataUtils.h
@@ -26,6 +26,7 @@
struct ABuffer;
bool MakeAVCCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size);
+bool MakeAVCCodecSpecificData(AMediaFormat *meta, const uint8_t *data, size_t size);
bool MakeAACCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size);
bool MakeAACCodecSpecificData(MetaDataBase &meta, unsigned profile, unsigned sampling_freq_index,
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/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index fb498d4..3debe34 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -552,8 +552,10 @@
hasStreamCA = true;
streamCA.mSystemID = br->getBits(16);
streamCA.mPID = br->getBits(16) & 0x1fff;
- ES_info_length -= 4;
- streamCA.mPrivateData.assign(br->data(), br->data() + descriptor_length - 4);
+ ES_info_length -= descriptor_length;
+ descriptor_length -= 4;
+ streamCA.mPrivateData.assign(br->data(), br->data() + descriptor_length);
+ br->skipBits(descriptor_length * 8);
} else if (info.mType == STREAMTYPE_PES_PRIVATE_DATA &&
descriptor_tag == DESCRIPTOR_DVB_EXTENSION && descriptor_length >= 1) {
unsigned descTagExt = br->getBits(8);
diff --git a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
index 672a37c..f9f7ec2 100644
--- a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
+++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
@@ -502,7 +502,8 @@
// These values were chosen to prevent integer overflows further down the line, and do
// not indicate support for 32kx32k video.
if (newWidth > 32768 || newHeight > 32768
- || video_def->nStride > 32768 || video_def->nSliceHeight > 32768) {
+ || video_def->nStride > 32768 || video_def->nStride < -32768
+ || video_def->nSliceHeight > 32768) {
ALOGE("b/22885421");
return OMX_ErrorBadParameter;
}
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index 5620cf8..33c1c18 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -19,6 +19,7 @@
#include <utils/Log.h>
#include "ARTSPConnection.h"
+#include "NetworkUtils.h"
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -59,8 +60,8 @@
if (mSocket >= 0) {
ALOGE("Connection is still open, closing the socket.");
if (mUIDValid) {
- HTTPBase::UnRegisterSocketUserTag(mSocket);
- HTTPBase::UnRegisterSocketUserMark(mSocket);
+ NetworkUtils::UnRegisterSocketUserTag(mSocket);
+ NetworkUtils::UnRegisterSocketUserMark(mSocket);
}
close(mSocket);
mSocket = -1;
@@ -214,8 +215,8 @@
if (mState != DISCONNECTED) {
if (mUIDValid) {
- HTTPBase::UnRegisterSocketUserTag(mSocket);
- HTTPBase::UnRegisterSocketUserMark(mSocket);
+ NetworkUtils::UnRegisterSocketUserTag(mSocket);
+ NetworkUtils::UnRegisterSocketUserMark(mSocket);
}
close(mSocket);
mSocket = -1;
@@ -266,9 +267,9 @@
mSocket = socket(AF_INET, SOCK_STREAM, 0);
if (mUIDValid) {
- HTTPBase::RegisterSocketUserTag(mSocket, mUID,
+ NetworkUtils::RegisterSocketUserTag(mSocket, mUID,
(uint32_t)*(uint32_t*) "RTSP");
- HTTPBase::RegisterSocketUserMark(mSocket, mUID);
+ NetworkUtils::RegisterSocketUserMark(mSocket, mUID);
}
MakeSocketBlocking(mSocket, false);
@@ -297,8 +298,8 @@
mState = DISCONNECTED;
if (mUIDValid) {
- HTTPBase::UnRegisterSocketUserTag(mSocket);
- HTTPBase::UnRegisterSocketUserMark(mSocket);
+ NetworkUtils::UnRegisterSocketUserTag(mSocket);
+ NetworkUtils::UnRegisterSocketUserMark(mSocket);
}
close(mSocket);
mSocket = -1;
@@ -315,8 +316,8 @@
void ARTSPConnection::performDisconnect() {
if (mUIDValid) {
- HTTPBase::UnRegisterSocketUserTag(mSocket);
- HTTPBase::UnRegisterSocketUserMark(mSocket);
+ NetworkUtils::UnRegisterSocketUserTag(mSocket);
+ NetworkUtils::UnRegisterSocketUserMark(mSocket);
}
close(mSocket);
mSocket = -1;
@@ -389,8 +390,8 @@
mState = DISCONNECTED;
if (mUIDValid) {
- HTTPBase::UnRegisterSocketUserTag(mSocket);
- HTTPBase::UnRegisterSocketUserMark(mSocket);
+ NetworkUtils::UnRegisterSocketUserTag(mSocket);
+ NetworkUtils::UnRegisterSocketUserMark(mSocket);
}
close(mSocket);
mSocket = -1;
diff --git a/media/libstagefright/rtsp/Android.bp b/media/libstagefright/rtsp/Android.bp
index debd07e..d1767d3 100644
--- a/media/libstagefright/rtsp/Android.bp
+++ b/media/libstagefright/rtsp/Android.bp
@@ -1,5 +1,5 @@
-cc_library_static {
- name: "libstagefright_rtsp",
+cc_defaults {
+ name: "libstagefright_rtsp_defaults",
srcs: [
"AAMRAssembler.cpp",
@@ -52,6 +52,21 @@
},
}
+cc_library_static {
+ name: "libstagefright_rtsp",
+
+ srcs: ["NetworkUtils.cpp"],
+ header_libs: ["libnetd_client_headers"],
+ defaults: ["libstagefright_rtsp_defaults"],
+}
+
+cc_library_static {
+ name: "libstagefright_rtsp_player2",
+
+ srcs: ["NetworkUtilsForAppProc.cpp"],
+ defaults: ["libstagefright_rtsp_defaults"],
+}
+
//###############################################################################
cc_test {
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index d183516..5d993db 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -31,6 +31,7 @@
#include "ARTPConnection.h"
#include "ARTSPConnection.h"
#include "ASessionDescription.h"
+#include "NetworkUtils.h"
#include <ctype.h>
#include <cutils/properties.h>
@@ -757,10 +758,10 @@
if (!track->mUsingInterleavedTCP) {
// Clear the tag
if (mUIDValid) {
- HTTPBase::UnRegisterSocketUserTag(track->mRTPSocket);
- HTTPBase::UnRegisterSocketUserMark(track->mRTPSocket);
- HTTPBase::UnRegisterSocketUserTag(track->mRTCPSocket);
- HTTPBase::UnRegisterSocketUserMark(track->mRTCPSocket);
+ NetworkUtils::UnRegisterSocketUserTag(track->mRTPSocket);
+ NetworkUtils::UnRegisterSocketUserMark(track->mRTPSocket);
+ NetworkUtils::UnRegisterSocketUserTag(track->mRTCPSocket);
+ NetworkUtils::UnRegisterSocketUserMark(track->mRTCPSocket);
}
close(track->mRTPSocket);
@@ -886,10 +887,10 @@
// Clear the tag
if (mUIDValid) {
- HTTPBase::UnRegisterSocketUserTag(info->mRTPSocket);
- HTTPBase::UnRegisterSocketUserMark(info->mRTPSocket);
- HTTPBase::UnRegisterSocketUserTag(info->mRTCPSocket);
- HTTPBase::UnRegisterSocketUserMark(info->mRTCPSocket);
+ NetworkUtils::UnRegisterSocketUserTag(info->mRTPSocket);
+ NetworkUtils::UnRegisterSocketUserMark(info->mRTPSocket);
+ NetworkUtils::UnRegisterSocketUserTag(info->mRTCPSocket);
+ NetworkUtils::UnRegisterSocketUserMark(info->mRTCPSocket);
}
close(info->mRTPSocket);
@@ -1665,12 +1666,12 @@
&info->mRTPSocket, &info->mRTCPSocket, &rtpPort);
if (mUIDValid) {
- HTTPBase::RegisterSocketUserTag(info->mRTPSocket, mUID,
- (uint32_t)*(uint32_t*) "RTP_");
- HTTPBase::RegisterSocketUserTag(info->mRTCPSocket, mUID,
- (uint32_t)*(uint32_t*) "RTP_");
- HTTPBase::RegisterSocketUserMark(info->mRTPSocket, mUID);
- HTTPBase::RegisterSocketUserMark(info->mRTCPSocket, mUID);
+ NetworkUtils::RegisterSocketUserTag(info->mRTPSocket, mUID,
+ (uint32_t)*(uint32_t*) "RTP_");
+ NetworkUtils::RegisterSocketUserTag(info->mRTCPSocket, mUID,
+ (uint32_t)*(uint32_t*) "RTP_");
+ NetworkUtils::RegisterSocketUserMark(info->mRTPSocket, mUID);
+ NetworkUtils::RegisterSocketUserMark(info->mRTCPSocket, mUID);
}
request.append("Transport: RTP/AVP/UDP;unicast;client_port=");
diff --git a/media/libstagefright/rtsp/NetworkUtils.cpp b/media/libstagefright/rtsp/NetworkUtils.cpp
new file mode 100644
index 0000000..cc36b78
--- /dev/null
+++ b/media/libstagefright/rtsp/NetworkUtils.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NetworkUtils"
+#include <utils/Log.h>
+
+#include "NetworkUtils.h"
+#include <cutils/qtaguid.h>
+#include <NetdClient.h>
+
+namespace android {
+
+// static
+void NetworkUtils::RegisterSocketUserTag(int sockfd, uid_t uid, uint32_t kTag) {
+ int res = qtaguid_tagSocket(sockfd, kTag, uid);
+ if (res != 0) {
+ ALOGE("Failed tagging socket %d for uid %d (My UID=%d)", sockfd, uid, geteuid());
+ }
+}
+
+// static
+void NetworkUtils::UnRegisterSocketUserTag(int sockfd) {
+ int res = qtaguid_untagSocket(sockfd);
+ if (res != 0) {
+ ALOGE("Failed untagging socket %d (My UID=%d)", sockfd, geteuid());
+ }
+}
+
+// static
+void NetworkUtils::RegisterSocketUserMark(int sockfd, uid_t uid) {
+ setNetworkForUser(uid, sockfd);
+}
+
+// static
+void NetworkUtils::UnRegisterSocketUserMark(int sockfd) {
+ RegisterSocketUserMark(sockfd, geteuid());
+}
+
+} // namespace android
diff --git a/media/libstagefright/rtsp/NetworkUtils.h b/media/libstagefright/rtsp/NetworkUtils.h
new file mode 100644
index 0000000..e25ee46
--- /dev/null
+++ b/media/libstagefright/rtsp/NetworkUtils.h
@@ -0,0 +1,32 @@
+/*
+ * 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 NETWORK_UTILS_H_
+#define NETWORK_UTILS_H_
+
+namespace android {
+
+struct NetworkUtils {
+ static void RegisterSocketUserTag(int sockfd, uid_t uid, uint32_t kTag);
+ static void UnRegisterSocketUserTag(int sockfd);
+
+ static void RegisterSocketUserMark(int sockfd, uid_t uid);
+ static void UnRegisterSocketUserMark(int sockfd);
+};
+
+} // namespace android
+
+#endif // NETWORK_UTILS_H_
diff --git a/media/libstagefright/rtsp/NetworkUtilsForAppProc.cpp b/media/libstagefright/rtsp/NetworkUtilsForAppProc.cpp
new file mode 100644
index 0000000..662159c
--- /dev/null
+++ b/media/libstagefright/rtsp/NetworkUtilsForAppProc.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NetworkUtils"
+#include <utils/Log.h>
+
+#include "NetworkUtils.h"
+
+// NetworkUtils implementation for application process.
+namespace android {
+
+// static
+void NetworkUtils::RegisterSocketUserTag(int, uid_t, uint32_t) {
+ // No op. Framework already handles the data usage billing for applications.
+}
+
+// static
+void NetworkUtils::UnRegisterSocketUserTag(int) {
+ // No op.
+}
+
+// static
+void NetworkUtils::RegisterSocketUserMark(int, uid_t) {
+ // No op. Framework already handles the data usage billing for applications.
+}
+
+// static
+void NetworkUtils::UnRegisterSocketUserMark(int) {
+ // No op.
+}
+
+} // namespace android
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 22fbb42..b282ed8 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -276,6 +276,7 @@
EXPORT const char* AMEDIAFORMAT_KEY_AUTHOR = "author";
EXPORT const char* AMEDIAFORMAT_KEY_BITRATE_MODE = "bitrate-mode";
EXPORT const char* AMEDIAFORMAT_KEY_BIT_RATE = "bitrate";
+EXPORT const char* AMEDIAFORMAT_KEY_BITS_PER_SAMPLE = "bits-per-sample";
EXPORT const char* AMEDIAFORMAT_KEY_CAPTURE_RATE = "capture-rate";
EXPORT const char* AMEDIAFORMAT_KEY_CDTRACKNUMBER = "cdtracknum";
EXPORT const char* AMEDIAFORMAT_KEY_CHANNEL_COUNT = "channel-count";
@@ -287,10 +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";
@@ -299,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";
@@ -315,26 +330,38 @@
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";
EXPORT const char* AMEDIAFORMAT_KEY_SAMPLE_RATE = "sample-rate";
+EXPORT const char* AMEDIAFORMAT_KEY_SAR_HEIGHT = "sar-height";
+EXPORT const char* AMEDIAFORMAT_KEY_SAR_WIDTH = "sar-width";
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";
@@ -344,7 +371,6 @@
EXPORT const char* AMEDIAFORMAT_KEY_WIDTH = "width";
EXPORT const char* AMEDIAFORMAT_KEY_YEAR = "year";
-
} // extern "C"
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 3fc28f3..89cfd5e 100644
--- a/media/ndk/include/media/NdkMediaFormat.h
+++ b/media/ndk/include/media/NdkMediaFormat.h
@@ -181,16 +181,47 @@
extern const char* AMEDIAFORMAT_KEY_ALBUMARTIST __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_ARTIST __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_AUTHOR __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_BITS_PER_SAMPLE __INTRODUCED_IN(29);
extern const char* AMEDIAFORMAT_KEY_CDTRACKNUMBER __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_COLOR_RANGE __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_COLOR_STANDARD __INTRODUCED_IN(29);
+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/Effects.cpp b/services/audioflinger/Effects.cpp
index 1ce48a9..6ab6369 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -309,8 +309,8 @@
// input and output effect buffers without an intermediary effect process.
// TODO: consider implementing channel conversion.
const size_t safeInputOutputSampleCount =
- inChannelCount != outChannelCount ? 0
- : outChannelCount * std::min(
+ mInChannelCountRequested != mOutChannelCountRequested ? 0
+ : mOutChannelCountRequested * std::min(
mConfig.inputCfg.buffer.frameCount,
mConfig.outputCfg.buffer.frameCount);
const auto accumulateInputToOutput = [this, safeInputOutputSampleCount]() {
@@ -480,7 +480,12 @@
// accumulate input onto output
sp<EffectChain> chain = mChain.promote();
if (chain.get() != nullptr && chain->activeTrackCnt() != 0) {
- accumulateInputToOutput();
+ // similar handling with data_bypass above.
+ if (mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
+ accumulateInputToOutput();
+ } else { // EFFECT_BUFFER_ACCESS_WRITE
+ copyInputToOutput();
+ }
}
}
}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 52a8fa8..f833cf7 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3185,7 +3185,6 @@
if (mType == OFFLOAD || mType == DIRECT) {
mTimestampVerifier.setDiscontinuityMode(mTimestampVerifier.DISCONTINUITY_MODE_ZERO);
}
- audio_utils::Statistics<double> downstreamLatencyStatMs(0.999 /* alpha */);
audio_patch_handle_t lastDownstreamPatchHandle = AUDIO_PATCH_HANDLE_NONE;
while (!exitPending())
@@ -3215,25 +3214,28 @@
downstreamPatchHandle = swPatches[0].getPatchHandle();
}
if (downstreamPatchHandle != lastDownstreamPatchHandle) {
- downstreamLatencyStatMs.reset();
+ mDownstreamLatencyStatMs.reset();
lastDownstreamPatchHandle = downstreamPatchHandle;
}
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;
}
+ mDownstreamLatencyStatMs.add(latencyMs);
}
mAudioFlinger->mLock.unlock();
}
} else {
if (lastDownstreamPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
// our device is no longer AUDIO_DEVICE_OUT_BUS, reset patch handle and stats.
- downstreamLatencyStatMs.reset();
+ mDownstreamLatencyStatMs.reset();
lastDownstreamPatchHandle = AUDIO_PATCH_HANDLE_NONE;
}
}
@@ -3282,10 +3284,10 @@
(long long)timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]);
// Note: Downstream latency only added if timestamp correction enabled.
- if (downstreamLatencyStatMs.getN() > 0) { // we have latency info.
+ if (mDownstreamLatencyStatMs.getN() > 0) { // we have latency info.
const int64_t newPosition =
timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]
- - int64_t(downstreamLatencyStatMs.getMean() * mSampleRate * 1e-3);
+ - int64_t(mDownstreamLatencyStatMs.getMean() * mSampleRate * 1e-3);
// prevent retrograde
timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = max(
newPosition,
@@ -3744,6 +3746,15 @@
uint64_t position64;
if (mOutput->getPresentationPosition(&position64, ×tamp.mTime) == OK) {
timestamp.mPosition = (uint32_t)position64;
+ if (mDownstreamLatencyStatMs.getN() > 0) {
+ const uint32_t positionOffset =
+ (uint32_t)(mDownstreamLatencyStatMs.getMean() * mSampleRate * 1e-3);
+ if (positionOffset > timestamp.mPosition) {
+ timestamp.mPosition = 0;
+ } else {
+ timestamp.mPosition -= positionOffset;
+ }
+ }
return NO_ERROR;
}
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 61f7baf..49fc234 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1082,6 +1082,8 @@
static const size_t kFastMixerLogSize = 8 * 1024;
sp<NBLog::Writer> mFastMixerNBLogWriter;
+ // Downstream patch latency, available if mDownstreamLatencyStatMs.getN() > 0.
+ audio_utils::Statistics<double> mDownstreamLatencyStatMs{0.999};
public:
virtual bool hasFastMixer() const = 0;
diff --git a/services/audiopolicy/config/msd_audio_policy_configuration.xml b/services/audiopolicy/config/msd_audio_policy_configuration.xml
index a84117e..a811f5e 100644
--- a/services/audiopolicy/config/msd_audio_policy_configuration.xml
+++ b/services/audiopolicy/config/msd_audio_policy_configuration.xml
@@ -36,7 +36,8 @@
samplingRates="32000,44100,48000"
channelMasks="AUDIO_CHANNEL_OUT_MONO,AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_5POINT1,AUDIO_CHANNEL_OUT_7POINT1"/>
</mixPort>
- <mixPort name="ms12 output" role="sink">
+ <!-- The HW AV Sync flag is not required, but is recommended -->
+ <mixPort name="ms12 output" role="sink" flags="AUDIO_INPUT_FLAG_HW_AV_SYNC">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
<profile name="" format="AUDIO_FORMAT_AC3"
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/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index ab4971d..c9c216b 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -1446,6 +1446,7 @@
if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
int takePictureCounter;
+ bool shouldSyncWithDevice = true;
{
SharedParameters::Lock l(mParameters);
switch (l.mParameters.state) {
@@ -1531,12 +1532,23 @@
__FUNCTION__, mCameraId);
mZslProcessor->clearZslQueue();
}
+
+ // We should always sync with the device in case flash is turned on,
+ // the camera device suggests that flash is needed (AE state FLASH_REQUIRED)
+ // or we are in some other AE state different from CONVERGED that may need
+ // precapture trigger.
+ if (l.mParameters.flashMode != Parameters::FLASH_MODE_ON &&
+ (l.mParameters.aeState == ANDROID_CONTROL_AE_STATE_CONVERGED)) {
+ shouldSyncWithDevice = false;
+ }
}
ATRACE_ASYNC_BEGIN(kTakepictureLabel, takePictureCounter);
- // Need HAL to have correct settings before (possibly) triggering precapture
- syncWithDevice();
+ // Make sure HAL has correct settings in case precapture trigger is needed.
+ if (shouldSyncWithDevice) {
+ syncWithDevice();
+ }
res = mCaptureSequencer->startCapture();
if (res != OK) {
@@ -1905,6 +1917,11 @@
void Camera2Client::notifyAutoExposure(uint8_t newState, int triggerId) {
ALOGV("%s: Autoexposure state now %d, last trigger %d",
__FUNCTION__, newState, triggerId);
+ {
+ SharedParameters::Lock l(mParameters);
+ // Update state
+ l.mParameters.aeState = newState;
+ }
mCaptureSequencer->notifyAutoExposure(newState, triggerId);
}
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 28d186a..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;
@@ -757,6 +766,7 @@
focusState = ANDROID_CONTROL_AF_STATE_INACTIVE;
shadowFocusMode = FOCUS_MODE_INVALID;
+ aeState = ANDROID_CONTROL_AE_STATE_INACTIVE;
camera_metadata_ro_entry_t max3aRegions = staticInfo(ANDROID_CONTROL_MAX_REGIONS,
Parameters::NUM_REGION, Parameters::NUM_REGION);
if (max3aRegions.count != Parameters::NUM_REGION) return NO_INIT;
@@ -1010,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 =
@@ -1021,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,
@@ -3106,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 42e7a47..3a709c9 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -122,6 +122,7 @@
int32_t high;
};
+ uint8_t aeState; //latest AE state from Hal
int32_t exposureCompensation;
bool autoExposureLock;
bool autoExposureLockAvailable;
@@ -247,6 +248,7 @@
bool useFlexibleYuv;
Size maxJpegSize;
Size maxZslSize;
+ bool supportsPreferredConfigs;
} fastInfo;
// Quirks information; these are short-lived flags to enable workarounds for
@@ -417,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);
@@ -438,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..4b938ed 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());
}
@@ -182,7 +181,14 @@
});
}
- mInterface = new HalInterface(session, queue);
+ 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);
+ }
+
+ mInterface = new HalInterface(session, queue, mUseHalBufManager);
std::string providerType;
mVendorTagId = manager->getProviderTagIdLocked(mId.string());
mTagMonitor.initialize(mVendorTagId);
@@ -190,6 +196,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();
}
@@ -209,6 +237,17 @@
/** Register in-flight map to the status tracker */
mInFlightStatusId = mStatusTracker->addComponent();
+ 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;
+ }
+ }
+
/** Create buffer manager */
mBufferManager = new Camera3BufferManager();
@@ -218,8 +257,10 @@
if (sessionKeysEntry.count > 0) {
sessionParamKeys.insertArrayAt(sessionKeysEntry.data.i32, 0, sessionKeysEntry.count);
}
+
/** 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,27 +1322,51 @@
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);
+ bool noBufferReturned = false;
+ buffer_handle_t *buffer = nullptr;
+ if (mUseHalBufManager) {
+ // This is suspicious most of the time but can be correct during flush where HAL
+ // has to return capture result before a buffer is requested
+ if (bSrc.bufferId == HalInterface::BUFFER_ID_NO_BUFFER) {
+ if (bSrc.status == BufferStatus::OK) {
+ ALOGE("%s: Frame %d: Buffer %zu: No bufferId for stream %d",
+ __FUNCTION__, result.frameNumber, i, bSrc.streamId);
+ // Still proceeds so other buffers can be returned
+ }
+ noBufferReturned = true;
+ }
+ if (noBufferReturned) {
+ res = OK;
+ } else {
+ 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;
if (bSrc.releaseFence == nullptr) {
bDst.release_fence = -1;
} else if (bSrc.releaseFence->numFds == 1) {
+ if (noBufferReturned) {
+ ALOGE("%s: got releaseFence without output buffer!", __FUNCTION__);
+ }
bDst.release_fence = dup(bSrc.releaseFence->data[0]);
} else {
ALOGE("%s: Frame %d: Invalid release fence for buffer %zu, fd count is %d, not 1",
@@ -1163,13 +1442,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 +1629,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 +1785,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 +1863,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 +1903,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 +1933,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 +2184,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 +2375,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 +2400,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 +2421,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 +2484,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 +2533,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 +2546,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 +2564,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 +2622,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 +2688,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 +2823,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 +2881,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 +2944,10 @@
return rc;
}
+ if (mDummyStreamId == NO_STREAM) {
+ mRequestBufferSM.onStreamsConfigured();
+ }
+
return OK;
}
@@ -2668,15 +2992,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 +3072,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 +3097,65 @@
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);
+ if (outputBuffers[i].buffer == nullptr) {
+ if (!mUseHalBufManager) {
+ // With HAL buffer management API, HAL sometimes will have to return buffers that
+ // has not got a output buffer handle filled yet. This is though illegal if HAL
+ // buffer management API is not being used.
+ ALOGE("%s: cannot return a null buffer!", __FUNCTION__);
+ }
+ continue;
+ }
+
+ 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 +3166,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 +3214,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 +3239,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 +3316,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 +3369,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 +3447,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 +3621,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 +3635,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 +3755,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 +3833,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);
@@ -3470,11 +3870,17 @@
Camera3Device::HalInterface::HalInterface(
sp<ICameraDeviceSession> &session,
- std::shared_ptr<RequestMetadataQueue> queue) :
+ std::shared_ptr<RequestMetadataQueue> queue,
+ bool useHalBufManager) :
mHidlSession(session),
- mRequestMetadataQueue(queue) {
+ mRequestMetadataQueue(queue),
+ mUseHalBufManager(useHalBufManager) {
// 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;
@@ -3485,11 +3891,12 @@
}
}
-Camera3Device::HalInterface::HalInterface() {}
+Camera3Device::HalInterface::HalInterface() : mUseHalBufManager(false) {}
Camera3Device::HalInterface::HalInterface(const HalInterface& other) :
mHidlSession(other.mHidlSession),
- mRequestMetadataQueue(other.mRequestMetadataQueue) {}
+ mRequestMetadataQueue(other.mRequestMetadataQueue),
+ mUseHalBufManager(other.mUseHalBufManager) {}
bool Camera3Device::HalInterface::valid() {
return (mHidlSession != nullptr);
@@ -3651,26 +4058,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
@@ -3782,14 +4212,14 @@
return res;
}
-void Camera3Device::HalInterface::wrapAsHidlRequest(camera3_capture_request_t* request,
+status_t Camera3Device::HalInterface::wrapAsHidlRequest(camera3_capture_request_t* request,
/*out*/device::V3_2::CaptureRequest* captureRequest,
/*out*/std::vector<native_handle_t*>* handlesCreated) {
ATRACE_CALL();
if (captureRequest == nullptr || handlesCreated == nullptr) {
ALOGE("%s: captureRequest (%p) and handlesCreated (%p) must not be null",
__FUNCTION__, captureRequest, handlesCreated);
- return;
+ return BAD_VALUE;
}
captureRequest->frameNumber = request->frame_number;
@@ -3830,26 +4260,37 @@
const camera3_stream_buffer_t *src = request->output_buffers + i;
StreamBuffer &dst = captureRequest->outputBuffers[i];
int32_t streamId = Camera3Stream::cast(src->stream)->getId();
- buffer_handle_t buf = *(src->buffer);
- auto pair = getBufferId(buf, streamId);
- bool isNewBuffer = pair.first;
- dst.streamId = streamId;
- dst.bufferId = pair.second;
- dst.buffer = isNewBuffer ? buf : nullptr;
- dst.status = BufferStatus::OK;
- native_handle_t *acquireFence = nullptr;
- if (src->acquire_fence != -1) {
- acquireFence = native_handle_create(1,0);
- acquireFence->data[0] = src->acquire_fence;
- handlesCreated->push_back(acquireFence);
+ if (src->buffer != nullptr) {
+ buffer_handle_t buf = *(src->buffer);
+ auto pair = getBufferId(buf, streamId);
+ bool isNewBuffer = pair.first;
+ dst.bufferId = pair.second;
+ dst.buffer = isNewBuffer ? buf : nullptr;
+ native_handle_t *acquireFence = nullptr;
+ if (src->acquire_fence != -1) {
+ acquireFence = native_handle_create(1,0);
+ acquireFence->data[0] = src->acquire_fence;
+ handlesCreated->push_back(acquireFence);
+ }
+ dst.acquireFence = acquireFence;
+ } else if (mUseHalBufManager) {
+ // HAL buffer management path
+ dst.bufferId = BUFFER_ID_NO_BUFFER;
+ dst.buffer = nullptr;
+ dst.acquireFence = nullptr;
+ } else {
+ ALOGE("%s: cannot send a null buffer in capture request!", __FUNCTION__);
+ return BAD_VALUE;
}
- dst.acquireFence = acquireFence;
+ dst.streamId = streamId;
+ dst.status = BufferStatus::OK;
dst.releaseFence = nullptr;
pushInflightBufferLocked(captureRequest->frameNumber, streamId,
src->buffer, src->acquire_fence);
}
}
+ return OK;
}
status_t Camera3Device::HalInterface::processBatchCaptureRequests(
@@ -3873,12 +4314,17 @@
}
std::vector<native_handle_t*> handlesCreated;
+ status_t res = OK;
for (size_t i = 0; i < batchSize; i++) {
if (hidlSession_3_4 != nullptr) {
- wrapAsHidlRequest(requests[i], /*out*/&captureRequests_3_4[i].v3_2,
+ res = wrapAsHidlRequest(requests[i], /*out*/&captureRequests_3_4[i].v3_2,
/*out*/&handlesCreated);
} else {
- wrapAsHidlRequest(requests[i], /*out*/&captureRequests[i], /*out*/&handlesCreated);
+ res = wrapAsHidlRequest(requests[i],
+ /*out*/&captureRequests[i], /*out*/&handlesCreated);
+ }
+ if (res != OK) {
+ return res;
}
}
@@ -4041,6 +4487,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 +4541,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 +4616,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 +4627,7 @@
mReconfigured(false),
mDoPause(false),
mPaused(true),
+ mNotifyPipelineDrain(false),
mFrameNumber(0),
mLatestRequestId(NAME_NOT_FOUND),
mCurrentAfTriggerId(0),
@@ -4149,7 +4638,8 @@
mConstrainedMode(false),
mRequestLatency(kRequestLatencyBinSize),
mSessionParamKeys(sessionParamKeys),
- mLatestSessionParams(sessionParamKeys.size()) {
+ mLatestSessionParams(sessionParamKeys.size()),
+ mUseHalBufManager(useHalBufManager) {
mStatusId = statusTracker->addComponent();
}
@@ -4764,6 +5254,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 +5405,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 +5428,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 +5507,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 +5585,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 +5596,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 +5605,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 +5798,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 +5891,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 +6355,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..419ac42 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);
@@ -270,7 +256,8 @@
class HalInterface : public camera3::Camera3StreamBufferFreedListener {
public:
HalInterface(sp<hardware::camera::device::V3_2::ICameraDeviceSession> &session,
- std::shared_ptr<RequestMetadataQueue> queue);
+ std::shared_ptr<RequestMetadataQueue> queue,
+ bool useHalBufManager);
HalInterface(const HalInterface &other);
HalInterface();
@@ -299,14 +286,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 +314,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;
@@ -321,7 +323,7 @@
// The output HIDL request still depends on input camera3_capture_request_t
// Do not free input camera3_capture_request_t before output HIDL request
- void wrapAsHidlRequest(camera3_capture_request_t* in,
+ status_t wrapAsHidlRequest(camera3_capture_request_t* in,
/*out*/hardware::camera::device::V3_2::CaptureRequest* out,
/*out*/std::vector<native_handle_t*>* handlesCreated);
@@ -365,19 +367,18 @@
// 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;
+
+ const bool mUseHalBufManager;
};
sp<HalInterface> mInterface;
@@ -412,9 +413,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 +497,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 +510,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 +726,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 +816,8 @@
mRequestLatency.dump(fd, name);
}
+ void signalPipelineDrain(const std::vector<int>& streamIds);
+
protected:
virtual bool threadLoop();
@@ -899,12 +927,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 +966,8 @@
Vector<int32_t> mSessionParamKeys;
CameraMetadata mLatestSessionParams;
+
+ const bool mUseHalBufManager;
};
sp<RequestThread> mRequestThread;
@@ -1003,6 +1034,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 +1054,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 +1069,8 @@
skipResultMetadata(false),
physicalCameraIds(physicalCameraIdSet),
stillCapture(isStillCapture),
- zslCapture(isZslCapture) {
+ zslCapture(isZslCapture),
+ outputSurfaces(outSurfaces) {
}
};
@@ -1051,7 +1087,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 +1198,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 +1258,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 8bc208d..2b5debf 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -83,8 +83,8 @@
// from input, and attached to the outputs. In this case, the input queue's
// dequeueBuffer can still allocate 1 extra buffer before being blocked by
// the output's attachBuffer().
- mBufferItemConsumer = new BufferItemConsumer(mConsumer, consumerUsage,
- mMaxConsumerBuffers+1);
+ mMaxConsumerBuffers++;
+ mBufferItemConsumer = new BufferItemConsumer(mConsumer, consumerUsage, mMaxConsumerBuffers);
if (mBufferItemConsumer == nullptr) {
return NO_MEMORY;
}
@@ -108,6 +108,7 @@
mHeight = height;
mFormat = format;
mProducerUsage = producerUsage;
+ mAcquiredInputBuffers = 0;
SP_LOGV("%s: connected", __FUNCTION__);
return res;
@@ -147,9 +148,12 @@
mMaxHalBuffers = 0;
mMaxConsumerBuffers = 0;
+ mAcquiredInputBuffers = 0;
SP_LOGV("%s: Disconnected", __FUNCTION__);
}
+Camera3StreamSplitter::Camera3StreamSplitter(bool useHalBufManager) :
+ mUseHalBufManager(useHalBufManager) {}
Camera3StreamSplitter::~Camera3StreamSplitter() {
disconnect();
@@ -165,7 +169,9 @@
return res;
}
- res = mConsumer->setMaxAcquiredBufferCount(mMaxConsumerBuffers+1);
+ if (mMaxConsumerBuffers > mAcquiredInputBuffers) {
+ res = mConsumer->setMaxAcquiredBufferCount(mMaxConsumerBuffers);
+ }
return res;
}
@@ -237,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);
@@ -266,10 +274,12 @@
return res;
}
- res = mConsumer->setMaxAcquiredBufferCount(mMaxConsumerBuffers+1);
- if (res != OK) {
- SP_LOGE("%s: setMaxAcquiredBufferCount failed %d", __FUNCTION__, res);
- return res;
+ if (mAcquiredInputBuffers < mMaxConsumerBuffers) {
+ res = mConsumer->setMaxAcquiredBufferCount(mMaxConsumerBuffers);
+ if (res != OK) {
+ SP_LOGE("%s: setMaxAcquiredBufferCount failed %d", __FUNCTION__, res);
+ return res;
+ }
}
return res;
@@ -430,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)) {
@@ -497,6 +508,7 @@
return;
}
+ mAcquiredInputBuffers++;
SP_LOGV("acquired buffer %" PRId64 " from input at slot %d",
bufferItem.mGraphicBuffer->getId(), bufferItem.mSlot);
@@ -599,6 +611,12 @@
} else {
SP_LOGE("%s: releaseBuffer returns %d", __FUNCTION__, res);
}
+ } else {
+ if (mAcquiredInputBuffers == 0) {
+ ALOGW("%s: Acquired input buffer count already at zero!", __FUNCTION__);
+ } else {
+ mAcquiredInputBuffers--;
+ }
}
}
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
index fea1bdb..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;
@@ -269,7 +272,12 @@
// Latest onFrameAvailable return value
std::atomic<status_t> mOnFrameAvailableRes{0};
+ // Currently acquired input buffers
+ 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