Commit c1e583cb by tofunmi Committed by Ian Baker

Update image transcoding pipeline to signal input COLOR_TRANSFER_SRGB

The production code changes are in transformer, but the tests in effect have also been updated to confirm the is no color regression `inputColorInfo.colorTransfer=C.COLOR_TRANSFER_SRGB`

PiperOrigin-RevId: 526950435
parent 1be1175d
...@@ -135,7 +135,7 @@ public final class ColorInfo implements Bundleable { ...@@ -135,7 +135,7 @@ public final class ColorInfo implements Bundleable {
* Color info representing SDR sRGB in accordance with {@link * Color info representing SDR sRGB in accordance with {@link
* android.hardware.DataSpace#DATASPACE_SRGB}, which is a common SDR image color format. * android.hardware.DataSpace#DATASPACE_SRGB}, which is a common SDR image color format.
*/ */
public static final ColorInfo SRGB_FULL = public static final ColorInfo SRGB_BT709_FULL =
new ColorInfo.Builder() new ColorInfo.Builder()
.setColorSpace(C.COLOR_SPACE_BT709) .setColorSpace(C.COLOR_SPACE_BT709)
.setColorRange(C.COLOR_RANGE_FULL) .setColorRange(C.COLOR_RANGE_FULL)
......
...@@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat; ...@@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.testutil.VideoFrameProcessorTestRunner; import com.google.android.exoplayer2.testutil.VideoFrameProcessorTestRunner;
import com.google.android.exoplayer2.video.ColorInfo;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
...@@ -126,6 +127,7 @@ public class DefaultVideoFrameProcessorImageFrameOutputTest { ...@@ -126,6 +127,7 @@ public class DefaultVideoFrameProcessorImageFrameOutputTest {
.setTestId(testId) .setTestId(testId)
.setVideoFrameProcessorFactory(new DefaultVideoFrameProcessor.Factory.Builder().build()) .setVideoFrameProcessorFactory(new DefaultVideoFrameProcessor.Factory.Builder().build())
.setInputType(INPUT_TYPE_BITMAP) .setInputType(INPUT_TYPE_BITMAP)
.setInputColorInfo(ColorInfo.SRGB_BT709_FULL)
.setOnOutputFrameAvailableListener( .setOnOutputFrameAvailableListener(
unused -> checkNotNull(framesProduced).incrementAndGet()); unused -> checkNotNull(framesProduced).incrementAndGet());
} }
......
...@@ -33,6 +33,7 @@ import com.google.android.exoplayer2.testutil.VideoFrameProcessorTestRunner; ...@@ -33,6 +33,7 @@ import com.google.android.exoplayer2.testutil.VideoFrameProcessorTestRunner;
import com.google.android.exoplayer2.util.Effect; import com.google.android.exoplayer2.util.Effect;
import com.google.android.exoplayer2.util.Size; import com.google.android.exoplayer2.util.Size;
import com.google.android.exoplayer2.util.VideoFrameProcessingException; import com.google.android.exoplayer2.util.VideoFrameProcessingException;
import com.google.android.exoplayer2.video.ColorInfo;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.junit.After; import org.junit.After;
...@@ -111,7 +112,10 @@ public final class DefaultVideoFrameProcessorPixelTest { ...@@ -111,7 +112,10 @@ public final class DefaultVideoFrameProcessorPixelTest {
public void noEffects_withImageInput_matchesGoldenFile() throws Exception { public void noEffects_withImageInput_matchesGoldenFile() throws Exception {
String testId = "noEffects_withImageInput_matchesGoldenFile"; String testId = "noEffects_withImageInput_matchesGoldenFile";
videoFrameProcessorTestRunner = videoFrameProcessorTestRunner =
getDefaultFrameProcessorTestRunnerBuilder(testId).setInputType(INPUT_TYPE_BITMAP).build(); getDefaultFrameProcessorTestRunnerBuilder(testId)
.setInputType(INPUT_TYPE_BITMAP)
.setInputColorInfo(ColorInfo.SRGB_BT709_FULL)
.build();
Bitmap originalBitmap = readBitmap(IMAGE_PNG_ASSET_PATH); Bitmap originalBitmap = readBitmap(IMAGE_PNG_ASSET_PATH);
Bitmap expectedBitmap = readBitmap(IMAGE_TO_VIDEO_PNG_ASSET_PATH); Bitmap expectedBitmap = readBitmap(IMAGE_TO_VIDEO_PNG_ASSET_PATH);
...@@ -131,6 +135,7 @@ public final class DefaultVideoFrameProcessorPixelTest { ...@@ -131,6 +135,7 @@ public final class DefaultVideoFrameProcessorPixelTest {
videoFrameProcessorTestRunner = videoFrameProcessorTestRunner =
getDefaultFrameProcessorTestRunnerBuilder(testId) getDefaultFrameProcessorTestRunnerBuilder(testId)
.setInputType(INPUT_TYPE_BITMAP) .setInputType(INPUT_TYPE_BITMAP)
.setInputColorInfo(ColorInfo.SRGB_BT709_FULL)
.setEffects( .setEffects(
new GlEffectWrapper( new GlEffectWrapper(
new Crop( new Crop(
......
...@@ -36,6 +36,7 @@ import com.google.android.exoplayer2.upstream.DataSourceBitmapLoader; ...@@ -36,6 +36,7 @@ import com.google.android.exoplayer2.upstream.DataSourceBitmapLoader;
import com.google.android.exoplayer2.upstream.DefaultDataSource; import com.google.android.exoplayer2.upstream.DefaultDataSource;
import com.google.android.exoplayer2.util.BitmapLoader; import com.google.android.exoplayer2.util.BitmapLoader;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.video.ColorInfo;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
...@@ -113,6 +114,7 @@ public final class ImageAssetLoader implements AssetLoader { ...@@ -113,6 +114,7 @@ public final class ImageAssetLoader implements AssetLoader {
.setHeight(bitmap.getHeight()) .setHeight(bitmap.getHeight())
.setWidth(bitmap.getWidth()) .setWidth(bitmap.getWidth())
.setSampleMimeType(MIME_TYPE_IMAGE_ALL) .setSampleMimeType(MIME_TYPE_IMAGE_ALL)
.setColorInfo(ColorInfo.SRGB_BT709_FULL)
.build(); .build();
listener.onTrackAdded(format, SUPPORTED_OUTPUT_TYPE_DECODED); listener.onTrackAdded(format, SUPPORTED_OUTPUT_TYPE_DECODED);
scheduledExecutorService.submit(() -> queueBitmapInternal(bitmap, format)); scheduledExecutorService.submit(() -> queueBitmapInternal(bitmap, format));
......
...@@ -25,6 +25,7 @@ import static com.google.android.exoplayer2.util.Assertions.checkNotNull; ...@@ -25,6 +25,7 @@ import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.VideoFrameProcessor.INPUT_TYPE_BITMAP; import static com.google.android.exoplayer2.util.VideoFrameProcessor.INPUT_TYPE_BITMAP;
import static com.google.android.exoplayer2.util.VideoFrameProcessor.INPUT_TYPE_SURFACE; import static com.google.android.exoplayer2.util.VideoFrameProcessor.INPUT_TYPE_SURFACE;
import static com.google.android.exoplayer2.video.ColorInfo.SDR_BT709_LIMITED; import static com.google.android.exoplayer2.video.ColorInfo.SDR_BT709_LIMITED;
import static com.google.android.exoplayer2.video.ColorInfo.SRGB_BT709_FULL;
import static com.google.android.exoplayer2.video.ColorInfo.isTransferHdr; import static com.google.android.exoplayer2.video.ColorInfo.isTransferHdr;
import android.content.Context; import android.content.Context;
...@@ -123,17 +124,25 @@ import org.checkerframework.dataflow.qual.Pure; ...@@ -123,17 +124,25 @@ import org.checkerframework.dataflow.qual.Pure;
boolean isGlToneMapping = boolean isGlToneMapping =
ColorInfo.isTransferHdr(decoderInputColor) ColorInfo.isTransferHdr(decoderInputColor)
&& transformationRequest.hdrMode == HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL; && transformationRequest.hdrMode == HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL;
// For consistency with the Android platform, OpenGL tone mapping outputs colors with ColorInfo videoFrameProcessorOutputColor;
// C.COLOR_TRANSFER_GAMMA_2_2 instead of C.COLOR_TRANSFER_SDR, and outputs this as if (videoFrameProcessorInputColor.colorTransfer == C.COLOR_TRANSFER_SRGB) {
// C.COLOR_TRANSFER_SDR to the encoder. // The sRGB color transfer is only used for images, so when an image gets transcoded into a
ColorInfo videoFrameProcessorOutputColor = // video, we use the SMPTE 170M transfer function for the resulting video.
isGlToneMapping videoFrameProcessorOutputColor = SDR_BT709_LIMITED;
? new ColorInfo.Builder() } else if (isGlToneMapping) {
.setColorSpace(C.COLOR_SPACE_BT709) // For consistency with the Android platform, OpenGL tone mapping outputs colors with
.setColorRange(C.COLOR_RANGE_LIMITED) // C.COLOR_TRANSFER_GAMMA_2_2 instead of C.COLOR_TRANSFER_SDR, and outputs this as
.setColorTransfer(C.COLOR_TRANSFER_GAMMA_2_2) // C.COLOR_TRANSFER_SDR to the encoder.
.build() videoFrameProcessorOutputColor =
: videoFrameProcessorInputColor; new ColorInfo.Builder()
.setColorSpace(C.COLOR_SPACE_BT709)
.setColorRange(C.COLOR_RANGE_LIMITED)
.setColorTransfer(C.COLOR_TRANSFER_GAMMA_2_2)
.build();
} else {
videoFrameProcessorOutputColor = videoFrameProcessorInputColor;
}
List<Effect> effectsWithPresentation = new ArrayList<>(effects); List<Effect> effectsWithPresentation = new ArrayList<>(effects);
if (presentation != null) { if (presentation != null) {
effectsWithPresentation.add(presentation); effectsWithPresentation.add(presentation);
...@@ -409,6 +418,9 @@ import org.checkerframework.dataflow.qual.Pure; ...@@ -409,6 +418,9 @@ import org.checkerframework.dataflow.qual.Pure;
// populate default color info, which depends on the resolution. // populate default color info, which depends on the resolution.
return ColorInfo.SDR_BT709_LIMITED; return ColorInfo.SDR_BT709_LIMITED;
} }
if (SRGB_BT709_FULL.equals(inputFormat.colorInfo)) {
return ColorInfo.SDR_BT709_LIMITED;
}
return checkNotNull(inputFormat.colorInfo); return checkNotNull(inputFormat.colorInfo);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment