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:
parent
c2fb2f1520
commit
5b3066f380
@ -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)).
|
||||
|
@ -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
|
||||
|
@ -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"})
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user