mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Add codec names to TransformationResult
PiperOrigin-RevId: 494736085
This commit is contained in:
parent
36e52691bb
commit
6c7e892d2f
@ -144,6 +144,18 @@ public class TransformationTestResult {
|
||||
if (transformationResult.videoFrameCount > 0) {
|
||||
jsonObject.put("videoFrameCount", transformationResult.videoFrameCount);
|
||||
}
|
||||
if (transformationResult.audioDecoderName != null) {
|
||||
jsonObject.put("audioDecoderName", transformationResult.audioDecoderName);
|
||||
}
|
||||
if (transformationResult.videoDecoderName != null) {
|
||||
jsonObject.put("videoDecoderName", transformationResult.videoDecoderName);
|
||||
}
|
||||
if (transformationResult.audioEncoderName != null) {
|
||||
jsonObject.put("audioEncoderName", transformationResult.audioEncoderName);
|
||||
}
|
||||
if (transformationResult.videoEncoderName != null) {
|
||||
jsonObject.put("videoEncoderName", transformationResult.videoEncoderName);
|
||||
}
|
||||
if (throughputFps != C.RATE_UNSET) {
|
||||
jsonObject.put("throughputFps", throughputFps);
|
||||
}
|
||||
|
@ -23,9 +23,7 @@ import android.net.ConnectivityManager;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.view.Surface;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.util.Log;
|
||||
import androidx.media3.common.util.SystemClock;
|
||||
@ -155,7 +153,8 @@ public class TransformerAndroidTestRunner {
|
||||
}
|
||||
|
||||
private final Context context;
|
||||
private final CodecNameForwardingCodecFactory transformerCodecFactory;
|
||||
private final CapturingDecoderFactory decoderFactory;
|
||||
private final CapturingEncoderFactory encoderFactory;
|
||||
private final Transformer transformer;
|
||||
private final int timeoutSeconds;
|
||||
private final boolean requestCalculateSsim;
|
||||
@ -170,13 +169,13 @@ public class TransformerAndroidTestRunner {
|
||||
boolean suppressAnalysisExceptions,
|
||||
@Nullable Map<String, Object> inputValues) {
|
||||
this.context = context;
|
||||
this.transformerCodecFactory =
|
||||
new CodecNameForwardingCodecFactory(transformer.decoderFactory, transformer.encoderFactory);
|
||||
this.decoderFactory = new CapturingDecoderFactory(transformer.decoderFactory);
|
||||
this.encoderFactory = new CapturingEncoderFactory(transformer.encoderFactory);
|
||||
this.transformer =
|
||||
transformer
|
||||
.buildUpon()
|
||||
.setDecoderFactory(transformerCodecFactory)
|
||||
.setEncoderFactory(transformerCodecFactory)
|
||||
.setDecoderFactory(decoderFactory)
|
||||
.setEncoderFactory(encoderFactory)
|
||||
.build();
|
||||
this.timeoutSeconds = timeoutSeconds;
|
||||
this.requestCalculateSsim = requestCalculateSsim;
|
||||
@ -209,7 +208,7 @@ public class TransformerAndroidTestRunner {
|
||||
resultJson.put("exception", AndroidTestUtil.exceptionAsJsonObject(e));
|
||||
throw e;
|
||||
} finally {
|
||||
resultJson.put("codecDetails", transformerCodecFactory.getCodecNamesAsJsonObject());
|
||||
resultJson.put("codecDetails", getCodecNamesAsJsonObject());
|
||||
AndroidTestUtil.writeTestSummaryToFile(context, testId, resultJson);
|
||||
}
|
||||
}
|
||||
@ -402,87 +401,20 @@ public class TransformerAndroidTestRunner {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Codec.EncoderFactory} that forwards all methods to another encoder factory, whilst
|
||||
* providing visibility into the names of last codecs created by it.
|
||||
*/
|
||||
private static class CodecNameForwardingCodecFactory
|
||||
implements Codec.DecoderFactory, Codec.EncoderFactory {
|
||||
|
||||
/** The name of the last audio {@link Codec decoder} created. */
|
||||
@Nullable public String audioDecoderName;
|
||||
/** The name of the last video {@link Codec decoder} created. */
|
||||
@Nullable public String videoDecoderName;
|
||||
/** The name of the last audio {@link Codec encoder} created. */
|
||||
@Nullable public String audioEncoderName;
|
||||
/** The name of the last video {@link Codec encoder} created. */
|
||||
@Nullable public String videoEncoderName;
|
||||
|
||||
private final Codec.DecoderFactory decoderFactory;
|
||||
private final Codec.EncoderFactory encoderFactory;
|
||||
|
||||
public CodecNameForwardingCodecFactory(
|
||||
Codec.DecoderFactory decoderFactory, Codec.EncoderFactory encoderFactory) {
|
||||
this.decoderFactory = decoderFactory;
|
||||
this.encoderFactory = encoderFactory;
|
||||
private JSONObject getCodecNamesAsJsonObject() throws JSONException {
|
||||
JSONObject detailsJson = new JSONObject();
|
||||
if (decoderFactory.getAudioDecoderName() != null) {
|
||||
detailsJson.put("audioDecoderName", decoderFactory.getAudioDecoderName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Codec createForAudioDecoding(Format format) throws TransformationException {
|
||||
Codec audioDecoder = decoderFactory.createForAudioDecoding(format);
|
||||
audioDecoderName = audioDecoder.getName();
|
||||
return audioDecoder;
|
||||
if (decoderFactory.getVideoDecoderName() != null) {
|
||||
detailsJson.put("videoDecoderName", decoderFactory.getVideoDecoderName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Codec createForVideoDecoding(
|
||||
Format format, Surface outputSurface, boolean requestSdrToneMapping)
|
||||
throws TransformationException {
|
||||
Codec videoDecoder =
|
||||
decoderFactory.createForVideoDecoding(format, outputSurface, requestSdrToneMapping);
|
||||
videoDecoderName = videoDecoder.getName();
|
||||
return videoDecoder;
|
||||
if (encoderFactory.getAudioEncoderName() != null) {
|
||||
detailsJson.put("audioEncoderName", encoderFactory.getAudioEncoderName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Codec createForAudioEncoding(Format format) throws TransformationException {
|
||||
Codec audioEncoder = encoderFactory.createForAudioEncoding(format);
|
||||
audioEncoderName = audioEncoder.getName();
|
||||
return audioEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Codec createForVideoEncoding(Format format) throws TransformationException {
|
||||
Codec videoEncoder = encoderFactory.createForVideoEncoding(format);
|
||||
videoEncoderName = videoEncoder.getName();
|
||||
return videoEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean audioNeedsEncoding() {
|
||||
return encoderFactory.audioNeedsEncoding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean videoNeedsEncoding() {
|
||||
return encoderFactory.videoNeedsEncoding();
|
||||
}
|
||||
|
||||
public JSONObject getCodecNamesAsJsonObject() throws JSONException {
|
||||
JSONObject detailsJson = new JSONObject();
|
||||
if (audioDecoderName != null) {
|
||||
detailsJson.put("audioDecoderName", audioDecoderName);
|
||||
}
|
||||
if (videoDecoderName != null) {
|
||||
detailsJson.put("videoDecoderName", videoDecoderName);
|
||||
}
|
||||
if (audioEncoderName != null) {
|
||||
detailsJson.put("audioEncoderName", audioEncoderName);
|
||||
}
|
||||
if (videoEncoderName != null) {
|
||||
detailsJson.put("videoEncoderName", videoEncoderName);
|
||||
}
|
||||
return detailsJson;
|
||||
if (encoderFactory.getVideoEncoderName() != null) {
|
||||
detailsJson.put("videoEncoderName", encoderFactory.getVideoEncoderName());
|
||||
}
|
||||
return detailsJson;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.media3.transformer;
|
||||
|
||||
import android.view.Surface;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.Format;
|
||||
|
||||
/** A forwarding {@link Codec.DecoderFactory} that captures details about the codecs created. */
|
||||
/* package */ final class CapturingDecoderFactory implements Codec.DecoderFactory {
|
||||
private final Codec.DecoderFactory decoderFactory;
|
||||
|
||||
@Nullable private String audioDecoderName;
|
||||
@Nullable private String videoDecoderName;
|
||||
|
||||
public CapturingDecoderFactory(Codec.DecoderFactory decoderFactory) {
|
||||
this.decoderFactory = decoderFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Codec createForAudioDecoding(Format format) throws TransformationException {
|
||||
Codec audioDecoder = decoderFactory.createForAudioDecoding(format);
|
||||
audioDecoderName = audioDecoder.getName();
|
||||
return audioDecoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Codec createForVideoDecoding(
|
||||
Format format, Surface outputSurface, boolean requestSdrToneMapping)
|
||||
throws TransformationException {
|
||||
Codec videoDecoder =
|
||||
decoderFactory.createForVideoDecoding(format, outputSurface, requestSdrToneMapping);
|
||||
videoDecoderName = videoDecoder.getName();
|
||||
return videoDecoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the last audio {@linkplain Codec decoder} created, or {@code null} if none
|
||||
* were created.
|
||||
*/
|
||||
@Nullable
|
||||
public String getAudioDecoderName() {
|
||||
return audioDecoderName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the last video {@linkplain Codec decoder} created, or {@code null} if none
|
||||
* were created.
|
||||
*/
|
||||
@Nullable
|
||||
public String getVideoDecoderName() {
|
||||
return videoDecoderName;
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.media3.transformer;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.Format;
|
||||
|
||||
/** A forwarding {@link Codec.EncoderFactory} that captures details about the codecs created. */
|
||||
/* package */ final class CapturingEncoderFactory implements Codec.EncoderFactory {
|
||||
private final Codec.EncoderFactory encoderFactory;
|
||||
|
||||
@Nullable private String audioEncoderName;
|
||||
@Nullable private String videoEncoderName;
|
||||
|
||||
public CapturingEncoderFactory(Codec.EncoderFactory encoderFactory) {
|
||||
this.encoderFactory = encoderFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Codec createForAudioEncoding(Format format) throws TransformationException {
|
||||
Codec audioEncoder = encoderFactory.createForAudioEncoding(format);
|
||||
audioEncoderName = audioEncoder.getName();
|
||||
return audioEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Codec createForVideoEncoding(Format format) throws TransformationException {
|
||||
Codec videoEncoder = encoderFactory.createForVideoEncoding(format);
|
||||
videoEncoderName = videoEncoder.getName();
|
||||
return videoEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean audioNeedsEncoding() {
|
||||
return encoderFactory.audioNeedsEncoding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean videoNeedsEncoding() {
|
||||
return encoderFactory.videoNeedsEncoding();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the last audio {@linkplain Codec encoder} created, or {@code null} if none
|
||||
* were created.
|
||||
*/
|
||||
@Nullable
|
||||
public String getAudioEncoderName() {
|
||||
return audioEncoderName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the last video {@linkplain Codec encoder} created, or {@code null} if none
|
||||
* were created.
|
||||
*/
|
||||
@Nullable
|
||||
public String getVideoEncoderName() {
|
||||
return videoEncoderName;
|
||||
}
|
||||
}
|
@ -21,8 +21,9 @@ import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import java.util.Objects;
|
||||
|
||||
/** Information about the result of a successful transformation. */
|
||||
/** Information about the result of a transformation. */
|
||||
@UnstableApi
|
||||
public final class TransformationResult {
|
||||
|
||||
@ -33,6 +34,10 @@ public final class TransformationResult {
|
||||
private int averageAudioBitrate;
|
||||
private int averageVideoBitrate;
|
||||
private int videoFrameCount;
|
||||
@Nullable private String audioDecoderName;
|
||||
@Nullable private String audioEncoderName;
|
||||
@Nullable private String videoDecoderName;
|
||||
@Nullable private String videoEncoderName;
|
||||
|
||||
public Builder() {
|
||||
durationMs = C.TIME_UNSET;
|
||||
@ -101,9 +106,45 @@ public final class TransformationResult {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Sets the name of the audio decoder used. */
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setAudioDecoderName(@Nullable String audioDecoderName) {
|
||||
this.audioDecoderName = audioDecoderName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Sets the name of the audio encoder used. */
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setAudioEncoderName(@Nullable String audioEncoderName) {
|
||||
this.audioEncoderName = audioEncoderName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Sets the name of the video decoder used. */
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setVideoDecoderName(@Nullable String videoDecoderName) {
|
||||
this.videoDecoderName = videoDecoderName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Sets the name of the video encoder used. */
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setVideoEncoderName(@Nullable String videoEncoderName) {
|
||||
this.videoEncoderName = videoEncoderName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TransformationResult build() {
|
||||
return new TransformationResult(
|
||||
durationMs, fileSizeBytes, averageAudioBitrate, averageVideoBitrate, videoFrameCount);
|
||||
durationMs,
|
||||
fileSizeBytes,
|
||||
averageAudioBitrate,
|
||||
averageVideoBitrate,
|
||||
videoFrameCount,
|
||||
audioDecoderName,
|
||||
audioEncoderName,
|
||||
videoDecoderName,
|
||||
videoEncoderName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,17 +163,34 @@ public final class TransformationResult {
|
||||
/** The number of video frames. */
|
||||
public final int videoFrameCount;
|
||||
|
||||
/** The name of the audio decoder used, or {@code null} if none were used. */
|
||||
@Nullable public final String audioDecoderName;
|
||||
/** The name of the audio encoder used, or {@code null} if none were used. */
|
||||
@Nullable public final String audioEncoderName;
|
||||
/** The name of the video decoder used, or {@code null} if none were used. */
|
||||
@Nullable public final String videoDecoderName;
|
||||
/** The name of the video encoder used, or {@code null} if none were used. */
|
||||
@Nullable public final String videoEncoderName;
|
||||
|
||||
private TransformationResult(
|
||||
long durationMs,
|
||||
long fileSizeBytes,
|
||||
int averageAudioBitrate,
|
||||
int averageVideoBitrate,
|
||||
int videoFrameCount) {
|
||||
int videoFrameCount,
|
||||
@Nullable String audioDecoderName,
|
||||
@Nullable String audioEncoderName,
|
||||
@Nullable String videoDecoderName,
|
||||
@Nullable String videoEncoderName) {
|
||||
this.durationMs = durationMs;
|
||||
this.fileSizeBytes = fileSizeBytes;
|
||||
this.averageAudioBitrate = averageAudioBitrate;
|
||||
this.averageVideoBitrate = averageVideoBitrate;
|
||||
this.videoFrameCount = videoFrameCount;
|
||||
this.audioDecoderName = audioDecoderName;
|
||||
this.audioEncoderName = audioEncoderName;
|
||||
this.videoDecoderName = videoDecoderName;
|
||||
this.videoEncoderName = videoEncoderName;
|
||||
}
|
||||
|
||||
public Builder buildUpon() {
|
||||
@ -141,7 +199,11 @@ public final class TransformationResult {
|
||||
.setFileSizeBytes(fileSizeBytes)
|
||||
.setAverageAudioBitrate(averageAudioBitrate)
|
||||
.setAverageVideoBitrate(averageVideoBitrate)
|
||||
.setVideoFrameCount(videoFrameCount);
|
||||
.setVideoFrameCount(videoFrameCount)
|
||||
.setAudioDecoderName(audioDecoderName)
|
||||
.setAudioEncoderName(audioEncoderName)
|
||||
.setVideoDecoderName(videoDecoderName)
|
||||
.setVideoEncoderName(videoEncoderName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -157,7 +219,11 @@ public final class TransformationResult {
|
||||
&& fileSizeBytes == result.fileSizeBytes
|
||||
&& averageAudioBitrate == result.averageAudioBitrate
|
||||
&& averageVideoBitrate == result.averageVideoBitrate
|
||||
&& videoFrameCount == result.videoFrameCount;
|
||||
&& videoFrameCount == result.videoFrameCount
|
||||
&& Objects.equals(audioDecoderName, result.audioDecoderName)
|
||||
&& Objects.equals(audioEncoderName, result.audioEncoderName)
|
||||
&& Objects.equals(videoDecoderName, result.videoDecoderName)
|
||||
&& Objects.equals(videoEncoderName, result.videoEncoderName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -167,6 +233,10 @@ public final class TransformationResult {
|
||||
result = 31 * result + averageAudioBitrate;
|
||||
result = 31 * result + averageVideoBitrate;
|
||||
result = 31 * result + videoFrameCount;
|
||||
result = 31 * result + Objects.hashCode(audioDecoderName);
|
||||
result = 31 * result + Objects.hashCode(audioEncoderName);
|
||||
result = 31 * result + Objects.hashCode(videoDecoderName);
|
||||
result = 31 * result + Objects.hashCode(videoEncoderName);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -94,8 +94,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
private final ImmutableList<AudioProcessor> audioProcessors;
|
||||
private final ImmutableList<Effect> videoEffects;
|
||||
private final boolean forceSilentAudio;
|
||||
private final Codec.DecoderFactory decoderFactory;
|
||||
private final Codec.EncoderFactory encoderFactory;
|
||||
private final CapturingDecoderFactory decoderFactory;
|
||||
private final CapturingEncoderFactory encoderFactory;
|
||||
private final FrameProcessor.Factory frameProcessorFactory;
|
||||
private final Listener listener;
|
||||
private final DebugViewProvider debugViewProvider;
|
||||
@ -141,8 +141,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
this.audioProcessors = audioProcessors;
|
||||
this.videoEffects = videoEffects;
|
||||
this.forceSilentAudio = forceSilentAudio;
|
||||
this.decoderFactory = decoderFactory;
|
||||
this.encoderFactory = encoderFactory;
|
||||
this.decoderFactory = new CapturingDecoderFactory(decoderFactory);
|
||||
this.encoderFactory = new CapturingEncoderFactory(encoderFactory);
|
||||
this.frameProcessorFactory = frameProcessorFactory;
|
||||
this.listener = listener;
|
||||
this.debugViewProvider = debugViewProvider;
|
||||
@ -316,6 +316,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
for (int i = 0; i < samplePipelines.size(); i++) {
|
||||
samplePipelines.get(i).release();
|
||||
}
|
||||
|
||||
// TODO(b/250564186): Create TransformationResult on END_REASON_ERROR as well.
|
||||
if (endReason == END_REASON_COMPLETED) {
|
||||
transformationResult =
|
||||
new TransformationResult.Builder()
|
||||
@ -326,6 +328,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
muxerWrapper.getTrackAverageBitrate(C.TRACK_TYPE_VIDEO))
|
||||
.setVideoFrameCount(muxerWrapper.getTrackSampleCount(C.TRACK_TYPE_VIDEO))
|
||||
.setFileSizeBytes(muxerWrapper.getCurrentOutputSizeBytes())
|
||||
.setAudioDecoderName(decoderFactory.getAudioDecoderName())
|
||||
.setAudioEncoderName(encoderFactory.getAudioEncoderName())
|
||||
.setVideoDecoderName(decoderFactory.getVideoDecoderName())
|
||||
.setVideoEncoderName(encoderFactory.getVideoEncoderName())
|
||||
.build();
|
||||
}
|
||||
} finally {
|
||||
|
Loading…
x
Reference in New Issue
Block a user