Adapt TransformationResult for multi-asset

PiperOrigin-RevId: 506898392
This commit is contained in:
kimvde 2023-02-03 15:32:32 +00:00 committed by microkatz
parent 937fcf9c4a
commit 90de454e1e
5 changed files with 113 additions and 42 deletions

View file

@ -26,6 +26,7 @@ import android.os.Build;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
import com.google.android.exoplayer2.util.Log;
@ -36,6 +37,7 @@ import com.google.common.collect.ImmutableList;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@ -501,6 +503,31 @@ public final class AndroidTestUtil {
.put("fingerprint", Build.FINGERPRINT);
}
/**
* Creates a {@link JSONArray} from {@link TransformationResult.ProcessedInput processed inputs}.
*
* @param processedInputs The list of {@link TransformationResult.ProcessedInput} instances.
* @return A {@link JSONArray} containing {@link JSONObject} instances representing the {@link
* TransformationResult.ProcessedInput} instances.
*/
public static JSONArray processedInputsAsJsonArray(
ImmutableList<TransformationResult.ProcessedInput> processedInputs) throws JSONException {
JSONArray jsonArray = new JSONArray();
for (int i = 0; i < processedInputs.size(); i++) {
TransformationResult.ProcessedInput processedInput = processedInputs.get(i);
JSONObject jsonObject = new JSONObject();
@Nullable
MediaItem.LocalConfiguration localConfiguration = processedInput.mediaItem.localConfiguration;
if (localConfiguration != null) {
jsonObject.put("mediaItemUri", localConfiguration.uri);
}
jsonObject.putOpt("audioDecoderName", processedInput.audioDecoderName);
jsonObject.putOpt("videoDecoderName", processedInput.videoDecoderName);
jsonArray.put(jsonObject);
}
return jsonArray;
}
/**
* Creates a {@link JSONObject} from the {@link Exception}.
*

View file

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.transformer;
import static com.google.android.exoplayer2.transformer.AndroidTestUtil.exceptionAsJsonObject;
import static com.google.android.exoplayer2.transformer.AndroidTestUtil.processedInputsAsJsonArray;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
@ -156,19 +157,22 @@ public class TransformationTestResult {
public JSONObject asJsonObject() throws JSONException {
JSONObject jsonObject =
new JSONObject()
.putOpt("audioDecoderName", transformationResult.audioDecoderName)
.putOpt("audioEncoderName", transformationResult.audioEncoderName)
.putOpt(
"fallbackDetails", fallbackDetails != null ? fallbackDetails.asJsonObject() : null)
.putOpt("filePath", filePath)
.putOpt("colorInfo", transformationResult.colorInfo)
.putOpt("videoDecoderName", transformationResult.videoDecoderName)
.putOpt("videoEncoderName", transformationResult.videoEncoderName)
.putOpt(
"testException",
exceptionAsJsonObject(transformationResult.transformationException))
.putOpt("analysisException", exceptionAsJsonObject(analysisException));
if (!transformationResult.processedInputs.isEmpty()) {
jsonObject.put(
"processedInputs", processedInputsAsJsonArray(transformationResult.processedInputs));
}
if (transformationResult.averageAudioBitrate != C.RATE_UNSET_INT) {
jsonObject.put("averageAudioBitrate", transformationResult.averageAudioBitrate);
}

View file

@ -32,6 +32,7 @@ import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.HandlerWrapper;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.video.ColorInfo;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.List;
@ -52,10 +53,12 @@ import java.util.concurrent.atomic.AtomicLong;
private final Listener compositeAssetLoaderListener;
private final Map<Integer, SampleConsumer> sampleConsumersByTrackType;
private final Map<Integer, OnMediaItemChangedListener> mediaItemChangedListenersByTrackType;
private final ImmutableList.Builder<TransformationResult.ProcessedInput> processedInputsBuilder;
private final AtomicLong totalDurationUs;
private final AtomicInteger nonEndedTracks;
private AssetLoader currentAssetLoader;
private int processedInputsSize;
private volatile long currentDurationUs;
@ -72,6 +75,7 @@ import java.util.concurrent.atomic.AtomicLong;
handler = clock.createHandler(looper, /* callback= */ null);
sampleConsumersByTrackType = new HashMap<>();
mediaItemChangedListenersByTrackType = new HashMap<>();
processedInputsBuilder = new ImmutableList.Builder<>();
totalDurationUs = new AtomicLong();
nonEndedTracks = new AtomicInteger();
// It's safe to use "this" because we don't start the AssetLoader before exiting the
@ -105,10 +109,18 @@ import java.util.concurrent.atomic.AtomicLong;
@Override
public ImmutableMap<Integer, String> getDecoderNames() {
// TODO(b/252537210): update TransformationResult to contain all the decoders used.
return currentAssetLoader.getDecoderNames();
}
/**
* Returns the partially or entirely {@linkplain TransformationResult.ProcessedInput processed
* inputs}.
*/
public ImmutableList<TransformationResult.ProcessedInput> getProcessedInputs() {
addCurrentProcessedInput();
return processedInputsBuilder.build();
}
@Override
public void release() {
currentAssetLoader.release();
@ -193,6 +205,18 @@ import java.util.concurrent.atomic.AtomicLong;
compositeAssetLoaderListener.onError(exception);
}
private void addCurrentProcessedInput() {
int currentMediaItemIndex = this.currentMediaItemIndex.get();
if (currentMediaItemIndex >= processedInputsSize) {
MediaItem mediaItem = editedMediaItems.get(currentMediaItemIndex).mediaItem;
ImmutableMap<Integer, String> decoders = currentAssetLoader.getDecoderNames();
processedInputsBuilder.add(
new TransformationResult.ProcessedInput(
mediaItem, decoders.get(C.TRACK_TYPE_AUDIO), decoders.get(C.TRACK_TYPE_VIDEO)));
processedInputsSize++;
}
}
private final class SampleConsumerWrapper implements SampleConsumer {
private final SampleConsumer sampleConsumer;
@ -270,6 +294,7 @@ import java.util.concurrent.atomic.AtomicLong;
totalDurationUs.addAndGet(currentDurationUs);
handler.post(
() -> {
addCurrentProcessedInput();
currentAssetLoader.release();
EditedMediaItem editedMediaItem =
editedMediaItems.get(currentMediaItemIndex.incrementAndGet());

View file

@ -20,32 +20,36 @@ import static com.google.android.exoplayer2.util.Assertions.checkArgument;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.video.ColorInfo;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** Information about the result of a transformation. */
public final class TransformationResult {
/** A builder for {@link TransformationResult} instances. */
public static final class Builder {
private ImmutableList<ProcessedInput> processedInputs;
private long durationMs;
private long fileSizeBytes;
private int averageAudioBitrate;
private int channelCount;
private @C.PcmEncoding int pcmEncoding;
private int sampleRate;
@Nullable private String audioDecoderName;
@Nullable private String audioEncoderName;
private int averageVideoBitrate;
@Nullable ColorInfo colorInfo;
private int height;
private int width;
private int videoFrameCount;
@Nullable private String videoDecoderName;
@Nullable private String videoEncoderName;
@Nullable private TransformationException transformationException;
/** Creates a builder. */
public Builder() {
processedInputs = ImmutableList.of();
durationMs = C.TIME_UNSET;
fileSizeBytes = C.LENGTH_UNSET;
averageAudioBitrate = C.RATE_UNSET_INT;
@ -57,6 +61,13 @@ public final class TransformationResult {
width = C.LENGTH_UNSET;
}
/** Sets the {@linkplain ProcessedInput processed inputs}. */
@CanIgnoreReturnValue
public Builder setProcessedInputs(ImmutableList<ProcessedInput> processedInputs) {
this.processedInputs = processedInputs;
return this;
}
/**
* Sets the duration of the output in milliseconds.
*
@ -124,13 +135,6 @@ 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) {
@ -193,13 +197,6 @@ public final class TransformationResult {
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) {
@ -215,27 +212,54 @@ public final class TransformationResult {
return this;
}
/** Builds a {@link TransformationResult} instance. */
public TransformationResult build() {
return new TransformationResult(
processedInputs,
durationMs,
fileSizeBytes,
averageAudioBitrate,
channelCount,
pcmEncoding,
sampleRate,
audioDecoderName,
audioEncoderName,
averageVideoBitrate,
colorInfo,
height,
width,
videoFrameCount,
videoDecoderName,
videoEncoderName,
transformationException);
}
}
/** An input entirely or partially processed. */
public static final class ProcessedInput {
/** The processed {@link MediaItem}. */
public final MediaItem mediaItem;
/**
* The name of the audio decoder used to process {@code mediaItem}. This field is {@code null}
* if no audio decoder was used.
*/
public final @MonotonicNonNull String audioDecoderName;
/**
* The name of the video decoder used to process {@code mediaItem}. This field is {@code null}
* if no video decoder was used.
*/
public final @MonotonicNonNull String videoDecoderName;
/** Creates an instance. */
public ProcessedInput(
MediaItem mediaItem, @Nullable String audioDecoderName, @Nullable String videoDecoderName) {
this.mediaItem = mediaItem;
this.audioDecoderName = audioDecoderName;
this.videoDecoderName = videoDecoderName;
}
}
/** The list of {@linkplain ProcessedInput processed inputs}. */
public final ImmutableList<ProcessedInput> processedInputs;
/** The duration of the file in milliseconds, or {@link C#TIME_UNSET} if unset or unknown. */
public final long durationMs;
/** The size of the file in bytes, or {@link C#LENGTH_UNSET} if unset or unknown. */
@ -247,12 +271,10 @@ public final class TransformationResult {
public final int averageAudioBitrate;
/** The channel count of the audio, or {@link C#LENGTH_UNSET} if unset or unknown. */
public final int channelCount;
/* The {@link C.PcmEncoding} of the audio, or {@link Format#NO_VALUE} if unset or unknown. */
/** The {@link C.PcmEncoding} of the audio, or {@link Format#NO_VALUE} if unset or unknown. */
public final @C.PcmEncoding int pcmEncoding;
/** The sample rate of the audio, or {@link C#RATE_UNSET_INT} if unset or unknown. */
public final int sampleRate;
/** 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;
@ -268,8 +290,6 @@ public final class TransformationResult {
public final int width;
/** The number of video frames. */
public final int videoFrameCount;
/** 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;
@ -280,56 +300,53 @@ public final class TransformationResult {
@Nullable public final TransformationException transformationException;
private TransformationResult(
ImmutableList<ProcessedInput> processedInputs,
long durationMs,
long fileSizeBytes,
int averageAudioBitrate,
int channelCount,
@C.PcmEncoding int pcmEncoding,
int sampleRate,
@Nullable String audioDecoderName,
@Nullable String audioEncoderName,
int averageVideoBitrate,
@Nullable ColorInfo colorInfo,
int height,
int width,
int videoFrameCount,
@Nullable String videoDecoderName,
@Nullable String videoEncoderName,
@Nullable TransformationException transformationException) {
this.processedInputs = processedInputs;
this.durationMs = durationMs;
this.fileSizeBytes = fileSizeBytes;
this.averageAudioBitrate = averageAudioBitrate;
this.channelCount = channelCount;
this.pcmEncoding = pcmEncoding;
this.sampleRate = sampleRate;
this.audioDecoderName = audioDecoderName;
this.audioEncoderName = audioEncoderName;
this.averageVideoBitrate = averageVideoBitrate;
this.colorInfo = colorInfo;
this.height = height;
this.width = width;
this.videoFrameCount = videoFrameCount;
this.videoDecoderName = videoDecoderName;
this.videoEncoderName = videoEncoderName;
this.transformationException = transformationException;
}
public Builder buildUpon() {
return new Builder()
.setProcessedInputs(processedInputs)
.setDurationMs(durationMs)
.setFileSizeBytes(fileSizeBytes)
.setAverageAudioBitrate(averageAudioBitrate)
.setChannelCount(channelCount)
.setPcmEncoding(pcmEncoding)
.setSampleRate(sampleRate)
.setAudioDecoderName(audioDecoderName)
.setAudioEncoderName(audioEncoderName)
.setAverageVideoBitrate(averageVideoBitrate)
.setColorInfo(colorInfo)
.setHeight(height)
.setWidth(width)
.setVideoFrameCount(videoFrameCount)
.setVideoDecoderName(videoDecoderName)
.setVideoEncoderName(videoEncoderName)
.setTransformationException(transformationException);
}
@ -343,40 +360,38 @@ public final class TransformationResult {
return false;
}
TransformationResult result = (TransformationResult) o;
return durationMs == result.durationMs
return Objects.equals(processedInputs, result.processedInputs)
&& durationMs == result.durationMs
&& fileSizeBytes == result.fileSizeBytes
&& averageAudioBitrate == result.averageAudioBitrate
&& channelCount == result.channelCount
&& pcmEncoding == result.pcmEncoding
&& sampleRate == result.sampleRate
&& Objects.equals(audioDecoderName, result.audioDecoderName)
&& Objects.equals(audioEncoderName, result.audioEncoderName)
&& averageVideoBitrate == result.averageVideoBitrate
&& Objects.equals(colorInfo, result.colorInfo)
&& height == result.height
&& width == result.width
&& videoFrameCount == result.videoFrameCount
&& Objects.equals(videoDecoderName, result.videoDecoderName)
&& Objects.equals(videoEncoderName, result.videoEncoderName)
&& Objects.equals(transformationException, result.transformationException);
}
@Override
public int hashCode() {
int result = (int) durationMs;
int result = Objects.hashCode(processedInputs);
result = 31 * result + (int) durationMs;
result = 31 * result + (int) fileSizeBytes;
result = 31 * result + averageAudioBitrate;
result = 31 * result + channelCount;
result = 31 * result + pcmEncoding;
result = 31 * result + sampleRate;
result = 31 * result + Objects.hashCode(audioDecoderName);
result = 31 * result + Objects.hashCode(audioEncoderName);
result = 31 * result + averageVideoBitrate;
result = 31 * result + Objects.hashCode(colorInfo);
result = 31 * result + height;
result = 31 * result + width;
result = 31 * result + videoFrameCount;
result = 31 * result + Objects.hashCode(videoDecoderName);
result = 31 * result + Objects.hashCode(videoEncoderName);
result = 31 * result + Objects.hashCode(transformationException);
return result;

View file

@ -43,7 +43,7 @@ import com.google.android.exoplayer2.util.Effect;
import com.google.android.exoplayer2.util.HandlerWrapper;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Size;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableList;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -243,10 +243,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private void endInternal(
@EndReason int endReason, @Nullable TransformationException transformationException) {
ImmutableMap<Integer, String> decoderNames = compositeAssetLoader.getDecoderNames();
ImmutableList<TransformationResult.ProcessedInput> processedInputs =
compositeAssetLoader.getProcessedInputs();
transformationResultBuilder
.setAudioDecoderName(decoderNames.get(C.TRACK_TYPE_AUDIO))
.setVideoDecoderName(decoderNames.get(C.TRACK_TYPE_VIDEO))
.setProcessedInputs(processedInputs)
.setAudioEncoderName(encoderFactory.getAudioEncoderName())
.setVideoEncoderName(encoderFactory.getVideoEncoderName());