Rename Mp4MetadataInfo to mp4Info

PiperOrigin-RevId: 593143940
This commit is contained in:
tofunmi 2023-12-22 11:07:27 -08:00 committed by Copybara-Service
parent 4fc11a98a1
commit e3056dacac
4 changed files with 77 additions and 81 deletions

View File

@ -40,8 +40,11 @@ import java.util.Map;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
/** Provides MP4 metadata like duration, last sync sample timestamp etc. */
/* package */ final class Mp4MetadataInfo {
/**
* Provides some specific MP4 metadata about an mp4 file such as the duration, last sync sample
* timestamp etc.
*/
/* package */ final class Mp4Info {
/**
* The duration (in microseconds) of the MP4 file or {@link C#TIME_UNSET} if the duration is
* unknown.
@ -67,7 +70,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
/** The audio {@link Format} or {@code null} if there is no audio track. */
public final @Nullable Format audioFormat;
private Mp4MetadataInfo(
private Mp4Info(
long durationUs,
long lastSyncSampleTimestampUs,
long firstSyncSampleTimestampUsAfterTimeUs,
@ -81,18 +84,18 @@ import org.checkerframework.checker.nullness.qual.Nullable;
}
/**
* Extracts the MP4 metadata synchronously and returns {@link Mp4MetadataInfo}.
* Extracts the MP4 metadata synchronously and returns {@link Mp4Info}.
*
* @param context A {@link Context}.
* @param filePath The file path of a valid MP4.
* @throws IOException If an error occurs during metadata extraction.
*/
public static Mp4MetadataInfo create(Context context, String filePath) throws IOException {
public static Mp4Info create(Context context, String filePath) throws IOException {
return create(context, filePath, C.TIME_UNSET);
}
/**
* Extracts the MP4 metadata synchronously and returns {@link Mp4MetadataInfo}.
* Extracts the MP4 metadata synchronously and returns {@link Mp4Info}.
*
* @param context A {@link Context}.
* @param filePath The file path of a valid MP4.
@ -100,8 +103,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
* #firstSyncSampleTimestampUsAfterTimeUs}. {@link C#TIME_UNSET} if not needed.
* @throws IOException If an error occurs during metadata extraction.
*/
public static Mp4MetadataInfo create(Context context, String filePath, long timeUs)
throws IOException {
public static Mp4Info create(Context context, String filePath, long timeUs) throws IOException {
Mp4Extractor mp4Extractor = new Mp4Extractor();
ExtractorOutputImpl extractorOutput = new ExtractorOutputImpl();
DefaultDataSource dataSource =
@ -169,7 +171,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
audioFormat = checkNotNull(audioTrackOutput.format);
}
return new Mp4MetadataInfo(
return new Mp4Info(
durationUs,
lastSyncSampleTimestampUs,
firstSyncSampleTimestampUsAfterTimeUs,

View File

@ -779,8 +779,7 @@ public final class Transformer {
private @MonotonicNonNull ListenableFuture<TransmuxTranscodeHelper.ResumeMetadata>
getResumeMetadataFuture;
private @MonotonicNonNull ListenableFuture<Void> copyOutputFuture;
private @MonotonicNonNull ListenableFuture<Mp4MetadataInfo> getMp4MetadataInfoFuture;
@Nullable private Mp4MetadataInfo mediaItemMetadataInfo;
@Nullable private Mp4Info mediaItemInfo;
private Transformer(
Context context,
@ -1089,15 +1088,14 @@ public final class Transformer {
}
private @ProgressState int getTrimOptimizationProgress(ProgressHolder progressHolder) {
if (mediaItemMetadataInfo == null) {
if (mediaItemInfo == null) {
return PROGRESS_STATE_WAITING_FOR_AVAILABILITY;
}
MediaItem firstMediaItem =
checkNotNull(composition).sequences.get(0).editedMediaItems.get(0).mediaItem;
long trimStartTimeUs = firstMediaItem.clippingConfiguration.startPositionUs;
long transcodeDuration =
mediaItemMetadataInfo.firstSyncSampleTimestampUsAfterTimeUs - trimStartTimeUs;
float transcodeWeighting = (float) transcodeDuration / mediaItemMetadataInfo.durationUs;
long transcodeDuration = mediaItemInfo.firstSyncSampleTimestampUsAfterTimeUs - trimStartTimeUs;
float transcodeWeighting = (float) transcodeDuration / mediaItemInfo.durationUs;
if (transformerState == TRANSFORMER_STATE_PROCESS_MEDIA_START) {
if (transformerInternal == null) {
@ -1315,24 +1313,24 @@ public final class Transformer {
checkNotNull(composition).sequences.get(0).editedMediaItems.get(0).mediaItem;
long trimStartTimeUs = firstMediaItem.clippingConfiguration.startPositionUs;
long trimEndTimeUs = firstMediaItem.clippingConfiguration.endPositionUs;
getMp4MetadataInfoFuture =
TransmuxTranscodeHelper.getMp4MetadataInfo(
ListenableFuture<Mp4Info> getMp4InfoFuture =
TransmuxTranscodeHelper.getMp4Info(
context,
checkNotNull(firstMediaItem.localConfiguration).uri.toString(),
trimStartTimeUs);
Futures.addCallback(
getMp4MetadataInfoFuture,
new FutureCallback<Mp4MetadataInfo>() {
getMp4InfoFuture,
new FutureCallback<Mp4Info>() {
@Override
public void onSuccess(Mp4MetadataInfo mp4MetadataInfo) {
if (mp4MetadataInfo.firstSyncSampleTimestampUsAfterTimeUs == C.TIME_UNSET) {
public void onSuccess(Mp4Info mp4Info) {
if (mp4Info.firstSyncSampleTimestampUsAfterTimeUs == C.TIME_UNSET) {
exportResultBuilder.setOptimizationResult(OPTIMIZATION_ABANDONED_OTHER);
processFullInput();
return;
}
if (mp4MetadataInfo.firstSyncSampleTimestampUsAfterTimeUs == C.TIME_END_OF_SOURCE
if (mp4Info.firstSyncSampleTimestampUsAfterTimeUs == C.TIME_END_OF_SOURCE
|| (trimEndTimeUs != C.TIME_END_OF_SOURCE
&& trimEndTimeUs < mp4MetadataInfo.firstSyncSampleTimestampUsAfterTimeUs)) {
&& trimEndTimeUs < mp4Info.firstSyncSampleTimestampUsAfterTimeUs)) {
exportResultBuilder.setOptimizationResult(
OPTIMIZATION_ABANDONED_KEYFRAME_PLACEMENT_OPTIMAL_FOR_TRIM);
processFullInput();
@ -1340,14 +1338,14 @@ public final class Transformer {
}
// Ensure there is an audio sample to mux between the two clip times to prevent
// Transformer from hanging because it received an audio track but no audio samples.
if (mp4MetadataInfo.firstSyncSampleTimestampUsAfterTimeUs - trimStartTimeUs
if (mp4Info.firstSyncSampleTimestampUsAfterTimeUs - trimStartTimeUs
< MAX_ENCODED_AUDIO_BUFFER_DURATION_US) {
Transformer.this.composition =
buildNewCompositionWithClipTimes(
composition,
mp4MetadataInfo.firstSyncSampleTimestampUsAfterTimeUs,
mp4Info.firstSyncSampleTimestampUsAfterTimeUs,
firstMediaItem.clippingConfiguration.endPositionUs,
mp4MetadataInfo.durationUs,
mp4Info.durationUs,
/* startsAtKeyFrame= */ true);
exportResultBuilder.setOptimizationResult(
OPTIMIZATION_ABANDONED_KEYFRAME_PLACEMENT_OPTIMAL_FOR_TRIM);
@ -1362,15 +1360,15 @@ public final class Transformer {
MuxerWrapper.MUXER_MODE_MUX_PARTIAL,
/* dropSamplesBeforeFirstVideoSample= */ false);
if (shouldTranscodeVideo(
checkNotNull(mp4MetadataInfo.videoFormat),
checkNotNull(mp4Info.videoFormat),
composition,
/* sequenceIndex= */ 0,
transformationRequest,
encoderFactory,
remuxingMuxerWrapper)
|| (mp4MetadataInfo.audioFormat != null
|| (mp4Info.audioFormat != null
&& shouldTranscodeAudio(
mp4MetadataInfo.audioFormat,
mp4Info.audioFormat,
composition,
/* sequenceIndex= */ 0,
transformationRequest,
@ -1382,13 +1380,13 @@ public final class Transformer {
processFullInput();
return;
}
Transformer.this.mediaItemMetadataInfo = mp4MetadataInfo;
Transformer.this.mediaItemInfo = mp4Info;
Composition trancodeComposition =
buildNewCompositionWithClipTimes(
composition,
trimStartTimeUs,
mp4MetadataInfo.firstSyncSampleTimestampUsAfterTimeUs,
mp4MetadataInfo.durationUs,
mp4Info.firstSyncSampleTimestampUsAfterTimeUs,
mp4Info.durationUs,
/* startsAtKeyFrame= */ false);
startInternal(
@ -1408,7 +1406,7 @@ public final class Transformer {
}
private void remuxRemainingMedia() {
Mp4MetadataInfo mediaItemMetadataInfo = checkNotNull(this.mediaItemMetadataInfo);
Mp4Info mediaItemInfo = checkNotNull(this.mediaItemInfo);
transformerState = TRANSFORMER_STATE_REMUX_REMAINING_MEDIA;
if (!doesFormatsMatch()) {
remuxingMuxerWrapper = null;
@ -1424,9 +1422,9 @@ public final class Transformer {
Composition transmuxComposition =
buildNewCompositionWithClipTimes(
composition,
mediaItemMetadataInfo.firstSyncSampleTimestampUsAfterTimeUs,
mediaItemInfo.firstSyncSampleTimestampUsAfterTimeUs,
trimEndTimeUs,
mediaItemMetadataInfo.durationUs,
mediaItemInfo.durationUs,
/* startsAtKeyFrame= */ true);
checkNotNull(remuxingMuxerWrapper);
remuxingMuxerWrapper.changeToAppendMode();
@ -1434,19 +1432,19 @@ public final class Transformer {
transmuxComposition,
remuxingMuxerWrapper,
componentListener,
/* initialTimestampOffsetUs= */ mediaItemMetadataInfo.firstSyncSampleTimestampUsAfterTimeUs
/* initialTimestampOffsetUs= */ mediaItemInfo.firstSyncSampleTimestampUsAfterTimeUs
- trimStartTimeUs);
}
private boolean doesFormatsMatch() {
Mp4MetadataInfo mediaItemMetadataInfo = checkNotNull(this.mediaItemMetadataInfo);
Mp4Info mediaItemInfo = checkNotNull(this.mediaItemInfo);
boolean videoFormatMatches =
checkNotNull(remuxingMuxerWrapper)
.getTrackFormat(C.TRACK_TYPE_VIDEO)
.initializationDataEquals(checkNotNull(mediaItemMetadataInfo.videoFormat));
.initializationDataEquals(checkNotNull(mediaItemInfo.videoFormat));
boolean audioFormatMatches =
mediaItemMetadataInfo.audioFormat == null
|| mediaItemMetadataInfo.audioFormat.initializationDataEquals(
mediaItemInfo.audioFormat == null
|| mediaItemInfo.audioFormat.initializationDataEquals(
checkNotNull(remuxingMuxerWrapper).getTrackFormat(C.TRACK_TYPE_AUDIO));
return videoFormatMatches && audioFormatMatches;
}
@ -1555,7 +1553,7 @@ public final class Transformer {
remuxRemainingMedia();
} else if (transformerState == TRANSFORMER_STATE_REMUX_REMAINING_MEDIA) {
transformerState = TRANSFORMER_STATE_PROCESS_FULL_INPUT;
mediaItemMetadataInfo = null;
mediaItemInfo = null;
exportResultBuilder.setOptimizationResult(ExportResult.OPTIMIZATION_SUCCEEDED);
onExportCompletedWithSuccess();
} else {

View File

@ -59,20 +59,20 @@ import java.util.List;
}
}
public static ListenableFuture<Mp4MetadataInfo> getMp4MetadataInfo(
public static ListenableFuture<Mp4Info> getMp4Info(
Context context, String filePath, long timeUs) {
SettableFuture<Mp4MetadataInfo> mp4MetadataInfoSettableFuture = SettableFuture.create();
new Thread("TransmuxTranscodeHelper:Mp4MetadataInfo") {
SettableFuture<Mp4Info> mp4InfoSettableFuture = SettableFuture.create();
new Thread("TransmuxTranscodeHelper:Mp4Info") {
@Override
public void run() {
try {
mp4MetadataInfoSettableFuture.set(Mp4MetadataInfo.create(context, filePath, timeUs));
mp4InfoSettableFuture.set(Mp4Info.create(context, filePath, timeUs));
} catch (Exception ex) {
mp4MetadataInfoSettableFuture.setException(ex);
mp4InfoSettableFuture.setException(ex);
}
}
}.start();
return mp4MetadataInfoSettableFuture;
return mp4InfoSettableFuture;
}
public static Composition buildNewCompositionWithClipTimes(
@ -261,7 +261,7 @@ import java.util.List;
return;
}
long lastSyncSampleTimestampUs =
Mp4MetadataInfo.create(context, filePath).lastSyncSampleTimestampUs;
Mp4Info.create(context, filePath).lastSyncSampleTimestampUs;
ImmutableList.Builder<Pair<Integer, Long>> firstMediaItemIndexAndOffsetInfoBuilder =
new ImmutableList.Builder<>();
@ -348,7 +348,7 @@ import java.util.List;
if (mediaItem.clippingConfiguration.endPositionMs != C.TIME_END_OF_SOURCE) {
endUs = Util.msToUs(mediaItem.clippingConfiguration.endPositionMs);
} else {
endUs = Mp4MetadataInfo.create(context, filePath).durationUs;
endUs = Mp4Info.create(context, filePath).durationUs;
}
return endUs - startUs;

View File

@ -31,9 +31,9 @@ import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
/** Unit tests for {@link Mp4MetadataInfo}. */
/** Unit tests for {@link Mp4Info}. */
@RunWith(AndroidJUnit4.class)
public class Mp4MetadataInfoTest {
public class Mp4InfoTest {
@Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
private final Context context = ApplicationProvider.getApplicationContext();
@ -42,58 +42,57 @@ public class Mp4MetadataInfoTest {
public void create_withEmptyFile_throws() throws IOException {
String emptyFilePath = temporaryFolder.newFile("EmptyFile").getPath();
assertThrows(IllegalStateException.class, () -> Mp4MetadataInfo.create(context, emptyFilePath));
assertThrows(IllegalStateException.class, () -> Mp4Info.create(context, emptyFilePath));
}
@Test
public void create_withNonMp4File_throws() {
String nonMp4FilePath = "asset:///media/mkv/sample.mkv";
assertThrows(
IllegalStateException.class, () -> Mp4MetadataInfo.create(context, nonMp4FilePath));
assertThrows(IllegalStateException.class, () -> Mp4Info.create(context, nonMp4FilePath));
}
@Test
public void lastSyncSampleTimestampUs_ofSmallMp4File_outputsFirstTimestamp() throws IOException {
String mp4FilePath = "asset:///media/mp4/sample.mp4";
Mp4MetadataInfo mp4MetadataInfo = Mp4MetadataInfo.create(context, mp4FilePath);
Mp4Info mp4Info = Mp4Info.create(context, mp4FilePath);
assertThat(mp4MetadataInfo.lastSyncSampleTimestampUs)
assertThat(mp4Info.lastSyncSampleTimestampUs)
.isEqualTo(0); // The timestamp of the first sample in sample.mp4.
}
@Test
public void lastSyncSampleTimestampUs_ofMp4File_outputMatchesExpected() throws IOException {
String mp4FilePath = "asset:///media/mp4/hdr10-720p.mp4";
Mp4MetadataInfo mp4MetadataInfo = Mp4MetadataInfo.create(context, mp4FilePath);
Mp4Info mp4Info = Mp4Info.create(context, mp4FilePath);
assertThat(mp4MetadataInfo.lastSyncSampleTimestampUs)
assertThat(mp4Info.lastSyncSampleTimestampUs)
.isEqualTo(4_003_277L); // The timestamp of the last sync sample in hdr10-720p.mp4.
}
@Test
public void lastSyncSampleTimestampUs_ofAudioOnlyMp4File_isUnset() throws IOException {
String audioOnlyMp4FilePath = "asset:///media/mp4/sample_ac3.mp4";
Mp4MetadataInfo mp4MetadataInfo = Mp4MetadataInfo.create(context, audioOnlyMp4FilePath);
Mp4Info mp4Info = Mp4Info.create(context, audioOnlyMp4FilePath);
assertThat(mp4MetadataInfo.lastSyncSampleTimestampUs).isEqualTo(C.TIME_UNSET);
assertThat(mp4Info.lastSyncSampleTimestampUs).isEqualTo(C.TIME_UNSET);
}
@Test
public void durationUs_ofMp4File_outputMatchesExpected() throws Exception {
String mp4FilePath = "asset:///media/mp4/hdr10-720p.mp4";
Mp4MetadataInfo mp4MetadataInfo = Mp4MetadataInfo.create(context, mp4FilePath);
Mp4Info mp4Info = Mp4Info.create(context, mp4FilePath);
assertThat(mp4MetadataInfo.durationUs).isEqualTo(4_236_600L); // The duration of hdr10-720p.mp4.
assertThat(mp4Info.durationUs).isEqualTo(4_236_600L); // The duration of hdr10-720p.mp4.
}
@Test
public void firstSyncSampleTimestampUsAfterTimeUs_timeUsIsSyncSample_outputsFirstTimestamp()
throws IOException {
String mp4FilePath = "asset:///media/mp4/sample.mp4";
Mp4MetadataInfo mp4MetadataInfo = Mp4MetadataInfo.create(context, mp4FilePath, /* timeUs= */ 0);
Mp4Info mp4Info = Mp4Info.create(context, mp4FilePath, /* timeUs= */ 0);
assertThat(mp4MetadataInfo.firstSyncSampleTimestampUsAfterTimeUs)
assertThat(mp4Info.firstSyncSampleTimestampUsAfterTimeUs)
.isEqualTo(0); // The timestamp of the first sample in sample.mp4.
}
@ -101,43 +100,40 @@ public class Mp4MetadataInfoTest {
public void firstSyncSampleTimestampUsAfterTimeUs_timeUsNotASyncSample_returnsCorrectTimestamp()
throws IOException {
String mp4FilePath = "asset:///media/mp4/hdr10-720p.mp4";
Mp4MetadataInfo mp4MetadataInfo =
Mp4MetadataInfo.create(context, mp4FilePath, /* timeUs= */ 400);
Mp4Info mp4Info = Mp4Info.create(context, mp4FilePath, /* timeUs= */ 400);
assertThat(mp4MetadataInfo.firstSyncSampleTimestampUsAfterTimeUs).isEqualTo(1_002_955L);
assertThat(mp4Info.firstSyncSampleTimestampUsAfterTimeUs).isEqualTo(1_002_955L);
}
@Test
public void firstSyncSampleTimestampUsAfterTimeUs_timeUsSetToDuration_returnsTimeEndOfSource()
throws IOException {
String mp4FilePath = "asset:///media/mp4/hdr10-720p.mp4";
Mp4MetadataInfo mp4MetadataInfo =
Mp4MetadataInfo.create(context, mp4FilePath, /* timeUs= */ 4_236_600L);
Mp4Info mp4Info = Mp4Info.create(context, mp4FilePath, /* timeUs= */ 4_236_600L);
assertThat(mp4MetadataInfo.firstSyncSampleTimestampUsAfterTimeUs)
.isEqualTo(C.TIME_END_OF_SOURCE);
assertThat(mp4Info.firstSyncSampleTimestampUsAfterTimeUs).isEqualTo(C.TIME_END_OF_SOURCE);
}
@Test
public void firstSyncSampleTimestampUsAfterTimeUs_ofAudioOnlyMp4File_returnsUnsetValue()
throws IOException {
String mp4FilePath = "asset:///media/mp4/sample_ac3.mp4";
Mp4MetadataInfo mp4MetadataInfo = Mp4MetadataInfo.create(context, mp4FilePath, /* timeUs= */ 0);
Mp4Info mp4Info = Mp4Info.create(context, mp4FilePath, /* timeUs= */ 0);
assertThat(mp4MetadataInfo.firstSyncSampleTimestampUsAfterTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(mp4Info.firstSyncSampleTimestampUsAfterTimeUs).isEqualTo(C.TIME_UNSET);
}
@Test
public void videoFormat_outputsFormatObjectWithCorrectRelevantFormatData() throws IOException {
String mp4FilePath = "asset:///media/mp4/sample.mp4";
Mp4MetadataInfo mp4MetadataInfo = Mp4MetadataInfo.create(context, mp4FilePath);
Mp4Info mp4Info = Mp4Info.create(context, mp4FilePath);
byte[] expectedCsd0 = {
0, 0, 0, 1, 103, 100, 0, 31, -84, -39, 64, 68, 5, -66, 95, 1, 16, 0, 0, 62, -112, 0, 14, -90,
0, -15, -125, 25, 96
};
byte[] expectedCsd1 = {0, 0, 0, 1, 104, -21, -29, -53, 34, -64};
Format actualFormat = mp4MetadataInfo.videoFormat;
Format actualFormat = mp4Info.videoFormat;
assertThat(actualFormat).isNotNull();
assertThat(actualFormat.initializationData.get(0)).isEqualTo(expectedCsd0);
@ -152,18 +148,18 @@ public class Mp4MetadataInfoTest {
@Test
public void videoFormat_audioOnlyMp4File_outputsNull() throws IOException {
String mp4FilePath = "asset:///media/mp4/sample_ac3.mp4";
Mp4MetadataInfo mp4MetadataInfo = Mp4MetadataInfo.create(context, mp4FilePath);
Mp4Info mp4Info = Mp4Info.create(context, mp4FilePath);
assertThat(mp4MetadataInfo.videoFormat).isNull();
assertThat(mp4Info.videoFormat).isNull();
}
@Test
public void audioFormat_outputsFormatObjectWithCorrectRelevantFormatData() throws IOException {
String mp4FilePath = "asset:///media/mp4/sample.mp4";
Mp4MetadataInfo mp4MetadataInfo = Mp4MetadataInfo.create(context, mp4FilePath);
Mp4Info mp4Info = Mp4Info.create(context, mp4FilePath);
byte[] expectedCsd0 = {18, 8};
Format actualFormat = mp4MetadataInfo.audioFormat;
Format actualFormat = mp4Info.audioFormat;
assertThat(actualFormat).isNotNull();
assertThat(actualFormat.sampleMimeType).isEqualTo(AUDIO_AAC);
@ -175,8 +171,8 @@ public class Mp4MetadataInfoTest {
@Test
public void audioFormat_videoOnlyMp4File_outputsNull() throws IOException {
String mp4FilePath = "asset:///media/mp4/sample_18byte_nclx_colr.mp4";
Mp4MetadataInfo mp4MetadataInfo = Mp4MetadataInfo.create(context, mp4FilePath);
Mp4Info mp4Info = Mp4Info.create(context, mp4FilePath);
assertThat(mp4MetadataInfo.audioFormat).isNull();
assertThat(mp4Info.audioFormat).isNull();
}
}