mohe_native_player 1.11.13 copy "mohe_native_player: ^1.11.13" to clipboard
mohe_native_player: ^1.11.13 copied to clipboard

A Flutter native video player plugin for iOS and Android with full-featured controls, HLS/DASH support, danmaku, DLNA casting, PiP, and more.

1.11.13 #

Follow-up to the iOS fallback work in 1.11.12; two gaps found in review after that release.

  • fix(ios): apply the asset-stage fallback on the resource-loader flow as well. 1.11.12 widened the standard flow to check the playable, tracks and duration errors, but the resource-loader branch was left checking only playable — and that branch is exactly where a live URL without an .m3u8 extension ends up, since those are not routed through the proxy. If -1002 surfaced on tracks or duration there, the fallback did not fire and the stream fell back to the 15-second detector, i.e. the long black screen the previous release set out to remove. Both branches now share one predicate, which also removes the possibility of updating one and forgetting the other. It returns the error that actually matched rather than a boolean, so the fallback log no longer prints (null)/0 when the trigger came from tracks or duration.
  • fix(ios): do not re-arm the load-failure detector during ad playback. The first-frame budget is deliberately not armed for pre-roll ads, but the re-arm added in 1.11.12 ran unconditionally on every VLC fallback, so an ad that fell back and then took more than 15 seconds to render would surface a -2001 load-timeout to the host mid-ad.

1.11.12 #

Both fixes in this release target the same root cause: some TV live sources emit #EXTINF:3E+00, — a duration in scientific notation, which RFC 8216 §4.3.2.1 does not allow (it requires decimal-floating-point or decimal-integer). Each platform failed differently, so the fixes differ; see the note at the end.

  • fix(android): accept playlists whose EXTINF duration is written in scientific notation. media3 1.2.1 reads the duration with #EXTINF:([\d\.]+)\b, which cannot match 3E+00, so the parser threw ParserException("Couldn't match ...") and the whole playlist was discarded — these channels simply would not play. Double.parseDouble("3E+00") handles the value fine; only that regex was the obstacle. A subtler variant is also covered: for #EXTINF:1.5E+01, (15s) the regex does not fail — it backtracks and matches 1., silently treating a 15-second segment as 1 second. The new HlsPlaylistSanitizer rewrites such durations to an equivalent plain-decimal form and is wired in through media3's official setPlaylistParserFactory extension point, so the sanitized text is still parsed by DefaultHlsPlaylistParserFactory and stays in step with future media3 releases. Well-formed playlists pass through byte-for-byte unchanged (the rewrite only applies when BigDecimal's plain-string form differs from the original text). Values that are negative, exceed 86400s, or carry more than 9 fractional digits are left untouched for media3 to reject, rather than fabricating data; that magnitude gate is also a safety measure, since BigDecimal happily constructs 1E+2000000000 and toPlainString() on it raises OutOfMemoryError — an Error that a NumberFormatException catch would not stop, turning a failed start into an app crash on remote input.
  • fix(ios): stop the 8-second black screen when AVPlayer cannot play such a stream. AVPlayer reports these as -1002 unsupported URL under NSURLErrorDomain / CoreMediaErrorDomain, neither of which is AVFoundationErrorDomain, so ILookIsMediaFormatError rejected them at its first check and the failure was classified as a transport problem: report the error, hide the spinner, do not fall back. The independent readyForDisplay watchdog then rescued playback anyway, but only after its 8-second timeout — and since the spinner had already been dismissed, those 8 seconds were a plain black screen. -1002 is now treated as a format failure (it is content, not transport: the URL is one we constructed and playback had already started), so the fallback happens immediately. The whitelist is otherwise unchanged — offline, timeout, DNS, 403 and 404 still report an error without switching engines, which is what that whitelist exists to guarantee. Verified on device: fallback now fires at once, the 8s watchdog no longer triggers, and first frame arrives about 1.6s after the failure.
  • fix(ios): keep the spinner up across a VLC fallback. hideLoading ran at the top of the AVPlayerItemStatusFailed branch, before the fallback decision, so every fallback left the screen black until VLC rendered its first frame; it now runs only on the path that gives up and reports the error. The re-show inside startVLCPlayerWithURL was also gated on the host having supplied a loading GIF, which no longer reflects how loading works — PlayerUIManager falls back to the system spinner without one, and the primary start path dropped that condition long ago. Hosts that never call setLoadingGif were therefore shown nothing during every fallback.
  • fix(ios): fall back to VLC when the same failure surfaces while loading the asset's keys rather than on the player item. No player exists at that point, so neither the 8s nor the 25s watchdog is armed and only the 15s load-failure detector remained — a longer black screen than the case above. Both the standard and resource-loader flows now run the failure through the same predicate (covering the playable, tracks and duration errors and their underlying errors) and hide the spinner plus reset the detector when they do give up.
  • fix(ios): preserve the custom User-Agent when the resource-loader path falls back to VLC. pendingVLCUserAgent was only recorded in the standard flow, so fallbacks from createAVAssetAndStartPlayback sent no UA and hotlink-protected sources returned 403.
  • fix(ios): report a failed load once instead of twice. Neither the AVPlayer error path nor vlcAdapter:didFailWithError: reset the load-failure detector, so a genuine error was followed by a spurious -2001 load_timeout if the 15s window had not elapsed. Falling back to VLC also re-opens that window, so VLC is no longer judged against a budget that AVPlayer had already spent.

