Set LogSessionId on Transformer decoders and encoders

PiperOrigin-RevId: 737636636
This commit is contained in:
shahddaghash 2025-03-17 09:09:37 -07:00 committed by Copybara-Service
parent 4932300b9a
commit 40ab0d40a1
25 changed files with 270 additions and 93 deletions

View File

@ -74,7 +74,10 @@ public final class TestTransformerBuilder {
maxDelayBetweenMuxerSamplesMs = Transformer.DEFAULT_MAX_DELAY_BETWEEN_MUXER_SAMPLES_MS;
assetLoaderFactory =
new DefaultAssetLoaderFactory(
context, new DefaultDecoderFactory.Builder(context).build(), clock);
context,
new DefaultDecoderFactory.Builder(context).build(),
clock,
/* logSessionId= */ null);
muxerFactory = new DefaultMuxer.Factory();
looper = Util.getCurrentOrMainLooper();
}

View File

@ -36,6 +36,7 @@ import android.graphics.Bitmap.Config;
import android.media.Image;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.metrics.LogSessionId;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.os.Build;
@ -1225,13 +1226,15 @@ public final class AndroidTestUtil {
}
@Override
public Codec createForAudioEncoding(Format format) throws ExportException {
return encoderFactory.createForAudioEncoding(format);
public Codec createForAudioEncoding(Format format, @Nullable LogSessionId logSessionId)
throws ExportException {
return encoderFactory.createForAudioEncoding(format, logSessionId);
}
@Override
public Codec createForVideoEncoding(Format format) throws ExportException {
return encoderFactory.createForVideoEncoding(format);
public Codec createForVideoEncoding(Format format, @Nullable LogSessionId logSessionId)
throws ExportException {
return encoderFactory.createForVideoEncoding(format, logSessionId);
}
@Override

View File

@ -26,6 +26,7 @@ import static org.junit.Assume.assumeTrue;
import android.content.Context;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.media.metrics.LogSessionId;
import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
@ -166,7 +167,8 @@ public class ForceEndOfStreamTest {
new DefaultAssetLoaderFactory(
context,
new FrameDroppingDecoderFactory(context, MP4_ASSET.videoFrameCount, framesToSkip),
Clock.DEFAULT))
Clock.DEFAULT,
/* logSessionId= */ null))
.build();
}
@ -192,17 +194,21 @@ public class ForceEndOfStreamTest {
}
@Override
public Codec createForAudioDecoding(Format format) throws ExportException {
return defaultDecoderFactory.createForAudioDecoding(format);
public Codec createForAudioDecoding(Format format, @Nullable LogSessionId logSessionId)
throws ExportException {
return defaultDecoderFactory.createForAudioDecoding(format, logSessionId);
}
@Override
public Codec createForVideoDecoding(
Format format, Surface outputSurface, boolean requestSdrToneMapping)
Format format,
Surface outputSurface,
boolean requestSdrToneMapping,
@Nullable LogSessionId logSessionId)
throws ExportException {
return new FrameDroppingDecoder(
defaultDecoderFactory.createForVideoDecoding(
format, outputSurface, requestSdrToneMapping),
format, outputSurface, requestSdrToneMapping, logSessionId),
sourceFrameCount,
framesToDrop);
}

View File

@ -224,7 +224,8 @@ public final class SequenceEffectTestUtil {
ImmutableList.of(decoderMediaCodecInfo))
.build();
AssetLoader.Factory assetLoaderFactory =
new DefaultAssetLoaderFactory(context, decoderFactory, Clock.DEFAULT);
new DefaultAssetLoaderFactory(
context, decoderFactory, Clock.DEFAULT, /* logSessionId= */ null);
Codec.EncoderFactory encoderFactory =
new DefaultEncoderFactory.Builder(context)
.setRequestedVideoEncoderSettings(

View File

@ -64,6 +64,7 @@ import static org.junit.Assume.assumeTrue;
import android.content.Context;
import android.graphics.Bitmap;
import android.media.MediaFormat;
import android.media.metrics.LogSessionId;
import android.net.Uri;
import android.opengl.EGLContext;
import android.os.Handler;
@ -71,6 +72,7 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Pair;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.Effect;
import androidx.media3.common.Format;
@ -2616,12 +2618,14 @@ public class TransformerEndToEndTest {
}
@Override
public Codec createForAudioEncoding(Format format) throws ExportException {
return encoderFactory.createForAudioEncoding(format);
public Codec createForAudioEncoding(Format format, @Nullable LogSessionId logSessionId)
throws ExportException {
return encoderFactory.createForAudioEncoding(format, logSessionId);
}
@Override
public Codec createForVideoEncoding(Format format) throws ExportException {
public Codec createForVideoEncoding(Format format, @Nullable LogSessionId logSessionId)
throws ExportException {
throw ExportException.createForCodec(
new IllegalArgumentException(),
ExportException.ERROR_CODE_ENCODER_INIT_FAILED,

View File

@ -152,7 +152,8 @@ public class TranscodeForegroundSpeedTest {
.setShouldConfigureOperatingRate(true)
.build();
AssetLoader.Factory assetLoaderFactory =
new DefaultAssetLoaderFactory(context, decoderFactory, Clock.DEFAULT);
new DefaultAssetLoaderFactory(
context, decoderFactory, Clock.DEFAULT, /* logSessionId= */ null);
Transformer transformer =
new Transformer.Builder(context)
.setVideoMimeType(MimeTypes.VIDEO_H264)

View File

@ -229,7 +229,8 @@ public class TranscodeSpeedTest {
Codec.DecoderFactory decoderFactory =
new DefaultDecoderFactory.Builder(context).setShouldConfigureOperatingRate(true).build();
AssetLoader.Factory assetLoaderFactory =
new DefaultAssetLoaderFactory(context, decoderFactory, Clock.DEFAULT);
new DefaultAssetLoaderFactory(
context, decoderFactory, Clock.DEFAULT, /* logSessionId= */ null);
Transformer transformer =
ExperimentalAnalyzerModeFactory.buildAnalyzer(context)
.buildUpon()

View File

@ -21,6 +21,7 @@ import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.decoder.DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED;
import static java.lang.Math.min;
import android.media.metrics.LogSessionId;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.Format;
@ -58,7 +59,8 @@ import org.checkerframework.dataflow.qual.Pure;
AudioMixer.Factory mixerFactory,
Codec.EncoderFactory encoderFactory,
MuxerWrapper muxerWrapper,
FallbackListener fallbackListener)
FallbackListener fallbackListener,
@Nullable LogSessionId logSessionId)
throws ExportException {
super(firstAssetLoaderTrackFormat, muxerWrapper);
SonicAudioProcessor outputResampler = new SonicAudioProcessor();
@ -96,7 +98,8 @@ import org.checkerframework.dataflow.qual.Pure;
findSupportedMimeTypeForEncoderAndMuxer(
requestedEncoderFormat,
muxerWrapper.getSupportedSampleMimeTypes(C.TRACK_TYPE_AUDIO)))
.build());
.build(),
logSessionId);
AudioFormat actualEncoderAudioFormat = new AudioFormat(encoder.getInputFormat());
// This occurs when the encoder does not support the requested format. In this case, the audio

View File

@ -15,6 +15,7 @@
*/
package androidx.media3.transformer;
import android.media.metrics.LogSessionId;
import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.media3.common.Format;
@ -31,17 +32,23 @@ import androidx.media3.common.Format;
}
@Override
public Codec createForAudioDecoding(Format format) throws ExportException {
Codec audioDecoder = decoderFactory.createForAudioDecoding(format);
public Codec createForAudioDecoding(Format format, @Nullable LogSessionId logSessionId)
throws ExportException {
Codec audioDecoder = decoderFactory.createForAudioDecoding(format, logSessionId);
audioDecoderName = audioDecoder.getName();
return audioDecoder;
}
@Override
public Codec createForVideoDecoding(
Format format, Surface outputSurface, boolean requestSdrToneMapping) throws ExportException {
Format format,
Surface outputSurface,
boolean requestSdrToneMapping,
@Nullable LogSessionId logSessionId)
throws ExportException {
Codec videoDecoder =
decoderFactory.createForVideoDecoding(format, outputSurface, requestSdrToneMapping);
decoderFactory.createForVideoDecoding(
format, outputSurface, requestSdrToneMapping, logSessionId);
videoDecoderName = videoDecoder.getName();
return videoDecoder;
}

View File

@ -15,6 +15,7 @@
*/
package androidx.media3.transformer;
import android.media.metrics.LogSessionId;
import androidx.annotation.Nullable;
import androidx.media3.common.Format;
@ -30,15 +31,17 @@ import androidx.media3.common.Format;
}
@Override
public Codec createForAudioEncoding(Format format) throws ExportException {
Codec audioEncoder = encoderFactory.createForAudioEncoding(format);
public Codec createForAudioEncoding(Format format, @Nullable LogSessionId logSessionId)
throws ExportException {
Codec audioEncoder = encoderFactory.createForAudioEncoding(format, logSessionId);
audioEncoderName = audioEncoder.getName();
return audioEncoder;
}
@Override
public Codec createForVideoEncoding(Format format) throws ExportException {
Codec videoEncoder = encoderFactory.createForVideoEncoding(format);
public Codec createForVideoEncoding(Format format, @Nullable LogSessionId logSessionId)
throws ExportException {
Codec videoEncoder = encoderFactory.createForVideoEncoding(format, logSessionId);
videoEncoderName = videoEncoder.getName();
return videoEncoder;
}

View File

@ -17,6 +17,7 @@
package androidx.media3.transformer;
import android.media.MediaCodec.BufferInfo;
import android.media.metrics.LogSessionId;
import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
@ -41,10 +42,13 @@ public interface Codec {
*
* @param format The {@link Format} (of the input data) used to determine the underlying decoder
* and its configuration values.
* @param logSessionId The optional {@link LogSessionId} of the {@link
* android.media.metrics.EditingSession}.
* @return A {@link Codec} for audio decoding.
* @throws ExportException If no suitable {@link Codec} can be created.
*/
Codec createForAudioDecoding(Format format) throws ExportException;
Codec createForAudioDecoding(Format format, @Nullable LogSessionId logSessionId)
throws ExportException;
/**
* Returns a {@link Codec} for video decoding.
@ -53,11 +57,17 @@ public interface Codec {
* and its configuration values.
* @param outputSurface The {@link Surface} to which the decoder output is rendered.
* @param requestSdrToneMapping Whether to request tone-mapping to SDR.
* @param logSessionId The optional {@link LogSessionId} of the {@link
* android.media.metrics.EditingSession}.
* @return A {@link Codec} for video decoding.
* @throws ExportException If no suitable {@link Codec} can be created.
*/
Codec createForVideoDecoding(
Format format, Surface outputSurface, boolean requestSdrToneMapping) throws ExportException;
Format format,
Surface outputSurface,
boolean requestSdrToneMapping,
@Nullable LogSessionId logSessionId)
throws ExportException;
}
/** A factory for {@linkplain Codec encoder} instances. */
@ -77,11 +87,14 @@ public interface Codec {
* encoder and its configuration values. {@link Format#sampleMimeType}, {@link
* Format#sampleRate}, {@link Format#channelCount} and {@link Format#bitrate} are set to
* those of the desired output video format.
* @param logSessionId The optional {@link LogSessionId} of the {@link
* android.media.metrics.EditingSession}.
* @return A {@link Codec} for encoding audio to the requested {@link Format#sampleMimeType MIME
* type}.
* @throws ExportException If no suitable {@link Codec} can be created.
*/
Codec createForAudioEncoding(Format format) throws ExportException;
Codec createForAudioEncoding(Format format, @Nullable LogSessionId logSessionId)
throws ExportException;
/**
* Returns a {@link Codec} for video encoding.
@ -100,11 +113,14 @@ public interface Codec {
* Format#frameRate} is set to the requested output frame rate, if available. {@link
* Format#colorInfo} is set to the requested output color characteristics, if available.
* {@link Format#rotationDegrees} is always 0.
* @param logSessionId The optional {@link LogSessionId} of the {@link
* android.media.metrics.EditingSession}.
* @return A {@link Codec} for encoding video to the requested {@linkplain Format#sampleMimeType
* MIME type}.
* @throws ExportException If no suitable {@link Codec} can be created.
*/
Codec createForVideoEncoding(Format format) throws ExportException;
Codec createForVideoEncoding(Format format, @Nullable LogSessionId logSessionId)
throws ExportException;
/** Returns whether the audio needs to be encoded because of encoder specific configuration. */
default boolean audioNeedsEncoding() {

View File

@ -22,6 +22,7 @@ import static androidx.media3.transformer.TransformerUtil.isImage;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.ColorSpace;
import android.media.metrics.LogSessionId;
import android.os.Looper;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
@ -58,6 +59,7 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
@Nullable private final MediaSource.Factory mediaSourceFactory;
private final BitmapLoader bitmapLoader;
@Nullable private final TrackSelector.Factory trackSelectorFactory;
@Nullable private final LogSessionId logSessionId;
private AssetLoader.@MonotonicNonNull Factory imageAssetLoaderFactory;
private AssetLoader.@MonotonicNonNull Factory exoPlayerAssetLoaderFactory;
@ -74,15 +76,21 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
* necessary).
* @param clock The {@link Clock} to use. It should always be {@link Clock#DEFAULT}, except for
* testing.
* @param logSessionId The optional {@link LogSessionId} of the {@link
* android.media.metrics.EditingSession}.
*/
public DefaultAssetLoaderFactory(
Context context, Codec.DecoderFactory decoderFactory, Clock clock) {
Context context,
Codec.DecoderFactory decoderFactory,
Clock clock,
@Nullable LogSessionId logSessionId) {
// TODO: b/381519379 - Deprecate this constructor and replace with a builder.
this.context = context.getApplicationContext();
this.decoderFactory = decoderFactory;
this.clock = clock;
this.mediaSourceFactory = null;
this.trackSelectorFactory = null;
this.logSessionId = logSessionId;
@Nullable BitmapFactory.Options options = null;
if (Util.SDK_INT >= 26) {
options = new BitmapFactory.Options();
@ -113,6 +121,7 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
clock = Clock.DEFAULT;
mediaSourceFactory = null;
trackSelectorFactory = null;
logSessionId = null;
}
/**
@ -140,6 +149,7 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
this.mediaSourceFactory = mediaSourceFactory;
this.bitmapLoader = bitmapLoader;
this.trackSelectorFactory = null;
this.logSessionId = null;
}
/**
@ -170,6 +180,7 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
this.mediaSourceFactory = mediaSourceFactory;
this.bitmapLoader = bitmapLoader;
this.trackSelectorFactory = trackSelectorFactory;
this.logSessionId = null;
}
@Override
@ -196,7 +207,12 @@ public final class DefaultAssetLoaderFactory implements AssetLoader.Factory {
if (exoPlayerAssetLoaderFactory == null) {
exoPlayerAssetLoaderFactory =
new ExoPlayerAssetLoader.Factory(
context, decoderFactory, clock, mediaSourceFactory, trackSelectorFactory);
context,
decoderFactory,
clock,
mediaSourceFactory,
trackSelectorFactory,
logSessionId);
}
return exoPlayerAssetLoaderFactory.createAssetLoader(
editedMediaItem, looper, listener, compositionSettings);

View File

@ -25,6 +25,7 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.media.metrics.LogSessionId;
import android.os.Build;
import android.util.Pair;
import android.view.Surface;
@ -237,16 +238,25 @@ public final class DefaultDecoderFactory implements Codec.DecoderFactory {
}
@Override
public DefaultCodec createForAudioDecoding(Format format) throws ExportException {
public DefaultCodec createForAudioDecoding(Format format, @Nullable LogSessionId logSessionId)
throws ExportException {
MediaFormat mediaFormat = createMediaFormatFromFormat(format);
return createCodecForMediaFormat(
mediaFormat, format, /* outputSurface= */ null, /* devicePrefersSoftwareDecoder= */ false);
mediaFormat,
format,
/* outputSurface= */ null,
/* devicePrefersSoftwareDecoder= */ false,
logSessionId);
}
@SuppressLint("InlinedApi")
@Override
public DefaultCodec createForVideoDecoding(
Format format, Surface outputSurface, boolean requestSdrToneMapping) throws ExportException {
Format format,
Surface outputSurface,
boolean requestSdrToneMapping,
@Nullable LogSessionId logSessionId)
throws ExportException {
if (ColorInfo.isTransferHdr(format.colorInfo)) {
if (requestSdrToneMapping
&& (SDK_INT < 31
@ -298,14 +308,15 @@ public final class DefaultDecoderFactory implements Codec.DecoderFactory {
}
return createCodecForMediaFormat(
mediaFormat, format, outputSurface, devicePrefersSoftwareDecoder(format));
mediaFormat, format, outputSurface, devicePrefersSoftwareDecoder(format), logSessionId);
}
private DefaultCodec createCodecForMediaFormat(
MediaFormat mediaFormat,
Format format,
@Nullable Surface outputSurface,
boolean devicePrefersSoftwareDecoder)
boolean devicePrefersSoftwareDecoder,
@Nullable LogSessionId logSessionId)
throws ExportException {
List<MediaCodecInfo> decoderInfos = ImmutableList.of();
checkNotNull(format.sampleMimeType);
@ -344,6 +355,9 @@ public final class DefaultDecoderFactory implements Codec.DecoderFactory {
mediaFormat.setInteger(
MediaFormat.KEY_COLOR_TRANSFER_REQUEST, MediaFormat.COLOR_TRANSFER_HLG);
}
if (SDK_INT >= 35 && logSessionId != null) {
TransformerUtil.Api35.setLogSessionIdToMediaCodecFormat(mediaFormat, logSessionId);
}
List<ExportException> codecInitExceptions = new ArrayList<>();
DefaultCodec codec =
createCodecFromDecoderInfos(

View File

@ -32,6 +32,7 @@ import static java.lang.Math.round;
import android.content.Context;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.metrics.LogSessionId;
import android.os.Build;
import android.util.Size;
import androidx.annotation.IntRange;
@ -190,7 +191,8 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
}
@Override
public DefaultCodec createForAudioEncoding(Format format) throws ExportException {
public DefaultCodec createForAudioEncoding(Format format, @Nullable LogSessionId logSessionId)
throws ExportException {
if (format.bitrate == Format.NO_VALUE) {
format = format.buildUpon().setAverageBitrate(DEFAULT_AUDIO_BITRATE).build();
}
@ -236,6 +238,9 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
if (requestedAudioEncoderSettings.bitrate != AudioEncoderSettings.NO_VALUE) {
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, requestedAudioEncoderSettings.bitrate);
}
if (SDK_INT >= 35 && logSessionId != null) {
TransformerUtil.Api35.setLogSessionIdToMediaCodecFormat(mediaFormat, logSessionId);
}
return new DefaultCodec(
context,
@ -254,7 +259,8 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
* in {@link Format} are ignored when {@link VideoEncoderSettings#bitrate} is set.
*/
@Override
public DefaultCodec createForVideoEncoding(Format format) throws ExportException {
public DefaultCodec createForVideoEncoding(Format format, @Nullable LogSessionId logSessionId)
throws ExportException {
if (format.frameRate == Format.NO_VALUE || deviceNeedsDefaultFrameRateWorkaround()) {
format = format.buildUpon().setFrameRate(DEFAULT_FRAME_RATE).build();
}
@ -385,6 +391,9 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
if (Util.SDK_INT >= 35) {
mediaFormat.setInteger(MediaFormat.KEY_IMPORTANCE, max(0, -codecPriority));
if (logSessionId != null) {
TransformerUtil.Api35.setLogSessionIdToMediaCodecFormat(mediaFormat, logSessionId);
}
}
return new DefaultCodec(

View File

@ -18,6 +18,7 @@ package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkNotNull;
import android.media.MediaCodec;
import android.media.metrics.LogSessionId;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.Format;
@ -30,15 +31,18 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private static final String TAG = "ExoAssetLoaderAudioRenderer";
private final Codec.DecoderFactory decoderFactory;
@Nullable private final LogSessionId logSessionId;
private boolean hasPendingConsumerInput;
public ExoAssetLoaderAudioRenderer(
Codec.DecoderFactory decoderFactory,
TransformerMediaClock mediaClock,
AssetLoader.Listener assetLoaderListener) {
AssetLoader.Listener assetLoaderListener,
@Nullable LogSessionId logSessionId) {
super(C.TRACK_TYPE_AUDIO, mediaClock, assetLoaderListener);
this.decoderFactory = decoderFactory;
this.logSessionId = logSessionId;
}
@Override
@ -48,7 +52,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
@Override
protected void initDecoder(Format inputFormat) throws ExportException {
decoder = decoderFactory.createForAudioDecoding(inputFormat);
decoder = decoderFactory.createForAudioDecoding(inputFormat, logSessionId);
}
@Override

View File

@ -20,6 +20,7 @@ import static androidx.media3.common.util.Assertions.checkStateNotNull;
import static androidx.media3.transformer.TransformerUtil.getDecoderOutputColor;
import android.media.MediaCodec;
import android.media.metrics.LogSessionId;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.ColorInfo;
@ -39,6 +40,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private final Codec.DecoderFactory decoderFactory;
private final @Composition.HdrMode int hdrMode;
private final List<Long> decodeOnlyPresentationTimestamps;
@Nullable private final LogSessionId logSessionId;
private @MonotonicNonNull SefSlowMotionFlattener sefVideoSlowMotionFlattener;
private int maxDecoderPendingFrameCount;
@ -48,11 +50,13 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
Codec.DecoderFactory decoderFactory,
@Composition.HdrMode int hdrMode,
TransformerMediaClock mediaClock,
AssetLoader.Listener assetLoaderListener) {
AssetLoader.Listener assetLoaderListener,
@Nullable LogSessionId logSessionId) {
super(C.TRACK_TYPE_VIDEO, mediaClock, assetLoaderListener);
this.flattenForSlowMotion = flattenForSlowMotion;
this.decoderFactory = decoderFactory;
this.hdrMode = hdrMode;
this.logSessionId = logSessionId;
decodeOnlyPresentationTimestamps = new ArrayList<>();
maxDecoderPendingFrameCount = C.INDEX_UNSET;
}
@ -118,7 +122,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
decoderFactory.createForVideoDecoding(
inputFormat,
checkNotNull(sampleConsumer.getInputSurface()),
isDecoderToneMappingRequired);
isDecoderToneMappingRequired,
logSessionId);
maxDecoderPendingFrameCount = decoder.getMaxPendingFrameCount();
}

View File

@ -31,6 +31,7 @@ import static androidx.media3.transformer.TransformerUtil.isImage;
import static java.lang.Math.min;
import android.content.Context;
import android.media.metrics.LogSessionId;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.Nullable;
@ -72,6 +73,7 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
private final Clock clock;
@Nullable private final MediaSource.Factory mediaSourceFactory;
@Nullable private final TrackSelector.Factory trackSelectorFactory;
@Nullable private final LogSessionId logSessionId;
/**
* Creates an instance using a {@link DefaultMediaSourceFactory}.
@ -89,7 +91,8 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
decoderFactory,
clock,
/* mediaSourceFactory= */ null,
/* trackSelectorFactory= */ null);
/* trackSelectorFactory= */ null,
/* logSessionId= */ null);
}
/**
@ -109,7 +112,13 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
Clock clock,
MediaSource.Factory mediaSourceFactory) {
// TODO: b/381519379 - Deprecate this constructor and replace with a builder.
this(context, decoderFactory, clock, mediaSourceFactory, /* trackSelectorFactory= */ null);
this(
context,
decoderFactory,
clock,
mediaSourceFactory,
/* trackSelectorFactory= */ null,
/* logSessionId= */ null);
}
/**
@ -124,19 +133,23 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
* transform.
* @param trackSelectorFactory The {@link TrackSelector.Factory} to use when selecting the track
* to transform.
* @param logSessionId The optional {@link LogSessionId} of the {@link
* android.media.metrics.EditingSession}.
*/
public Factory(
Context context,
Codec.DecoderFactory decoderFactory,
Clock clock,
@Nullable MediaSource.Factory mediaSourceFactory,
@Nullable TrackSelector.Factory trackSelectorFactory) {
@Nullable TrackSelector.Factory trackSelectorFactory,
@Nullable LogSessionId logSessionId) {
// TODO: b/381519379 - Deprecate this constructor and replace with a builder.
this.context = context;
this.decoderFactory = decoderFactory;
this.clock = clock;
this.mediaSourceFactory = mediaSourceFactory;
this.trackSelectorFactory = trackSelectorFactory;
this.logSessionId = logSessionId;
}
@Override
@ -176,7 +189,8 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
looper,
listener,
clock,
trackSelectorFactory);
trackSelectorFactory,
logSessionId);
}
}
@ -198,7 +212,8 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
Looper looper,
Listener listener,
Clock clock,
TrackSelector.Factory trackSelectorFactory) {
TrackSelector.Factory trackSelectorFactory,
@Nullable LogSessionId logSessionId) {
this.context = context;
this.editedMediaItem = editedMediaItem;
this.decoderFactory = new CapturingDecoderFactory(decoderFactory);
@ -223,7 +238,8 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
editedMediaItem.flattenForSlowMotion,
this.decoderFactory,
hdrMode,
listener))
listener,
logSessionId))
.setMediaSourceFactory(mediaSourceFactory)
.setTrackSelector(trackSelector)
.setLoadControl(loadControl)
@ -291,6 +307,7 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
private final Codec.DecoderFactory decoderFactory;
private final @Composition.HdrMode int hdrMode;
private final Listener assetLoaderListener;
@Nullable private final LogSessionId logSessionId;
public RenderersFactoryImpl(
boolean removeAudio,
@ -298,13 +315,15 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
boolean flattenForSlowMotion,
Codec.DecoderFactory decoderFactory,
@Composition.HdrMode int hdrMode,
Listener assetLoaderListener) {
Listener assetLoaderListener,
@Nullable LogSessionId logSessionId) {
this.removeAudio = removeAudio;
this.removeVideo = removeVideo;
this.flattenForSlowMotion = flattenForSlowMotion;
this.decoderFactory = decoderFactory;
this.hdrMode = hdrMode;
this.assetLoaderListener = assetLoaderListener;
this.logSessionId = logSessionId;
mediaClock = new TransformerMediaClock();
}
@ -318,12 +337,18 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
ArrayList<Renderer> renderers = new ArrayList<>();
if (!removeAudio) {
renderers.add(
new ExoAssetLoaderAudioRenderer(decoderFactory, mediaClock, assetLoaderListener));
new ExoAssetLoaderAudioRenderer(
decoderFactory, mediaClock, assetLoaderListener, logSessionId));
}
if (!removeVideo) {
renderers.add(
new ExoAssetLoaderVideoRenderer(
flattenForSlowMotion, decoderFactory, hdrMode, mediaClock, assetLoaderListener));
flattenForSlowMotion,
decoderFactory,
hdrMode,
mediaClock,
assetLoaderListener,
logSessionId));
}
return renderers.toArray(new Renderer[renderers.size()]);
}

