Merge "CamcorderProfile: guard unknown profile qualities" into lmp-sprout-dev
diff --git a/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py b/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py
index b1f51f3..563cebd 100644
--- a/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py
+++ b/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py
@@ -17,6 +17,9 @@
import its.objects
import its.target
+# AE must converge within this number of auto requests under scene1
+THRESH_AE_CONVERGE = 8
+
def main():
"""Test the AE state machine when using the precapture trigger.
"""
@@ -68,7 +71,7 @@
# Capture some more auto requests, and AE should converge.
auto_req['android.control.aePrecaptureTrigger'] = 0
- caps = cam.do_capture([auto_req]*5, fmt)
+ caps = cam.do_capture([auto_req] * THRESH_AE_CONVERGE, fmt)
state = caps[-1]['metadata']['android.control.aeState']
print "AE state after auto request:", state
assert(state == CONVERGED)
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataCollectionTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataCollectionTest.java
new file mode 100644
index 0000000..283f09b
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataCollectionTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.cts;
+
+import android.content.pm.PackageManager;
+import android.cts.util.DeviceReportLog;
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.hardware.camera2.cts.helpers.CameraMetadataGetter;
+import android.util.Log;
+
+import com.android.cts.util.ResultType;
+import com.android.cts.util.ResultUnit;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.util.Iterator;
+
+/**
+ * This test collects camera2 API static metadata and reports to device report.
+ *
+ */
+public class StaticMetadataCollectionTest extends Camera2SurfaceViewTestCase {
+ private static final String TAG = "StaticMetadataCollectionTest";
+
+ private DeviceReportLog mReportLog;
+
+ @Override
+ protected void setUp() throws Exception {
+ mReportLog = new DeviceReportLog();
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ // Deliver the report to host will automatically clear the report log.
+ mReportLog.deliverReportToHost(getInstrumentation());
+ super.tearDown();
+ }
+
+ public void testDataCollection() {
+ if (hasCameraFeature()) {
+ CameraMetadataGetter cameraInfoGetter = new CameraMetadataGetter(mCameraManager);
+ for (String id : mCameraIds) {
+ // Gather camera info
+ JSONObject cameraInfo = cameraInfoGetter.getCameraInfo(id);
+ dumpJsonObjectAsCtsResult(String.format("camera2_id%s_static_info", id), cameraInfo);
+ dumpDoubleAsCtsResult(String.format("camera2_id%s_static_info:", id)
+ + cameraInfo.toString(), 0);
+
+ JSONObject[] templates = cameraInfoGetter.getCaptureRequestTemplates(id);
+ for (int i = 0; i < templates.length; i++) {
+ dumpJsonObjectAsCtsResult(String.format("camera2_id%s_capture_template%d",
+ id, CameraMetadataGetter.TEMPLATE_IDS[i]), templates[i]);
+ if (templates[i] != null) {
+ dumpDoubleAsCtsResult(String.format("camera2_id%s_capture_template%d:",
+ id, CameraMetadataGetter.TEMPLATE_IDS[i])
+ + templates[i].toString(), 0);
+ }
+ }
+ }
+
+ try {
+ cameraInfoGetter.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to close camera info getter " + e.getMessage());
+ }
+
+ mReportLog.printSummary("Camera data collection for static info and capture request"
+ + " templates",
+ 0.0, ResultType.NEUTRAL, ResultUnit.NONE);
+ }
+
+ }
+
+ private void dumpDoubleAsCtsResult(String name, double value) {
+ mReportLog.printValue(name, value, ResultType.NEUTRAL, ResultUnit.NONE);
+ }
+
+ public void dumpDoubleArrayAsCtsResult(String name, double[] values) {
+ mReportLog.printArray(name, values, ResultType.NEUTRAL, ResultUnit.NONE);
+ }
+
+ private double getJsonValueAsDouble(String name, Object obj) throws Exception {
+ if (obj == null) {
+ Log.e(TAG, "Null value: " + name);
+ throw new Exception();
+ } else if (obj instanceof Double) {
+ return ((Double)obj).doubleValue();
+ } else if (obj instanceof Float) {
+ return ((Float)obj).floatValue();
+ } else if (obj instanceof Long) {
+ return ((Long)obj).longValue();
+ } else if (obj instanceof Integer) {
+ return ((Integer)obj).intValue();
+ } else if (obj instanceof Byte) {
+ return ((Byte)obj).intValue();
+ } else if (obj instanceof Short) {
+ return ((Short)obj).intValue();
+ } else if (obj instanceof Boolean) {
+ return ((Boolean)obj) ? 1 : 0;
+ } else {
+ Log.e(TAG, "Unsupported value type: " + name);
+ throw new Exception();
+ }
+ }
+
+ private void dumpJsonArrayAsCtsResult(String name, JSONArray arr) throws Exception {
+ if (arr == null || arr.length() == 0) {
+ dumpDoubleAsCtsResult(name + "[]", 0);
+ } else if (arr.get(0) instanceof JSONObject) {
+ for (int i = 0; i < arr.length(); i++) {
+ dumpJsonObjectAsCtsResult(name+String.format("[%04d]",i),(JSONObject)arr.get(i));
+ }
+ } else if (arr.get(0) instanceof JSONArray) {
+ for (int i = 0; i < arr.length(); i++) {
+ dumpJsonArrayAsCtsResult(name+String.format("[%04d]",i),(JSONArray)arr.get(i));
+ }
+ } else if (!(arr.get(0) instanceof String)) {
+ double[] values = new double[arr.length()];
+ for (int i = 0; i < arr.length(); i++) {
+ values[i] = getJsonValueAsDouble(name + "[]", arr.get(i));
+ }
+ dumpDoubleArrayAsCtsResult(name + "[]", values);
+ } else if (arr.get(0) instanceof String) {
+ for (int i = 0; i < arr.length(); i++) {
+ dumpDoubleAsCtsResult(
+ name+String.format("[%04d]",i)+" = "+(String)arr.get(i), 0);
+ }
+ } else {
+ Log.e(TAG, "Unsupported array value type: " + name);
+ throw new Exception();
+ }
+ }
+
+ private void dumpJsonObjectAsCtsResult(String name, JSONObject obj) {
+ if (obj == null) {
+ dumpDoubleAsCtsResult(name + "{}", 0);
+ return;
+ }
+ Iterator<?> keys = obj.keys();
+ while (keys.hasNext()) {
+ try {
+ String key = (String)keys.next();
+ if (obj.get(key) instanceof JSONObject) {
+ dumpJsonObjectAsCtsResult(name+"."+key, (JSONObject)obj.get(key));
+ } else if (obj.get(key) instanceof JSONArray) {
+ dumpJsonArrayAsCtsResult(name+"."+key, (JSONArray)obj.get(key));
+ } else if (!(obj.get(key) instanceof String)) {
+ dumpDoubleAsCtsResult(name+"."+key,
+ getJsonValueAsDouble(name+"."+key, obj.get(key)));
+ } else if (obj.get(key) instanceof String) {
+ dumpDoubleAsCtsResult(name+"."+key + " = " + (String)obj.get(key), 0);
+ } else {
+ Log.e(TAG, "Unsupported object field type: " + name + "." + key);
+ }
+ } catch (Exception e) {
+ // Swallow
+ }
+ }
+ }
+
+ private boolean hasCameraFeature() {
+ PackageManager packageManager = getActivity().getPackageManager();
+ return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
index f18a1cf..e816659 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -291,6 +291,12 @@
try {
Log.i(TAG, "Testing AE compensation for Camera " + id);
openDevice(id);
+
+ if (mStaticInfo.isHardwareLevelLegacy()) {
+ Log.i(TAG, "Skipping test on legacy devices");
+ continue;
+ }
+
aeCompensationTestByCamera();
} finally {
closeDevice();
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraMetadataGetter.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraMetadataGetter.java
new file mode 100755
index 0000000..db75cdd
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraMetadataGetter.java
@@ -0,0 +1,690 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.cts.helpers;
+
+import static com.android.ex.camera2.blocking.BlockingStateCallback.*;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.BlackLevelPattern;
+import android.hardware.camera2.params.ColorSpaceTransform;
+import android.hardware.camera2.params.Face;
+import android.hardware.camera2.params.LensShadingMap;
+import android.hardware.camera2.params.MeteringRectangle;
+import android.hardware.camera2.params.RggbChannelVector;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.params.TonemapCurve;
+import android.location.Location;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Rational;
+import android.util.Size;
+import android.util.SizeF;
+import android.util.Range;
+
+import com.android.ex.camera2.blocking.BlockingCameraManager;
+import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
+import com.android.ex.camera2.blocking.BlockingStateCallback;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+/**
+ * Utility class to dump the camera metadata.
+ */
+public final class CameraMetadataGetter implements AutoCloseable {
+ private static final String TAG = CameraMetadataGetter.class.getSimpleName();
+ private static final int CAMERA_CLOSE_TIMEOUT_MS = 5000;
+ public static final int[] TEMPLATE_IDS = {
+ CameraDevice.TEMPLATE_PREVIEW,
+ CameraDevice.TEMPLATE_STILL_CAPTURE,
+ CameraDevice.TEMPLATE_RECORD,
+ CameraDevice.TEMPLATE_VIDEO_SNAPSHOT,
+ CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG,
+ CameraDevice.TEMPLATE_MANUAL,
+ };
+ private CameraManager mCameraManager;
+ private BlockingStateCallback mCameraListener;
+ private HandlerThread mHandlerThread;
+ private Handler mHandler;
+
+ private static class MetadataEntry {
+ public MetadataEntry(String k, Object v) {
+ key = k;
+ value = v;
+ }
+
+ public String key;
+ public Object value;
+ }
+
+ public CameraMetadataGetter(CameraManager cameraManager) {
+ if (cameraManager == null) {
+ throw new IllegalArgumentException("can not create an CameraMetadataGetter object"
+ + " with null CameraManager");
+ }
+
+ mCameraManager = cameraManager;
+
+ mCameraListener = new BlockingStateCallback();
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ }
+
+ public String getCameraInfo() {
+ StringBuffer cameraInfo = new StringBuffer("{\"CameraStaticMetadata\":{");
+ CameraCharacteristics staticMetadata;
+ String[] cameraIds;
+ try {
+ cameraIds = mCameraManager.getCameraIdList();
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Unable to get camera ids, skip this info, error: " + e.getMessage());
+ return "";
+ }
+ for (String id : cameraIds) {
+ String value = null;
+ try {
+ staticMetadata = mCameraManager.getCameraCharacteristics(id);
+ value = serialize(staticMetadata).toString();
+ } catch (CameraAccessException e) {
+ Log.e(TAG,
+ "Unable to get camera camera static info, skip this camera, error: "
+ + e.getMessage());
+ }
+ cameraInfo.append("\"camera" + id + "\":"); // Key
+ cameraInfo.append(value); // Value
+ // If not last, print "," // Separator
+ if (!id.equals(cameraIds[cameraIds.length - 1])) {
+ cameraInfo.append(",");
+ }
+ }
+ cameraInfo.append("}}");
+
+ return cameraInfo.toString();
+ }
+
+ public JSONObject getCameraInfo(String cameraId) {
+ JSONObject staticMetadata = null;
+ try {
+ staticMetadata = serialize(mCameraManager.getCameraCharacteristics(cameraId));
+ } catch (CameraAccessException e) {
+ Log.e(TAG,
+ "Unable to get camera camera static info, skip this camera, error: "
+ + e.getMessage());
+ }
+ return staticMetadata;
+ }
+
+ public JSONObject[] getCaptureRequestTemplates(String cameraId) {
+ JSONObject[] templates = new JSONObject[TEMPLATE_IDS.length];
+ CameraDevice camera = null;
+ try {
+ camera = (new BlockingCameraManager(mCameraManager)).openCamera(cameraId,
+ mCameraListener, mHandler);
+ for (int i = 0; i < TEMPLATE_IDS.length; i++) {
+ CaptureRequest.Builder request;
+ try {
+ request = camera.createCaptureRequest(TEMPLATE_IDS[i]);
+ templates[i] = serialize(request.build());
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to create template " + TEMPLATE_IDS[i]
+ + " because of error " + e.getMessage());
+ templates[i] = null;
+ }
+ }
+ return templates;
+ } catch (CameraAccessException | BlockingOpenException e) {
+ Log.e(TAG, "Unable to open camera " + cameraId + " because of error "
+ + e.getMessage());
+ return new JSONObject[0];
+ } finally {
+ if (camera != null) {
+ camera.close();
+ }
+ }
+ }
+
+ public String getCaptureRequestTemplates() {
+ StringBuffer templates = new StringBuffer("{\"CameraRequestTemplates\":{");
+ String[] cameraIds;
+ try {
+ cameraIds = mCameraManager.getCameraIdList();
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Unable to get camera ids, skip this info, error: " + e.getMessage());
+ return "";
+ }
+ CameraDevice camera = null;
+ for (String id : cameraIds) {
+ try {
+ try {
+ camera = (new BlockingCameraManager(mCameraManager)).openCamera(id,
+ mCameraListener, mHandler);
+ } catch (CameraAccessException | BlockingOpenException e) {
+ Log.e(TAG, "Unable to open camera " + id + " because of error "
+ + e.getMessage());
+ continue;
+ }
+
+ for (int i = 0; i < TEMPLATE_IDS.length; i++) {
+ String value = null;
+ CaptureRequest.Builder request;
+ try {
+ request = camera.createCaptureRequest(TEMPLATE_IDS[i]);
+ value = serialize(request.build()).toString();
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to create template " + TEMPLATE_IDS[i]
+ + " because of error " + e.getMessage());
+ }
+ templates.append("\"Camera" + id + "CaptureTemplate" +
+ TEMPLATE_IDS[i] + "\":");
+ templates.append(value);
+ if (!id.equals(cameraIds[cameraIds.length - 1]) ||
+ i < (TEMPLATE_IDS.length - 1)) {
+ templates.append(",");
+ }
+ }
+ } finally {
+ if (camera != null) {
+ camera.close();
+ mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+ }
+ }
+ }
+
+ templates.append("}}");
+ return templates.toString();
+ }
+
+ /*
+ * Cleanup the resources.
+ */
+ @Override
+ public void close() throws Exception {
+ mHandlerThread.quitSafely();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object serializeRational(Rational rat) throws org.json.JSONException {
+ JSONObject ratObj = new JSONObject();
+ ratObj.put("numerator", rat.getNumerator());
+ ratObj.put("denominator", rat.getDenominator());
+ return ratObj;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object serializeSize(Size size) throws org.json.JSONException {
+ JSONObject sizeObj = new JSONObject();
+ sizeObj.put("width", size.getWidth());
+ sizeObj.put("height", size.getHeight());
+ return sizeObj;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object serializeSizeF(SizeF size) throws org.json.JSONException {
+ JSONObject sizeObj = new JSONObject();
+ sizeObj.put("width", size.getWidth());
+ sizeObj.put("height", size.getHeight());
+ return sizeObj;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object serializeRect(Rect rect) throws org.json.JSONException {
+ JSONObject rectObj = new JSONObject();
+ rectObj.put("left", rect.left);
+ rectObj.put("right", rect.right);
+ rectObj.put("top", rect.top);
+ rectObj.put("bottom", rect.bottom);
+ return rectObj;
+ }
+
+ private static Object serializePoint(Point point) throws org.json.JSONException {
+ JSONObject pointObj = new JSONObject();
+ pointObj.put("x", point.x);
+ pointObj.put("y", point.y);
+ return pointObj;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object serializeFace(Face face)
+ throws org.json.JSONException {
+ JSONObject faceObj = new JSONObject();
+ faceObj.put("bounds", serializeRect(face.getBounds()));
+ faceObj.put("score", face.getScore());
+ faceObj.put("id", face.getId());
+ faceObj.put("leftEye", serializePoint(face.getLeftEyePosition()));
+ faceObj.put("rightEye", serializePoint(face.getRightEyePosition()));
+ faceObj.put("mouth", serializePoint(face.getMouthPosition()));
+ return faceObj;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object serializeStreamConfigurationMap(
+ StreamConfigurationMap map)
+ throws org.json.JSONException {
+ // TODO: Serialize the rest of the StreamConfigurationMap fields.
+ JSONObject mapObj = new JSONObject();
+ JSONArray cfgArray = new JSONArray();
+ int fmts[] = map.getOutputFormats();
+ if (fmts != null) {
+ for (int fi = 0; fi < Array.getLength(fmts); fi++) {
+ Size sizes[] = map.getOutputSizes(fmts[fi]);
+ if (sizes != null) {
+ for (int si = 0; si < Array.getLength(sizes); si++) {
+ JSONObject obj = new JSONObject();
+ obj.put("format", fmts[fi]);
+ obj.put("width", sizes[si].getWidth());
+ obj.put("height", sizes[si].getHeight());
+ obj.put("input", false);
+ obj.put("minFrameDuration",
+ map.getOutputMinFrameDuration(fmts[fi], sizes[si]));
+ cfgArray.put(obj);
+ }
+ }
+ }
+ }
+ mapObj.put("availableStreamConfigurations", cfgArray);
+ return mapObj;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object serializeMeteringRectangle(MeteringRectangle rect)
+ throws org.json.JSONException {
+ JSONObject rectObj = new JSONObject();
+ rectObj.put("x", rect.getX());
+ rectObj.put("y", rect.getY());
+ rectObj.put("width", rect.getWidth());
+ rectObj.put("height", rect.getHeight());
+ rectObj.put("weight", rect.getMeteringWeight());
+ return rectObj;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object serializePair(Pair pair)
+ throws org.json.JSONException {
+ JSONArray pairObj = new JSONArray();
+ pairObj.put(pair.first);
+ pairObj.put(pair.second);
+ return pairObj;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object serializeRange(Range range)
+ throws org.json.JSONException {
+ JSONArray rangeObj = new JSONArray();
+ rangeObj.put(range.getLower());
+ rangeObj.put(range.getUpper());
+ return rangeObj;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object serializeColorSpaceTransform(ColorSpaceTransform xform)
+ throws org.json.JSONException {
+ JSONArray xformObj = new JSONArray();
+ for (int row = 0; row < 3; row++) {
+ for (int col = 0; col < 3; col++) {
+ xformObj.put(serializeRational(xform.getElement(col, row)));
+ }
+ }
+ return xformObj;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object serializeTonemapCurve(TonemapCurve curve)
+ throws org.json.JSONException {
+ JSONObject curveObj = new JSONObject();
+ String names[] = {
+ "red", "green", "blue" };
+ for (int ch = 0; ch < 3; ch++) {
+ JSONArray curveArr = new JSONArray();
+ int len = curve.getPointCount(ch);
+ for (int i = 0; i < len; i++) {
+ curveArr.put(curve.getPoint(ch, i).x);
+ curveArr.put(curve.getPoint(ch, i).y);
+ }
+ curveObj.put(names[ch], curveArr);
+ }
+ return curveObj;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object serializeRggbChannelVector(RggbChannelVector vec)
+ throws org.json.JSONException {
+ JSONArray vecObj = new JSONArray();
+ vecObj.put(vec.getRed());
+ vecObj.put(vec.getGreenEven());
+ vecObj.put(vec.getGreenOdd());
+ vecObj.put(vec.getBlue());
+ return vecObj;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object serializeBlackLevelPattern(BlackLevelPattern pat)
+ throws org.json.JSONException {
+ int patVals[] = new int[4];
+ pat.copyTo(patVals, 0);
+ JSONArray patObj = new JSONArray();
+ patObj.put(patVals[0]);
+ patObj.put(patVals[1]);
+ patObj.put(patVals[2]);
+ patObj.put(patVals[3]);
+ return patObj;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object serializeLocation(Location loc)
+ throws org.json.JSONException {
+ return loc.toString();
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object serializeLensShadingMap(LensShadingMap map)
+ throws org.json.JSONException {
+ JSONArray mapObj = new JSONArray();
+ for (int row = 0; row < map.getRowCount(); row++) {
+ for (int col = 0; col < map.getColumnCount(); col++) {
+ for (int ch = 0; ch < 4; ch++) {
+ mapObj.put(map.getGainFactor(ch, col, row));
+ }
+ }
+ }
+ return mapObj;
+ }
+
+ private static String getKeyName(Object keyObj) {
+ if (keyObj.getClass() == CaptureResult.Key.class
+ || keyObj.getClass() == TotalCaptureResult.class) {
+ return ((CaptureResult.Key) keyObj).getName();
+ } else if (keyObj.getClass() == CaptureRequest.Key.class) {
+ return ((CaptureRequest.Key) keyObj).getName();
+ } else if (keyObj.getClass() == CameraCharacteristics.Key.class) {
+ return ((CameraCharacteristics.Key) keyObj).getName();
+ }
+
+ throw new IllegalArgumentException("Invalid key object");
+ }
+
+ private static Object getKeyValue(CameraMetadata md, Object keyObj) {
+ if (md.getClass() == CaptureResult.class || md.getClass() == TotalCaptureResult.class) {
+ return ((CaptureResult) md).get((CaptureResult.Key) keyObj);
+ } else if (md.getClass() == CaptureRequest.class) {
+ return ((CaptureRequest) md).get((CaptureRequest.Key) keyObj);
+ } else if (md.getClass() == CameraCharacteristics.class) {
+ return ((CameraCharacteristics) md).get((CameraCharacteristics.Key) keyObj);
+ }
+
+ throw new IllegalArgumentException("Invalid key object");
+ }
+
+ @SuppressWarnings("unchecked")
+ private static MetadataEntry serializeEntry(Type keyType, Object keyObj, CameraMetadata md) {
+ String keyName = getKeyName(keyObj);
+
+ try {
+ Object keyValue = getKeyValue(md, keyObj);
+ if (keyValue == null) {
+ return new MetadataEntry(keyName, JSONObject.NULL);
+ } else if (keyType == Float.class) {
+ // The JSON serializer doesn't handle floating point NaN or Inf.
+ if (((Float) keyValue).isInfinite() || ((Float) keyValue).isNaN()) {
+ Log.w(TAG, "Inf/NaN floating point value serialized: " + keyName);
+ return null;
+ }
+ return new MetadataEntry(keyName, keyValue);
+ } else if (keyType == Integer.class || keyType == Long.class || keyType == Byte.class ||
+ keyType == Boolean.class || keyType == String.class) {
+ return new MetadataEntry(keyName, keyValue);
+ } else if (keyType == Rational.class) {
+ return new MetadataEntry(keyName, serializeRational((Rational) keyValue));
+ } else if (keyType == Size.class) {
+ return new MetadataEntry(keyName, serializeSize((Size) keyValue));
+ } else if (keyType == SizeF.class) {
+ return new MetadataEntry(keyName, serializeSizeF((SizeF) keyValue));
+ } else if (keyType == Rect.class) {
+ return new MetadataEntry(keyName, serializeRect((Rect) keyValue));
+ } else if (keyType == Face.class) {
+ return new MetadataEntry(keyName, serializeFace((Face) keyValue));
+ } else if (keyType == StreamConfigurationMap.class) {
+ return new MetadataEntry(keyName,
+ serializeStreamConfigurationMap((StreamConfigurationMap) keyValue));
+ } else if (keyType instanceof ParameterizedType &&
+ ((ParameterizedType) keyType).getRawType() == Range.class) {
+ return new MetadataEntry(keyName, serializeRange((Range) keyValue));
+ } else if (keyType == ColorSpaceTransform.class) {
+ return new MetadataEntry(keyName,
+ serializeColorSpaceTransform((ColorSpaceTransform) keyValue));
+ } else if (keyType == MeteringRectangle.class) {
+ return new MetadataEntry(keyName,
+ serializeMeteringRectangle((MeteringRectangle) keyValue));
+ } else if (keyType == Location.class) {
+ return new MetadataEntry(keyName,
+ serializeLocation((Location) keyValue));
+ } else if (keyType == RggbChannelVector.class) {
+ return new MetadataEntry(keyName,
+ serializeRggbChannelVector((RggbChannelVector) keyValue));
+ } else if (keyType == BlackLevelPattern.class) {
+ return new MetadataEntry(keyName,
+ serializeBlackLevelPattern((BlackLevelPattern) keyValue));
+ } else if (keyType == TonemapCurve.class) {
+ return new MetadataEntry(keyName,
+ serializeTonemapCurve((TonemapCurve) keyValue));
+ } else if (keyType == Point.class) {
+ return new MetadataEntry(keyName,
+ serializePoint((Point) keyValue));
+ } else if (keyType == LensShadingMap.class) {
+ return new MetadataEntry(keyName,
+ serializeLensShadingMap((LensShadingMap) keyValue));
+ } else {
+ Log.w(TAG, String.format("Serializing unsupported key type: " + keyType));
+ return null;
+ }
+ } catch (org.json.JSONException e) {
+ throw new IllegalStateException("JSON error for key: " + keyName + ": ", e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static MetadataEntry serializeArrayEntry(Type keyType, Object keyObj,
+ CameraMetadata md) {
+ String keyName = getKeyName(keyObj);
+ try {
+ Object keyValue = getKeyValue(md, keyObj);
+ if (keyValue == null) {
+ return new MetadataEntry(keyName, JSONObject.NULL);
+ }
+ int arrayLen = Array.getLength(keyValue);
+ Type elmtType = ((GenericArrayType) keyType).getGenericComponentType();
+ if (elmtType == int.class || elmtType == float.class || elmtType == byte.class ||
+ elmtType == long.class || elmtType == double.class
+ || elmtType == boolean.class) {
+ return new MetadataEntry(keyName, new JSONArray(keyValue));
+ } else if (elmtType == Rational.class) {
+ JSONArray jsonArray = new JSONArray();
+ for (int i = 0; i < arrayLen; i++) {
+ jsonArray.put(serializeRational((Rational) Array.get(keyValue, i)));
+ }
+ return new MetadataEntry(keyName, jsonArray);
+ } else if (elmtType == Size.class) {
+ JSONArray jsonArray = new JSONArray();
+ for (int i = 0; i < arrayLen; i++) {
+ jsonArray.put(serializeSize((Size) Array.get(keyValue, i)));
+ }
+ return new MetadataEntry(keyName, jsonArray);
+ } else if (elmtType == Rect.class) {
+ JSONArray jsonArray = new JSONArray();
+ for (int i = 0; i < arrayLen; i++) {
+ jsonArray.put(serializeRect((Rect) Array.get(keyValue, i)));
+ }
+ return new MetadataEntry(keyName, jsonArray);
+ } else if (elmtType == Face.class) {
+ JSONArray jsonArray = new JSONArray();
+ for (int i = 0; i < arrayLen; i++) {
+ jsonArray.put(serializeFace((Face) Array.get(keyValue, i)));
+ }
+ return new MetadataEntry(keyName, jsonArray);
+ } else if (elmtType == StreamConfigurationMap.class) {
+ JSONArray jsonArray = new JSONArray();
+ for (int i = 0; i < arrayLen; i++) {
+ jsonArray.put(serializeStreamConfigurationMap(
+ (StreamConfigurationMap) Array.get(keyValue, i)));
+ }
+ return new MetadataEntry(keyName, jsonArray);
+ } else if (elmtType instanceof ParameterizedType &&
+ ((ParameterizedType) elmtType).getRawType() == Range.class) {
+ JSONArray jsonArray = new JSONArray();
+ for (int i = 0; i < arrayLen; i++) {
+ jsonArray.put(serializeRange((Range) Array.get(keyValue, i)));
+ }
+ return new MetadataEntry(keyName, jsonArray);
+ } else if (elmtType instanceof ParameterizedType &&
+ ((ParameterizedType) elmtType).getRawType() == Pair.class) {
+ JSONArray jsonArray = new JSONArray();
+ for (int i = 0; i < arrayLen; i++) {
+ jsonArray.put(serializePair((Pair) Array.get(keyValue, i)));
+ }
+ return new MetadataEntry(keyName, jsonArray);
+ } else if (elmtType == MeteringRectangle.class) {
+ JSONArray jsonArray = new JSONArray();
+ for (int i = 0; i < arrayLen; i++) {
+ jsonArray.put(serializeMeteringRectangle(
+ (MeteringRectangle) Array.get(keyValue, i)));
+ }
+ return new MetadataEntry(keyName, jsonArray);
+ } else if (elmtType == Location.class) {
+ JSONArray jsonArray = new JSONArray();
+ for (int i = 0; i < arrayLen; i++) {
+ jsonArray.put(serializeLocation((Location) Array.get(keyValue, i)));
+ }
+ return new MetadataEntry(keyName, jsonArray);
+ } else if (elmtType == RggbChannelVector.class) {
+ JSONArray jsonArray = new JSONArray();
+ for (int i = 0; i < arrayLen; i++) {
+ jsonArray.put(serializeRggbChannelVector(
+ (RggbChannelVector) Array.get(keyValue, i)));
+ }
+ return new MetadataEntry(keyName, jsonArray);
+ } else if (elmtType == BlackLevelPattern.class) {
+ JSONArray jsonArray = new JSONArray();
+ for (int i = 0; i < arrayLen; i++) {
+ jsonArray.put(serializeBlackLevelPattern(
+ (BlackLevelPattern) Array.get(keyValue, i)));
+ }
+ return new MetadataEntry(keyName, jsonArray);
+ } else if (elmtType == Point.class) {
+ JSONArray jsonArray = new JSONArray();
+ for (int i = 0; i < arrayLen; i++) {
+ jsonArray.put(serializePoint((Point) Array.get(keyValue, i)));
+ }
+ return new MetadataEntry(keyName, jsonArray);
+ } else {
+ Log.w(TAG, String.format("Serializing unsupported array type: " + elmtType));
+ return null;
+ }
+ } catch (org.json.JSONException e) {
+ throw new IllegalStateException("JSON error for key: " + keyName + ": ", e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static JSONObject serialize(CameraMetadata md) {
+ JSONObject jsonObj = new JSONObject();
+ Field[] allFields = md.getClass().getDeclaredFields();
+ if (md.getClass() == TotalCaptureResult.class) {
+ allFields = CaptureResult.class.getDeclaredFields();
+ }
+ for (Field field : allFields) {
+ if (Modifier.isPublic(field.getModifiers()) &&
+ Modifier.isStatic(field.getModifiers()) &&
+ (field.getType() == CaptureRequest.Key.class
+ || field.getType() == CaptureResult.Key.class
+ || field.getType() == TotalCaptureResult.Key.class
+ || field.getType() == CameraCharacteristics.Key.class)
+ &&
+ field.getGenericType() instanceof ParameterizedType) {
+ ParameterizedType paramType = (ParameterizedType) field.getGenericType();
+ Type[] argTypes = paramType.getActualTypeArguments();
+ if (argTypes.length > 0) {
+ try {
+ Type keyType = argTypes[0];
+ Object keyObj = field.get(md);
+ MetadataEntry entry;
+ if (keyType instanceof GenericArrayType) {
+ entry = serializeArrayEntry(keyType, keyObj, md);
+ } else {
+ entry = serializeEntry(keyType, keyObj, md);
+ }
+
+ // TODO: Figure this weird case out.
+ // There is a weird case where the entry is non-null but
+ // the toString
+ // of the entry is null, and if this happens, the
+ // null-ness spreads like
+ // a virus and makes the whole JSON object null from the
+ // top level down.
+ // Not sure if it's a bug in the library or I'm just not
+ // using it right.
+ // Workaround by checking for this case explicitly and
+ // not adding the
+ // value to the jsonObj when it is detected.
+ if (entry != null && entry.key != null && entry.value != null
+ && entry.value.toString() == null) {
+ Log.w(TAG, "Error encountered serializing value for key: "
+ + entry.key);
+ } else if (entry != null) {
+ jsonObj.put(entry.key, entry.value);
+ } else {
+ // Ignore.
+ }
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException(
+ "Access error for field: " + field + ": ", e);
+ } catch (org.json.JSONException e) {
+ throw new IllegalStateException(
+ "JSON error for field: " + field + ": ", e);
+ }
+ }
+ }
+ }
+ return jsonObj;
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
index 673c1d7..ff05246 100644
--- a/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
+++ b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
@@ -403,11 +403,6 @@
}
}
- private boolean hasAudioOutput() {
- return getInstrumentation().getTargetContext().getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT);
- }
-
/**
* Tests clear key system playback.
*/
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
index 8917144..d71d38a 100644
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -79,42 +79,6 @@
masterFd.close();
}
- private boolean hasCodecForMimeType(String mimeType) {
- MediaCodecList list = new MediaCodecList(MediaCodecList.ALL_CODECS);
- for (MediaCodecInfo info : list.getCodecInfos()) {
- for (String type : info.getSupportedTypes()) {
- if (type.equalsIgnoreCase(mimeType)) {
- return true;
- }
- }
- }
- return false;
- }
-
- private boolean hasH264() {
- return hasCodecForMimeType("video/avc");
- }
-
- private boolean hasHEVC() {
- return hasCodecForMimeType("video/hevc");
- }
-
- private boolean hasH263() {
- return hasCodecForMimeType("video/3gpp");
- }
-
- private boolean hasMpeg4() {
- return hasCodecForMimeType("video/mp4v-es");
- }
-
- private boolean hasVP8() {
- return hasCodecForMimeType("video/x-vnd.on2.vp8");
- }
-
- private boolean hasVP9() {
- return hasCodecForMimeType("video/x-vnd.on2.vp9");
- }
-
// TODO: add similar tests for other audio and video formats
public void testBug11696552() throws Exception {
MediaCodec mMediaCodec = MediaCodec.createDecoderByType("audio/mp4a-latm");
@@ -212,8 +176,8 @@
MediaFormat format = ex.getTrackFormat(0);
String mime = format.getString(MediaFormat.KEY_MIME);
assertTrue("not a video track. Wrong test file?", mime.startsWith("video/"));
- if (!hasCodecForMimeType(mime)) {
- Log.i(TAG, "Could not find a codec for mimeType: " + mime);
+ if (!hasCodecForMimeType(mime, false)) {
+ Log.i(TAG, "SKIPPING testBFrames(): Could not find a codec for mimeType: " + mime);
return;
}
MediaCodec dec = MediaCodec.createDecoderByType(mime);
@@ -881,7 +845,8 @@
}
public void testCodecBasicH264() throws Exception {
- if (!hasH264()) {
+ if (!hasH264(false)) {
+ Log.i(TAG, "SKIPPING testCodecBasicH264(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -897,7 +862,8 @@
}
public void testCodecBasicHEVC() throws Exception {
- if (!hasHEVC()) {
+ if (!hasHEVC(false)) {
+ Log.i(TAG, "SKIPPING testCodecBasicHEVC(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -913,7 +879,8 @@
}
public void testCodecBasicH263() throws Exception {
- if (!hasH263()) {
+ if (!hasH263(false)) {
+ Log.i(TAG, "SKIPPING testCodecBasicH263(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -929,7 +896,8 @@
}
public void testCodecBasicMpeg4() throws Exception {
- if (!hasMpeg4()) {
+ if (!hasMpeg4(false)) {
+ Log.i(TAG, "SKIPPING testCodecBasicMpeg4(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -945,7 +913,8 @@
}
public void testCodecBasicVP8() throws Exception {
- if (!hasVP8()) {
+ if (!hasVP8(false)) {
+ Log.i(TAG, "SKIPPING testCodecBasicVP8(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -961,7 +930,8 @@
}
public void testCodecBasicVP9() throws Exception {
- if (!hasVP9()) {
+ if (!hasVP9(false)) {
+ Log.i(TAG, "SKIPPING testCodecBasicVP9(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -977,7 +947,8 @@
}
public void testCodecEarlyEOSH263() throws Exception {
- if (!hasH263()) {
+ if (!hasH263(false)) {
+ Log.i(TAG, "SKIPPING testCodecEarlyEOSH263(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -988,7 +959,8 @@
}
public void testCodecEarlyEOSH264() throws Exception {
- if (!hasH264()) {
+ if (!hasH264(false)) {
+ Log.i(TAG, "SKIPPING testCodecEarlyEOSH264(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -999,7 +971,8 @@
}
public void testCodecEarlyEOSHEVC() throws Exception {
- if (!hasHEVC()) {
+ if (!hasHEVC(false)) {
+ Log.i(TAG, "SKIPPING testCodecEarlyEOSHEVC(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -1010,7 +983,8 @@
}
public void testCodecEarlyEOSMpeg4() throws Exception {
- if (!hasMpeg4()) {
+ if (!hasMpeg4(false)) {
+ Log.i(TAG, "SKIPPING testCodecEarlyEOSMpeg4(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -1021,7 +995,8 @@
}
public void testCodecEarlyEOSVP8() throws Exception {
- if (!hasVP8()) {
+ if (!hasVP8(false)) {
+ Log.i(TAG, "SKIPPING testCodecEarlyEOSVP8(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -1032,7 +1007,8 @@
}
public void testCodecEarlyEOSVP9() throws Exception {
- if (!hasVP9()) {
+ if (!hasVP9(false)) {
+ Log.i(TAG, "SKIPPING testCodecEarlyEOSVP9(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -1043,7 +1019,8 @@
}
public void testCodecResetsH264WithoutSurface() throws Exception {
- if (!hasH264()) {
+ if (!hasH264(false)) {
+ Log.i(TAG, "SKIPPING testCodecResetsH264WithoutSurface(): No codec found.");
return;
}
testCodecResets(
@@ -1051,7 +1028,8 @@
}
public void testCodecResetsH264WithSurface() throws Exception {
- if (!hasH264()) {
+ if (!hasH264(false)) {
+ Log.i(TAG, "SKIPPING testCodecResetsH264WithSurface(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -1060,7 +1038,8 @@
}
public void testCodecResetsHEVCWithoutSurface() throws Exception {
- if (!hasHEVC()) {
+ if (!hasHEVC(false)) {
+ Log.i(TAG, "SKIPPING testCodecResetsHEVCWithoutSurface(): No codec found.");
return;
}
testCodecResets(
@@ -1068,7 +1047,8 @@
}
public void testCodecResetsHEVCWithSurface() throws Exception {
- if (!hasHEVC()) {
+ if (!hasHEVC(false)) {
+ Log.i(TAG, "SKIPPING testCodecResetsHEVCWithSurface(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -1077,7 +1057,8 @@
}
public void testCodecResetsH263WithoutSurface() throws Exception {
- if (!hasH263()) {
+ if (!hasH263(false)) {
+ Log.i(TAG, "SKIPPING testCodecResetsH263WithoutSurface(): No codec found.");
return;
}
testCodecResets(
@@ -1085,7 +1066,8 @@
}
public void testCodecResetsH263WithSurface() throws Exception {
- if (!hasH263()) {
+ if (!hasH263(false)) {
+ Log.i(TAG, "SKIPPING testCodecResetsH263WithSurface(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -1094,7 +1076,8 @@
}
public void testCodecResetsMpeg4WithoutSurface() throws Exception {
- if (!hasMpeg4()) {
+ if (!hasMpeg4(false)) {
+ Log.i(TAG, "SKIPPING testCodecResetsMpeg4WithoutSurface(): No codec found.");
return;
}
testCodecResets(
@@ -1102,7 +1085,8 @@
}
public void testCodecResetsMpeg4WithSurface() throws Exception {
- if (!hasMpeg4()) {
+ if (!hasMpeg4(false)) {
+ Log.i(TAG, "SKIPPING testCodecResetsMpeg4WithSurface(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -1111,7 +1095,8 @@
}
public void testCodecResetsVP8WithoutSurface() throws Exception {
- if (!hasVP8()) {
+ if (!hasVP8(false)) {
+ Log.i(TAG, "SKIPPING testCodecResetsVP8WithoutSurface(): No codec found.");
return;
}
testCodecResets(
@@ -1119,7 +1104,8 @@
}
public void testCodecResetsVP8WithSurface() throws Exception {
- if (!hasVP8()) {
+ if (!hasVP8(false)) {
+ Log.i(TAG, "SKIPPING testCodecResetsVP8WithSurface(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -1128,7 +1114,8 @@
}
public void testCodecResetsVP9WithoutSurface() throws Exception {
- if (!hasVP9()) {
+ if (!hasVP9(false)) {
+ Log.i(TAG, "SKIPPING testCodecResetsVP9WithoutSurface(): No codec found.");
return;
}
testCodecResets(
@@ -1136,7 +1123,8 @@
}
public void testCodecResetsVP9WithSurface() throws Exception {
- if (!hasVP9()) {
+ if (!hasVP9(false)) {
+ Log.i(TAG, "SKIPPING testCodecResetsVP9WithSurface(): No codec found.");
return;
}
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -1245,6 +1233,13 @@
extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
testFd.getLength());
extractor.selectTrack(0); // consider variable looping on track
+ MediaFormat format = extractor.getTrackFormat(0);
+ String mimeType = format.getString(MediaFormat.KEY_MIME);
+ if (!hasCodecForMimeType(mimeType, false)) {
+ Log.i(TAG, "SKIPPING testEOSBehavior() for resid=" + movie + " No codec found for "
+ + "mimeType = " + mimeType);
+ return;
+ }
List<Long> outputChecksums = new ArrayList<Long>();
List<Long> outputTimestamps = new ArrayList<Long>();
Arrays.sort(stopAtSample);
diff --git a/tests/tests/media/src/android/media/cts/EnvReverbTest.java b/tests/tests/media/src/android/media/cts/EnvReverbTest.java
index e2e9b6d..4cfb744 100644
--- a/tests/tests/media/src/android/media/cts/EnvReverbTest.java
+++ b/tests/tests/media/src/android/media/cts/EnvReverbTest.java
@@ -304,6 +304,9 @@
//Test case 2.1: test setEnabled() throws exception after release
public void test2_1SetEnabledAfterRelease() throws Exception {
+ if (!isEnvReverbAvailable()) {
+ return;
+ }
getReverb(0);
mReverb.release();
try {
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index 78ba149..108aa8b 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -21,6 +21,11 @@
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.media.AudioManager;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaRecorder;
@@ -304,6 +309,11 @@
}
public void testPlayAudioTwice() throws Exception {
+ if (!hasAudioOutput()) {
+ Log.i(LOG_TAG, "SKIPPING testPlayAudioTwice(). No audio output.");
+ return;
+ }
+
final int resid = R.raw.camera_click;
MediaPlayer mp = MediaPlayer.create(mContext, resid);
@@ -550,6 +560,10 @@
}
private void testGapless(int resid1, int resid2) throws Exception {
+ if (!hasAudioOutput()) {
+ Log.i(LOG_TAG, "SKIPPING testPlayAudioTwice(). No audio output.");
+ return;
+ }
MediaPlayer mp1 = new MediaPlayer();
mp1.setAudioStreamType(AudioManager.STREAM_MUSIC);
@@ -660,7 +674,12 @@
}
});
- loadResource(R.raw.testvideo);
+ try {
+ loadResource(R.raw.testvideo);
+ } catch (UnsupportedCodecException e) {
+ Log.i(LOG_TAG, "SKIPPING testVideoSurfaceResetting(). Could not find codec.");
+ return;
+ }
playLoadedVideo(352, 288, -1);
Thread.sleep(SLEEP_TIME);
@@ -1011,7 +1030,12 @@
}
public void testDeselectTrack() throws Throwable {
- loadResource(R.raw.testvideo_with_2_subtitles);
+ try {
+ loadResource(R.raw.testvideo_with_2_subtitles);
+ } catch (UnsupportedCodecException e) {
+ Log.i(LOG_TAG, "SKIPPING testDeselectTrack(). Could not find codec.");
+ return;
+ }
runTestOnUiThread(new Runnable() {
public void run() {
try {
@@ -1082,7 +1106,12 @@
}
public void testChangeSubtitleTrack() throws Throwable {
- loadResource(R.raw.testvideo_with_2_subtitles);
+ try {
+ loadResource(R.raw.testvideo_with_2_subtitles);
+ } catch (UnsupportedCodecException e) {
+ Log.i(LOG_TAG, "SKIPPING testChangeSubtitleTrack(). Could not find codec.");
+ return;
+ }
mMediaPlayer.setDisplay(getActivity().getSurfaceHolder());
mMediaPlayer.setScreenOnWhilePlaying(true);
@@ -1170,7 +1199,12 @@
}
public void testGetTrackInfo() throws Throwable {
- loadResource(R.raw.testvideo_with_2_subtitles);
+ try {
+ loadResource(R.raw.testvideo_with_2_subtitles);
+ } catch (UnsupportedCodecException e) {
+ Log.i(LOG_TAG, "SKIPPING testGetTrackInfo(). Could not find codec.");
+ return;
+ }
runTestOnUiThread(new Runnable() {
public void run() {
try {
@@ -1245,7 +1279,13 @@
public void testCallback() throws Throwable {
final int mp4Duration = 8484;
- loadResource(R.raw.testvideo);
+ try {
+ loadResource(R.raw.testvideo);
+ } catch (UnsupportedCodecException e) {
+ Log.i(LOG_TAG, "SKIPPING testCallback(). Could not find codec.");
+ return;
+ }
+
mMediaPlayer.setDisplay(getActivity().getSurfaceHolder());
mMediaPlayer.setScreenOnWhilePlaying(true);
@@ -1317,6 +1357,11 @@
public void testRecordAndPlay() throws Exception {
if (!hasMicrophone()) {
+ Log.i(LOG_TAG, "SKIPPING testRecordAndPlay(). No microphone.");
+ return;
+ }
+ if (!hasH263(false)) {
+ Log.i(LOG_TAG, "SKIPPING testRecordAndPlay(). Cound not find codec.");
return;
}
File outputFile = new File(Environment.getExternalStorageDirectory(),
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTestBase.java b/tests/tests/media/src/android/media/cts/MediaPlayerTestBase.java
index 61d8792..9225203 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTestBase.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTestBase.java
@@ -16,8 +16,15 @@
package android.media.cts;
import android.content.Context;
+
+import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
import android.media.MediaPlayer;
import android.test.ActivityInstrumentationTestCase2;
@@ -143,6 +150,10 @@
}
protected void loadResource(int resid) throws Exception {
+ if (!supportsPlayback(resid)) {
+ throw new UnsupportedCodecException();
+ }
+
AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
try {
mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
@@ -197,6 +208,12 @@
}
protected void playVideoTest(int resid, int width, int height) throws Exception {
+ if (!supportsPlayback(resid)) {
+ LOG.info("SKIPPING playVideoTest() for resid=" + resid
+ + " Could not find a codec for playback.");
+ return;
+ }
+
loadResource(resid);
playLoadedVideo(width, height, 0);
}
@@ -278,4 +295,69 @@
}
private static class PrepareFailedException extends Exception {}
+ public static class UnsupportedCodecException extends Exception {}
+
+ public boolean supportsPlayback(int resid) throws IOException {
+ // First determine if the device supports playback of the requested resource.
+ AssetFileDescriptor fd = mResources.openRawResourceFd(resid);
+ MediaExtractor ex = new MediaExtractor();
+ ex.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
+ MediaFormat format = ex.getTrackFormat(0);
+ String mimeType = format.getString(MediaFormat.KEY_MIME);
+ return hasCodecForMimeType(mimeType, false);
+ }
+
+ public boolean supportsPlayback(String path) throws IOException {
+ MediaExtractor ex = new MediaExtractor();
+ ex.setDataSource(path);
+ MediaFormat format = ex.getTrackFormat(0);
+ String mimeType = format.getString(MediaFormat.KEY_MIME);
+ return hasCodecForMimeType(mimeType, false);
+ }
+
+ public static boolean hasCodecForMimeType(String mimeType, boolean encoder) {
+ MediaCodecList list = new MediaCodecList(MediaCodecList.ALL_CODECS);
+ for (MediaCodecInfo info : list.getCodecInfos()) {
+ if (encoder != info.isEncoder()) {
+ continue;
+ }
+
+ for (String type : info.getSupportedTypes()) {
+ if (type.equalsIgnoreCase(mimeType)) {
+ LOG.info("Found codec for mimeType=" + mimeType + " codec=" + info.getName());
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public static boolean hasH264(boolean encoder) {
+ return hasCodecForMimeType("video/avc", encoder);
+ }
+
+ public static boolean hasHEVC(boolean encoder) {
+ return hasCodecForMimeType("video/hevc", encoder);
+ }
+
+ public static boolean hasH263(boolean encoder) {
+ return hasCodecForMimeType("video/3gpp", encoder);
+ }
+
+ public static boolean hasMpeg4(boolean encoder) {
+ return hasCodecForMimeType("video/mp4v-es", encoder);
+ }
+
+ public static boolean hasVP8(boolean encoder) {
+ return hasCodecForMimeType("video/x-vnd.on2.vp8", encoder);
+ }
+
+ public static boolean hasVP9(boolean encoder) {
+ return hasCodecForMimeType("video/x-vnd.on2.vp9", encoder);
+ }
+
+ public boolean hasAudioOutput() {
+ return getInstrumentation().getTargetContext().getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT);
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/NativeDecoderTest.java b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
index fc27dfa..76620c1 100644
--- a/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
@@ -195,10 +195,14 @@
testDecoder(R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_44100hz);
testDecoder(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz);
testDecoder(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz);
-
}
private void testDecoder(int res) throws Exception {
+ if (!supportsPlayback(res)) {
+ Log.i(TAG, "SKIPPING testDecoder() resid=" + res + " Unsupported decorder.");
+ return;
+ }
+
AssetFileDescriptor fd = mResources.openRawResourceFd(res);
int[] jdata = getDecodedData(
@@ -382,6 +386,11 @@
}
private void testVideoPlayback(int res) throws Exception {
+ if (!supportsPlayback(res)) {
+ Log.i(TAG, "SKIPPING testVideoPlayback() resid=" + res + " Unsupported decorder.");
+ return;
+ }
+
AssetFileDescriptor fd = mResources.openRawResourceFd(res);
boolean ret = testPlaybackNative(mActivity.getSurfaceHolder().getSurface(),
diff --git a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
index 2b93064..6198d5f 100644
--- a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
@@ -28,6 +28,8 @@
* Tests of MediaPlayer streaming capabilities.
*/
public class StreamingMediaPlayerTest extends MediaPlayerTestBase {
+ private static final String TAG = "StreamingMediaPlayerTest";
+
private CtsTestServer mServer;
/* RTSP tests are more flaky and vulnerable to network condition.
@@ -62,6 +64,11 @@
*/
// Streaming HTTP video from YouTube
public void testHTTP_H263_AMR_Video1() throws Exception {
+ if (!hasH263(false)) {
+ Log.i(TAG, "Skipping testHTTP_H263_AMR_Video1(): No codec found.");
+ return;
+ }
+
playVideoTest("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+ "&itag=13&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
+ "&sparams=ip,ipbits,expire,id,itag,source"
@@ -70,6 +77,11 @@
+ "&key=test_key1&user=android-device-test", 176, 144);
}
public void testHTTP_H263_AMR_Video2() throws Exception {
+ if (!hasH263(false)) {
+ Log.i(TAG, "Skipping testHTTP_H263_AMR_Video2(): No codec found.");
+ return;
+ }
+
playVideoTest("http://redirector.c.youtube.com/videoplayback?id=c80658495af60617"
+ "&itag=13&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
+ "&sparams=ip,ipbits,expire,id,itag,source"
@@ -79,6 +91,11 @@
}
public void testHTTP_MPEG4SP_AAC_Video1() throws Exception {
+ if (!hasH264(false)) {
+ Log.i(TAG, "Skipping testHTTP_MPEG4SP_AAC_Video1(): No codec found.");
+ return;
+ }
+
playVideoTest("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+ "&itag=17&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
+ "&sparams=ip,ipbits,expire,id,itag,source"
@@ -87,6 +104,11 @@
+ "&key=test_key1&user=android-device-test", 176, 144);
}
public void testHTTP_MPEG4SP_AAC_Video2() throws Exception {
+ if (!hasH264(false)) {
+ Log.i(TAG, "Skipping testHTTP_MPEG4SP_AAC_Video2(): No codec found.");
+ return;
+ }
+
playVideoTest("http://redirector.c.youtube.com/videoplayback?id=c80658495af60617"
+ "&itag=17&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
+ "&sparams=ip,ipbits,expire,id,itag,source"
@@ -96,6 +118,11 @@
}
public void testHTTP_H264Base_AAC_Video1() throws Exception {
+ if (!hasH264(false)) {
+ Log.i(TAG, "Skipping testHTTP_H264Base_AAC_Video1(): No codec found.");
+ return;
+ }
+
playVideoTest("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+ "&itag=18&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
+ "&sparams=ip,ipbits,expire,id,itag,source"
@@ -104,6 +131,11 @@
+ "&key=test_key1&user=android-device-test", 640, 360);
}
public void testHTTP_H264Base_AAC_Video2() throws Exception {
+ if (!hasH264(false)) {
+ Log.i(TAG, "Skipping testHTTP_H264Base_AAC_Video2(): No codec found.");
+ return;
+ }
+
playVideoTest("http://redirector.c.youtube.com/videoplayback?id=c80658495af60617"
+ "&itag=18&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
+ "&sparams=ip,ipbits,expire,id,itag,source"
@@ -114,6 +146,11 @@
// Streaming HLS video from YouTube
public void testHLS() throws Exception {
+ if (!hasH264(false)) {
+ Log.i(TAG, "Skipping testHLS(): No codec found.");
+ return;
+ }
+
// Play stream for 60 seconds
playLiveVideoTest("http://www.youtube.com/api/manifest/hls_variant/id/"
+ "0168724d02bd9945/itag/5/source/youtube/playlist_type/DVR/ip/"
@@ -165,6 +202,11 @@
stream_url = stream_url + "?" + CtsTestServer.NOLENGTH_POSTFIX;
}
+ if (!supportsPlayback(stream_url)) {
+ Log.i(TAG, "Failed to find codec for: '" + stream_url + "'. Skipping test.");
+ return;
+ }
+
mMediaPlayer.setDataSource(stream_url);
mMediaPlayer.setDisplay(getActivity().getSurfaceHolder());
@@ -252,14 +294,26 @@
}
public void testPlayHlsStream() throws Throwable {
+ if (!hasH264(false)) {
+ Log.i(TAG, "Skipping testPlayHlsStream(): No codec found.");
+ return;
+ }
localHlsTest("hls.m3u8", false, false);
}
public void testPlayHlsStreamWithQueryString() throws Throwable {
+ if (!hasH264(false)) {
+ Log.i(TAG, "Skipping testPlayHlsStreamWithQueryString(): No codec found.");
+ return;
+ }
localHlsTest("hls.m3u8", true, false);
}
public void testPlayHlsStreamWithRedirect() throws Throwable {
+ if (!hasH264(false)) {
+ Log.i(TAG, "Skipping testPlayHlsStreamWithRedirect(): No codec found.");
+ return;
+ }
localHlsTest("hls.m3u8", false, true);
}
diff --git a/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechTest.java b/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechTest.java
index 799fd8d..69acdd0 100644
--- a/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechTest.java
+++ b/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechTest.java
@@ -15,6 +15,7 @@
*/
package android.speech.tts.cts;
+import android.content.pm.PackageManager;
import android.os.Environment;
import android.speech.tts.TextToSpeech;
import android.test.AndroidTestCase;
@@ -39,6 +40,15 @@
protected void setUp() throws Exception {
super.setUp();
mTts = TextToSpeechWrapper.createTextToSpeechWrapper(getContext());
+ if (mTts == null) {
+ PackageManager pm = getContext().getPackageManager();
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+ // It is OK to have no TTS, when audio-out is not supported.
+ return;
+ } else {
+ fail("FEATURE_AUDIO_OUTPUT is set, but there is no TTS engine");
+ }
+ }
assertNotNull(mTts);
assertTrue(checkAndSetLanguageAvailable());
}
@@ -46,7 +56,9 @@
@Override
protected void tearDown() throws Exception {
super.tearDown();
- mTts.shutdown();
+ if (mTts != null) {
+ mTts.shutdown();
+ }
}
private TextToSpeech getTts() {
@@ -83,6 +95,9 @@
}
public void testSynthesizeToFile() throws Exception {
+ if (mTts == null) {
+ return;
+ }
File sampleFile = new File(Environment.getExternalStorageDirectory(), SAMPLE_FILE_NAME);
try {
assertFalse(sampleFile.exists());
@@ -101,18 +116,27 @@
}
public void testSpeak() throws Exception {
+ if (mTts == null) {
+ return;
+ }
int result = getTts().speak(SAMPLE_TEXT, TextToSpeech.QUEUE_FLUSH, createParams());
assertEquals("speak() failed", TextToSpeech.SUCCESS, result);
assertTrue("speak() completion timeout", waitForUtterance());
}
public void testGetEnginesIncludesDefault() throws Exception {
+ if (mTts == null) {
+ return;
+ }
List<TextToSpeech.EngineInfo> engines = getTts().getEngines();
assertNotNull("getEngines() returned null", engines);
assertContainsEngine(getTts().getDefaultEngine(), engines);
}
public void testGetEnginesIncludesMock() throws Exception {
+ if (mTts == null) {
+ return;
+ }
List<TextToSpeech.EngineInfo> engines = getTts().getEngines();
assertNotNull("getEngines() returned null", engines);
assertContainsEngine(TextToSpeechWrapper.MOCK_TTS_ENGINE, engines);