Note on the platform split: Android is fixed at the source (the playlist is normalised and ExoPlayer keeps decoding in hardware), whereas iOS relies on the existing VLC fallback and therefore decodes these channels in software. Normalising the playlist on iOS as well would require routing every HLS stream through the local proxy, which is a much larger change; it is not done here.

1.11.11 #

  • fix(ios): implement setNativeUIVisible:. The Flutter bridge header declared it but TestComponent.m never defined it, and PlayerPlatformView sends the message unguarded — so every PlayerViewWithControls mount raised unrecognized selector sent to instance, meaning that widget had never been usable on iOS. It was the only declared-but-unimplemented method among the bridge header's 78 declarations. The implementation is the master switch the dartdoc already described (native control bar plus both back buttons) and routes through the existing per-item setters, because setPortraitBackButtonVisible: also records _userPortraitBackButtonVisibleOverride — without that, any later setPlayerMode: re-shows the portrait back button. The control-bar flag is written synchronously first rather than through setControlBarVisible:: PlayerUIManager's setLandscapeBackButtonHidden: reads controlBarHidden, and that setter defers its write to the next main-queue turn, so on the restore path the stale read pinned the landscape top bar visible with no auto-hide scheduled.
  • fix(android): add the missing setNativeUIVisible case, which fell through to notImplemented() and raised MissingPluginException. It drives the same three switches as iOS; UIManager already had a symmetric userPortraitBackButtonVisibleOverride.
  • fix(dart): attach catchError to both fire-and-forget setNativeUIVisible calls in PlayerViewWithControls. Neither was awaited, so on any platform lacking the method the rejected future surfaced as an uncaught async exception in the host's zone, and _nativeUiHidden stayed false so the dispose-time restore never ran either.
  • fix(android): accept the setCoverImage RPC instead of raising MissingPluginException. Android has no MediaSession or media notification, so the URL is only recorded for a future implementation and nothing renders it — the dartdoc now says so per platform, since a silently-succeeding no-op is easier to misread than a thrown exception.
  • fix(ios): drop two unreachable duplicate branches (setCoverImage, restoreSystemUI) from handleMethodCall:. Both were shadowed by earlier branches in the same if/else chain, so behaviour is unchanged; the NSLog lost with the dead restoreSystemUI copy had never executed.

1.11.10 #

  • fix(ios): stop the progress bar from snapping back to its old position after a drag. seek: gained a scrub-state reset that also cleared the UI manager's isSeeking flag, but only re-set the component's own flag afterwards. During the seconds a zero-tolerance seek is in flight on slow HLS, the 0.5s periodic refresh saw the flag cleared and overwrote the slider with the stale currentTime — the slider jumped back, then jumped to the target once the seek landed. The flag is now re-set alongside the component's, matching what the ffmpeg branch already did.
  • fix(ios): register UIControlEventTouchCancel on both the portrait and landscape progress sliders. An incoming call, a system edge gesture, or the gesture arena stealing the touch delivers only that event; without a handler the isSeeking flag set on touch-down was never cleared and the progress bar stopped updating for the rest of the session. This affected the default seek-feedback mode, not just realtime scrubbing.
  • fix(ios): rework realtime scrubbing (seek-feedback mode 2) around Apple's QA1820 chase-time pattern. Dragging now only records the latest target, with at most one seek in flight and the completion handler chasing the newest target — replacing the previous throttle plus cancelPendingSeeks, which made high-frequency seeks cancel each other so that almost no frame was ever presented on slow HLS. The handler deliberately ignores finished (it is always NO when a seek is superseded, and rolling the UI back on it was the direct cause of the snap-back), invalidates callbacks from previous sources by generation, and is bounded by a 60s scrub watchdog and an 8s settle timeout. Scrubbing state is now split into isScrubbing (finger still down) and isSeeking (seek not yet settled), so loading feedback is suppressed only while the finger is down instead of for the entire wait.
  • fix(ios): widen the first-frame timeout from 1s to 8s and distinguish "cannot render" from "merely slow". 1s only covered local files and fast CDNs — one measured route needed 3.7s for a 2.7MB first segment at 1.3s TTFB — so streams AVPlayer could have decoded in hardware were misclassified as unsupported and pushed onto VLC software decoding. When the timeout fires with buffering progress, playback is now given a further grace period with the readyForDisplay observer left in place (removing it would leave the spinner up forever on exactly the slow sources this is meant to rescue) plus a 25s final fallback for sources that keep downloading but never render a video track.
  • fix(ios): only fall back to VLC on AVPlayerItemStatusFailed when the failure is a documented media-format error (AVErrorDecodeFailed, AVErrorFileFormatNotRecognized, AVErrorFailedToParse, AVErrorContentIsUnavailable, AVErrorFormatUnsupported, including the underlying error). Falling back unconditionally swept transport and server failures — a dropped connection, an expired signature, a 404 — into the "codec unsupported" path: VLC fails on the same URL and merely defers the error event until libvlc gives up, while showing no spinner during its own startup, so the user faced a silent black screen instead of an immediate failure prompt. Domain-based exclusion is not usable here because CoreMediaErrorDomain is undeclared in the public headers and mixes HTTP status codes with genuine format errors.
  • fix(ios): seek VLC by time instead of position. libvlc derives position from a byte offset, which is badly distorted on VBR or multi-segment sources — one title carrying 623 EXT-X-DISCONTINUITY markers landed at 2198s for a 1413s target, off by 785s. The adapter already read currentTime from vlcPlayer.time, so writing through the same property makes the read and write units agree and the reported seek deviation meaningful. The position fallback for non-seekable inputs was removed — VLCMediaPlayer.h states that setting either property on such inputs fails silently — and the guard now returns before the in-progress flag is set, so a live stream can no longer wedge the flag and have every later seek coalesced away.