View File

@ -20,6 +20,7 @@ import static androidx.media3.common.util.Assertions.checkState;
import android.content.Context;
import android.media.MediaCodec.BufferInfo;
import android.media.metrics.LogSessionId;
import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
@ -93,12 +94,12 @@ public final class ExperimentalAnalyzerModeFactory {
}
@Override
public Codec createForAudioEncoding(Format format) {
public Codec createForAudioEncoding(Format format, @Nullable LogSessionId logSessionId) {
return new DroppingEncoder(context, format);
}
@Override
public Codec createForVideoEncoding(Format format) {
public Codec createForVideoEncoding(Format format, @Nullable LogSessionId logSessionId) {
return new DroppingEncoder(context, format);
}
}

View File

@ -36,6 +36,7 @@ import static java.lang.Math.round;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.content.Context;
import android.media.metrics.LogSessionId;
import android.os.Looper;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
@ -1601,15 +1602,7 @@ public final class Transformer {
transformationRequest =
transformationRequest.buildUpon().setHdrMode(composition.hdrMode).build();
}
FallbackListener fallbackListener =
new FallbackListener(composition, listeners, applicationHandler, transformationRequest);
AssetLoader.Factory assetLoaderFactory = this.assetLoaderFactory;
if (useDefaultAssetLoaderFactory || assetLoaderFactory == null) {
assetLoaderFactory =
new DefaultAssetLoaderFactory(
context, new DefaultDecoderFactory.Builder(context).build(), clock);
}
DebugTraceUtil.reset();
LogSessionId logSessionId = null;
if (canCollectEditingMetrics()) {
@Nullable String muxerName = null;
if (muxerFactory instanceof InAppMp4Muxer.Factory) {
@ -1619,10 +1612,24 @@ public final class Transformer {
} else if (muxerFactory instanceof DefaultMuxer.Factory) {
muxerName = DefaultMuxer.MUXER_NAME;
}
EditingMetricsCollector.MetricsReporter metricsReporter =
checkNotNull(metricsReporterFactory).create();
if (metricsReporter instanceof EditingMetricsCollector.DefaultMetricsReporter) {
logSessionId =
((EditingMetricsCollector.DefaultMetricsReporter) metricsReporter).getLogSessionId();
}
editingMetricsCollector =
new EditingMetricsCollector(
checkNotNull(metricsReporterFactory).create(), EXPORTER_NAME, muxerName);
new EditingMetricsCollector(metricsReporter, EXPORTER_NAME, muxerName);
}
FallbackListener fallbackListener =
new FallbackListener(composition, listeners, applicationHandler, transformationRequest);
AssetLoader.Factory assetLoaderFactory = this.assetLoaderFactory;
if (useDefaultAssetLoaderFactory || assetLoaderFactory == null) {
assetLoaderFactory =
new DefaultAssetLoaderFactory(
context, new DefaultDecoderFactory.Builder(context).build(), clock, logSessionId);
}
DebugTraceUtil.reset();
transformerInternal =
new TransformerInternal(
context,
@ -1640,7 +1647,8 @@ public final class Transformer {
applicationHandler,
debugViewProvider,
clock,
initialTimestampOffsetUs);
initialTimestampOffsetUs,
logSessionId);
transformerInternal.start();
}

View File

@ -42,6 +42,7 @@ import static java.lang.Math.max;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.content.Context;
import android.media.metrics.LogSessionId;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
@ -201,7 +202,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
HandlerWrapper applicationHandler,
DebugViewProvider debugViewProvider,
Clock clock,
long videoSampleTimestampOffsetUs) {
long videoSampleTimestampOffsetUs,
@Nullable LogSessionId logSessionId) {
this.context = context;
this.composition = composition;
this.encoderFactory = new CapturingEncoderFactory(encoderFactory);
@ -240,7 +242,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
audioMixerFactory,
videoFrameProcessorFactory,
fallbackListener,
debugViewProvider);
debugViewProvider,
logSessionId);
EditedMediaItemSequence sequence = composition.sequences.get(i);
sequenceAssetLoaders.add(
new SequenceAssetLoader(
@ -555,6 +558,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private final VideoFrameProcessor.Factory videoFrameProcessorFactory;
private final FallbackListener fallbackListener;
private final DebugViewProvider debugViewProvider;
@Nullable private final LogSessionId logSessionId;
private long currentSequenceDurationUs;
public SequenceAssetLoaderListener(
@ -564,7 +568,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
AudioMixer.Factory audioMixerFactory,
VideoFrameProcessor.Factory videoFrameProcessorFactory,
FallbackListener fallbackListener,
DebugViewProvider debugViewProvider) {
DebugViewProvider debugViewProvider,
@Nullable LogSessionId logSessionId) {
this.sequenceIndex = sequenceIndex;
this.firstEditedMediaItem = composition.sequences.get(sequenceIndex).editedMediaItems.get(0);
this.composition = composition;
@ -573,6 +578,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
this.videoFrameProcessorFactory = videoFrameProcessorFactory;
this.fallbackListener = fallbackListener;
this.debugViewProvider = debugViewProvider;
this.logSessionId = logSessionId;
}
@Override
@ -696,7 +702,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
audioMixerFactory,
encoderFactory,
muxerWrapper,
fallbackListener));
fallbackListener,
logSessionId));
} else {
Format firstFormat;
if (MimeTypes.isVideo(assetLoaderOutputFormat.sampleMimeType)) {
@ -738,7 +745,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
videoSampleTimestampOffsetUs,
/* hasMultipleInputs= */ assetLoaderInputTracker.hasMultipleConcurrentVideoTracks(),
portraitEncodingEnabled,
maxFramesInEncoder));
maxFramesInEncoder,
logSessionId));
}
}

