mirror of
https://github.com/samsonjs/media.git
synced 2026-04-18 13:25:47 +00:00
Adapt TransformationResult for multi-asset
PiperOrigin-RevId: 506898392
This commit is contained in:
parent
937fcf9c4a
commit
90de454e1e
5 changed files with 113 additions and 42 deletions
|
|
@ -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}.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue