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
H.262 HLS streams
([#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:
* Fix DTS:X Profile 2 encoding attributes for passthrough playback
([#1299](https://github.com/androidx/media/pull/1299)).

View File

@ -27,6 +27,8 @@ import androidx.media3.extractor.MpegAudioUtil;
private final int bitrate;
/**
* Constructs an instance.
*
* @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 mpegAudioHeader The MPEG audio header associated with the first frame.
@ -39,15 +41,25 @@ import androidx.media3.extractor.MpegAudioUtil;
MpegAudioUtil.Header mpegAudioHeader,
boolean allowSeeksIfLengthUnknown) {
// 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
// size streams.
super(
// streams have variable frame sizes due to padding) to avoid the need to re-synchronize for
// constant frame size streams.
this(
inputLength,
firstFramePosition,
mpegAudioHeader.bitrate,
mpegAudioHeader.frameSize,
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

View File

@ -27,6 +27,7 @@ import androidx.media3.common.ParserException;
import androidx.media3.common.PlaybackException;
import androidx.media3.common.Player;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.ParsableByteArray;
import androidx.media3.common.util.UnstableApi;
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.TextInformationFrame;
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.IOException;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.math.RoundingMode;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
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;
private static final String TAG = "Mp3Extractor";
/** Predicate that matches ID3 frames containing only required gapless/seeking metadata. */
private static final FramePredicate REQUIRED_ID3_FRAME_PREDICATE =
(majorVersion, id0, id1, id2, id3) ->
@ -528,22 +534,26 @@ public final class Mp3Extractor implements Extractor {
gaplessInfoHolder.encoderPadding = xingFrame.encoderPadding;
}
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);
// 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
// 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.
if (seekHeader == SEEK_HEADER_XING) {
seeker = XingSeeker.create(input.getLength(), xingFrame, startPosition);
seeker = XingSeeker.create(xingFrame, startPosition);
} else { // seekHeader == SEEK_HEADER_INFO
long streamLength =
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);
seeker = getConstantBitrateSeeker(startPosition, xingFrame, input.getLength());
}
break;
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. */
private Seeker getConstantBitrateSeeker(ExtractorInput input, boolean allowSeeksIfLengthUnknown)
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);
scratch.setPosition(0);
synchronizedHeader.setForHeaderData(scratch.readInt());
return new ConstantBitrateSeeker(
input.getLength() != C.LENGTH_UNSET ? input.getLength() : streamLengthFallback,
input.getPosition(),
synchronizedHeader,
allowSeeksIfLengthUnknown);
input.getLength(), 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"})

View File

@ -18,6 +18,7 @@ package androidx.media3.extractor.mp3;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.util.ParsableByteArray;
import androidx.media3.common.util.Util;
import androidx.media3.extractor.MpegAudioUtil;
/** Representation of a LAME Xing or Info frame. */
@ -115,4 +116,20 @@ import androidx.media3.extractor.MpegAudioUtil;
return new XingFrame(
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.media3.common.C;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.Util;
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
* 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 xingFrame The parsed XING data from this audio frame.
* @return A {@link XingSeeker} for seeking in the stream, or {@code null} if the required
* information is not present.
*/
@Nullable
public static XingSeeker create(long inputLength, XingFrame xingFrame, long position) {
if (xingFrame.frameCount == C.LENGTH_UNSET || xingFrame.frameCount == 0) {
// If the frame count is missing/invalid, the header can't be used to determine the duration.
public static XingSeeker create(XingFrame xingFrame, long position) {
long durationUs = xingFrame.computeDurationUs();
if (durationUs == C.TIME_UNSET) {
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 the size in bytes or table of contents is missing, the stream is not seekable.
return new XingSeeker(
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(
position,
xingFrame.header.frameSize,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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