View File

@ -29,8 +29,11 @@ import android.content.ContentResolver;
import android.content.Context;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.metrics.LogSessionId;
import android.util.Pair;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.media3.common.C;
import androidx.media3.common.ColorInfo;
import androidx.media3.common.Effect;
@ -375,4 +378,23 @@ public final class TransformerUtil {
return null;
}
}
/** Utility for setting LogSessionId on MediaFormat (API 35+). */
@RequiresApi(35)
public static final class Api35 {
private Api35() {}
/**
* Sets the log session ID to the provided {@link MediaFormat}.
*
* @param mediaFormat The {@link MediaFormat} to set the log session ID on.
* @param logSessionId The {@link LogSessionId} to set.
*/
public static void setLogSessionIdToMediaCodecFormat(
MediaFormat mediaFormat, LogSessionId logSessionId) {
if (!logSessionId.equals(LogSessionId.LOG_SESSION_ID_NONE)) {
mediaFormat.setString("log-session-id", logSessionId.getStringId());
}
}
}
}

View File

@ -37,6 +37,7 @@ import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import android.content.Context;
import android.graphics.Bitmap;
import android.media.MediaCodec;
import android.media.metrics.LogSessionId;
import android.util.Pair;
import android.view.Surface;
import androidx.annotation.Nullable;
@ -99,7 +100,8 @@ import org.checkerframework.dataflow.qual.Pure;
long initialTimestampOffsetUs,
boolean hasMultipleInputs,
boolean portraitEncodingEnabled,
int maxFramesInEncoder)
int maxFramesInEncoder,
@Nullable LogSessionId logSessionId)
throws ExportException {
// TODO: b/278259383 - Consider delaying configuration of VideoSampleExporter to use the decoder
// output format instead of the extractor output format, to match AudioSampleExporter behavior.
@ -137,7 +139,8 @@ import org.checkerframework.dataflow.qual.Pure;
portraitEncodingEnabled,
muxerWrapper.getSupportedSampleMimeTypes(C.TRACK_TYPE_VIDEO),
transformationRequest,
fallbackListener);
fallbackListener,
logSessionId);
encoderOutputBuffer =
new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED);
@ -252,6 +255,7 @@ import org.checkerframework.dataflow.qual.Pure;
private final FallbackListener fallbackListener;
private final String requestedOutputMimeType;
private final @Composition.HdrMode int hdrModeAfterFallback;
@Nullable private final LogSessionId logSessionId;
private @MonotonicNonNull SurfaceInfo encoderSurfaceInfo;
@ -265,7 +269,8 @@ import org.checkerframework.dataflow.qual.Pure;
boolean portraitEncodingEnabled,
List<String> muxerSupportedMimeTypes,
TransformationRequest transformationRequest,
FallbackListener fallbackListener) {
FallbackListener fallbackListener,
@Nullable LogSessionId logSessionId) {
checkArgument(inputFormat.colorInfo != null);
this.encoderFactory = encoderFactory;
this.inputFormat = inputFormat;
@ -273,6 +278,7 @@ import org.checkerframework.dataflow.qual.Pure;
this.muxerSupportedMimeTypes = muxerSupportedMimeTypes;
this.transformationRequest = transformationRequest;
this.fallbackListener = fallbackListener;
this.logSessionId = logSessionId;
Pair<String, Integer> outputMimeTypeAndHdrModeAfterFallback =
getRequestedOutputMimeTypeAndHdrModeAfterFallback(inputFormat, transformationRequest);
requestedOutputMimeType = outputMimeTypeAndHdrModeAfterFallback.first;
@ -347,7 +353,8 @@ import org.checkerframework.dataflow.qual.Pure;
.setSampleMimeType(
findSupportedMimeTypeForEncoderAndMuxer(
requestedEncoderFormat, muxerSupportedMimeTypes))
.build());
.build(),
logSessionId);
Format actualEncoderFormat = encoder.getConfigurationFormat();