1.11.9 #

  • fix(ios): recover encrypted playback from mid-playback time-base freezes that the watchdog previously ignored. A stall in waitingToPlayAtSpecifiedRate with reasonForWaitingToPlay == minimizeStalls used to be exempted unconditionally, so a wedged time base went unhandled until the host's 15s stall timeout surfaced a black "load failed" screen. Buffering stalls now get a longer (10s) grace period instead of a free pass, after which the player rebuilds the playback pipeline and seeks back to the current position. Verified on device: previously 5 black screens in 3.6h, now none across multi-hour runs.
  • fix(ios): eliminate a setRate feedback loop during 2x playback. When the buffer ran dry, AVPlayer normalised rate back to 1.0, the rate observer re-applied the desired rate, and AVPlayer normalised it again — measured at 2–28 setRate calls per second, amplifying through the level-triggered timeControlStatus observer into 283–505 redundant loading callbacks (and as many buffering events to the host) per single stall, plus an audio-session reactivation each time. The observer no longer fights the player while it is waiting for data, buffering state is now reported edge-triggered, and the watchdog reconciles the actual rate once per second so the user's speed is still restored — without the spin. Only non-1.0 playback rates were affected.
  • fix(ios): keep the freeze watchdog responsive by moving its diagnostic snapshot (which reads cross-process player-item properties) off the watchdog queue, and surface a warning when a tick is delayed. A blocked tick previously disabled freeze detection and recovery entirely.
  • fix(ios): reuse the decryption key when the same encrypted source is re-loaded. The key was cleared immediately after a successful start, so a reload without a freshly supplied key fell through to the plain playback path, where encrypted bytes never become ready and the host reported a first-frame timeout as a black screen. Reuse is gated on an exact source-URL match.
  • feat(ios/android): add a live player mode and an independent setLandscapeBackButtonVisible API. On iOS, live mode hides the native control chrome and keeps only the landscape fullscreen back button, re-applying that state after fullscreen transitions and layout passes so rotation no longer reopens a control bar the host explicitly closed. Android exposes the landscape back-button toggle (kept as INVISIBLE so the title keeps its position) and currently treats live as normal.

1.11.8 #

  • fix(ios): recover encrypted AVPlayer playback from persistent non-buffering freezes by rebuilding the player item and resuming at the previous position, with bounded retries and per-instance diagnostics to prevent recovery loops.

1.11.7 #

  • fix(ios): rebuild the AVPlayer render pipeline after Flutter platform views transition from 0x0 to a valid size, preventing startup/initial-seek playback from freezing on the first frame when the layer was created without a renderable canvas.
  • fix(ios): reduce false load-failure prompts near natural video end by suppressing end-of-video buffer-empty handling and resetting the load-failure detector when playback finishes.
  • fix(ios): add encrypted/Huolong playback freeze diagnostics with a wall-clock watchdog, periodic alive logs, and switch-path diagnostics while keeping the watchdog observation-only to avoid unintended playback recovery side effects.
  • perf(ios): throttle Now Playing heartbeat reloads when playback is paused or ended, so the lock-screen card commits the stop state once instead of reloading unchanged metadata every second.

1.11.6 #

  • fix(ios): align the long-press speed default in the speed-settings page with the long-press overlay, so a fresh install no longer shows "2.0X" in the overlay but "3.0X" in settings — both now resolve from the same persisted default.
  • fix(android): harden crash/ANR paths — keep HuolongTranscoder$Listener.isCancelled() from R8 renaming so cancellation stays effective (no soft-decode thread pile-up), add JNI calloc-OOM and null-context guards, short-circuit lazy-init/async callbacks after dispose to prevent zombie managers, guard every isInPictureInPictureMode() behind API 24+ to avoid NoSuchMethodError on older devices, release the previous Surface on texture re-attach to fix a leak, wrap fullscreen reparent in try-catch against "already has a parent", and correct the keep-alive onSurfaceTextureDestroyed return value.
  • fix(ios): harden crash paths — remove AVPlayerItem KVO before replaceCurrentItemWithPlayerItem:nil during teardown (fixes NSInternalInconsistencyException), wrap all enqueueSampleBuffer: calls in @try with drop-frame + flush recovery on failed/torn-down layers, and replace raw send() in LocalHLSServer with a bounded safeSend to fix partial-write truncation on large .ts/.m4s segments.

