react-native-video-provider 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AuVideo.podspec +28 -0
- package/LICENSE +20 -0
- package/README.md +183 -0
- package/android/build.gradle +59 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/auvideo/AuVideoModule.kt +259 -0
- package/android/src/main/java/com/auvideo/AuVideoPackage.kt +35 -0
- package/android/src/main/java/com/auvideo/AuVideoSurfaceView.kt +40 -0
- package/android/src/main/java/com/auvideo/AuVideoSurfaceViewManager.kt +38 -0
- package/android/src/main/java/com/auvideo/PlayerCore.kt +366 -0
- package/android/src/main/java/com/auvideo/SurfaceRegistry.kt +26 -0
- package/android/src/main/res/layout/au_video_player_view.xml +10 -0
- package/ios/AuVideo.h +10 -0
- package/ios/AuVideo.mm +232 -0
- package/ios/AuVideoOrientation.swift +59 -0
- package/ios/AuVideoPlayerCore.swift +415 -0
- package/ios/AuVideoSurfaceRegistry.swift +30 -0
- package/ios/AuVideoSurfaceView.h +15 -0
- package/ios/AuVideoSurfaceView.mm +63 -0
- package/lib/module/AuVideoSurfaceNativeComponent.ts +8 -0
- package/lib/module/NativeAuVideo.js +5 -0
- package/lib/module/NativeAuVideo.js.map +1 -0
- package/lib/module/components/FloatingPlayer.js +119 -0
- package/lib/module/components/FloatingPlayer.js.map +1 -0
- package/lib/module/components/FullscreenPlayer.js +59 -0
- package/lib/module/components/FullscreenPlayer.js.map +1 -0
- package/lib/module/components/GestureOverlay.js +56 -0
- package/lib/module/components/GestureOverlay.js.map +1 -0
- package/lib/module/components/MiniPlayer.js +89 -0
- package/lib/module/components/MiniPlayer.js.map +1 -0
- package/lib/module/components/VideoControls.js +191 -0
- package/lib/module/components/VideoControls.js.map +1 -0
- package/lib/module/components/VideoPlayer.js +65 -0
- package/lib/module/components/VideoPlayer.js.map +1 -0
- package/lib/module/components/VideoSurface.js +35 -0
- package/lib/module/components/VideoSurface.js.map +1 -0
- package/lib/module/core/VideoManager.js +436 -0
- package/lib/module/core/VideoManager.js.map +1 -0
- package/lib/module/hooks/useFullscreen.js +19 -0
- package/lib/module/hooks/useFullscreen.js.map +1 -0
- package/lib/module/hooks/usePiP.js +17 -0
- package/lib/module/hooks/usePiP.js.map +1 -0
- package/lib/module/hooks/usePlayback.js +21 -0
- package/lib/module/hooks/usePlayback.js.map +1 -0
- package/lib/module/hooks/useVideo.js +17 -0
- package/lib/module/hooks/useVideo.js.map +1 -0
- package/lib/module/hooks/useVideoEvents.js +26 -0
- package/lib/module/hooks/useVideoEvents.js.map +1 -0
- package/lib/module/index.js +19 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/provider/VideoContext.js +14 -0
- package/lib/module/provider/VideoContext.js.map +1 -0
- package/lib/module/provider/VideoProvider.js +36 -0
- package/lib/module/provider/VideoProvider.js.map +1 -0
- package/lib/module/state/createVideoStore.js +33 -0
- package/lib/module/state/createVideoStore.js.map +1 -0
- package/lib/module/types/events.js +4 -0
- package/lib/module/types/events.js.map +1 -0
- package/lib/module/types/index.js +2 -0
- package/lib/module/types/index.js.map +1 -0
- package/lib/module/types/video.js +2 -0
- package/lib/module/types/video.js.map +1 -0
- package/lib/module/utils/Emitter.js +39 -0
- package/lib/module/utils/Emitter.js.map +1 -0
- package/lib/module/utils/formatTime.js +15 -0
- package/lib/module/utils/formatTime.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/AuVideoSurfaceNativeComponent.d.ts +8 -0
- package/lib/typescript/src/AuVideoSurfaceNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/NativeAuVideo.d.ts +85 -0
- package/lib/typescript/src/NativeAuVideo.d.ts.map +1 -0
- package/lib/typescript/src/components/FloatingPlayer.d.ts +12 -0
- package/lib/typescript/src/components/FloatingPlayer.d.ts.map +1 -0
- package/lib/typescript/src/components/FullscreenPlayer.d.ts +12 -0
- package/lib/typescript/src/components/FullscreenPlayer.d.ts.map +1 -0
- package/lib/typescript/src/components/GestureOverlay.d.ts +14 -0
- package/lib/typescript/src/components/GestureOverlay.d.ts.map +1 -0
- package/lib/typescript/src/components/MiniPlayer.d.ts +16 -0
- package/lib/typescript/src/components/MiniPlayer.d.ts.map +1 -0
- package/lib/typescript/src/components/VideoControls.d.ts +17 -0
- package/lib/typescript/src/components/VideoControls.d.ts.map +1 -0
- package/lib/typescript/src/components/VideoPlayer.d.ts +27 -0
- package/lib/typescript/src/components/VideoPlayer.d.ts.map +1 -0
- package/lib/typescript/src/components/VideoSurface.d.ts +18 -0
- package/lib/typescript/src/components/VideoSurface.d.ts.map +1 -0
- package/lib/typescript/src/core/VideoManager.d.ts +88 -0
- package/lib/typescript/src/core/VideoManager.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useFullscreen.d.ts +7 -0
- package/lib/typescript/src/hooks/useFullscreen.d.ts.map +1 -0
- package/lib/typescript/src/hooks/usePiP.d.ts +6 -0
- package/lib/typescript/src/hooks/usePiP.d.ts.map +1 -0
- package/lib/typescript/src/hooks/usePlayback.d.ts +13 -0
- package/lib/typescript/src/hooks/usePlayback.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useVideo.d.ts +13 -0
- package/lib/typescript/src/hooks/useVideo.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useVideoEvents.d.ts +14 -0
- package/lib/typescript/src/hooks/useVideoEvents.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +18 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/provider/VideoContext.d.ts +8 -0
- package/lib/typescript/src/provider/VideoContext.d.ts.map +1 -0
- package/lib/typescript/src/provider/VideoProvider.d.ts +18 -0
- package/lib/typescript/src/provider/VideoProvider.d.ts.map +1 -0
- package/lib/typescript/src/state/createVideoStore.d.ts +6 -0
- package/lib/typescript/src/state/createVideoStore.d.ts.map +1 -0
- package/lib/typescript/src/types/events.d.ts +46 -0
- package/lib/typescript/src/types/events.d.ts.map +1 -0
- package/lib/typescript/src/types/index.d.ts +3 -0
- package/lib/typescript/src/types/index.d.ts.map +1 -0
- package/lib/typescript/src/types/video.d.ts +70 -0
- package/lib/typescript/src/types/video.d.ts.map +1 -0
- package/lib/typescript/src/utils/Emitter.d.ts +15 -0
- package/lib/typescript/src/utils/Emitter.d.ts.map +1 -0
- package/lib/typescript/src/utils/formatTime.d.ts +2 -0
- package/lib/typescript/src/utils/formatTime.d.ts.map +1 -0
- package/package.json +154 -0
- package/src/AuVideoSurfaceNativeComponent.ts +8 -0
- package/src/NativeAuVideo.ts +106 -0
- package/src/components/FloatingPlayer.tsx +130 -0
- package/src/components/FullscreenPlayer.tsx +64 -0
- package/src/components/GestureOverlay.tsx +76 -0
- package/src/components/MiniPlayer.tsx +93 -0
- package/src/components/VideoControls.tsx +213 -0
- package/src/components/VideoPlayer.tsx +77 -0
- package/src/components/VideoSurface.tsx +39 -0
- package/src/core/VideoManager.ts +441 -0
- package/src/hooks/useFullscreen.ts +14 -0
- package/src/hooks/usePiP.ts +13 -0
- package/src/hooks/usePlayback.ts +26 -0
- package/src/hooks/useVideo.ts +16 -0
- package/src/hooks/useVideoEvents.ts +33 -0
- package/src/index.ts +54 -0
- package/src/provider/VideoContext.ts +12 -0
- package/src/provider/VideoProvider.tsx +41 -0
- package/src/state/createVideoStore.ts +33 -0
- package/src/types/events.ts +22 -0
- package/src/types/index.ts +11 -0
- package/src/types/video.ts +85 -0
- package/src/utils/Emitter.ts +51 -0
- package/src/utils/formatTime.ts +12 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import AVFoundation
|
|
2
|
+
import AVKit
|
|
3
|
+
import Foundation
|
|
4
|
+
import UIKit
|
|
5
|
+
|
|
6
|
+
@objc(AuVideoSourceSpec)
|
|
7
|
+
public final class AuVideoSourceSpec: NSObject {
|
|
8
|
+
@objc public let videoId: String
|
|
9
|
+
@objc public let uri: String
|
|
10
|
+
@objc public let headers: [String: String]
|
|
11
|
+
@objc public let title: String?
|
|
12
|
+
@objc public let artist: String?
|
|
13
|
+
@objc public let artworkUri: String?
|
|
14
|
+
@objc public let startPosition: Double
|
|
15
|
+
|
|
16
|
+
@objc public init(
|
|
17
|
+
videoId: String,
|
|
18
|
+
uri: String,
|
|
19
|
+
headers: [String: String],
|
|
20
|
+
title: String?,
|
|
21
|
+
artist: String?,
|
|
22
|
+
artworkUri: String?,
|
|
23
|
+
startPosition: Double
|
|
24
|
+
) {
|
|
25
|
+
self.videoId = videoId
|
|
26
|
+
self.uri = uri
|
|
27
|
+
self.headers = headers
|
|
28
|
+
self.title = title
|
|
29
|
+
self.artist = artist
|
|
30
|
+
self.artworkUri = artworkUri
|
|
31
|
+
self.startPosition = startPosition
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@objc(AuVideoCoreDelegate)
|
|
36
|
+
public protocol AuVideoCoreDelegate: AnyObject {
|
|
37
|
+
func onStatusChange(_ status: String)
|
|
38
|
+
func onLoad(_ videoId: String, duration: Double, width: Double, height: Double)
|
|
39
|
+
func onProgress(_ position: Double, duration: Double, buffered: Double)
|
|
40
|
+
func onSeek(_ position: Double)
|
|
41
|
+
func onEnd()
|
|
42
|
+
func onError(_ code: String, message: String)
|
|
43
|
+
func onAttach(_ surfaceId: String)
|
|
44
|
+
func onDetach(_ surfaceId: String)
|
|
45
|
+
func onPipChange(_ active: Bool)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// UIView whose backing layer is the AVPlayerLayer. Moving this view between
|
|
49
|
+
/// surface containers re-parents rendering without touching playback.
|
|
50
|
+
@objc(AuVideoHostView)
|
|
51
|
+
public final class AuVideoHostView: UIView {
|
|
52
|
+
public override static var layerClass: AnyClass { AVPlayerLayer.self }
|
|
53
|
+
public var playerLayer: AVPlayerLayer { layer as! AVPlayerLayer }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/// The ONE playback engine of the app: a single AVPlayer plus a single host
|
|
57
|
+
/// view, re-parented between registered surfaces. Created once, never owned
|
|
58
|
+
/// by React components; destroyed only by an explicit releasePlayer().
|
|
59
|
+
@objc(AuVideoPlayerCore)
|
|
60
|
+
public final class AuVideoPlayerCore: NSObject {
|
|
61
|
+
|
|
62
|
+
@objc public static let shared = AuVideoPlayerCore()
|
|
63
|
+
|
|
64
|
+
@objc public weak var delegate: AuVideoCoreDelegate?
|
|
65
|
+
|
|
66
|
+
private let player = AVPlayer()
|
|
67
|
+
private let hostView = AuVideoHostView()
|
|
68
|
+
|
|
69
|
+
private var currentVideoId: String?
|
|
70
|
+
private var currentSurfaceId: String?
|
|
71
|
+
/// Surface we want but that hasn't registered (yet, or again).
|
|
72
|
+
private var pendingSurfaceId: String?
|
|
73
|
+
|
|
74
|
+
private var preloaded: [String: AVPlayerItem] = [:]
|
|
75
|
+
private var loadReported = false
|
|
76
|
+
private var repeatEnabled = false
|
|
77
|
+
private var playbackRate: Double = 1
|
|
78
|
+
private var initialized = false
|
|
79
|
+
|
|
80
|
+
private var timeObserver: Any?
|
|
81
|
+
private var timeControlObservation: NSKeyValueObservation?
|
|
82
|
+
private var itemStatusObservation: NSKeyValueObservation?
|
|
83
|
+
private var pipController: AVPictureInPictureController?
|
|
84
|
+
|
|
85
|
+
private override init() {
|
|
86
|
+
super.init()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ------------------------------------------------------------ lifecycle
|
|
90
|
+
|
|
91
|
+
/// Idempotent; main thread.
|
|
92
|
+
@objc public func initialize() {
|
|
93
|
+
if initialized { return }
|
|
94
|
+
initialized = true
|
|
95
|
+
|
|
96
|
+
hostView.playerLayer.player = player
|
|
97
|
+
hostView.playerLayer.videoGravity = .resizeAspect
|
|
98
|
+
player.actionAtItemEnd = .pause
|
|
99
|
+
|
|
100
|
+
try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)
|
|
101
|
+
try? AVAudioSession.sharedInstance().setActive(true)
|
|
102
|
+
|
|
103
|
+
let interval = CMTime(seconds: 0.5, preferredTimescale: 600)
|
|
104
|
+
timeObserver = player.addPeriodicTimeObserver(forInterval: interval, queue: .main) {
|
|
105
|
+
[weak self] _ in
|
|
106
|
+
self?.emitProgress()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
timeControlObservation = player.observe(\.timeControlStatus, options: [.new]) {
|
|
110
|
+
[weak self] player, _ in
|
|
111
|
+
guard let self else { return }
|
|
112
|
+
switch player.timeControlStatus {
|
|
113
|
+
case .playing:
|
|
114
|
+
self.delegate?.onStatusChange("playing")
|
|
115
|
+
case .waitingToPlayAtSpecifiedRate:
|
|
116
|
+
self.delegate?.onStatusChange("buffering")
|
|
117
|
+
case .paused:
|
|
118
|
+
if player.currentItem != nil {
|
|
119
|
+
self.delegate?.onStatusChange("paused")
|
|
120
|
+
}
|
|
121
|
+
@unknown default:
|
|
122
|
+
break
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
NotificationCenter.default.addObserver(
|
|
127
|
+
self,
|
|
128
|
+
selector: #selector(itemDidPlayToEnd(_:)),
|
|
129
|
+
name: .AVPlayerItemDidPlayToEndTime,
|
|
130
|
+
object: nil
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if AVPictureInPictureController.isPictureInPictureSupported() {
|
|
134
|
+
pipController = AVPictureInPictureController(playerLayer: hostView.playerLayer)
|
|
135
|
+
pipController?.delegate = self
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@objc public func releasePlayer() {
|
|
140
|
+
guard initialized else { return }
|
|
141
|
+
initialized = false
|
|
142
|
+
if let observer = timeObserver {
|
|
143
|
+
player.removeTimeObserver(observer)
|
|
144
|
+
timeObserver = nil
|
|
145
|
+
}
|
|
146
|
+
timeControlObservation?.invalidate()
|
|
147
|
+
itemStatusObservation?.invalidate()
|
|
148
|
+
NotificationCenter.default.removeObserver(self)
|
|
149
|
+
detach()
|
|
150
|
+
player.replaceCurrentItem(with: nil)
|
|
151
|
+
pipController = nil
|
|
152
|
+
currentVideoId = nil
|
|
153
|
+
pendingSurfaceId = nil
|
|
154
|
+
preloaded.removeAll()
|
|
155
|
+
delegate?.onStatusChange("idle")
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// --------------------------------------------------------------- source
|
|
159
|
+
|
|
160
|
+
@objc public func setSource(_ source: AuVideoSourceSpec, autoplay: Bool) {
|
|
161
|
+
initialize()
|
|
162
|
+
|
|
163
|
+
if source.videoId == currentVideoId {
|
|
164
|
+
// Same-video handoff: never reload; at most honor autoplay.
|
|
165
|
+
if autoplay, player.timeControlStatus != .playing {
|
|
166
|
+
play()
|
|
167
|
+
}
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
currentVideoId = source.videoId
|
|
172
|
+
loadReported = false
|
|
173
|
+
delegate?.onStatusChange("loading")
|
|
174
|
+
|
|
175
|
+
let item = preloaded.removeValue(forKey: source.videoId) ?? makeItem(source)
|
|
176
|
+
observeItem(item, videoId: source.videoId)
|
|
177
|
+
player.replaceCurrentItem(with: item)
|
|
178
|
+
|
|
179
|
+
if source.startPosition > 0 {
|
|
180
|
+
let target = CMTime(seconds: source.startPosition, preferredTimescale: 600)
|
|
181
|
+
player.seek(to: target, toleranceBefore: .zero, toleranceAfter: .zero)
|
|
182
|
+
}
|
|
183
|
+
if autoplay {
|
|
184
|
+
player.play()
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@objc public func preload(_ source: AuVideoSourceSpec) {
|
|
189
|
+
// AVPlayerItem starts loading its asset as soon as it exists, so a
|
|
190
|
+
// later attach starts near-instantly.
|
|
191
|
+
guard preloaded[source.videoId] == nil, source.videoId != currentVideoId else { return }
|
|
192
|
+
preloaded[source.videoId] = makeItem(source)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private func makeItem(_ source: AuVideoSourceSpec) -> AVPlayerItem {
|
|
196
|
+
let url = URL(string: source.uri) ?? URL(fileURLWithPath: source.uri)
|
|
197
|
+
var options: [String: Any] = [:]
|
|
198
|
+
if !source.headers.isEmpty {
|
|
199
|
+
options["AVURLAssetHTTPHeaderFieldsKey"] = source.headers
|
|
200
|
+
}
|
|
201
|
+
let asset = AVURLAsset(url: url, options: options)
|
|
202
|
+
return AVPlayerItem(asset: asset)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private func observeItem(_ item: AVPlayerItem, videoId: String) {
|
|
206
|
+
itemStatusObservation?.invalidate()
|
|
207
|
+
itemStatusObservation = item.observe(\.status, options: [.new]) {
|
|
208
|
+
[weak self] item, _ in
|
|
209
|
+
guard let self else { return }
|
|
210
|
+
switch item.status {
|
|
211
|
+
case .readyToPlay:
|
|
212
|
+
if !self.loadReported {
|
|
213
|
+
self.loadReported = true
|
|
214
|
+
let size = item.presentationSize
|
|
215
|
+
let duration = item.duration.isNumeric ? item.duration.seconds : 0
|
|
216
|
+
self.delegate?.onStatusChange("ready")
|
|
217
|
+
self.delegate?.onLoad(
|
|
218
|
+
videoId,
|
|
219
|
+
duration: duration,
|
|
220
|
+
width: Double(size.width),
|
|
221
|
+
height: Double(size.height)
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
case .failed:
|
|
225
|
+
let error = item.error as NSError?
|
|
226
|
+
self.delegate?.onError(
|
|
227
|
+
String(error?.code ?? -1),
|
|
228
|
+
message: error?.localizedDescription ?? "Playback failed"
|
|
229
|
+
)
|
|
230
|
+
default:
|
|
231
|
+
break
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ------------------------------------------------------------- commands
|
|
237
|
+
|
|
238
|
+
@objc public func play() {
|
|
239
|
+
if player.currentItem == nil { return }
|
|
240
|
+
player.play()
|
|
241
|
+
if playbackRate != 1 {
|
|
242
|
+
player.rate = Float(playbackRate)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
@objc public func pause() {
|
|
247
|
+
player.pause()
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
@objc public func stop() {
|
|
251
|
+
player.pause()
|
|
252
|
+
player.seek(to: .zero)
|
|
253
|
+
delegate?.onStatusChange("idle")
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
@objc public func seek(to position: Double) {
|
|
257
|
+
let target = CMTime(seconds: max(position, 0), preferredTimescale: 600)
|
|
258
|
+
player.seek(to: target, toleranceBefore: .zero, toleranceAfter: .zero) {
|
|
259
|
+
[weak self] _ in
|
|
260
|
+
self?.delegate?.onSeek(position)
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
@objc public func setRate(_ rate: Double) {
|
|
265
|
+
playbackRate = rate
|
|
266
|
+
// Setting rate on a paused player starts playback; mirror ExoPlayer by
|
|
267
|
+
// only applying immediately when already playing (play() re-applies it).
|
|
268
|
+
if player.timeControlStatus == .playing {
|
|
269
|
+
player.rate = Float(rate)
|
|
270
|
+
}
|
|
271
|
+
if #available(iOS 16.0, *) {
|
|
272
|
+
player.defaultRate = Float(rate)
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
@objc public func setVolume(_ volume: Double) {
|
|
277
|
+
player.volume = Float(min(max(volume, 0), 1))
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
@objc public func setMuted(_ muted: Bool) {
|
|
281
|
+
player.isMuted = muted
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
@objc public func setRepeat(_ enabled: Bool) {
|
|
285
|
+
repeatEnabled = enabled
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
@objc public func setResizeMode(_ mode: String) {
|
|
289
|
+
switch mode {
|
|
290
|
+
case "cover":
|
|
291
|
+
hostView.playerLayer.videoGravity = .resizeAspectFill
|
|
292
|
+
case "stretch":
|
|
293
|
+
hostView.playerLayer.videoGravity = .resize
|
|
294
|
+
default:
|
|
295
|
+
hostView.playerLayer.videoGravity = .resizeAspect
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
@objc public func positionSeconds() -> Double {
|
|
300
|
+
let time = player.currentTime()
|
|
301
|
+
return time.isNumeric ? time.seconds : 0
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// ------------------------------------------------------------- surfaces
|
|
305
|
+
|
|
306
|
+
@objc public func attach(_ surfaceId: String) {
|
|
307
|
+
initialize()
|
|
308
|
+
guard let container = AuVideoSurfaceRegistry.view(for: surfaceId) else {
|
|
309
|
+
// Screen still mounting — attach the moment it registers.
|
|
310
|
+
pendingSurfaceId = surfaceId
|
|
311
|
+
return
|
|
312
|
+
}
|
|
313
|
+
attachTo(container, surfaceId: surfaceId)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
@objc public func detach() {
|
|
317
|
+
hostView.removeFromSuperview()
|
|
318
|
+
if let surfaceId = currentSurfaceId {
|
|
319
|
+
delegate?.onDetach(surfaceId)
|
|
320
|
+
}
|
|
321
|
+
currentSurfaceId = nil
|
|
322
|
+
pendingSurfaceId = nil
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
@objc public func onSurfaceAvailable(_ surfaceId: String, view: UIView) {
|
|
326
|
+
// Also re-attach when the active surface's view was recreated (e.g.
|
|
327
|
+
// navigating back to a screen Fabric re-materialized).
|
|
328
|
+
if surfaceId == pendingSurfaceId || surfaceId == currentSurfaceId {
|
|
329
|
+
attachTo(view, surfaceId: surfaceId)
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
@objc public func onSurfaceUnavailable(_ surfaceId: String, view: UIView) {
|
|
334
|
+
if currentSurfaceId == surfaceId, hostView.superview === view {
|
|
335
|
+
hostView.removeFromSuperview()
|
|
336
|
+
currentSurfaceId = nil
|
|
337
|
+
// Keep playing hidden (audio); remounting the same surface re-attaches.
|
|
338
|
+
pendingSurfaceId = surfaceId
|
|
339
|
+
delegate?.onDetach(surfaceId)
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
private func attachTo(_ container: UIView, surfaceId: String) {
|
|
344
|
+
if currentSurfaceId == surfaceId, hostView.superview === container {
|
|
345
|
+
pendingSurfaceId = nil
|
|
346
|
+
return
|
|
347
|
+
}
|
|
348
|
+
if let previous = currentSurfaceId, previous != surfaceId {
|
|
349
|
+
delegate?.onDetach(previous)
|
|
350
|
+
}
|
|
351
|
+
hostView.removeFromSuperview()
|
|
352
|
+
hostView.frame = container.bounds
|
|
353
|
+
hostView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
354
|
+
container.addSubview(hostView)
|
|
355
|
+
currentSurfaceId = surfaceId
|
|
356
|
+
pendingSurfaceId = nil
|
|
357
|
+
delegate?.onAttach(surfaceId)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// ------------------------------------------------------------------ pip
|
|
361
|
+
|
|
362
|
+
@objc public func enterPip() -> Bool {
|
|
363
|
+
guard let pip = pipController, pip.isPictureInPicturePossible else {
|
|
364
|
+
return false
|
|
365
|
+
}
|
|
366
|
+
pip.startPictureInPicture()
|
|
367
|
+
return true
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
@objc public func exitPip() {
|
|
371
|
+
pipController?.stopPictureInPicture()
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// --------------------------------------------------------------- events
|
|
375
|
+
|
|
376
|
+
@objc private func itemDidPlayToEnd(_ notification: Notification) {
|
|
377
|
+
guard let item = notification.object as? AVPlayerItem, item === player.currentItem else {
|
|
378
|
+
return
|
|
379
|
+
}
|
|
380
|
+
if repeatEnabled {
|
|
381
|
+
player.seek(to: .zero)
|
|
382
|
+
player.play()
|
|
383
|
+
return
|
|
384
|
+
}
|
|
385
|
+
emitProgress()
|
|
386
|
+
delegate?.onStatusChange("ended")
|
|
387
|
+
delegate?.onEnd()
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
private func emitProgress() {
|
|
391
|
+
guard let item = player.currentItem else { return }
|
|
392
|
+
let position = positionSeconds()
|
|
393
|
+
let duration = item.duration.isNumeric ? item.duration.seconds : 0
|
|
394
|
+
var buffered: Double = 0
|
|
395
|
+
if let range = item.loadedTimeRanges.first?.timeRangeValue {
|
|
396
|
+
let end = range.start.seconds + range.duration.seconds
|
|
397
|
+
buffered = max(end - position, 0)
|
|
398
|
+
}
|
|
399
|
+
delegate?.onProgress(position, duration: duration, buffered: buffered)
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
extension AuVideoPlayerCore: AVPictureInPictureControllerDelegate {
|
|
404
|
+
public func pictureInPictureControllerDidStartPictureInPicture(
|
|
405
|
+
_ pictureInPictureController: AVPictureInPictureController
|
|
406
|
+
) {
|
|
407
|
+
delegate?.onPipChange(true)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
public func pictureInPictureControllerDidStopPictureInPicture(
|
|
411
|
+
_ pictureInPictureController: AVPictureInPictureController
|
|
412
|
+
) {
|
|
413
|
+
delegate?.onPipChange(false)
|
|
414
|
+
}
|
|
415
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
/// Maps surface ids to their mounted container views. Weak references only —
|
|
5
|
+
/// an unmounted screen can never be leaked by the registry.
|
|
6
|
+
@objc(AuVideoSurfaceRegistry)
|
|
7
|
+
public final class AuVideoSurfaceRegistry: NSObject {
|
|
8
|
+
|
|
9
|
+
private static let views = NSMapTable<NSString, UIView>.strongToWeakObjects()
|
|
10
|
+
|
|
11
|
+
@objc(registerSurface:view:)
|
|
12
|
+
public static func register(_ surfaceId: String, view: UIView) {
|
|
13
|
+
views.setObject(view, forKey: surfaceId as NSString)
|
|
14
|
+
AuVideoPlayerCore.shared.onSurfaceAvailable(surfaceId, view: view)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@objc(unregisterSurface:view:)
|
|
18
|
+
public static func unregister(_ surfaceId: String, view: UIView) {
|
|
19
|
+
let registered = views.object(forKey: surfaceId as NSString)
|
|
20
|
+
if registered == nil || registered === view {
|
|
21
|
+
views.removeObject(forKey: surfaceId as NSString)
|
|
22
|
+
AuVideoPlayerCore.shared.onSurfaceUnavailable(surfaceId, view: view)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@objc(viewForSurface:)
|
|
27
|
+
public static func view(for surfaceId: String) -> UIView? {
|
|
28
|
+
return views.object(forKey: surfaceId as NSString)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#import <React/RCTViewComponentView.h>
|
|
2
|
+
#import <UIKit/UIKit.h>
|
|
3
|
+
|
|
4
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Fabric component view for <VideoSurface>. A dumb mount point: registers
|
|
8
|
+
* itself in the surface registry under its surfaceId prop; the singleton
|
|
9
|
+
* player's host view gets re-parented into it by the core.
|
|
10
|
+
*/
|
|
11
|
+
@interface AuVideoSurfaceView : RCTViewComponentView
|
|
12
|
+
|
|
13
|
+
@end
|
|
14
|
+
|
|
15
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#import "AuVideoSurfaceView.h"
|
|
2
|
+
|
|
3
|
+
#import <react/renderer/components/AuVideoSpec/ComponentDescriptors.h>
|
|
4
|
+
#import <react/renderer/components/AuVideoSpec/Props.h>
|
|
5
|
+
#import <react/renderer/components/AuVideoSpec/RCTComponentViewHelpers.h>
|
|
6
|
+
|
|
7
|
+
#import <React/RCTConversions.h>
|
|
8
|
+
|
|
9
|
+
#import <AVKit/AVKit.h>
|
|
10
|
+
|
|
11
|
+
#if __has_include(<AuVideo/AuVideo-Swift.h>)
|
|
12
|
+
#import <AuVideo/AuVideo-Swift.h>
|
|
13
|
+
#else
|
|
14
|
+
#import "AuVideo-Swift.h"
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
using namespace facebook::react;
|
|
18
|
+
|
|
19
|
+
@interface AuVideoSurfaceView () <RCTAuVideoSurfaceViewViewProtocol>
|
|
20
|
+
@end
|
|
21
|
+
|
|
22
|
+
@implementation AuVideoSurfaceView {
|
|
23
|
+
NSString *_surfaceId;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
27
|
+
{
|
|
28
|
+
return concreteComponentDescriptorProvider<AuVideoSurfaceViewComponentDescriptor>();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
|
32
|
+
{
|
|
33
|
+
const auto &newViewProps = *std::static_pointer_cast<AuVideoSurfaceViewProps const>(props);
|
|
34
|
+
NSString *newSurfaceId = RCTNSStringFromStringNilIfEmpty(newViewProps.surfaceId);
|
|
35
|
+
|
|
36
|
+
if (![newSurfaceId isEqualToString:_surfaceId]) {
|
|
37
|
+
if (_surfaceId != nil) {
|
|
38
|
+
[AuVideoSurfaceRegistry unregisterSurface:_surfaceId view:self];
|
|
39
|
+
}
|
|
40
|
+
_surfaceId = newSurfaceId;
|
|
41
|
+
if (_surfaceId != nil) {
|
|
42
|
+
[AuVideoSurfaceRegistry registerSurface:_surfaceId view:self];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
[super updateProps:props oldProps:oldProps];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
- (void)prepareForRecycle
|
|
50
|
+
{
|
|
51
|
+
[super prepareForRecycle];
|
|
52
|
+
if (_surfaceId != nil) {
|
|
53
|
+
[AuVideoSurfaceRegistry unregisterSurface:_surfaceId view:self];
|
|
54
|
+
_surfaceId = nil;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@end
|
|
59
|
+
|
|
60
|
+
Class<RCTComponentViewProtocol> AuVideoSurfaceViewCls(void)
|
|
61
|
+
{
|
|
62
|
+
return AuVideoSurfaceView.class;
|
|
63
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { codegenNativeComponent, type ViewProps } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export interface NativeProps extends ViewProps {
|
|
4
|
+
/** Unique id this surface registers under in the native surface registry. */
|
|
5
|
+
surfaceId: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default codegenNativeComponent<NativeProps>('AuVideoSurfaceView');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeAuVideo.ts"],"mappings":";;AAAA,SACEA,mBAAmB,QAGd,cAAc;AAqGrB,eAAeA,mBAAmB,CAACC,YAAY,CAAO,SAAS,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useRef } from 'react';
|
|
4
|
+
import { Animated, PanResponder, Pressable, StyleSheet, Text, useWindowDimensions } from 'react-native';
|
|
5
|
+
import { FLOATING_SURFACE_ID } from "../core/VideoManager.js";
|
|
6
|
+
import { usePlayback } from "../hooks/usePlayback.js";
|
|
7
|
+
import { useVideoManager } from "../provider/VideoContext.js";
|
|
8
|
+
import { VideoSurface } from "./VideoSurface.js";
|
|
9
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
|
+
/**
|
|
11
|
+
* Built-in draggable in-app floating window, rendered by VideoProvider and
|
|
12
|
+
* shown by `showFloating()`. Same engine, same position — expand returns
|
|
13
|
+
* to fullscreen, ✕ hides the window (playback continues; call pause()
|
|
14
|
+
* in onModeChanged if you want otherwise).
|
|
15
|
+
*/
|
|
16
|
+
export function FloatingPlayer({
|
|
17
|
+
width = 200
|
|
18
|
+
}) {
|
|
19
|
+
const manager = useVideoManager();
|
|
20
|
+
const floating = usePlayback(s => s.floating);
|
|
21
|
+
const screen = useWindowDimensions();
|
|
22
|
+
const height = width * 9 / 16;
|
|
23
|
+
const pan = useRef(new Animated.ValueXY({
|
|
24
|
+
x: screen.width - width - 12,
|
|
25
|
+
y: screen.height - height - 96
|
|
26
|
+
})).current;
|
|
27
|
+
const responder = useRef(PanResponder.create({
|
|
28
|
+
onMoveShouldSetPanResponder: (_e, g) => Math.abs(g.dx) > 4 || Math.abs(g.dy) > 4,
|
|
29
|
+
onPanResponderGrant: () => {
|
|
30
|
+
pan.extractOffset();
|
|
31
|
+
},
|
|
32
|
+
onPanResponderMove: Animated.event([null, {
|
|
33
|
+
dx: pan.x,
|
|
34
|
+
dy: pan.y
|
|
35
|
+
}], {
|
|
36
|
+
useNativeDriver: false
|
|
37
|
+
}),
|
|
38
|
+
onPanResponderRelease: () => {
|
|
39
|
+
pan.flattenOffset();
|
|
40
|
+
}
|
|
41
|
+
})).current;
|
|
42
|
+
if (!floating) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return /*#__PURE__*/_jsxs(Animated.View, {
|
|
46
|
+
style: [styles.window, {
|
|
47
|
+
width,
|
|
48
|
+
height,
|
|
49
|
+
transform: pan.getTranslateTransform()
|
|
50
|
+
}],
|
|
51
|
+
...responder.panHandlers,
|
|
52
|
+
children: [/*#__PURE__*/_jsx(VideoSurface, {
|
|
53
|
+
surfaceId: FLOATING_SURFACE_ID,
|
|
54
|
+
autoAttach: true,
|
|
55
|
+
style: styles.surface
|
|
56
|
+
}), /*#__PURE__*/_jsx(Pressable, {
|
|
57
|
+
style: [styles.button, styles.closeButton],
|
|
58
|
+
hitSlop: 8,
|
|
59
|
+
onPress: () => manager.hideFloating(),
|
|
60
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
61
|
+
style: styles.icon,
|
|
62
|
+
children: "\u2715"
|
|
63
|
+
})
|
|
64
|
+
}), /*#__PURE__*/_jsx(Pressable, {
|
|
65
|
+
style: [styles.button, styles.expandButton],
|
|
66
|
+
hitSlop: 8,
|
|
67
|
+
onPress: () => {
|
|
68
|
+
manager.hideFloating();
|
|
69
|
+
manager.enterFullscreen();
|
|
70
|
+
},
|
|
71
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
72
|
+
style: styles.icon,
|
|
73
|
+
children: "\u2922"
|
|
74
|
+
})
|
|
75
|
+
})]
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const styles = StyleSheet.create({
|
|
79
|
+
window: {
|
|
80
|
+
position: 'absolute',
|
|
81
|
+
top: 0,
|
|
82
|
+
left: 0,
|
|
83
|
+
borderRadius: 8,
|
|
84
|
+
overflow: 'hidden',
|
|
85
|
+
backgroundColor: '#000',
|
|
86
|
+
elevation: 8,
|
|
87
|
+
shadowColor: '#000',
|
|
88
|
+
shadowOpacity: 0.4,
|
|
89
|
+
shadowRadius: 8,
|
|
90
|
+
shadowOffset: {
|
|
91
|
+
width: 0,
|
|
92
|
+
height: 4
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
surface: {
|
|
96
|
+
flex: 1
|
|
97
|
+
},
|
|
98
|
+
button: {
|
|
99
|
+
position: 'absolute',
|
|
100
|
+
top: 4,
|
|
101
|
+
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
102
|
+
borderRadius: 12,
|
|
103
|
+
width: 24,
|
|
104
|
+
height: 24,
|
|
105
|
+
alignItems: 'center',
|
|
106
|
+
justifyContent: 'center'
|
|
107
|
+
},
|
|
108
|
+
closeButton: {
|
|
109
|
+
right: 4
|
|
110
|
+
},
|
|
111
|
+
expandButton: {
|
|
112
|
+
left: 4
|
|
113
|
+
},
|
|
114
|
+
icon: {
|
|
115
|
+
color: '#fff',
|
|
116
|
+
fontSize: 12
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
//# sourceMappingURL=FloatingPlayer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useRef","Animated","PanResponder","Pressable","StyleSheet","Text","useWindowDimensions","FLOATING_SURFACE_ID","usePlayback","useVideoManager","VideoSurface","jsx","_jsx","jsxs","_jsxs","FloatingPlayer","width","manager","floating","s","screen","height","pan","ValueXY","x","y","current","responder","create","onMoveShouldSetPanResponder","_e","g","Math","abs","dx","dy","onPanResponderGrant","extractOffset","onPanResponderMove","event","useNativeDriver","onPanResponderRelease","flattenOffset","View","style","styles","window","transform","getTranslateTransform","panHandlers","children","surfaceId","autoAttach","surface","button","closeButton","hitSlop","onPress","hideFloating","icon","expandButton","enterFullscreen","position","top","left","borderRadius","overflow","backgroundColor","elevation","shadowColor","shadowOpacity","shadowRadius","shadowOffset","flex","alignItems","justifyContent","right","color","fontSize"],"sourceRoot":"../../../src","sources":["components/FloatingPlayer.tsx"],"mappings":"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","ignoreList":[]}
|