Rename LAST_FRAME_DURATION_BEHAVIOR to LAST_SAMPLE_DURATION_BEHAVIOR
The original idea was to set duration for the last video frame, but this behavior actually applies to all tracks. PiperOrigin-RevId: 667990099
This commit is contained in:
parent
d0676245b5
commit
ebd60acc95
@ -131,7 +131,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
|
||||
MetadataCollector metadataCollector,
|
||||
long minInputPtsUs,
|
||||
boolean isFragmentedMp4,
|
||||
@Mp4Muxer.LastFrameDurationBehavior int lastFrameDurationBehavior) {
|
||||
@Mp4Muxer.LastSampleDurationBehavior int lastSampleDurationBehavior) {
|
||||
// The timestamp will always fit into a 32-bit integer. This is already validated in the
|
||||
// Mp4Muxer.setTimestampData() API. The value after type casting might be negative, but it is
|
||||
// still valid because it is meant to be read as an unsigned integer.
|
||||
@ -157,7 +157,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
|
||||
track.writtenSamples(),
|
||||
minInputPtsUs,
|
||||
track.videoUnitTimebase(),
|
||||
lastFrameDurationBehavior);
|
||||
lastSampleDurationBehavior);
|
||||
|
||||
long trackDurationInTrackUnitsVu = 0;
|
||||
for (int j = 0; j < sampleDurationsVu.size(); j++) {
|
||||
@ -808,7 +808,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
|
||||
* Otherwise this should be equal to the presentation timestamp of first sample present in the
|
||||
* {@code samplesInfo} list.
|
||||
* @param videoUnitTimescale The timescale of the track.
|
||||
* @param lastDurationBehavior The behaviour for the last sample duration.
|
||||
* @param lastSampleDurationBehavior The behaviour for the last sample duration.
|
||||
* @return A list of all the sample durations.
|
||||
*/
|
||||
// TODO: b/280084657 - Add support for setting last sample duration.
|
||||
@ -816,7 +816,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
|
||||
List<BufferInfo> samplesInfo,
|
||||
long firstSamplePresentationTimeUs,
|
||||
int videoUnitTimescale,
|
||||
@Mp4Muxer.LastFrameDurationBehavior int lastDurationBehavior) {
|
||||
@Mp4Muxer.LastSampleDurationBehavior int lastSampleDurationBehavior) {
|
||||
List<Long> presentationTimestampsUs = new ArrayList<>(samplesInfo.size());
|
||||
List<Integer> durationsVu = new ArrayList<>(samplesInfo.size());
|
||||
|
||||
@ -855,7 +855,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
|
||||
// Default duration for the last sample.
|
||||
durationsVu.add(0);
|
||||
|
||||
adjustLastSampleDuration(durationsVu, lastDurationBehavior);
|
||||
adjustLastSampleDuration(durationsVu, lastSampleDurationBehavior);
|
||||
return durationsVu;
|
||||
}
|
||||
|
||||
@ -1264,7 +1264,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
|
||||
// TODO: b/317117431 - Change this method to getLastSampleDuration().
|
||||
/** Adjusts the duration of the very last sample if needed. */
|
||||
private static void adjustLastSampleDuration(
|
||||
List<Integer> durationsToBeAdjustedVu, @Mp4Muxer.LastFrameDurationBehavior int behavior) {
|
||||
List<Integer> durationsToBeAdjustedVu, @Mp4Muxer.LastSampleDurationBehavior int behavior) {
|
||||
// Technically, MP4 file stores frame durations, not timestamps. If a frame starts at a
|
||||
// given timestamp then the duration of the last frame is not obvious. If samples follow each
|
||||
// other in roughly regular intervals (e.g. in a normal, 30 fps video), it can be safely assumed
|
||||
@ -1278,14 +1278,14 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
|
||||
}
|
||||
|
||||
switch (behavior) {
|
||||
case Mp4Muxer.LAST_FRAME_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION:
|
||||
case Mp4Muxer.LAST_SAMPLE_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION:
|
||||
// This is the default MediaMuxer behavior: the last sample duration is a copy of the
|
||||
// previous sample duration.
|
||||
durationsToBeAdjustedVu.set(
|
||||
durationsToBeAdjustedVu.size() - 1,
|
||||
durationsToBeAdjustedVu.get(durationsToBeAdjustedVu.size() - 2));
|
||||
break;
|
||||
case Mp4Muxer.LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME:
|
||||
case Mp4Muxer.LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE:
|
||||
// Keep the last sample duration as short as possible.
|
||||
checkState(Iterables.getLast(durationsToBeAdjustedVu) == 0L);
|
||||
break;
|
||||
|
@ -68,7 +68,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
private final AnnexBToAvccConverter annexBToAvccConverter;
|
||||
private final long fragmentDurationUs;
|
||||
private final boolean sampleCopyEnabled;
|
||||
private final @Mp4Muxer.LastFrameDurationBehavior int lastFrameDurationBehavior;
|
||||
private final @Mp4Muxer.LastSampleDurationBehavior int lastSampleDurationBehavior;
|
||||
private final List<Track> tracks;
|
||||
|
||||
private @MonotonicNonNull Track videoTrack;
|
||||
@ -100,7 +100,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
this.annexBToAvccConverter = annexBToAvccConverter;
|
||||
this.fragmentDurationUs = fragmentDurationMs * 1_000;
|
||||
this.sampleCopyEnabled = sampleCopyEnabled;
|
||||
lastFrameDurationBehavior = Mp4Muxer.LAST_FRAME_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION;
|
||||
lastSampleDurationBehavior = Mp4Muxer.LAST_SAMPLE_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION;
|
||||
tracks = new ArrayList<>();
|
||||
minInputPresentationTimeUs = Long.MAX_VALUE;
|
||||
currentFragmentSequenceNumber = 1;
|
||||
@ -207,7 +207,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
metadataCollector,
|
||||
/* minInputPtsUs= */ 0L,
|
||||
/* isFragmentedMp4= */ true,
|
||||
lastFrameDurationBehavior));
|
||||
lastSampleDurationBehavior));
|
||||
}
|
||||
|
||||
private boolean shouldFlushPendingSamples(
|
||||
@ -333,7 +333,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
? minInputPresentationTimeUs
|
||||
: pendingSamplesBufferInfo.get(0).presentationTimeUs,
|
||||
track.videoUnitTimebase(),
|
||||
Mp4Muxer.LAST_FRAME_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION);
|
||||
Mp4Muxer.LAST_SAMPLE_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION);
|
||||
|
||||
List<Integer> sampleCompositionTimeOffsets =
|
||||
Boxes.calculateSampleCompositionTimeOffsets(
|
||||
|
@ -145,19 +145,19 @@ public final class Mp4Muxer implements Muxer {
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target(TYPE_USE)
|
||||
@IntDef({
|
||||
LAST_FRAME_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION,
|
||||
LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME
|
||||
LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE,
|
||||
LAST_SAMPLE_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION,
|
||||
})
|
||||
public @interface LastFrameDurationBehavior {}
|
||||
public @interface LastSampleDurationBehavior {}
|
||||
|
||||
/** Insert a zero-length last sample. */
|
||||
public static final int LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME = 0;
|
||||
public static final int LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE = 0;
|
||||
|
||||
/**
|
||||
* Use the difference between the last timestamp and the one before that as the duration of the
|
||||
* last sample.
|
||||
*/
|
||||
public static final int LAST_FRAME_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION = 1;
|
||||
public static final int LAST_SAMPLE_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION = 1;
|
||||
|
||||
/** The specific MP4 file format. */
|
||||
@Documented
|
||||
@ -183,7 +183,7 @@ public final class Mp4Muxer implements Muxer {
|
||||
public static final class Builder {
|
||||
private final FileOutputStream outputStream;
|
||||
|
||||
private @LastFrameDurationBehavior int lastFrameDurationBehavior;
|
||||
private @LastSampleDurationBehavior int lastSampleDurationBehavior;
|
||||
@Nullable private AnnexBToAvccConverter annexBToAvccConverter;
|
||||
private boolean sampleCopyEnabled;
|
||||
private boolean attemptStreamableOutputEnabled;
|
||||
@ -197,21 +197,21 @@ public final class Mp4Muxer implements Muxer {
|
||||
*/
|
||||
public Builder(FileOutputStream outputStream) {
|
||||
this.outputStream = outputStream;
|
||||
lastFrameDurationBehavior = LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME;
|
||||
lastSampleDurationBehavior = LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE;
|
||||
sampleCopyEnabled = true;
|
||||
attemptStreamableOutputEnabled = true;
|
||||
outputFileFormat = FILE_FORMAT_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link LastFrameDurationBehavior} for the video track.
|
||||
* Sets the {@link LastSampleDurationBehavior}.
|
||||
*
|
||||
* <p>The default value is {@link #LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME}.
|
||||
* <p>The default value is {@link #LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE}.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public Mp4Muxer.Builder setLastFrameDurationBehavior(
|
||||
@LastFrameDurationBehavior int lastFrameDurationBehavior) {
|
||||
this.lastFrameDurationBehavior = lastFrameDurationBehavior;
|
||||
public Mp4Muxer.Builder setLastSampleDurationBehavior(
|
||||
@LastSampleDurationBehavior int lastSampleDurationBehavior) {
|
||||
this.lastSampleDurationBehavior = lastSampleDurationBehavior;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -290,7 +290,7 @@ public final class Mp4Muxer implements Muxer {
|
||||
"EditablevideoParameters must be set for FILE_FORMAT_EDITABLE_VIDEO");
|
||||
return new Mp4Muxer(
|
||||
outputStream,
|
||||
lastFrameDurationBehavior,
|
||||
lastSampleDurationBehavior,
|
||||
annexBToAvccConverter == null ? AnnexBToAvccConverter.DEFAULT : annexBToAvccConverter,
|
||||
sampleCopyEnabled,
|
||||
attemptStreamableOutputEnabled,
|
||||
@ -303,7 +303,7 @@ public final class Mp4Muxer implements Muxer {
|
||||
|
||||
private final FileOutputStream outputStream;
|
||||
private final FileChannel outputChannel;
|
||||
private final @LastFrameDurationBehavior int lastFrameDurationBehavior;
|
||||
private final @LastSampleDurationBehavior int lastSampleDurationBehavior;
|
||||
private final AnnexBToAvccConverter annexBToAvccConverter;
|
||||
private final boolean sampleCopyEnabled;
|
||||
private final boolean attemptStreamableOutputEnabled;
|
||||
@ -320,7 +320,7 @@ public final class Mp4Muxer implements Muxer {
|
||||
|
||||
private Mp4Muxer(
|
||||
FileOutputStream outputStream,
|
||||
@LastFrameDurationBehavior int lastFrameDurationBehavior,
|
||||
@LastSampleDurationBehavior int lastFrameDurationBehavior,
|
||||
AnnexBToAvccConverter annexBToAvccConverter,
|
||||
boolean sampleCopyEnabled,
|
||||
boolean attemptStreamableOutputEnabled,
|
||||
@ -328,7 +328,7 @@ public final class Mp4Muxer implements Muxer {
|
||||
@Nullable EditableVideoParameters editableVideoParameters) {
|
||||
this.outputStream = outputStream;
|
||||
outputChannel = outputStream.getChannel();
|
||||
this.lastFrameDurationBehavior = lastFrameDurationBehavior;
|
||||
this.lastSampleDurationBehavior = lastFrameDurationBehavior;
|
||||
this.annexBToAvccConverter = annexBToAvccConverter;
|
||||
this.sampleCopyEnabled = sampleCopyEnabled;
|
||||
this.attemptStreamableOutputEnabled = attemptStreamableOutputEnabled;
|
||||
@ -504,7 +504,7 @@ public final class Mp4Muxer implements Muxer {
|
||||
cacheFileOutputStream.getChannel(),
|
||||
checkNotNull(editableVideoMetadataCollector),
|
||||
annexBToAvccConverter,
|
||||
lastFrameDurationBehavior,
|
||||
lastSampleDurationBehavior,
|
||||
sampleCopyEnabled,
|
||||
attemptStreamableOutputEnabled);
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
private final FileChannel outputFileChannel;
|
||||
private final MetadataCollector metadataCollector;
|
||||
private final AnnexBToAvccConverter annexBToAvccConverter;
|
||||
private final @Mp4Muxer.LastFrameDurationBehavior int lastFrameDurationBehavior;
|
||||
private final @Mp4Muxer.LastSampleDurationBehavior int lastSampleDurationBehavior;
|
||||
private final boolean sampleCopyEnabled;
|
||||
private final List<Track> tracks;
|
||||
private final List<Track> editableVideoTracks;
|
||||
@ -77,8 +77,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
* @param annexBToAvccConverter The {@link AnnexBToAvccConverter} to be used to convert H.264 and
|
||||
* H.265 NAL units from the Annex-B format (using start codes to delineate NAL units) to the
|
||||
* AVCC format (which uses length prefixes).
|
||||
* @param lastFrameDurationBehavior The {@link Mp4Muxer.LastFrameDurationBehavior} for the video
|
||||
* track.
|
||||
* @param lastSampleDurationBehavior The {@link Mp4Muxer.LastSampleDurationBehavior}.
|
||||
* @param sampleCopyEnabled Whether sample copying is enabled.
|
||||
* @param attemptStreamableOutputEnabled Whether to attempt to write a streamable output.
|
||||
*/
|
||||
@ -86,13 +85,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
FileChannel fileChannel,
|
||||
MetadataCollector metadataCollector,
|
||||
AnnexBToAvccConverter annexBToAvccConverter,
|
||||
@Mp4Muxer.LastFrameDurationBehavior int lastFrameDurationBehavior,
|
||||
@Mp4Muxer.LastSampleDurationBehavior int lastSampleDurationBehavior,
|
||||
boolean sampleCopyEnabled,
|
||||
boolean attemptStreamableOutputEnabled) {
|
||||
this.outputFileChannel = fileChannel;
|
||||
this.metadataCollector = metadataCollector;
|
||||
this.annexBToAvccConverter = annexBToAvccConverter;
|
||||
this.lastFrameDurationBehavior = lastFrameDurationBehavior;
|
||||
this.lastSampleDurationBehavior = lastSampleDurationBehavior;
|
||||
this.sampleCopyEnabled = sampleCopyEnabled;
|
||||
tracks = new ArrayList<>();
|
||||
editableVideoTracks = new ArrayList<>();
|
||||
@ -205,7 +204,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
editableVideoMetadataCollector,
|
||||
findMinimumPresentationTimestampUsAcrossTracks(editableVideoTracks),
|
||||
/* isFragmentedMp4= */ false,
|
||||
lastFrameDurationBehavior);
|
||||
lastSampleDurationBehavior);
|
||||
ByteBuffer edvdBoxHeader =
|
||||
getEdvdBoxHeader(/* payloadSize= */ ftypBox.remaining() + moovBox.remaining());
|
||||
return BoxUtils.concatenateBuffers(edvdBoxHeader, ftypBox, moovBox);
|
||||
@ -323,7 +322,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
metadataCollector,
|
||||
minInputPtsUs,
|
||||
/* isFragmentedMp4= */ false,
|
||||
lastFrameDurationBehavior);
|
||||
lastSampleDurationBehavior);
|
||||
} else {
|
||||
// Skip moov box, if there are no samples.
|
||||
moovHeader = ByteBuffer.allocate(0);
|
||||
|
@ -15,8 +15,8 @@
|
||||
*/
|
||||
package androidx.media3.muxer;
|
||||
|
||||
import static androidx.media3.muxer.Mp4Muxer.LAST_FRAME_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION;
|
||||
import static androidx.media3.muxer.Mp4Muxer.LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME;
|
||||
import static androidx.media3.muxer.Mp4Muxer.LAST_SAMPLE_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION;
|
||||
import static androidx.media3.muxer.Mp4Muxer.LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE;
|
||||
import static androidx.media3.muxer.MuxerTestUtil.FAKE_AUDIO_FORMAT;
|
||||
import static androidx.media3.muxer.MuxerTestUtil.FAKE_CSD_0;
|
||||
import static androidx.media3.muxer.MuxerTestUtil.FAKE_VIDEO_FORMAT;
|
||||
@ -477,7 +477,7 @@ public class BoxesTest {
|
||||
sampleBufferInfos,
|
||||
/* firstSamplePresentationTimeUs= */ 0L,
|
||||
VU_TIMEBASE,
|
||||
LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME);
|
||||
LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE);
|
||||
|
||||
assertThat(durationsVu).containsExactly(0);
|
||||
}
|
||||
@ -493,7 +493,7 @@ public class BoxesTest {
|
||||
sampleBufferInfos,
|
||||
/* firstSamplePresentationTimeUs= */ 0L,
|
||||
VU_TIMEBASE,
|
||||
LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME);
|
||||
LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE);
|
||||
|
||||
assertThat(durationsVu).containsExactly(0);
|
||||
}
|
||||
@ -509,7 +509,7 @@ public class BoxesTest {
|
||||
sampleBufferInfos,
|
||||
/* firstSamplePresentationTimeUs= */ 0L,
|
||||
VU_TIMEBASE,
|
||||
LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME);
|
||||
LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE);
|
||||
|
||||
assertThat(durationsVu).containsExactly(3_000, 5_000, 0);
|
||||
}
|
||||
@ -525,7 +525,7 @@ public class BoxesTest {
|
||||
sampleBufferInfos,
|
||||
/* firstSamplePresentationTimeUs= */ 0L,
|
||||
VU_TIMEBASE,
|
||||
LAST_FRAME_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION);
|
||||
LAST_SAMPLE_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION);
|
||||
|
||||
assertThat(durationsVu).containsExactly(3_000, 5_000, 5_000);
|
||||
}
|
||||
@ -541,7 +541,7 @@ public class BoxesTest {
|
||||
sampleBufferInfos,
|
||||
/* firstSamplePresentationTimeUs= */ 0L,
|
||||
VU_TIMEBASE,
|
||||
LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME);
|
||||
LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE);
|
||||
|
||||
assertThat(durationsVu).containsExactly(100, 100, 800, 100, 0);
|
||||
}
|
||||
@ -595,7 +595,7 @@ public class BoxesTest {
|
||||
sampleBufferInfos,
|
||||
/* firstSamplePresentationTimeUs= */ 0L,
|
||||
VU_TIMEBASE,
|
||||
LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME);
|
||||
LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE);
|
||||
|
||||
ByteBuffer cttsBox = Boxes.ctts(sampleBufferInfos, durationsVu, VU_TIMEBASE);
|
||||
|
||||
@ -612,7 +612,7 @@ public class BoxesTest {
|
||||
sampleBufferInfos,
|
||||
/* firstSamplePresentationTimeUs= */ 0L,
|
||||
VU_TIMEBASE,
|
||||
LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME);
|
||||
LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE);
|
||||
|
||||
ByteBuffer cttsBox = Boxes.ctts(sampleBufferInfos, durationsVu, VU_TIMEBASE);
|
||||
|
||||
@ -631,7 +631,7 @@ public class BoxesTest {
|
||||
sampleBufferInfos,
|
||||
/* firstSamplePresentationTimeUs= */ 0L,
|
||||
VU_TIMEBASE,
|
||||
LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME);
|
||||
LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE);
|
||||
|
||||
ByteBuffer cttsBox = Boxes.ctts(sampleBufferInfos, durationsVu, VU_TIMEBASE);
|
||||
|
||||
@ -651,7 +651,7 @@ public class BoxesTest {
|
||||
sampleBufferInfos,
|
||||
/* firstSamplePresentationTimeUs= */ 23698215060L,
|
||||
VU_TIMEBASE,
|
||||
LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME);
|
||||
LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE);
|
||||
|
||||
ByteBuffer cttsBox = Boxes.ctts(sampleBufferInfos, durationsVu, VU_TIMEBASE);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user