1.11.5 #

  • feat(android/ios): add customizable long-press speed boost settings with persisted preferences, a redesigned speed feedback overlay, high-speed playback handling, and animated directional chevrons for swipe feedback.
  • fix(android/ios): harden crash-prone playback entry points, ad playback parameters, HLS proxy URL/response handling, SIGPIPE handling, and resource-loader retry state under invalid input or concurrent requests.
  • fix(android/ios): improve Huolong, encrypted playback, DLNA, local HTTP, and native decode shutdown stability by guarding memory allocation, mid-stream resolution changes, socket lifecycle races, native-context use-after-free windows, and AudioTrack release races.
  • perf(ios): reduce peak memory during continuous short-drama swiping by capping AVPlayer forward buffering, lowering concurrent FFmpeg decrypt instances, and limiting Huolong decoder thread usage.

1.11.4 #

  • feat(android/ios/dart): detect load timeout (no first frame within the threshold) and mid-playback stall timeout, then stop the infinite loading spinner and report through the existing error event with a reason field (load_timeout / stall_timeout, codes -2001 / -2002) so hosts can show a "load failed / retry" prompt. New Dart API setLoadFailureDetection(firstFrameTimeoutSec, stallTimeoutSec) and PlayerLoadFailureReason constants; defaults are 15s / 15s, and a timeout of 0 disables that check. Timeout is result-driven — a cache hit that plays will not trigger it — so it stays decoupled from caching/network business; network-reachability detection is intentionally left to the host (e.g. connectivity_plus, which can turn a load_timeout into the right "no network" vs "bad source" message). Covers AVPlayer, encrypted FFmpeg, Huolong (VVC) and VLC engines on iOS, and ExoPlayer / encrypted / Huolong on Android (including the download/decrypt phase). Detection is skipped in ad mode and paused while backgrounded.

1.11.3 #

  • fix(ios): start Huolong playback intent before transcoding and add a readyForDisplay fallback so episode switches no longer stall on the first frame when EVENT HLS playlists do not reach readyToPlay.
  • fix(ios): trim old Huolong local HLS cache directories with a session LRU policy, preventing repeated episode switches from growing tmp storage until segment writes fail.

1.11.2 #

  • fix(ios): guard the Huolong VVC pipeline (MoheVVCPlayer / HuolongHLSTranscoder) behind #if !TARGET_OS_SIMULATOR so iOS Simulator builds no longer fail linking the device-only ff8_* FFmpeg 8 symbols; the simulator falls back to a stub that reports VVC as unsupported, while real-device playback is unchanged.

1.11.1 #

  • feat(huolong): add Huolong VVC/H.266 playback support, including the Dart playHuolong entry and routing for platform == 'huolong'.
  • feat(android): add VVC-to-HEVC transcoding with bundled FFmpeg 8/vvdec, fMP4 HLS edge playback, audio handling, cancellation, and end-list integrity gating.
  • feat(ios): add the Huolong VVC pipeline with segmented Apple HLS output and playback through the existing native player UI.
  • fix(huolong): harden transcoding/download failure handling, timestamp rescaling, decoder threading, temporary output finalization, and CDN/error diagnostics.
  • chore(android): ship Huolong VVC JNI as a prebuilt arm64 library and exclude build-only FFmpeg8/vvdec static inputs from the pub package.

1.10.45 #

  • fix(ios): forward customHttpHeaders['User-Agent'] to the VLC fallback engine so CDN-sensitive HLS segments keep the caller-provided user agent after AVPlayer fallback.

1.10.44 #

  • fix(ios): exit active PiP when the app is reopened from the home screen or app switcher, while preserving PiP for transient inactive states and protected-data lock-screen recovery.

1.10.43 #

  • chore: version bump.

1.10.42 #

  • feat(android): wrap quality selector content in a ScrollView so panels with many custom quality options scroll rather than overflow.

1.10.41 #

  • refactor(android): extract getIntOrNull helper in QualitySelectorView for robust null/type-safe value parsing; fix getFlutterOptionName to handle non-String name fields via String.valueOf.

1.10.40 #

  • fix(android): disable swipe-up-to-next-episode gesture when ad mode is active, preventing vertical brightness/volume gestures from being incorrectly blocked during ads.
  • fix(ios): disable swipe-up-to-next-episode gesture when ad mode is active; gate all writes to shortDramaLandscapeNextEpisodeEnabled with !isAdModeEnabled.
  • fix(android): remove unused getOptBool dead code from QualitySelectorView.
  • fix(ios): correct stale comment "满 3 个" → "满 2 个" in QualitySelectorView layout loop.
  • fix(dart): add @Deprecated annotation to setVideoCurrentQuality() so call sites get compile-time warnings.
  • fix(android): call ensureUIManager() at the start of setPlaybackQuality() so the landscape quality button is updated immediately even when called before the UI is initialized.
  • fix(ios): replay quality button display after qualityButton is wired in viewDidLoad, covering both custom-JSON and built-in quality paths.
  • fix(android): pass explicit isCustomQualityValue=false when resetting quality on setOptions([]), and call refreshCustomPlaybackQualityDisplay() on all active players after a non-empty setOptions call.
  • fix(ios): forward isCustomQualityValue argument through the native dispatch layer; post PlayerQualityOptionsDidUpdate notification after a non-empty setOptions call to trigger landscape button refresh on all active players.

