mohe_native_player 1.11.13
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,tracksanddurationerrors, but the resource-loader branch was left checking onlyplayable— and that branch is exactly where a live URL without an.m3u8extension ends up, since those are not routed through the proxy. If-1002surfaced ontracksordurationthere, 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)/0when the trigger came fromtracksorduration. - 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
-2001load-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
EXTINFduration is written in scientific notation. media3 1.2.1 reads the duration with#EXTINF:([\d\.]+)\b, which cannot match3E+00, so the parser threwParserException("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 matches1., silently treating a 15-second segment as 1 second. The newHlsPlaylistSanitizerrewrites such durations to an equivalent plain-decimal form and is wired in through media3's officialsetPlaylistParserFactoryextension point, so the sanitized text is still parsed byDefaultHlsPlaylistParserFactoryand stays in step with future media3 releases. Well-formed playlists pass through byte-for-byte unchanged (the rewrite only applies whenBigDecimal'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, sinceBigDecimalhappily constructs1E+2000000000andtoPlainString()on it raisesOutOfMemoryError— anErrorthat aNumberFormatExceptioncatch 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 URLunderNSURLErrorDomain/CoreMediaErrorDomain, neither of which isAVFoundationErrorDomain, soILookIsMediaFormatErrorrejected 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 independentreadyForDisplaywatchdog 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.-1002is 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.
hideLoadingran at the top of theAVPlayerItemStatusFailedbranch, 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 insidestartVLCPlayerWithURLwas also gated on the host having supplied a loading GIF, which no longer reflects how loading works —PlayerUIManagerfalls back to the system spinner without one, and the primary start path dropped that condition long ago. Hosts that never callsetLoadingGifwere 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,tracksanddurationerrors and their underlying errors) and hide the spinner plus reset the detector when they do give up. - fix(ios): preserve the custom
User-Agentwhen the resource-loader path falls back to VLC.pendingVLCUserAgentwas only recorded in the standard flow, so fallbacks fromcreateAVAssetAndStartPlaybacksent 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_timeoutif 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 butTestComponent.mnever defined it, andPlayerPlatformViewsends the message unguarded — so everyPlayerViewWithControlsmount raisedunrecognized 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, becausesetPortraitBackButtonVisible:also records_userPortraitBackButtonVisibleOverride— without that, any latersetPlayerMode:re-shows the portrait back button. The control-bar flag is written synchronously first rather than throughsetControlBarVisible::PlayerUIManager'ssetLandscapeBackButtonHidden:readscontrolBarHidden, 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
setNativeUIVisiblecase, which fell through tonotImplemented()and raisedMissingPluginException. It drives the same three switches as iOS;UIManageralready had a symmetricuserPortraitBackButtonVisibleOverride. - fix(dart): attach
catchErrorto both fire-and-forgetsetNativeUIVisiblecalls inPlayerViewWithControls. 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_nativeUiHiddenstayedfalseso the dispose-time restore never ran either. - fix(android): accept the
setCoverImageRPC instead of raisingMissingPluginException. 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) fromhandleMethodCall:. Both were shadowed by earlier branches in the same if/else chain, so behaviour is unchanged; theNSLoglost with the deadrestoreSystemUIcopy 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'sisSeekingflag, 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 stalecurrentTime— 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
UIControlEventTouchCancelon 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 theisSeekingflag 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 ignoresfinished(it is alwaysNOwhen 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 intoisScrubbing(finger still down) andisSeeking(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
readyForDisplayobserver 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
AVPlayerItemStatusFailedwhen 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 becauseCoreMediaErrorDomainis 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
positionfrom a byte offset, which is badly distorted on VBR or multi-segment sources — one title carrying 623EXT-X-DISCONTINUITYmarkers landed at 2198s for a 1413s target, off by 785s. The adapter already readcurrentTimefromvlcPlayer.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.hstates 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
waitingToPlayAtSpecifiedRatewithreasonForWaitingToPlay == minimizeStallsused 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
setRatefeedback loop during 2x playback. When the buffer ran dry, AVPlayer normalisedrateback to 1.0, the rate observer re-applied the desired rate, and AVPlayer normalised it again — measured at 2–28setRatecalls per second, amplifying through the level-triggeredtimeControlStatusobserver into 283–505 redundant loading callbacks (and as manybufferingevents 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
liveplayer mode and an independentsetLandscapeBackButtonVisibleAPI. 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 asINVISIBLEso the title keeps its position) and currently treatsliveasnormal.
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 everyisInPictureInPictureMode()behind API 24+ to avoidNoSuchMethodErroron 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-aliveonSurfaceTextureDestroyedreturn value. - fix(ios): harden crash paths — remove
AVPlayerItemKVO beforereplaceCurrentItemWithPlayerItem:nilduring teardown (fixesNSInternalInconsistencyException), wrap allenqueueSampleBuffer:calls in@trywith drop-frame + flush recovery on failed/torn-down layers, and replace rawsend()inLocalHLSServerwith a boundedsafeSendto fix partial-write truncation on large.ts/.m4ssegments.
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
errorevent with areasonfield (load_timeout/stall_timeout, codes -2001 / -2002) so hosts can show a "load failed / retry" prompt. New Dart APIsetLoadFailureDetection(firstFrameTimeoutSec, stallTimeoutSec)andPlayerLoadFailureReasonconstants; 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 aload_timeoutinto 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
readyForDisplayfallback so episode switches no longer stall on the first frame when EVENT HLS playlists do not reachreadyToPlay. - 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_SIMULATORso iOS Simulator builds no longer fail linking the device-onlyff8_*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
playHuolongentry and routing forplatform == '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
ScrollViewso panels with many custom quality options scroll rather than overflow.
1.10.41 #
- refactor(android): extract
getIntOrNullhelper inQualitySelectorViewfor robust null/type-safe value parsing; fixgetFlutterOptionNameto handle non-Stringnamefields viaString.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
shortDramaLandscapeNextEpisodeEnabledwith!isAdModeEnabled. - fix(android): remove unused
getOptBooldead code fromQualitySelectorView. - fix(ios): correct stale comment "满 3 个" → "满 2 个" in
QualitySelectorViewlayout loop. - fix(dart): add
@Deprecatedannotation tosetVideoCurrentQuality()so call sites get compile-time warnings. - fix(android): call
ensureUIManager()at the start ofsetPlaybackQuality()so the landscape quality button is updated immediately even when called before the UI is initialized. - fix(ios): replay quality button display after
qualityButtonis wired inviewDidLoad, covering both custom-JSON and built-in quality paths. - fix(android): pass explicit
isCustomQualityValue=falsewhen resetting quality onsetOptions([]), and callrefreshCustomPlaybackQualityDisplay()on all active players after a non-emptysetOptionscall. - fix(ios): forward
isCustomQualityValueargument through the native dispatch layer; postPlayerQualityOptionsDidUpdatenotification after a non-emptysetOptionscall 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;setVideoCurrentQualityis 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
vipflag andvaluefield 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
.pubignoreexclusions 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
.pubignoreexclusions 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.ratereset logs.
1.10.26 #
- feat(ios): add
setCoverImageRPC 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
lockcompleteprivate 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
mediaInfowhenpresentationSizearrives afterreadyToPlay, 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
mediaInfoevent after sync overwrite. - fix(ios): reset
mediaInfoon VLC fallback and guardvlcAdapterReadyToPlayhandling. - sync(ios): update bundled iLookPlayerCore to ilook-ios-player v1.0.497.
1.10.17 #
- fix(android): remove the duplicated
ShortDramaImmersiveCoordinator.forceRestore(Activity)method somohe_native_playercompiles on Android.
1.10.16 #
- fix(android):
restoreSystemUInow callsShortDramaImmersiveCoordinator.forceRestore()to unconditionally reset immersive state and restore system bars, preventing permanent status-bar hide when refCount is imbalanced. - fix(ios): default
isPiPEnabledtoYES, aligning with native player defaults. - fix(ios): detect
AVPlayerItemStatusFailedinrecoverFromBackgroundIfNeededand escalate torecoverAfterLongBackground(replaceCurrentItem) so a background-failed item no longer blocks recovery. - feat(ios): add
isLayerReadyForDisplayproperty toFFmpegDecryptPlayer/DecryptFileAVPlayerfor 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
mediaInfoevent (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):
recoverAfterLongBackgroundusesreplaceCurrentItemWithPlayerItemto 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
playbackLikelyToKeepUpKVO. - fix(ios):
lockcompleteduplicate-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 usesMATCH_PARENTso 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
PlatformViewWrapperrebounding inline-mode layout params beforeTextureViewcould push the correct frame). Proactively re-assertMATCH_PARENTlayout params across multiple frames after reparenting, and force PiP-modeonMeasureto 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 callrestoreSystemUI()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 RPCssetNowPlayingEnabled/setCoverImage; default enabled. iOS-only feature.
1.10.9 #
- fix: unify list-style episode selection with
episodeselectand 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.m3u8directly 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
mediaInfoevent 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
videosizechangeevent to Flutter when first valid video size arrives, matching iOS. Detail payload{width, height}. Subscribers receive it viaPlayerController.eventStream.
1.10.3 #
- fix(android): ship consumer ProGuard rules so JNI-resolved methods on
OkHttpStreamingIOandFFmpegHlsRemuxer$CancelChecker / $ProgressListenersurvive host R8/minify. Without this, host apps withminifyEnabled truecrash on entering encrypted HLS playback withjava.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.disposenow callsrestoreFromPiPto 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 —
hideAllSelectorsno longer callsremoveAllViews, preventing layout disappear. - fix(android): prevent
MPEG4WriterFORTIFY abort — add Annex-B structure validation before writing. - fix(android): fix
DFEncryptedPlayerresource leak —shutdownnow enforces main-thread dispatch.
1.8.0 #
- feat: add
PlayerController.setFitMode(PlayerFitMode)— switch fill mode at runtime without rebuilding the widget. - feat(ios): fire
videosizechangeevent with{width, height}on video ready and VLC video size change. - feat(ios): ad mode now allows
videosizechangeevent through (previously blocked). - fix(android): danmaku settings panel — SeekBar thumb clipped on landscape; add padding equal to thumb radius and disable
clipChildrenon parent containers; addWindowInsetslistener for display cutout / waterfall right safe-inset. - chore(ios): bump
ILOOK_PLAYER_VERSIONto1.0.359.
1.7.0 #
- feat: add
PlayerFitModeenum (contain/cover) andPlayerView.fitModeparameter — passed viacreationParamsto native layer; cover = fill screen (short drama), contain = letterbox (default). - feat(ios): sync native core to v1.0.348 —
isHostFlutterflag blocks shortDrama auto-fullscreen black screen;setupViewWithFrame:attributes:dispatchescreationParamsfit-mode tosetFitMode:. - feat(ios):
FullscreenManagerenhancements for Flutter hosting — suppresses auto-rotate onisHostFlutter, correct safe-area handling inFullscreenPlayerViewController. - feat(android):
setPlayerMode(shortDrama)enters immersive fullscreen (hides status/nav bar, addsKEEP_SCREEN_ON);ShortDramaImmersiveCoordinatoruses activity-level ref counting so multiplePlayerViewinstances cooperate. - feat(android):
creationParamsfit-modemapped toCENTER_CROP(cover) orFIT_CENTER(contain) onTextureView.
1.6.0 #
- feat: add
PlayerPreload/PreloadRequestDart API — priority-based video preload withpreload,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):
PlayerPluginadds plugin-levelglobalChannelforshutdownAllPlayers,preloadVideos,cancelPreload,cancelPreloadExcept,configurePreload. - feat(android):
DFEncryptedPlayeradd full lifecycle —setDataSource,prepare,start,pause,stop,seekTo,getState,getDuration,getCurrentPosition. - fix(android):
FFDecryptMuxerstability 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:
playWithAdsadds optionallandingUrlparameter for "了解详情" ad tap-through. - feat: expose
shutdownAllPlayersvia global Flutter channel for batch player cleanup. - fix(android): add
setControlBarVisiblesupport on Android to align with iOS v1.4.0 — fixesMissingPluginExceptionthat 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_VERSIONto1.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):
PlayerUIManagerrespectscontrolBarHiddenin bothshowControlsWithAutoHideandtoggleLandscapeControls. - feat(ios): add
methodSignatureForSelector/forwardInvocationguard inTestComponentto 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
startPictureInPictureinsceneWillDeactivate(app still visible). applicationWillResignActiveonly setsisPiPStartPendingflag.applicationDidEnterBackgroundstarts AVPlayer PiP manually.- SampleBuffer PiP (VLC/FFmpeg) relies on system
canStartPictureInPictureAutomaticallyFromInline. - Add
isPiPStartPendingguard, pause protection, andfailedToStartfallback.
- Remove manual
1.3.2 #
- fix(ios/pip): disable
canStartPictureInPictureAutomaticallyFromInlinefor 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
isBufferingandseekableTimeRangesPiP delegate callbacks. - fix(ios/pip): fix PiP
setPlayingdelegate 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
gravityRotationAllowednaming comment to explain why it differs from the exportedsetGravityRotationEnabled:method name.
1.3.0 #
- feat:
PlayerViewnow accepts amodeparameter (PlayerMode.normal/PlayerMode.shortDrama). In short drama mode, Flutter gesture recognizers are not registered so outerPageViewcan handle vertical swipes natively. - feat(ios): short drama mode disables double-tap and long-press gestures to prevent
UITapGestureRecognizer.delaysTouchesEndedfrom blocking Flutter'sVerticalDragGestureRecognizer. - 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
portraitBackButtonHiddenproperty and respect user override acrossshowControlsand fullscreen transitions. - fix(android): align Android short drama mode with iOS —
setPlayerMode(SHORT_DRAMA)now hides back button by default; explicitsetPortraitBackButtonVisiblecalls 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, andseekToSecondssupport on Android.
1.1.1 #
- Fix: restore gravity rotation detection —
startDeviceMotionUpdatesToQueuewas accidentally commented out, causing auto-rotation to stop working. - Fix: add
setGravityRotationEnabledandsetPortraitBackButtonVisiblebridge 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_playertomohe_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.