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
This commit is contained in:
parent
42c9c28daf
commit
8612d2820d
@ -134,7 +134,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)
|
||||||
|
@ -21,6 +21,7 @@ import static androidx.media3.test.utils.BitmapPixelTestUtil.readBitmap;
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.ColorInfo;
|
||||||
import androidx.media3.test.utils.VideoFrameProcessorTestRunner;
|
import androidx.media3.test.utils.VideoFrameProcessorTestRunner;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
@ -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());
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import android.content.Context;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.ColorInfo;
|
||||||
import androidx.media3.common.Effect;
|
import androidx.media3.common.Effect;
|
||||||
import androidx.media3.common.VideoFrameProcessingException;
|
import androidx.media3.common.VideoFrameProcessingException;
|
||||||
import androidx.media3.common.util.Size;
|
import androidx.media3.common.util.Size;
|
||||||
@ -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 {
|
|||||||
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(
|
||||||
|
@ -29,6 +29,7 @@ import android.graphics.Bitmap;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.ColorInfo;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
@ -115,6 +116,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));
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package androidx.media3.transformer;
|
package androidx.media3.transformer;
|
||||||
|
|
||||||
import static androidx.media3.common.ColorInfo.SDR_BT709_LIMITED;
|
import static androidx.media3.common.ColorInfo.SDR_BT709_LIMITED;
|
||||||
|
import static androidx.media3.common.ColorInfo.SRGB_BT709_FULL;
|
||||||
import static androidx.media3.common.ColorInfo.isTransferHdr;
|
import static androidx.media3.common.ColorInfo.isTransferHdr;
|
||||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_BITMAP;
|
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_BITMAP;
|
||||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_SURFACE;
|
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_SURFACE;
|
||||||
@ -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;
|
||||||
|
ColorInfo videoFrameProcessorOutputColor;
|
||||||
|
if (videoFrameProcessorInputColor.colorTransfer == C.COLOR_TRANSFER_SRGB) {
|
||||||
|
// The sRGB color transfer is only used for images, so when an image gets transcoded into a
|
||||||
|
// video, we use the SMPTE 170M transfer function for the resulting video.
|
||||||
|
videoFrameProcessorOutputColor = SDR_BT709_LIMITED;
|
||||||
|
} else if (isGlToneMapping) {
|
||||||
// For consistency with the Android platform, OpenGL tone mapping outputs colors with
|
// For consistency with the Android platform, OpenGL tone mapping outputs colors with
|
||||||
// C.COLOR_TRANSFER_GAMMA_2_2 instead of C.COLOR_TRANSFER_SDR, and outputs this as
|
// C.COLOR_TRANSFER_GAMMA_2_2 instead of C.COLOR_TRANSFER_SDR, and outputs this as
|
||||||
// C.COLOR_TRANSFER_SDR to the encoder.
|
// C.COLOR_TRANSFER_SDR to the encoder.
|
||||||
ColorInfo videoFrameProcessorOutputColor =
|
videoFrameProcessorOutputColor =
|
||||||
isGlToneMapping
|
new ColorInfo.Builder()
|
||||||
? new ColorInfo.Builder()
|
|
||||||
.setColorSpace(C.COLOR_SPACE_BT709)
|
.setColorSpace(C.COLOR_SPACE_BT709)
|
||||||
.setColorRange(C.COLOR_RANGE_LIMITED)
|
.setColorRange(C.COLOR_RANGE_LIMITED)
|
||||||
.setColorTransfer(C.COLOR_TRANSFER_GAMMA_2_2)
|
.setColorTransfer(C.COLOR_TRANSFER_GAMMA_2_2)
|
||||||
.build()
|
.build();
|
||||||
: videoFrameProcessorInputColor;
|
} 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;
|
|||||||
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user