1.10.39 #

  • feat(android/ios): add setPlaybackQuality(int quality) as the canonical API for switching playback resolution; setVideoCurrentQuality is now deprecated and delegates to it.

1.10.38 #

  • feat(android): add swipe-up-to-next-episode gesture in short-drama landscape fullscreen mode via shortDramaLandscapeNextEpisodeEnabled.
  • feat(ios): add swipe-up-to-next-episode gesture in short-drama landscape fullscreen mode, disabled automatically in ad mode.
  • feat(android/ios): support vip flag and value field in quality selector options for custom quality item rendering.

1.10.37 #

  • fix(ios): preserve the playback rate while paused so a speed change made during a pause is no longer discarded, keeping the selected speed in effect after switching episodes or resuming.

1.10.36 #

  • fix(ios): recover the local HLS server when an established connection is dropped after screen-off (NSURLErrorNetworkConnectionLost / -1005) — the transport-phase counterpart of the -1004 listen-socket reclaim case, per Apple TN2277.
  • fix(ios): walk the NSUnderlyingErrorKey chain when classifying server failures, so an NSURLError wrapped inside an AVFoundation/CoreMedia error is no longer missed.
  • fix(ios): reset the server self-heal retry quota on a time window instead of on every ReadyToPlay, so a readable playlist whose later segments keep failing can no longer defeat the 2-retry cap and loop forever without reporting the failure.

1.10.35 #

  • fix(ios): recover the local HLS server when its listening socket is reclaimed after screen-off (NSURLErrorCannotConnectToHost / -1004), preventing a permanent black screen on reconnect.
  • fix(ios): reset the server self-heal retry quota whenever a player item becomes ready, so the "give up after 2 consecutive failures" limit is not mistaken for a per-instance lifetime cap.
  • fix(ios): restore fullscreen pan gestures for short drama.

1.10.34 #

  • feat: support custom HTTP headers for long video playback.
  • feat(ios): forward custom HTTP headers to AVURLAsset resource requests.

1.10.33 #

  • feat(android): support custom progress slider thumb images via setProgressSliderThumbImage, including raw base64 and data URL payloads.
  • fix(android): restore the default progress slider thumb when the custom image payload is empty or invalid.

1.10.32 #

  • fix(ios): report encrypted HLS remux failures to Flutter instead of leaving playback stuck on a black buffering screen.
  • fix(ios): isolate concurrent encrypted HLS remux cache directories to avoid cache cleanup races under the same cache key.
  • fix(ios): improve encrypted HLS loading feedback on slow networks, including a system loading indicator fallback when no GIF is provided.

1.10.31 #

  • fix(ios): start PiP explicitly after the app enters background and keep transient inactive states from triggering PiP prematurely.

1.10.30 #

  • fix(ios): prevent HLS preload cleanup from deleting the active main playback cache directory, fixing short-drama black screens after lock-screen recovery.
  • chore: expand .pubignore exclusions to keep local release scripts and test credentials out of the pub.dev package.

1.10.29 #

  • fix(ios): prevent HLS preload cleanup from deleting the active main playback cache directory, fixing short-drama black screens after lock-screen recovery.
  • chore: expand .pubignore exclusions to keep local release scripts and test credentials out of the pub.dev package.

1.10.28 #

  • chore: log the mohe_native_player plugin version on startup (iOS and Android).

1.10.27 #

  • fix(ios): cover short-drama lock-screen black-card and blank NowPlaying edge cases.
  • fix(ios): harden LocalHLSServer path validation and DecryptFileAVPlayer failure recovery.
  • fix(ios): verify m3u8 existence before server-failure recovery to close a shutdown race.
  • chore(ios): mute noisy internal AVPlayer.rate reset logs.

1.10.26 #

  • feat(ios): add setCoverImage RPC for NowPlaying artwork.
  • fix(ios): force a non-mixable playback audio session so the NowPlaying card appears during PiP.

1.10.25 #

  • fix(ios): activate the playback audio session before local playback and FFmpeg audio queue setup.

1.10.24 #

  • fix(android): allow episode, playback speed, and quality panels to scroll in short-drama fullscreen mode.

1.10.23 #

  • fix(ios): sync the landscape speed label when Flutter updates playback rate.
  • fix(ios): refresh the landscape play/pause button after FFmpeg short-drama playback starts, including auto next episode.

1.10.22 #

  • fix(ios): remove SpringBoard lockcomplete private API usage to pass App Store review.
  • fix(ios): restore the portrait control bar when setControlBarHidden(false) is called from fullscreen.

1.10.21 #

  • fix(ios): prevent short-drama playback from auto-entering PiP after PiP is disabled and the app goes to background.

