Skip to content

Commit

Permalink
Move TS binary search to using DTS; fix PCR based duration; update a…
Browse files Browse the repository at this point in the history
…nd add dumps; add .gitattributes
  • Loading branch information
dsparano committed May 17, 2024
1 parent 9e9495b commit a514548
Show file tree
Hide file tree
Showing 70 changed files with 745 additions and 683 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,25 @@
/**
* A seeker that supports seeking within TS stream using binary search.
*
* <p>This seeker uses the first and last PCR values within the stream, as well as the stream
* duration to interpolate the PCR value of the seeking position. Then it performs binary search
* within the stream to find a packets whose PCR value is within {@link #SEEK_TOLERANCE_US} from the
* target PCR.
* <p>This seeker uses the first and last DTS values within the stream, as well as the stream
* duration to interpolate the DTS value of the seeking position. Then it performs binary search
* within the stream to find a packets whose DTS value is within {@link #SEEK_TOLERANCE_US} from the
* target DTS.
*/
/* package */ final class TsBinarySearchSeeker extends BinarySearchSeeker {

private static final long SEEK_TOLERANCE_US = 100_000;
private static final int MINIMUM_SEARCH_RANGE_BYTES = 5 * TsExtractor.TS_PACKET_SIZE;

public TsBinarySearchSeeker(
TimestampAdjuster pcrTimestampAdjuster,
TimestampAdjuster timestampAdjuster,
long streamDurationUs,
long inputLength,
int pcrPid,
int selectedPid,
int timestampSearchBytes) {
super(
new DefaultSeekTimestampConverter(),
new TsPcrSeeker(pcrPid, pcrTimestampAdjuster, timestampSearchBytes),
new TsTimestampSeeker(selectedPid, timestampAdjuster, timestampSearchBytes),
streamDurationUs,
/* floorTimePosition= */ 0,
/* ceilingTimePosition= */ streamDurationUs + 1,
Expand All @@ -57,25 +57,25 @@ public TsBinarySearchSeeker(
}

/**
* A {@link TimestampSeeker} implementation that looks for a given PCR timestamp at a given
* A {@link TimestampSeeker} implementation that looks for a given DTS timestamp at a given
* position in a TS stream.
*
* <p>Given a PCR timestamp, and a position within a TS stream, this seeker will peek up to {@link
* <p>Given a DTS timestamp, and a position within a TS stream, this seeker will peek up to {@link
* #timestampSearchBytes} from that stream position, look for all packets with PID equal to
* PCR_PID, and then compare the PCR timestamps (if available) of these packets to the target
* SELECTED_PID, and then compare the DTS timestamps (if available) of these packets to the target
* timestamp.
*/
private static final class TsPcrSeeker implements TimestampSeeker {
private static final class TsTimestampSeeker implements TimestampSeeker {

private final TimestampAdjuster pcrTimestampAdjuster;
private final TimestampAdjuster timestampAdjuster;
private final ParsableByteArray packetBuffer;
private final int pcrPid;
private final int selectedPid;
private final int timestampSearchBytes;

public TsPcrSeeker(
int pcrPid, TimestampAdjuster pcrTimestampAdjuster, int timestampSearchBytes) {
this.pcrPid = pcrPid;
this.pcrTimestampAdjuster = pcrTimestampAdjuster;
public TsTimestampSeeker(
int selectedPid, TimestampAdjuster timestampAdjuster, int timestampSearchBytes) {
this.selectedPid = selectedPid;
this.timestampAdjuster = timestampAdjuster;
this.timestampSearchBytes = timestampSearchBytes;
packetBuffer = new ParsableByteArray();
}
Expand All @@ -89,16 +89,16 @@ public TimestampSearchResult searchForTimestamp(ExtractorInput input, long targe
packetBuffer.reset(bytesToSearch);
input.peekFully(packetBuffer.getData(), /* offset= */ 0, bytesToSearch);

return searchForPcrValueInBuffer(packetBuffer, targetTimestamp, inputPosition);
return searchForDtsValueInBuffer(packetBuffer, targetTimestamp, inputPosition);
}

private TimestampSearchResult searchForPcrValueInBuffer(
ParsableByteArray packetBuffer, long targetPcrTimeUs, long bufferStartOffset) {
private TimestampSearchResult searchForDtsValueInBuffer(
ParsableByteArray packetBuffer, long targetDtsTimeUs, long bufferStartOffset) {
int limit = packetBuffer.limit();

long startOfLastPacketPosition = C.INDEX_UNSET;
long endOfLastPacketPosition = C.INDEX_UNSET;
long lastPcrTimeUsInRange = C.TIME_UNSET;
long lastDtsTimeUsInRange = C.TIME_UNSET;

while (packetBuffer.bytesLeft() >= TsExtractor.TS_PACKET_SIZE) {
int startOfPacket =
Expand All @@ -107,34 +107,34 @@ private TimestampSearchResult searchForPcrValueInBuffer(
if (endOfPacket > limit) {
break;
}
long pcrValue = TsUtil.readPcrFromPacket(packetBuffer, startOfPacket, pcrPid);
if (pcrValue != C.TIME_UNSET) {
long pcrTimeUs = pcrTimestampAdjuster.adjustTsTimestamp(pcrValue);
if (pcrTimeUs > targetPcrTimeUs) {
if (lastPcrTimeUsInRange == C.TIME_UNSET) {
// First PCR timestamp is already over target.
return TimestampSearchResult.overestimatedResult(pcrTimeUs, bufferStartOffset);
long dtsValue = TsUtil.readDtsFromPacket(packetBuffer, startOfPacket, selectedPid);
if (dtsValue != C.TIME_UNSET) {
long dtsTimeUs = timestampAdjuster.adjustTsTimestamp(dtsValue);
if (dtsTimeUs > targetDtsTimeUs) {
if (lastDtsTimeUsInRange == C.TIME_UNSET) {
// First DTS timestamp is already over target.
return TimestampSearchResult.overestimatedResult(dtsTimeUs, bufferStartOffset);
} else {
// Last PCR timestamp < target timestamp < this timestamp.
// Last DTS timestamp < target timestamp < this timestamp.
return TimestampSearchResult.targetFoundResult(
bufferStartOffset + startOfLastPacketPosition);
}
} else if (pcrTimeUs + SEEK_TOLERANCE_US > targetPcrTimeUs) {
} else if (dtsTimeUs + SEEK_TOLERANCE_US > targetDtsTimeUs) {
long startOfPacketInStream = bufferStartOffset + startOfPacket;
return TimestampSearchResult.targetFoundResult(startOfPacketInStream);
}

lastPcrTimeUsInRange = pcrTimeUs;
lastDtsTimeUsInRange = dtsTimeUs;
startOfLastPacketPosition = startOfPacket;
}
packetBuffer.setPosition(endOfPacket);
endOfLastPacketPosition = endOfPacket;
}

if (lastPcrTimeUsInRange != C.TIME_UNSET) {
if (lastDtsTimeUsInRange != C.TIME_UNSET) {
long endOfLastPacketPositionInStream = bufferStartOffset + endOfLastPacketPosition;
return TimestampSearchResult.underestimatedResult(
lastPcrTimeUsInRange, endOfLastPacketPositionInStream);
lastDtsTimeUsInRange, endOfLastPacketPositionInStream);
} else {
return TimestampSearchResult.NO_TIMESTAMP_IN_RANGE_RESULT;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package androidx.media3.extractor.ts;

import static java.lang.Math.min;
import static java.lang.Math.max;

import androidx.media3.common.C;
import androidx.media3.common.util.ParsableByteArray;
Expand Down Expand Up @@ -61,6 +62,7 @@
private long secondDtsValue;
private long lastDtsValue;

private int firstPcrPosition;
private int firstDtsPosition;

private long durationUs;
Expand All @@ -73,6 +75,7 @@
firstDtsValue = C.TIME_UNSET;
secondDtsValue = C.TIME_UNSET;
lastDtsValue = C.TIME_UNSET;
firstPcrPosition = C.INDEX_UNSET;
firstDtsPosition = C.INDEX_UNSET;
durationUs = C.TIME_UNSET;
packetBuffer = new ParsableByteArray();
Expand Down Expand Up @@ -126,17 +129,17 @@ public boolean isDurationReadFinished() {
isDurationReadFromPcr = true;
return Extractor.RESULT_CONTINUE;
}
if (!isLastPcrValueRead) {
return readLastPcrValue(input, seekPositionHolder, pcrPid);
if (!isFirstPcrValueRead) {
return readFirstPcrValue(input, seekPositionHolder, pcrPid);
}
if (lastPcrValue == C.TIME_UNSET) {
if (firstPcrValue == C.TIME_UNSET) {
isDurationReadFromPcr = true;
return Extractor.RESULT_CONTINUE;
}
if (!isFirstPcrValueRead) {
return readFirstPcrValue(input, seekPositionHolder, pcrPid);
if (!isLastPcrValueRead) {
return readLastPcrValue(input, seekPositionHolder, pcrPid);
}
if (firstPcrValue == C.TIME_UNSET) {
if (lastPcrValue == C.TIME_UNSET) {
isDurationReadFromPcr = true;
return Extractor.RESULT_CONTINUE;
}
Expand Down Expand Up @@ -171,13 +174,6 @@ public boolean isDurationReadFinished() {
isDurationReadFromDts = true;
return Extractor.RESULT_CONTINUE;
}
if (!isLastDtsValueRead) {
return readLastDtsValue(input, seekPositionHolder, pesPid);
}
if (lastDtsValue == C.TIME_UNSET) {
isDurationReadFromDts = true;
return Extractor.RESULT_CONTINUE;
}
if (!isFirstDtsValueRead) {
return readFirstDtsValue(input, seekPositionHolder, pesPid);
}
Expand All @@ -192,6 +188,13 @@ public boolean isDurationReadFinished() {
isDurationReadFromDts = true;
return Extractor.RESULT_CONTINUE;
}
if (!isLastDtsValueRead) {
return readLastDtsValue(input, seekPositionHolder, pesPid);
}
if (lastDtsValue == C.TIME_UNSET) {
isDurationReadFromDts = true;
return Extractor.RESULT_CONTINUE;
}

long firstDtsPositionUs = timestampAdjuster.adjustTsTimestamp(firstDtsValue);
long secondDtsPositionUs = timestampAdjuster.adjustTsTimestamp(secondDtsValue);
Expand Down Expand Up @@ -256,6 +259,7 @@ private long readFirstPcrValueFromBuffer(ParsableByteArray packetBuffer, int pcr
}
long pcrValue = TsUtil.readPcrFromPacket(packetBuffer, searchPosition, pcrPid);
if (pcrValue != C.TIME_UNSET) {
firstPcrPosition = packetBuffer.getPosition();
return pcrValue;
}
}
Expand All @@ -265,7 +269,7 @@ private long readFirstPcrValueFromBuffer(ParsableByteArray packetBuffer, int pcr
private int readLastPcrValue(ExtractorInput input, PositionHolder seekPositionHolder, int pcrPid)
throws IOException {
long inputLength = input.getLength();
int bytesToSearch = (int) min(timestampSearchBytes, inputLength);
int bytesToSearch = (int) min(timestampSearchBytes, inputLength - firstPcrPosition);
long searchStartPosition = inputLength - bytesToSearch;
if (input.getPosition() != searchStartPosition) {
seekPositionHolder.position = searchStartPosition;
Expand Down Expand Up @@ -358,7 +362,7 @@ private int readSecondDtsValue(ExtractorInput input, PositionHolder seekPosition
private int readLastDtsValue(ExtractorInput input, PositionHolder seekPositionHolder, int pesPid)
throws IOException {
long inputLength = input.getLength();
int bytesToSearch = (int) min(timestampSearchBytes, inputLength);
int bytesToSearch = (int) min(timestampSearchBytes, inputLength - firstDtsPosition);
long searchStartPosition = inputLength - bytesToSearch;
if (input.getPosition() != searchStartPosition) {
seekPositionHolder.position = searchStartPosition;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
seekMap:
isSeekable = true
duration = 252977
duration = 288000
getPosition(0) = [[timeUs=0, position=0]]
getPosition(1) = [[timeUs=1, position=0]]
getPosition(126488) = [[timeUs=126488, position=9099]]
getPosition(252977) = [[timeUs=252977, position=18386]]
getPosition(144000) = [[timeUs=144000, position=9099]]
getPosition(288000) = [[timeUs=288000, position=18386]]
numberOfTracks = 1
track 1900:
total output bytes = 13281
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
seekMap:
isSeekable = true
duration = 252977
duration = 288000
getPosition(0) = [[timeUs=0, position=0]]
getPosition(1) = [[timeUs=1, position=0]]
getPosition(126488) = [[timeUs=126488, position=9099]]
getPosition(252977) = [[timeUs=252977, position=18386]]
getPosition(144000) = [[timeUs=144000, position=9099]]
getPosition(288000) = [[timeUs=288000, position=18386]]
numberOfTracks = 1
track 1900:
total output bytes = 10209
sample count = 6
total output bytes = 8673
sample count = 5
format 0:
averageBitrate = 384000
peakBitrate = 384000
Expand All @@ -17,26 +17,22 @@ track 1900:
channelCount = 6
sampleRate = 48000
sample 0:
time = 96000
flags = 1
data = length 1536, hash 5D09685
sample 1:
time = 128000
flags = 1
data = length 1536, hash A9A24E44
sample 2:
sample 1:
time = 160000
flags = 1
data = length 1536, hash 6F856273
sample 3:
sample 2:
time = 192000
flags = 1
data = length 1536, hash B1737D3C
sample 4:
sample 3:
time = 224000
flags = 1
data = length 1536, hash 98FDEB9D
sample 5:
sample 4:
time = 256000
flags = 1
data = length 1536, hash 99B9B943
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
seekMap:
isSeekable = true
duration = 252977
duration = 288000
getPosition(0) = [[timeUs=0, position=0]]
getPosition(1) = [[timeUs=1, position=0]]
getPosition(126488) = [[timeUs=126488, position=9099]]
getPosition(252977) = [[timeUs=252977, position=18386]]
getPosition(144000) = [[timeUs=144000, position=9099]]
getPosition(288000) = [[timeUs=288000, position=18386]]
numberOfTracks = 1
track 1900:
total output bytes = 7137
sample count = 4
total output bytes = 4065
sample count = 2
format 0:
averageBitrate = 384000
peakBitrate = 384000
Expand All @@ -17,18 +17,10 @@ track 1900:
channelCount = 6
sampleRate = 48000
sample 0:
time = 160000
flags = 1
data = length 1536, hash 6F856273
sample 1:
time = 192000
flags = 1
data = length 1536, hash B1737D3C
sample 2:
time = 224000
flags = 1
data = length 1536, hash 98FDEB9D
sample 3:
sample 1:
time = 256000
flags = 1
data = length 1536, hash 99B9B943
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
seekMap:
isSeekable = true
duration = 252977
duration = 288000
getPosition(0) = [[timeUs=0, position=0]]
getPosition(1) = [[timeUs=1, position=0]]
getPosition(126488) = [[timeUs=126488, position=9099]]
getPosition(252977) = [[timeUs=252977, position=18386]]
getPosition(144000) = [[timeUs=144000, position=9099]]
getPosition(288000) = [[timeUs=288000, position=18386]]
numberOfTracks = 1
track 1900:
total output bytes = 0
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
seekMap:
isSeekable = true
duration = 796888
duration = 760000
getPosition(0) = [[timeUs=0, position=0]]
getPosition(1) = [[timeUs=1, position=0]]
getPosition(398444) = [[timeUs=398444, position=13385]]
getPosition(796888) = [[timeUs=796888, position=26959]]
getPosition(380000) = [[timeUs=380000, position=13385]]
getPosition(760000) = [[timeUs=760000, position=26959]]
numberOfTracks = 1
track 1900:
total output bytes = 7594
Expand Down
Loading

0 comments on commit a514548

Please sign in to comment.