You've already forked android_translation_layer
mirror of
https://gitlab.com/android_translation_layer/android_translation_layer.git
synced 2025-10-27 11:48:10 -07:00
impement android.media.MediaCodec using libavcodec
The current implementation requires a VA-API driver and a Wayland compositor with YUV-buffer support. GNOME supports YUV-buffers since the recent version 45 release
This commit is contained in:
@@ -1,9 +1,150 @@
|
||||
package android.media;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Queue;
|
||||
import android.view.Surface;
|
||||
|
||||
public class MediaCodec {
|
||||
|
||||
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;
|
||||
|
||||
private MediaCodec(String codecName) {
|
||||
this.codecName = codecName;
|
||||
native_codec = native_constructor(codecName);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public static MediaCodec createByCodecName(String codecName) {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
public static final class CryptoInfo {}
|
||||
|
||||
public static final class BufferInfo {}
|
||||
public static final class BufferInfo {
|
||||
public int size;
|
||||
public int flags;
|
||||
public int offset;
|
||||
public long presentationTimeUs;
|
||||
}
|
||||
|
||||
public static interface OnFrameRenderedListener {}
|
||||
|
||||
}
|
||||
|
||||
32
src/api-impl/android/media/MediaCodecInfo.java
Normal file
32
src/api-impl/android/media/MediaCodecInfo.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package android.media;
|
||||
|
||||
public class MediaCodecInfo {
|
||||
|
||||
private String name;
|
||||
private String mime;
|
||||
|
||||
public MediaCodecInfo(String name, String mime) {
|
||||
this.name = name;
|
||||
this.mime = mime;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean isEncoder() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String[] getSupportedTypes() {
|
||||
return new String[] { mime };
|
||||
}
|
||||
|
||||
public CodecCapabilities getCapabilitiesForType(String type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class CodecCapabilities {}
|
||||
|
||||
public static class CodecProfileLevel {}
|
||||
}
|
||||
@@ -3,7 +3,18 @@ package android.media;
|
||||
public class MediaCodecList {
|
||||
|
||||
public static int getCodecCount() {
|
||||
return 0;
|
||||
return 2;
|
||||
}
|
||||
|
||||
public static MediaCodecInfo getCodecInfoAt(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return new MediaCodecInfo("aac", "audio/mp4a-latm");
|
||||
case 1:
|
||||
return new MediaCodecInfo("h264", "video/avc");
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
4
src/api-impl/android/media/MediaCrypto.java
Normal file
4
src/api-impl/android/media/MediaCrypto.java
Normal file
@@ -0,0 +1,4 @@
|
||||
package android.media;
|
||||
|
||||
public class MediaCrypto {
|
||||
}
|
||||
42
src/api-impl/android/media/MediaFormat.java
Normal file
42
src/api-impl/android/media/MediaFormat.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package android.media;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MediaFormat {
|
||||
|
||||
private Map<String, Object> map = new HashMap<>();
|
||||
|
||||
public void setString(String key, String value) {
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
public void setInteger(String key, int value) {
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
public void setByteBuffer(String key, ByteBuffer value) {
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
public void setFloat(String key, float value) {
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
public ByteBuffer getByteBuffer(String name) {
|
||||
return (ByteBuffer) map.get(name);
|
||||
}
|
||||
|
||||
public int getInteger(String name) {
|
||||
return (int) map.get(name);
|
||||
}
|
||||
|
||||
public boolean containsKey(String name) {
|
||||
return map.containsKey(name);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return map.toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user