View File

@ -111,7 +111,7 @@ public class DefaultEncoderFactoryTest {
Format actualVideoFormat =
new DefaultEncoderFactory.Builder(context)
.build()
.createForVideoEncoding(requestedVideoFormat)
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null)
.getConfigurationFormat();
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
@ -129,7 +129,9 @@ public class DefaultEncoderFactoryTest {
ExportException exportException =
assertThrows(
ExportException.class,
() -> encoderFactory.createForVideoEncoding(requestedVideoFormat));
() ->
encoderFactory.createForVideoEncoding(
requestedVideoFormat, /* logSessionId= */ null));
assertThat(exportException.errorCode).isEqualTo(ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED);
}
@ -140,7 +142,7 @@ public class DefaultEncoderFactoryTest {
Format actualVideoFormat =
new DefaultEncoderFactory.Builder(context)
.build()
.createForVideoEncoding(requestedVideoFormat)
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null)
.getConfigurationFormat();
assertThat(actualVideoFormat.width).isEqualTo(1920);
@ -158,7 +160,7 @@ public class DefaultEncoderFactoryTest {
new DefaultEncoderFactory.Builder(context)
.setRequestedVideoEncoderSettings(VideoEncoderSettings.DEFAULT)
.build()
.createForVideoEncoding(requestedVideoFormat)
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null)
.getConfigurationFormat();
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
@ -175,7 +177,7 @@ public class DefaultEncoderFactoryTest {
Format actualVideoFormat =
new DefaultEncoderFactory.Builder(context)
.build()
.createForVideoEncoding(requestedVideoFormat)
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null)
.getConfigurationFormat();
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
@ -198,7 +200,7 @@ public class DefaultEncoderFactoryTest {
.setRequestedVideoEncoderSettings(
new VideoEncoderSettings.Builder().setBitrate(10_000_000).build())
.build()
.createForVideoEncoding(requestedVideoFormat)
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null)
.getConfigurationFormat();
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
@ -216,7 +218,7 @@ public class DefaultEncoderFactoryTest {
Codec videoEncoder =
new DefaultEncoderFactory.Builder(context)
.build()
.createForVideoEncoding(requestedVideoFormat);
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null);
assertThat(videoEncoder).isInstanceOf(DefaultCodec.class);
MediaFormat configurationMediaFormat =
@ -241,7 +243,7 @@ public class DefaultEncoderFactoryTest {
.setEncoderPerformanceParameters(/* operatingRate= */ -1, /* priority= */ 1)
.build())
.build()
.createForVideoEncoding(requestedVideoFormat);
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null);
assertThat(videoEncoder).isInstanceOf(DefaultCodec.class);
MediaFormat configurationMediaFormat =
@ -266,7 +268,7 @@ public class DefaultEncoderFactoryTest {
/* operatingRate= */ VideoEncoderSettings.RATE_UNSET, /* priority= */ 1)
.build())
.build()
.createForVideoEncoding(requestedVideoFormat);
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null);
assertThat(videoEncoder).isInstanceOf(DefaultCodec.class);
MediaFormat configurationMediaFormat =
@ -290,7 +292,7 @@ public class DefaultEncoderFactoryTest {
VideoEncoderSettings.RATE_UNSET, VideoEncoderSettings.RATE_UNSET)
.build())
.build()
.createForVideoEncoding(requestedVideoFormat);
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null);
assertThat(videoEncoder).isInstanceOf(DefaultCodec.class);
MediaFormat configurationMediaFormat =
@ -309,7 +311,7 @@ public class DefaultEncoderFactoryTest {
.setRequestedVideoEncoderSettings(
new VideoEncoderSettings.Builder().setRepeatPreviousFrameIntervalUs(33_333).build())
.build()
.createForVideoEncoding(requestedVideoFormat);
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null);
assertThat(
videoEncoder
@ -326,7 +328,7 @@ public class DefaultEncoderFactoryTest {
DefaultCodec videoEncoder =
new DefaultEncoderFactory.Builder(context)
.build()
.createForVideoEncoding(requestedVideoFormat);
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null);
assertThat(
videoEncoder
@ -344,7 +346,7 @@ public class DefaultEncoderFactoryTest {
new DefaultEncoderFactory.Builder(context)
.setVideoEncoderSelector((mimeType) -> ImmutableList.of())
.build()
.createForVideoEncoding(requestedVideoFormat));
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null));
}
@Test
@ -357,7 +359,7 @@ public class DefaultEncoderFactoryTest {
new DefaultEncoderFactory.Builder(context)
.setEnableFallback(true)
.build()
.createForAudioEncoding(requestedAudioFormat);
.createForAudioEncoding(requestedAudioFormat, /* logSessionId= */ null);
Format inputFormat = codec.getInputFormat();
Format configurationFormat = codec.getConfigurationFormat();

View File

@ -173,7 +173,12 @@ public class ExoPlayerAssetLoaderTest {
Codec.DecoderFactory decoderFactory = new DefaultDecoderFactory.Builder(context).build();
EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(MediaItem.fromUri(uri)).build();
return new ExoPlayerAssetLoader.Factory(
context, decoderFactory, clock, /* mediaSourceFactory= */ null, trackSelectorFactory)
context,
decoderFactory,
clock,
/* mediaSourceFactory= */ null,
trackSelectorFactory,
/* logSessionId= */ null)
.createAssetLoader(
editedMediaItem,
Looper.myLooper(),

View File

@ -25,8 +25,10 @@ import static org.mockito.Mockito.when;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecProfileLevel;
import android.media.MediaFormat;
import android.media.metrics.LogSessionId;
import android.net.Uri;
import android.os.Looper;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.ColorInfo;
import androidx.media3.common.Format;
@ -74,7 +76,8 @@ public final class VideoEncoderWrapperTest {
/* portraitEncodingEnabled= */ false,
/* muxerSupportedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264),
emptyTransformationRequest,
fallbackListener);
fallbackListener,
/* logSessionId= */ null);
@Before
public void setUp() {
@ -184,12 +187,12 @@ public final class VideoEncoderWrapperTest {
}
@Override
public Codec createForAudioEncoding(Format format) {
public Codec createForAudioEncoding(Format format, @Nullable LogSessionId logSessionId) {
throw new UnsupportedOperationException();
}
@Override
public Codec createForVideoEncoding(Format format) {
public Codec createForVideoEncoding(Format format, @Nullable LogSessionId logSessionId) {
Codec mockEncoder = mock(Codec.class);
if (fallbackWidth != C.LENGTH_UNSET) {
format = format.buildUpon().setWidth(fallbackWidth).build();