From 1995fc6b756d1519982a6821025fe6f4eac7cc59 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Mon, 24 Aug 2026 20:16:21 +0200 Subject: [PATCH] refactor(player): demux through random-access IO instead of unwinding libavformat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resuming or seeking a large MKV on the Android FFmpeg demux path buffered for minutes with no error and no fallback. The cause was the seam, not the container: libavformat's demuxers own their seeking — end-of-file index, back to the header, binary search — while media3's Extractor is forward only, so every backward jump had to abort the in-flight libavformat call with a synthetic IO error and replay the whole open. matroskadec attempts its deferred Cues parse exactly once per context, so that abort burned the index for the session and every later seek fell back to libavformat's linear generic scan, reading the file up to the target. That protocol had produced a bug of this shape repeatedly (a NULL deref resuming find_stream_info across an abort, a use-after-free on fallback teardown, matroskadec resync skipping the keyframe cluster) and it was held together by four empirical retry budgets whose exhaustion degraded silently — which is why #2096 looked like an unbounded spinner instead of an error. Give libavformat what it requires instead. FfmpegRandomAccessSource serves any absolute position from a second DataSource built by the same factory media3 uses, cloning the DataSpec media3 opened for the item so Cronet, download caches, SAF and per-item request headers still apply. Reads that the loader's ExtractorInput can serve still go through it, so sample delivery keeps feeding media3's byte accounting, back-pressure and load-error policy; the extractor nudges the loader to follow the demuxer with one RESULT_SEEK, which is an optimization that can never stall playback because the read succeeds either way. Deleted with the protocol: the synthetic AVERROR_NEED_SEEK unwind, the 24 MB replay cache, the Cues priming seek and its read guard, the sticky AVIO error clearing, and all four budgets (seekAttempts, stickyRecovers, MAX_RECONCILES, MAX_OPEN_LOADER_ROUND_TRIPS). Opens run straight through, seeks are one avformat_seek_file on the loader thread, and an IO failure is an IOException that reaches media3's retry policy instead of a silent degradation. The demuxer is now the only component that resolves seeks, so the extractor's sample-derived seek index is gone too. Verified on a SEI Robotics Android TV box against Jellyfin: the DV P8.1 28.4 GB MKV resume that started at a 4m28s spinner opens in 1.1 s and resolves its seek in one call, a mid-playback scrub resolves in 1 ms from the in-memory index, a 2 GB MKV resume opens in 0.5 s, fresh playback needs no index read at all, and MP4/AVI still open and deliver. New coverage: FfmpegRandomAccessSourceTest pins the read/reopen contract, and FfmpegExtractorSeekTest asserts on-device that a cued seek lands on target without walking the file and that a cueless file still reaches it. close #2096 --- android/app/proguard-rules.pro | 7 +- .../androidTest/assets/ffmpeg/seek_cued.mkv | Bin 0 -> 125794 bytes .../assets/ffmpeg/seek_cueless.mkv | Bin 0 -> 130542 bytes .../FfmpegExtractorAudioCsdPlaybackTest.kt | 7 +- .../exoplayer/FfmpegExtractorSeekTest.kt | 321 +++++++++++++ .../ffmpeg_demuxer_jni.cc | 431 +++++------------- .../edde746/plezy/exoplayer/ExoPlayerCore.kt | 101 ++-- .../plezy/exoplayer/FfmpegDemuxerJni.kt | 49 +- .../plezy/exoplayer/FfmpegExtractor.kt | 282 +++++------- .../exoplayer/FfmpegRandomAccessSource.kt | 167 +++++++ .../exoplayer/FfmpegRandomAccessSourceTest.kt | 222 +++++++++ 11 files changed, 1024 insertions(+), 563 deletions(-) create mode 100644 android/app/src/androidTest/assets/ffmpeg/seek_cued.mkv create mode 100644 android/app/src/androidTest/assets/ffmpeg/seek_cueless.mkv create mode 100644 android/app/src/androidTest/kotlin/com/edde746/plezy/exoplayer/FfmpegExtractorSeekTest.kt create mode 100644 android/app/src/main/kotlin/com/edde746/plezy/exoplayer/FfmpegRandomAccessSource.kt create mode 100644 android/app/src/test/kotlin/com/edde746/plezy/exoplayer/FfmpegRandomAccessSourceTest.kt diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro index fe1f0eb7d..2a20311e7 100644 --- a/android/app/proguard-rules.pro +++ b/android/app/proguard-rules.pro @@ -20,11 +20,12 @@ -keep class androidx.media3.decoder.SimpleDecoderOutputBuffer { *; } # ffmpeg_demuxer_jni.cc resolves the AVIO input proxy's callbacks by name -# (FindClass on the interface, GetMethodID for position/read/length). Keeping -# the interface members pins the names on every implementation, including the -# anonymous proxy inside FfmpegExtractor. +# (FindClass on the interface, GetMethodID for position/read/readAt/length). +# Keeping the interface members pins the names on every implementation, +# including the anonymous proxy inside FfmpegExtractor. -keep interface com.edde746.plezy.exoplayer.FfmpegDemuxerJni$Input { long position(); int read(byte[], int); + int readAt(long, byte[], int); long length(); } diff --git a/android/app/src/androidTest/assets/ffmpeg/seek_cued.mkv b/android/app/src/androidTest/assets/ffmpeg/seek_cued.mkv new file mode 100644 index 0000000000000000000000000000000000000000..776cdc3c7a8677fd8ea0fcbb4e88eb73d33f22b8 GIT binary patch literal 125794 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%0AQWAN;bPgApd*LusYX?z~f=Can2$%^t%^ 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

qr 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

qx=hkZa;e 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 mYdg;^wB@1enl?2B=V>2TbTD$S?qQ&diqp literal 0 HcmV?d00001 diff --git a/android/app/src/androidTest/assets/ffmpeg/seek_cueless.mkv b/android/app/src/androidTest/assets/ffmpeg/seek_cueless.mkv new file mode 100644 index 0000000000000000000000000000000000000000..85ce735662ad04c040f7b96bf3b85268a7533865 GIT binary patch literal 130542 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 literal 0 HcmV?d00001 diff --git a/android/app/src/androidTest/kotlin/com/edde746/plezy/exoplayer/FfmpegExtractorAudioCsdPlaybackTest.kt b/android/app/src/androidTest/kotlin/com/edde746/plezy/exoplayer/FfmpegExtractorAudioCsdPlaybackTest.kt index d37792c76..3acebb25c 100644 --- a/android/app/src/androidTest/kotlin/com/edde746/plezy/exoplayer/FfmpegExtractorAudioCsdPlaybackTest.kt +++ b/android/app/src/androidTest/kotlin/com/edde746/plezy/exoplayer/FfmpegExtractorAudioCsdPlaybackTest.kt @@ -7,6 +7,7 @@ import android.os.HandlerThread import androidx.media3.common.MediaItem import androidx.media3.common.PlaybackException import androidx.media3.common.Player +import androidx.media3.datasource.DataSpec import androidx.media3.datasource.DefaultDataSource import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.source.ProgressiveMediaSource @@ -66,12 +67,14 @@ class FfmpegExtractorAudioCsdPlaybackTest { handler.post { try { + val fixtureUri = Uri.fromFile(fixtureFile) val ffmpegOnlyExtractors = ExtractorsFactory { val extractor = FfmpegExtractor.create( { FfmpegDemuxerPolicy.Preference.FFMPEG }, DvConversionMode.DISABLED, DefaultSubtitleParserFactory(), - AssHandler() + AssHandler(), + FfmpegRandomAccessSource(DefaultDataSource.Factory(context)) { DataSpec(fixtureUri) } ) assertNotNull("FFmpeg demuxer JNI library is unavailable", extractor) arrayOf(extractor!!) @@ -95,7 +98,7 @@ class FfmpegExtractorAudioCsdPlaybackTest { val source = ProgressiveMediaSource.Factory( DefaultDataSource.Factory(context), ffmpegOnlyExtractors - ).createMediaSource(MediaItem.fromUri(Uri.fromFile(fixtureFile))) + ).createMediaSource(MediaItem.fromUri(fixtureUri)) player.setMediaSource(source) player.prepare() player.play() diff --git a/android/app/src/androidTest/kotlin/com/edde746/plezy/exoplayer/FfmpegExtractorSeekTest.kt b/android/app/src/androidTest/kotlin/com/edde746/plezy/exoplayer/FfmpegExtractorSeekTest.kt new file mode 100644 index 000000000..6e21f441f --- /dev/null +++ b/android/app/src/androidTest/kotlin/com/edde746/plezy/exoplayer/FfmpegExtractorSeekTest.kt @@ -0,0 +1,321 @@ +package com.edde746.plezy.exoplayer + +import android.content.Context +import android.net.Uri +import androidx.media3.common.C +import androidx.media3.common.DataReader +import androidx.media3.common.Format +import androidx.media3.common.util.ParsableByteArray +import androidx.media3.datasource.DataSource +import androidx.media3.datasource.DataSpec +import androidx.media3.datasource.DefaultDataSource +import androidx.media3.datasource.TransferListener +import androidx.media3.extractor.DefaultExtractorInput +import androidx.media3.extractor.Extractor +import androidx.media3.extractor.ExtractorInput +import androidx.media3.extractor.ExtractorOutput +import androidx.media3.extractor.PositionHolder +import androidx.media3.extractor.SeekMap +import androidx.media3.extractor.TrackOutput +import androidx.media3.extractor.text.DefaultSubtitleParserFactory +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.edde746.plezy.libass.media.AssHandler +import java.io.File +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Regression coverage for #2096 and for the seam that caused it. + * + * libavformat resolves seeks through its own index, reading it via + * [FfmpegRandomAccessSource]. When that read cannot be served, matroskadec + * burns its one-shot Cues parse and every later seek degrades to libavformat's + * linear generic scan — which is what buffered a resume for minutes. The cued + * fixture here has the same geometry as the file from the report (front + * SeekHead, Cues at 99.5% of the file), so a scan is trivially distinguishable + * from an index seek: it has to read most of the file. + */ +@RunWith(AndroidJUnit4::class) +class FfmpegExtractorSeekTest { + + private companion object { + // Regenerate with: + // ffmpeg -f lavfi -i "testsrc=size=160x120:rate=10:duration=30" \ + // -c:v libx264 -g 10 -pix_fmt yuv420p -preset veryfast seek_cued.mkv + // mkvmerge --no-cues -o seek_cueless.mkv seek_cued.mkv + // The cued fixture keeps ffmpeg's default layout: a SeekHead at byte 52 + // pointing at Cues written after the clusters, at 99.5% of the file. + const val CUED = "ffmpeg/seek_cued.mkv" + const val CUELESS = "ffmpeg/seek_cueless.mkv" + + /** The fixtures are 30 s at 10 fps with a keyframe every second. */ + const val TARGET_US = 25_000_000L + const val KEYFRAME_TOLERANCE_US = 1_500_000L + } + + @Test + fun cuedSeekLandsOnTheTargetWithoutWalkingTheFile() { + withHarness(CUED) { harness -> + harness.pumpUntilSampleDelivered() + assertTrue( + "playback should start at the beginning, got ${harness.lastSampleUs}us", + harness.lastSampleUs < 500_000L + ) + + harness.resetByteCounter() + harness.seekTo(TARGET_US) + val landed = harness.pumpUntilSampleAtLeast(TARGET_US - KEYFRAME_TOLERANCE_US) + + assertTrue("no sample delivered after seeking to ${TARGET_US}us", landed) + assertTrue( + "seek landed at ${harness.lastSampleUs}us, expected within " + + "${KEYFRAME_TOLERANCE_US}us of ${TARGET_US}us", + harness.lastSampleUs <= TARGET_US + KEYFRAME_TOLERANCE_US + ) + // An index seek reads the Cues plus the target cluster. A generic scan + // has to read everything up to 83% of the file to get here. + val budget = harness.fixtureSize / 3 + assertTrue( + "seek read ${harness.bytesRead} bytes of a ${harness.fixtureSize}-byte file; " + + "expected under $budget (index seek, not a scan)", + harness.bytesRead < budget + ) + // Reopening per read would mean one request per 64 KB in steady state. + assertTrue("random-access opens after seek: ${harness.randomAccessOpens}", harness.randomAccessOpens <= 4) + } + } + + /** + * Without Cues libavformat falls back to its generic scan, exactly as media3's + * own MatroskaExtractor scans clusters. That is slow but it must still land on + * the target rather than stalling or silently delivering from the wrong place. + */ + @Test + fun cuelessSeekStillReachesTheTarget() { + withHarness(CUELESS) { harness -> + harness.pumpUntilSampleDelivered() + harness.seekTo(TARGET_US) + val landed = harness.pumpUntilSampleAtLeast(TARGET_US - KEYFRAME_TOLERANCE_US) + + assertTrue("cueless seek to ${TARGET_US}us delivered nothing", landed) + assertTrue( + "cueless seek landed at ${harness.lastSampleUs}us", + harness.lastSampleUs <= TARGET_US + KEYFRAME_TOLERANCE_US + ) + } + } + + @Test + fun playbackFromTheStartDeliversEverySampleFromTheBeginning() { + withHarness(CUED) { harness -> + val timestamps = harness.pumpSamples(count = 40) + + assertTrue("expected samples from the start, got ${timestamps.size}", timestamps.size >= 40) + assertTrue("first sample at ${timestamps.first()}us", timestamps.first() < 500_000L) + // Delivery is in decode order, which is not presentation order once the + // encoder emits B-frames; what must hold is that the set of samples is + // gapless from zero (this fixture is a steady 10 fps). + val sorted = timestamps.sorted() + assertEquals(sorted.indices.map { it * 100_000L }, sorted) + } + } + + private fun withHarness(fixture: String, block: (Harness) -> Unit) { + val instrumentation = InstrumentationRegistry.getInstrumentation() + val context = instrumentation.targetContext + val file = copyFixture(instrumentation.context, context, fixture) + val harness = Harness(context, file) + try { + harness.prepare() + block(harness) + } finally { + harness.close() + file.delete() + } + } + + private fun copyFixture(instrumentationContext: Context, targetContext: Context, fixture: String): File { + val output = File.createTempFile("ffmpeg-seek-fixture-", ".mkv", targetContext.cacheDir) + instrumentationContext.assets.open(fixture).use { input -> + output.outputStream().use(input::copyTo) + } + return output + } + + /** + * Minimal stand-in for ProgressiveMediaPeriod's loader: it opens the data + * source, feeds an ExtractorInput, and honours RESULT_SEEK by re-opening — + * the same contract media3 implements in ExtractingLoadable.load(). + */ + private class Harness(context: Context, private val fixture: File) { + val fixtureSize = fixture.length() + private val uri: Uri = Uri.fromFile(fixture) + private val counting = CountingDataSourceFactory(DefaultDataSource.Factory(context)) + private val io = FfmpegRandomAccessSource(counting) { DataSpec(uri) } + private val output = CapturingOutput() + private val positionHolder = PositionHolder() + private val extractor = requireNotNull( + FfmpegExtractor.create( + { FfmpegDemuxerPolicy.Preference.FFMPEG }, + DvConversionMode.DISABLED, + DefaultSubtitleParserFactory(), + AssHandler(), + io + ) + ) { "native ffmpeg demuxer unavailable" } + + private var dataSource: DataSource? = null + private var input: ExtractorInput? = null + private var opensBaseline = 0 + + val lastSampleUs: Long + get() = output.lastSampleUs + + val bytesRead: Long + get() = counting.bytesRead + + val randomAccessOpens: Int + get() = io.openCount - opensBaseline + + fun prepare() { + openAt(0) + assertTrue("fixture should be sniffed by the ffmpeg demuxer", extractor.sniff(input!!)) + input!!.resetPeekPosition() + extractor.init(output) + } + + fun resetByteCounter() { + counting.reset() + opensBaseline = io.openCount + } + + fun seekTo(timeUs: Long) { + // media3 restarts the load at the SeekMap's byte hint, then calls seek() + // on the loader thread before the next read. + val hint = output.seekMap?.getSeekPoints(timeUs)?.first?.position ?: 0L + openAt(hint) + extractor.seek(hint, timeUs) + } + + fun pumpUntilSampleDelivered(): Boolean { + val before = output.sampleCount + return pump { output.sampleCount > before } + } + + fun pumpUntilSampleAtLeast(timeUs: Long): Boolean = pump { output.lastSampleUs >= timeUs } + + fun pumpSamples(count: Int): List { + pump { output.sampleTimestamps.size >= count } + return output.sampleTimestamps.toList() + } + + private fun pump(until: () -> Boolean): Boolean { + repeat(200_000) { + if (until()) return true + when (extractor.read(input!!, positionHolder)) { + Extractor.RESULT_SEEK -> openAt(positionHolder.position) + Extractor.RESULT_END_OF_INPUT -> return until() + else -> Unit + } + } + return until() + } + + private fun openAt(position: Long) { + dataSource?.close() + val source = counting.createDataSource() + val remaining = source.open(DataSpec.Builder().setUri(uri).setPosition(position).build()) + dataSource = source + input = DefaultExtractorInput( + source, + position, + if (remaining == C.LENGTH_UNSET.toLong()) C.LENGTH_UNSET.toLong() else position + remaining + ) + } + + fun close() { + extractor.release() + dataSource?.close() + } + } + + private class CapturingOutput : ExtractorOutput { + val sampleTimestamps = mutableListOf() + var seekMap: SeekMap? = null + private set + + val sampleCount: Int + get() = sampleTimestamps.size + + val lastSampleUs: Long + get() = sampleTimestamps.lastOrNull() ?: C.TIME_UNSET + + override fun track(id: Int, type: Int): TrackOutput = CapturingTrack(type, sampleTimestamps) + + override fun endTracks() = Unit + + override fun seekMap(seekMap: SeekMap) { + this.seekMap = seekMap + } + } + + private class CapturingTrack(private val type: Int, private val timestamps: MutableList) : TrackOutput { + private val scratch = ByteArray(64 * 1024) + + override fun format(format: Format) = Unit + + override fun sampleData(input: DataReader, length: Int, allowEndOfInput: Boolean): Int = sampleData(input, length, allowEndOfInput, TrackOutput.SAMPLE_DATA_PART_MAIN) + + override fun sampleData(input: DataReader, length: Int, allowEndOfInput: Boolean, sampleDataPart: Int): Int { + val read = input.read(scratch, 0, minOf(length, scratch.size)) + return if (read == C.RESULT_END_OF_INPUT) 0 else read + } + + override fun sampleData(data: ParsableByteArray, length: Int) { + sampleData(data, length, TrackOutput.SAMPLE_DATA_PART_MAIN) + } + + override fun sampleData(data: ParsableByteArray, length: Int, sampleDataPart: Int) { + data.skipBytes(length) + } + + override fun sampleMetadata(timeUs: Long, flags: Int, size: Int, offset: Int, cryptoData: TrackOutput.CryptoData?) { + if (type == C.TRACK_TYPE_VIDEO) timestamps.add(timeUs) + } + } + + /** Counts every byte the demuxer pulls, through either read path. */ + private class CountingDataSourceFactory(private val delegate: DataSource.Factory) : DataSource.Factory { + @Volatile private var counter = 0L + + val bytesRead: Long + get() = counter + + fun reset() { + counter = 0L + } + + override fun createDataSource(): DataSource = Counting(delegate.createDataSource()) + + private inner class Counting(private val inner: DataSource) : DataSource { + override fun open(dataSpec: DataSpec): Long = inner.open(dataSpec) + + override fun read(buffer: ByteArray, offset: Int, length: Int): Int { + val read = inner.read(buffer, offset, length) + if (read > 0) counter += read + return read + } + + override fun addTransferListener(transferListener: TransferListener) = inner.addTransferListener(transferListener) + + override fun getUri(): Uri? = inner.uri + + override fun getResponseHeaders(): Map> = inner.responseHeaders + + override fun close() = inner.close() + } + } +} diff --git a/android/app/src/main/cpp/media3_ffmpeg_demuxer/ffmpeg_demuxer_jni.cc b/android/app/src/main/cpp/media3_ffmpeg_demuxer/ffmpeg_demuxer_jni.cc index eac4b4e56..3519cc1f7 100644 --- a/android/app/src/main/cpp/media3_ffmpeg_demuxer/ffmpeg_demuxer_jni.cc +++ b/android/app/src/main/cpp/media3_ffmpeg_demuxer/ffmpeg_demuxer_jni.cc @@ -5,30 +5,33 @@ * is served by a media3 ExtractorInput, so FFmpeg parses the container while * media3 keeps owning decoders, renderers, and the whole playback surface. * - * The hard seam is seeking. media3's Extractor is a pull API: an extractor + * The seam used to be seeking. media3's Extractor is a pull API: an extractor * cannot move the input; it returns RESULT_SEEK and the loader re-opens the - * data source at the requested position. libavformat expects a random-access - * AVIO layer and seeks whenever it pleases (MP4 moov at end of file, AVI idx1, - * MKV Cues, avformat_find_stream_info read-ahead). The bridge reconciles the - * two with a deferral protocol: + * data source. libavformat expects random access and seeks whenever it pleases + * (MP4 moov at end of file, AVI idx1, MKV Cues, MPEG-PS binary search), so + * serving it from the loader alone meant unwinding the in-flight libavformat + * call on every backward jump and replaying the whole open — which a + * one-shot lazy parse like matroska's Cues cannot survive (#2096). * - * - The read callback only serves when the AVIO logical position equals the - * ExtractorInput position. Any divergence aborts the in-flight libavformat - * call with AVERROR_NEED_SEEK and records the wanted position. - * - Kotlin maps that to RESULT_SEEK (or, when the loader already moved the - * input to exactly that position, to a local resume). After the input is in - * place, resumeAfterSeek() runs avio_seek(), whose success invalidates the - * stale AVIO buffers, and the interrupted operation is retried. - * - During avformat_open_input/avformat_find_stream_info a block cache serves - * header re-reads from memory so each distinct seek position costs at most - * one loader round trip even though open restarts from zero. + * libavformat now gets what it actually requires: the Kotlin side hands this + * shim a random-access byte source (FfmpegRandomAccessSource), so every AVIO + * read succeeds at the position libavformat asked for and nothing is ever + * unwound. Two paths serve reads, chosen per read: + * + * - The loader's ExtractorInput when it already stands at that position. This + * is the steady state for sample delivery, and it keeps media3's byte + * accounting, back-pressure and load-error policy in charge of streaming. + * - The random-access source otherwise (header, index, post-seek jumps). + * + * Kotlin nudges the loader back into step after a jump by returning + * RESULT_SEEK to nativeLogicalPosition(); that is an optimization, never a + * correctness requirement, so no budgets or retries guard it. */ #include #include #include #include -#include #include extern "C" { @@ -56,18 +59,12 @@ extern "C" { namespace { -// Returned by the AVIO callbacks to unwind into Java when the ExtractorInput -// cannot serve the requested position synchronously. FFERRTAG values are -// distinct from every real AVERROR code. -const int AVERROR_NEED_SEEK = FFERRTAG('P', 'L', 'Z', 'S'); - // readPacket() result codes written to out[0]. Negative values are raw -// AVERROR codes; ERR_JAVA signals the ExtractorInput threw and Kotlin must +// AVERROR codes; ERR_JAVA signals the byte source threw and Kotlin must // rethrow the stored IOException. const jint CODE_PACKET = 0; const jint CODE_EOF = 1; -const jint CODE_NEED_SEEK = 2; -const jint CODE_GROW = 3; +const jint CODE_GROW = 2; const jint ERR_NOT_OPEN = -101; const jint ERR_JAVA = -102; @@ -108,21 +105,14 @@ const jint ROLE_FLAG_MAIN = 1; const jint ROLE_FLAG_DESCRIPTION = 64; // C.ROLE_FLAG_DESCRIPTION const jint ROLE_FLAG_HARD_OF_HEARING = 512; // C.ROLE_FLAG_HARD_OF_HEARING -constexpr int64_t kNoPendingSeek = -1; -constexpr int64_t kNoPendingTimeSeek = INT64_MIN; -constexpr size_t kMaxCacheBytes = 24u * 1024 * 1024; constexpr jint kReadChunkBytes = 64 * 1024; -struct CacheBlock { - int64_t pos; - std::vector data; -}; - struct DemuxState { JavaVM* vm = nullptr; jobject input = nullptr; jmethodID midPosition = nullptr; jmethodID midRead = nullptr; + jmethodID midReadAt = nullptr; jmethodID midLength = nullptr; jbyteArray readBuffer = nullptr; @@ -130,25 +120,14 @@ struct DemuxState { AVIOContext* avio = nullptr; bool opened = false; + // Byte position libavformat will read next. Authoritative: the AVIO seek + // callback moves it, and both read paths serve exactly this offset. int64_t logicalPos = 0; - int64_t pendingSeek = kNoPendingSeek; - // Presentation-time seek requested via Extractor.seek(); executed by the - // next nativeReadPacket, where the input proxy is bound and deferrals can - // round-trip through the loader. - int64_t pendingTimeSeekUs = kNoPendingTimeSeek; - // Loader round trips consumed by the pending time seek; a seek whose - // avformat_seek_file keeps deferring past this budget is abandoned so - // playback continues instead of ping-ponging forever. - int seekAttempts = 0; // format->start_time in microseconds; subtracted from delivered pts so // media3's zero-based timeline matches the duration we report (MPEG-PS/TS // containers commonly start at a nonzero PCR). int64_t startTimeUs = 0; - bool cacheActive = true; - std::deque cache; - size_t cacheBytes = 0; - AVPacket* packet = nullptr; AVPacket* filteredPacket = nullptr; // A packet that outgrew the Java-side buffer: already read and filtered, @@ -208,28 +187,16 @@ jint callRead(JNIEnv* env, DemuxState* s, jint length) { return n; } -void cacheClear(DemuxState* s) { - s->cache.clear(); - s->cacheBytes = 0; -} - -void cacheAppend(DemuxState* s, int64_t pos, const uint8_t* data, size_t size) { - if (!s->cacheActive) return; - s->cache.push_back(CacheBlock{pos, std::vector(data, data + size)}); - s->cacheBytes += size; - while (s->cacheBytes > kMaxCacheBytes && !s->cache.empty()) { - s->cacheBytes -= s->cache.front().data.size(); - s->cache.pop_front(); +// Same contract as callRead, but at an absolute position: the Kotlin source +// reopens its handle when the position is not where it stands. +jint callReadAt(JNIEnv* env, DemuxState* s, int64_t position, jint length) { + jint n = env->CallIntMethod(s->input, s->midReadAt, static_cast(position), s->readBuffer, length); + if (javaPending(env)) { + javaClear(env); + s->javaError = true; + return -1; } -} - -const CacheBlock* cacheFind(const DemuxState* s, int64_t pos) { - for (const CacheBlock& block : s->cache) { - if (pos >= block.pos && pos < block.pos + static_cast(block.data.size())) { - return █ - } - } - return nullptr; + return n; } int avioReadPacket(void* opaque, uint8_t* buf, int bufSize) { @@ -238,55 +205,22 @@ int avioReadPacket(void* opaque, uint8_t* buf, int bufSize) { if (env == nullptr) return AVERROR(EIO); if (bufSize <= 0) return 0; - // Cache-first: bytes already read are served at any logical position, - // which lets restarted header phases (probe → hdrl → idx1 at end of file - // → back to movi) replay through seeks that never touch the loader. - const CacheBlock* block = cacheFind(s, s->logicalPos); - if (block != nullptr) { - size_t offset = static_cast(s->logicalPos - block->pos); - size_t n = std::min(bufSize, block->data.size() - offset); - std::memcpy(buf, block->data.data() + offset, n); - s->logicalPos += static_cast(n); - return static_cast(n); - } - - // A read at or beyond the known end of file is EOF regardless of where the - // loader sits. EOF yields no bytes, so it can never enter the block cache; - // deferring it would let an open that alternates between low offsets and an - // end-of-file probe (avi idx1/ODML index scans) ping-pong against the - // loader forever, one round trip per attempt. - int64_t knownLength = callLength(env, s); + const jint want = bufSize < kReadChunkBytes ? bufSize : kReadChunkBytes; + // Prefer the loader's input when it already stands here: those bytes then + // count as media3's own load progress, which is what drives its buffering + // and back-pressure. Anywhere else, read at the absolute position. + const int64_t inputPos = callPosition(env, s); if (javaPending(env)) { javaClear(env); s->javaError = true; return AVERROR(EIO); } - if (knownLength >= 0 && s->logicalPos >= knownLength) return AVERROR_EOF; - - // Uncached read: the input must be exactly where libavformat thinks it is. - // Any divergence aborts the in-flight call so Kotlin reconciles through - // avio_seek, which also invalidates the stale AVIO buffers. - if (s->logicalPos != callPosition(env, s)) { - if (javaPending(env)) { - javaClear(env); - s->javaError = true; - return AVERROR(EIO); - } - LOGW( - "read defer: logical=%lld input=%lld active=%d cacheBlocks=%zu cacheBytes=%zu first=%lld last=%lld", - (long long)s->logicalPos, (long long)callPosition(env, s), s->cacheActive ? 1 : 0, s->cache.size(), - s->cacheBytes, s->cache.empty() ? -1 : (long long)s->cache.front().pos, - s->cache.empty() ? -1 : (long long)s->cache.back().pos); - s->pendingSeek = s->logicalPos; - return AVERROR_NEED_SEEK; - } - - jint n = callRead(env, s, bufSize < kReadChunkBytes ? bufSize : kReadChunkBytes); + const jint n = inputPos == s->logicalPos ? callRead(env, s, want) : callReadAt(env, s, s->logicalPos, want); if (s->javaError) return AVERROR(EIO); - if (n <= 0) return AVERROR_EOF; + if (n < 0) return AVERROR(EIO); + if (n == 0) return AVERROR_EOF; env->GetByteArrayRegion(s->readBuffer, 0, n, reinterpret_cast(buf)); - cacheAppend(s, s->logicalPos, buf, static_cast(n)); s->logicalPos += n; return n; } @@ -323,21 +257,23 @@ int64_t avioSeek(void* opaque, int64_t offset, int whence) { target = offset; } - // Seeks are pure bookkeeping and never touch the loader. Restarted header - // replays hop through cached regions for free; a seek into an uncached - // region surfaces as a deferral on the next uncached read, with the input - // still where the loader left it. avio invalidates its buffers after this - // successful callback. - // - // Deliberately does NOT touch s->pendingSeek: an unconsumed deferral target - // must survive the avformat unwind until Kotlin consumes it. + // Pure bookkeeping: the next read serves this position from whichever path + // can, so a seek can no longer fail or need reconciling. avio invalidates + // its own buffers after this successful callback. s->logicalPos = target; - // A deferral leaves AVIOContext::error sticky; once a seek has been adopted - // it must not replay into later reads. - if (s->avio != nullptr) s->avio->error = 0; return target; } +// Probing (ffio_ensure_seekback) can swap the AVIO buffer for a larger +// allocation and free the original, so the pointer handed to +// avio_alloc_context may be stale. Free whichever buffer the context +// currently holds, exactly once. +void freeAvio(DemuxState* s) { + if (s->avio == nullptr) return; + av_freep(&s->avio->buffer); + avio_context_free(&s->avio); +} + void closeState() { DemuxState* s = gState; if (s == nullptr) return; // Close without (or after) an open is a no-op. @@ -357,14 +293,7 @@ void closeState() { if (s->format != nullptr) { avformat_close_input(&s->format); } - if (s->avio != nullptr) { - // Probing (ffio_ensure_seekback) can swap the AVIO buffer for a larger - // allocation and free the original, so the pointer handed to - // avio_alloc_context may be stale. Free whichever buffer the context - // currently holds, exactly once. - av_freep(&s->avio->buffer); - avio_context_free(&s->avio); - } + freeAvio(s); if (env != nullptr && s->input != nullptr) { if (s->readBuffer != nullptr) env->DeleteGlobalRef(s->readBuffer); env->DeleteGlobalRef(s->input); @@ -373,8 +302,6 @@ void closeState() { gState = nullptr; } -bool deferred(DemuxState* s, int err) { return err == AVERROR_NEED_SEEK || s->pendingSeek != kNoPendingSeek; } - const char* videoMime(AVCodecID id) { switch (id) { case AV_CODEC_ID_H264: @@ -528,10 +455,9 @@ Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeProbeFormat(JNIEnv* env, return env->NewStringUTF(format->name); } -// Returns 0 on success, CODE_NEED_SEEK when a loader round trip is required, -// or a negative AVERROR. The stream count is NOT the return value: positive -// counts share the result-code namespace (CODE_NEED_SEEK == 2 would collide -// with every two-stream container), so callers use nativeStreamCount(). +// Returns 0 on success or a negative AVERROR (ERR_JAVA when the byte source +// itself threw). The stream count is NOT the return value: positive counts +// would share the result-code namespace, so callers use nativeStreamCount(). JNIEXPORT jint JNICALL Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeOpen(JNIEnv* env, jobject thiz, jobject input) { StateLock lock; @@ -544,7 +470,6 @@ Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeOpen(JNIEnv* env, jobjec gState->vm = gVm; } DemuxState* s = gState; - if (s->avio != nullptr) s->avio->error = 0; // FindClass on the declared interface (not GetObjectClass on the concrete // proxy): JNI dispatches interface method IDs virtually, and the named @@ -556,9 +481,10 @@ Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeOpen(JNIEnv* env, jobjec } jmethodID midPosition = env->GetMethodID(inputClass, "position", "()J"); jmethodID midRead = env->GetMethodID(inputClass, "read", "([BI)I"); + jmethodID midReadAt = env->GetMethodID(inputClass, "readAt", "(J[BI)I"); jmethodID midLength = env->GetMethodID(inputClass, "length", "()J"); env->DeleteLocalRef(inputClass); - if (midPosition == nullptr || midRead == nullptr || midLength == nullptr) { + if (midPosition == nullptr || midRead == nullptr || midReadAt == nullptr || midLength == nullptr) { javaClear(env); return ERR_NOT_OPEN; } @@ -573,6 +499,7 @@ Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeOpen(JNIEnv* env, jobjec s->input = env->NewGlobalRef(input); s->midPosition = midPosition; s->midRead = midRead; + s->midReadAt = midReadAt; s->midLength = midLength; jbyteArray readBuffer = env->NewByteArray(kReadChunkBytes); s->readBuffer = static_cast(env->NewGlobalRef(readBuffer)); @@ -584,44 +511,29 @@ Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeOpen(JNIEnv* env, jobjec if (s->packet == nullptr || s->filteredPacket == nullptr) return AVERROR(ENOMEM); } + // One AVIOContext per item: a stale buffer or a sticky error from the + // previous item must not leak into this open, and reallocating is cheaper + // than reasoning about which of libavformat's internal fields to reset. + freeAvio(s); + constexpr int kAvioBufferSize = 64 * 1024; + unsigned char* avioBuffer = static_cast(av_malloc(kAvioBufferSize)); + if (avioBuffer == nullptr) return AVERROR(ENOMEM); + s->avio = avio_alloc_context( + avioBuffer, kAvioBufferSize, /*write_flag=*/0, s, avioReadPacket, + /*write_packet=*/nullptr, avioSeek); if (s->avio == nullptr) { - constexpr int kAvioBufferSize = 64 * 1024; - unsigned char* avioBuffer = static_cast(av_malloc(kAvioBufferSize)); - s->avio = avio_alloc_context( - avioBuffer, kAvioBufferSize, /*write_flag=*/0, s, avioReadPacket, - /*write_packet=*/nullptr, avioSeek); - if (s->avio == nullptr) return AVERROR(ENOMEM); + av_freep(&avioBuffer); + return AVERROR(ENOMEM); } s->javaError = false; - s->pendingSeek = kNoPendingSeek; - s->pendingTimeSeekUs = kNoPendingTimeSeek; - s->seekAttempts = 0; s->packetPending = false; - // Every open attempt restarts from scratch and replays the header phase - // through the block cache. Resuming avformat_find_stream_info across - // synthetic IO aborts is not something libavformat contracts to survive - // (observed as a NULL deref inside avformat_find_stream_info), and with the - // cache a full replay costs nothing on the wire. - // - // The cache must survive retries within one open: matroska's header phase - // jumps backward twice (SeekHead -> Cues -> clusters), and clearing here — - // per attempt — makes every retry replay against an empty cache and - // ping-pong against the loader until the reconcile cap kills the open. - // Invalidation is per media item and lives in nativeResetCache, called - // from FfmpegExtractor.init when media3 binds a new source. + s->logicalPos = 0; + // Nothing is ever resumed across calls now: every read succeeds at the + // position libavformat asked for, so open_input and find_stream_info each + // run once, to completion. avformat_close_input(&s->format); s->opened = false; - s->cacheActive = true; - - int64_t seekResult = avio_seek(s->avio, 0, SEEK_SET); - if (s->pendingSeek != kNoPendingSeek) { - return CODE_NEED_SEEK; - } - if (seekResult < 0) { - LOGW("open seek0 failed=%lld sticky=%d", (long long)seekResult, s->avio != nullptr ? s->avio->error : 0); - return static_cast(seekResult); - } s->format = avformat_alloc_context(); if (s->format == nullptr) return AVERROR(ENOMEM); @@ -631,33 +543,22 @@ Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeOpen(JNIEnv* env, jobjec s->format->flags |= AVFMT_FLAG_GENPTS; int err = avformat_open_input(&s->format, "", nullptr, nullptr); - if (deferred(s, err)) { - LOGW("open_input defer pending=%lld err=%d", (long long)s->pendingSeek, err); - return CODE_NEED_SEEK; - } + if (s->javaError) return ERR_JAVA; if (err < 0) { LOGW("avformat_open_input failed: %d", err); return err; } err = avformat_find_stream_info(s->format, nullptr); - if (deferred(s, err)) { - LOGW("find_info defer pending=%lld err=%d", (long long)s->pendingSeek, err); - return CODE_NEED_SEEK; - } + if (s->javaError) return ERR_JAVA; if (err < 0) { LOGW("avformat_find_stream_info failed: %d", err); return err; } - s->opened = true; // Zero-base the timeline: delivered pts subtract this so media3 sees // [0, duration] even when the container starts at a nonzero PCR/PTS. s->startTimeUs = s->format->start_time != AV_NOPTS_VALUE ? s->format->start_time : 0; - - // Header work is done: packet reads are sequential and every backward move - // goes through a real loader seek. - s->cacheActive = false; - cacheClear(s); + s->opened = true; bool anySupported = false; for (unsigned i = 0; i < s->format->nb_streams; i++) { @@ -733,18 +634,6 @@ JNIEXPORT void JNICALL Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeC closeState(); } -JNIEXPORT void JNICALL Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeResetCache(JNIEnv* env, jobject thiz) { - StateLock lock; - // Per-item boundary: media3 reuses extractor instances across sources, and - // a new item's header occupies the same low offsets as the previous item's. - // Serving those stale bytes would corrupt the new open, so drop everything; - // nativeOpen re-arms the active flag for its retry phase. - DemuxState* s = gState; - if (s == nullptr) return; - cacheClear(s); - s->cacheActive = true; -} - JNIEXPORT jint JNICALL Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeStreamCount(JNIEnv* env, jobject thiz) { StateLock lock; if (gState == nullptr || gState->format == nullptr) return ERR_NOT_OPEN; @@ -932,7 +821,6 @@ Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeAttachmentData(JNIEnv* e // [1]=streamIndex [2]=ptsUs (start-time normalized; packets without any // timestamp are dropped, media3 cannot schedule them) [3]=flags [4]=size // [5]=packet position in input coordinates [6]=durationUs (-1 unknown). -// Deferral targets travel through nativeConsumePendingSeek, not this array. JNIEXPORT jint JNICALL Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeReadPacket( JNIEnv* env, jobject thiz, jbyteArray buffer, jlongArray out) { StateLock lock; @@ -942,94 +830,7 @@ JNIEXPORT jint JNICALL Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeR jint capacity = env->GetArrayLength(buffer); jlong result[7] = {}; s->javaError = false; - s->pendingSeek = kNoPendingSeek; - - int stickyRecovers = 0; while (true) { - // A presentation-time seek from Extractor.seek() runs here, where the - // input proxy is bound and a deferral can round-trip through the loader. - // The demuxer picks the byte position and resets its own parse state — - // jumping the AVIO position under it would leave demuxer-private state - // (avi->remaining, PES continuations) pointing into the old stream. - // avformat_seek_file restarts from scratch on every retry; the block - // cache is re-armed so index/probe reads replayed across loader round - // trips (matroska Cues, mpegps binary search) stay free on the wire. - if (s->pendingTimeSeekUs != kNoPendingTimeSeek) { - if (++s->seekAttempts > 64) { - // Non-converging seek (each attempt = one loader round trip): - // abandon it and keep delivering from the current position rather - // than ping-ponging forever. - LOGW( - "time seek to %lld abandoned after %d loader round trips", (long long)s->pendingTimeSeekUs, - s->seekAttempts - 1); - s->pendingTimeSeekUs = kNoPendingTimeSeek; - s->cacheActive = false; - cacheClear(s); - } else { - if (s->bsf != nullptr) { - for (unsigned i = 0; i < s->nbStreams; i++) { - if (s->bsf[i] != nullptr) av_bsf_flush(s->bsf[i]); - } - } - s->packetPending = false; - s->cacheActive = true; - int64_t ts = s->pendingTimeSeekUs + s->startTimeUs; - int err = avformat_seek_file(s->format, -1, INT64_MIN, ts, ts, 0); - LOGD( - "time seek to %lld: err=%d logical=%lld attempts=%d", (long long)ts, err, (long long)s->logicalPos, - s->seekAttempts); - if (deferred(s, err)) { - if (s->pendingSeek == kNoPendingSeek) { - if (++stickyRecovers > 16 || s->avio == nullptr) return ERR_JAVA; - s->avio->error = 0; - continue; - } - // pendingTimeSeekUs stays set: the retry after the loader round - // trip re-runs the whole seek. - result[0] = CODE_NEED_SEEK; - env->SetLongArrayRegion(out, 0, 7, result); - return CODE_NEED_SEEK; - } - if (s->javaError) { - result[0] = ERR_JAVA; - env->SetLongArrayRegion(out, 0, 7, result); - return ERR_JAVA; - } - s->pendingTimeSeekUs = kNoPendingTimeSeek; - if (err < 0) { - // Unseekable stream or demuxer refusal: keep playing from the - // current position instead of killing the session; media3 will - // decode-discard toward the target if it can. - LOGW("avformat_seek_file to %lld failed: %d", (long long)ts, err); - } else { - // Align the loader with the demuxer's chosen byte position BEFORE - // the first read. avformat_seek_file resolves in memory (cues) and - // typically leaves the AVIO position behind the loader's SeekMap - // guess; letting av_read_frame discover that and unwind with a - // synthetic IO error poisons matroskadec into resync, which skips - // the keyframe cluster — the video renderer then starves until the - // next keyframe while audio keeps advancing, and the resume-stall - // watchdog seeks in a loop. The cache stays armed across this round - // trip (until the first packet delivers) so replayed header bytes - // stay free. - int64_t inputPos = callPosition(env, s); - if (javaPending(env)) { - javaClear(env); - s->javaError = true; - result[0] = ERR_JAVA; - env->SetLongArrayRegion(out, 0, 7, result); - return ERR_JAVA; - } - if (inputPos >= 0 && inputPos != s->logicalPos) { - s->pendingSeek = s->logicalPos; - result[0] = CODE_NEED_SEEK; - env->SetLongArrayRegion(out, 0, 7, result); - return CODE_NEED_SEEK; - } - } - } - } - if (s->packetPending) { // Oversized packet held across a CODE_GROW round trip: deliver it now // instead of reading (and losing) a new frame. @@ -1037,19 +838,6 @@ JNIEXPORT jint JNICALL Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeR } else { av_packet_unref(s->packet); int err = av_read_frame(s->format, s->packet); - if (deferred(s, err)) { - if (s->pendingSeek == kNoPendingSeek) { - // Sticky AVIO error replayed from a deferral Kotlin already - // reconciled: clear it and retry instead of surfacing a targetless - // seek. - if (++stickyRecovers > 16 || s->avio == nullptr) return ERR_JAVA; - s->avio->error = 0; - continue; - } - result[0] = CODE_NEED_SEEK; - env->SetLongArrayRegion(out, 0, 7, result); - return CODE_NEED_SEEK; - } if (err == AVERROR_EOF) { result[0] = CODE_EOF; env->SetLongArrayRegion(out, 0, 7, result); @@ -1111,13 +899,6 @@ JNIEXPORT jint JNICALL Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeR } ptsUs -= s->startTimeUs; - // Post-seek convergence is over once a packet flows; steady-state packet - // reads are sequential, so drop the replay cache until the next seek. - if (s->cacheActive) { - s->cacheActive = false; - cacheClear(s); - } - result[0] = CODE_PACKET; result[1] = s->packet->stream_index; result[2] = ptsUs; @@ -1132,42 +913,40 @@ JNIEXPORT jint JNICALL Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeR } JNIEXPORT jlong JNICALL -Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeConsumePendingSeek(JNIEnv* env, jobject thiz) { +Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeLogicalPosition(JNIEnv* env, jobject thiz) { StateLock lock; - if (gState == nullptr) return -9223372036854775807L - 1; // Long.MIN_VALUE - int64_t pending = gState->pendingSeek; - gState->pendingSeek = kNoPendingSeek; - return pending; + DemuxState* s = gState; + if (s == nullptr || !s->opened) return -1; + return static_cast(s->logicalPos); } -// Re-establishes the AVIO position after the loader moved the input to -// `position`. Must only be called when the ExtractorInput is already there. +// Seeks to a presentation time (media3 timeline microseconds, start-time +// normalized). Called on the loader thread, where blocking IO belongs: +// libavformat reads its own index through the random-access source, so this +// either lands on the target keyframe or reports that the container has no +// usable index. JNIEXPORT jint JNICALL -Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeResumeAfterSeek(JNIEnv* env, jobject thiz, jlong position) { +Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeSeek(JNIEnv* env, jobject thiz, jlong timeUs) { StateLock lock; DemuxState* s = gState; - if (s == nullptr || s->avio == nullptr) return ERR_NOT_OPEN; - s->pendingSeek = kNoPendingSeek; + if (s == nullptr || !s->opened || s->format == nullptr) return ERR_NOT_OPEN; s->javaError = false; - int64_t result = avio_seek(s->avio, position, SEEK_SET); - if (result >= 0) return 0; - if (s->pendingSeek != kNoPendingSeek) return CODE_NEED_SEEK; - return static_cast(result); -} - -// Records a presentation-time seek (media3 timeline microseconds, i.e. -// start-time normalized). Executed by the next nativeReadPacket, which owns -// a bound input proxy and can defer through the loader; running -// avformat_seek_file here would read against a dead input. -JNIEXPORT void JNICALL -Java_com_edde746_plezy_exoplayer_FfmpegDemuxerJni_nativeSeekTo(JNIEnv* env, jobject thiz, jlong timeUs) { - StateLock lock; - DemuxState* s = gState; - if (s == nullptr || !s->opened) return; - s->pendingTimeSeekUs = timeUs; - s->seekAttempts = 0; // Any packet parked for a CODE_GROW retry belongs to the pre-seek stream. s->packetPending = false; + if (s->bsf != nullptr) { + for (unsigned i = 0; i < s->nbStreams; i++) { + if (s->bsf[i] != nullptr) av_bsf_flush(s->bsf[i]); + } + } + const int64_t ts = timeUs + s->startTimeUs; + const int err = avformat_seek_file(s->format, -1, INT64_MIN, ts, ts, 0); + if (s->javaError) return ERR_JAVA; + if (err < 0) { + LOGW("seek to %lld failed: %d", (long long)ts, err); + return err; + } + LOGD("seek to %lld: logical=%lld", (long long)ts, (long long)s->logicalPos); + return 0; } } // extern "C" diff --git a/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/ExoPlayerCore.kt b/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/ExoPlayerCore.kt index d2ff045cf..f25b2ecd8 100644 --- a/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/ExoPlayerCore.kt +++ b/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/ExoPlayerCore.kt @@ -746,6 +746,11 @@ class ExoPlayerCore(private val activity: Activity) : } httpDataSourceFactory = httpFactory dataSourceFactory = DefaultDataSource.Factory(activity, httpFactory) + // libavformat owns its own seeking, so the demuxer reads through a + // random-access source (FfmpegRandomAccessSource). Recording the DataSpec + // media3 opens lets it clone that spec — same URI, cache key, flags and + // request headers — instead of re-deriving one. + val recordingFactory = RecordingDataSourceFactory(dataSourceFactory!!) val extractorsFactory = DefaultExtractorsFactory() // High-bitrate Plex DVR MPEG-TS recordings can have sparse PCR packets; the default // 600-packet window may leave duration unknown and seeking disabled. @@ -773,55 +778,73 @@ class ExoPlayerCore(private val activity: Activity) : // Reads this.dvMode and this.demuxerPreference each time (not captured) so // DV7→8.1 retry and demuxer-mode changes can reload without // reinitializing the player. - val wrappedExtractorsFactory = androidx.media3.extractor.ExtractorsFactory { - val currentDvMode = this.dvMode - val doviEnabled = currentDvMode != DvConversionMode.DISABLED - val currentDemuxerPreference = this.demuxerPreference - Log.i(TAG, "[init] extractors: demuxer=${currentDemuxerPreference.wireName}") - // media3 invokes the factory once per player session and reuses the - // extractor instances for every item, so the sniff reads the live - // preference instead of a captured one. - val liveDemuxerPreference = { this@ExoPlayerCore.demuxerPreference } - val ffmpegFirst = - listOfNotNull( - FfmpegExtractor.create( - liveDemuxerPreference, - currentDvMode, - subtitleParserFactory, - handler - )?.also { activeFfmpegExtractor = it } + val wrappedExtractorsFactory = object : androidx.media3.extractor.ExtractorsFactory { + // media3's progressive path always supplies the item URI. Without one + // the demuxer cannot be handed random access, so FFmpeg stays out + // rather than failing later on its first index jump. + override fun createExtractors(): Array { + Log.w(TAG, "[init] extractors: no item uri, ffmpeg demuxer unavailable") + return buildExtractors(null) + } + + override fun createExtractors( + uri: Uri, + responseHeaders: Map> + ): Array = buildExtractors(uri) + + private fun buildExtractors(uri: Uri?): Array { + val currentDvMode = this@ExoPlayerCore.dvMode + val doviEnabled = currentDvMode != DvConversionMode.DISABLED + val currentDemuxerPreference = this@ExoPlayerCore.demuxerPreference + Log.i(TAG, "[init] extractors: demuxer=${currentDemuxerPreference.wireName}") + // media3 invokes the factory once per item and reuses the extractor + // instances, so the sniff reads the live preference instead of a + // captured one. + val liveDemuxerPreference = { this@ExoPlayerCore.demuxerPreference } + val ffmpegFirst = listOfNotNull( + uri?.let { itemUri -> + val io = FfmpegRandomAccessSource(dataSourceFactory!!) { recordingFactory.specFor(itemUri) } + FfmpegExtractor.create( + liveDemuxerPreference, + currentDvMode, + subtitleParserFactory, + handler, + io + )?.also { activeFfmpegExtractor = it } + } ) - ( - ffmpegFirst + extractorsFactory.createExtractors().map { extractor -> - when { - extractor is MatroskaExtractor -> { - val withTextPipeline = OutputWrappingExtractor(extractor) { out -> - AssSubtitleExtractorOutput(SubtitleTranscodingExtractorOutput(out, subtitleParserFactory), handler) + return ( + ffmpegFirst + extractorsFactory.createExtractors().map { extractor -> + when { + extractor is MatroskaExtractor -> { + val withTextPipeline = OutputWrappingExtractor(extractor) { out -> + AssSubtitleExtractorOutput(SubtitleTranscodingExtractorOutput(out, subtitleParserFactory), handler) + } + if (doviEnabled) { + DoviExtractorWrapper(withTextPipeline, currentDvMode) { level, prefix, message -> + emitLog(level, prefix, message) + }.also { + activeDoviMkvWrapper = it + } + } else { + withTextPipeline + } } - if (doviEnabled) { - DoviExtractorWrapper(withTextPipeline, currentDvMode) { level, prefix, message -> + doviEnabled && (extractor is Mp4Extractor || extractor is FragmentedMp4Extractor) -> { + DoviExtractorWrapper(extractor, currentDvMode) { level, prefix, message -> emitLog(level, prefix, message) }.also { - activeDoviMkvWrapper = it + activeDoviMp4Wrapper = it } - } else { - withTextPipeline } + else -> extractor } - doviEnabled && (extractor is Mp4Extractor || extractor is FragmentedMp4Extractor) -> { - DoviExtractorWrapper(extractor, currentDvMode) { level, prefix, message -> - emitLog(level, prefix, message) - }.also { - activeDoviMp4Wrapper = it - } - } - else -> extractor } - } - ).toTypedArray() + ).toTypedArray() + } } - val mediaSourceFactory = DefaultMediaSourceFactory(dataSourceFactory!!, wrappedExtractorsFactory) + val mediaSourceFactory = DefaultMediaSourceFactory(recordingFactory, wrappedExtractorsFactory) .setSubtitleParserFactory(subtitleParserFactory) playbackMediaSourceFactory = mediaSourceFactory diff --git a/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/FfmpegDemuxerJni.kt b/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/FfmpegDemuxerJni.kt index 86423bf0b..db63ad9bf 100644 --- a/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/FfmpegDemuxerJni.kt +++ b/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/FfmpegDemuxerJni.kt @@ -15,25 +15,32 @@ import androidx.media3.common.util.UnstableApi */ @OptIn(UnstableApi::class) internal object FfmpegDemuxerJni { - /** Re-entry point for the AVIO callbacks while a native call is in flight. */ + /** Byte source the AVIO callbacks read through while a native call is in flight. */ interface Input { - /** Current byte position of the underlying [androidx.media3.extractor.ExtractorInput]. */ + /** Current byte position of the loader's [androidx.media3.extractor.ExtractorInput]. */ fun position(): Long /** - * Reads up to [length] bytes into [buf]. Returns the number of bytes read, - * `0` at end of input, or `-1` after storing the failure in [lastError]. + * Reads up to [length] bytes from the loader's input at its current + * position. Returns the number of bytes read, `0` at end of input, or `-1` + * after storing the failure in [lastError]. */ fun read(buf: ByteArray, length: Int): Int + /** + * Reads up to [length] bytes at absolute [position], wherever the loader + * stands. Same return contract as [read]. This is what lets libavformat + * seek its own index without the shim unwinding the in-flight call. + */ + fun readAt(position: Long, buf: ByteArray, length: Int): Int + /** Total input length in bytes, or `-1` when unknown. */ fun length(): Long var lastError: String? } - // readPacket out[] indices. Deferral targets travel through - // [nativeConsumePendingSeek], not this array. + // readPacket out[] indices. const val OUT_CODE = 0 const val OUT_STREAM_INDEX = 1 const val OUT_PTS_US = 2 @@ -46,8 +53,7 @@ internal object FfmpegDemuxerJni { // Result codes written to out[OUT_CODE]; negative values are raw AVERRORs. const val CODE_PACKET = 0 const val CODE_EOF = 1 - const val CODE_NEED_SEEK = 2 - const val CODE_GROW = 3 + const val CODE_GROW = 2 const val ERR_JAVA = -102 // streamInfo long[] layout. @@ -81,10 +87,10 @@ internal object FfmpegDemuxerJni { external fun nativeProbeFormat(header: ByteArray): String? /** - * Opens (and resumes opening) the demuxer against [input]. Returns 0 on - * success, [CODE_NEED_SEEK] when the loader must move the input first, or a - * negative AVERROR. The stream count is read separately via - * [nativeStreamCount]; it must not ride the same namespace as result codes. + * Opens the demuxer against [input]. Returns 0 on success or a negative + * AVERROR ([ERR_JAVA] when the input itself failed). The stream count is read + * separately via [nativeStreamCount]; it must not ride the same namespace as + * result codes. */ external fun nativeOpen(input: Input): Int @@ -120,19 +126,14 @@ internal object FfmpegDemuxerJni { */ external fun nativeReadPacket(buffer: ByteArray, out: LongArray): Int - /** Returns the recorded seek target, or [Long.MIN_VALUE] when none. */ - external fun nativeConsumePendingSeek(): Long - - /** Adopts [position] as the AVIO position; the input must already be there. */ - external fun nativeResumeAfterSeek(position: Long): Int + /** Byte position libavformat will read next, or -1 when not open. */ + external fun nativeLogicalPosition(): Long /** - * Records a presentation-time seek (media3 timeline microseconds). The next - * [nativeReadPacket] executes it via avformat_seek_file, deferring through - * the loader as needed; the demuxer picks the byte position itself. + * Seeks to [timeUs] (media3 timeline microseconds, i.e. start-time + * normalized). The demuxer resolves the byte position itself through its own + * index. Returns 0 on success, or a negative AVERROR when the container has + * no usable index and libavformat refused the seek. */ - external fun nativeSeekTo(timeUs: Long) - - /** Drops cached header blocks; called when the extractor binds a new source. */ - external fun nativeResetCache() + external fun nativeSeek(timeUs: Long): Int } diff --git a/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/FfmpegExtractor.kt b/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/FfmpegExtractor.kt index f512cb5bc..76e384929 100644 --- a/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/FfmpegExtractor.kt +++ b/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/FfmpegExtractor.kt @@ -36,10 +36,10 @@ import java.io.IOException * composed in [init] then feeds libass dialogue and transcodes cues the same * way the media3 Matroska path does. * - * Seeking protocol: see `ffmpeg_demuxer_jni.cc`. The extractor never moves the - * input itself; position divergences abort the native call, and Kotlin either - * returns RESULT_SEEK or resumes in place when the loader already positioned - * the input at the requested byte. + * Seeking: libavformat resolves seeks itself, reading its own index through + * [FfmpegRandomAccessSource]. media3's byte hints are ignored, nothing is ever + * unwound, and the loader is merely nudged to follow along so that sample + * delivery keeps streaming on its connection (see [alignLoader]). * * The instance sits before media3's extractors and sniffs everything FFmpeg * can probe while [FfmpegDemuxerPolicy] enables it; media3's list behind it @@ -50,28 +50,24 @@ internal class FfmpegExtractor private constructor( private val preferenceSupplier: () -> FfmpegDemuxerPolicy.Preference, private val dvMode: DvConversionMode, private val subtitleParserFactory: SubtitleParser.Factory, - private val assHandler: AssHandler + private val assHandler: AssHandler, + private val io: FfmpegRandomAccessSource ) : Extractor { companion object { private const val TAG = "FfmpegExtractor" private const val SNIFF_BYTES = 64 * 1024 - private const val MAX_RECONCILES = 24 - - // Loader round trips (RESULT_SEEK) allowed for one item's whole open - // phase; matroska needs ~3, avi with an end-of-file index ~4. - private const val MAX_OPEN_LOADER_ROUND_TRIPS = 96 private const val INITIAL_PACKET_BUFFER_BYTES = 2 * 1024 * 1024 - private const val AUDIO_INDEX_INTERVAL_US = 500_000L /** Null when the native library is unavailable. */ fun create( preferenceSupplier: () -> FfmpegDemuxerPolicy.Preference, dvMode: DvConversionMode, subtitleParserFactory: SubtitleParser.Factory, - assHandler: AssHandler + assHandler: AssHandler, + io: FfmpegRandomAccessSource ): FfmpegExtractor? = if (FfmpegDemuxerJni.available) { - FfmpegExtractor(preferenceSupplier, dvMode, subtitleParserFactory, assHandler) + FfmpegExtractor(preferenceSupplier, dvMode, subtitleParserFactory, assHandler, io) } else { null } @@ -94,10 +90,23 @@ internal class FfmpegExtractor private constructor( } } + override fun readAt(position: Long, buf: ByteArray, length: Int): Int { + lastError = null + return try { + io.readAt(position, buf, length) + } catch (e: IOException) { + lastError = e.message ?: "demuxer random access read failed" + -1 + } + } + override fun length(): Long { - val input = currentInput ?: return -1L - val length = input.length - return if (length == C.LENGTH_UNSET.toLong()) -1L else length + val fromLoader = currentInput?.length ?: C.LENGTH_UNSET.toLong() + if (fromLoader != C.LENGTH_UNSET.toLong()) return fromLoader + // Length-less loader open (progressive live stream): fall back to + // whatever a random-access open already resolved. + val resolved = io.length() + return if (resolved == C.LENGTH_UNSET.toLong()) -1L else resolved } override var lastError: String? = null @@ -107,8 +116,6 @@ internal class FfmpegExtractor private constructor( private var doviOutputWrapper: DoviExtractorOutputWrapper? = null private var durationUs = C.TIME_UNSET private var trackOutputs: Array = emptyArray() - private var primaryStreamIndex = -1 - private var primaryStreamIsAudio = false private var packetBytes = ByteArray(INITIAL_PACKET_BUFFER_BYTES) private val packetParsable = ParsableByteArray() private val packetOut = LongArray(FfmpegDemuxerJni.OUT_LENGTH) @@ -122,12 +129,11 @@ internal class FfmpegExtractor private constructor( // the loader thread, read by ExoPlayerCore.getStats on another thread. @Volatile private var bitrateMeters: Array = emptyArray() - // Seek index for the primary stream: (presentation time, input position) of - // video keyframes or periodic audio packets. The SeekMap reads it on the - // playback thread while the loader thread appends, hence the lock. - private val seekIndexTimes = ArrayList() - private val seekIndexPositions = ArrayList() - private val seekIndexLock = Any() + // Set by seek(), applied by the next read(): media3 calls both on the loader + // thread, but only read() is allowed to throw IOException and the demuxer's + // seek reads its index. + private var pendingSeekUs = C.TIME_UNSET + private var deliveredAnyPacket = false private val sniffScratch = ByteArray(SNIFF_BYTES) @@ -167,9 +173,8 @@ internal class FfmpegExtractor private constructor( override fun init(output: ExtractorOutput) { // media3 reuses extractor instances across sources; drop the previous - // item's header cache so this open cannot replay stale bytes. - FfmpegDemuxerJni.nativeResetCache() - openLoaderRoundTrips = 0 + // item's demuxer and byte source so this open starts clean. + releaseDemuxer() // Same composition as the MP4/MKV paths: DoviConvertingTrackOutput // inspects each video track's codecs string and passes non-DV through. val doviWrapped = if (dvMode != DvConversionMode.DISABLED) { @@ -202,8 +207,16 @@ internal class FfmpegExtractor private constructor( override fun read(input: ExtractorInput, seekPosition: PositionHolder): Int { currentInput = input try { - if (!opened && !openStreams(input, seekPosition)) return Extractor.RESULT_SEEK - return readPacket(input, seekPosition) + if (!opened) openStreams() + applyPendingSeek() + alignLoader(input)?.let { position -> + seekPosition.position = position + return Extractor.RESULT_SEEK + } + // Aligned: the loader serves the samples, so nothing needs a second + // handle open until the demuxer jumps again. + if (io.isOpen) io.close() + return readPacket() } catch (e: Throwable) { Log.w(TAG, "read threw at input=${input.position}", e) throw e @@ -213,28 +226,72 @@ internal class FfmpegExtractor private constructor( } /** - * Hands the presentation-time target to the demuxer: the next read runs - * avformat_seek_file, which picks the byte position and resets libavformat's - * parse state (jumping the AVIO position under the demuxer would leave - * demuxer-private state pointing into the old stream). The byte position - * media3 supplies is only its SeekMap guess; the loader converges on the - * demuxer's choice through the normal deferral round trip. The DV - * converter's buffered NAL state resets here as well. + * Byte position the loader should move to, or null when it already stands + * where libavformat reads. + * + * Reads outside the loader's window are served by [io] regardless, so this + * is an optimization, never a correctness requirement: it keeps sample + * delivery on media3's own connection, which is what drives its byte + * accounting, back-pressure (ProgressiveMediaPeriod watches the input + * position) and load-error policy. Nothing here needs a retry budget. + */ + private fun alignLoader(input: ExtractorInput): Long? { + val demuxerPosition = FfmpegDemuxerJni.nativeLogicalPosition() + if (demuxerPosition < 0 || demuxerPosition == input.position) return null + val length = input.length + // Opening a range at or past the end is a failed request, not an + // alignment; let the read report end of input instead. + if (length != C.LENGTH_UNSET.toLong() && demuxerPosition >= length) return null + return demuxerPosition + } + + /** + * Records the target; [read] performs it. media3's byte position is its + * SeekMap guess and is deliberately ignored — libavformat picks the byte + * position from its own index. */ override fun seek(position: Long, timeUs: Long) { - Log.i(TAG, "extractor seek position=$position timeUs=$timeUs opened=$opened") + Log.i(TAG, "extractor seek timeUs=$timeUs opened=$opened") doviOutputWrapper?.resetTracks() // Buffered LOAS bytes belong to the pre-seek stream; the StreamMuxConfig // survives so audio resumes without waiting for the next config. for (latm in latmOutputs) latm.reset() - if (opened) FfmpegDemuxerJni.nativeSeekTo(timeUs) + pendingSeekUs = timeUs + } + + private fun applyPendingSeek() { + val target = pendingSeekUs + if (target == C.TIME_UNSET) return + pendingSeekUs = C.TIME_UNSET + // media3 issues seek(0) before the first read of every item; a demuxer + // that has not delivered a packet yet is already at the start, and + // seeking would throw away find_stream_info's buffered packets and + // re-read the header region. + if (target == 0L && !deliveredAnyPacket) return + val code = FfmpegDemuxerJni.nativeSeek(target) + if (code == FfmpegDemuxerJni.ERR_JAVA) { + throw IOException(inputProxy.lastError ?: "demuxer input failed") + } + if (code != 0) { + // No usable index in this container: libavformat keeps delivering from + // where it stands and the renderers decode-discard toward the target, + // which is what media3's own extractors do for a cueless file. + Log.w(TAG, "demuxer refused seek to ${target}us: $code") + } } override fun release() { + releaseDemuxer() + } + + private fun releaseDemuxer() { if (opened) { opened = false FfmpegDemuxerJni.nativeClose() } + io.close() + pendingSeekUs = C.TIME_UNSET + deliveredAnyPacket = false // media3's extractors may demux a later item while this instance keeps // the previous one's stream layout; a stale measurement must not leak // into stats. @@ -252,49 +309,15 @@ internal class FfmpegExtractor private constructor( return bitrateMeters.getOrNull(index)?.bitrateBps() } - // The in-call `reconciles` counter resets on every read() invocation, so it - // cannot bound RESULT_SEEK ping-pong across loader restarts. This budget - // spans the whole open of one item (reset in init) and turns a - // non-converging header phase into a clean fallback instead of a hang. - private var openLoaderRoundTrips = 0 - - /** Returns false when a loader seek was requested via [seekPosition]. */ - private fun openStreams(input: ExtractorInput, seekPosition: PositionHolder): Boolean { - var reconciles = 0 - while (true) { - when (val code = FfmpegDemuxerJni.nativeOpen(inputProxy)) { - 0 -> { - prepareTracks() - opened = true - Log.i(TAG, "ffmpeg demuxer ready: ${trackOutputs.count { it != null }} tracks") - return true - } - FfmpegDemuxerJni.CODE_NEED_SEEK -> { - val target = FfmpegDemuxerJni.nativeConsumePendingSeek() - Log.i(TAG, "open: NEED_SEEK target=$target inputPos=${input.position}") - if (target == Long.MIN_VALUE || target < 0) { - // Sticky AVIO error replayed without a fresh deferral: re-sync at - // the loader's current position and continue (bounded below). - if (++reconciles > MAX_RECONCILES) { - throw malformed("demuxer produced an invalid seek target: $target") - } - if (FfmpegDemuxerJni.nativeResumeAfterSeek(input.position) != 0) { - throw malformed("demuxer resync failed at ${input.position}") - } - continue - } - if (++reconciles > MAX_RECONCILES) throw malformed("too many demuxer repositions") - if (!reconcile(input, seekPosition, target)) { - if (++openLoaderRoundTrips > MAX_OPEN_LOADER_ROUND_TRIPS) { - throw malformed("demuxer open did not converge after $openLoaderRoundTrips loader seeks") - } - return false - } - } - FfmpegDemuxerJni.ERR_JAVA -> - throw IOException(inputProxy.lastError ?: "demuxer input failed") - else -> throw malformed("ffmpeg demuxer open failed: $code") + private fun openStreams() { + when (val code = FfmpegDemuxerJni.nativeOpen(inputProxy)) { + 0 -> { + prepareTracks() + opened = true + Log.i(TAG, "ffmpeg demuxer ready: ${trackOutputs.count { it != null }} tracks") } + FfmpegDemuxerJni.ERR_JAVA -> throw IOException(inputProxy.lastError ?: "demuxer input failed") + else -> throw malformed("ffmpeg demuxer open failed: $code") } } @@ -435,12 +458,6 @@ internal class FfmpegExtractor private constructor( trackOutputs[index] = trackOutput } - primaryStreamIndex = if (primaryVideo >= 0) primaryVideo else primaryAudio - primaryStreamIsAudio = primaryVideo < 0 && primaryAudio >= 0 - synchronized(seekIndexLock) { - seekIndexTimes.clear() - seekIndexPositions.clear() - } output.endTracks() output.seekMap(SeekMapImpl()) } @@ -468,8 +485,7 @@ internal class FfmpegExtractor private constructor( } } - private fun readPacket(input: ExtractorInput, seekPosition: PositionHolder): Int { - var reconciles = 0 + private fun readPacket(): Int { while (true) { val code = FfmpegDemuxerJni.nativeReadPacket(packetBytes, packetOut) when (code) { @@ -521,24 +537,11 @@ internal class FfmpegExtractor private constructor( /* cryptoData= */ null ) - recordSeekPoint(streamIndex, ptsUs, packetOut[FfmpegDemuxerJni.OUT_POSITION], isKeyframe) + deliveredAnyPacket = true bitrateMeters.getOrNull(streamIndex)?.onPacket(ptsUs, size) return Extractor.RESULT_CONTINUE } FfmpegDemuxerJni.CODE_EOF -> return Extractor.RESULT_END_OF_INPUT - FfmpegDemuxerJni.CODE_NEED_SEEK -> { - val target = FfmpegDemuxerJni.nativeConsumePendingSeek() - if (target == Long.MIN_VALUE || target < 0) { - // Sticky AVIO error replayed without a fresh deferral: resync in - // place (bounded by reconciles) instead of killing the stream. - if (++reconciles > MAX_RECONCILES || !reSync(input)) { - throw malformed("demuxer produced an invalid seek target: $target") - } - continue - } - if (++reconciles > MAX_RECONCILES) throw malformed("too many demuxer repositions") - if (!reconcile(input, seekPosition, target)) return Extractor.RESULT_SEEK - } FfmpegDemuxerJni.CODE_GROW -> { val required = packetOut[FfmpegDemuxerJni.OUT_SIZE].toInt() packetBytes = ByteArray(required + 64 * 1024) @@ -550,81 +553,22 @@ internal class FfmpegExtractor private constructor( } } - /** Re-establishes the AVIO position at the loader's current input position. */ - private fun reSync(input: ExtractorInput): Boolean { - val position = input.position - return position >= 0 && FfmpegDemuxerJni.nativeResumeAfterSeek(position) == 0 - } - - /** Returns true when the input is already at [target] and native resumed. */ - private fun reconcile(input: ExtractorInput, seekPosition: PositionHolder, target: Long): Boolean { - if (target == input.position) { - val code = FfmpegDemuxerJni.nativeResumeAfterSeek(target) - if (code != 0) throw malformed("demuxer resume failed: $code") - return true - } - seekPosition.position = target - return false - } - - private fun recordSeekPoint(streamIndex: Int, timeUs: Long, position: Long, keyframe: Boolean) { - if (streamIndex != primaryStreamIndex || position <= 0) return - if (!primaryStreamIsAudio && !keyframe) return - synchronized(seekIndexLock) { - val lastIndex = seekIndexTimes.size - 1 - if (lastIndex >= 0) { - if (timeUs <= seekIndexTimes[lastIndex]) return - if (primaryStreamIsAudio && timeUs - seekIndexTimes[lastIndex] < AUDIO_INDEX_INTERVAL_US) { - return - } - } - seekIndexTimes.add(timeUs) - seekIndexPositions.add(position) - } - } - private fun malformed(message: String): ParserException = ParserException.createForMalformedContainer(message, null) private inner class SeekMapImpl : SeekMap { - // Seeks are executed by the demuxer (avformat_seek_file), so seekability - // does not depend on the sample-derived index below — that index only - // improves the loader's initial byte guess. The timeline is published - // from this value once, at prepare time, when the index is still empty. override fun isSeekable(): Boolean = this@FfmpegExtractor.durationUs != C.TIME_UNSET - override fun getSeekPoints(timeUs: Long): SeekMap.SeekPoints { - synchronized(seekIndexLock) { - if (seekIndexTimes.isEmpty()) return SeekMap.SeekPoints(SeekPoint(0, 0)) - val index = seekIndexTimes.binarySearchFloor(timeUs) - if (index < 0) return SeekMap.SeekPoints(SeekPoint(seekIndexTimes[0], seekIndexPositions[0])) - val preceding = SeekPoint(seekIndexTimes[index], seekIndexPositions[index]) - if (index + 1 >= seekIndexTimes.size || seekIndexTimes[index] == timeUs) { - return SeekMap.SeekPoints(preceding) - } - val following = SeekPoint(seekIndexTimes[index + 1], seekIndexPositions[index + 1]) - return SeekMap.SeekPoints(preceding, following) - } - } + /** + * The demuxer owns seek resolution: it reads its own index and lands on + * the right keyframe by itself, so there is no second index here to keep + * consistent. The byte position exists only to satisfy the interface — + * [alignLoader] moves the loader to wherever libavformat actually ended + * up, on the read that follows the seek. + */ + override fun getSeekPoints(timeUs: Long): SeekMap.SeekPoints = SeekMap.SeekPoints(SeekPoint(timeUs, 0)) // Qualified on purpose: bare `durationUs` binds to the inherited Java // getter as a synthetic property (this.getDurationUs()) and recurses. override fun getDurationUs(): Long = this@FfmpegExtractor.durationUs } } - -/** Floor binary search over an ascending list; returns the greatest index with value <= target. */ -private fun List.binarySearchFloor(value: Long): Int { - var low = 0 - var high = size - 1 - var result = -1 - while (low <= high) { - val mid = (low + high) ushr 1 - if (this[mid] <= value) { - result = mid - low = mid + 1 - } else { - high = mid - 1 - } - } - return result -} diff --git a/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/FfmpegRandomAccessSource.kt b/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/FfmpegRandomAccessSource.kt new file mode 100644 index 000000000..2a7227ef0 --- /dev/null +++ b/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/FfmpegRandomAccessSource.kt @@ -0,0 +1,167 @@ +package com.edde746.plezy.exoplayer + +import android.net.Uri +import androidx.annotation.OptIn +import androidx.media3.common.C +import androidx.media3.common.util.UnstableApi +import androidx.media3.datasource.DataSource +import androidx.media3.datasource.DataSpec +import androidx.media3.datasource.TransferListener +import java.io.IOException + +/** + * Random-access byte source for the libavformat demuxer. + * + * libavformat demuxers own their own IO: they jump to the end of the file for an + * index (matroska Cues, MP4 moov, AVI idx1), back to the header, and binary + * search (MPEG-PS) whenever they please. media3's [ + * androidx.media3.extractor.ExtractorInput] is forward-only — an extractor may + * only ask the loader to re-open elsewhere and be invoked again — so serving + * libavformat from the loader alone means every backward jump has to unwind the + * in-flight libavformat call and replay it, which is what the demuxer shim used + * to do (and what made a matroska Cues read unrecoverable, #2096). + * + * This class removes the mismatch instead of working around it: reads outside + * the loader's window open a second [DataSource] from the same factory media3 + * uses, positioned exactly where libavformat asked. Cronet, download caches, + * SAF and the item's own request headers therefore still apply, because the + * [DataSpec] is cloned from the one media3 opened for this item. + * + * Sequential sample delivery deliberately does not come through here: it stays + * on the loader's connection so media3 keeps its byte accounting, back-pressure + * and load-error policy (see [FfmpegExtractor]). This source is opened for + * header/index bursts and closed again as soon as the loader is back in step. + */ +@OptIn(UnstableApi::class) +internal class FfmpegRandomAccessSource( + private val factory: DataSource.Factory, + private val specProvider: () -> DataSpec? +) { + private var source: DataSource? = null + + /** Absolute position the open handle returns next; [C.INDEX_UNSET] when closed. */ + private var handlePosition = C.INDEX_UNSET.toLong() + + private var resolvedLength = C.LENGTH_UNSET.toLong() + + private var opens = 0 + + /** Handles opened so far; the tests assert an index seek does not walk the file. */ + val openCount: Int + get() = synchronized(this) { opens } + + val isOpen: Boolean + get() = synchronized(this) { source != null } + + /** + * Reads up to [length] bytes at absolute [position]. Returns the number of + * bytes read, or 0 at end of input. Reopens the underlying source whenever + * the requested position is not where the current handle stands, which is the + * only way to move backwards on a byte stream. + */ + fun readAt(position: Long, buffer: ByteArray, length: Int): Int = synchronized(this) { + if (length <= 0) return 0 + val handle = handleAt(position) + val read = handle.read(buffer, 0, length) + if (read == C.RESULT_END_OF_INPUT) return 0 + handlePosition = position + read + read + } + + /** + * Total resource length once some open has resolved it, else + * [C.LENGTH_UNSET]. Never performs IO of its own: the extractor prefers the + * length media3 already resolved for the loader. + */ + fun length(): Long = synchronized(this) { resolvedLength } + + fun close() = synchronized(this) { + closeHandle() + handlePosition = C.INDEX_UNSET.toLong() + } + + private fun handleAt(position: Long): DataSource { + val existing = source + if (existing != null && handlePosition == position) return existing + closeHandle() + val spec = specProvider() ?: throw IOException("ffmpeg demuxer has no data spec for random access") + // Clone rather than subrange: the recorded spec carries the loader's own + // position, and subrange() is relative to it. + val positioned = spec.buildUpon() + .setPosition(position) + .setLength(C.LENGTH_UNSET.toLong()) + .build() + val created = factory.createDataSource() + val remaining = try { + created.open(positioned) + } catch (e: Throwable) { + closeQuietly(created) + throw e + } + source = created + handlePosition = position + opens++ + if (remaining != C.LENGTH_UNSET.toLong()) resolvedLength = position + remaining + return created + } + + private fun closeHandle() { + source?.let { closeQuietly(it) } + source = null + } + + private fun closeQuietly(dataSource: DataSource) { + try { + dataSource.close() + } catch (_: IOException) { + // Closing a handle we are abandoning cannot fail the read that replaces it. + } + } +} + +/** + * [DataSource.Factory] that remembers the [DataSpec] media3 opened for each + * item, so [FfmpegRandomAccessSource] can clone it — same URI, cache key, flags + * and request headers — instead of re-deriving one and silently losing, say, a + * download's cache key. + * + * Keyed by URI because ExoPlayer prepares the next item's period while the + * current one is still loading; the demuxer must never read the next item's + * bytes. + */ +@OptIn(UnstableApi::class) +internal class RecordingDataSourceFactory(private val delegate: DataSource.Factory) : DataSource.Factory { + + private val specs = object : LinkedHashMap(8, 0.75f, false) { + override fun removeEldestEntry(eldest: MutableMap.MutableEntry): Boolean = size > MAX_TRACKED_ITEMS + } + + fun specFor(uri: Uri): DataSpec? = synchronized(specs) { specs[uri] } + + override fun createDataSource(): DataSource = RecordingDataSource(delegate.createDataSource()) + + private fun record(dataSpec: DataSpec) { + synchronized(specs) { specs[dataSpec.uri] = dataSpec } + } + + private inner class RecordingDataSource(private val inner: DataSource) : DataSource { + override fun open(dataSpec: DataSpec): Long { + record(dataSpec) + return inner.open(dataSpec) + } + + override fun read(buffer: ByteArray, offset: Int, length: Int): Int = inner.read(buffer, offset, length) + + override fun addTransferListener(transferListener: TransferListener) = inner.addTransferListener(transferListener) + + override fun getUri(): Uri? = inner.uri + + override fun getResponseHeaders(): Map> = inner.responseHeaders + + override fun close() = inner.close() + } + + private companion object { + const val MAX_TRACKED_ITEMS = 4 + } +} diff --git a/android/app/src/test/kotlin/com/edde746/plezy/exoplayer/FfmpegRandomAccessSourceTest.kt b/android/app/src/test/kotlin/com/edde746/plezy/exoplayer/FfmpegRandomAccessSourceTest.kt new file mode 100644 index 000000000..7a7cd9e0e --- /dev/null +++ b/android/app/src/test/kotlin/com/edde746/plezy/exoplayer/FfmpegRandomAccessSourceTest.kt @@ -0,0 +1,222 @@ +package com.edde746.plezy.exoplayer + +import android.net.Uri +import androidx.media3.common.C +import androidx.media3.datasource.DataSource +import androidx.media3.datasource.DataSpec +import androidx.media3.datasource.TransferListener +import java.io.IOException +import org.junit.Assert.assertArrayEquals +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertSame +import org.junit.Assert.assertThrows +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +/** + * The demuxer's correctness now rests on this source answering any absolute + * position, so these pin the contract libavformat depends on: a read lands on + * the requested byte, a backward jump reopens rather than returning the wrong + * bytes, and sequential reads do not reopen (which would turn steady-state + * playback into one request per 64 KB). + */ +@RunWith(RobolectricTestRunner::class) +class FfmpegRandomAccessSourceTest { + + private val content = ByteArray(4096) { (it % 251).toByte() } + private val uri: Uri = Uri.parse("https://example.test/movie.mkv") + + private class FakeDataSource(private val content: ByteArray) : DataSource { + var openedAt: Long = -1 + var closed = false + private var position = 0 + + override fun open(dataSpec: DataSpec): Long { + openedAt = dataSpec.position + position = dataSpec.position.toInt() + return (content.size - position).toLong() + } + + override fun read(buffer: ByteArray, offset: Int, length: Int): Int { + if (position >= content.size) return C.RESULT_END_OF_INPUT + val count = minOf(length, content.size - position) + content.copyInto(buffer, offset, position, position + count) + position += count + return count + } + + override fun addTransferListener(transferListener: TransferListener) = Unit + + override fun getUri(): Uri? = null + + override fun getResponseHeaders(): Map> = emptyMap() + + override fun close() { + closed = true + } + } + + private inner class FakeFactory : DataSource.Factory { + val created = mutableListOf() + + override fun createDataSource(): DataSource = FakeDataSource(content).also { created.add(it) } + } + + private fun sourceFor(factory: DataSource.Factory, spec: DataSpec? = DataSpec(uri)) = FfmpegRandomAccessSource(factory) { spec } + + @Test + fun readsLandOnTheRequestedAbsolutePosition() { + val factory = FakeFactory() + val source = sourceFor(factory) + val buffer = ByteArray(16) + + assertEquals(16, source.readAt(1000, buffer, 16)) + + assertArrayEquals(content.copyOfRange(1000, 1016), buffer) + assertEquals(1000L, factory.created.single().openedAt) + } + + @Test + fun sequentialReadsReuseOneHandle() { + val factory = FakeFactory() + val source = sourceFor(factory) + val buffer = ByteArray(64) + + source.readAt(0, buffer, 64) + source.readAt(64, buffer, 64) + source.readAt(128, buffer, 64) + + assertEquals(1, source.openCount) + assertArrayEquals(content.copyOfRange(128, 192), buffer) + } + + @Test + fun backwardJumpReopensAndReturnsTheRequestedBytes() { + val factory = FakeFactory() + val source = sourceFor(factory) + val buffer = ByteArray(32) + + source.readAt(2048, buffer, 32) + // The jump libavformat makes for an index at the end of the file, then back. + assertEquals(32, source.readAt(16, buffer, 32)) + + assertArrayEquals(content.copyOfRange(16, 48), buffer) + assertEquals(2, source.openCount) + assertTrue("abandoned handle must be closed", factory.created.first().closed) + } + + @Test + fun endOfInputReportsZeroWithoutAdvancing() { + val factory = FakeFactory() + val source = sourceFor(factory) + val buffer = ByteArray(8) + + assertEquals(0, source.readAt(content.size.toLong(), buffer, 8)) + } + + @Test + fun lengthResolvesFromAnOpenAndStaysKnown() { + val factory = FakeFactory() + val source = sourceFor(factory) + + assertEquals(C.LENGTH_UNSET.toLong(), source.length()) + source.readAt(512, ByteArray(8), 8) + + assertEquals(content.size.toLong(), source.length()) + } + + @Test + fun clonedSpecKeepsCacheKeyAndFlagsButTakesOurPosition() { + val recorded = DataSpec.Builder() + .setUri(uri) + .setPosition(9999) + .setLength(1234) + .setKey("download-cache-key") + .setFlags(DataSpec.FLAG_ALLOW_CACHE_FRAGMENTATION) + .setHttpRequestHeaders(mapOf("X-Test" to "1")) + .build() + var openedSpec: DataSpec? = null + val factory = DataSource.Factory { + object : DataSource { + override fun open(dataSpec: DataSpec): Long { + openedSpec = dataSpec + return C.LENGTH_UNSET.toLong() + } + + override fun read(buffer: ByteArray, offset: Int, length: Int): Int = C.RESULT_END_OF_INPUT + + override fun addTransferListener(transferListener: TransferListener) = Unit + + override fun getUri(): Uri? = null + + override fun getResponseHeaders(): Map> = emptyMap() + + override fun close() = Unit + } + } + + sourceFor(factory, recorded).readAt(4242, ByteArray(4), 4) + + val spec = requireNotNull(openedSpec) + assertEquals(4242L, spec.position) + assertEquals(C.LENGTH_UNSET.toLong(), spec.length) + assertEquals("download-cache-key", spec.key) + assertEquals(DataSpec.FLAG_ALLOW_CACHE_FRAGMENTATION, spec.flags) + assertEquals("1", spec.httpRequestHeaders["X-Test"]) + assertEquals(uri, spec.uri) + } + + @Test + fun readWithoutARecordedSpecFails() { + val source = sourceFor(FakeFactory(), spec = null) + + assertThrows(IOException::class.java) { source.readAt(0, ByteArray(4), 4) } + } + + @Test + fun closeReleasesTheHandle() { + val factory = FakeFactory() + val source = sourceFor(factory) + source.readAt(0, ByteArray(4), 4) + + assertTrue(source.isOpen) + source.close() + + assertFalse(source.isOpen) + assertTrue(factory.created.single().closed) + } + + @Test + fun recordingFactoryReportsTheSpecMedia3OpenedPerUri() { + val delegate = FakeFactory() + val recorder = RecordingDataSourceFactory(delegate) + val other = Uri.parse("https://example.test/next-episode.mkv") + + assertNull(recorder.specFor(uri)) + recorder.createDataSource().open(DataSpec.Builder().setUri(uri).setPosition(64).setKey("a").build()) + recorder.createDataSource().open(DataSpec.Builder().setUri(other).setPosition(128).build()) + + // Preloading the next item must not redirect the current demuxer's reads. + assertEquals(64L, recorder.specFor(uri)?.position) + assertEquals("a", recorder.specFor(uri)?.key) + assertEquals(128L, recorder.specFor(other)?.position) + } + + @Test + fun recordingFactoryDelegatesReads() { + val delegate = FakeFactory() + val recorder = RecordingDataSourceFactory(delegate) + val buffer = ByteArray(16) + + val dataSource = recorder.createDataSource() + dataSource.open(DataSpec.Builder().setUri(uri).setPosition(32).build()) + assertEquals(16, dataSource.read(buffer, 0, 16)) + + assertArrayEquals(content.copyOfRange(32, 48), buffer) + assertSame(delegate.created.single(), delegate.created.first()) + } +}