Synchronously null surface fields in dispose() before deferred view
removal, add session generation counter to cancel stale MPV fallbacks,
and use postAtFrontOfQueue for defense-in-depth message ordering.
- Lower OkHttp readTimeout from 30s to 10s so stalled Range requests retry faster
- Reduce bufferForPlaybackMs from 2500 to 1000 for quicker playback start
- Add HTTP transfer timing logs (TTFB, bytes, duration) to diagnose startup delays
- ExoPlayerPlugin: move all MPV JNI calls off main thread via runOnExecutor
- WatchNextPlugin: run ContentProvider ops on IO executor with error handling
- MpvPlayerCore: guard executor methods against dispose race (RejectedExecutionException)
- MainActivity: fix PiP null engine NPE with safe flutterEngine?.let
Catch COMMAND_FAILED/NOT_INITIALIZED in PlayerNative.seek() and
PlayerAndroid.seek() directly, preventing unhandled PlatformException
crashes on macOS app resume. Remove the ~200-line
_PendingSeekCoordinator system and revert all callsites to direct
player.seek(clampSeekPosition(...)). Keep seekable property/stream
and media controls gating. Add ExoPlayer seekable emission.
Scale raw AudioTrack position by playback speed to convert from real
time to media time. Captures reference points on speed changes for
correct mid-playback transitions.
Fixes video stutter on files with 120ms Opus audio frames (e.g. Amlogic
S905X4). DefaultAudioSink clamps getCurrentPositionUs to writtenDuration
which lags between bursts, creating a sawtooth pattern that drops video
frames. PositionFixAudioSink bypasses the clamp using the raw AudioTrack
position when the delegate falls behind.
- Detect decoder hangs via AnalyticsListener (onVideoDecoderInitialized
→ onRenderedFirstFrame gap, confirmed by DecoderCounters)
- Detect deselected video tracks and trigger immediate MPV fallback
- Use reinhard tone-mapping on GPUs without compute shaders
Prevents ANR when Dio's responseDecoder synchronously decodes large API
responses on the main thread. Responses >50KB use compute(). Also caches
MediaCodecList query results in ExoPlayerCore companion object.
ExoPlayerCore emitLog() forwards native diagnostics (init, tracks,
tunneling, watchdog, errors, framerate, audio focus) to Dart via the
existing log-message event when debug logging is enabled. URIs are
redacted before forwarding. setLogLevel method channel wires into the
existing debug toggle in video_player_screen.
Logs screen now shows a device info header (app version, OS, model,
TV mode) and includes it in copy/upload output.