|
|
|
|
@@ -2,6 +2,8 @@ package com.limelight.binding.video;
|
|
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
|
import java.nio.ByteOrder;
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.concurrent.LinkedBlockingQueue;
|
|
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
|
@@ -49,6 +51,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
|
|
|
|
|
private byte[] ppsBuffer;
|
|
|
|
|
private boolean submittedCsd;
|
|
|
|
|
private boolean submitCsdNextCall;
|
|
|
|
|
private byte[] currentHdrMetadata;
|
|
|
|
|
|
|
|
|
|
private int nextInputBufferIndex = -1;
|
|
|
|
|
private ByteBuffer nextInputBuffer;
|
|
|
|
|
@@ -379,10 +382,64 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
|
|
|
|
|
videoFormat.setInteger(MediaFormat.KEY_MAX_HEIGHT, initialHeight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Android 7.0 adds color options to the MediaFormat
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
|
|
|
videoFormat.setInteger(MediaFormat.KEY_COLOR_RANGE,
|
|
|
|
|
getPreferredColorRange() == MoonBridge.COLOR_RANGE_FULL ?
|
|
|
|
|
MediaFormat.COLOR_RANGE_FULL : MediaFormat.COLOR_RANGE_LIMITED);
|
|
|
|
|
|
|
|
|
|
// If the stream is HDR-capable, the decoder will detect transitions in color standards
|
|
|
|
|
// rather than us hardcoding them into the MediaFormat.
|
|
|
|
|
if (getActiveVideoFormat() != MoonBridge.VIDEO_FORMAT_H265_MAIN10) {
|
|
|
|
|
// Set color format keys when not in HDR mode, since we know they won't change
|
|
|
|
|
videoFormat.setInteger(MediaFormat.KEY_COLOR_TRANSFER, MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
|
|
|
|
|
switch (getPreferredColorSpace()) {
|
|
|
|
|
case MoonBridge.COLORSPACE_REC_601:
|
|
|
|
|
videoFormat.setInteger(MediaFormat.KEY_COLOR_STANDARD, MediaFormat.COLOR_STANDARD_BT601_NTSC);
|
|
|
|
|
break;
|
|
|
|
|
case MoonBridge.COLORSPACE_REC_709:
|
|
|
|
|
videoFormat.setInteger(MediaFormat.KEY_COLOR_STANDARD, MediaFormat.COLOR_STANDARD_BT709);
|
|
|
|
|
break;
|
|
|
|
|
case MoonBridge.COLORSPACE_REC_2020:
|
|
|
|
|
videoFormat.setInteger(MediaFormat.KEY_COLOR_STANDARD, MediaFormat.COLOR_STANDARD_BT2020);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return videoFormat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void configureAndStartDecoder(MediaFormat format) {
|
|
|
|
|
// Set HDR metadata if present
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
|
|
|
if (currentHdrMetadata != null) {
|
|
|
|
|
ByteBuffer hdrStaticInfo = ByteBuffer.allocate(25).order(ByteOrder.LITTLE_ENDIAN);
|
|
|
|
|
ByteBuffer hdrMetadata = ByteBuffer.wrap(currentHdrMetadata).order(ByteOrder.LITTLE_ENDIAN);
|
|
|
|
|
|
|
|
|
|
// Create a HDMI Dynamic Range and Mastering InfoFrame as defined by CTA-861.3
|
|
|
|
|
hdrStaticInfo.put((byte) 0); // Metadata type
|
|
|
|
|
hdrStaticInfo.putShort(hdrMetadata.getShort()); // RX
|
|
|
|
|
hdrStaticInfo.putShort(hdrMetadata.getShort()); // RY
|
|
|
|
|
hdrStaticInfo.putShort(hdrMetadata.getShort()); // GX
|
|
|
|
|
hdrStaticInfo.putShort(hdrMetadata.getShort()); // GY
|
|
|
|
|
hdrStaticInfo.putShort(hdrMetadata.getShort()); // BX
|
|
|
|
|
hdrStaticInfo.putShort(hdrMetadata.getShort()); // BY
|
|
|
|
|
hdrStaticInfo.putShort(hdrMetadata.getShort()); // White X
|
|
|
|
|
hdrStaticInfo.putShort(hdrMetadata.getShort()); // White Y
|
|
|
|
|
hdrStaticInfo.putShort(hdrMetadata.getShort()); // Max mastering luminance
|
|
|
|
|
hdrStaticInfo.putShort(hdrMetadata.getShort()); // Min mastering luminance
|
|
|
|
|
hdrStaticInfo.putShort(hdrMetadata.getShort()); // Max content luminance
|
|
|
|
|
hdrStaticInfo.putShort(hdrMetadata.getShort()); // Max frame average luminance
|
|
|
|
|
|
|
|
|
|
hdrStaticInfo.rewind();
|
|
|
|
|
format.setByteBuffer(MediaFormat.KEY_HDR_STATIC_INFO, hdrStaticInfo);
|
|
|
|
|
}
|
|
|
|
|
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
|
|
|
format.removeKey(MediaFormat.KEY_HDR_STATIC_INFO);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LimeLog.info("Configuring with format: "+format);
|
|
|
|
|
|
|
|
|
|
videoDecoder.configure(format, renderTarget.getSurface(), null, 0);
|
|
|
|
|
@@ -1140,8 +1197,33 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void setHdrMode(boolean enabled) {
|
|
|
|
|
// TODO: Set HDR metadata?
|
|
|
|
|
public void setHdrMode(boolean enabled, byte[] hdrMetadata) {
|
|
|
|
|
// HDR metadata is only supported in Android 7.0 and later, so don't bother
|
|
|
|
|
// restarting the codec on anything earlier than that.
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
|
|
|
if (currentHdrMetadata != null && (!enabled || hdrMetadata == null)) {
|
|
|
|
|
currentHdrMetadata = null;
|
|
|
|
|
}
|
|
|
|
|
else if (enabled && hdrMetadata != null && !Arrays.equals(currentHdrMetadata, hdrMetadata)) {
|
|
|
|
|
currentHdrMetadata = hdrMetadata;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// Nothing to do
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we reach this point, we need to restart the MediaCodec instance to
|
|
|
|
|
// pick up the HDR metadata change. This will happen on the next input
|
|
|
|
|
// or output buffer.
|
|
|
|
|
|
|
|
|
|
// HACK: Reset codec recovery attempt counter, since this is an expected "recovery"
|
|
|
|
|
codecRecoveryAttempts = 0;
|
|
|
|
|
|
|
|
|
|
// Promote None/Flush to Restart and leave Reset alone
|
|
|
|
|
if (!codecRecoveryType.compareAndSet(CR_RECOVERY_TYPE_NONE, CR_RECOVERY_TYPE_RESTART)) {
|
|
|
|
|
codecRecoveryType.compareAndSet(CR_RECOVERY_TYPE_FLUSH, CR_RECOVERY_TYPE_RESTART);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean queueNextInputBuffer(long timestampUs, int codecFlags) {
|
|
|
|
|
|