1.10.20 #

  • fix(ios): preserve Flutter-hidden native control bar state when entering landscape fullscreen, preventing duplicate progress bars when Flutter renders its own controls.

1.10.19 #

  • fix(ios): hide the portrait back button during fullscreen so it no longer duplicates the landscape control-bar back button.
  • fix(ios): refresh mediaInfo when presentationSize arrives after readyToPlay, and reset stale media metadata during engine/video switches.
  • fix(android): show cellular generation text next to the landscape status-bar cellular icon.

1.10.18 #

  • fix(ios): restore the mediaInfo event after sync overwrite.
  • fix(ios): reset mediaInfo on VLC fallback and guard vlcAdapterReadyToPlay handling.
  • sync(ios): update bundled iLookPlayerCore to ilook-ios-player v1.0.497.

1.10.17 #

  • fix(android): remove the duplicated ShortDramaImmersiveCoordinator.forceRestore(Activity) method so mohe_native_player compiles on Android.

1.10.16 #

  • fix(android): restoreSystemUI now calls ShortDramaImmersiveCoordinator.forceRestore() to unconditionally reset immersive state and restore system bars, preventing permanent status-bar hide when refCount is imbalanced.
  • fix(ios): default isPiPEnabled to YES, aligning with native player defaults.
  • fix(ios): detect AVPlayerItemStatusFailed in recoverFromBackgroundIfNeeded and escalate to recoverAfterLongBackground (replaceCurrentItem) so a background-failed item no longer blocks recovery.
  • feat(ios): add isLayerReadyForDisplay property to FFmpegDecryptPlayer / DecryptFileAVPlayer for callers to detect AVPlayer render channel health after background.

1.10.13 #

  • fix(ios): add instance guards to all VLC/FFmpeg delegate callbacks — prevents stale engine instances from firing error events, corrupting seek/loading UI state, or leaking CMSampleBuffers on engine switch.

1.10.12 #

  • feat(ios): add mediaInfo event (fires once when both duration and video size are available), aligning with Android behavior. All three engines (FFmpeg / AVPlayer / VLC) supported.
  • feat(ios): PiP handoff session — preserves background playback state across episode switches during PiP, fixing black-screen freeze on didStopPictureInPicture.
  • feat(ios): recoverAfterLongBackground uses replaceCurrentItemWithPlayerItem to rebuild the RemoteXPC render channel after long background/PiP.
  • fix(ios): memory-warning handler now only evicts paused instances, preserving playing and stalling instances.
  • fix(ios): stall recovery re-applies user playback rate via playbackLikelyToKeepUp KVO.
  • fix(ios): lockcomplete duplicate-fire guard; volume KVO dedup for multi-instance short-drama.
  • feat(ios/android): PlayerController.restoreSystemUI() — restore system bars after exiting short-drama immersive mode (Android functional; iOS no-op).
  • fix(dart): commands issued before the platform channel is ready are now queued and flushed automatically, eliminating initialization race conditions.

1.10.11 #

  • fix(android): fix PiP aspect ratio flash and incorrect view restore on Samsung One UI. setPictureInPictureParams() now only fires for the active PiP player; layout restore always uses MATCH_PARENT so the view fills the container regardless of when reparenting occurred.

1.10.10 #

  • fix(android): drama videos rendered shrunk inside landscape PiP on certain OEM ROMs (Flutter HC PlatformViewWrapper rebounding inline-mode layout params before TextureView could push the correct frame). Proactively re-assert MATCH_PARENT layout params across multiple frames after reparenting, and force PiP-mode onMeasure to honor the parent container size regardless of explicit-pixel layout params.
  • feat(android): add PlayerController.restoreSystemUI() API. Exiting short-drama immersive no longer auto-restores system bars — the host app should now call restoreSystemUI() explicitly after popping the short-drama route. View destroy still emergency-restores when this view is the last immersive holder.
  • feat(ios): lock-screen / control-center "Now Playing" card via MPNowPlayingInfoCenter + MPRemoteCommandCenter. New RPCs setNowPlayingEnabled / setCoverImage; default enabled. iOS-only feature.

1.10.9 #

  • fix: unify list-style episode selection with episodeselect and remove the legacy list-item event path.
  • fix(android): add PiP play/pause action state handling and resume an existing PiP player after screen-off recovery.

1.10.8 #

  • fix(android): start the localhost HLS server when an existing encrypted HLS cache is hit, so cached short-drama playback uses the local index.m3u8 directly instead of falling back to the legacy MP4 path.

1.10.7 #

  • feat(android): implement runtime PlayerController.setFitMode(...), allowing contain/cover switches without rebuilding the platform view.
  • feat(android): emit a one-shot mediaInfo event after duration and video size are both available.

1.10.6 #

  • fix(android): report encrypted HLS source duration as soon as FFmpeg discovers it, so Flutter can receive the short-drama total duration before HLS remuxing finishes.
  • fix(android): only auto-enter PiP after an explicit user-leave signal, while screen-off/background stops pause playback instead.

1.10.5 #

  • fix(android): ignore provisional HLS live-window duration until the real VOD duration is known, preventing short-drama progress jumps and premature next-episode switching on slow networks.
  • fix(android): hand off the PiP video owner when short-drama auto-play moves to the next episode, so PiP continues showing the video instead of the whole Activity.

1.10.4 #

  • feat(android): fire videosizechange event to Flutter when first valid video size arrives, matching iOS. Detail payload {width, height}. Subscribers receive it via PlayerController.eventStream.

1.10.3 #

  • fix(android): ship consumer ProGuard rules so JNI-resolved methods on OkHttpStreamingIO and FFmpegHlsRemuxer$CancelChecker / $ProgressListener survive host R8/minify. Without this, host apps with minifyEnabled true crash on entering encrypted HLS playback with java.lang.NoSuchMethodError: no non-static method "...readBytes([BI)I".

1.10.2 #

  • feat: expose PlayerController.getCurrentVideoDisplayInfo() for native video display metadata.
  • fix(ios): sync native player core updates for encrypted playback, fullscreen cleanup, PiP state, and initial seek handling.
  • fix(android): align FFmpeg HLS remux bridge with the updated decrypt core callback signature.

1.10.1 #

  • fix(android): notify the encrypted player of the first rendered frame when playback enters PLAYING.

1.10.0 #

  • feat(android): add encrypted HLS streaming playback with FFmpeg remuxing and local segment serving.
  • feat(android): enable MP4/MPEG-TS/HLS muxer support in bundled FFmpeg libraries.
  • fix(android): harden FFmpeg/JNI, streaming I/O, local HTTP server, and memory-pressure handling for playback stability.

1.9.0 #

  • feat(ios): add HLS streaming preload support for encrypted playback.
  • fix(flutter): clean up fullscreen gesture overlay analyzer issues before release.

1.8.1 #

  • fix(android): PlayerPlatformView.dispose now calls restoreFromPiP to prevent PiP window leak on view destroy.
  • fix(android): immersive short drama mode — fix top status bar black strip on OnePlus / ColorOS devices.
  • fix(android): landscape pause ad → portrait transition — hideAllSelectors no longer calls removeAllViews, preventing layout disappear.
  • fix(android): prevent MPEG4Writer FORTIFY abort — add Annex-B structure validation before writing.
  • fix(android): fix DFEncryptedPlayer resource leak — shutdown now enforces main-thread dispatch.

1.8.0 #

  • feat: add PlayerController.setFitMode(PlayerFitMode) — switch fill mode at runtime without rebuilding the widget.
  • feat(ios): fire videosizechange event with {width, height} on video ready and VLC video size change.
  • feat(ios): ad mode now allows videosizechange event through (previously blocked).
  • fix(android): danmaku settings panel — SeekBar thumb clipped on landscape; add padding equal to thumb radius and disable clipChildren on parent containers; add WindowInsets listener for display cutout / waterfall right safe-inset.
  • chore(ios): bump ILOOK_PLAYER_VERSION to 1.0.359.

1.7.0 #

  • feat: add PlayerFitMode enum (contain / cover) and PlayerView.fitMode parameter — passed via creationParams to native layer; cover = fill screen (short drama), contain = letterbox (default).
  • feat(ios): sync native core to v1.0.348 — isHostFlutter flag blocks shortDrama auto-fullscreen black screen; setupViewWithFrame:attributes: dispatches creationParams fit-mode to setFitMode:.
  • feat(ios): FullscreenManager enhancements for Flutter hosting — suppresses auto-rotate on isHostFlutter, correct safe-area handling in FullscreenPlayerViewController.
  • feat(android): setPlayerMode(shortDrama) enters immersive fullscreen (hides status/nav bar, adds KEEP_SCREEN_ON); ShortDramaImmersiveCoordinator uses activity-level ref counting so multiple PlayerView instances cooperate.
  • feat(android): creationParams fit-mode mapped to CENTER_CROP (cover) or FIT_CENTER (contain) on TextureView.

1.6.0 #

  • feat: add PlayerPreload / PreloadRequest Dart API — priority-based video preload with preload, cancel, cancelExcept, configure, shutdownAllPlayers.
  • feat(android): add ILookPreloadManager / ILookPreloadTask / ILookCacheKey — Android preload system aligned with iOS, with disk cache, priority queue, and inflight preemption.
  • feat(android): PlayerPlugin adds plugin-level globalChannel for shutdownAllPlayers, preloadVideos, cancelPreload, cancelPreloadExcept, configurePreload.
  • feat(android): DFEncryptedPlayer add full lifecycle — setDataSource, prepare, start, pause, stop, seekTo, getState, getDuration, getCurrentPosition.
  • fix(android): FFDecryptMuxer stability improvements.

1.5.1 #

  • feat(ios): sync ILookPlayerCore to v1.0.322 — enable FFmpeg Class Cluster (auto-routes to DecryptFileAVPlayer), add offline-encryption engine (DFEnc* / DFURLSessionSourceDownloader / FFVideoPacketQueue).
  • feat: playWithAds adds optional landingUrl parameter for "了解详情" ad tap-through.
  • feat: expose shutdownAllPlayers via global Flutter channel for batch player cleanup.
  • fix(android): add setControlBarVisible support on Android to align with iOS v1.4.0 — fixes MissingPluginException that broke short-drama playback.

