diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 9353d1e73a..c0c78f9ac2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -14,6 +14,8 @@ * Extractors: * Add support for .mp2 boxes in the `AtomParsers` ([#7967](https://github.com/google/ExoPlayer/issues/7967)). + * Use TLEN ID3 tag to compute the duration in Mp3Extractor + ([#7949](https://github.com/google/ExoPlayer/issues/7949)). ### 2.12.0 (2020-09-11) ### diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/MlltSeeker.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/MlltSeeker.java index 1b627483f0..f30b830249 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/MlltSeeker.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/MlltSeeker.java @@ -29,9 +29,11 @@ import com.google.android.exoplayer2.util.Util; * * @param firstFramePosition The position of the start of the first frame in the stream. * @param mlltFrame The MLLT frame with seeking metadata. + * @param durationUs The stream duration in microseconds, or {@link C#TIME_UNSET} if it is + * unknown. * @return An {@link MlltSeeker} for seeking in the stream. */ - public static MlltSeeker create(long firstFramePosition, MlltFrame mlltFrame) { + public static MlltSeeker create(long firstFramePosition, MlltFrame mlltFrame, long durationUs) { int referenceCount = mlltFrame.bytesDeviations.length; long[] referencePositions = new long[1 + referenceCount]; long[] referenceTimesMs = new long[1 + referenceCount]; @@ -45,19 +47,22 @@ import com.google.android.exoplayer2.util.Util; referencePositions[i] = position; referenceTimesMs[i] = timeMs; } - return new MlltSeeker(referencePositions, referenceTimesMs); + return new MlltSeeker(referencePositions, referenceTimesMs, durationUs); } private final long[] referencePositions; private final long[] referenceTimesMs; private final long durationUs; - private MlltSeeker(long[] referencePositions, long[] referenceTimesMs) { + private MlltSeeker(long[] referencePositions, long[] referenceTimesMs, long durationUs) { this.referencePositions = referencePositions; this.referenceTimesMs = referenceTimesMs; - // Use the last reference point as the duration, as extrapolating variable bitrate at the end of - // the stream may give a large error. - durationUs = C.msToUs(referenceTimesMs[referenceTimesMs.length - 1]); + // Use the last reference point as the duration if it is unknown, as extrapolating variable + // bitrate at the end of the stream may give a large error. + this.durationUs = + durationUs != C.TIME_UNSET + ? durationUs + : C.msToUs(referenceTimesMs[referenceTimesMs.length - 1]); } @Override diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java index 59d128ab9b..c2aba6d7bd 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java @@ -35,6 +35,7 @@ import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.id3.Id3Decoder; import com.google.android.exoplayer2.metadata.id3.Id3Decoder.FramePredicate; import com.google.android.exoplayer2.metadata.id3.MlltFrame; +import com.google.android.exoplayer2.metadata.id3.TextInformationFrame; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; @@ -432,7 +433,7 @@ public final class Mp3Extractor implements Extractor { @Nullable Seeker resultSeeker = null; if ((flags & FLAG_ENABLE_INDEX_SEEKING) != 0) { - long durationUs = C.TIME_UNSET; + long durationUs; long dataEndPosition = C.POSITION_UNSET; if (metadataSeeker != null) { durationUs = metadataSeeker.getDurationUs(); @@ -440,6 +441,8 @@ public final class Mp3Extractor implements Extractor { } else if (seekFrameSeeker != null) { durationUs = seekFrameSeeker.getDurationUs(); dataEndPosition = seekFrameSeeker.getDataEndPosition(); + } else { + durationUs = getId3TlenUs(metadata); } resultSeeker = new IndexSeeker( @@ -554,10 +557,24 @@ public final class Mp3Extractor implements Extractor { for (int i = 0; i < length; i++) { Metadata.Entry entry = metadata.get(i); if (entry instanceof MlltFrame) { - return MlltSeeker.create(firstFramePosition, (MlltFrame) entry); + return MlltSeeker.create(firstFramePosition, (MlltFrame) entry, getId3TlenUs(metadata)); } } } return null; } + + private static long getId3TlenUs(@Nullable Metadata metadata) { + if (metadata != null) { + int length = metadata.length(); + for (int i = 0; i < length; i++) { + Metadata.Entry entry = metadata.get(i); + if (entry instanceof TextInformationFrame + && ((TextInformationFrame) entry).id.equals("TLEN")) { + return C.msToUs(Long.parseLong(((TextInformationFrame) entry).value)); + } + } + } + return C.TIME_UNSET; + } }