Merge "audio policy: fix sensor privacy listener"
diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp
index 936943c..4995dc4 100644
--- a/camera/ndk/impl/ACameraMetadata.cpp
+++ b/camera/ndk/impl/ACameraMetadata.cpp
@@ -587,6 +587,7 @@
ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
ANDROID_CONTROL_AE_PRECAPTURE_ID,
ANDROID_CONTROL_AF_TRIGGER_ID,
+ ANDROID_CONTROL_SETTINGS_OVERRIDING_FRAME_NUMBER,
ANDROID_DEMOSAIC_MODE,
ANDROID_EDGE_STRENGTH,
ANDROID_FLASH_FIRING_POWER,
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 587c431..c0b0052 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -2169,7 +2169,7 @@
* @see ACAMERA_SCALER_CROP_REGION
*/
ACAMERA_CONTROL_AUTOFRAMING = // byte (acamera_metadata_enum_android_control_autoframing_t)
- ACAMERA_CONTROL_START + 51,
+ ACAMERA_CONTROL_START + 52,
/**
* <p>Whether the camera device supports ACAMERA_CONTROL_AUTOFRAMING.</p>
*
@@ -2185,7 +2185,7 @@
* <p>Will be <code>false</code> if auto-framing is not available.</p>
*/
ACAMERA_CONTROL_AUTOFRAMING_AVAILABLE = // byte (acamera_metadata_enum_android_control_autoframing_available_t)
- ACAMERA_CONTROL_START + 52,
+ ACAMERA_CONTROL_START + 53,
/**
* <p>Current state of auto-framing.</p>
*
@@ -2212,7 +2212,7 @@
* @see ACAMERA_CONTROL_AUTOFRAMING_AVAILABLE
*/
ACAMERA_CONTROL_AUTOFRAMING_STATE = // byte (acamera_metadata_enum_android_control_autoframing_state_t)
- ACAMERA_CONTROL_START + 53,
+ ACAMERA_CONTROL_START + 54,
ACAMERA_CONTROL_END,
/**
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index 6d3c348..386e42c 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -125,6 +125,7 @@
AMediaFormat* AMediaExtractor_getFileFormat(AMediaExtractor *mData) {
sp<AMessage> format;
mData->mImpl->getFileFormat(&format);
+ // ignore any error, we want to return the empty format
return AMediaFormat_fromMsg(&format);
}
@@ -247,7 +248,10 @@
}
sp<AMessage> format;
- ex->mImpl->getFileFormat(&format);
+ if (ex->mImpl->getFileFormat(&format) != OK) {
+ android_errorWriteWithInfoLog(0x534e4554, "243222985", -1, nullptr, 0);
+ return NULL;
+ }
sp<ABuffer> buffer;
if(!format->findBuffer("pssh", &buffer)) {
return NULL;
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index a95e874..c0de4e4 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -332,6 +332,9 @@
if (name == nullptr) {
return;
}
+ if (value == nullptr) {
+ return;
+ }
// AMessage::setString() makes a copy of the string
format->mFormat->setString(name, value, strlen(value));
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/Android.bp b/media/tests/benchmark/MediaBenchmarkTest/Android.bp
index 4b44dcf..d41a7f9 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/Android.bp
+++ b/media/tests/benchmark/MediaBenchmarkTest/Android.bp
@@ -69,6 +69,6 @@
java_defaults {
name: "MediaBenchmark-defaults",
- min_sdk_version: "28",
+ min_sdk_version: "29",
target_sdk_version: "30",
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/AndroidManifest.xml b/media/tests/benchmark/MediaBenchmarkTest/AndroidManifest.xml
index eea9914..772a29b 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/AndroidManifest.xml
+++ b/media/tests/benchmark/MediaBenchmarkTest/AndroidManifest.xml
@@ -20,8 +20,6 @@
package="com.android.media.benchmark">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<application
tools:ignore="AllowBackup,GoogleAppIndexingWarning,MissingApplicationIcon"
@@ -31,4 +29,4 @@
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.media.benchmark"
android:label="Benchmark Media Test"/>
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/media/tests/benchmark/MediaBenchmarkTest/build.gradle b/media/tests/benchmark/MediaBenchmarkTest/build.gradle
index b222d47..a2af701 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/build.gradle
+++ b/media/tests/benchmark/MediaBenchmarkTest/build.gradle
@@ -30,7 +30,7 @@
compileSdkVersion 30
defaultConfig {
applicationId "com.android.media.benchmark"
- minSdkVersion 28
+ minSdkVersion 29
targetSdkVersion 30
versionCode 1
versionName "1.0"
@@ -73,4 +73,4 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
-}
\ No newline at end of file
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
index 08035c9..1e10b37 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
@@ -2,13 +2,12 @@
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
+import android.media.MediaFormat;
import android.os.Build;
-
import java.util.ArrayList;
public class CodecUtils {
private CodecUtils() {}
-
/**
* Queries the MediaCodecList and returns codec names of supported codecs.
*
@@ -36,4 +35,46 @@
}
return supportedCodecs;
}
+ /**
+ * Returns a decoder that supports the given MediaFormat along with the "features".
+ *
+ * @param format MediaFormat that the codec should support
+ * @param isSoftware Specifies if this is a software / hardware decoder
+ * @param isEncoder Specifies if the request is for encoders or not.
+ * @param features is the feature that should be supported.
+ * @return name of the codec.
+ */
+ public static String getMediaCodec(MediaFormat format, boolean isSoftware,
+ String[] features, boolean isEncoder) {
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
+ MediaCodecInfo[] codecInfos = mcl.getCodecInfos();
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ for (MediaCodecInfo codecInfo : codecInfos) {
+ if (codecInfo.isEncoder() != isEncoder) continue;
+ if (isSoftware != codecInfo.isSoftwareOnly()) continue;
+ String[] types = codecInfo.getSupportedTypes();
+ for (String type : types) {
+ if (type.equalsIgnoreCase(mime)) {
+ boolean isOk = true;
+ MediaCodecInfo.CodecCapabilities codecCapabilities =
+ codecInfo.getCapabilitiesForType(type);
+ if (!codecCapabilities.isFormatSupported(format)) {
+ isOk = false;
+ }
+ if (features != null) {
+ for (String feature : features) {
+ if (!codecCapabilities.isFeatureSupported(feature)) {
+ isOk = false;
+ break;
+ }
+ }
+ }
+ if (isOk) {
+ return codecInfo.getName();
+ }
+ }
+ }
+ }
+ return null;
+ }
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
index c2ed82c..a4ba36a 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
@@ -16,6 +16,8 @@
package com.android.media.benchmark.library;
+import android.view.Surface;
+
import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaFormat;
@@ -28,13 +30,17 @@
import java.nio.ByteBuffer;
import java.util.ArrayList;
-public class Decoder {
+import com.android.media.benchmark.library.IBufferXfer;
+
+public class Decoder implements IBufferXfer.IReceiveBuffer {
private static final String TAG = "Decoder";
private static final boolean DEBUG = false;
private static final int kQueueDequeueTimeoutUs = 1000;
private final Object mLock = new Object();
private MediaCodec mCodec;
+ private Surface mSurface = null;
+ private boolean mRender = false;
private ArrayList<BufferInfo> mInputBufferInfo;
private Stats mStats;
@@ -47,9 +53,29 @@
private ArrayList<ByteBuffer> mInputBuffer;
private FileOutputStream mOutputStream;
+ private FrameReleaseQueue mFrameReleaseQueue = null;
+ private IBufferXfer.ISendBuffer mIBufferSend = null;
+ /* success for decoder */
+ public static final int DECODE_SUCCESS = 0;
+ /* some error happened during decoding */
+ public static final int DECODE_DECODER_ERROR = -1;
+ /* error while creating a decoder */
+ public static final int DECODE_CREATE_ERROR = -2;
public Decoder() { mStats = new Stats(); }
public Stats getStats() { return mStats; };
+ @Override
+ public boolean receiveBuffer(IBufferXfer.BufferXferInfo info) {
+ MediaCodec codec = (MediaCodec)info.obj;
+ codec.releaseOutputBuffer(info.idx, mRender);
+ return true;
+ }
+ @Override
+ public boolean connect(IBufferXfer.ISendBuffer receiver) {
+ Log.d(TAG,"Setting interface of the sender");
+ mIBufferSend = receiver;
+ return true;
+ }
/**
* Setup of decoder
*
@@ -59,6 +85,17 @@
mSignalledError = false;
mOutputStream = outputStream;
}
+ public void setupDecoder(Surface surface, boolean render,
+ boolean useFrameReleaseQueue, int frameRate) {
+ mSignalledError = false;
+ mOutputStream = null;
+ mSurface = surface;
+ mRender = render;
+ if (useFrameReleaseQueue) {
+ Log.i(TAG, "Using FrameReleaseQueue with frameRate " + frameRate);
+ mFrameReleaseQueue = new FrameReleaseQueue(mRender, frameRate);
+ }
+ }
private MediaCodec createCodec(String codecName, MediaFormat format) throws IOException {
String mime = format.getString(MediaFormat.KEY_MIME);
@@ -95,7 +132,8 @@
* @param asyncMode Will run on async implementation if true
* @param format For creating the decoder if codec name is empty and configuring it
* @param codecName Will create the decoder with codecName
- * @return 0 if decode was successful , -1 for fail, -2 for decoder not created
+ * @return DECODE_SUCCESS if decode was successful, DECODE_DECODER_ERROR for fail,
+ * DECODE_CREATE_ERROR for decoder not created
* @throws IOException if the codec cannot be created.
*/
public int decode(@NonNull ArrayList<ByteBuffer> inputBuffer,
@@ -112,7 +150,10 @@
long sTime = mStats.getCurTime();
mCodec = createCodec(codecName, format);
if (mCodec == null) {
- return -2;
+ return DECODE_CREATE_ERROR;
+ }
+ if (mFrameReleaseQueue != null) {
+ mFrameReleaseQueue.setMediaCodec(mCodec);
}
if (asyncMode) {
mCodec.setCallback(new MediaCodec.Callback() {
@@ -158,7 +199,7 @@
if (DEBUG) {
Log.d(TAG, "Media Format : " + format.toString());
}
- mCodec.configure(format, null, null, isEncoder);
+ mCodec.configure(format, mSurface, null, isEncoder);
mCodec.start();
Log.i(TAG, "Codec started ");
long eTime = mStats.getCurTime();
@@ -168,7 +209,7 @@
try {
synchronized (mLock) { mLock.wait(); }
if (mSignalledError) {
- return -1;
+ return DECODE_DECODER_ERROR;
}
} catch (InterruptedException e) {
e.printStackTrace();
@@ -201,7 +242,7 @@
Log.e(TAG,
"MediaCodec.dequeueOutputBuffer"
+ " returned invalid index " + outputBufferId);
- return -1;
+ return DECODE_DECODER_ERROR;
}
} else {
mStats.addOutputTime();
@@ -212,9 +253,13 @@
}
}
}
+ if (mFrameReleaseQueue != null) {
+ Log.i(TAG, "Ending FrameReleaseQueue");
+ mFrameReleaseQueue.stopFrameRelease();
+ }
mInputBuffer.clear();
mInputBufferInfo.clear();
- return 0;
+ return DECODE_SUCCESS;
}
/**
@@ -290,7 +335,9 @@
if (DEBUG) {
Log.d(TAG,
"In OutputBufferAvailable ,"
- + " output frame number = " + mNumOutputFrame);
+ + " output frame number = " + mNumOutputFrame
+ + " timestamp = " + outputBufferInfo.presentationTimeUs
+ + " size = " + outputBufferInfo.size);
}
if (mOutputStream != null) {
try {
@@ -303,7 +350,21 @@
Log.d(TAG, "Error Dumping File: Exception " + e.toString());
}
}
- mediaCodec.releaseOutputBuffer(outputBufferId, false);
+ if (mFrameReleaseQueue != null) {
+ mFrameReleaseQueue.pushFrame(mNumOutputFrame, outputBufferId,
+ outputBufferInfo.presentationTimeUs);
+ } else if (mIBufferSend != null) {
+ IBufferXfer.BufferXferInfo info = new IBufferXfer.BufferXferInfo();
+ info.buf = mediaCodec.getOutputBuffer(outputBufferId);
+ info.idx = outputBufferId;
+ info.obj = mediaCodec;
+ info.bytesRead = outputBufferInfo.size;
+ info.presentationTimeUs = outputBufferInfo.presentationTimeUs;
+ info.flag = outputBufferInfo.flags;
+ mIBufferSend.sendBuffer(this, info);
+ } else {
+ mediaCodec.releaseOutputBuffer(outputBufferId, mRender);
+ }
mSawOutputEOS = (outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
if (mSawOutputEOS) {
Log.i(TAG, "Saw output EOS");
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
index 754cd8e..63d17ee 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
@@ -19,6 +19,7 @@
import android.media.MediaCodec;
import android.media.MediaCodec.CodecException;
import android.media.MediaFormat;
+import android.view.Surface;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -28,34 +29,43 @@
import java.io.IOException;
import java.nio.ByteBuffer;
-public class Encoder {
+public class Encoder implements IBufferXfer.IReceiveBuffer {
// Change in AUDIO_ENCODE_DEFAULT_MAX_INPUT_SIZE should also be taken to
// kDefaultAudioEncodeFrameSize present in BenchmarkCommon.h
private static final int AUDIO_ENCODE_DEFAULT_MAX_INPUT_SIZE = 4096;
private static final String TAG = "Encoder";
private static final boolean DEBUG = false;
private static final int kQueueDequeueTimeoutUs = 1000;
-
private final Object mLock = new Object();
- private MediaCodec mCodec;
+ private MediaCodec mCodec = null;
private String mMime;
private Stats mStats;
private int mOffset;
private int mFrameSize;
private int mNumInputFrame;
- private int mNumFrames;
+ private int mNumFrames = 0;
private int mFrameRate;
private int mSampleRate;
private long mInputBufferSize;
+ private int mMinOutputBuffers = 0;
+ private int mNumOutputBuffers = 0;
+ private boolean mUseSurface = false;
+
private boolean mSawInputEOS;
private boolean mSawOutputEOS;
private boolean mSignalledError;
- private FileInputStream mInputStream;
- private FileOutputStream mOutputStream;
-
+ private FileInputStream mInputStream = null;
+ private FileOutputStream mOutputStream = null;
+ private IBufferXfer.ISendBuffer mIBufferSend = null;
+ /* success for encoder */
+ public static final int ENCODE_SUCCESS = 0;
+ /* some error happened during encoding */
+ public static final int ENCODE_ENCODER_ERROR = -1;
+ /* error while creating an encoder */
+ public static final int ENCODE_CREATE_ERROR = -2;
public Encoder() {
mStats = new Stats();
mNumInputFrame = 0;
@@ -63,6 +73,25 @@
mSawOutputEOS = false;
mSignalledError = false;
}
+ @Override
+ public boolean receiveBuffer(IBufferXfer.BufferXferInfo info) {
+ if (DEBUG) {
+ Log.d(TAG,"Encoder Getting buffers from external: "
+ + " Bytes Read: " + info.bytesRead
+ + " PresentationUs " + info.presentationTimeUs
+ + " flags: " + info.flag);
+ }
+ MediaCodec codec = (MediaCodec)info.obj;
+ codec.queueInputBuffer(info.idx, 0, info.bytesRead,
+ info.presentationTimeUs, info.flag);
+ return true;
+ }
+ @Override
+ public boolean connect(IBufferXfer.ISendBuffer receiver) {
+ mIBufferSend = receiver;
+ return true;
+ }
+ public Stats getStats() { return mStats; };
/**
* Setup of encoder
@@ -75,6 +104,17 @@
this.mInputStream = fileInputStream;
this.mOutputStream = encoderOutputStream;
}
+ /**
+ * Setup of encoder
+ *
+ * @param useSurface, indicates that application is using surface for input
+ * @param numOutputBuffers indicate the minimum buffers to signal Output
+ * end of stream
+ */
+ public void setupEncoder(boolean useSurface, int numOutputBuffers) {
+ this.mUseSurface = useSurface;
+ this.mMinOutputBuffers = numOutputBuffers;
+ }
private MediaCodec createCodec(String codecName, String mime) throws IOException {
try {
@@ -100,7 +140,52 @@
return null;
}
}
+ /**
+ * Creates and configures the encoder with the given name, format and mime.
+ * provided a valid list of parameters are passed as inputs. This is needed
+ * to first configure the codec and then may be get surface etc and then
+ * use for encode.
+ *
+ * @param codecName Will create the encoder with codecName
+ * @param encodeFormat Format of the output data
+ * @param mime For creating encode format
+ * @return ENCODE_SUCCESS if encode was successful,
+ * ENCODE_CREATE_ERROR for encoder not created
+ * @throws IOException If the codec cannot be created.
+ */
+ public int createAndConfigure(String codecName, MediaFormat encodeFormat,
+ String mime) throws IOException {
+ if (mCodec == null) {
+ mMime = mime;
+ mCodec = createCodec(codecName, mime);
+ if (mCodec == null) {
+ return ENCODE_CREATE_ERROR;
+ }
+ /*Configure Codec*/
+ try {
+ mCodec.configure(encodeFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+ } catch(IllegalArgumentException
+ | IllegalStateException
+ | MediaCodec.CryptoException e) {
+ Log.e(TAG, "Failed to configure " + mCodec.getName() + " encoder.");
+ e.printStackTrace();
+ return ENCODE_CREATE_ERROR;
+ }
+ }
+ return ENCODE_SUCCESS;
+ }
+ /**
+ * Requests the surface to use as input to the encoder
+ * @return a valid surface or null if not called after configure.
+ */
+ public Surface getInputSurface() {
+ Surface inputSurface = null;
+ if (mCodec != null) {
+ inputSurface = mCodec.createInputSurface();
+ }
+ return inputSurface;
+ }
/**
* Encodes the given raw input file and measures the performance of encode operation,
* provided a valid list of parameters are passed as inputs.
@@ -110,43 +195,39 @@
* @param encodeFormat Format of the output data
* @param frameSize Size of the frame
* @param asyncMode Will run on async implementation if true
- * @return 0 if encode was successful , -1 for fail, -2 for encoder not created
+ * @return ENCODE_SUCCESS if encode was successful ,ENCODE_ENCODER_ERROR for fail,
+ * ENCODE_CREATE_ERROR for encoder not created
* @throws IOException If the codec cannot be created.
*/
public int encode(String codecName, MediaFormat encodeFormat, String mime, int frameRate,
int sampleRate, int frameSize, boolean asyncMode) throws IOException {
- mInputBufferSize = mInputStream.getChannel().size();
- mMime = mime;
+ mInputBufferSize = (mInputStream != null) ? mInputStream.getChannel().size() : 0;
mOffset = 0;
mFrameRate = frameRate;
mSampleRate = sampleRate;
long sTime = mStats.getCurTime();
- mCodec = createCodec(codecName, mime);
if (mCodec == null) {
- return -2;
- }
- /*Configure Codec*/
- try {
- mCodec.configure(encodeFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
- } catch (IllegalArgumentException | IllegalStateException | MediaCodec.CryptoException e) {
- Log.e(TAG, "Failed to configure " + mCodec.getName() + " encoder.");
- e.printStackTrace();
- return -2;
- }
- if (mMime.startsWith("video/")) {
- mFrameSize = frameSize;
- } else {
- int maxInputSize = AUDIO_ENCODE_DEFAULT_MAX_INPUT_SIZE;
- MediaFormat format = mCodec.getInputFormat();
- if (format.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
- maxInputSize = format.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
- }
- mFrameSize = frameSize;
- if (mFrameSize > maxInputSize && maxInputSize > 0) {
- mFrameSize = maxInputSize;
+ int status = createAndConfigure(codecName, encodeFormat, mime);
+ if(status != ENCODE_SUCCESS) {
+ return status;
}
}
- mNumFrames = (int) ((mInputBufferSize + mFrameSize - 1) / mFrameSize);
+ if (!mUseSurface) {
+ if (mMime.startsWith("video/")) {
+ mFrameSize = frameSize;
+ } else {
+ int maxInputSize = AUDIO_ENCODE_DEFAULT_MAX_INPUT_SIZE;
+ MediaFormat format = mCodec.getInputFormat();
+ if (format.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
+ maxInputSize = format.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
+ }
+ mFrameSize = frameSize;
+ if (mFrameSize > maxInputSize && maxInputSize > 0) {
+ mFrameSize = maxInputSize;
+ }
+ }
+ mNumFrames = (int) ((mInputBufferSize + mFrameSize - 1) / mFrameSize);
+ }
if (asyncMode) {
mCodec.setCallback(new MediaCodec.Callback() {
@Override
@@ -196,7 +277,7 @@
try {
synchronized (mLock) { mLock.wait(); }
if (mSignalledError) {
- return -1;
+ return ENCODE_ENCODER_ERROR;
}
} catch (InterruptedException e) {
e.printStackTrace();
@@ -204,12 +285,12 @@
} else {
while (!mSawOutputEOS && !mSignalledError) {
/* Queue input data */
- if (!mSawInputEOS) {
+ if (!mSawInputEOS && !mUseSurface) {
int inputBufferId = mCodec.dequeueInputBuffer(kQueueDequeueTimeoutUs);
if (inputBufferId < 0 && inputBufferId != MediaCodec.INFO_TRY_AGAIN_LATER) {
Log.e(TAG, "MediaCodec.dequeueInputBuffer " + "returned invalid index : " +
inputBufferId);
- return -1;
+ return ENCODE_ENCODER_ERROR;
}
mStats.addInputTime();
onInputAvailable(mCodec, inputBufferId);
@@ -225,7 +306,7 @@
} else if (outputBufferId != MediaCodec.INFO_TRY_AGAIN_LATER) {
Log.e(TAG, "MediaCodec.dequeueOutputBuffer" + " returned invalid index " +
outputBufferId);
- return -1;
+ return ENCODE_ENCODER_ERROR;
}
} else {
mStats.addOutputTime();
@@ -236,7 +317,7 @@
}
}
}
- return 0;
+ return ENCODE_SUCCESS;
}
private void onOutputAvailable(MediaCodec mediaCodec, int outputBufferId,
@@ -260,13 +341,25 @@
return;
}
}
+ mNumOutputBuffers++;
+ if (DEBUG) {
+ Log.d(TAG,
+ "In OutputBufferAvailable ,"
+ + " timestamp = " + outputBufferInfo.presentationTimeUs
+ + " size = " + outputBufferInfo.size
+ + " flags = " + outputBufferInfo.flags);
+ }
+
mStats.addFrameSize(outputBuffer.remaining());
mediaCodec.releaseOutputBuffer(outputBufferId, false);
mSawOutputEOS = (outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+ if (mUseSurface && !mSawOutputEOS) {
+ mSawOutputEOS = (mNumOutputBuffers >= mMinOutputBuffers) ? true : false;
+ }
}
private void onInputAvailable(MediaCodec mediaCodec, int inputBufferId) throws IOException {
- if (mSawInputEOS || inputBufferId < 0) {
+ if (mSawInputEOS || inputBufferId < 0 || this.mUseSurface) {
if (mSawInputEOS) {
Log.i(TAG, "Saw input EOS");
}
@@ -282,6 +375,14 @@
mSignalledError = true;
return;
}
+ if (mIBufferSend != null) {
+ IBufferXfer.BufferXferInfo info = new IBufferXfer.BufferXferInfo();
+ info.buf = inputBuffer;
+ info.idx = inputBufferId;
+ info.obj = mediaCodec;
+ mIBufferSend.sendBuffer(this, info);
+ return;
+ }
int bufSize = inputBuffer.capacity();
int bytesToRead = mFrameSize;
if (mInputBufferSize - mOffset < mFrameSize) {
@@ -356,9 +457,11 @@
mOffset = 0;
mInputBufferSize = 0;
mNumInputFrame = 0;
+ mMinOutputBuffers = 0;
mSawInputEOS = false;
mSawOutputEOS = false;
mSignalledError = false;
+ mUseSurface = false;
mStats.reset();
}
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java
new file mode 100644
index 0000000..4b9b505
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2022 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 com.android.media.benchmark.library;
+
+import android.media.MediaCodec;
+import android.util.Log;
+import androidx.annotation.NonNull;
+import java.nio.ByteBuffer;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public class FrameReleaseQueue {
+ private static final String TAG = "FrameReleaseQueue";
+
+ private MediaCodec mCodec;
+ private LinkedBlockingQueue<FrameInfo> mFrameInfoQueue;
+ private ReleaseThread mReleaseThread;
+ private boolean doFrameRelease = false;
+ private boolean mRender = false;
+ private int mWaitTime = 40; // milliseconds per frame
+ private int firstReleaseTime = -1;
+ private int THRESHOLD_TIME = 5;
+
+ private static class FrameInfo {
+ private int number;
+ private int bufferId;
+ private int displayTime;
+ public FrameInfo(int frameNumber, int frameBufferId, int frameDisplayTime) {
+ this.number = frameNumber;
+ this.bufferId = frameBufferId;
+ this.displayTime = frameDisplayTime;
+ }
+ }
+
+ private class ReleaseThread extends Thread {
+ public void run() {
+ int nextReleaseTime = 0;
+ while (doFrameRelease || mFrameInfoQueue.size() > 0) {
+ FrameInfo curFrameInfo = mFrameInfoQueue.peek();
+ if (curFrameInfo == null) {
+ nextReleaseTime += mWaitTime;
+ } else {
+ if (firstReleaseTime == -1) {
+ firstReleaseTime = getCurSysTime();
+ nextReleaseTime = firstReleaseTime + mWaitTime;
+ popAndRelease(curFrameInfo);
+ } else {
+ nextReleaseTime += mWaitTime;
+ int curSysTime = getCurSysTime();
+ int curMediaTime = curSysTime - firstReleaseTime;
+ while (curFrameInfo != null && curFrameInfo.displayTime <= curMediaTime) {
+ if (!((curMediaTime - curFrameInfo.displayTime) < THRESHOLD_TIME)) {
+ Log.d(TAG, "Dropping expired frame " + curFrameInfo.number);
+ }
+ popAndRelease(curFrameInfo);
+ curFrameInfo = mFrameInfoQueue.peek();
+ }
+ if (curFrameInfo != null && curFrameInfo.displayTime > curMediaTime) {
+ if ((curFrameInfo.displayTime - curMediaTime) < THRESHOLD_TIME) {
+ popAndRelease(curFrameInfo);
+ }
+ }
+ }
+ }
+ int sleepTime = nextReleaseTime - getCurSysTime();
+ if (sleepTime > 0) {
+ try {
+ mReleaseThread.sleep(sleepTime);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Threw InterruptedException on sleep");
+ }
+ } else {
+ Log.d(TAG, "Thread sleep time less than 1");
+ }
+ }
+ }
+ }
+
+ public FrameReleaseQueue(boolean render, int frameRate) {
+ this.mFrameInfoQueue = new LinkedBlockingQueue();
+ this.mReleaseThread = new ReleaseThread();
+ this.doFrameRelease = true;
+ this.mRender = render;
+ this.mWaitTime = (int)(1.0f/frameRate * 1000); // wait time in milliseconds per frame
+ Log.i(TAG, "Constructed FrameReleaseQueue with wait time " + this.mWaitTime + " ms");
+ }
+
+ public void setMediaCodec(MediaCodec mediaCodec) {
+ this.mCodec = mediaCodec;
+ }
+
+ public boolean pushFrame(int frameNumber, int frameBufferId, long frameDisplayTime) {
+ int frameDisplayTimeMs = (int)(frameDisplayTime/1000);
+ FrameInfo curFrameInfo = new FrameInfo(frameNumber, frameBufferId, frameDisplayTimeMs);
+ boolean pushSuccess = mFrameInfoQueue.offer(curFrameInfo);
+ if (!pushSuccess) {
+ Log.e(TAG, "Failed to push frame with buffer id " + curFrameInfo.bufferId);
+ return false;
+ }
+ if (!mReleaseThread.isAlive()) {
+ mReleaseThread.start();
+ Log.i(TAG, "Started frame release thread");
+ }
+ return true;
+ }
+
+ private int getCurSysTime() {
+ return (int)(System.nanoTime()/1000000);
+ }
+
+ private void popAndRelease(FrameInfo curFrameInfo) {
+ try {
+ curFrameInfo = mFrameInfoQueue.take();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Threw InterruptedException on take");
+ }
+ try {
+ mCodec.releaseOutputBuffer(curFrameInfo.bufferId, mRender);
+ } catch (IllegalStateException e) {
+ Log.e(TAG,
+ "Threw IllegalStateException on releaseOutputBuffer for frame "
+ + curFrameInfo.number);
+ }
+ }
+
+ public void stopFrameRelease() {
+ doFrameRelease = false;
+ try {
+ mReleaseThread.join();
+ Log.i(TAG, "Joined frame release thread");
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Threw InterruptedException on thread join");
+ }
+ }
+}
+
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java
new file mode 100644
index 0000000..a75962c
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 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 com.android.media.benchmark.library;
+import android.media.MediaCodec;
+import java.nio.ByteBuffer;
+/**
+ * interfaces that can be used to implement
+ * sending of buffers to external and receive using callbacks
+ */
+public class IBufferXfer {
+ static class BufferXferInfo {
+ public ByteBuffer buf;
+ public int idx;
+ public Object obj;
+ int flag;
+ int bytesRead;
+ long presentationTimeUs;
+ }
+
+ public interface IReceiveBuffer {
+ // Implemented by sender to get buffers back
+ boolean receiveBuffer(BufferXferInfo info);
+ // Establishes a connection between the buffer sender and receiver.
+ // Implemented by the entity that sends the buffers to receiver.
+ // the receiverInterface is the interface of the receiver.
+ // The sender uses this interface to send buffers.
+ boolean connect(IBufferXfer.ISendBuffer receiverInterface);
+ }
+ // Implemented by an entity that does not own the buffers and only
+ // wants to manage the buffers. ( Usually the receiver)
+ // The receiver uses returnIface to return the buffers to sender
+ public interface ISendBuffer {
+ boolean sendBuffer(IBufferXfer.IReceiveBuffer returnIface,
+ BufferXferInfo info);
+ }
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java
new file mode 100644
index 0000000..ab55df5
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 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 com.android.media.benchmark.library;
+
+/**
+ * Class that manages the buffer senders
+*/
+import com.android.media.benchmark.library.IBufferXfer;
+import java.util.ArrayDeque;
+import android.util.Log;
+public class IBufferXferImpl implements IBufferXfer.ISendBuffer {
+
+ private static class BufferInfo {
+ public IBufferXfer.IReceiveBuffer rIface;
+ public IBufferXfer.BufferXferInfo info;
+ }
+ private final String TAG = "IBufferXferImpl";
+ private final ArrayDeque<BufferInfo> mProducerQueue = new ArrayDeque<>();
+ private final ArrayDeque<BufferInfo> mConsumerQueue = new ArrayDeque<>();
+ private IBufferXfer.IReceiveBuffer mProducer = null;
+ private IBufferXfer.IReceiveBuffer mConsumer = null;
+ private final Object mLock = new Object();
+
+ public IBufferXferImpl(IBufferXfer.IReceiveBuffer producer,
+ IBufferXfer.IReceiveBuffer consumer) {
+ mProducer = producer;
+ mConsumer = consumer;
+ // Attach this to be their receiver
+ mProducer.connect(this);
+ mConsumer.connect(this);
+ }
+ @Override
+ public boolean sendBuffer(IBufferXfer.IReceiveBuffer rIface,
+ IBufferXfer.BufferXferInfo bufferInfo) {
+ if (rIface != mProducer && rIface != mConsumer) {
+ Log.e(TAG, "Interfaces does not match");
+ return false;
+ }
+ boolean status = true;
+ BufferInfo pBuf = null, cBuf = null;
+ synchronized(mLock) {
+ // see which interface this buffer belongs to
+ // producer has a filled buffer and the consumer
+ // buffer needs to be filled.
+ if ( rIface == mProducer ) {
+ if (mConsumerQueue.size() > 0) {
+ cBuf = mConsumerQueue.remove();
+ pBuf = new BufferInfo();
+ pBuf.rIface = rIface;
+ pBuf.info = bufferInfo;
+ } else {
+ BufferInfo info = new BufferInfo();
+ info.rIface = rIface;
+ info.info = bufferInfo;
+ mProducerQueue.add(info);
+ }
+ } else if(rIface == mConsumer) {
+ if (mProducerQueue.size() > 0) {
+ pBuf = mProducerQueue.remove();
+ cBuf = new BufferInfo();
+ cBuf.rIface = rIface;
+ cBuf.info = bufferInfo;
+ } else {
+ BufferInfo info = new BufferInfo();
+ info.rIface = rIface;
+ info.info = bufferInfo;
+ mConsumerQueue.add(info);
+ }
+ } else {
+ status = false;
+ }
+ }
+
+ if ( pBuf != null && cBuf != null) {
+ int bytesRead = 0;
+ if (cBuf.info.buf != null && pBuf.info.buf != null) {
+ if (cBuf.info.buf.remaining() >= pBuf.info.buf.remaining()) {
+ bytesRead = pBuf.info.buf.remaining();
+ cBuf.info.buf.put(pBuf.info.buf);
+ } else {
+ Log.e(TAG, "Something is wrong with the sizes P:" +
+ pBuf.info.buf.remaining() +" C:" + cBuf.info.buf.remaining());
+ }
+ }
+ cBuf.info.bytesRead = bytesRead;
+ cBuf.info.presentationTimeUs = pBuf.info.presentationTimeUs;
+ cBuf.info.flag = pBuf.info.flag;
+
+ if (pBuf.rIface != null) {
+ pBuf.rIface.receiveBuffer(pBuf.info);
+ }
+ if (cBuf.rIface != null) {
+ cBuf.rIface.receiveBuffer(cBuf.info);
+ }
+ }
+ return status;
+ }
+ public boolean resetAll() {
+ synchronized(mLock) {
+ while (mProducerQueue.size() > 0) {
+ BufferInfo info = mProducerQueue.remove();
+ info.rIface.receiveBuffer(info.info);
+ }
+ while (mConsumerQueue.size() > 0) {
+ BufferInfo info = mConsumerQueue.remove();
+ info.rIface.receiveBuffer(info.info);
+ }
+ mProducer = null;
+ mConsumer = null;
+ }
+ return true;
+ }
+}
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
index 65c45ff..7506f79 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
@@ -494,6 +494,32 @@
return found;
}
+const std::set<std::string>& getCameraIdsWithZoomLocked(
+ const InFlightRequestMap& inflightMap, const CameraMetadata& metadata,
+ const std::set<std::string>& cameraIdsWithZoom) {
+ camera_metadata_ro_entry overrideEntry =
+ metadata.find(ANDROID_CONTROL_SETTINGS_OVERRIDE);
+ camera_metadata_ro_entry frameNumberEntry =
+ metadata.find(ANDROID_CONTROL_SETTINGS_OVERRIDING_FRAME_NUMBER);
+ if (overrideEntry.count != 1
+ || overrideEntry.data.i32[0] != ANDROID_CONTROL_SETTINGS_OVERRIDE_ZOOM
+ || frameNumberEntry.count != 1) {
+ // No valid overriding frame number, skip
+ return cameraIdsWithZoom;
+ }
+
+ uint32_t overridingFrameNumber = frameNumberEntry.data.i32[0];
+ ssize_t idx = inflightMap.indexOfKey(overridingFrameNumber);
+ if (idx < 0) {
+ ALOGE("%s: Failed to find pending request #%d in inflight map",
+ __FUNCTION__, overridingFrameNumber);
+ return cameraIdsWithZoom;
+ }
+
+ const InFlightRequest &r = inflightMap.valueFor(overridingFrameNumber);
+ return r.cameraIdsWithZoom;
+}
+
void processCaptureResult(CaptureOutputStates& states, const camera_capture_result *result) {
ATRACE_CALL();
@@ -682,10 +708,12 @@
} else if (request.hasCallback) {
CameraMetadata metadata;
metadata = result->result;
+ auto cameraIdsWithZoom = getCameraIdsWithZoomLocked(
+ states.inflightMap, metadata, request.cameraIdsWithZoom);
sendCaptureResult(states, metadata, request.resultExtras,
collectedPartialResult, frameNumber,
hasInputBufferInRequest, request.zslCapture && request.stillCapture,
- request.rotateAndCropAuto, request.cameraIdsWithZoom,
+ request.rotateAndCropAuto, cameraIdsWithZoom,
request.physicalMetadatas);
}
}
@@ -912,11 +940,13 @@
states.listener->notifyShutter(r.resultExtras, msg.timestamp);
}
// send pending result and buffers
+ const auto& cameraIdsWithZoom = getCameraIdsWithZoomLocked(
+ inflightMap, r.pendingMetadata, r.cameraIdsWithZoom);
sendCaptureResult(states,
r.pendingMetadata, r.resultExtras,
r.collectedPartialResult, msg.frame_number,
r.hasInputBuffer, r.zslCapture && r.stillCapture,
- r.rotateAndCropAuto, r.cameraIdsWithZoom, r.physicalMetadatas);
+ r.rotateAndCropAuto, cameraIdsWithZoom, r.physicalMetadatas);
}
returnAndRemovePendingOutputBuffers(
states.useHalBufManager, states.listener, r, states.sessionStatsBuilder);
diff --git a/services/camera/libcameraservice/hidl/VndkVersionMetadataTags.h b/services/camera/libcameraservice/hidl/VndkVersionMetadataTags.h
index e6149da..45c9840 100644
--- a/services/camera/libcameraservice/hidl/VndkVersionMetadataTags.h
+++ b/services/camera/libcameraservice/hidl/VndkVersionMetadataTags.h
@@ -98,5 +98,6 @@
ANDROID_CONTROL_AUTOFRAMING,
ANDROID_CONTROL_AUTOFRAMING_STATE,
ANDROID_CONTROL_SETTINGS_OVERRIDE,
+ ANDROID_CONTROL_SETTINGS_OVERRIDING_FRAME_NUMBER,
} },
};