1.5.1 #

  • feat: add setBarrageSettings({displayAreaRatio, opacity, fontSize, speedLevel}) — batch configure danmaku display parameters in one call.
  • feat: add setPiPEnabled(bool) — enable or disable PiP at runtime.
  • feat(ios): new ILookPreloadManager / ILookPreloadTask — priority-based video preload system with configurable concurrency and disk cache limit.
  • feat(ios): expose preload API via Flutter plugin channel (preloadVideos, cancelPreload, cancelPreloadExcept, configurePreload).
  • chore(ios): bump ILOOK_PLAYER_VERSION to 1.0.330.

1.4.0 #

  • feat: add setControlBarVisible(bool) — show/hide the control bar (play/pause, progress, etc.). When hidden, tap gestures will not reveal controls.
  • feat(ios): PlayerUIManager respects controlBarHidden in both showControlsWithAutoHide and toggleLandscapeControls.
  • feat(ios): add methodSignatureForSelector / forwardInvocation guard in TestComponent to prevent crashes when a native method is missing after sync.sh.

1.3.3 #

  • fix(ios/pip): align PiP lifecycle with original uni-app implementation to fix double-video on background.
    • Remove manual startPictureInPicture in sceneWillDeactivate (app still visible).
    • applicationWillResignActive only sets isPiPStartPending flag.
    • applicationDidEnterBackground starts AVPlayer PiP manually.
    • SampleBuffer PiP (VLC/FFmpeg) relies on system canStartPictureInPictureAutomaticallyFromInline.
    • Add isPiPStartPending guard, pause protection, and failedToStart fallback.

1.3.2 #

  • fix(ios/pip): disable canStartPictureInPictureAutomaticallyFromInline for AVPlayer PiP to prevent double-video during app switch animation; SampleBuffer PiP still uses auto-start as required.
  • fix(ios/pip): add FFmpeg player path to isBuffering and seekableTimeRanges PiP delegate callbacks.
  • fix(ios/pip): fix PiP setPlaying delegate to handle VLC, FFmpeg, and no-player cases independently.
  • chore(ios): comment out verbose [BarrageSpeed] debug NSLogs in BarrageManager, BarrageRenderer, and BarrageScheduler.

1.3.1 #

  • chore(ios): clarify gravityRotationAllowed naming comment to explain why it differs from the exported setGravityRotationEnabled: method name.

1.3.0 #

  • feat: PlayerView now accepts a mode parameter (PlayerMode.normal / PlayerMode.shortDrama). In short drama mode, Flutter gesture recognizers are not registered so outer PageView can handle vertical swipes natively.
  • feat(ios): short drama mode disables double-tap and long-press gestures to prevent UITapGestureRecognizer.delaysTouchesEnded from blocking Flutter's VerticalDragGestureRecognizer.
  • fix(ios): gravity rotation no longer starts automatically on player init — must be explicitly enabled via setGravityRotationEnabled(true).
  • fix(ios): reset motion manager orientation state on stop to prevent stale landscape/portrait state on next start.

1.2.1 #

  • fix(ios): back button hidden state was being reset by internal UI refresh — add portraitBackButtonHidden property and respect user override across showControls and fullscreen transitions.
  • fix(android): align Android short drama mode with iOS — setPlayerMode(SHORT_DRAMA) now hides back button by default; explicit setPortraitBackButtonVisible calls take priority over mode defaults.

1.2.0 #

  • feat: add playerMode (normal / shortDrama) — short drama mode disables mid-video pan gestures.
  • feat: add backButtonInsets — customize portrait back button position to avoid Dynamic Island.
  • feat: add seekToSeconds — precise seek for both normal and short drama modes.
  • fix: sync adMode priority fix — ad mode now takes precedence over shortDrama, keeping pan gesture enabled during ads.
  • feat(android): add playerMode, backButtonInsets, and seekToSeconds support on Android.

1.1.1 #

  • Fix: restore gravity rotation detection — startDeviceMotionUpdatesToQueue was accidentally commented out, causing auto-rotation to stop working.
  • Fix: add setGravityRotationEnabled and setPortraitBackButtonVisible bridge methods to iOS Flutter bridge.

1.0.1 #

  • Add missing Android entry point: PlayerPlugin.java, AndroidManifest.xml.
  • Add full Android Java sources: managers, views, models, utils, DanmakuFlameMaster library.

1.0.0 #

  • Rename package from nova_native_player to mohe_native_player.
  • Fix unused field, unused method, and unnecessary import warnings.

0.0.1 #

  • Initial release.
  • Native video player for iOS and Android.
  • HLS/DASH streaming support.
  • Full playback controls, danmaku, DLNA, PiP, fullscreen.
2
likes
0
points
1.22k
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter native video player plugin for iOS and Android with full-featured controls, HLS/DASH support, danmaku, DLNA casting, PiP, and more.

Repository

License

unknown (license)

Dependencies

flutter, screen_brightness, volume_controller

More

Packages that depend on mohe_native_player

Packages that implement mohe_native_player