mirror of
https://github.com/androidx/media.git
synced 2025-05-11 17:49:52 +08:00
HDR: Put force HDR as SDR into AssetLoader
This allows us to fix usage of HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR. Before, this was checked in the VideoSamplePipeline, which no longer decides on the decoder configuration input format. PiperOrigin-RevId: 510142097
This commit is contained in:
parent
221c5afb1b
commit
fb8bbce5f1
@ -34,6 +34,7 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
|
||||
|
||||
private final Context context;
|
||||
private final Codec.DecoderFactory decoderFactory;
|
||||
private final boolean forceInterpretHdrAsSdr;
|
||||
private final Clock clock;
|
||||
private final MediaSource.@MonotonicNonNull Factory mediaSourceFactory;
|
||||
|
||||
@ -45,13 +46,19 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
|
||||
* @param context The {@link Context}.
|
||||
* @param decoderFactory The {@link Codec.DecoderFactory} to use to decode the samples (if
|
||||
* necessary).
|
||||
* @param forceInterpretHdrAsSdr Whether to apply {@link
|
||||
* TransformationRequest#HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR}.
|
||||
* @param clock The {@link Clock} to use. It should always be {@link Clock#DEFAULT}, except for
|
||||
* testing.
|
||||
*/
|
||||
public DefaultAssetLoaderFactory(
|
||||
Context context, Codec.DecoderFactory decoderFactory, Clock clock) {
|
||||
Context context,
|
||||
Codec.DecoderFactory decoderFactory,
|
||||
boolean forceInterpretHdrAsSdr,
|
||||
Clock clock) {
|
||||
this.context = context;
|
||||
this.decoderFactory = decoderFactory;
|
||||
this.forceInterpretHdrAsSdr = forceInterpretHdrAsSdr;
|
||||
this.clock = clock;
|
||||
this.mediaSourceFactory = null;
|
||||
}
|
||||
@ -62,6 +69,8 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
|
||||
* @param context The {@link Context}.
|
||||
* @param decoderFactory The {@link Codec.DecoderFactory} to use to decode the samples (if
|
||||
* necessary).
|
||||
* @param forceInterpretHdrAsSdr Whether to apply {@link
|
||||
* TransformationRequest#HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR}.
|
||||
* @param clock The {@link Clock} to use. It should always be {@link Clock#DEFAULT}, except for
|
||||
* testing.
|
||||
* @param mediaSourceFactory The {@link MediaSource.Factory} to use to retrieve the samples to
|
||||
@ -70,10 +79,12 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
|
||||
public DefaultAssetLoaderFactory(
|
||||
Context context,
|
||||
Codec.DecoderFactory decoderFactory,
|
||||
boolean forceInterpretHdrAsSdr,
|
||||
Clock clock,
|
||||
MediaSource.Factory mediaSourceFactory) {
|
||||
this.context = context;
|
||||
this.decoderFactory = decoderFactory;
|
||||
this.forceInterpretHdrAsSdr = forceInterpretHdrAsSdr;
|
||||
this.clock = clock;
|
||||
this.mediaSourceFactory = mediaSourceFactory;
|
||||
}
|
||||
@ -91,8 +102,10 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
|
||||
if (exoPlayerAssetLoaderFactory == null) {
|
||||
exoPlayerAssetLoaderFactory =
|
||||
mediaSourceFactory != null
|
||||
? new ExoPlayerAssetLoader.Factory(context, decoderFactory, clock, mediaSourceFactory)
|
||||
: new ExoPlayerAssetLoader.Factory(context, decoderFactory, clock);
|
||||
? new ExoPlayerAssetLoader.Factory(
|
||||
context, decoderFactory, forceInterpretHdrAsSdr, clock, mediaSourceFactory)
|
||||
: new ExoPlayerAssetLoader.Factory(
|
||||
context, decoderFactory, forceInterpretHdrAsSdr, clock);
|
||||
}
|
||||
return exoPlayerAssetLoaderFactory.createAssetLoader(editedMediaItem, looper, listener);
|
||||
}
|
||||
|
@ -135,6 +135,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
}
|
||||
}
|
||||
|
||||
/** Overrides the {@code inputFormat}. */
|
||||
protected Format overrideFormat(Format inputFormat) throws ExportException {
|
||||
return inputFormat;
|
||||
}
|
||||
|
||||
/** Called when the {@link Format} of the samples fed to the renderer is known. */
|
||||
protected void onInputFormatRead(Format inputFormat) {}
|
||||
|
||||
@ -176,7 +181,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
if (result != C.RESULT_FORMAT_READ) {
|
||||
return false;
|
||||
}
|
||||
Format inputFormat = checkNotNull(formatHolder.format);
|
||||
Format inputFormat = overrideFormat(checkNotNull(formatHolder.format));
|
||||
@AssetLoader.SupportedOutputTypes
|
||||
int supportedOutputTypes = SUPPORTED_OUTPUT_TYPE_ENCODED | SUPPORTED_OUTPUT_TYPE_DECODED;
|
||||
sampleConsumer =
|
||||
|
@ -16,6 +16,7 @@
|
||||
package androidx.media3.transformer;
|
||||
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.common.util.Util.SDK_INT;
|
||||
|
||||
import android.media.MediaCodec;
|
||||
import androidx.annotation.Nullable;
|
||||
@ -35,6 +36,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
|
||||
private final boolean flattenForSlowMotion;
|
||||
private final Codec.DecoderFactory decoderFactory;
|
||||
private final boolean forceInterpretHdrAsSdr;
|
||||
private final List<Long> decodeOnlyPresentationTimestamps;
|
||||
|
||||
private @MonotonicNonNull SefSlowMotionFlattener sefVideoSlowMotionFlattener;
|
||||
@ -43,11 +45,13 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
public ExoAssetLoaderVideoRenderer(
|
||||
boolean flattenForSlowMotion,
|
||||
Codec.DecoderFactory decoderFactory,
|
||||
boolean forceInterpretHdrAsSdr,
|
||||
TransformerMediaClock mediaClock,
|
||||
AssetLoader.Listener assetLoaderListener) {
|
||||
super(C.TRACK_TYPE_VIDEO, mediaClock, assetLoaderListener);
|
||||
this.flattenForSlowMotion = flattenForSlowMotion;
|
||||
this.decoderFactory = decoderFactory;
|
||||
this.forceInterpretHdrAsSdr = forceInterpretHdrAsSdr;
|
||||
decodeOnlyPresentationTimestamps = new ArrayList<>();
|
||||
}
|
||||
|
||||
@ -56,6 +60,23 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Format overrideFormat(Format inputFormat) throws ExportException {
|
||||
if (forceInterpretHdrAsSdr && ColorInfo.isTransferHdr(inputFormat.colorInfo)) {
|
||||
if (SDK_INT < 29) {
|
||||
throw ExportException.createForCodec(
|
||||
new IllegalArgumentException(
|
||||
"Interpreting HDR video as SDR is not supported on this device."),
|
||||
ExportException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED,
|
||||
/* isVideo= */ true,
|
||||
/* isDecoder= */ true,
|
||||
inputFormat);
|
||||
}
|
||||
return inputFormat.buildUpon().setColorInfo(ColorInfo.SDR_BT709_LIMITED).build();
|
||||
}
|
||||
return inputFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInputFormatRead(Format inputFormat) {
|
||||
if (flattenForSlowMotion) {
|
||||
|
@ -64,6 +64,7 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
||||
|
||||
private final Context context;
|
||||
private final Codec.DecoderFactory decoderFactory;
|
||||
private final boolean forceInterpretHdrAsSdr;
|
||||
private final Clock clock;
|
||||
@Nullable private final MediaSource.Factory mediaSourceFactory;
|
||||
|
||||
@ -73,12 +74,19 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
||||
* @param context The {@link Context}.
|
||||
* @param decoderFactory The {@link Codec.DecoderFactory} to use to decode the samples (if
|
||||
* necessary).
|
||||
* @param forceInterpretHdrAsSdr Whether to apply {@link
|
||||
* TransformationRequest#HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR}.
|
||||
* @param clock The {@link Clock} to use. It should always be {@link Clock#DEFAULT}, except for
|
||||
* testing.
|
||||
*/
|
||||
public Factory(Context context, Codec.DecoderFactory decoderFactory, Clock clock) {
|
||||
public Factory(
|
||||
Context context,
|
||||
Codec.DecoderFactory decoderFactory,
|
||||
boolean forceInterpretHdrAsSdr,
|
||||
Clock clock) {
|
||||
this.context = context;
|
||||
this.decoderFactory = decoderFactory;
|
||||
this.forceInterpretHdrAsSdr = forceInterpretHdrAsSdr;
|
||||
this.clock = clock;
|
||||
this.mediaSourceFactory = null;
|
||||
}
|
||||
@ -89,6 +97,8 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
||||
* @param context The {@link Context}.
|
||||
* @param decoderFactory The {@link Codec.DecoderFactory} to use to decode the samples (if
|
||||
* necessary).
|
||||
* @param forceInterpretHdrAsSdr Whether to apply {@link
|
||||
* TransformationRequest#HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR}.
|
||||
* @param clock The {@link Clock} to use. It should always be {@link Clock#DEFAULT}, except for
|
||||
* testing.
|
||||
* @param mediaSourceFactory The {@link MediaSource.Factory} to use to retrieve the samples to
|
||||
@ -97,10 +107,12 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
||||
public Factory(
|
||||
Context context,
|
||||
Codec.DecoderFactory decoderFactory,
|
||||
boolean forceInterpretHdrAsSdr,
|
||||
Clock clock,
|
||||
MediaSource.Factory mediaSourceFactory) {
|
||||
this.context = context;
|
||||
this.decoderFactory = decoderFactory;
|
||||
this.forceInterpretHdrAsSdr = forceInterpretHdrAsSdr;
|
||||
this.clock = clock;
|
||||
this.mediaSourceFactory = mediaSourceFactory;
|
||||
}
|
||||
@ -117,7 +129,14 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
||||
mediaSourceFactory = new DefaultMediaSourceFactory(context, defaultExtractorsFactory);
|
||||
}
|
||||
return new ExoPlayerAssetLoader(
|
||||
context, editedMediaItem, mediaSourceFactory, decoderFactory, looper, listener, clock);
|
||||
context,
|
||||
editedMediaItem,
|
||||
mediaSourceFactory,
|
||||
decoderFactory,
|
||||
forceInterpretHdrAsSdr,
|
||||
looper,
|
||||
listener,
|
||||
clock);
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,6 +151,7 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
||||
EditedMediaItem editedMediaItem,
|
||||
MediaSource.Factory mediaSourceFactory,
|
||||
Codec.DecoderFactory decoderFactory,
|
||||
boolean forceInterpretHdrAsSdr,
|
||||
Looper looper,
|
||||
Listener listener,
|
||||
Clock clock) {
|
||||
@ -161,6 +181,7 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
||||
editedMediaItem.removeVideo,
|
||||
editedMediaItem.flattenForSlowMotion,
|
||||
this.decoderFactory,
|
||||
forceInterpretHdrAsSdr,
|
||||
listener))
|
||||
.setMediaSourceFactory(mediaSourceFactory)
|
||||
.setTrackSelector(trackSelector)
|
||||
@ -223,6 +244,7 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
||||
private final boolean removeVideo;
|
||||
private final boolean flattenForSlowMotion;
|
||||
private final Codec.DecoderFactory decoderFactory;
|
||||
private final boolean forceInterpretHdrAsSdr;
|
||||
private final Listener assetLoaderListener;
|
||||
|
||||
public RenderersFactoryImpl(
|
||||
@ -230,11 +252,13 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
||||
boolean removeVideo,
|
||||
boolean flattenForSlowMotion,
|
||||
Codec.DecoderFactory decoderFactory,
|
||||
boolean forceInterpretHdrAsSdr,
|
||||
Listener assetLoaderListener) {
|
||||
this.removeAudio = removeAudio;
|
||||
this.removeVideo = removeVideo;
|
||||
this.flattenForSlowMotion = flattenForSlowMotion;
|
||||
this.decoderFactory = decoderFactory;
|
||||
this.forceInterpretHdrAsSdr = forceInterpretHdrAsSdr;
|
||||
this.assetLoaderListener = assetLoaderListener;
|
||||
mediaClock = new TransformerMediaClock();
|
||||
}
|
||||
@ -257,7 +281,11 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
|
||||
if (!removeVideo) {
|
||||
renderers[index] =
|
||||
new ExoAssetLoaderVideoRenderer(
|
||||
flattenForSlowMotion, decoderFactory, mediaClock, assetLoaderListener);
|
||||
flattenForSlowMotion,
|
||||
decoderFactory,
|
||||
forceInterpretHdrAsSdr,
|
||||
mediaClock,
|
||||
assetLoaderListener);
|
||||
index++;
|
||||
}
|
||||
return renderers;
|
||||
|
@ -437,7 +437,12 @@ public final class Transformer {
|
||||
}
|
||||
if (assetLoaderFactory == null) {
|
||||
assetLoaderFactory =
|
||||
new DefaultAssetLoaderFactory(context, new DefaultDecoderFactory(context), clock);
|
||||
new DefaultAssetLoaderFactory(
|
||||
context,
|
||||
new DefaultDecoderFactory(context),
|
||||
/* forceInterpretHdrAsSdr= */ transformationRequest.hdrMode
|
||||
== TransformationRequest.HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR,
|
||||
clock);
|
||||
}
|
||||
return new Transformer(
|
||||
context,
|
||||
|
@ -18,9 +18,7 @@ package androidx.media3.transformer;
|
||||
|
||||
import static androidx.media3.common.ColorInfo.isTransferHdr;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.common.util.Util.SDK_INT;
|
||||
import static androidx.media3.transformer.EncoderUtil.getSupportedEncodersForHdrEditing;
|
||||
import static androidx.media3.transformer.TransformationRequest.HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR;
|
||||
import static androidx.media3.transformer.TransformationRequest.HDR_MODE_KEEP_HDR;
|
||||
import static androidx.media3.transformer.TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_MEDIACODEC;
|
||||
import static androidx.media3.transformer.TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL;
|
||||
@ -87,25 +85,6 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||
throws ExportException {
|
||||
super(firstInputFormat, streamStartPositionUs, muxerWrapper);
|
||||
|
||||
boolean isGlToneMapping = false;
|
||||
if (isTransferHdr(firstInputFormat.colorInfo)) {
|
||||
if (transformationRequest.hdrMode == HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR) {
|
||||
if (SDK_INT < 29) {
|
||||
throw ExportException.createForCodec(
|
||||
new IllegalArgumentException(
|
||||
"Interpreting HDR video as SDR is not supported on this device."),
|
||||
ExportException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED,
|
||||
/* isVideo= */ true,
|
||||
/* isDecoder= */ true,
|
||||
firstInputFormat);
|
||||
}
|
||||
firstInputFormat =
|
||||
firstInputFormat.buildUpon().setColorInfo(ColorInfo.SDR_BT709_LIMITED).build();
|
||||
} else if (transformationRequest.hdrMode == HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL) {
|
||||
isGlToneMapping = true;
|
||||
}
|
||||
}
|
||||
|
||||
finalFramePresentationTimeUs = C.TIME_UNSET;
|
||||
|
||||
encoderOutputBuffer =
|
||||
@ -122,6 +101,9 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||
ColorInfo encoderInputColor = encoderWrapper.getSupportedInputColor();
|
||||
// If not tone mapping using OpenGL, the decoder will output the encoderInputColor,
|
||||
// possibly by tone mapping.
|
||||
boolean isGlToneMapping =
|
||||
ColorInfo.isTransferHdr(firstInputFormat.colorInfo)
|
||||
&& transformationRequest.hdrMode == HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL;
|
||||
videoFrameProcessorInputColor =
|
||||
isGlToneMapping ? checkNotNull(firstInputFormat.colorInfo) : encoderInputColor;
|
||||
// For consistency with the Android platform, OpenGL tone mapping outputs colors with
|
||||
|
@ -119,7 +119,8 @@ public class ExoPlayerAssetLoaderTest {
|
||||
Codec.DecoderFactory decoderFactory = new DefaultDecoderFactory(context);
|
||||
EditedMediaItem editedMediaItem =
|
||||
new EditedMediaItem.Builder(MediaItem.fromUri("asset:///media/mp4/sample.mp4")).build();
|
||||
return new ExoPlayerAssetLoader.Factory(context, decoderFactory, clock)
|
||||
return new ExoPlayerAssetLoader.Factory(
|
||||
context, decoderFactory, /* forceInterpretHdrAsSdr= */ false, clock)
|
||||
.createAssetLoader(editedMediaItem, looper, listener);
|
||||
}
|
||||
|
||||
|
@ -802,7 +802,12 @@ public final class TransformerEndToEndTest {
|
||||
context, new SlowExtractorsFactory(/* delayBetweenReadsMs= */ 10));
|
||||
Codec.DecoderFactory decoderFactory = new DefaultDecoderFactory(context);
|
||||
AssetLoader.Factory assetLoaderFactory =
|
||||
new ExoPlayerAssetLoader.Factory(context, decoderFactory, clock, mediaSourceFactory);
|
||||
new ExoPlayerAssetLoader.Factory(
|
||||
context,
|
||||
decoderFactory,
|
||||
/* forceInterpretHdrAsSdr= */ false,
|
||||
clock,
|
||||
mediaSourceFactory);
|
||||
Muxer.Factory muxerFactory = new TestMuxerFactory(/* maxDelayBetweenSamplesMs= */ 1);
|
||||
Transformer transformer =
|
||||
createTransformerBuilder(/* enableFallback= */ false)
|
||||
|
Loading…
x
Reference in New Issue
Block a user