diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/FrameworkMuxer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/FrameworkMuxer.java new file mode 100644 index 0000000000..d5d04dd579 --- /dev/null +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/FrameworkMuxer.java @@ -0,0 +1,188 @@ +/* + * Copyright 2021 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 com.google.android.exoplayer2.transformer; + +import static com.google.android.exoplayer2.util.Assertions.checkNotNull; +import static com.google.android.exoplayer2.util.Util.SDK_INT; +import static com.google.android.exoplayer2.util.Util.castNonNull; + +import android.media.MediaCodec; +import android.media.MediaFormat; +import android.media.MediaMuxer; +import android.os.ParcelFileDescriptor; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.mediacodec.MediaFormatUtil; +import com.google.android.exoplayer2.util.MimeTypes; +import com.google.android.exoplayer2.util.Util; +import java.io.IOException; +import java.lang.reflect.Field; +import java.nio.ByteBuffer; + +/** Muxer implementation that uses a {@link MediaMuxer}. */ +@RequiresApi(18) +/* package */ final class FrameworkMuxer implements Muxer { + + public static final class Factory implements Muxer.Factory { + @Override + public FrameworkMuxer create(String path, String outputMimeType) throws IOException { + MediaMuxer mediaMuxer = new MediaMuxer(path, mimeTypeToMuxerOutputFormat(outputMimeType)); + return new FrameworkMuxer(mediaMuxer, outputMimeType); + } + + @RequiresApi(26) + @Override + public FrameworkMuxer create(ParcelFileDescriptor parcelFileDescriptor, String outputMimeType) + throws IOException { + MediaMuxer mediaMuxer = + new MediaMuxer( + parcelFileDescriptor.getFileDescriptor(), + mimeTypeToMuxerOutputFormat(outputMimeType)); + return new FrameworkMuxer(mediaMuxer, outputMimeType); + } + + @Override + public boolean supportsOutputMimeType(String mimeType) { + try { + mimeTypeToMuxerOutputFormat(mimeType); + } catch (IllegalStateException e) { + return false; + } + return true; + } + } + + private final MediaMuxer mediaMuxer; + private final String outputMimeType; + private final MediaCodec.BufferInfo bufferInfo; + + private boolean isStarted; + + private FrameworkMuxer(MediaMuxer mediaMuxer, String outputMimeType) { + this.mediaMuxer = mediaMuxer; + this.outputMimeType = outputMimeType; + bufferInfo = new MediaCodec.BufferInfo(); + } + + @Override + public boolean supportsSampleMimeType(@Nullable String mimeType) { + // MediaMuxer supported sample formats are documented in MediaMuxer.addTrack(MediaFormat). + boolean isAudio = MimeTypes.isAudio(mimeType); + boolean isVideo = MimeTypes.isVideo(mimeType); + if (outputMimeType.equals(MimeTypes.VIDEO_MP4)) { + if (isVideo) { + return MimeTypes.VIDEO_H263.equals(mimeType) + || MimeTypes.VIDEO_H264.equals(mimeType) + || MimeTypes.VIDEO_MP4V.equals(mimeType) + || (Util.SDK_INT >= 24 && MimeTypes.VIDEO_H265.equals(mimeType)); + } else if (isAudio) { + return MimeTypes.AUDIO_AAC.equals(mimeType) + || MimeTypes.AUDIO_AMR_NB.equals(mimeType) + || MimeTypes.AUDIO_AMR_WB.equals(mimeType); + } + } else if (outputMimeType.equals(MimeTypes.VIDEO_WEBM) && SDK_INT >= 21) { + if (isVideo) { + return MimeTypes.VIDEO_VP8.equals(mimeType) + || (Util.SDK_INT >= 24 && MimeTypes.VIDEO_VP9.equals(mimeType)); + } else if (isAudio) { + return MimeTypes.AUDIO_VORBIS.equals(mimeType); + } + } + return false; + } + + @Override + public int addTrack(Format format) { + String sampleMimeType = checkNotNull(format.sampleMimeType); + MediaFormat mediaFormat; + if (MimeTypes.isAudio(sampleMimeType)) { + mediaFormat = + MediaFormat.createAudioFormat( + castNonNull(sampleMimeType), format.sampleRate, format.channelCount); + } else { + mediaFormat = + MediaFormat.createVideoFormat(castNonNull(sampleMimeType), format.width, format.height); + mediaMuxer.setOrientationHint(format.rotationDegrees); + } + MediaFormatUtil.setCsdBuffers(mediaFormat, format.initializationData); + return mediaMuxer.addTrack(mediaFormat); + } + + @Override + public void writeSampleData( + int trackIndex, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs) { + if (!isStarted) { + isStarted = true; + mediaMuxer.start(); + } + int offset = data.position(); + int size = data.limit() - offset; + int flags = isKeyFrame ? C.BUFFER_FLAG_KEY_FRAME : 0; + bufferInfo.set(offset, size, presentationTimeUs, flags); + mediaMuxer.writeSampleData(trackIndex, data, bufferInfo); + } + + @Override + public void release() { + if (isStarted) { + isStarted = false; + try { + mediaMuxer.stop(); + } catch (IllegalStateException e) { + if (SDK_INT < 30) { + // Set the muxer state to stopped even if mediaMuxer.stop() failed so that + // mediaMuxer.release() doesn't attempt to stop the muxer and therefore doesn't throw the + // same exception without releasing its resources. This is already implemented in + // MediaMuxer + // from API level 30. + try { + Field muxerStoppedStateField = MediaMuxer.class.getDeclaredField("MUXER_STATE_STOPPED"); + muxerStoppedStateField.setAccessible(true); + int muxerStoppedState = castNonNull((Integer) muxerStoppedStateField.get(mediaMuxer)); + Field muxerStateField = MediaMuxer.class.getDeclaredField("mState"); + muxerStateField.setAccessible(true); + muxerStateField.set(mediaMuxer, muxerStoppedState); + } catch (Exception reflectionException) { + // Do nothing. + } + } + throw e; + } + } + mediaMuxer.release(); + } + + /** + * Converts a {@link MimeTypes MIME type} into a {@link MediaMuxer.OutputFormat MediaMuxer output + * format}. + * + * @param mimeType The {@link MimeTypes MIME type} to convert. + * @return The corresponding {@link MediaMuxer.OutputFormat MediaMuxer output format}. + * @throws IllegalArgumentException If the {@link MimeTypes MIME type} is not supported as output + * format. + */ + private static int mimeTypeToMuxerOutputFormat(String mimeType) { + if (mimeType.equals(MimeTypes.VIDEO_MP4)) { + return MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4; + } else if (SDK_INT >= 21 && mimeType.equals(MimeTypes.VIDEO_WEBM)) { + return MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM; + } else { + throw new IllegalArgumentException("Unsupported output MIME type: " + mimeType); + } + } +} diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Muxer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Muxer.java new file mode 100644 index 0000000000..24e71215fa --- /dev/null +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Muxer.java @@ -0,0 +1,89 @@ +/* + * Copyright 2021 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 com.google.android.exoplayer2.transformer; + +import android.os.ParcelFileDescriptor; +import androidx.annotation.Nullable; +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.util.MimeTypes; +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * Abstracts media muxing operations. + * + *

Query whether {@link #supportsSampleMimeType(String) sample MIME types are supported} and + * {@link #addTrack(Format) add all tracks}, then {@link #writeSampleData(int, ByteBuffer, boolean, + * long) write sample data} to mux samples. Once any sample data has been written, it is not + * possible to add tracks. After writing all sample data, {@link #release() release} the instance to + * finish writing to the output and return any resources to the system. + */ +/* package */ interface Muxer { + + /** Factory for muxers. */ + interface Factory { + /** + * Returns a new muxer writing to a file. + * + * @param path The path to the output file. + * @param outputMimeType The container {@link MimeTypes MIME type} of the output file. + * @throws IllegalArgumentException If the path is invalid or the MIME type is not supported. + * @throws IOException If an error occurs opening the output file for writing. + */ + Muxer create(String path, String outputMimeType) throws IOException; + + /** + * Returns a new muxer writing to a file descriptor. + * + * @param parcelFileDescriptor A readable and writable {@link ParcelFileDescriptor} of the + * output. The file referenced by this ParcelFileDescriptor should not be used before the + * muxer is released. It is the responsibility of the caller to close the + * ParcelFileDescriptor. This can be done after this method returns. + * @param outputMimeType The {@link MimeTypes MIME type} of the output. + * @throws IllegalArgumentException If the file descriptor is invalid or the MIME type is not + * supported. + * @throws IOException If an error occurs opening the output file descriptor for writing. + */ + Muxer create(ParcelFileDescriptor parcelFileDescriptor, String outputMimeType) + throws IOException; + + /** Returns whether the {@link MimeTypes MIME type} provided is a supported output format. */ + boolean supportsOutputMimeType(String mimeType); + } + + /** Returns whether the sample {@link MimeTypes MIME type} is supported. */ + boolean supportsSampleMimeType(@Nullable String mimeType); + + /** + * Adds a track with the specified format, and returns its index (to be passed in subsequent calls + * to {@link #writeSampleData(int, ByteBuffer, boolean, long)}). + */ + int addTrack(Format format); + + /** + * Writes the specified sample. + * + * @param trackIndex The index of the track, previously returned by {@link #addTrack(Format)}. + * @param data Buffer containing the sample data to write to the container. + * @param isKeyFrame Whether the sample is a key frame. + * @param presentationTimeUs The presentation time of the sample in microseconds. + */ + void writeSampleData( + int trackIndex, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs); + + /** Releases any resources associated with muxing. */ + void release(); +} diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/MuxerWrapper.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/MuxerWrapper.java index 274a4857cb..3d9dc45b6f 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/MuxerWrapper.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/MuxerWrapper.java @@ -17,25 +17,15 @@ package com.google.android.exoplayer2.transformer; import static com.google.android.exoplayer2.util.Assertions.checkState; -import static com.google.android.exoplayer2.util.Util.SDK_INT; -import static com.google.android.exoplayer2.util.Util.castNonNull; import static com.google.android.exoplayer2.util.Util.minValue; -import android.media.MediaCodec; -import android.media.MediaFormat; -import android.media.MediaMuxer; -import android.os.ParcelFileDescriptor; import android.util.SparseIntArray; import android.util.SparseLongArray; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.mediacodec.MediaFormatUtil; import com.google.android.exoplayer2.util.MimeTypes; -import com.google.android.exoplayer2.util.Util; -import java.io.IOException; -import java.lang.reflect.Field; import java.nio.ByteBuffer; /** @@ -54,11 +44,9 @@ import java.nio.ByteBuffer; */ private static final long MAX_TRACK_WRITE_AHEAD_US = C.msToUs(500); - private final MediaMuxer mediaMuxer; - private final String outputMimeType; + private final Muxer muxer; private final SparseIntArray trackTypeToIndex; private final SparseLongArray trackTypeToTimeUs; - private final MediaCodec.BufferInfo bufferInfo; private int trackCount; private int trackFormatCount; @@ -66,45 +54,10 @@ import java.nio.ByteBuffer; private int previousTrackType; private long minTrackTimeUs; - /** - * Constructs an instance. - * - * @param path The path to the output file. - * @param outputMimeType The {@link MimeTypes MIME type} of the output. - * @throws IllegalArgumentException If the path is invalid or the MIME type is not supported. - * @throws IOException If an error occurs opening the output file for writing. - */ - public MuxerWrapper(String path, String outputMimeType) throws IOException { - this(new MediaMuxer(path, mimeTypeToMuxerOutputFormat(outputMimeType)), outputMimeType); - } - - /** - * Constructs an instance. - * - * @param parcelFileDescriptor A readable and writable {@link ParcelFileDescriptor} of the output. - * The file referenced by this ParcelFileDescriptor should not be used before the muxer is - * released. It is the responsibility of the caller to close the ParcelFileDescriptor. This - * can be done after this constructor returns. - * @param outputMimeType The {@link MimeTypes MIME type} of the output. - * @throws IllegalArgumentException If the file descriptor is invalid or the MIME type is not - * supported. - * @throws IOException If an error occurs opening the output file for writing. - */ - @RequiresApi(26) - public MuxerWrapper(ParcelFileDescriptor parcelFileDescriptor, String outputMimeType) - throws IOException { - this( - new MediaMuxer( - parcelFileDescriptor.getFileDescriptor(), mimeTypeToMuxerOutputFormat(outputMimeType)), - outputMimeType); - } - - private MuxerWrapper(MediaMuxer mediaMuxer, String outputMimeType) { - this.mediaMuxer = mediaMuxer; - this.outputMimeType = outputMimeType; + public MuxerWrapper(Muxer muxer) { + this.muxer = muxer; trackTypeToIndex = new SparseIntArray(); trackTypeToTimeUs = new SparseLongArray(); - bufferInfo = new MediaCodec.BufferInfo(); previousTrackType = C.TRACK_TYPE_NONE; } @@ -123,6 +76,11 @@ import java.nio.ByteBuffer; trackCount++; } + /** Returns whether the sample {@link MimeTypes MIME type} is supported. */ + public boolean supportsSampleMimeType(@Nullable String mimeType) { + return muxer.supportsSampleMimeType(mimeType); + } + /** * Adds a track format to the muxer. * @@ -131,9 +89,8 @@ import java.nio.ByteBuffer; * long) written}. * * @param format The {@link Format} to be added. - * @throws IllegalArgumentException If the format is invalid. - * @throws IllegalStateException If the format is unsupported, if there is already a track format - * of the same type (audio or video) or if the muxer is in the wrong state. + * @throws IllegalStateException If the format is unsupported or if there is already a track + * format of the same type (audio or video). */ public void addTrackFormat(Format format) { checkState(trackCount > 0, "All tracks should be registered before the formats are added."); @@ -147,23 +104,11 @@ import java.nio.ByteBuffer; trackTypeToIndex.get(trackType, /* valueIfKeyNotFound= */ C.INDEX_UNSET) == C.INDEX_UNSET, "There is already a track of type " + trackType); - MediaFormat mediaFormat; - if (isAudio) { - mediaFormat = - MediaFormat.createAudioFormat( - castNonNull(sampleMimeType), format.sampleRate, format.channelCount); - } else { - mediaFormat = - MediaFormat.createVideoFormat(castNonNull(sampleMimeType), format.width, format.height); - mediaMuxer.setOrientationHint(format.rotationDegrees); - } - MediaFormatUtil.setCsdBuffers(mediaFormat, format.initializationData); - int trackIndex = mediaMuxer.addTrack(mediaFormat); + int trackIndex = muxer.addTrack(format); trackTypeToIndex.put(trackType, trackIndex); trackTypeToTimeUs.put(trackType, 0L); trackFormatCount++; if (trackFormatCount == trackCount) { - mediaMuxer.start(); isReady = true; } } @@ -180,9 +125,8 @@ import java.nio.ByteBuffer; * {@link #addTrackFormat(Format) received a format} for every {@link #registerTrack() * registered track}, or if it should write samples of other track types first to ensure a * good interleaving. - * @throws IllegalArgumentException If the sample in {@code buffer} is invalid. * @throws IllegalStateException If the muxer doesn't have any {@link #endTrack(int) non-ended} - * track of the given track type or if the muxer is in the wrong state. + * track of the given track type. */ public boolean writeSample( int trackType, @Nullable ByteBuffer data, boolean isKeyFrame, long presentationTimeUs) { @@ -197,11 +141,7 @@ import java.nio.ByteBuffer; return true; } - int offset = data.position(); - int size = data.limit() - offset; - int flags = isKeyFrame ? C.BUFFER_FLAG_KEY_FRAME : 0; - bufferInfo.set(offset, size, presentationTimeUs, flags); - mediaMuxer.writeSampleData(trackIndex, data, bufferInfo); + muxer.writeSampleData(trackIndex, data, isKeyFrame, presentationTimeUs); trackTypeToTimeUs.put(trackType, presentationTimeUs); previousTrackType = trackType; return true; @@ -222,35 +162,10 @@ import java.nio.ByteBuffer; * Stops the muxer. * *

The muxer cannot be used anymore once it is stopped. - * - * @throws IllegalStateException If the muxer is in the wrong state (for example if it didn't - * receive any samples). */ public void stop() { - if (!isReady) { - return; - } - isReady = false; - try { - mediaMuxer.stop(); - } catch (IllegalStateException e) { - if (SDK_INT < 30) { - // Set the muxer state to stopped even if mediaMuxer.stop() failed so that - // mediaMuxer.release() doesn't attempt to stop the muxer and therefore doesn't throw the - // same exception without releasing its resources. This is already implemented in MediaMuxer - // from API level 30. - try { - Field muxerStoppedStateField = MediaMuxer.class.getDeclaredField("MUXER_STATE_STOPPED"); - muxerStoppedStateField.setAccessible(true); - int muxerStoppedState = castNonNull((Integer) muxerStoppedStateField.get(mediaMuxer)); - Field muxerStateField = MediaMuxer.class.getDeclaredField("mState"); - muxerStateField.setAccessible(true); - muxerStateField.set(mediaMuxer, muxerStoppedState); - } catch (Exception reflectionException) { - // Do nothing. - } - } - throw e; + if (isReady) { + isReady = false; } } @@ -261,7 +176,7 @@ import java.nio.ByteBuffer; */ public void release() { isReady = false; - mediaMuxer.release(); + muxer.release(); } /** Returns the number of {@link #registerTrack() registered} tracks. */ @@ -269,48 +184,6 @@ import java.nio.ByteBuffer; return trackCount; } - /** - * Returns whether the sample {@link MimeTypes MIME type} is supported. - * - *

Supported sample formats are documented in {@link MediaMuxer#addTrack(MediaFormat)}. - */ - public boolean supportsSampleMimeType(@Nullable String mimeType) { - boolean isAudio = MimeTypes.isAudio(mimeType); - boolean isVideo = MimeTypes.isVideo(mimeType); - if (outputMimeType.equals(MimeTypes.VIDEO_MP4)) { - if (isVideo) { - return MimeTypes.VIDEO_H263.equals(mimeType) - || MimeTypes.VIDEO_H264.equals(mimeType) - || MimeTypes.VIDEO_MP4V.equals(mimeType) - || (Util.SDK_INT >= 24 && MimeTypes.VIDEO_H265.equals(mimeType)); - } else if (isAudio) { - return MimeTypes.AUDIO_AAC.equals(mimeType) - || MimeTypes.AUDIO_AMR_NB.equals(mimeType) - || MimeTypes.AUDIO_AMR_WB.equals(mimeType); - } - } else if (outputMimeType.equals(MimeTypes.VIDEO_WEBM) && SDK_INT >= 21) { - if (isVideo) { - return MimeTypes.VIDEO_VP8.equals(mimeType) - || (Util.SDK_INT >= 24 && MimeTypes.VIDEO_VP9.equals(mimeType)); - } else if (isAudio) { - return MimeTypes.AUDIO_VORBIS.equals(mimeType); - } - } - return false; - } - - /** - * Returns whether the {@link MimeTypes MIME type} provided is a supported muxer output format. - */ - public static boolean supportsOutputMimeType(String mimeType) { - try { - mimeTypeToMuxerOutputFormat(mimeType); - } catch (IllegalStateException e) { - return false; - } - return true; - } - /** * Returns whether the muxer can write a sample of the given track type. * @@ -337,22 +210,4 @@ import java.nio.ByteBuffer; return trackTimeUs - minTrackTimeUs <= MAX_TRACK_WRITE_AHEAD_US; } - /** - * Converts a {@link MimeTypes MIME type} into a {@link MediaMuxer.OutputFormat MediaMuxer output - * format}. - * - * @param mimeType The {@link MimeTypes MIME type} to convert. - * @return The corresponding {@link MediaMuxer.OutputFormat MediaMuxer output format}. - * @throws IllegalArgumentException If the {@link MimeTypes MIME type} is not supported as output - * format. - */ - private static int mimeTypeToMuxerOutputFormat(String mimeType) { - if (mimeType.equals(MimeTypes.VIDEO_MP4)) { - return MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4; - } else if (SDK_INT >= 21 && mimeType.equals(MimeTypes.VIDEO_WEBM)) { - return MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM; - } else { - throw new IllegalArgumentException("Unsupported output MIME type: " + mimeType); - } - } } diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java index 8546c84027..0c88b32f80 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java @@ -79,7 +79,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * of the application's main thread is used. In all cases the Looper of the thread from which the * transformer must be accessed can be queried using {@link #getApplicationLooper()}. */ - @RequiresApi(18) public final class Transformer { @@ -88,6 +87,7 @@ public final class Transformer { private @MonotonicNonNull Context context; private @MonotonicNonNull MediaSourceFactory mediaSourceFactory; + private Muxer.Factory muxerFactory; private boolean removeAudio; private boolean removeVideo; private boolean flattenForSlowMotion; @@ -98,6 +98,7 @@ public final class Transformer { /** Creates a builder with default values. */ public Builder() { + muxerFactory = new FrameworkMuxer.Factory(); outputMimeType = MimeTypes.VIDEO_MP4; listener = new Listener() {}; looper = Util.getCurrentOrMainLooper(); @@ -108,6 +109,7 @@ public final class Transformer { private Builder(Transformer transformer) { this.context = transformer.context; this.mediaSourceFactory = transformer.mediaSourceFactory; + this.muxerFactory = transformer.muxerFactory; this.removeAudio = transformer.transformation.removeAudio; this.removeVideo = transformer.transformation.removeVideo; this.flattenForSlowMotion = transformer.transformation.flattenForSlowMotion; @@ -212,12 +214,8 @@ public final class Transformer { * * @param outputMimeType The MIME type of the output. * @return This builder. - * @throws IllegalArgumentException If the MIME type is not supported. */ public Builder setOutputMimeType(String outputMimeType) { - if (!MuxerWrapper.supportsOutputMimeType(outputMimeType)) { - throw new IllegalArgumentException("Unsupported output MIME type: " + outputMimeType); - } this.outputMimeType = outputMimeType; return this; } @@ -262,12 +260,25 @@ public final class Transformer { return this; } + /** + * Sets the factory for muxers that write the media container. + * + * @param muxerFactory A {@link Muxer.Factory}. + * @return This builder. + */ + @VisibleForTesting + /* package */ Builder setMuxerFactory(Muxer.Factory muxerFactory) { + this.muxerFactory = muxerFactory; + return this; + } + /** * Builds a {@link Transformer} instance. * * @throws IllegalStateException If the {@link Context} has not been provided. * @throws IllegalStateException If both audio and video have been removed (otherwise the output * would not contain any samples). + * @throws IllegalStateException If the muxer doesn't support the requested output MIME type. */ public Transformer build() { checkStateNotNull(context); @@ -278,9 +289,13 @@ public final class Transformer { } mediaSourceFactory = new DefaultMediaSourceFactory(context, defaultExtractorsFactory); } + checkState( + muxerFactory.supportsOutputMimeType(outputMimeType), + "Unsupported output MIME type: " + outputMimeType); Transformation transformation = new Transformation(removeAudio, removeVideo, flattenForSlowMotion, outputMimeType); - return new Transformer(context, mediaSourceFactory, transformation, listener, looper, clock); + return new Transformer( + context, mediaSourceFactory, muxerFactory, transformation, listener, looper, clock); } } @@ -332,6 +347,7 @@ public final class Transformer { private final Context context; private final MediaSourceFactory mediaSourceFactory; + private final Muxer.Factory muxerFactory; private final Transformation transformation; private final Looper looper; private final Clock clock; @@ -344,6 +360,7 @@ public final class Transformer { private Transformer( Context context, MediaSourceFactory mediaSourceFactory, + Muxer.Factory muxerFactory, Transformation transformation, Transformer.Listener listener, Looper looper, @@ -353,6 +370,7 @@ public final class Transformer { "Audio and video cannot both be removed."); this.context = context; this.mediaSourceFactory = mediaSourceFactory; + this.muxerFactory = muxerFactory; this.transformation = transformation; this.listener = listener; this.looper = looper; @@ -397,7 +415,7 @@ public final class Transformer { * @throws IOException If an error occurs opening the output file for writing. */ public void startTransformation(MediaItem mediaItem, String path) throws IOException { - startTransformation(mediaItem, new MuxerWrapper(path, transformation.outputMimeType)); + startTransformation(mediaItem, muxerFactory.create(path, transformation.outputMimeType)); } /** @@ -427,17 +445,17 @@ public final class Transformer { public void startTransformation(MediaItem mediaItem, ParcelFileDescriptor parcelFileDescriptor) throws IOException { startTransformation( - mediaItem, new MuxerWrapper(parcelFileDescriptor, transformation.outputMimeType)); + mediaItem, muxerFactory.create(parcelFileDescriptor, transformation.outputMimeType)); } - private void startTransformation(MediaItem mediaItem, MuxerWrapper muxerWrapper) { + private void startTransformation(MediaItem mediaItem, Muxer muxer) { verifyApplicationThread(); if (player != null) { throw new IllegalStateException("There is already a transformation in progress."); } + MuxerWrapper muxerWrapper = new MuxerWrapper(muxer); this.muxerWrapper = muxerWrapper; - DefaultTrackSelector trackSelector = new DefaultTrackSelector(context); trackSelector.setParameters( new DefaultTrackSelector.ParametersBuilder(context) diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerBaseRenderer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerBaseRenderer.java index 445a91723a..33888226b8 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerBaseRenderer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerBaseRenderer.java @@ -50,7 +50,7 @@ import com.google.android.exoplayer2.util.MimeTypes; @C.FormatSupport public final int supportsFormat(Format format) { @Nullable String sampleMimeType = format.sampleMimeType; - if (MimeTypes.getTrackType(format.sampleMimeType) != getTrackType()) { + if (MimeTypes.getTrackType(sampleMimeType) != getTrackType()) { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); } else if (muxerWrapper.supportsSampleMimeType(sampleMimeType)) { return RendererCapabilities.create(C.FORMAT_HANDLED); diff --git a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TestMuxer.java b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TestMuxer.java new file mode 100644 index 0000000000..1c43f7ccc9 --- /dev/null +++ b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TestMuxer.java @@ -0,0 +1,109 @@ +/* + * Copyright 2021 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 com.google.android.exoplayer2.transformer; + +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.testutil.DumpableFormat; +import com.google.android.exoplayer2.testutil.Dumper; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * An implementation of {@link Muxer} that supports dumping information about all interactions (for + * testing purposes) and delegates the actual muxing operations to a {@link FrameworkMuxer}. + */ +public final class TestMuxer implements Muxer, Dumper.Dumpable { + + private final Muxer frameworkMuxer; + private final List dumpables; + + /** Creates a new test muxer. */ + public TestMuxer(String path, String outputMimeType) throws IOException { + frameworkMuxer = new FrameworkMuxer.Factory().create(path, outputMimeType); + dumpables = new ArrayList<>(); + dumpables.add(dumper -> dumper.add("containerMimeType", outputMimeType)); + } + + // Muxer implementation. + + @Override + public boolean supportsSampleMimeType(String mimeType) { + return frameworkMuxer.supportsSampleMimeType(mimeType); + } + + @Override + public int addTrack(Format format) { + int trackIndex = frameworkMuxer.addTrack(format); + dumpables.add(new DumpableFormat(format, trackIndex)); + return trackIndex; + } + + @Override + public void writeSampleData( + int trackIndex, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs) { + dumpables.add(new DumpableSample(trackIndex, data, isKeyFrame, presentationTimeUs)); + frameworkMuxer.writeSampleData(trackIndex, data, isKeyFrame, presentationTimeUs); + } + + @Override + public void release() { + dumpables.add(dumper -> dumper.add("released", true)); + frameworkMuxer.release(); + } + + // Dumper.Dumpable implementation. + + @Override + public void dump(Dumper dumper) { + for (Dumper.Dumpable dumpable : dumpables) { + dumpable.dump(dumper); + } + } + + private static final class DumpableSample implements Dumper.Dumpable { + + private final int trackIndex; + private final long presentationTimeUs; + private final boolean isKeyFrame; + private final int sampleDataHashCode; + + public DumpableSample( + int trackIndex, ByteBuffer sample, boolean isKeyFrame, long presentationTimeUs) { + this.trackIndex = trackIndex; + this.presentationTimeUs = presentationTimeUs; + this.isKeyFrame = isKeyFrame; + int initialPosition = sample.position(); + byte[] data = new byte[sample.remaining()]; + sample.get(data); + sample.position(initialPosition); + sampleDataHashCode = Arrays.hashCode(data); + } + + @Override + public void dump(Dumper dumper) { + dumper + .startBlock("sample") + .add("trackIndex", trackIndex) + .add("dataHashCode", sampleDataHashCode) + .add("isKeyFrame", isKeyFrame) + .add("presentationTimeUs", presentationTimeUs) + .endBlock(); + } + } +} diff --git a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerBuilderTest.java b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerBuilderTest.java index 7d787be2e8..8cfba3156d 100644 --- a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerBuilderTest.java +++ b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerBuilderTest.java @@ -32,8 +32,8 @@ public class TransformerBuilderTest { @Test public void setOutputMimeType_unsupportedMimeType_throws() { assertThrows( - IllegalArgumentException.class, - () -> new Transformer.Builder().setOutputMimeType(MimeTypes.VIDEO_FLV)); + IllegalStateException.class, + () -> new Transformer.Builder().setOutputMimeType(MimeTypes.VIDEO_FLV).build()); } @Test diff --git a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerTest.java b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerTest.java index d3ef423217..9dc96421c0 100644 --- a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerTest.java +++ b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerTest.java @@ -28,15 +28,16 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; +import android.os.ParcelFileDescriptor; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.testutil.AutoAdvancingFakeClock; +import com.google.android.exoplayer2.testutil.DumpFileAsserts; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; import com.google.common.collect.Iterables; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; @@ -56,18 +57,20 @@ import org.robolectric.shadows.ShadowMediaCodec; @RunWith(AndroidJUnit4.class) public final class TransformerTest { - private static final String FILE_VIDEO_ONLY = "asset:///media/mkv/sample.mkv"; - private static final String FILE_AUDIO_ONLY = "asset:///media/amr/sample_nb.amr"; - private static final String FILE_AUDIO_VIDEO = "asset:///media/mp4/sample.mp4"; - - // The ShadowMediaMuxer only outputs sample data to the output file. - private static final int FILE_VIDEO_ONLY_SAMPLE_DATA_LENGTH = 89_502; - private static final int FILE_AUDIO_ONLY_SAMPLE_DATA_LENGTH = 2834; - private static final int FILE_AUDIO_VIDEO_AUDIO_SAMPLE_DATA_LENGTH = 9529; - private static final int FILE_AUDIO_VIDEO_VIDEO_SAMPLE_DATA_LENGTH = 89_876; + private static final String URI_PREFIX = "asset:///media/"; + private static final String FILE_VIDEO_ONLY = "mkv/sample.mkv"; + private static final String FILE_AUDIO_ONLY = "amr/sample_nb.amr"; + private static final String FILE_AUDIO_VIDEO = "mp4/sample.mp4"; + private static final String FILE_WITH_SUBTITLES = "mkv/sample_with_srt.mkv"; + private static final String FILE_WITH_SEF_SLOW_MOTION = "mp4/sample_sef_slow_motion.mp4"; + private static final String FILE_WITH_ALL_SAMPLE_FORMATS_UNSUPPORTED = "mp4/sample_ac3.mp4"; + private static final String FILE_UNKNOWN_DURATION = "mp4/sample_fragmented.mp4"; + public static final String DUMP_FILE_OUTPUT_DIRECTORY = "transformerdumps"; + public static final String DUMP_FILE_EXTENSION = "dump"; private Context context; private String outputPath; + private TestMuxer testMuxer; private AutoAdvancingFakeClock clock; private ProgressHolder progressHolder; @@ -88,55 +91,78 @@ public final class TransformerTest { @Test public void startTransformation_videoOnly_completesSuccessfully() throws Exception { - Transformer transformer = new Transformer.Builder().setContext(context).setClock(clock).build(); - MediaItem mediaItem = MediaItem.fromUri(FILE_VIDEO_ONLY); + Transformer transformer = + new Transformer.Builder() + .setContext(context) + .setClock(clock) + .setMuxerFactory(new TestMuxerFactory()) + .build(); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); transformer.startTransformation(mediaItem, outputPath); TransformerTestRunner.runUntilCompleted(transformer); - assertThat(new File(outputPath).length()).isEqualTo(FILE_VIDEO_ONLY_SAMPLE_DATA_LENGTH); + DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_VIDEO_ONLY)); } @Test public void startTransformation_audioOnly_completesSuccessfully() throws Exception { - Transformer transformer = new Transformer.Builder().setContext(context).setClock(clock).build(); - MediaItem mediaItem = MediaItem.fromUri(FILE_AUDIO_ONLY); + Transformer transformer = + new Transformer.Builder() + .setContext(context) + .setClock(clock) + .setMuxerFactory(new TestMuxerFactory()) + .build(); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_ONLY); transformer.startTransformation(mediaItem, outputPath); TransformerTestRunner.runUntilCompleted(transformer); - assertThat(new File(outputPath).length()).isEqualTo(FILE_AUDIO_ONLY_SAMPLE_DATA_LENGTH); + DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_ONLY)); } @Test public void startTransformation_audioAndVideo_completesSuccessfully() throws Exception { - Transformer transformer = new Transformer.Builder().setContext(context).setClock(clock).build(); - MediaItem mediaItem = MediaItem.fromUri(FILE_AUDIO_VIDEO); + Transformer transformer = + new Transformer.Builder() + .setContext(context) + .setClock(clock) + .setMuxerFactory(new TestMuxerFactory()) + .build(); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); transformer.startTransformation(mediaItem, outputPath); TransformerTestRunner.runUntilCompleted(transformer); - assertThat(new File(outputPath).length()) - .isEqualTo( - FILE_AUDIO_VIDEO_VIDEO_SAMPLE_DATA_LENGTH + FILE_AUDIO_VIDEO_AUDIO_SAMPLE_DATA_LENGTH); + DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO)); } @Test public void startTransformation_withSubtitles_completesSuccessfully() throws Exception { - Transformer transformer = new Transformer.Builder().setContext(context).setClock(clock).build(); - MediaItem mediaItem = MediaItem.fromUri("asset:///media/mkv/sample_with_srt.mkv"); + Transformer transformer = + new Transformer.Builder() + .setContext(context) + .setClock(clock) + .setMuxerFactory(new TestMuxerFactory()) + .build(); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_WITH_SUBTITLES); transformer.startTransformation(mediaItem, outputPath); TransformerTestRunner.runUntilCompleted(transformer); - assertThat(new File(outputPath).length()).isEqualTo(89_502); + DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_WITH_SUBTITLES)); } @Test public void startTransformation_successiveTransformations_completesSuccessfully() throws Exception { - Transformer transformer = new Transformer.Builder().setContext(context).setClock(clock).build(); - MediaItem mediaItem = MediaItem.fromUri(FILE_VIDEO_ONLY); + Transformer transformer = + new Transformer.Builder() + .setContext(context) + .setClock(clock) + .setMuxerFactory(new TestMuxerFactory()) + .build(); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); // Transform first media item. transformer.startTransformation(mediaItem, outputPath); @@ -147,13 +173,13 @@ public final class TransformerTest { transformer.startTransformation(mediaItem, outputPath); TransformerTestRunner.runUntilCompleted(transformer); - assertThat(new File(outputPath).length()).isEqualTo(FILE_VIDEO_ONLY_SAMPLE_DATA_LENGTH); + DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_VIDEO_ONLY)); } @Test public void startTransformation_concurrentTransformations_throwsError() throws Exception { Transformer transformer = new Transformer.Builder().setContext(context).setClock(clock).build(); - MediaItem mediaItem = MediaItem.fromUri(FILE_VIDEO_ONLY); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); transformer.startTransformation(mediaItem, outputPath); @@ -164,25 +190,37 @@ public final class TransformerTest { @Test public void startTransformation_removeAudio_completesSuccessfully() throws Exception { Transformer transformer = - new Transformer.Builder().setContext(context).setRemoveAudio(true).setClock(clock).build(); - MediaItem mediaItem = MediaItem.fromUri(FILE_AUDIO_VIDEO); + new Transformer.Builder() + .setContext(context) + .setRemoveAudio(true) + .setClock(clock) + .setMuxerFactory(new TestMuxerFactory()) + .build(); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); transformer.startTransformation(mediaItem, outputPath); TransformerTestRunner.runUntilCompleted(transformer); - assertThat(new File(outputPath).length()).isEqualTo(FILE_AUDIO_VIDEO_VIDEO_SAMPLE_DATA_LENGTH); + DumpFileAsserts.assertOutput( + context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".noaudio")); } @Test public void startTransformation_removeVideo_completesSuccessfully() throws Exception { Transformer transformer = - new Transformer.Builder().setContext(context).setRemoveVideo(true).setClock(clock).build(); - MediaItem mediaItem = MediaItem.fromUri(FILE_AUDIO_VIDEO); + new Transformer.Builder() + .setContext(context) + .setRemoveVideo(true) + .setClock(clock) + .setMuxerFactory(new TestMuxerFactory()) + .build(); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); transformer.startTransformation(mediaItem, outputPath); TransformerTestRunner.runUntilCompleted(transformer); - assertThat(new File(outputPath).length()).isEqualTo(FILE_AUDIO_VIDEO_AUDIO_SAMPLE_DATA_LENGTH); + DumpFileAsserts.assertOutput( + context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".novideo")); } @Test @@ -192,13 +230,14 @@ public final class TransformerTest { .setContext(context) .setFlattenForSlowMotion(true) .setClock(clock) + .setMuxerFactory(new TestMuxerFactory()) .build(); - MediaItem mediaItem = MediaItem.fromUri("asset:///media/mp4/sample_sef_slow_motion.mp4"); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_WITH_SEF_SLOW_MOTION); transformer.startTransformation(mediaItem, outputPath); TransformerTestRunner.runUntilCompleted(transformer); - assertThat(new File(outputPath).length()).isEqualTo(18_172); + DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_WITH_SEF_SLOW_MOTION)); } @Test @@ -217,7 +256,7 @@ public final class TransformerTest { public void startTransformation_withAllSampleFormatsUnsupported_completesWithError() throws Exception { Transformer transformer = new Transformer.Builder().setContext(context).setClock(clock).build(); - MediaItem mediaItem = MediaItem.fromUri("asset:///media/mp4/sample_ac3.mp4"); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_WITH_ALL_SAMPLE_FORMATS_UNSUPPORTED); transformer.startTransformation(mediaItem, outputPath); Exception exception = TransformerTestRunner.runUntilError(transformer); @@ -227,8 +266,13 @@ public final class TransformerTest { @Test public void startTransformation_afterCancellation_completesSuccessfully() throws Exception { - Transformer transformer = new Transformer.Builder().setContext(context).setClock(clock).build(); - MediaItem mediaItem = MediaItem.fromUri(FILE_VIDEO_ONLY); + Transformer transformer = + new Transformer.Builder() + .setContext(context) + .setClock(clock) + .setMuxerFactory(new TestMuxerFactory()) + .build(); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); transformer.startTransformation(mediaItem, outputPath); transformer.cancel(); @@ -238,7 +282,7 @@ public final class TransformerTest { transformer.startTransformation(mediaItem, outputPath); TransformerTestRunner.runUntilCompleted(transformer); - assertThat(new File(outputPath).length()).isEqualTo(FILE_VIDEO_ONLY_SAMPLE_DATA_LENGTH); + DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_VIDEO_ONLY)); } @Test @@ -247,8 +291,13 @@ public final class TransformerTest { anotherThread.start(); Looper looper = anotherThread.getLooper(); Transformer transformer = - new Transformer.Builder().setContext(context).setLooper(looper).setClock(clock).build(); - MediaItem mediaItem = MediaItem.fromUri(FILE_AUDIO_ONLY); + new Transformer.Builder() + .setContext(context) + .setLooper(looper) + .setClock(clock) + .setMuxerFactory(new TestMuxerFactory()) + .build(); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_ONLY); AtomicReference exception = new AtomicReference<>(); CountDownLatch countDownLatch = new CountDownLatch(1); @@ -267,13 +316,13 @@ public final class TransformerTest { countDownLatch.await(); assertThat(exception.get()).isNull(); - assertThat(new File(outputPath).length()).isEqualTo(FILE_AUDIO_ONLY_SAMPLE_DATA_LENGTH); + DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_ONLY)); } @Test public void startTransformation_fromWrongThread_throwsError() throws Exception { Transformer transformer = new Transformer.Builder().setContext(context).setClock(clock).build(); - MediaItem mediaItem = MediaItem.fromUri(FILE_AUDIO_ONLY); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_ONLY); HandlerThread anotherThread = new HandlerThread("AnotherThread"); AtomicReference illegalStateException = new AtomicReference<>(); CountDownLatch countDownLatch = new CountDownLatch(1); @@ -300,7 +349,7 @@ public final class TransformerTest { @Test public void getProgress_knownDuration_returnsConsistentStates() throws Exception { Transformer transformer = new Transformer.Builder().setContext(context).setClock(clock).build(); - MediaItem mediaItem = MediaItem.fromUri(FILE_VIDEO_ONLY); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); AtomicInteger previousProgressState = new AtomicInteger(PROGRESS_STATE_WAITING_FOR_AVAILABILITY); AtomicBoolean foundInconsistentState = new AtomicBoolean(); @@ -346,7 +395,7 @@ public final class TransformerTest { @Test public void getProgress_knownDuration_givesIncreasingPercentages() throws Exception { Transformer transformer = new Transformer.Builder().setContext(context).setClock(clock).build(); - MediaItem mediaItem = MediaItem.fromUri(FILE_VIDEO_ONLY); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); List progresses = new ArrayList<>(); Handler progressHandler = new Handler(Looper.myLooper()) { @@ -381,7 +430,7 @@ public final class TransformerTest { @Test public void getProgress_noCurrentTransformation_returnsNoTransformation() throws Exception { Transformer transformer = new Transformer.Builder().setContext(context).setClock(clock).build(); - MediaItem mediaItem = MediaItem.fromUri(FILE_VIDEO_ONLY); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); @Transformer.ProgressState int stateBeforeTransform = transformer.getProgress(progressHolder); transformer.startTransformation(mediaItem, outputPath); @@ -395,7 +444,7 @@ public final class TransformerTest { @Test public void getProgress_unknownDuration_returnsConsistentStates() throws Exception { Transformer transformer = new Transformer.Builder().setContext(context).setClock(clock).build(); - MediaItem mediaItem = MediaItem.fromUri("asset:///media/mp4/sample_fragmented.mp4"); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_UNKNOWN_DURATION); AtomicInteger previousProgressState = new AtomicInteger(PROGRESS_STATE_WAITING_FOR_AVAILABILITY); AtomicBoolean foundInconsistentState = new AtomicBoolean(); @@ -462,7 +511,7 @@ public final class TransformerTest { @Test public void cancel_afterCompletion_doesNotThrow() throws Exception { Transformer transformer = new Transformer.Builder().setContext(context).setClock(clock).build(); - MediaItem mediaItem = MediaItem.fromUri(FILE_VIDEO_ONLY); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); transformer.startTransformation(mediaItem, outputPath); TransformerTestRunner.runUntilCompleted(transformer); @@ -507,4 +556,28 @@ public final class TransformerTest { private static void removeEncodersAndDecoders() { ShadowMediaCodec.clearCodecs(); } + + private static String getDumpFileName(String originalFileName) { + return DUMP_FILE_OUTPUT_DIRECTORY + '/' + originalFileName + '.' + DUMP_FILE_EXTENSION; + } + + private final class TestMuxerFactory implements Muxer.Factory { + @Override + public Muxer create(String path, String outputMimeType) throws IOException { + testMuxer = new TestMuxer(path, outputMimeType); + return testMuxer; + } + + @Override + public Muxer create(ParcelFileDescriptor parcelFileDescriptor, String outputMimeType) + throws IOException { + testMuxer = new TestMuxer("FD:" + parcelFileDescriptor.getFd(), outputMimeType); + return testMuxer; + } + + @Override + public boolean supportsOutputMimeType(String mimeType) { + return true; + } + } } diff --git a/testdata/src/test/assets/transformerdumps/amr/sample_nb.amr.dump b/testdata/src/test/assets/transformerdumps/amr/sample_nb.amr.dump new file mode 100644 index 0000000000..f48431a0f7 --- /dev/null +++ b/testdata/src/test/assets/transformerdumps/amr/sample_nb.amr.dump @@ -0,0 +1,1096 @@ +containerMimeType = video/mp4 +format 0: + sampleMimeType = audio/3gpp + channelCount = 1 + sampleRate = 8000 +sample: + trackIndex = 0 + dataHashCode = 924517484 + isKeyFrame = true + presentationTimeUs = 0 +sample: + trackIndex = 0 + dataHashCode = -835666085 + isKeyFrame = true + presentationTimeUs = 750 +sample: + trackIndex = 0 + dataHashCode = 430283125 + isKeyFrame = true + presentationTimeUs = 1500 +sample: + trackIndex = 0 + dataHashCode = 1215919932 + isKeyFrame = true + presentationTimeUs = 2250 +sample: + trackIndex = 0 + dataHashCode = -386387943 + isKeyFrame = true + presentationTimeUs = 3000 +sample: + trackIndex = 0 + dataHashCode = -765080119 + isKeyFrame = true + presentationTimeUs = 3750 +sample: + trackIndex = 0 + dataHashCode = -1855636054 + isKeyFrame = true + presentationTimeUs = 4500 +sample: + trackIndex = 0 + dataHashCode = -946579722 + isKeyFrame = true + presentationTimeUs = 5250 +sample: + trackIndex = 0 + dataHashCode = -841202654 + isKeyFrame = true + presentationTimeUs = 6000 +sample: + trackIndex = 0 + dataHashCode = -638764303 + isKeyFrame = true + presentationTimeUs = 6750 +sample: + trackIndex = 0 + dataHashCode = -1162388941 + isKeyFrame = true + presentationTimeUs = 7500 +sample: + trackIndex = 0 + dataHashCode = 572634367 + isKeyFrame = true + presentationTimeUs = 8250 +sample: + trackIndex = 0 + dataHashCode = -1774188021 + isKeyFrame = true + presentationTimeUs = 9000 +sample: + trackIndex = 0 + dataHashCode = 92464891 + isKeyFrame = true + presentationTimeUs = 9750 +sample: + trackIndex = 0 + dataHashCode = -991397659 + isKeyFrame = true + presentationTimeUs = 10500 +sample: + trackIndex = 0 + dataHashCode = -934698563 + isKeyFrame = true + presentationTimeUs = 11250 +sample: + trackIndex = 0 + dataHashCode = -811030035 + isKeyFrame = true + presentationTimeUs = 12000 +sample: + trackIndex = 0 + dataHashCode = 1892305159 + isKeyFrame = true + presentationTimeUs = 12750 +sample: + trackIndex = 0 + dataHashCode = -1266858924 + isKeyFrame = true + presentationTimeUs = 13500 +sample: + trackIndex = 0 + dataHashCode = 673814721 + isKeyFrame = true + presentationTimeUs = 14250 +sample: + trackIndex = 0 + dataHashCode = 1061124709 + isKeyFrame = true + presentationTimeUs = 15000 +sample: + trackIndex = 0 + dataHashCode = -869356712 + isKeyFrame = true + presentationTimeUs = 15750 +sample: + trackIndex = 0 + dataHashCode = 664729362 + isKeyFrame = true + presentationTimeUs = 16500 +sample: + trackIndex = 0 + dataHashCode = -1439741143 + isKeyFrame = true + presentationTimeUs = 17250 +sample: + trackIndex = 0 + dataHashCode = -151627580 + isKeyFrame = true + presentationTimeUs = 18000 +sample: + trackIndex = 0 + dataHashCode = -673268457 + isKeyFrame = true + presentationTimeUs = 18750 +sample: + trackIndex = 0 + dataHashCode = 1839962647 + isKeyFrame = true + presentationTimeUs = 19500 +sample: + trackIndex = 0 + dataHashCode = 1858999665 + isKeyFrame = true + presentationTimeUs = 20250 +sample: + trackIndex = 0 + dataHashCode = -1278193537 + isKeyFrame = true + presentationTimeUs = 21000 +sample: + trackIndex = 0 + dataHashCode = 568547001 + isKeyFrame = true + presentationTimeUs = 21750 +sample: + trackIndex = 0 + dataHashCode = 68217362 + isKeyFrame = true + presentationTimeUs = 22500 +sample: + trackIndex = 0 + dataHashCode = 1396217256 + isKeyFrame = true + presentationTimeUs = 23250 +sample: + trackIndex = 0 + dataHashCode = -971293094 + isKeyFrame = true + presentationTimeUs = 24000 +sample: + trackIndex = 0 + dataHashCode = -1742638874 + isKeyFrame = true + presentationTimeUs = 24750 +sample: + trackIndex = 0 + dataHashCode = 2047109317 + isKeyFrame = true + presentationTimeUs = 25500 +sample: + trackIndex = 0 + dataHashCode = -1668945241 + isKeyFrame = true + presentationTimeUs = 26250 +sample: + trackIndex = 0 + dataHashCode = -1229766218 + isKeyFrame = true + presentationTimeUs = 27000 +sample: + trackIndex = 0 + dataHashCode = 1765233454 + isKeyFrame = true + presentationTimeUs = 27750 +sample: + trackIndex = 0 + dataHashCode = -1930255456 + isKeyFrame = true + presentationTimeUs = 28500 +sample: + trackIndex = 0 + dataHashCode = -764925242 + isKeyFrame = true + presentationTimeUs = 29250 +sample: + trackIndex = 0 + dataHashCode = -1144688369 + isKeyFrame = true + presentationTimeUs = 30000 +sample: + trackIndex = 0 + dataHashCode = 1493699436 + isKeyFrame = true + presentationTimeUs = 30750 +sample: + trackIndex = 0 + dataHashCode = -468614511 + isKeyFrame = true + presentationTimeUs = 31500 +sample: + trackIndex = 0 + dataHashCode = -1578782058 + isKeyFrame = true + presentationTimeUs = 32250 +sample: + trackIndex = 0 + dataHashCode = -675743397 + isKeyFrame = true + presentationTimeUs = 33000 +sample: + trackIndex = 0 + dataHashCode = -863790111 + isKeyFrame = true + presentationTimeUs = 33750 +sample: + trackIndex = 0 + dataHashCode = -732307506 + isKeyFrame = true + presentationTimeUs = 34500 +sample: + trackIndex = 0 + dataHashCode = -693298708 + isKeyFrame = true + presentationTimeUs = 35250 +sample: + trackIndex = 0 + dataHashCode = -799131843 + isKeyFrame = true + presentationTimeUs = 36000 +sample: + trackIndex = 0 + dataHashCode = 1782866119 + isKeyFrame = true + presentationTimeUs = 36750 +sample: + trackIndex = 0 + dataHashCode = -912205505 + isKeyFrame = true + presentationTimeUs = 37500 +sample: + trackIndex = 0 + dataHashCode = 1067981287 + isKeyFrame = true + presentationTimeUs = 38250 +sample: + trackIndex = 0 + dataHashCode = 490520060 + isKeyFrame = true + presentationTimeUs = 39000 +sample: + trackIndex = 0 + dataHashCode = -1950632957 + isKeyFrame = true + presentationTimeUs = 39750 +sample: + trackIndex = 0 + dataHashCode = 565485817 + isKeyFrame = true + presentationTimeUs = 40500 +sample: + trackIndex = 0 + dataHashCode = -1057414703 + isKeyFrame = true + presentationTimeUs = 41250 +sample: + trackIndex = 0 + dataHashCode = 1568746155 + isKeyFrame = true + presentationTimeUs = 42000 +sample: + trackIndex = 0 + dataHashCode = 1355412472 + isKeyFrame = true + presentationTimeUs = 42750 +sample: + trackIndex = 0 + dataHashCode = 1546368465 + isKeyFrame = true + presentationTimeUs = 43500 +sample: + trackIndex = 0 + dataHashCode = 1811529381 + isKeyFrame = true + presentationTimeUs = 44250 +sample: + trackIndex = 0 + dataHashCode = 658031078 + isKeyFrame = true + presentationTimeUs = 45000 +sample: + trackIndex = 0 + dataHashCode = 1606584486 + isKeyFrame = true + presentationTimeUs = 45750 +sample: + trackIndex = 0 + dataHashCode = 2123252778 + isKeyFrame = true + presentationTimeUs = 46500 +sample: + trackIndex = 0 + dataHashCode = -1364579398 + isKeyFrame = true + presentationTimeUs = 47250 +sample: + trackIndex = 0 + dataHashCode = 1311427887 + isKeyFrame = true + presentationTimeUs = 48000 +sample: + trackIndex = 0 + dataHashCode = -691467569 + isKeyFrame = true + presentationTimeUs = 48750 +sample: + trackIndex = 0 + dataHashCode = 1876470084 + isKeyFrame = true + presentationTimeUs = 49500 +sample: + trackIndex = 0 + dataHashCode = -1472873479 + isKeyFrame = true + presentationTimeUs = 50250 +sample: + trackIndex = 0 + dataHashCode = -143574992 + isKeyFrame = true + presentationTimeUs = 51000 +sample: + trackIndex = 0 + dataHashCode = 984180453 + isKeyFrame = true + presentationTimeUs = 51750 +sample: + trackIndex = 0 + dataHashCode = -113645527 + isKeyFrame = true + presentationTimeUs = 52500 +sample: + trackIndex = 0 + dataHashCode = 1987501641 + isKeyFrame = true + presentationTimeUs = 53250 +sample: + trackIndex = 0 + dataHashCode = -1816426230 + isKeyFrame = true + presentationTimeUs = 54000 +sample: + trackIndex = 0 + dataHashCode = -1250050360 + isKeyFrame = true + presentationTimeUs = 54750 +sample: + trackIndex = 0 + dataHashCode = 1722852790 + isKeyFrame = true + presentationTimeUs = 55500 +sample: + trackIndex = 0 + dataHashCode = 225656333 + isKeyFrame = true + presentationTimeUs = 56250 +sample: + trackIndex = 0 + dataHashCode = -2137778394 + isKeyFrame = true + presentationTimeUs = 57000 +sample: + trackIndex = 0 + dataHashCode = 1433327155 + isKeyFrame = true + presentationTimeUs = 57750 +sample: + trackIndex = 0 + dataHashCode = -974261023 + isKeyFrame = true + presentationTimeUs = 58500 +sample: + trackIndex = 0 + dataHashCode = 1797813317 + isKeyFrame = true + presentationTimeUs = 59250 +sample: + trackIndex = 0 + dataHashCode = -594033497 + isKeyFrame = true + presentationTimeUs = 60000 +sample: + trackIndex = 0 + dataHashCode = -628310540 + isKeyFrame = true + presentationTimeUs = 60750 +sample: + trackIndex = 0 + dataHashCode = 1868627831 + isKeyFrame = true + presentationTimeUs = 61500 +sample: + trackIndex = 0 + dataHashCode = 1051863958 + isKeyFrame = true + presentationTimeUs = 62250 +sample: + trackIndex = 0 + dataHashCode = -1279059211 + isKeyFrame = true + presentationTimeUs = 63000 +sample: + trackIndex = 0 + dataHashCode = 408201874 + isKeyFrame = true + presentationTimeUs = 63750 +sample: + trackIndex = 0 + dataHashCode = 1686644299 + isKeyFrame = true + presentationTimeUs = 64500 +sample: + trackIndex = 0 + dataHashCode = 1288226241 + isKeyFrame = true + presentationTimeUs = 65250 +sample: + trackIndex = 0 + dataHashCode = 432829731 + isKeyFrame = true + presentationTimeUs = 66000 +sample: + trackIndex = 0 + dataHashCode = -1679312600 + isKeyFrame = true + presentationTimeUs = 66750 +sample: + trackIndex = 0 + dataHashCode = 1206680829 + isKeyFrame = true + presentationTimeUs = 67500 +sample: + trackIndex = 0 + dataHashCode = -325844704 + isKeyFrame = true + presentationTimeUs = 68250 +sample: + trackIndex = 0 + dataHashCode = 1941808848 + isKeyFrame = true + presentationTimeUs = 69000 +sample: + trackIndex = 0 + dataHashCode = -87346412 + isKeyFrame = true + presentationTimeUs = 69750 +sample: + trackIndex = 0 + dataHashCode = -329133765 + isKeyFrame = true + presentationTimeUs = 70500 +sample: + trackIndex = 0 + dataHashCode = -1299416212 + isKeyFrame = true + presentationTimeUs = 71250 +sample: + trackIndex = 0 + dataHashCode = -1314599219 + isKeyFrame = true + presentationTimeUs = 72000 +sample: + trackIndex = 0 + dataHashCode = 1456741286 + isKeyFrame = true + presentationTimeUs = 72750 +sample: + trackIndex = 0 + dataHashCode = 151296500 + isKeyFrame = true + presentationTimeUs = 73500 +sample: + trackIndex = 0 + dataHashCode = 1708763603 + isKeyFrame = true + presentationTimeUs = 74250 +sample: + trackIndex = 0 + dataHashCode = 227542220 + isKeyFrame = true + presentationTimeUs = 75000 +sample: + trackIndex = 0 + dataHashCode = 1094305517 + isKeyFrame = true + presentationTimeUs = 75750 +sample: + trackIndex = 0 + dataHashCode = -990377604 + isKeyFrame = true + presentationTimeUs = 76500 +sample: + trackIndex = 0 + dataHashCode = -1798036230 + isKeyFrame = true + presentationTimeUs = 77250 +sample: + trackIndex = 0 + dataHashCode = -1027148291 + isKeyFrame = true + presentationTimeUs = 78000 +sample: + trackIndex = 0 + dataHashCode = 359763976 + isKeyFrame = true + presentationTimeUs = 78750 +sample: + trackIndex = 0 + dataHashCode = 1332016420 + isKeyFrame = true + presentationTimeUs = 79500 +sample: + trackIndex = 0 + dataHashCode = -102753250 + isKeyFrame = true + presentationTimeUs = 80250 +sample: + trackIndex = 0 + dataHashCode = 1959063156 + isKeyFrame = true + presentationTimeUs = 81000 +sample: + trackIndex = 0 + dataHashCode = 2129089853 + isKeyFrame = true + presentationTimeUs = 81750 +sample: + trackIndex = 0 + dataHashCode = 1658742073 + isKeyFrame = true + presentationTimeUs = 82500 +sample: + trackIndex = 0 + dataHashCode = 2136916514 + isKeyFrame = true + presentationTimeUs = 83250 +sample: + trackIndex = 0 + dataHashCode = 105121407 + isKeyFrame = true + presentationTimeUs = 84000 +sample: + trackIndex = 0 + dataHashCode = -839464484 + isKeyFrame = true + presentationTimeUs = 84750 +sample: + trackIndex = 0 + dataHashCode = -1956791168 + isKeyFrame = true + presentationTimeUs = 85500 +sample: + trackIndex = 0 + dataHashCode = -1387546109 + isKeyFrame = true + presentationTimeUs = 86250 +sample: + trackIndex = 0 + dataHashCode = 128410432 + isKeyFrame = true + presentationTimeUs = 87000 +sample: + trackIndex = 0 + dataHashCode = 907081136 + isKeyFrame = true + presentationTimeUs = 87750 +sample: + trackIndex = 0 + dataHashCode = 1124845067 + isKeyFrame = true + presentationTimeUs = 88500 +sample: + trackIndex = 0 + dataHashCode = -1714479962 + isKeyFrame = true + presentationTimeUs = 89250 +sample: + trackIndex = 0 + dataHashCode = 322029323 + isKeyFrame = true + presentationTimeUs = 90000 +sample: + trackIndex = 0 + dataHashCode = -1116281187 + isKeyFrame = true + presentationTimeUs = 90750 +sample: + trackIndex = 0 + dataHashCode = 1571181228 + isKeyFrame = true + presentationTimeUs = 91500 +sample: + trackIndex = 0 + dataHashCode = 997979854 + isKeyFrame = true + presentationTimeUs = 92250 +sample: + trackIndex = 0 + dataHashCode = -1413492413 + isKeyFrame = true + presentationTimeUs = 93000 +sample: + trackIndex = 0 + dataHashCode = -381390490 + isKeyFrame = true + presentationTimeUs = 93750 +sample: + trackIndex = 0 + dataHashCode = -331348340 + isKeyFrame = true + presentationTimeUs = 94500 +sample: + trackIndex = 0 + dataHashCode = -1568238592 + isKeyFrame = true + presentationTimeUs = 95250 +sample: + trackIndex = 0 + dataHashCode = -941591445 + isKeyFrame = true + presentationTimeUs = 96000 +sample: + trackIndex = 0 + dataHashCode = 1616911281 + isKeyFrame = true + presentationTimeUs = 96750 +sample: + trackIndex = 0 + dataHashCode = -1755664741 + isKeyFrame = true + presentationTimeUs = 97500 +sample: + trackIndex = 0 + dataHashCode = -1950609742 + isKeyFrame = true + presentationTimeUs = 98250 +sample: + trackIndex = 0 + dataHashCode = 1476082149 + isKeyFrame = true + presentationTimeUs = 99000 +sample: + trackIndex = 0 + dataHashCode = 1289547483 + isKeyFrame = true + presentationTimeUs = 99750 +sample: + trackIndex = 0 + dataHashCode = -367599018 + isKeyFrame = true + presentationTimeUs = 100500 +sample: + trackIndex = 0 + dataHashCode = 679378334 + isKeyFrame = true + presentationTimeUs = 101250 +sample: + trackIndex = 0 + dataHashCode = 1437306809 + isKeyFrame = true + presentationTimeUs = 102000 +sample: + trackIndex = 0 + dataHashCode = 311988463 + isKeyFrame = true + presentationTimeUs = 102750 +sample: + trackIndex = 0 + dataHashCode = -1870442665 + isKeyFrame = true + presentationTimeUs = 103500 +sample: + trackIndex = 0 + dataHashCode = 1530013920 + isKeyFrame = true + presentationTimeUs = 104250 +sample: + trackIndex = 0 + dataHashCode = -585506443 + isKeyFrame = true + presentationTimeUs = 105000 +sample: + trackIndex = 0 + dataHashCode = -293690558 + isKeyFrame = true + presentationTimeUs = 105750 +sample: + trackIndex = 0 + dataHashCode = -616893325 + isKeyFrame = true + presentationTimeUs = 106500 +sample: + trackIndex = 0 + dataHashCode = 632210495 + isKeyFrame = true + presentationTimeUs = 107250 +sample: + trackIndex = 0 + dataHashCode = -291767937 + isKeyFrame = true + presentationTimeUs = 108000 +sample: + trackIndex = 0 + dataHashCode = -270265 + isKeyFrame = true + presentationTimeUs = 108750 +sample: + trackIndex = 0 + dataHashCode = -1095959376 + isKeyFrame = true + presentationTimeUs = 109500 +sample: + trackIndex = 0 + dataHashCode = -1363867284 + isKeyFrame = true + presentationTimeUs = 110250 +sample: + trackIndex = 0 + dataHashCode = 185415707 + isKeyFrame = true + presentationTimeUs = 111000 +sample: + trackIndex = 0 + dataHashCode = 1033720098 + isKeyFrame = true + presentationTimeUs = 111750 +sample: + trackIndex = 0 + dataHashCode = 1813896085 + isKeyFrame = true + presentationTimeUs = 112500 +sample: + trackIndex = 0 + dataHashCode = -1381192241 + isKeyFrame = true + presentationTimeUs = 113250 +sample: + trackIndex = 0 + dataHashCode = 362689054 + isKeyFrame = true + presentationTimeUs = 114000 +sample: + trackIndex = 0 + dataHashCode = -1320787356 + isKeyFrame = true + presentationTimeUs = 114750 +sample: + trackIndex = 0 + dataHashCode = 1306489379 + isKeyFrame = true + presentationTimeUs = 115500 +sample: + trackIndex = 0 + dataHashCode = -910313430 + isKeyFrame = true + presentationTimeUs = 116250 +sample: + trackIndex = 0 + dataHashCode = -1533334115 + isKeyFrame = true + presentationTimeUs = 117000 +sample: + trackIndex = 0 + dataHashCode = -700061723 + isKeyFrame = true + presentationTimeUs = 117750 +sample: + trackIndex = 0 + dataHashCode = 474100444 + isKeyFrame = true + presentationTimeUs = 118500 +sample: + trackIndex = 0 + dataHashCode = -2096659943 + isKeyFrame = true + presentationTimeUs = 119250 +sample: + trackIndex = 0 + dataHashCode = -690442126 + isKeyFrame = true + presentationTimeUs = 120000 +sample: + trackIndex = 0 + dataHashCode = 158718784 + isKeyFrame = true + presentationTimeUs = 120750 +sample: + trackIndex = 0 + dataHashCode = -1587553019 + isKeyFrame = true + presentationTimeUs = 121500 +sample: + trackIndex = 0 + dataHashCode = 1266916929 + isKeyFrame = true + presentationTimeUs = 122250 +sample: + trackIndex = 0 + dataHashCode = 1947792537 + isKeyFrame = true + presentationTimeUs = 123000 +sample: + trackIndex = 0 + dataHashCode = 2051622372 + isKeyFrame = true + presentationTimeUs = 123750 +sample: + trackIndex = 0 + dataHashCode = 1648973196 + isKeyFrame = true + presentationTimeUs = 124500 +sample: + trackIndex = 0 + dataHashCode = -1119069213 + isKeyFrame = true + presentationTimeUs = 125250 +sample: + trackIndex = 0 + dataHashCode = -1162670307 + isKeyFrame = true + presentationTimeUs = 126000 +sample: + trackIndex = 0 + dataHashCode = 505180178 + isKeyFrame = true + presentationTimeUs = 126750 +sample: + trackIndex = 0 + dataHashCode = -1707111799 + isKeyFrame = true + presentationTimeUs = 127500 +sample: + trackIndex = 0 + dataHashCode = 549350779 + isKeyFrame = true + presentationTimeUs = 128250 +sample: + trackIndex = 0 + dataHashCode = -895461091 + isKeyFrame = true + presentationTimeUs = 129000 +sample: + trackIndex = 0 + dataHashCode = 1834306839 + isKeyFrame = true + presentationTimeUs = 129750 +sample: + trackIndex = 0 + dataHashCode = -646169807 + isKeyFrame = true + presentationTimeUs = 130500 +sample: + trackIndex = 0 + dataHashCode = 123454915 + isKeyFrame = true + presentationTimeUs = 131250 +sample: + trackIndex = 0 + dataHashCode = 2074179659 + isKeyFrame = true + presentationTimeUs = 132000 +sample: + trackIndex = 0 + dataHashCode = 488070546 + isKeyFrame = true + presentationTimeUs = 132750 +sample: + trackIndex = 0 + dataHashCode = -1379245827 + isKeyFrame = true + presentationTimeUs = 133500 +sample: + trackIndex = 0 + dataHashCode = 922846867 + isKeyFrame = true + presentationTimeUs = 134250 +sample: + trackIndex = 0 + dataHashCode = 1163092079 + isKeyFrame = true + presentationTimeUs = 135000 +sample: + trackIndex = 0 + dataHashCode = -817674907 + isKeyFrame = true + presentationTimeUs = 135750 +sample: + trackIndex = 0 + dataHashCode = -765143209 + isKeyFrame = true + presentationTimeUs = 136500 +sample: + trackIndex = 0 + dataHashCode = 1337234415 + isKeyFrame = true + presentationTimeUs = 137250 +sample: + trackIndex = 0 + dataHashCode = 152696122 + isKeyFrame = true + presentationTimeUs = 138000 +sample: + trackIndex = 0 + dataHashCode = -1037369189 + isKeyFrame = true + presentationTimeUs = 138750 +sample: + trackIndex = 0 + dataHashCode = 93852784 + isKeyFrame = true + presentationTimeUs = 139500 +sample: + trackIndex = 0 + dataHashCode = -1512860804 + isKeyFrame = true + presentationTimeUs = 140250 +sample: + trackIndex = 0 + dataHashCode = -1571797975 + isKeyFrame = true + presentationTimeUs = 141000 +sample: + trackIndex = 0 + dataHashCode = -1390710594 + isKeyFrame = true + presentationTimeUs = 141750 +sample: + trackIndex = 0 + dataHashCode = 775548254 + isKeyFrame = true + presentationTimeUs = 142500 +sample: + trackIndex = 0 + dataHashCode = 329825934 + isKeyFrame = true + presentationTimeUs = 143250 +sample: + trackIndex = 0 + dataHashCode = 449672203 + isKeyFrame = true + presentationTimeUs = 144000 +sample: + trackIndex = 0 + dataHashCode = 135215283 + isKeyFrame = true + presentationTimeUs = 144750 +sample: + trackIndex = 0 + dataHashCode = -627202145 + isKeyFrame = true + presentationTimeUs = 145500 +sample: + trackIndex = 0 + dataHashCode = 565795710 + isKeyFrame = true + presentationTimeUs = 146250 +sample: + trackIndex = 0 + dataHashCode = -853390981 + isKeyFrame = true + presentationTimeUs = 147000 +sample: + trackIndex = 0 + dataHashCode = 1904980829 + isKeyFrame = true + presentationTimeUs = 147750 +sample: + trackIndex = 0 + dataHashCode = 1772857005 + isKeyFrame = true + presentationTimeUs = 148500 +sample: + trackIndex = 0 + dataHashCode = -1159621303 + isKeyFrame = true + presentationTimeUs = 149250 +sample: + trackIndex = 0 + dataHashCode = 712585139 + isKeyFrame = true + presentationTimeUs = 150000 +sample: + trackIndex = 0 + dataHashCode = 7470296 + isKeyFrame = true + presentationTimeUs = 150750 +sample: + trackIndex = 0 + dataHashCode = 1154659763 + isKeyFrame = true + presentationTimeUs = 151500 +sample: + trackIndex = 0 + dataHashCode = 512209179 + isKeyFrame = true + presentationTimeUs = 152250 +sample: + trackIndex = 0 + dataHashCode = 2026712081 + isKeyFrame = true + presentationTimeUs = 153000 +sample: + trackIndex = 0 + dataHashCode = -1625715216 + isKeyFrame = true + presentationTimeUs = 153750 +sample: + trackIndex = 0 + dataHashCode = -1299058326 + isKeyFrame = true + presentationTimeUs = 154500 +sample: + trackIndex = 0 + dataHashCode = -813560096 + isKeyFrame = true + presentationTimeUs = 155250 +sample: + trackIndex = 0 + dataHashCode = 1311045251 + isKeyFrame = true + presentationTimeUs = 156000 +sample: + trackIndex = 0 + dataHashCode = 1388107407 + isKeyFrame = true + presentationTimeUs = 156750 +sample: + trackIndex = 0 + dataHashCode = 1113099440 + isKeyFrame = true + presentationTimeUs = 157500 +sample: + trackIndex = 0 + dataHashCode = -339743582 + isKeyFrame = true + presentationTimeUs = 158250 +sample: + trackIndex = 0 + dataHashCode = -1055895345 + isKeyFrame = true + presentationTimeUs = 159000 +sample: + trackIndex = 0 + dataHashCode = 1869841923 + isKeyFrame = true + presentationTimeUs = 159750 +sample: + trackIndex = 0 + dataHashCode = 229443301 + isKeyFrame = true + presentationTimeUs = 160500 +sample: + trackIndex = 0 + dataHashCode = 1526951012 + isKeyFrame = true + presentationTimeUs = 161250 +sample: + trackIndex = 0 + dataHashCode = -1517436626 + isKeyFrame = true + presentationTimeUs = 162000 +sample: + trackIndex = 0 + dataHashCode = -1403405700 + isKeyFrame = true + presentationTimeUs = 162750 +released = true diff --git a/testdata/src/test/assets/transformerdumps/mkv/sample.mkv.dump b/testdata/src/test/assets/transformerdumps/mkv/sample.mkv.dump new file mode 100644 index 0000000000..00d39b034e --- /dev/null +++ b/testdata/src/test/assets/transformerdumps/mkv/sample.mkv.dump @@ -0,0 +1,163 @@ +containerMimeType = video/mp4 +format 0: + id = 1 + sampleMimeType = video/avc + codecs = avc1.640034 + width = 1080 + height = 720 + selectionFlags = 1 + language = und + initializationData: + data = length 30, hash F6F3D010 + data = length 10, hash 7A0D0F2B +sample: + trackIndex = 0 + dataHashCode = -252482306 + isKeyFrame = true + presentationTimeUs = 67000 +sample: + trackIndex = 0 + dataHashCode = 67864034 + isKeyFrame = false + presentationTimeUs = 134000 +sample: + trackIndex = 0 + dataHashCode = 897273234 + isKeyFrame = false + presentationTimeUs = 100000 +sample: + trackIndex = 0 + dataHashCode = -1549870586 + isKeyFrame = false + presentationTimeUs = 267000 +sample: + trackIndex = 0 + dataHashCode = 672384813 + isKeyFrame = false + presentationTimeUs = 200000 +sample: + trackIndex = 0 + dataHashCode = -988996493 + isKeyFrame = false + presentationTimeUs = 167000 +sample: + trackIndex = 0 + dataHashCode = 1711151377 + isKeyFrame = false + presentationTimeUs = 234000 +sample: + trackIndex = 0 + dataHashCode = -506806036 + isKeyFrame = false + presentationTimeUs = 400000 +sample: + trackIndex = 0 + dataHashCode = 1902167649 + isKeyFrame = false + presentationTimeUs = 334000 +sample: + trackIndex = 0 + dataHashCode = 2054873212 + isKeyFrame = false + presentationTimeUs = 300000 +sample: + trackIndex = 0 + dataHashCode = 1556608231 + isKeyFrame = false + presentationTimeUs = 367000 +sample: + trackIndex = 0 + dataHashCode = -1648978019 + isKeyFrame = false + presentationTimeUs = 500000 +sample: + trackIndex = 0 + dataHashCode = -484808327 + isKeyFrame = false + presentationTimeUs = 467000 +sample: + trackIndex = 0 + dataHashCode = -20706048 + isKeyFrame = false + presentationTimeUs = 434000 +sample: + trackIndex = 0 + dataHashCode = 2085064574 + isKeyFrame = false + presentationTimeUs = 634000 +sample: + trackIndex = 0 + dataHashCode = -637074022 + isKeyFrame = false + presentationTimeUs = 567000 +sample: + trackIndex = 0 + dataHashCode = -1824027029 + isKeyFrame = false + presentationTimeUs = 534000 +sample: + trackIndex = 0 + dataHashCode = -1701945306 + isKeyFrame = false + presentationTimeUs = 600000 +sample: + trackIndex = 0 + dataHashCode = -952425536 + isKeyFrame = false + presentationTimeUs = 767000 +sample: + trackIndex = 0 + dataHashCode = -1978031576 + isKeyFrame = false + presentationTimeUs = 700000 +sample: + trackIndex = 0 + dataHashCode = -2128215508 + isKeyFrame = false + presentationTimeUs = 667000 +sample: + trackIndex = 0 + dataHashCode = -259850011 + isKeyFrame = false + presentationTimeUs = 734000 +sample: + trackIndex = 0 + dataHashCode = 1920983928 + isKeyFrame = false + presentationTimeUs = 900000 +sample: + trackIndex = 0 + dataHashCode = 1100642337 + isKeyFrame = false + presentationTimeUs = 834000 +sample: + trackIndex = 0 + dataHashCode = 1544917830 + isKeyFrame = false + presentationTimeUs = 800000 +sample: + trackIndex = 0 + dataHashCode = -116205995 + isKeyFrame = false + presentationTimeUs = 867000 +sample: + trackIndex = 0 + dataHashCode = 696343585 + isKeyFrame = false + presentationTimeUs = 1034000 +sample: + trackIndex = 0 + dataHashCode = -644371190 + isKeyFrame = false + presentationTimeUs = 967000 +sample: + trackIndex = 0 + dataHashCode = -1606273467 + isKeyFrame = false + presentationTimeUs = 934000 +sample: + trackIndex = 0 + dataHashCode = -571265861 + isKeyFrame = false + presentationTimeUs = 1000000 +released = true diff --git a/testdata/src/test/assets/transformerdumps/mkv/sample_with_srt.mkv.dump b/testdata/src/test/assets/transformerdumps/mkv/sample_with_srt.mkv.dump new file mode 100644 index 0000000000..05a19cd924 --- /dev/null +++ b/testdata/src/test/assets/transformerdumps/mkv/sample_with_srt.mkv.dump @@ -0,0 +1,163 @@ +containerMimeType = video/mp4 +format 0: + id = 1 + sampleMimeType = video/avc + codecs = avc1.640034 + width = 1080 + height = 720 + selectionFlags = 1 + language = und + initializationData: + data = length 30, hash F6F3D010 + data = length 10, hash 7A0D0F2B +sample: + trackIndex = 0 + dataHashCode = -252482306 + isKeyFrame = true + presentationTimeUs = 0 +sample: + trackIndex = 0 + dataHashCode = 67864034 + isKeyFrame = false + presentationTimeUs = 67000 +sample: + trackIndex = 0 + dataHashCode = 897273234 + isKeyFrame = false + presentationTimeUs = 33000 +sample: + trackIndex = 0 + dataHashCode = -1549870586 + isKeyFrame = false + presentationTimeUs = 200000 +sample: + trackIndex = 0 + dataHashCode = 672384813 + isKeyFrame = false + presentationTimeUs = 133000 +sample: + trackIndex = 0 + dataHashCode = -988996493 + isKeyFrame = false + presentationTimeUs = 100000 +sample: + trackIndex = 0 + dataHashCode = 1711151377 + isKeyFrame = false + presentationTimeUs = 167000 +sample: + trackIndex = 0 + dataHashCode = -506806036 + isKeyFrame = false + presentationTimeUs = 333000 +sample: + trackIndex = 0 + dataHashCode = 1902167649 + isKeyFrame = false + presentationTimeUs = 267000 +sample: + trackIndex = 0 + dataHashCode = 2054873212 + isKeyFrame = false + presentationTimeUs = 233000 +sample: + trackIndex = 0 + dataHashCode = 1556608231 + isKeyFrame = false + presentationTimeUs = 300000 +sample: + trackIndex = 0 + dataHashCode = -1648978019 + isKeyFrame = false + presentationTimeUs = 433000 +sample: + trackIndex = 0 + dataHashCode = -484808327 + isKeyFrame = false + presentationTimeUs = 400000 +sample: + trackIndex = 0 + dataHashCode = -20706048 + isKeyFrame = false + presentationTimeUs = 367000 +sample: + trackIndex = 0 + dataHashCode = 2085064574 + isKeyFrame = false + presentationTimeUs = 567000 +sample: + trackIndex = 0 + dataHashCode = -637074022 + isKeyFrame = false + presentationTimeUs = 500000 +sample: + trackIndex = 0 + dataHashCode = -1824027029 + isKeyFrame = false + presentationTimeUs = 467000 +sample: + trackIndex = 0 + dataHashCode = -1701945306 + isKeyFrame = false + presentationTimeUs = 533000 +sample: + trackIndex = 0 + dataHashCode = -952425536 + isKeyFrame = false + presentationTimeUs = 700000 +sample: + trackIndex = 0 + dataHashCode = -1978031576 + isKeyFrame = false + presentationTimeUs = 633000 +sample: + trackIndex = 0 + dataHashCode = -2128215508 + isKeyFrame = false + presentationTimeUs = 600000 +sample: + trackIndex = 0 + dataHashCode = -259850011 + isKeyFrame = false + presentationTimeUs = 667000 +sample: + trackIndex = 0 + dataHashCode = 1920983928 + isKeyFrame = false + presentationTimeUs = 833000 +sample: + trackIndex = 0 + dataHashCode = 1100642337 + isKeyFrame = false + presentationTimeUs = 767000 +sample: + trackIndex = 0 + dataHashCode = 1544917830 + isKeyFrame = false + presentationTimeUs = 733000 +sample: + trackIndex = 0 + dataHashCode = -116205995 + isKeyFrame = false + presentationTimeUs = 800000 +sample: + trackIndex = 0 + dataHashCode = 696343585 + isKeyFrame = false + presentationTimeUs = 967000 +sample: + trackIndex = 0 + dataHashCode = -644371190 + isKeyFrame = false + presentationTimeUs = 900000 +sample: + trackIndex = 0 + dataHashCode = -1606273467 + isKeyFrame = false + presentationTimeUs = 867000 +sample: + trackIndex = 0 + dataHashCode = -571265861 + isKeyFrame = false + presentationTimeUs = 933000 +released = true diff --git a/testdata/src/test/assets/transformerdumps/mp4/sample.mp4.dump b/testdata/src/test/assets/transformerdumps/mp4/sample.mp4.dump new file mode 100644 index 0000000000..4ccbeae3d7 --- /dev/null +++ b/testdata/src/test/assets/transformerdumps/mp4/sample.mp4.dump @@ -0,0 +1,392 @@ +containerMimeType = video/mp4 +format 0: + sampleMimeType = audio/mp4a-latm + channelCount = 1 + sampleRate = 44100 +format 1: + id = 1 + sampleMimeType = video/avc + codecs = avc1.64001F + maxInputSize = 36722 + width = 1080 + height = 720 + frameRate = 29.970028 + initializationData: + data = length 29, hash 4746B5D9 + data = length 10, hash 7A0D0F2B +sample: + trackIndex = 1 + dataHashCode = -770308242 + isKeyFrame = true + presentationTimeUs = 0 +sample: + trackIndex = 1 + dataHashCode = -732087136 + isKeyFrame = false + presentationTimeUs = 66733 +sample: + trackIndex = 1 + dataHashCode = 468156717 + isKeyFrame = false + presentationTimeUs = 33366 +sample: + trackIndex = 1 + dataHashCode = 1150349584 + isKeyFrame = false + presentationTimeUs = 200200 +sample: + trackIndex = 1 + dataHashCode = 1443582006 + isKeyFrame = false + presentationTimeUs = 133466 +sample: + trackIndex = 1 + dataHashCode = -310585145 + isKeyFrame = false + presentationTimeUs = 100100 +sample: + trackIndex = 1 + dataHashCode = 807460688 + isKeyFrame = false + presentationTimeUs = 166833 +sample: + trackIndex = 1 + dataHashCode = 1936487090 + isKeyFrame = false + presentationTimeUs = 333666 +sample: + trackIndex = 1 + dataHashCode = -32297181 + isKeyFrame = false + presentationTimeUs = 266933 +sample: + trackIndex = 1 + dataHashCode = 1529616406 + isKeyFrame = false + presentationTimeUs = 233566 +sample: + trackIndex = 1 + dataHashCode = 1949198785 + isKeyFrame = false + presentationTimeUs = 300300 +sample: + trackIndex = 1 + dataHashCode = -147880287 + isKeyFrame = false + presentationTimeUs = 433766 +sample: + trackIndex = 1 + dataHashCode = 1369083472 + isKeyFrame = false + presentationTimeUs = 400400 +sample: + trackIndex = 1 + dataHashCode = 965782073 + isKeyFrame = false + presentationTimeUs = 367033 +sample: + trackIndex = 1 + dataHashCode = -261176150 + isKeyFrame = false + presentationTimeUs = 567233 +sample: + trackIndex = 0 + dataHashCode = 1205768497 + isKeyFrame = true + presentationTimeUs = 0 +sample: + trackIndex = 0 + dataHashCode = 837571078 + isKeyFrame = true + presentationTimeUs = 249 +sample: + trackIndex = 0 + dataHashCode = -1991633045 + isKeyFrame = true + presentationTimeUs = 317 +sample: + trackIndex = 0 + dataHashCode = -822987359 + isKeyFrame = true + presentationTimeUs = 1995 +sample: + trackIndex = 0 + dataHashCode = -1141508176 + isKeyFrame = true + presentationTimeUs = 4126 +sample: + trackIndex = 0 + dataHashCode = -226971245 + isKeyFrame = true + presentationTimeUs = 6438 +sample: + trackIndex = 0 + dataHashCode = -2099636855 + isKeyFrame = true + presentationTimeUs = 8818 +sample: + trackIndex = 0 + dataHashCode = 1541550559 + isKeyFrame = true + presentationTimeUs = 11198 +sample: + trackIndex = 0 + dataHashCode = 411148001 + isKeyFrame = true + presentationTimeUs = 13533 +sample: + trackIndex = 0 + dataHashCode = -897603973 + isKeyFrame = true + presentationTimeUs = 16072 +sample: + trackIndex = 0 + dataHashCode = 1478106136 + isKeyFrame = true + presentationTimeUs = 18498 +sample: + trackIndex = 0 + dataHashCode = -1380417145 + isKeyFrame = true + presentationTimeUs = 20878 +sample: + trackIndex = 0 + dataHashCode = 780903644 + isKeyFrame = true + presentationTimeUs = 23326 +sample: + trackIndex = 0 + dataHashCode = 586204432 + isKeyFrame = true + presentationTimeUs = 25911 +sample: + trackIndex = 0 + dataHashCode = -2038771492 + isKeyFrame = true + presentationTimeUs = 28541 +sample: + trackIndex = 0 + dataHashCode = -2065161304 + isKeyFrame = true + presentationTimeUs = 31194 +sample: + trackIndex = 0 + dataHashCode = 468662933 + isKeyFrame = true + presentationTimeUs = 33801 +sample: + trackIndex = 0 + dataHashCode = -358398546 + isKeyFrame = true + presentationTimeUs = 36363 +sample: + trackIndex = 0 + dataHashCode = 1767325983 + isKeyFrame = true + presentationTimeUs = 38811 +sample: + trackIndex = 0 + dataHashCode = 1093095458 + isKeyFrame = true + presentationTimeUs = 41396 +sample: + trackIndex = 0 + dataHashCode = 1687543702 + isKeyFrame = true + presentationTimeUs = 43867 +sample: + trackIndex = 0 + dataHashCode = 1675188486 + isKeyFrame = true + presentationTimeUs = 46588 +sample: + trackIndex = 0 + dataHashCode = 888567545 + isKeyFrame = true + presentationTimeUs = 49173 +sample: + trackIndex = 0 + dataHashCode = -439631803 + isKeyFrame = true + presentationTimeUs = 51871 +sample: + trackIndex = 0 + dataHashCode = 1606694497 + isKeyFrame = true + presentationTimeUs = 54524 +sample: + trackIndex = 0 + dataHashCode = 1747388653 + isKeyFrame = true + presentationTimeUs = 57131 +sample: + trackIndex = 0 + dataHashCode = -734560004 + isKeyFrame = true + presentationTimeUs = 59579 +sample: + trackIndex = 0 + dataHashCode = -975079040 + isKeyFrame = true + presentationTimeUs = 62277 +sample: + trackIndex = 0 + dataHashCode = -1403504710 + isKeyFrame = true + presentationTimeUs = 65020 +sample: + trackIndex = 0 + dataHashCode = 379512981 + isKeyFrame = true + presentationTimeUs = 67627 +sample: + trackIndex = 1 + dataHashCode = -1830836678 + isKeyFrame = false + presentationTimeUs = 500500 +sample: + trackIndex = 1 + dataHashCode = 1767407540 + isKeyFrame = false + presentationTimeUs = 467133 +sample: + trackIndex = 1 + dataHashCode = 918440283 + isKeyFrame = false + presentationTimeUs = 533866 +sample: + trackIndex = 1 + dataHashCode = -1408463661 + isKeyFrame = false + presentationTimeUs = 700700 +sample: + trackIndex = 0 + dataHashCode = -997198863 + isKeyFrame = true + presentationTimeUs = 70234 +sample: + trackIndex = 0 + dataHashCode = 1394492825 + isKeyFrame = true + presentationTimeUs = 72932 +sample: + trackIndex = 0 + dataHashCode = -885232755 + isKeyFrame = true + presentationTimeUs = 75471 +sample: + trackIndex = 0 + dataHashCode = 260871367 + isKeyFrame = true + presentationTimeUs = 78101 +sample: + trackIndex = 0 + dataHashCode = -1505318960 + isKeyFrame = true + presentationTimeUs = 80844 +sample: + trackIndex = 0 + dataHashCode = -390625371 + isKeyFrame = true + presentationTimeUs = 83474 +sample: + trackIndex = 0 + dataHashCode = 1067950751 + isKeyFrame = true + presentationTimeUs = 86149 +sample: + trackIndex = 0 + dataHashCode = -1179436278 + isKeyFrame = true + presentationTimeUs = 88734 +sample: + trackIndex = 0 + dataHashCode = 1906607774 + isKeyFrame = true + presentationTimeUs = 91387 +sample: + trackIndex = 0 + dataHashCode = -800475828 + isKeyFrame = true + presentationTimeUs = 94380 +sample: + trackIndex = 0 + dataHashCode = 1718972977 + isKeyFrame = true + presentationTimeUs = 97282 +sample: + trackIndex = 0 + dataHashCode = -1120448741 + isKeyFrame = true + presentationTimeUs = 99844 +sample: + trackIndex = 0 + dataHashCode = -1718323210 + isKeyFrame = true + presentationTimeUs = 102406 +sample: + trackIndex = 0 + dataHashCode = -422416 + isKeyFrame = true + presentationTimeUs = 105059 +sample: + trackIndex = 0 + dataHashCode = 833757830 + isKeyFrame = true + presentationTimeUs = 107644 +sample: + trackIndex = 1 + dataHashCode = 1569455924 + isKeyFrame = false + presentationTimeUs = 633966 +sample: + trackIndex = 1 + dataHashCode = -1723778407 + isKeyFrame = false + presentationTimeUs = 600600 +sample: + trackIndex = 1 + dataHashCode = 1578275472 + isKeyFrame = false + presentationTimeUs = 667333 +sample: + trackIndex = 1 + dataHashCode = 1989768395 + isKeyFrame = false + presentationTimeUs = 834166 +sample: + trackIndex = 1 + dataHashCode = -1215674502 + isKeyFrame = false + presentationTimeUs = 767433 +sample: + trackIndex = 1 + dataHashCode = -814473606 + isKeyFrame = false + presentationTimeUs = 734066 +sample: + trackIndex = 1 + dataHashCode = 498370894 + isKeyFrame = false + presentationTimeUs = 800800 +sample: + trackIndex = 1 + dataHashCode = -1051506468 + isKeyFrame = false + presentationTimeUs = 967633 +sample: + trackIndex = 1 + dataHashCode = -1025604144 + isKeyFrame = false + presentationTimeUs = 900900 +sample: + trackIndex = 1 + dataHashCode = -913586520 + isKeyFrame = false + presentationTimeUs = 867533 +sample: + trackIndex = 1 + dataHashCode = 1340459242 + isKeyFrame = false + presentationTimeUs = 934266 +released = true diff --git a/testdata/src/test/assets/transformerdumps/mp4/sample.mp4.noaudio.dump b/testdata/src/test/assets/transformerdumps/mp4/sample.mp4.noaudio.dump new file mode 100644 index 0000000000..d4484cbfb4 --- /dev/null +++ b/testdata/src/test/assets/transformerdumps/mp4/sample.mp4.noaudio.dump @@ -0,0 +1,163 @@ +containerMimeType = video/mp4 +format 0: + id = 1 + sampleMimeType = video/avc + codecs = avc1.64001F + maxInputSize = 36722 + width = 1080 + height = 720 + frameRate = 29.970028 + initializationData: + data = length 29, hash 4746B5D9 + data = length 10, hash 7A0D0F2B +sample: + trackIndex = 0 + dataHashCode = -770308242 + isKeyFrame = true + presentationTimeUs = 0 +sample: + trackIndex = 0 + dataHashCode = -732087136 + isKeyFrame = false + presentationTimeUs = 66733 +sample: + trackIndex = 0 + dataHashCode = 468156717 + isKeyFrame = false + presentationTimeUs = 33366 +sample: + trackIndex = 0 + dataHashCode = 1150349584 + isKeyFrame = false + presentationTimeUs = 200200 +sample: + trackIndex = 0 + dataHashCode = 1443582006 + isKeyFrame = false + presentationTimeUs = 133466 +sample: + trackIndex = 0 + dataHashCode = -310585145 + isKeyFrame = false + presentationTimeUs = 100100 +sample: + trackIndex = 0 + dataHashCode = 807460688 + isKeyFrame = false + presentationTimeUs = 166833 +sample: + trackIndex = 0 + dataHashCode = 1936487090 + isKeyFrame = false + presentationTimeUs = 333666 +sample: + trackIndex = 0 + dataHashCode = -32297181 + isKeyFrame = false + presentationTimeUs = 266933 +sample: + trackIndex = 0 + dataHashCode = 1529616406 + isKeyFrame = false + presentationTimeUs = 233566 +sample: + trackIndex = 0 + dataHashCode = 1949198785 + isKeyFrame = false + presentationTimeUs = 300300 +sample: + trackIndex = 0 + dataHashCode = -147880287 + isKeyFrame = false + presentationTimeUs = 433766 +sample: + trackIndex = 0 + dataHashCode = 1369083472 + isKeyFrame = false + presentationTimeUs = 400400 +sample: + trackIndex = 0 + dataHashCode = 965782073 + isKeyFrame = false + presentationTimeUs = 367033 +sample: + trackIndex = 0 + dataHashCode = -261176150 + isKeyFrame = false + presentationTimeUs = 567233 +sample: + trackIndex = 0 + dataHashCode = -1830836678 + isKeyFrame = false + presentationTimeUs = 500500 +sample: + trackIndex = 0 + dataHashCode = 1767407540 + isKeyFrame = false + presentationTimeUs = 467133 +sample: + trackIndex = 0 + dataHashCode = 918440283 + isKeyFrame = false + presentationTimeUs = 533866 +sample: + trackIndex = 0 + dataHashCode = -1408463661 + isKeyFrame = false + presentationTimeUs = 700700 +sample: + trackIndex = 0 + dataHashCode = 1569455924 + isKeyFrame = false + presentationTimeUs = 633966 +sample: + trackIndex = 0 + dataHashCode = -1723778407 + isKeyFrame = false + presentationTimeUs = 600600 +sample: + trackIndex = 0 + dataHashCode = 1578275472 + isKeyFrame = false + presentationTimeUs = 667333 +sample: + trackIndex = 0 + dataHashCode = 1989768395 + isKeyFrame = false + presentationTimeUs = 834166 +sample: + trackIndex = 0 + dataHashCode = -1215674502 + isKeyFrame = false + presentationTimeUs = 767433 +sample: + trackIndex = 0 + dataHashCode = -814473606 + isKeyFrame = false + presentationTimeUs = 734066 +sample: + trackIndex = 0 + dataHashCode = 498370894 + isKeyFrame = false + presentationTimeUs = 800800 +sample: + trackIndex = 0 + dataHashCode = -1051506468 + isKeyFrame = false + presentationTimeUs = 967633 +sample: + trackIndex = 0 + dataHashCode = -1025604144 + isKeyFrame = false + presentationTimeUs = 900900 +sample: + trackIndex = 0 + dataHashCode = -913586520 + isKeyFrame = false + presentationTimeUs = 867533 +sample: + trackIndex = 0 + dataHashCode = 1340459242 + isKeyFrame = false + presentationTimeUs = 934266 +released = true diff --git a/testdata/src/test/assets/transformerdumps/mp4/sample.mp4.novideo.dump b/testdata/src/test/assets/transformerdumps/mp4/sample.mp4.novideo.dump new file mode 100644 index 0000000000..25e3aa791b --- /dev/null +++ b/testdata/src/test/assets/transformerdumps/mp4/sample.mp4.novideo.dump @@ -0,0 +1,231 @@ +containerMimeType = video/mp4 +format 0: + sampleMimeType = audio/mp4a-latm + channelCount = 1 + sampleRate = 44100 +sample: + trackIndex = 0 + dataHashCode = 1205768497 + isKeyFrame = true + presentationTimeUs = 0 +sample: + trackIndex = 0 + dataHashCode = 837571078 + isKeyFrame = true + presentationTimeUs = 249 +sample: + trackIndex = 0 + dataHashCode = -1991633045 + isKeyFrame = true + presentationTimeUs = 317 +sample: + trackIndex = 0 + dataHashCode = -822987359 + isKeyFrame = true + presentationTimeUs = 1995 +sample: + trackIndex = 0 + dataHashCode = -1141508176 + isKeyFrame = true + presentationTimeUs = 4126 +sample: + trackIndex = 0 + dataHashCode = -226971245 + isKeyFrame = true + presentationTimeUs = 6438 +sample: + trackIndex = 0 + dataHashCode = -2099636855 + isKeyFrame = true + presentationTimeUs = 8818 +sample: + trackIndex = 0 + dataHashCode = 1541550559 + isKeyFrame = true + presentationTimeUs = 11198 +sample: + trackIndex = 0 + dataHashCode = 411148001 + isKeyFrame = true + presentationTimeUs = 13533 +sample: + trackIndex = 0 + dataHashCode = -897603973 + isKeyFrame = true + presentationTimeUs = 16072 +sample: + trackIndex = 0 + dataHashCode = 1478106136 + isKeyFrame = true + presentationTimeUs = 18498 +sample: + trackIndex = 0 + dataHashCode = -1380417145 + isKeyFrame = true + presentationTimeUs = 20878 +sample: + trackIndex = 0 + dataHashCode = 780903644 + isKeyFrame = true + presentationTimeUs = 23326 +sample: + trackIndex = 0 + dataHashCode = 586204432 + isKeyFrame = true + presentationTimeUs = 25911 +sample: + trackIndex = 0 + dataHashCode = -2038771492 + isKeyFrame = true + presentationTimeUs = 28541 +sample: + trackIndex = 0 + dataHashCode = -2065161304 + isKeyFrame = true + presentationTimeUs = 31194 +sample: + trackIndex = 0 + dataHashCode = 468662933 + isKeyFrame = true + presentationTimeUs = 33801 +sample: + trackIndex = 0 + dataHashCode = -358398546 + isKeyFrame = true + presentationTimeUs = 36363 +sample: + trackIndex = 0 + dataHashCode = 1767325983 + isKeyFrame = true + presentationTimeUs = 38811 +sample: + trackIndex = 0 + dataHashCode = 1093095458 + isKeyFrame = true + presentationTimeUs = 41396 +sample: + trackIndex = 0 + dataHashCode = 1687543702 + isKeyFrame = true + presentationTimeUs = 43867 +sample: + trackIndex = 0 + dataHashCode = 1675188486 + isKeyFrame = true + presentationTimeUs = 46588 +sample: + trackIndex = 0 + dataHashCode = 888567545 + isKeyFrame = true + presentationTimeUs = 49173 +sample: + trackIndex = 0 + dataHashCode = -439631803 + isKeyFrame = true + presentationTimeUs = 51871 +sample: + trackIndex = 0 + dataHashCode = 1606694497 + isKeyFrame = true + presentationTimeUs = 54524 +sample: + trackIndex = 0 + dataHashCode = 1747388653 + isKeyFrame = true + presentationTimeUs = 57131 +sample: + trackIndex = 0 + dataHashCode = -734560004 + isKeyFrame = true + presentationTimeUs = 59579 +sample: + trackIndex = 0 + dataHashCode = -975079040 + isKeyFrame = true + presentationTimeUs = 62277 +sample: + trackIndex = 0 + dataHashCode = -1403504710 + isKeyFrame = true + presentationTimeUs = 65020 +sample: + trackIndex = 0 + dataHashCode = 379512981 + isKeyFrame = true + presentationTimeUs = 67627 +sample: + trackIndex = 0 + dataHashCode = -997198863 + isKeyFrame = true + presentationTimeUs = 70234 +sample: + trackIndex = 0 + dataHashCode = 1394492825 + isKeyFrame = true + presentationTimeUs = 72932 +sample: + trackIndex = 0 + dataHashCode = -885232755 + isKeyFrame = true + presentationTimeUs = 75471 +sample: + trackIndex = 0 + dataHashCode = 260871367 + isKeyFrame = true + presentationTimeUs = 78101 +sample: + trackIndex = 0 + dataHashCode = -1505318960 + isKeyFrame = true + presentationTimeUs = 80844 +sample: + trackIndex = 0 + dataHashCode = -390625371 + isKeyFrame = true + presentationTimeUs = 83474 +sample: + trackIndex = 0 + dataHashCode = 1067950751 + isKeyFrame = true + presentationTimeUs = 86149 +sample: + trackIndex = 0 + dataHashCode = -1179436278 + isKeyFrame = true + presentationTimeUs = 88734 +sample: + trackIndex = 0 + dataHashCode = 1906607774 + isKeyFrame = true + presentationTimeUs = 91387 +sample: + trackIndex = 0 + dataHashCode = -800475828 + isKeyFrame = true + presentationTimeUs = 94380 +sample: + trackIndex = 0 + dataHashCode = 1718972977 + isKeyFrame = true + presentationTimeUs = 97282 +sample: + trackIndex = 0 + dataHashCode = -1120448741 + isKeyFrame = true + presentationTimeUs = 99844 +sample: + trackIndex = 0 + dataHashCode = -1718323210 + isKeyFrame = true + presentationTimeUs = 102406 +sample: + trackIndex = 0 + dataHashCode = -422416 + isKeyFrame = true + presentationTimeUs = 105059 +sample: + trackIndex = 0 + dataHashCode = 833757830 + isKeyFrame = true + presentationTimeUs = 107644 +released = true diff --git a/testdata/src/test/assets/transformerdumps/mp4/sample_sef_slow_motion.mp4.dump b/testdata/src/test/assets/transformerdumps/mp4/sample_sef_slow_motion.mp4.dump new file mode 100644 index 0000000000..5262f11ea1 --- /dev/null +++ b/testdata/src/test/assets/transformerdumps/mp4/sample_sef_slow_motion.mp4.dump @@ -0,0 +1,188 @@ +containerMimeType = video/mp4 +format 0: + sampleMimeType = audio/mp4a-latm + channelCount = 2 + sampleRate = 12000 +format 1: + id = 2 + sampleMimeType = video/avc + codecs = avc1.64000D + maxInputSize = 5476 + width = 320 + height = 240 + frameRate = 29.523811 + metadata = entries=[mdta: key=com.android.capture.fps, smta: captureFrameRate=240.0, svcTemporalLayerCount=4, SlowMotion: segments=[Segment: startTimeMs=88, endTimeMs=879, speedDivisor=2, Segment: startTimeMs=1255, endTimeMs=1970, speedDivisor=8]] + initializationData: + data = length 33, hash D3FB879D + data = length 10, hash 7A0D0F2B +sample: + trackIndex = 1 + dataHashCode = -549003117 + isKeyFrame = true + presentationTimeUs = 0 +sample: + trackIndex = 1 + dataHashCode = 593600631 + isKeyFrame = false + presentationTimeUs = 14000 +sample: + trackIndex = 1 + dataHashCode = -961321612 + isKeyFrame = false + presentationTimeUs = 47333 +sample: + trackIndex = 1 + dataHashCode = -386347143 + isKeyFrame = false + presentationTimeUs = 80667 +sample: + trackIndex = 1 + dataHashCode = -1289764147 + isKeyFrame = false + presentationTimeUs = 114000 +sample: + trackIndex = 1 + dataHashCode = 1337088875 + isKeyFrame = false + presentationTimeUs = 147333 +sample: + trackIndex = 1 + dataHashCode = -322406979 + isKeyFrame = false + presentationTimeUs = 180667 +sample: + trackIndex = 1 + dataHashCode = -1688033783 + isKeyFrame = false + presentationTimeUs = 228042 +sample: + trackIndex = 1 + dataHashCode = -700344608 + isKeyFrame = false + presentationTimeUs = 244708 +sample: + trackIndex = 1 + dataHashCode = -1441653629 + isKeyFrame = false + presentationTimeUs = 334083 +sample: + trackIndex = 1 + dataHashCode = 1201357091 + isKeyFrame = false + presentationTimeUs = 267416 +sample: + trackIndex = 1 + dataHashCode = -668484307 + isKeyFrame = false + presentationTimeUs = 234083 +sample: + trackIndex = 1 + dataHashCode = 653508165 + isKeyFrame = false + presentationTimeUs = 300750 +sample: + trackIndex = 1 + dataHashCode = -816848987 + isKeyFrame = false + presentationTimeUs = 467416 +sample: + trackIndex = 1 + dataHashCode = 1842436292 + isKeyFrame = false + presentationTimeUs = 400750 +sample: + trackIndex = 1 + dataHashCode = -559603233 + isKeyFrame = false + presentationTimeUs = 367416 +sample: + trackIndex = 1 + dataHashCode = -666437886 + isKeyFrame = false + presentationTimeUs = 434083 +sample: + trackIndex = 1 + dataHashCode = 182521759 + isKeyFrame = false + presentationTimeUs = 600750 +sample: + trackIndex = 0 + dataHashCode = -212376212 + isKeyFrame = true + presentationTimeUs = 0 +sample: + trackIndex = 0 + dataHashCode = -833872563 + isKeyFrame = true + presentationTimeUs = 416 +sample: + trackIndex = 0 + dataHashCode = -135901925 + isKeyFrame = true + presentationTimeUs = 36499 +sample: + trackIndex = 0 + dataHashCode = -2124187794 + isKeyFrame = true + presentationTimeUs = 44415 +sample: + trackIndex = 0 + dataHashCode = 1016665126 + isKeyFrame = true + presentationTimeUs = 63081 +sample: + trackIndex = 1 + dataHashCode = 2139021989 + isKeyFrame = false + presentationTimeUs = 534083 +sample: + trackIndex = 1 + dataHashCode = 2013165108 + isKeyFrame = false + presentationTimeUs = 500750 +sample: + trackIndex = 1 + dataHashCode = 405675195 + isKeyFrame = false + presentationTimeUs = 567416 +sample: + trackIndex = 1 + dataHashCode = -1893277090 + isKeyFrame = false + presentationTimeUs = 734083 +sample: + trackIndex = 1 + dataHashCode = -1554795381 + isKeyFrame = false + presentationTimeUs = 667416 +sample: + trackIndex = 1 + dataHashCode = 1197099206 + isKeyFrame = false + presentationTimeUs = 634083 +sample: + trackIndex = 1 + dataHashCode = -674808173 + isKeyFrame = false + presentationTimeUs = 700750 +sample: + trackIndex = 1 + dataHashCode = -775517313 + isKeyFrame = false + presentationTimeUs = 867416 +sample: + trackIndex = 1 + dataHashCode = -2045106113 + isKeyFrame = false + presentationTimeUs = 800750 +sample: + trackIndex = 1 + dataHashCode = 305167697 + isKeyFrame = false + presentationTimeUs = 767416 +sample: + trackIndex = 1 + dataHashCode = 554021920 + isKeyFrame = false + presentationTimeUs = 834083 +released = true diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/DumpableFormat.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/DumpableFormat.java new file mode 100644 index 0000000000..80c211914a --- /dev/null +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/DumpableFormat.java @@ -0,0 +1,101 @@ +/* + * Copyright 2021 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 com.google.android.exoplayer2.testutil; + +import androidx.annotation.Nullable; +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.util.Util; +import com.google.common.base.Function; +import org.checkerframework.checker.nullness.compatqual.NullableType; + +/** Wraps a {@link Format} to allow dumping it. */ +public final class DumpableFormat implements Dumper.Dumpable { + private final Format format; + public final int index; + + private static final Format DEFAULT_FORMAT = new Format.Builder().build(); + + public DumpableFormat(Format format, int index) { + this.format = format; + this.index = index; + } + + @Override + public void dump(Dumper dumper) { + dumper.startBlock("format " + index); + addIfNonDefault(dumper, "averageBitrate", format -> format.averageBitrate); + addIfNonDefault(dumper, "peakBitrate", format -> format.peakBitrate); + addIfNonDefault(dumper, "id", format -> format.id); + addIfNonDefault(dumper, "containerMimeType", format -> format.containerMimeType); + addIfNonDefault(dumper, "sampleMimeType", format -> format.sampleMimeType); + addIfNonDefault(dumper, "codecs", format -> format.codecs); + addIfNonDefault(dumper, "maxInputSize", format -> format.maxInputSize); + addIfNonDefault(dumper, "width", format -> format.width); + addIfNonDefault(dumper, "height", format -> format.height); + addIfNonDefault(dumper, "frameRate", format -> format.frameRate); + addIfNonDefault(dumper, "rotationDegrees", format -> format.rotationDegrees); + addIfNonDefault(dumper, "pixelWidthHeightRatio", format -> format.pixelWidthHeightRatio); + addIfNonDefault(dumper, "channelCount", format -> format.channelCount); + addIfNonDefault(dumper, "sampleRate", format -> format.sampleRate); + addIfNonDefault(dumper, "pcmEncoding", format -> format.pcmEncoding); + addIfNonDefault(dumper, "encoderDelay", format -> format.encoderDelay); + addIfNonDefault(dumper, "encoderPadding", format -> format.encoderPadding); + addIfNonDefault(dumper, "subsampleOffsetUs", format -> format.subsampleOffsetUs); + addIfNonDefault(dumper, "selectionFlags", format -> format.selectionFlags); + addIfNonDefault(dumper, "language", format -> format.language); + addIfNonDefault(dumper, "label", format -> format.label); + if (format.drmInitData != null) { + dumper.add("drmInitData", format.drmInitData.hashCode()); + } + addIfNonDefault(dumper, "metadata", format -> format.metadata); + if (!format.initializationData.isEmpty()) { + dumper.startBlock("initializationData"); + for (int i = 0; i < format.initializationData.size(); i++) { + dumper.add("data", format.initializationData.get(i)); + } + dumper.endBlock(); + } + dumper.endBlock(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DumpableFormat that = (DumpableFormat) o; + return index == that.index && format.equals(that.format); + } + + @Override + public int hashCode() { + int result = format.hashCode(); + result = 31 * result + index; + return result; + } + + private void addIfNonDefault( + Dumper dumper, String field, Function getFieldFunction) { + @Nullable Object thisValue = getFieldFunction.apply(format); + @Nullable Object defaultValue = getFieldFunction.apply(DEFAULT_FORMAT); + if (!Util.areEqual(thisValue, defaultValue)) { + dumper.add(field, thisValue); + } + } +} diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackOutput.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackOutput.java index 4e636d993c..a5a88da03e 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackOutput.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackOutput.java @@ -26,7 +26,6 @@ import com.google.android.exoplayer2.upstream.DataReader; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; -import com.google.common.base.Function; import com.google.common.primitives.Bytes; import java.io.EOFException; import java.io.IOException; @@ -34,7 +33,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.checkerframework.checker.nullness.compatqual.NullableType; /** A fake {@link TrackOutput}. */ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable { @@ -284,81 +282,4 @@ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable { } } - private static final class DumpableFormat implements Dumper.Dumpable { - private final Format format; - public final int index; - - private static final Format DEFAULT_FORMAT = new Format.Builder().build(); - - public DumpableFormat(Format format, int index) { - this.format = format; - this.index = index; - } - - @Override - public void dump(Dumper dumper) { - dumper.startBlock("format " + index); - addIfNonDefault(dumper, "averageBitrate", format -> format.averageBitrate); - addIfNonDefault(dumper, "peakBitrate", format -> format.peakBitrate); - addIfNonDefault(dumper, "id", format -> format.id); - addIfNonDefault(dumper, "containerMimeType", format -> format.containerMimeType); - addIfNonDefault(dumper, "sampleMimeType", format -> format.sampleMimeType); - addIfNonDefault(dumper, "codecs", format -> format.codecs); - addIfNonDefault(dumper, "maxInputSize", format -> format.maxInputSize); - addIfNonDefault(dumper, "width", format -> format.width); - addIfNonDefault(dumper, "height", format -> format.height); - addIfNonDefault(dumper, "frameRate", format -> format.frameRate); - addIfNonDefault(dumper, "rotationDegrees", format -> format.rotationDegrees); - addIfNonDefault(dumper, "pixelWidthHeightRatio", format -> format.pixelWidthHeightRatio); - addIfNonDefault(dumper, "channelCount", format -> format.channelCount); - addIfNonDefault(dumper, "sampleRate", format -> format.sampleRate); - addIfNonDefault(dumper, "pcmEncoding", format -> format.pcmEncoding); - addIfNonDefault(dumper, "encoderDelay", format -> format.encoderDelay); - addIfNonDefault(dumper, "encoderPadding", format -> format.encoderPadding); - addIfNonDefault(dumper, "subsampleOffsetUs", format -> format.subsampleOffsetUs); - addIfNonDefault(dumper, "selectionFlags", format -> format.selectionFlags); - addIfNonDefault(dumper, "language", format -> format.language); - addIfNonDefault(dumper, "label", format -> format.label); - if (format.drmInitData != null) { - dumper.add("drmInitData", format.drmInitData.hashCode()); - } - addIfNonDefault(dumper, "metadata", format -> format.metadata); - if (!format.initializationData.isEmpty()) { - dumper.startBlock("initializationData"); - for (int i = 0; i < format.initializationData.size(); i++) { - dumper.add("data", format.initializationData.get(i)); - } - dumper.endBlock(); - } - dumper.endBlock(); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - DumpableFormat that = (DumpableFormat) o; - return index == that.index && format.equals(that.format); - } - - @Override - public int hashCode() { - int result = format.hashCode(); - result = 31 * result + index; - return result; - } - - private void addIfNonDefault( - Dumper dumper, String field, Function getFieldFunction) { - @Nullable Object thisValue = getFieldFunction.apply(format); - @Nullable Object defaultValue = getFieldFunction.apply(DEFAULT_FORMAT); - if (!Util.areEqual(thisValue, defaultValue)) { - dumper.add(field, thisValue); - } - } - } }