ultra HDR: send gainmap downstream if HDR output requested

removing the useHdr parameter from queueInputBitmap() it suggests we support changing between HDR and SDR within a stream, which we don't support. instead, identifying whether to use HDR from the shaderprogram which is informed by the inputColorInfo when the stream is registered.

PiperOrigin-RevId: 603681736
This commit is contained in:
tofunmi 2024-02-02 07:27:52 -08:00 committed by Copybara-Service
parent 271eb88b48
commit c79f950d26
4 changed files with 79 additions and 44 deletions

View File

@ -15,6 +15,10 @@
*/ */
package androidx.media3.common; package androidx.media3.common;
import static androidx.media3.common.util.Assertions.checkState;
import android.graphics.Gainmap;
import androidx.annotation.Nullable;
import androidx.media3.common.util.GlUtil; import androidx.media3.common.util.GlUtil;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
@ -51,8 +55,17 @@ public final class GlTextureInfo {
/** The height of the texture, in pixels, or {@link C#LENGTH_UNSET} if not specified. */ /** The height of the texture, in pixels, or {@link C#LENGTH_UNSET} if not specified. */
public final int height; public final int height;
/** The {@link Gainmap} associated to this texture, or {@code null} if not specified. */
@Nullable public final Gainmap gainmap;
/** /**
* Creates a new instance. * The OpenGL texture identifier for a gainmap associated to this contents of {@link #texId}, or
* {@link C#INDEX_UNSET} if not specified.
*/
public final int gainmapTexId;
/**
* Creates a new instance with {@code null} {@link Gainmap} and unspecified {@code gainmapTexId}.
* *
* @param texId The OpenGL texture identifier, or {@link C#INDEX_UNSET} if not specified. * @param texId The OpenGL texture identifier, or {@link C#INDEX_UNSET} if not specified.
* @param fboId Identifier of a framebuffer object associated with the texture, or {@link * @param fboId Identifier of a framebuffer object associated with the texture, or {@link
@ -68,6 +81,35 @@ public final class GlTextureInfo {
this.rboId = rboId; this.rboId = rboId;
this.width = width; this.width = width;
this.height = height; this.height = height;
this.gainmap = null;
this.gainmapTexId = C.INDEX_UNSET;
}
/**
* Creates a new instance.
*
* @param texId The OpenGL texture identifier, or {@link C#INDEX_UNSET} if not specified.
* @param fboId Identifier of a framebuffer object associated with the texture, or {@link
* C#INDEX_UNSET} if not specified.
* @param rboId Identifier of a renderbuffer object associated with the texture, or {@link
* C#INDEX_UNSET} if not specified.
* @param width The width of the texture, in pixels, or {@link C#LENGTH_UNSET} if not specified.
* @param height The height of the texture, in pixels, or {@link C#LENGTH_UNSET} if not specified.
* @param gainmap The {@link Gainmap} associated to this texture.
* @param gainmapTexId The OpenGL texture identifier for a gainmap associated with the contents of
* {@link #texId}.
*/
public GlTextureInfo(
int texId, int fboId, int rboId, int width, int height, Gainmap gainmap, int gainmapTexId) {
this.texId = texId;
this.fboId = fboId;
this.rboId = rboId;
this.width = width;
this.height = height;
this.gainmap = gainmap;
checkState(
gainmapTexId != C.INDEX_UNSET, "If gainmap is non-null, the gainmapTexId must be set");
this.gainmapTexId = gainmapTexId;
} }
/** Releases all information associated with this instance. */ /** Releases all information associated with this instance. */
@ -81,5 +123,8 @@ public final class GlTextureInfo {
if (rboId != C.INDEX_UNSET) { if (rboId != C.INDEX_UNSET) {
GlUtil.deleteRbo(rboId); GlUtil.deleteRbo(rboId);
} }
if (gainmapTexId != C.INDEX_UNSET) {
GlUtil.deleteTexture(gainmapTexId);
}
} }
} }

View File

@ -21,6 +21,7 @@ import static androidx.media3.common.util.Assertions.checkState;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.ColorInfo;
import androidx.media3.common.FrameInfo; import androidx.media3.common.FrameInfo;
import androidx.media3.common.GlObjectsProvider; import androidx.media3.common.GlObjectsProvider;
import androidx.media3.common.GlTextureInfo; import androidx.media3.common.GlTextureInfo;
@ -40,24 +41,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@UnstableApi @UnstableApi
/* package */ final class BitmapTextureManager extends TextureManager { /* package */ final class BitmapTextureManager extends TextureManager {
private static final String UNSUPPORTED_IMAGE_CONFIGURATION =
"Unsupported Image Configuration: No more than 8 bits of precision should be used for each"
+ " RGB channel.";
private final GlObjectsProvider glObjectsProvider;
private @MonotonicNonNull GlShaderProgram shaderProgram;
// The queue holds all bitmaps with one or more frames pending to be sent downstream. // The queue holds all bitmaps with one or more frames pending to be sent downstream.
private final Queue<BitmapFrameSequenceInfo> pendingBitmaps; private final Queue<BitmapFrameSequenceInfo> pendingBitmaps;
private final GlObjectsProvider glObjectsProvider;
private @MonotonicNonNull GlShaderProgram shaderProgram;
private @MonotonicNonNull GlTextureInfo currentGlTextureInfo; private @MonotonicNonNull GlTextureInfo currentGlTextureInfo;
private int downstreamShaderProgramCapacity; private int downstreamShaderProgramCapacity;
// TODO - b/262693274: Support HDR.
@SuppressWarnings({"UnusedVariable", "FieldCanBeLocal"})
private boolean useHdr;
private boolean currentInputStreamEnded; private boolean currentInputStreamEnded;
private boolean isNextFrameInTexture; private boolean isNextFrameInTexture;
private boolean useHdr;
/** /**
* Creates a new instance. * Creates a new instance.
@ -91,17 +84,19 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override @Override
public void queueInputBitmap( public void queueInputBitmap(
Bitmap inputBitmap, Bitmap inputBitmap, FrameInfo frameInfo, TimestampIterator inStreamOffsetsUs) {
FrameInfo frameInfo,
TimestampIterator inStreamOffsetsUs,
boolean useHdr) {
videoFrameProcessingTaskExecutor.submit( videoFrameProcessingTaskExecutor.submit(
() -> { () -> {
setupBitmap(inputBitmap, frameInfo, inStreamOffsetsUs, useHdr); setupBitmap(inputBitmap, frameInfo, inStreamOffsetsUs);
currentInputStreamEnded = false; currentInputStreamEnded = false;
}); });
} }
@Override
public void setInputFrameInfo(FrameInfo inputFrameInfo) {
this.useHdr = ColorInfo.isTransferHdr(inputFrameInfo.colorInfo);
}
@Override @Override
public int getPendingFrameCount() { public int getPendingFrameCount() {
// Always treat all queued bitmaps as immediately processed. // Always treat all queued bitmaps as immediately processed.
@ -134,20 +129,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
} }
// Methods that must be called on the GL thread. // Methods that must be called on the GL thread.
private void setupBitmap( private void setupBitmap(Bitmap bitmap, FrameInfo frameInfo, TimestampIterator inStreamOffsetsUs)
Bitmap bitmap, FrameInfo frameInfo, TimestampIterator inStreamOffsetsUs, boolean useHdr)
throws VideoFrameProcessingException { throws VideoFrameProcessingException {
if (Util.SDK_INT >= 26) {
checkState(
!checkNotNull(bitmap.getConfig()).equals(Bitmap.Config.RGBA_F16),
UNSUPPORTED_IMAGE_CONFIGURATION);
}
if (Util.SDK_INT >= 33) {
checkState(
!checkNotNull(bitmap.getConfig()).equals(Bitmap.Config.RGBA_1010102),
UNSUPPORTED_IMAGE_CONFIGURATION);
}
this.useHdr = useHdr;
checkArgument(inStreamOffsetsUs.hasNext(), "Bitmap queued but no timestamps provided."); checkArgument(inStreamOffsetsUs.hasNext(), "Bitmap queued but no timestamps provided.");
pendingBitmaps.add(new BitmapFrameSequenceInfo(bitmap, frameInfo, inStreamOffsetsUs)); pendingBitmaps.add(new BitmapFrameSequenceInfo(bitmap, frameInfo, inStreamOffsetsUs));
maybeQueueToShaderProgram(); maybeQueueToShaderProgram();
@ -222,9 +205,17 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
currentGlTextureInfo.release(); currentGlTextureInfo.release();
} }
currentTexId = GlUtil.createTexture(bitmap); currentTexId = GlUtil.createTexture(bitmap);
} catch (GlUtil.GlException e) { if (useHdr && Util.SDK_INT >= 34 && bitmap.hasGainmap()) {
throw VideoFrameProcessingException.from(e); currentGlTextureInfo =
} new GlTextureInfo(
currentTexId,
/* fboId= */ C.INDEX_UNSET,
/* rboId= */ C.INDEX_UNSET,
frameInfo.width,
frameInfo.height,
checkNotNull(bitmap.getGainmap()),
GlUtil.createTexture(bitmap.getGainmap().getGainmapContents()));
} else {
currentGlTextureInfo = currentGlTextureInfo =
new GlTextureInfo( new GlTextureInfo(
currentTexId, currentTexId,
@ -233,4 +224,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
frameInfo.width, frameInfo.width,
frameInfo.height); frameInfo.height);
} }
} catch (GlUtil.GlException e) {
throw VideoFrameProcessingException.from(e);
}
}
} }

View File

@ -397,8 +397,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
.queueInputBitmap( .queueInputBitmap(
inputBitmap, inputBitmap,
new FrameInfo.Builder(frameInfo).setOffsetToAddUs(frameInfo.offsetToAddUs).build(), new FrameInfo.Builder(frameInfo).setOffsetToAddUs(frameInfo.offsetToAddUs).build(),
timestampIterator, timestampIterator);
/* useHdr= */ false);
return true; return true;
} }

View File

@ -79,13 +79,9 @@ import androidx.media3.common.util.TimestampIterator;
* @param frameInfo Information about the bitmap being queued. * @param frameInfo Information about the bitmap being queued.
* @param inStreamOffsetsUs The times within the current stream that the bitmap should be shown * @param inStreamOffsetsUs The times within the current stream that the bitmap should be shown
* at. The timestamps should be monotonically increasing. * at. The timestamps should be monotonically increasing.
* @param useHdr Whether input and/or output colors are HDR.
*/ */
public void queueInputBitmap( public void queueInputBitmap(
Bitmap inputBitmap, Bitmap inputBitmap, FrameInfo frameInfo, TimestampIterator inStreamOffsetsUs) {
FrameInfo frameInfo,
TimestampIterator inStreamOffsetsUs,
boolean useHdr) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }