2023-09-21 22:49:36 +02:00
|
|
|
package android.media;
|
|
|
|
|
|
2024-06-12 16:37:21 +02:00
|
|
|
import java.io.IOException;
|
2023-10-08 16:09:27 +02:00
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
|
import java.nio.ByteOrder;
|
|
|
|
|
import java.util.ArrayDeque;
|
|
|
|
|
import java.util.Queue;
|
|
|
|
|
import android.view.Surface;
|
|
|
|
|
|
2023-09-21 22:49:36 +02:00
|
|
|
public class MediaCodec {
|
|
|
|
|
|
2023-10-08 16:09:27 +02:00
|
|
|
private String codecName;
|
|
|
|
|
private ByteBuffer[] inputBuffers;
|
|
|
|
|
private ByteBuffer[] outputBuffers;
|
|
|
|
|
private long[] inputBufferTimestamps;
|
|
|
|
|
private long native_codec;
|
|
|
|
|
private boolean outputFormatSet = false;
|
|
|
|
|
private MediaFormat mediaFormat;
|
|
|
|
|
|
|
|
|
|
private Queue<Integer> freeOutputBuffers;
|
|
|
|
|
private Queue<Integer> queuedInputBuffers;
|
|
|
|
|
private Queue<Integer> freeInputBuffers;
|
|
|
|
|
|
2024-06-12 16:37:21 +02:00
|
|
|
private MediaCodec(String codecName) throws IOException {
|
2023-10-08 16:09:27 +02:00
|
|
|
this.codecName = codecName;
|
|
|
|
|
native_codec = native_constructor(codecName);
|
2024-06-12 16:37:21 +02:00
|
|
|
if (native_codec == 0) {
|
|
|
|
|
throw new IOException("Unable to create MediaCodec: " + codecName);
|
|
|
|
|
}
|
2023-10-08 16:09:27 +02:00
|
|
|
inputBuffers = new ByteBuffer[1];
|
|
|
|
|
inputBufferTimestamps = new long[inputBuffers.length];
|
|
|
|
|
freeInputBuffers = new ArrayDeque<>(inputBuffers.length);
|
|
|
|
|
queuedInputBuffers = new ArrayDeque<>(inputBuffers.length);
|
|
|
|
|
for (int i = 0; i < inputBuffers.length; i++) {
|
|
|
|
|
inputBuffers[i] = ByteBuffer.allocate(262144).order(ByteOrder.LITTLE_ENDIAN);
|
|
|
|
|
freeInputBuffers.add(i);
|
|
|
|
|
}
|
|
|
|
|
outputBuffers = new ByteBuffer[2];
|
|
|
|
|
freeOutputBuffers = new ArrayDeque<>(outputBuffers.length);
|
|
|
|
|
for (int i = 0; i < outputBuffers.length; i++) {
|
|
|
|
|
outputBuffers[i] = ByteBuffer.allocate(4096).order(ByteOrder.LITTLE_ENDIAN);
|
|
|
|
|
freeOutputBuffers.add(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 16:37:21 +02:00
|
|
|
public static MediaCodec createByCodecName(String codecName) throws IOException {
|
2023-10-08 16:09:27 +02:00
|
|
|
return new MediaCodec(codecName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags) {
|
|
|
|
|
System.out.println("MediaCodec.configure(" + format + ", " + surface + ", " + crypto + ", " + flags + "): codecName=" + codecName);
|
|
|
|
|
this.mediaFormat = format;
|
|
|
|
|
if ("aac".equals(codecName)) {
|
|
|
|
|
native_configure_audio(native_codec, format.getByteBuffer("csd-0"), format.getInteger("sample-rate"), format.getInteger("channel-count"));
|
|
|
|
|
} else if ("h264".equals(codecName)) {
|
|
|
|
|
native_configure_video(native_codec, format.getByteBuffer("csd-0"), format.getByteBuffer("csd-1"), surface);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void start() {
|
|
|
|
|
System.out.println("MediaCodec.start(): codecName=" + codecName);
|
|
|
|
|
native_start(native_codec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ByteBuffer[] getInputBuffers() {
|
|
|
|
|
return inputBuffers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ByteBuffer[] getOutputBuffers() {
|
|
|
|
|
return outputBuffers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int dequeueOutputBuffer(BufferInfo info, long timeoutUs) {
|
|
|
|
|
if (!outputFormatSet) {
|
|
|
|
|
outputFormatSet = true;
|
|
|
|
|
return /*INFO_OUTPUT_FORMAT_CHANGED*/ -2;
|
|
|
|
|
}
|
|
|
|
|
Integer index = freeOutputBuffers.poll();
|
|
|
|
|
if (index == null) {
|
|
|
|
|
return /*INFO_TRY_AGAIN_LATER*/ -1;
|
|
|
|
|
}
|
|
|
|
|
outputBuffers[index].clear();
|
|
|
|
|
int ret = native_dequeueOutputBuffer(native_codec, outputBuffers[index], info);
|
|
|
|
|
if (ret == 0) {
|
|
|
|
|
ret = index;
|
|
|
|
|
} else {
|
|
|
|
|
freeOutputBuffers.add(index);
|
|
|
|
|
ret = /*INFO_TRY_AGAIN_LATER*/ -1;
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void releaseOutputBuffer(int index, boolean render) {
|
|
|
|
|
native_releaseOutputBuffer(native_codec, outputBuffers[index], render);
|
|
|
|
|
freeOutputBuffers.add(index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public MediaFormat getOutputFormat() {
|
|
|
|
|
return mediaFormat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void flush() {}
|
|
|
|
|
|
|
|
|
|
private void tryProcessInputBuffer() {
|
|
|
|
|
Integer index = queuedInputBuffers.peek();
|
|
|
|
|
if (index != null) {
|
|
|
|
|
int ret = native_queueInputBuffer(native_codec, inputBuffers[index], inputBufferTimestamps[index]);
|
|
|
|
|
if (ret == 0) {
|
|
|
|
|
queuedInputBuffers.remove(index);
|
|
|
|
|
freeInputBuffers.add(index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int dequeueInputBuffer(long timeoutUs) {
|
|
|
|
|
tryProcessInputBuffer();
|
|
|
|
|
Integer index = freeInputBuffers.poll();
|
|
|
|
|
if (index == null) {
|
|
|
|
|
return /*INFO_TRY_AGAIN_LATER*/ -1;
|
|
|
|
|
} else {
|
|
|
|
|
return index;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void queueInputBuffer(int index, int offset, int size, long presentationTimeUs, int flags) {
|
|
|
|
|
inputBufferTimestamps[index] = presentationTimeUs;
|
|
|
|
|
queuedInputBuffers.add(index);
|
|
|
|
|
tryProcessInputBuffer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setVideoScalingMode(int mode) {
|
|
|
|
|
System.out.println("MediaCodec.setVideoScalingMode(" + mode + "): codecName=" + codecName);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-17 15:17:37 +01:00
|
|
|
public void release() {
|
|
|
|
|
System.out.println("MediaCodec.release(): codecName=" + codecName);
|
|
|
|
|
native_release(native_codec);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-08 16:09:27 +02:00
|
|
|
private native long native_constructor(String codecName);
|
|
|
|
|
private native void native_configure_audio(long codec, ByteBuffer extradata, int sampleRate, int channelCount);
|
|
|
|
|
private native void native_configure_video(long codec, ByteBuffer csd0, ByteBuffer csd1, Surface surface);
|
|
|
|
|
private native void native_start(long codec);
|
|
|
|
|
private native int native_queueInputBuffer(long codec, ByteBuffer buffer, long presentationTimeUs);
|
|
|
|
|
private native int native_dequeueOutputBuffer(long codec, ByteBuffer buffer, BufferInfo info);
|
|
|
|
|
private native void native_releaseOutputBuffer(long codec, ByteBuffer buffer, boolean render);
|
2024-02-17 15:17:37 +01:00
|
|
|
private native void native_release(long codec);
|
2023-10-08 16:09:27 +02:00
|
|
|
|
2023-09-21 22:49:36 +02:00
|
|
|
public static final class CryptoInfo {}
|
|
|
|
|
|
2023-10-08 16:09:27 +02:00
|
|
|
public static final class BufferInfo {
|
|
|
|
|
public int size;
|
|
|
|
|
public int flags;
|
|
|
|
|
public int offset;
|
|
|
|
|
public long presentationTimeUs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static interface OnFrameRenderedListener {}
|
2023-09-21 22:49:36 +02:00
|
|
|
|
|
|
|
|
}
|