MP3: Derive duration and bitrate from frame count in Info header

`Info` header is used for CBR files, but in some cases not **every**
frame in these files is the same size. This change stops using the
single frame after the `Info` frame as the 'template' (and assuming all
subsequent frames are the same size/bitrate), and instead derives the
bitrate from fields in the `Info` header. This works for files which are
'almost' constant bitrate, like the one in Issue: androidx/media#1376 where every
frame is either 1044 or 1045 bytes except the one immediately after the
`Info` frame which is 104 bytes (32kbps), resulting in a wildly
incorrect duration calculation.

PiperOrigin-RevId: 636151605
This commit is contained in:
ibaker 2024-05-22 06:56:35 -07:00 committed by Copybara-Service
parent c2fb2f1520
commit 5b3066f380
16 changed files with 289 additions and 220 deletions

View File

@ -58,6 +58,16 @@
HLS streams([#1150](https://github.com/androidx/media/issues/1150)) and HLS streams([#1150](https://github.com/androidx/media/issues/1150)) and
H.262 HLS streams H.262 HLS streams
([#1126](https://github.com/androidx/media/issues/1126)). ([#1126](https://github.com/androidx/media/issues/1126)).
* MP3: Prefer the data size from an `Info` frame over the size reported by
the underlying stream (e.g. file size, or HTTP `Content-Length` header).
This helps to exclude non-playable trailer data (e.g. album artwork)
from constant bitrate seeking calculations, making seeks more accurate
([#1376](https://github.com/androidx/media/issues/1376)).
* MP3: Use the frame count and other data in an `Info` frame (if present)
to compute an average bitrate for constant bitrate seeking, rather than
extrapolating from the bitrate of the frame after the `Info` frame,
which may be artificially small, e.g. `PCUT` frame
([#1376](https://github.com/androidx/media/issues/1376)).
* Audio: * Audio:
* Fix DTS:X Profile 2 encoding attributes for passthrough playback * Fix DTS:X Profile 2 encoding attributes for passthrough playback
([#1299](https://github.com/androidx/media/pull/1299)). ([#1299](https://github.com/androidx/media/pull/1299)).

View File

@ -27,6 +27,8 @@ import androidx.media3.extractor.MpegAudioUtil;
private final int bitrate; private final int bitrate;
/** /**
* Constructs an instance.
*
* @param inputLength The length of the stream in bytes, or {@link C#LENGTH_UNSET} if unknown. * @param inputLength The length of the stream in bytes, or {@link C#LENGTH_UNSET} if unknown.
* @param firstFramePosition The position of the first frame in the stream. * @param firstFramePosition The position of the first frame in the stream.
* @param mpegAudioHeader The MPEG audio header associated with the first frame. * @param mpegAudioHeader The MPEG audio header associated with the first frame.
@ -39,15 +41,25 @@ import androidx.media3.extractor.MpegAudioUtil;
MpegAudioUtil.Header mpegAudioHeader, MpegAudioUtil.Header mpegAudioHeader,
boolean allowSeeksIfLengthUnknown) { boolean allowSeeksIfLengthUnknown) {
// Set the seeker frame size to the size of the first frame (even though some constant bitrate // Set the seeker frame size to the size of the first frame (even though some constant bitrate
// streams have variable frame sizes) to avoid the need to re-synchronize for constant frame // streams have variable frame sizes due to padding) to avoid the need to re-synchronize for
// size streams. // constant frame size streams.
super( this(
inputLength, inputLength,
firstFramePosition, firstFramePosition,
mpegAudioHeader.bitrate, mpegAudioHeader.bitrate,
mpegAudioHeader.frameSize, mpegAudioHeader.frameSize,
allowSeeksIfLengthUnknown); allowSeeksIfLengthUnknown);
bitrate = mpegAudioHeader.bitrate; }
/** See {@link ConstantBitrateSeekMap#ConstantBitrateSeekMap(long, long, int, int, boolean)}. */
public ConstantBitrateSeeker(
long inputLength,
long firstFramePosition,
int bitrate,
int frameSize,
boolean allowSeeksIfLengthUnknown) {
super(inputLength, firstFramePosition, bitrate, frameSize, allowSeeksIfLengthUnknown);
this.bitrate = bitrate;
} }
@Override @Override

View File

@ -27,6 +27,7 @@ import androidx.media3.common.ParserException;
import androidx.media3.common.PlaybackException; import androidx.media3.common.PlaybackException;
import androidx.media3.common.Player; import androidx.media3.common.Player;
import androidx.media3.common.util.Assertions; import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.ParsableByteArray;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
@ -45,11 +46,14 @@ import androidx.media3.extractor.metadata.id3.Id3Decoder.FramePredicate;
import androidx.media3.extractor.metadata.id3.MlltFrame; import androidx.media3.extractor.metadata.id3.MlltFrame;
import androidx.media3.extractor.metadata.id3.TextInformationFrame; import androidx.media3.extractor.metadata.id3.TextInformationFrame;
import androidx.media3.extractor.mp3.Seeker.UnseekableSeeker; import androidx.media3.extractor.mp3.Seeker.UnseekableSeeker;
import com.google.common.math.LongMath;
import com.google.common.primitives.Ints;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.math.RoundingMode;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull; import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull; import org.checkerframework.checker.nullness.qual.RequiresNonNull;
@ -122,6 +126,8 @@ public final class Mp3Extractor implements Extractor {
*/ */
public static final int FLAG_DISABLE_ID3_METADATA = 1 << 3; public static final int FLAG_DISABLE_ID3_METADATA = 1 << 3;
private static final String TAG = "Mp3Extractor";
/** Predicate that matches ID3 frames containing only required gapless/seeking metadata. */ /** Predicate that matches ID3 frames containing only required gapless/seeking metadata. */
private static final FramePredicate REQUIRED_ID3_FRAME_PREDICATE = private static final FramePredicate REQUIRED_ID3_FRAME_PREDICATE =
(majorVersion, id0, id1, id2, id3) -> (majorVersion, id0, id1, id2, id3) ->
@ -528,22 +534,26 @@ public final class Mp3Extractor implements Extractor {
gaplessInfoHolder.encoderPadding = xingFrame.encoderPadding; gaplessInfoHolder.encoderPadding = xingFrame.encoderPadding;
} }
long startPosition = input.getPosition(); long startPosition = input.getPosition();
if (input.getLength() != C.LENGTH_UNSET
&& xingFrame.dataSize != C.LENGTH_UNSET
&& input.getLength() != startPosition + xingFrame.dataSize) {
Log.i(
TAG,
"Data size mismatch between stream ("
+ input.getLength()
+ ") and Xing frame ("
+ (startPosition + xingFrame.dataSize)
+ "), using Xing value.");
}
input.skipFully(synchronizedHeader.frameSize); input.skipFully(synchronizedHeader.frameSize);
// An Xing frame indicates the file is VBR (so we have to use the seek header for seeking) // An Xing frame indicates the file is VBR (so we have to use the seek header for seeking)
// while an Info header indicates the file is CBR, in which case ConstantBitrateSeeker will // while an Info header indicates the file is CBR, in which case ConstantBitrateSeeker will
// give more accurate seeking than the low-resolution seek table in the Info header. We can // give more accurate seeking than the low-resolution seek table in the Info header. We can
// still use the length from the Info frame if we don't know the stream length directly. // still use the length from the Info frame if we don't know the stream length directly.
if (seekHeader == SEEK_HEADER_XING) { if (seekHeader == SEEK_HEADER_XING) {
seeker = XingSeeker.create(input.getLength(), xingFrame, startPosition); seeker = XingSeeker.create(xingFrame, startPosition);
} else { // seekHeader == SEEK_HEADER_INFO } else { // seekHeader == SEEK_HEADER_INFO
long streamLength = seeker = getConstantBitrateSeeker(startPosition, xingFrame, input.getLength());
xingFrame.dataSize != C.LENGTH_UNSET
? startPosition + xingFrame.dataSize
: C.LENGTH_UNSET;
// TODO: b/319235116 - Consider using the duration derived from the Xing/Info frame when
// it considers encoding delay and padding.
seeker =
getConstantBitrateSeeker(input, streamLength, /* allowSeeksIfLengthUnknown= */ false);
} }
break; break;
case SEEK_HEADER_VBRI: case SEEK_HEADER_VBRI:
@ -563,26 +573,67 @@ public final class Mp3Extractor implements Extractor {
/** Peeks the next frame and returns a {@link ConstantBitrateSeeker} based on its bitrate. */ /** Peeks the next frame and returns a {@link ConstantBitrateSeeker} based on its bitrate. */
private Seeker getConstantBitrateSeeker(ExtractorInput input, boolean allowSeeksIfLengthUnknown) private Seeker getConstantBitrateSeeker(ExtractorInput input, boolean allowSeeksIfLengthUnknown)
throws IOException { throws IOException {
return getConstantBitrateSeeker(input, C.LENGTH_UNSET, allowSeeksIfLengthUnknown);
}
/**
* Peeks the next frame and returns a {@link ConstantBitrateSeeker} based on its bitrate. {@code
* streamLengthFallback} is used if {@link ExtractorInput#getLength() input.getLength()} is {@link
* C#LENGTH_UNSET}. {@code streamLengthFallback} may also be {@link C#LENGTH_UNSET} to indicate
* the length is unknown.
*/
private Seeker getConstantBitrateSeeker(
ExtractorInput input, long streamLengthFallback, boolean allowSeeksIfLengthUnknown)
throws IOException {
input.peekFully(scratch.getData(), 0, 4); input.peekFully(scratch.getData(), 0, 4);
scratch.setPosition(0); scratch.setPosition(0);
synchronizedHeader.setForHeaderData(scratch.readInt()); synchronizedHeader.setForHeaderData(scratch.readInt());
return new ConstantBitrateSeeker( return new ConstantBitrateSeeker(
input.getLength() != C.LENGTH_UNSET ? input.getLength() : streamLengthFallback, input.getLength(), input.getPosition(), synchronizedHeader, allowSeeksIfLengthUnknown);
input.getPosition(), }
synchronizedHeader,
allowSeeksIfLengthUnknown); /**
* Returns a {@link ConstantBitrateSeeker} based on the provided {@link XingFrame Info frame}.
*
* @param infoFramePosition The position of the Info frame (from the beginning of the stream).
* @param infoFrame The parsed Info frame.
* @param fallbackStreamLength The complete length of the input stream (only used if {@link
* XingFrame#dataSize} is unset). Can be {@link C#LENGTH_UNSET} if the length is not known.
* @return A {@link Seeker} if the {@link XingFrame} contains enough info to seek, or {@code null}
* otherwise.
*/
@Nullable
private Seeker getConstantBitrateSeeker(
long infoFramePosition, XingFrame infoFrame, long fallbackStreamLength) {
long durationUs = infoFrame.computeDurationUs();
if (durationUs == C.TIME_UNSET) {
return null;
}
long streamLength;
long audioLength;
// Prefer the stream length from the Info frame, because it may deliberately
// exclude some unplayable/non-MP3 trailer data (e.g. album artwork), see
// https://github.com/androidx/media/issues/1376#issuecomment-2117211184.
if (infoFrame.dataSize != C.LENGTH_UNSET) {
streamLength = infoFramePosition + infoFrame.dataSize;
audioLength = infoFrame.dataSize - infoFrame.header.frameSize;
} else if (fallbackStreamLength != C.LENGTH_UNSET) {
streamLength = fallbackStreamLength;
audioLength = fallbackStreamLength - infoFramePosition - infoFrame.header.frameSize;
} else {
return null;
}
// Derive the bitrate and frame size by averaging over the length of playable audio, to allow
// for 'mostly' CBR streams that might have a small number of frames with a different bitrate.
// We can assume infoFrame.frameCount is set, because otherwise computeDurationUs() would
// have returned C.TIME_UNSET above. See also https://github.com/androidx/media/issues/1376.
int averageBitrate =
Ints.checkedCast(
Util.scaleLargeValue(
audioLength,
C.BITS_PER_BYTE * C.MICROS_PER_SECOND,
durationUs,
RoundingMode.HALF_UP));
int frameSize =
Ints.checkedCast(LongMath.divide(audioLength, infoFrame.frameCount, RoundingMode.HALF_UP));
// Set the seeker frame size to the average frame size (even though some constant bitrate
// streams have variable frame sizes due to padding), to avoid the need to re-synchronize for
// constant frame size streams.
return new ConstantBitrateSeeker(
streamLength,
/* firstFramePosition= */ infoFramePosition + infoFrame.header.frameSize,
averageBitrate,
frameSize,
/* allowSeeksIfLengthUnknown= */ false);
} }
@EnsuresNonNull({"extractorOutput", "realTrackOutput"}) @EnsuresNonNull({"extractorOutput", "realTrackOutput"})

View File

@ -18,6 +18,7 @@ package androidx.media3.extractor.mp3;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.ParsableByteArray;
import androidx.media3.common.util.Util;
import androidx.media3.extractor.MpegAudioUtil; import androidx.media3.extractor.MpegAudioUtil;
/** Representation of a LAME Xing or Info frame. */ /** Representation of a LAME Xing or Info frame. */
@ -115,4 +116,20 @@ import androidx.media3.extractor.MpegAudioUtil;
return new XingFrame( return new XingFrame(
mpegAudioHeader, frameCount, dataSize, tableOfContents, encoderDelay, encoderPadding); mpegAudioHeader, frameCount, dataSize, tableOfContents, encoderDelay, encoderPadding);
} }
/**
* Compute the stream duration, in microseconds, represented by this frame. Returns {@link
* C#LENGTH_UNSET} if the frame doesn't contain enough information to compute a duration.
*/
// TODO: b/319235116 - Handle encoder delay and padding when calculating duration.
public long computeDurationUs() {
if (frameCount == C.LENGTH_UNSET || frameCount == 0) {
// If the frame count is missing/invalid, the header can't be used to determine the duration.
return C.TIME_UNSET;
}
// Audio requires both a start and end PCM sample, so subtract one from the sample count before
// calculating the duration.
return Util.sampleCountToDurationUs(
(frameCount * header.samplesPerFrame) - 1, header.sampleRate);
}
} }

View File

@ -18,7 +18,6 @@ package androidx.media3.extractor.mp3;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.util.Assertions; import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.extractor.SeekPoint; import androidx.media3.extractor.SeekPoint;
@ -32,35 +31,22 @@ import androidx.media3.extractor.SeekPoint;
* Returns {@code null} if not. On returning, {@code frame}'s position is not specified so the * Returns {@code null} if not. On returning, {@code frame}'s position is not specified so the
* caller should reset it. * caller should reset it.
* *
* @param inputLength The length of the stream in bytes, or {@link C#LENGTH_UNSET} if unknown. * @param xingFrame The parsed Xing data from this audio frame.
* @param position The position of the start of this frame in the stream. * @param position The position of the start of this frame in the stream.
* @param xingFrame The parsed XING data from this audio frame.
* @return A {@link XingSeeker} for seeking in the stream, or {@code null} if the required * @return A {@link XingSeeker} for seeking in the stream, or {@code null} if the required
* information is not present. * information is not present.
*/ */
@Nullable @Nullable
public static XingSeeker create(long inputLength, XingFrame xingFrame, long position) { public static XingSeeker create(XingFrame xingFrame, long position) {
if (xingFrame.frameCount == C.LENGTH_UNSET || xingFrame.frameCount == 0) { long durationUs = xingFrame.computeDurationUs();
// If the frame count is missing/invalid, the header can't be used to determine the duration. if (durationUs == C.TIME_UNSET) {
return null; return null;
} }
// TODO: b/319235116 - Handle encoder delay and padding when calculating duration.
// Audio requires both a start and end PCM sample, so subtract one from the sample count before
// calculating the duration.
long durationUs =
Util.sampleCountToDurationUs(
(xingFrame.frameCount * xingFrame.header.samplesPerFrame) - 1,
xingFrame.header.sampleRate);
if (xingFrame.dataSize == C.LENGTH_UNSET || xingFrame.tableOfContents == null) { if (xingFrame.dataSize == C.LENGTH_UNSET || xingFrame.tableOfContents == null) {
// If the size in bytes or table of contents is missing, the stream is not seekable. // If the size in bytes or table of contents is missing, the stream is not seekable.
return new XingSeeker( return new XingSeeker(
position, xingFrame.header.frameSize, durationUs, xingFrame.header.bitrate); position, xingFrame.header.frameSize, durationUs, xingFrame.header.bitrate);
} }
if (inputLength != C.LENGTH_UNSET && inputLength != position + xingFrame.dataSize) {
Log.w(
TAG, "XING data size mismatch: " + inputLength + ", " + (position + xingFrame.dataSize));
}
return new XingSeeker( return new XingSeeker(
position, position,
xingFrame.header.frameSize, xingFrame.header.frameSize,

View File

@ -17,7 +17,6 @@ package androidx.media3.extractor.mp3;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import androidx.media3.common.C;
import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.ParsableByteArray;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.extractor.MpegAudioUtil; import androidx.media3.extractor.MpegAudioUtil;
@ -63,12 +62,10 @@ public final class XingSeekerTest {
xingFrameHeader.setForHeaderData(XING_FRAME_HEADER_DATA); xingFrameHeader.setForHeaderData(XING_FRAME_HEADER_DATA);
seeker = seeker =
XingSeeker.create( XingSeeker.create(
C.LENGTH_UNSET,
XingFrame.parse(xingFrameHeader, new ParsableByteArray(XING_FRAME_PAYLOAD)), XingFrame.parse(xingFrameHeader, new ParsableByteArray(XING_FRAME_PAYLOAD)),
XING_FRAME_POSITION); XING_FRAME_POSITION);
seekerWithInputLength = seekerWithInputLength =
XingSeeker.create( XingSeeker.create(
C.LENGTH_UNSET,
XingFrame.parse(xingFrameHeader, new ParsableByteArray(XING_FRAME_PAYLOAD)), XingFrame.parse(xingFrameHeader, new ParsableByteArray(XING_FRAME_PAYLOAD)),
XING_FRAME_POSITION); XING_FRAME_POSITION);
xingFrameSize = xingFrameHeader.frameSize; xingFrameSize = xingFrameHeader.frameSize;

View File

@ -1,16 +1,16 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 2115750 duration = 1070994
getPosition(0) = [[timeUs=0, position=227]] getPosition(0) = [[timeUs=0, position=227]]
getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=331]] getPosition(1) = [[timeUs=0, position=227], [timeUs=26069, position=433]]
getPosition(1057875) = [[timeUs=1040000, position=4387], [timeUs=1066000, position=4491]] getPosition(535497) = [[timeUs=521386, position=4347], [timeUs=547456, position=4553]]
getPosition(2115750) = [[timeUs=2089750, position=8586]] getPosition(1070994) = [[timeUs=1044925, position=8484]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 8463 total output bytes = 8463
sample count = 41 sample count = 41
format 0: format 0:
averageBitrate = 32000 averageBitrate = 63216
sampleMimeType = audio/mpeg sampleMimeType = audio/mpeg
maxInputSize = 4096 maxInputSize = 4096
channelCount = 1 channelCount = 1

View File

@ -1,16 +1,16 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 2115750 duration = 1070994
getPosition(0) = [[timeUs=0, position=227]] getPosition(0) = [[timeUs=0, position=227]]
getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=331]] getPosition(1) = [[timeUs=0, position=227], [timeUs=26069, position=433]]
getPosition(1057875) = [[timeUs=1040000, position=4387], [timeUs=1066000, position=4491]] getPosition(535497) = [[timeUs=521386, position=4347], [timeUs=547456, position=4553]]
getPosition(2115750) = [[timeUs=2089750, position=8586]] getPosition(1070994) = [[timeUs=1044925, position=8484]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 5643 total output bytes = 5643
sample count = 27 sample count = 27
format 0: format 0:
averageBitrate = 32000 averageBitrate = 63216
sampleMimeType = audio/mpeg sampleMimeType = audio/mpeg
maxInputSize = 4096 maxInputSize = 4096
channelCount = 1 channelCount = 1
@ -19,111 +19,111 @@ track 0:
encoderPadding = 1404 encoderPadding = 1404
metadata = entries=[TSSE: description=null: values=[Lavf58.45.100]] metadata = entries=[TSSE: description=null: values=[Lavf58.45.100]]
sample 0: sample 0:
time = 705000 time = 356871
flags = 1 flags = 1
data = length 209, hash 6CCBBB3B data = length 209, hash 6CCBBB3B
sample 1: sample 1:
time = 731122 time = 382993
flags = 1 flags = 1
data = length 209, hash 34191E1 data = length 209, hash 34191E1
sample 2: sample 2:
time = 757244 time = 409115
flags = 1 flags = 1
data = length 209, hash 57323ED7 data = length 209, hash 57323ED7
sample 3: sample 3:
time = 783367 time = 435238
flags = 1 flags = 1
data = length 209, hash 75618CF3 data = length 209, hash 75618CF3
sample 4: sample 4:
time = 809489 time = 461360
flags = 1 flags = 1
data = length 209, hash 784C973B data = length 209, hash 784C973B
sample 5: sample 5:
time = 835612 time = 487483
flags = 1 flags = 1
data = length 209, hash 49106390 data = length 209, hash 49106390
sample 6: sample 6:
time = 861734 time = 513605
flags = 1 flags = 1
data = length 209, hash 70F6A563 data = length 209, hash 70F6A563
sample 7: sample 7:
time = 887857 time = 539728
flags = 1 flags = 1
data = length 209, hash 721882B0 data = length 209, hash 721882B0
sample 8: sample 8:
time = 913979 time = 565850
flags = 1 flags = 1
data = length 209, hash 81C62AEE data = length 209, hash 81C62AEE
sample 9: sample 9:
time = 940102 time = 591973
flags = 1 flags = 1
data = length 209, hash 16D22463 data = length 209, hash 16D22463
sample 10: sample 10:
time = 966224 time = 618095
flags = 1 flags = 1
data = length 209, hash 47033534 data = length 209, hash 47033534
sample 11: sample 11:
time = 992346 time = 644217
flags = 1 flags = 1
data = length 209, hash CECB37A6 data = length 209, hash CECB37A6
sample 12: sample 12:
time = 1018469 time = 670340
flags = 1 flags = 1
data = length 209, hash 6C9C307B data = length 209, hash 6C9C307B
sample 13: sample 13:
time = 1044591 time = 696462
flags = 1 flags = 1
data = length 209, hash 3EB1A364 data = length 209, hash 3EB1A364
sample 14: sample 14:
time = 1070714 time = 722585
flags = 1 flags = 1
data = length 209, hash 30962500 data = length 209, hash 30962500
sample 15: sample 15:
time = 1096836 time = 748707
flags = 1 flags = 1
data = length 209, hash 2C5CCBB7 data = length 209, hash 2C5CCBB7
sample 16: sample 16:
time = 1122959 time = 774830
flags = 1 flags = 1
data = length 209, hash F9CB9E37 data = length 209, hash F9CB9E37
sample 17: sample 17:
time = 1149081 time = 800952
flags = 1 flags = 1
data = length 209, hash F75BC8C0 data = length 209, hash F75BC8C0
sample 18: sample 18:
time = 1175204 time = 827075
flags = 1 flags = 1
data = length 209, hash D00ED607 data = length 209, hash D00ED607
sample 19: sample 19:
time = 1201326 time = 853197
flags = 1 flags = 1
data = length 209, hash B4338395 data = length 209, hash B4338395
sample 20: sample 20:
time = 1227448 time = 879319
flags = 1 flags = 1
data = length 209, hash E3E838A0 data = length 209, hash E3E838A0
sample 21: sample 21:
time = 1253571 time = 905442
flags = 1 flags = 1
data = length 209, hash 2B0CF78 data = length 209, hash 2B0CF78
sample 22: sample 22:
time = 1279693 time = 931564
flags = 1 flags = 1
data = length 209, hash 31906FA9 data = length 209, hash 31906FA9
sample 23: sample 23:
time = 1305816 time = 957687
flags = 1 flags = 1
data = length 209, hash C92FC08F data = length 209, hash C92FC08F
sample 24: sample 24:
time = 1331938 time = 983809
flags = 1 flags = 1
data = length 209, hash 7C89994 data = length 209, hash 7C89994
sample 25: sample 25:
time = 1358061 time = 1009932
flags = 1 flags = 1
data = length 209, hash EC37743B data = length 209, hash EC37743B
sample 26: sample 26:
time = 1384183 time = 1036054
flags = 1 flags = 1
data = length 209, hash C974F6FB data = length 209, hash C974F6FB
tracksEnded = true tracksEnded = true

View File

@ -1,16 +1,16 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 2115750 duration = 1070994
getPosition(0) = [[timeUs=0, position=227]] getPosition(0) = [[timeUs=0, position=227]]
getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=331]] getPosition(1) = [[timeUs=0, position=227], [timeUs=26069, position=433]]
getPosition(1057875) = [[timeUs=1040000, position=4387], [timeUs=1066000, position=4491]] getPosition(535497) = [[timeUs=521386, position=4347], [timeUs=547456, position=4553]]
getPosition(2115750) = [[timeUs=2089750, position=8586]] getPosition(1070994) = [[timeUs=1044925, position=8484]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 2717 total output bytes = 2717
sample count = 13 sample count = 13
format 0: format 0:
averageBitrate = 32000 averageBitrate = 63216
sampleMimeType = audio/mpeg sampleMimeType = audio/mpeg
maxInputSize = 4096 maxInputSize = 4096
channelCount = 1 channelCount = 1
@ -19,55 +19,55 @@ track 0:
encoderPadding = 1404 encoderPadding = 1404
metadata = entries=[TSSE: description=null: values=[Lavf58.45.100]] metadata = entries=[TSSE: description=null: values=[Lavf58.45.100]]
sample 0: sample 0:
time = 1436500 time = 727157
flags = 1 flags = 1
data = length 209, hash 30962500 data = length 209, hash 30962500
sample 1: sample 1:
time = 1462622 time = 753279
flags = 1 flags = 1
data = length 209, hash 2C5CCBB7 data = length 209, hash 2C5CCBB7
sample 2: sample 2:
time = 1488744 time = 779401
flags = 1 flags = 1
data = length 209, hash F9CB9E37 data = length 209, hash F9CB9E37
sample 3: sample 3:
time = 1514867 time = 805524
flags = 1 flags = 1
data = length 209, hash F75BC8C0 data = length 209, hash F75BC8C0
sample 4: sample 4:
time = 1540989 time = 831646
flags = 1 flags = 1
data = length 209, hash D00ED607 data = length 209, hash D00ED607
sample 5: sample 5:
time = 1567112 time = 857769
flags = 1 flags = 1
data = length 209, hash B4338395 data = length 209, hash B4338395
sample 6: sample 6:
time = 1593234 time = 883891
flags = 1 flags = 1
data = length 209, hash E3E838A0 data = length 209, hash E3E838A0
sample 7: sample 7:
time = 1619357 time = 910014
flags = 1 flags = 1
data = length 209, hash 2B0CF78 data = length 209, hash 2B0CF78
sample 8: sample 8:
time = 1645479 time = 936136
flags = 1 flags = 1
data = length 209, hash 31906FA9 data = length 209, hash 31906FA9
sample 9: sample 9:
time = 1671602 time = 962259
flags = 1 flags = 1
data = length 209, hash C92FC08F data = length 209, hash C92FC08F
sample 10: sample 10:
time = 1697724 time = 988381
flags = 1 flags = 1
data = length 209, hash 7C89994 data = length 209, hash 7C89994
sample 11: sample 11:
time = 1723846 time = 1014503
flags = 1 flags = 1
data = length 209, hash EC37743B data = length 209, hash EC37743B
sample 12: sample 12:
time = 1749969 time = 1040626
flags = 1 flags = 1
data = length 209, hash C974F6FB data = length 209, hash C974F6FB
tracksEnded = true tracksEnded = true

View File

@ -1,16 +1,16 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 2115750 duration = 1070994
getPosition(0) = [[timeUs=0, position=227]] getPosition(0) = [[timeUs=0, position=227]]
getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=331]] getPosition(1) = [[timeUs=0, position=227], [timeUs=26069, position=433]]
getPosition(1057875) = [[timeUs=1040000, position=4387], [timeUs=1066000, position=4491]] getPosition(535497) = [[timeUs=521386, position=4347], [timeUs=547456, position=4553]]
getPosition(2115750) = [[timeUs=2089750, position=8586]] getPosition(1070994) = [[timeUs=1044925, position=8484]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 0 total output bytes = 0
sample count = 0 sample count = 0
format 0: format 0:
averageBitrate = 32000 averageBitrate = 63216
sampleMimeType = audio/mpeg sampleMimeType = audio/mpeg
maxInputSize = 4096 maxInputSize = 4096
channelCount = 1 channelCount = 1

View File

@ -1,16 +1,16 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 2115750 duration = 1070994
getPosition(0) = [[timeUs=0, position=227]] getPosition(0) = [[timeUs=0, position=227]]
getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=331]] getPosition(1) = [[timeUs=0, position=227], [timeUs=26069, position=433]]
getPosition(1057875) = [[timeUs=1040000, position=4387], [timeUs=1066000, position=4491]] getPosition(535497) = [[timeUs=521386, position=4347], [timeUs=547456, position=4553]]
getPosition(2115750) = [[timeUs=2089750, position=8586]] getPosition(1070994) = [[timeUs=1044925, position=8484]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 8463 total output bytes = 8463
sample count = 41 sample count = 41
format 0: format 0:
averageBitrate = 32000 averageBitrate = 63216
sampleMimeType = audio/mpeg sampleMimeType = audio/mpeg
maxInputSize = 4096 maxInputSize = 4096
channelCount = 1 channelCount = 1

View File

@ -2,9 +2,9 @@ seekMap:
isSeekable = true isSeekable = true
duration = 1044875 duration = 1044875
getPosition(0) = [[timeUs=0, position=227]] getPosition(0) = [[timeUs=0, position=227]]
getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=435]] getPosition(1) = [[timeUs=0, position=227], [timeUs=26125, position=436]]
getPosition(522437) = [[timeUs=520000, position=4387], [timeUs=546000, position=4595]] getPosition(522437) = [[timeUs=496375, position=4198], [timeUs=522500, position=4407]]
getPosition(1044875) = [[timeUs=1018875, position=8378]] getPosition(1044875) = [[timeUs=1018750, position=8377]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 8359 total output bytes = 8359

View File

@ -2,13 +2,13 @@ seekMap:
isSeekable = true isSeekable = true
duration = 1044875 duration = 1044875
getPosition(0) = [[timeUs=0, position=227]] getPosition(0) = [[timeUs=0, position=227]]
getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=435]] getPosition(1) = [[timeUs=0, position=227], [timeUs=26125, position=436]]
getPosition(522437) = [[timeUs=520000, position=4387], [timeUs=546000, position=4595]] getPosition(522437) = [[timeUs=496375, position=4198], [timeUs=522500, position=4407]]
getPosition(1044875) = [[timeUs=1018875, position=8378]] getPosition(1044875) = [[timeUs=1018750, position=8377]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 5643 total output bytes = 5434
sample count = 27 sample count = 26
format 0: format 0:
averageBitrate = 64000 averageBitrate = 64000
sampleMimeType = audio/mpeg sampleMimeType = audio/mpeg
@ -19,111 +19,107 @@ track 0:
encoderPadding = 1404 encoderPadding = 1404
metadata = entries=[TSSE: description=null: values=[Lavf58.45.100]] metadata = entries=[TSSE: description=null: values=[Lavf58.45.100]]
sample 0: sample 0:
time = 339500 time = 365625
flags = 1
data = length 209, hash 6CCBBB3B
sample 1:
time = 365622
flags = 1 flags = 1
data = length 209, hash 34191E1 data = length 209, hash 34191E1
sample 2: sample 1:
time = 391744 time = 391747
flags = 1 flags = 1
data = length 209, hash 57323ED7 data = length 209, hash 57323ED7
sample 3: sample 2:
time = 417867 time = 417869
flags = 1 flags = 1
data = length 209, hash 75618CF3 data = length 209, hash 75618CF3
sample 4: sample 3:
time = 443989 time = 443992
flags = 1 flags = 1
data = length 209, hash 784C973B data = length 209, hash 784C973B
sample 5: sample 4:
time = 470112 time = 470114
flags = 1 flags = 1
data = length 209, hash 49106390 data = length 209, hash 49106390
sample 6: sample 5:
time = 496234 time = 496237
flags = 1 flags = 1
data = length 209, hash 70F6A563 data = length 209, hash 70F6A563
sample 7: sample 6:
time = 522357 time = 522359
flags = 1 flags = 1
data = length 209, hash 721882B0 data = length 209, hash 721882B0
sample 8: sample 7:
time = 548479 time = 548482
flags = 1 flags = 1
data = length 209, hash 81C62AEE data = length 209, hash 81C62AEE
sample 9: sample 8:
time = 574602 time = 574604
flags = 1 flags = 1
data = length 209, hash 16D22463 data = length 209, hash 16D22463
sample 10: sample 9:
time = 600724 time = 600727
flags = 1 flags = 1
data = length 209, hash 47033534 data = length 209, hash 47033534
sample 11: sample 10:
time = 626846 time = 626849
flags = 1 flags = 1
data = length 209, hash CECB37A6 data = length 209, hash CECB37A6
sample 12: sample 11:
time = 652969 time = 652971
flags = 1 flags = 1
data = length 209, hash 6C9C307B data = length 209, hash 6C9C307B
sample 13: sample 12:
time = 679091 time = 679094
flags = 1 flags = 1
data = length 209, hash 3EB1A364 data = length 209, hash 3EB1A364
sample 14: sample 13:
time = 705214 time = 705216
flags = 1 flags = 1
data = length 209, hash 30962500 data = length 209, hash 30962500
sample 15: sample 14:
time = 731336 time = 731339
flags = 1 flags = 1
data = length 209, hash 2C5CCBB7 data = length 209, hash 2C5CCBB7
sample 16: sample 15:
time = 757459 time = 757461
flags = 1 flags = 1
data = length 209, hash F9CB9E37 data = length 209, hash F9CB9E37
sample 17: sample 16:
time = 783581 time = 783584
flags = 1 flags = 1
data = length 209, hash F75BC8C0 data = length 209, hash F75BC8C0
sample 18: sample 17:
time = 809704 time = 809706
flags = 1 flags = 1
data = length 209, hash D00ED607 data = length 209, hash D00ED607
sample 19: sample 18:
time = 835826 time = 835829
flags = 1 flags = 1
data = length 209, hash B4338395 data = length 209, hash B4338395
sample 20: sample 19:
time = 861948 time = 861951
flags = 1 flags = 1
data = length 209, hash E3E838A0 data = length 209, hash E3E838A0
sample 21: sample 20:
time = 888071 time = 888073
flags = 1 flags = 1
data = length 209, hash 2B0CF78 data = length 209, hash 2B0CF78
sample 22: sample 21:
time = 914193 time = 914196
flags = 1 flags = 1
data = length 209, hash 31906FA9 data = length 209, hash 31906FA9
sample 23: sample 22:
time = 940316 time = 940318
flags = 1 flags = 1
data = length 209, hash C92FC08F data = length 209, hash C92FC08F
sample 24: sample 23:
time = 966438 time = 966441
flags = 1 flags = 1
data = length 209, hash 7C89994 data = length 209, hash 7C89994
sample 25: sample 24:
time = 992561 time = 992563
flags = 1 flags = 1
data = length 209, hash EC37743B data = length 209, hash EC37743B
sample 26: sample 25:
time = 1018683 time = 1018686
flags = 1 flags = 1
data = length 209, hash C974F6FB data = length 209, hash C974F6FB
tracksEnded = true tracksEnded = true

View File

@ -2,13 +2,13 @@ seekMap:
isSeekable = true isSeekable = true
duration = 1044875 duration = 1044875
getPosition(0) = [[timeUs=0, position=227]] getPosition(0) = [[timeUs=0, position=227]]
getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=435]] getPosition(1) = [[timeUs=0, position=227], [timeUs=26125, position=436]]
getPosition(522437) = [[timeUs=520000, position=4387], [timeUs=546000, position=4595]] getPosition(522437) = [[timeUs=496375, position=4198], [timeUs=522500, position=4407]]
getPosition(1044875) = [[timeUs=1018875, position=8378]] getPosition(1044875) = [[timeUs=1018750, position=8377]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 2926 total output bytes = 2717
sample count = 14 sample count = 13
format 0: format 0:
averageBitrate = 64000 averageBitrate = 64000
sampleMimeType = audio/mpeg sampleMimeType = audio/mpeg
@ -19,59 +19,55 @@ track 0:
encoderPadding = 1404 encoderPadding = 1404
metadata = entries=[TSSE: description=null: values=[Lavf58.45.100]] metadata = entries=[TSSE: description=null: values=[Lavf58.45.100]]
sample 0: sample 0:
time = 679125 time = 705250
flags = 1
data = length 209, hash 3EB1A364
sample 1:
time = 705247
flags = 1 flags = 1
data = length 209, hash 30962500 data = length 209, hash 30962500
sample 2: sample 1:
time = 731369 time = 731372
flags = 1 flags = 1
data = length 209, hash 2C5CCBB7 data = length 209, hash 2C5CCBB7
sample 3: sample 2:
time = 757492 time = 757494
flags = 1 flags = 1
data = length 209, hash F9CB9E37 data = length 209, hash F9CB9E37
sample 4: sample 3:
time = 783614 time = 783617
flags = 1 flags = 1
data = length 209, hash F75BC8C0 data = length 209, hash F75BC8C0
sample 5: sample 4:
time = 809737 time = 809739
flags = 1 flags = 1
data = length 209, hash D00ED607 data = length 209, hash D00ED607
sample 6: sample 5:
time = 835859 time = 835862
flags = 1 flags = 1
data = length 209, hash B4338395 data = length 209, hash B4338395
sample 7: sample 6:
time = 861982 time = 861984
flags = 1 flags = 1
data = length 209, hash E3E838A0 data = length 209, hash E3E838A0
sample 8: sample 7:
time = 888104 time = 888107
flags = 1 flags = 1
data = length 209, hash 2B0CF78 data = length 209, hash 2B0CF78
sample 9: sample 8:
time = 914227 time = 914229
flags = 1 flags = 1
data = length 209, hash 31906FA9 data = length 209, hash 31906FA9
sample 10: sample 9:
time = 940349 time = 940352
flags = 1 flags = 1
data = length 209, hash C92FC08F data = length 209, hash C92FC08F
sample 11: sample 10:
time = 966471 time = 966474
flags = 1 flags = 1
data = length 209, hash 7C89994 data = length 209, hash 7C89994
sample 12: sample 11:
time = 992594 time = 992596
flags = 1 flags = 1
data = length 209, hash EC37743B data = length 209, hash EC37743B
sample 13: sample 12:
time = 1018716 time = 1018719
flags = 1 flags = 1
data = length 209, hash C974F6FB data = length 209, hash C974F6FB
tracksEnded = true tracksEnded = true

View File

@ -2,13 +2,13 @@ seekMap:
isSeekable = true isSeekable = true
duration = 1044875 duration = 1044875
getPosition(0) = [[timeUs=0, position=227]] getPosition(0) = [[timeUs=0, position=227]]
getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=435]] getPosition(1) = [[timeUs=0, position=227], [timeUs=26125, position=436]]
getPosition(522437) = [[timeUs=520000, position=4387], [timeUs=546000, position=4595]] getPosition(522437) = [[timeUs=496375, position=4198], [timeUs=522500, position=4407]]
getPosition(1044875) = [[timeUs=1018875, position=8378]] getPosition(1044875) = [[timeUs=1018750, position=8377]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 0 total output bytes = 209
sample count = 0 sample count = 1
format 0: format 0:
averageBitrate = 64000 averageBitrate = 64000
sampleMimeType = audio/mpeg sampleMimeType = audio/mpeg
@ -18,4 +18,8 @@ track 0:
encoderDelay = 576 encoderDelay = 576
encoderPadding = 1404 encoderPadding = 1404
metadata = entries=[TSSE: description=null: values=[Lavf58.45.100]] metadata = entries=[TSSE: description=null: values=[Lavf58.45.100]]
sample 0:
time = 1018750
flags = 1
data = length 209, hash C974F6FB
tracksEnded = true tracksEnded = true

View File

@ -2,9 +2,9 @@ seekMap:
isSeekable = true isSeekable = true
duration = 1044875 duration = 1044875
getPosition(0) = [[timeUs=0, position=227]] getPosition(0) = [[timeUs=0, position=227]]
getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=435]] getPosition(1) = [[timeUs=0, position=227], [timeUs=26125, position=436]]
getPosition(522437) = [[timeUs=520000, position=4387], [timeUs=546000, position=4595]] getPosition(522437) = [[timeUs=496375, position=4198], [timeUs=522500, position=4407]]
getPosition(1044875) = [[timeUs=1018875, position=8378]] getPosition(1044875) = [[timeUs=1018750, position=8377]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 8359 total output bytes = 8359