Support MediaCodec tonemapping in CompositionPlayer

PiperOrigin-RevId: 708995517
This commit is contained in:
claincly 2024-12-23 02:03:42 -08:00 committed by Copybara-Service
parent f22d91d9e5
commit 515de89973
2 changed files with 52 additions and 7 deletions

View File

@ -691,7 +691,9 @@ public final class CompositionPlayer extends SimpleBasePlayer
playbackAudioGraphWrapper, playbackAudioGraphWrapper,
playbackVideoGraphWrapper.getSink(), playbackVideoGraphWrapper.getSink(),
imageDecoderFactory, imageDecoderFactory,
/* inputIndex= */ i) /* inputIndex= */ i,
/* requestToneMapping= */ composition.hdrMode
== Composition.HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_MEDIACODEC)
: SequenceRenderersFactory.createForAudio( : SequenceRenderersFactory.createForAudio(
context, editedMediaItemSequence, playbackAudioGraphWrapper, /* inputIndex= */ i); context, editedMediaItemSequence, playbackAudioGraphWrapper, /* inputIndex= */ i);
ExoPlayer.Builder playerBuilder = ExoPlayer.Builder playerBuilder =

View File

@ -23,6 +23,7 @@ import static androidx.media3.exoplayer.DefaultRenderersFactory.MAX_DROPPED_VIDE
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.media.MediaFormat;
import android.os.Handler; import android.os.Handler;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media3.common.C; import androidx.media3.common.C;
@ -33,6 +34,7 @@ import androidx.media3.common.MimeTypes;
import androidx.media3.common.PlaybackException; import androidx.media3.common.PlaybackException;
import androidx.media3.common.Timeline; import androidx.media3.common.Timeline;
import androidx.media3.common.util.ConstantRateTimestampIterator; import androidx.media3.common.util.ConstantRateTimestampIterator;
import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.ExoPlaybackException; import androidx.media3.exoplayer.ExoPlaybackException;
import androidx.media3.exoplayer.Renderer; import androidx.media3.exoplayer.Renderer;
import androidx.media3.exoplayer.RenderersFactory; import androidx.media3.exoplayer.RenderersFactory;
@ -66,6 +68,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Nullable private final VideoSink videoSink; @Nullable private final VideoSink videoSink;
@Nullable private final ImageDecoder.Factory imageDecoderFactory; @Nullable private final ImageDecoder.Factory imageDecoderFactory;
private final int inputIndex; private final int inputIndex;
private final boolean requestToneMapping;
/** Creates a renderers factory for a player that will play video, image and audio. */ /** Creates a renderers factory for a player that will play video, image and audio. */
public static SequenceRenderersFactory create( public static SequenceRenderersFactory create(
@ -74,9 +77,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
PlaybackAudioGraphWrapper playbackAudioGraphWrapper, PlaybackAudioGraphWrapper playbackAudioGraphWrapper,
VideoSink videoSink, VideoSink videoSink,
ImageDecoder.Factory imageDecoderFactory, ImageDecoder.Factory imageDecoderFactory,
int inputIndex) { int inputIndex,
boolean requestToneMapping) {
return new SequenceRenderersFactory( return new SequenceRenderersFactory(
context, sequence, playbackAudioGraphWrapper, videoSink, imageDecoderFactory, inputIndex); context,
sequence,
playbackAudioGraphWrapper,
videoSink,
imageDecoderFactory,
inputIndex,
requestToneMapping);
} }
/** Creates a renderers factory that for a player that will only play audio. */ /** Creates a renderers factory that for a player that will only play audio. */
@ -91,7 +101,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
playbackAudioGraphWrapper, playbackAudioGraphWrapper,
/* videoSink= */ null, /* videoSink= */ null,
/* imageDecoderFactory= */ null, /* imageDecoderFactory= */ null,
inputIndex); inputIndex,
/* requestToneMapping= */ false);
} }
private SequenceRenderersFactory( private SequenceRenderersFactory(
@ -100,13 +111,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
PlaybackAudioGraphWrapper playbackAudioGraphWrapper, PlaybackAudioGraphWrapper playbackAudioGraphWrapper,
@Nullable VideoSink videoSink, @Nullable VideoSink videoSink,
@Nullable ImageDecoder.Factory imageDecoderFactory, @Nullable ImageDecoder.Factory imageDecoderFactory,
int inputIndex) { int inputIndex,
boolean requestToneMapping) {
this.context = context; this.context = context;
this.sequence = sequence; this.sequence = sequence;
this.playbackAudioGraphWrapper = playbackAudioGraphWrapper; this.playbackAudioGraphWrapper = playbackAudioGraphWrapper;
this.videoSink = videoSink; this.videoSink = videoSink;
this.imageDecoderFactory = imageDecoderFactory; this.imageDecoderFactory = imageDecoderFactory;
this.inputIndex = inputIndex; this.inputIndex = inputIndex;
this.requestToneMapping = requestToneMapping;
} }
@Override @Override
@ -133,7 +146,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
eventHandler, eventHandler,
videoRendererEventListener, videoRendererEventListener,
sequence, sequence,
videoSink)); videoSink,
requestToneMapping));
renderers.add( renderers.add(
new SequenceImageRenderer(sequence, checkStateNotNull(imageDecoderFactory), videoSink)); new SequenceImageRenderer(sequence, checkStateNotNull(imageDecoderFactory), videoSink));
} }
@ -266,8 +280,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
} }
private static final class SequenceVideoRenderer extends MediaCodecVideoRenderer { private static final class SequenceVideoRenderer extends MediaCodecVideoRenderer {
private static final String TAG = "SequenceVideoRenderer";
private final EditedMediaItemSequence sequence; private final EditedMediaItemSequence sequence;
private final VideoSink videoSink; private final VideoSink videoSink;
private final boolean requestToneMapping;
@Nullable private ImmutableList<Effect> pendingEffect; @Nullable private ImmutableList<Effect> pendingEffect;
@Nullable private EditedMediaItem currentEditedMediaItem; @Nullable private EditedMediaItem currentEditedMediaItem;
@ -278,7 +296,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
Handler eventHandler, Handler eventHandler,
VideoRendererEventListener videoRendererEventListener, VideoRendererEventListener videoRendererEventListener,
EditedMediaItemSequence sequence, EditedMediaItemSequence sequence,
VideoSink videoSink) { VideoSink videoSink,
boolean requestToneMapping) {
super( super(
context, context,
MediaCodecAdapter.Factory.getDefault(context), MediaCodecAdapter.Factory.getDefault(context),
@ -292,6 +311,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
videoSink); videoSink);
this.sequence = sequence; this.sequence = sequence;
this.videoSink = videoSink; this.videoSink = videoSink;
this.requestToneMapping = requestToneMapping;
experimentalEnableProcessedStreamChangedAtStart(); experimentalEnableProcessedStreamChangedAtStart();
} }
@ -313,6 +333,29 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
super.onStreamChanged(formats, startPositionUs, offsetUs, mediaPeriodId); super.onStreamChanged(formats, startPositionUs, offsetUs, mediaPeriodId);
} }
@Override
protected MediaFormat getMediaFormat(
Format format,
String codecMimeType,
CodecMaxValues codecMaxValues,
float codecOperatingRate,
boolean deviceNeedsNoPostProcessWorkaround,
int tunnelingAudioSessionId) {
MediaFormat mediaFormat =
super.getMediaFormat(
format,
codecMimeType,
codecMaxValues,
codecOperatingRate,
deviceNeedsNoPostProcessWorkaround,
tunnelingAudioSessionId);
if (requestToneMapping && Util.SDK_INT >= 31) {
mediaFormat.setInteger(
MediaFormat.KEY_COLOR_TRANSFER_REQUEST, MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
}
return mediaFormat;
}
@Override @Override
protected long getBufferTimestampAdjustmentUs() { protected long getBufferTimestampAdjustmentUs() {
return offsetToCompositionTimeUs; return offsetToCompositionTimeUs;