Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class Decoder(
companion object {
private val ID = trackMapOf(AtomicInteger(0), AtomicInteger(0))
private const val timeoutUs = 2000L
private const val VERBOSE = false
}

private val log = Logger("Decoder(${format.trackType},${ID[format.trackType].getAndIncrement()})")
Expand Down Expand Up @@ -90,6 +91,9 @@ class Decoder(
dequeuedInputs--
val (chunk, id) = data
val flag = if (chunk.keyframe) BUFFER_FLAG_SYNC_FRAME else 0
if(VERBOSE) {
log.v("enqueue(): queueInputBuffer ${chunk.timeUs}")
}
codec.queueInputBuffer(id, chunk.buffer.position(), chunk.buffer.remaining(), chunk.timeUs, flag)
dropper.input(chunk.timeUs, chunk.render)
}
Expand Down Expand Up @@ -123,11 +127,17 @@ class Decoder(
// Ideally, we shouldn't rely on the fact that the buffer is properly configured.
// We should configure its position and limit based on the buffer info's position and size.
val data = DecoderData(buffer, timeUs) {
if(VERBOSE) {
log.v("drain(): released successfully presentation ts ${info.presentationTimeUs} and $timeUs")
}
codec.releaseOutputBuffer(result, it)
dequeuedOutputs--
}
if (isEos) State.Eos(data) else State.Ok(data)
} else {
if(VERBOSE) {
log.v("drain(): released because decoder dropper gave null ts ${info.presentationTimeUs}")
}
codec.releaseOutputBuffer(result, false)
State.Wait
}.also {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ class DefaultThumbnailsEngine(
return source.keyFrameTimestamps
}

override fun getSeekThreshold() = source.seekThreshold

override fun isDrained(): Boolean {
if (source.isDrained) {
source.seekTo(stubs.firstOrNull()?.positionUs ?: -1)
Expand Down Expand Up @@ -121,15 +123,20 @@ class DefaultThumbnailsEngine(
val nextKeyFrameUs = source.keyFrameAt(nextKeyFrameIndex) { Long.MAX_VALUE }
val previousKeyFrameUs = source.keyFrameAt(nextKeyFrameIndex - 1) { source.lastKeyFrame() }

log.i(
"seek: current ${source.positionUs}," +
" requested $requested, threshold $threshold, nextKeyFrameUs $nextKeyFrameUs"
)

val rightGap = nextKeyFrameUs - requested
val nextKeyFrameInThreshold = rightGap <= threshold
seek = nextKeyFrameInThreshold || previousKeyFrameUs > current || (current - requested > threshold)
seekUs = if (nextKeyFrameInThreshold) nextKeyFrameUs else previousKeyFrameUs
seekUs =
(if (nextKeyFrameInThreshold) nextKeyFrameUs else previousKeyFrameUs) + source.seekThreshold

if (VERBOSE) {
log.i(
"seek: current ${source.positionUs}," +
" requested $requested, threshold $threshold, nextKeyFrameUs $nextKeyFrameUs," +
" nextKeyFrameInThreshold:$nextKeyFrameInThreshold, seekUs: $seekUs, flushing : $seek"
)
}

shouldFlush = seek
shouldSeek = false
Expand Down Expand Up @@ -287,5 +294,6 @@ class DefaultThumbnailsEngine(

companion object {
private const val WAIT_MS = 5L
private const val VERBOSE = false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ default ArrayList<Long> getKeyFrameTimestamps() {
return new ArrayList<>();
}

default long getSeekThreshold() {
return 0;
}
default String mediaId() { return "";}
/**
* Rewinds this source, moving it to its default state.
* To be used again, tracks will be selected again.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public abstract class DefaultDataSource implements DataSource {
private long mDontRenderRangeEnd = -1L;

private final ArrayList<Long> keyFrameTimestamps = new ArrayList<>();
private final long SEEK_THRESHOLD = 10001L; // 10ms because extractor doesn't seek accurately
private final boolean VERBOSE = false;
@Override
public void initialize() {
LOG.i("initialize(): initializing...");
Expand Down Expand Up @@ -102,14 +104,14 @@ public long requestKeyFrameTimestamps() {

if(lastKeyFrame) return -1L;
if(keyFrameTimestamps.size() > 0) {
mExtractor.seekTo(keyFrameTimestamps.get(keyFrameTimestamps.size() - 1) + 10001L, MediaExtractor.SEEK_TO_NEXT_SYNC);
mExtractor.seekTo(keyFrameTimestamps.get(keyFrameTimestamps.size() - 1) + SEEK_THRESHOLD, MediaExtractor.SEEK_TO_NEXT_SYNC);
}

long sampleTime = mExtractor.getSampleTime();

if (sampleTime == -1 || (keyFrameTimestamps.size() > 0 && sampleTime == keyFrameTimestamps.get(keyFrameTimestamps.size() - 1))) {
lastKeyFrame = true;
mExtractor.seekTo(keyFrameTimestamps.get(keyFrameTimestamps.size() - 1) + 10001L, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
mExtractor.seekTo(keyFrameTimestamps.get(keyFrameTimestamps.size() - 1) + SEEK_THRESHOLD, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
return -1;
}
LOG.i("keyFrameStartTime:" + sampleTime);
Expand All @@ -124,7 +126,7 @@ public long requestKeyFrameTimestamps() {
}
keyFrameTimestamps.add(sampleTime);
}
mExtractor.seekTo(sampleTime + 10001L, MediaExtractor.SEEK_TO_NEXT_SYNC);
mExtractor.seekTo(sampleTime + SEEK_THRESHOLD, MediaExtractor.SEEK_TO_NEXT_SYNC);
lastSampleTime = sampleTime;
sampleTime = mExtractor.getSampleTime();
count++;
Expand All @@ -134,6 +136,11 @@ public long requestKeyFrameTimestamps() {
}


@Override
public long getSeekThreshold() {
return SEEK_THRESHOLD;
}

@Override
public void deinitialize() {
LOG.i("deinitialize(): deinitializing...");
Expand Down Expand Up @@ -189,13 +196,15 @@ public void releaseTrack(@NonNull TrackType type) {
public long seekTo(long desiredPositionUs) {
boolean hasVideo = mSelectedTracks.contains(TrackType.VIDEO);
boolean hasAudio = mSelectedTracks.contains(TrackType.AUDIO);
LOG.i("seekTo(): seeking to " + (mOriginUs + desiredPositionUs)
+ " originUs=" + mOriginUs
+ " extractorUs=" + mExtractor.getSampleTime()
+ " externalUs=" + desiredPositionUs
+ " hasVideo=" + hasVideo
+ " hasAudio=" + hasAudio);
if (hasVideo && hasAudio) {
if (VERBOSE) {
LOG.i("seekTo(): seeking to " + (mOriginUs + desiredPositionUs)
+ " originUs=" + mOriginUs
+ " extractorUs=" + mExtractor.getSampleTime()
+ " externalUs=" + desiredPositionUs
+ " hasVideo=" + hasVideo
+ " hasAudio=" + hasAudio);
}
// Special case: audio can be moved to any timestamp, but video will only stop in
// sync frames. MediaExtractor is not smart enough to sync the two tracks at the
// video sync frame, so we must take care of this with the following trick.
Expand All @@ -208,6 +217,14 @@ public long seekTo(long desiredPositionUs) {
mExtractor.seekTo(mExtractor.getSampleTime(), MediaExtractor.SEEK_TO_CLOSEST_SYNC);
LOG.v("seekTo(): seek workaround completed. (extractorUs=" + mExtractor.getSampleTime() + ")");
} else {
if (VERBOSE) {
LOG.i("seekTo(): seeking to " + (desiredPositionUs)
+ " originUs=" + mOriginUs
+ " extractorUs=" + mExtractor.getSampleTime()
+ " externalUs=" + desiredPositionUs
+ " hasVideo=" + hasVideo
+ " hasAudio=" + hasAudio);
}
mExtractor.seekTo(desiredPositionUs, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
}
mDontRenderRangeStart = mExtractor.getSampleTime();
Expand Down