diff --git a/Dreamio.xcodeproj/project.pbxproj b/Dreamio.xcodeproj/project.pbxproj index af6a9dc..d7fbe19 100644 --- a/Dreamio.xcodeproj/project.pbxproj +++ b/Dreamio.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 00160C2FABC913A6DDCAB0C4 /* Pods_Dreamio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 908FA15B08AB341C116BAB46 /* Pods_Dreamio.framework */; }; 6F2A2B362C00100100DREAMIO /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2A2B332C00100100DREAMIO /* AppDelegate.swift */; }; 6F2A2B372C00100100DREAMIO /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2A2B342C00100100DREAMIO /* SceneDelegate.swift */; }; 6F2A2B382C00100100DREAMIO /* DreamioWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2A2B352C00100100DREAMIO /* DreamioWebViewController.swift */; }; @@ -15,7 +16,6 @@ 6F2A2B442C00100100DREAMIO /* VLCNativePlaybackBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2A2B482C00100100DREAMIO /* VLCNativePlaybackBackend.swift */; }; 6F2A2B452C00100100DREAMIO /* NativePlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2A2B492C00100100DREAMIO /* NativePlayerViewController.swift */; }; 6F2A2B502C00100100DREAMIO /* StreamResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2A2B512C00100100DREAMIO /* StreamResolver.swift */; }; - BA013CEC876B829A86AE8DCB /* Pods_Dreamio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 908FA15B08AB341C116BAB46 /* Pods_Dreamio.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -39,6 +39,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 00160C2FABC913A6DDCAB0C4 /* Pods_Dreamio.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Dreamio/VLCNativePlaybackBackend.swift b/Dreamio/VLCNativePlaybackBackend.swift index c3c2318..0fa779a 100644 --- a/Dreamio/VLCNativePlaybackBackend.swift +++ b/Dreamio/VLCNativePlaybackBackend.swift @@ -5,6 +5,8 @@ import MobileVLCKit #endif final class VLCNativePlaybackBackend: NSObject, NativePlaybackBackend { + private static let seekBufferMilliseconds = 30_000 + static var isAvailable: Bool { #if canImport(MobileVLCKit) true @@ -67,10 +69,11 @@ final class VLCNativePlaybackBackend: NSObject, NativePlaybackBackend { if !headerValue.isEmpty { media.addOption(":http-header=\(headerValue)") } + configureSeekBuffer(for: media) mediaPlayer.media = media #if DEBUG - print("[DreamioVLC] opening url=\(URLRedactor.redactedURLString(request.playbackURL.absoluteString))") + print("[DreamioVLC] opening url=\(URLRedactor.redactedURLString(request.playbackURL.absoluteString)) seekBufferMilliseconds=\(Self.seekBufferMilliseconds)") #endif mediaPlayer.play() #else @@ -84,6 +87,18 @@ final class VLCNativePlaybackBackend: NSObject, NativePlaybackBackend { #endif } +#if canImport(MobileVLCKit) + private func configureSeekBuffer(for media: VLCMedia) { + let cachingOptions = [ + ":network-caching=\(Self.seekBufferMilliseconds)", + ":http-caching=\(Self.seekBufferMilliseconds)", + ":file-caching=\(Self.seekBufferMilliseconds)" + ] + + cachingOptions.forEach { media.addOption($0) } + } +#endif + func pause() { #if canImport(MobileVLCKit) mediaPlayer.pause() diff --git a/docs/turns/2026-05-25-add-native-playback-seek-buffer.html b/docs/turns/2026-05-25-add-native-playback-seek-buffer.html new file mode 100644 index 0000000..a5334da --- /dev/null +++ b/docs/turns/2026-05-25-add-native-playback-seek-buffer.html @@ -0,0 +1,263 @@ + + + + + + Add Native Playback Seek Buffer + + + +
+
+

Add Native Playback Seek Buffer

+

Native VLC playback now applies a centralized 30-second media caching value before opening streams, giving short forward and backward seeks more room to avoid feeling like a fresh stream start.

+
+ Date: 2026-05-25 + Issue: dreamio-3yb + Area: native playback +
+
+ +
+

Summary

+

Added VLC-side caching options to the native playback backend only. The player UI, skip controls, scrubber, subtitle flow, audio-track flow, and NativePlaybackBackend protocol were left unchanged.

+
+ +
+

Changes Made

+ +
+ +
+

Context

+

The requested buffer is a VLC media caching setting, not a custom proxy or a new app-visible setting. Keeping the change inside VLCNativePlaybackBackend.play(request:) preserves the existing player contract and keeps the first tuning point small and easy to adjust after real-device testing.

+
+ +
+

Important Implementation Details

+

The caching options are added after request headers and VLC HTTP options are configured, and before the media is assigned to the player. This ensures the options belong to the same VLCMedia instance used for the native stream.

+

The helper is compiled only when MobileVLCKit is available, matching the rest of the backend's conditional compilation pattern.

+
+ +
+

Relevant Diff Snippets

+

The repository asks for diffs.com rendering through @pierre/diffs/ssr. The package was not installed in this worktree, so the ESM availability check failed with ERR_MODULE_NOT_FOUND. A plain unified diff is included as the fallback.

+
diff --git a/Dreamio.xcodeproj/project.pbxproj b/Dreamio.xcodeproj/project.pbxproj
+index af6a9dc..d7fbe19 100644
+--- a/Dreamio.xcodeproj/project.pbxproj
++++ b/Dreamio.xcodeproj/project.pbxproj
+@@ -7,6 +7,7 @@
+  objects = {
+ 
+ /* Begin PBXBuildFile section */
++        00160C2FABC913A6DDCAB0C4 /* Pods_Dreamio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 908FA15B08AB341C116BAB46 /* Pods_Dreamio.framework */; };
+@@ -15,7 +16,6 @@
+-        BA013CEC876B829A86AE8DCB /* Pods_Dreamio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 908FA15B08AB341C116BAB46 /* Pods_Dreamio.framework */; };
+@@ -39,6 +39,7 @@
+          files = (
++                00160C2FABC913A6DDCAB0C4 /* Pods_Dreamio.framework in Frameworks */,
+          );
+
+diff --git a/Dreamio/VLCNativePlaybackBackend.swift b/Dreamio/VLCNativePlaybackBackend.swift
+index c3c2318..0fa779a 100644
+--- a/Dreamio/VLCNativePlaybackBackend.swift
++++ b/Dreamio/VLCNativePlaybackBackend.swift
+@@ -5,6 +5,8 @@ import MobileVLCKit
+ #endif
+ 
+ final class VLCNativePlaybackBackend: NSObject, NativePlaybackBackend {
++    private static let seekBufferMilliseconds = 30_000
++
+@@ -67,10 +69,11 @@ final class VLCNativePlaybackBackend: NSObject, NativePlaybackBackend {
+          if !headerValue.isEmpty {
+              media.addOption(":http-header=\(headerValue)")
+          }
++        configureSeekBuffer(for: media)
+ 
+          mediaPlayer.media = media
+ #if DEBUG
+-        print("[DreamioVLC] opening url=\(URLRedactor.redactedURLString(request.playbackURL.absoluteString))")
++        print("[DreamioVLC] opening url=\(URLRedactor.redactedURLString(request.playbackURL.absoluteString)) seekBufferMilliseconds=\(Self.seekBufferMilliseconds)")
+ #endif
+@@ -84,6 +87,18 @@ final class VLCNativePlaybackBackend: NSObject, NativePlaybackBackend {
+ #endif
+      }
+ 
++#if canImport(MobileVLCKit)
++    private func configureSeekBuffer(for media: VLCMedia) {
++        let cachingOptions = [
++            ":network-caching=\(Self.seekBufferMilliseconds)",
++            ":http-caching=\(Self.seekBufferMilliseconds)",
++            ":file-caching=\(Self.seekBufferMilliseconds)"
++        ]
++
++        cachingOptions.forEach { media.addOption($0) }
++    }
++#endif
+
+ +
+

Expected Impact for End-Users

+

Short native playback seeks should have more buffered media available, especially around repeated 15-second skip forward and backward actions. Users should not see any new controls or settings.

+
+ +
+

Validation

+ +
+ +
+

Issues, Limitations, and Mitigations

+ +
+ +
+

Follow-up Work

+ +
+
+ +