@cinecrew/cinecrew-player 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/CineCrewPlayer.podspec +22 -0
- package/LICENSE +21 -0
- package/NOTICE +6 -0
- package/README.md +574 -0
- package/android/build.gradle +48 -0
- package/android/proguard-rules.pro +29 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/yuanzhou/vlc/ReactVlcPlayerPackage.java +28 -0
- package/android/src/main/java/com/yuanzhou/vlc/vlcplayer/ReactVlcPlayerView.java +988 -0
- package/android/src/main/java/com/yuanzhou/vlc/vlcplayer/ReactVlcPlayerViewManager.java +208 -0
- package/android/src/main/java/com/yuanzhou/vlc/vlcplayer/VideoEventEmitter.java +136 -0
- package/android/src/main/res/values/strings.xml +3 -0
- package/app.plugin.cjs +1 -0
- package/assets/cinecrew-player-logo.svg +44 -0
- package/package.json +132 -0
- package/packages/react-native-vlc-media-player/LICENSE +21 -0
- package/packages/react-native-vlc-media-player/VLCPlayer.js +207 -0
- package/packages/react-native-vlc-media-player/expo/android/withGradleTasks.cjs +49 -0
- package/packages/react-native-vlc-media-player/expo/ios/withMobileVlcKit.cjs +28 -0
- package/packages/react-native-vlc-media-player/expo/withVlcMediaPlayer.cjs +10 -0
- package/packages/react-native-vlc-media-player/index.d.ts +354 -0
- package/packages/react-native-vlc-media-player/ios/RCTVLCPlayer/RCTVLCPlayer.h +36 -0
- package/packages/react-native-vlc-media-player/ios/RCTVLCPlayer/RCTVLCPlayer.m +635 -0
- package/packages/react-native-vlc-media-player/ios/RCTVLCPlayer/RCTVLCPlayerManager.h +5 -0
- package/packages/react-native-vlc-media-player/ios/RCTVLCPlayer/RCTVLCPlayerManager.m +100 -0
- package/src/native/InlineLivePlayer.js +321 -0
- package/src/native/MediaPlayerView.js +3093 -0
- package/src/native/customization.js +44 -0
- package/src/native/index.js +107 -0
- package/src/native/media/ElectronVideoPlayer.js +288 -0
- package/src/native/media/EmojiPickerModal.js +30 -0
- package/src/native/media/LiveChatDrawer.js +1177 -0
- package/src/native/media/LiveRecordingOverlay.js +319 -0
- package/src/native/media/WebVideoPlayer.js +320 -0
- package/src/native/media/YouTubeVideoPlayer.native.js +120 -0
- package/src/native/media/YouTubeVideoPlayer.web.js +169 -0
- package/src/native/media/player/AudioOnlyView.js +337 -0
- package/src/native/media/player/CenterControls.js +94 -0
- package/src/native/media/player/ExoVideoFallback.js +329 -0
- package/src/native/media/player/PipOverlay.js +1 -0
- package/src/native/media/player/PlayerBottomBar.js +295 -0
- package/src/native/media/player/PlayerTopBar.js +255 -0
- package/src/native/media/player/VLCBoundary.js +41 -0
- package/src/native/media/player/VerticalIndicator.js +184 -0
- package/src/native/media/player/index.js +9 -0
- package/src/native/media/player/playerConstants.js +159 -0
- package/src/native/media/web/useWebAc3AudioPlayback.native.js +6 -0
- package/src/native/media/web/useWebAc3AudioPlayback.web.js +219 -0
- package/src/native/media/web/useWebHlsPlayback.native.js +3 -0
- package/src/native/media/web/useWebHlsPlayback.web.js +66 -0
- package/src/native/media/web/useWebMediaSession.js +90 -0
- package/src/native/media/web/useWebMpegTsPlayback.native.js +3 -0
- package/src/native/media/web/useWebMpegTsPlayback.web.js +356 -0
- package/src/native/media/web/useWebVideoAspectRatio.js +52 -0
- package/src/native/media/web/webPlaybackErrors.js +4 -0
- package/src/utils/layoutUtils.js +12 -0
- package/src/utils/mediaUtils.js +7 -0
- package/src/utils/recording.js +8 -0
- package/src/utils/runtimePlatform.js +19 -0
- package/src/utils/sourceUtils.js +99 -0
- package/src/utils/youtubeHtml.js +34 -0
- package/src/web/index.js +1129 -0
- package/src/web/styles.css +378 -0
- package/types/index.d.ts +268 -0
- package/vendor/mpegts.js/LICENSE +202 -0
- package/vendor/mpegts.js/mpegts.js +3 -0
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
#import "React/RCTConvert.h"
|
|
2
|
+
#import "RCTVLCPlayer.h"
|
|
3
|
+
#import "React/RCTBridgeModule.h"
|
|
4
|
+
#import "React/RCTEventDispatcher.h"
|
|
5
|
+
#import "React/UIView+React.h"
|
|
6
|
+
#if TARGET_OS_TV
|
|
7
|
+
#import <TVVLCKit/TVVLCKit.h>
|
|
8
|
+
#else
|
|
9
|
+
#import <MobileVLCKit/MobileVLCKit.h>
|
|
10
|
+
#endif
|
|
11
|
+
#import <AVFoundation/AVFoundation.h>
|
|
12
|
+
static NSString *const statusKeyPath = @"status";
|
|
13
|
+
static NSString *const playbackLikelyToKeepUpKeyPath = @"playbackLikelyToKeepUp";
|
|
14
|
+
static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
|
|
15
|
+
static NSString *const readyForDisplayKeyPath = @"readyForDisplay";
|
|
16
|
+
static NSString *const playbackRate = @"rate";
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
#if !defined(DEBUG) || !(TARGET_IPHONE_SIMULATOR)
|
|
20
|
+
#define NSLog(...)
|
|
21
|
+
#endif
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@implementation RCTVLCPlayer
|
|
25
|
+
{
|
|
26
|
+
|
|
27
|
+
/* Required to publish events */
|
|
28
|
+
RCTEventDispatcher *_eventDispatcher;
|
|
29
|
+
VLCMediaPlayer *_player;
|
|
30
|
+
|
|
31
|
+
NSDictionary * _videoInfo;
|
|
32
|
+
NSString * _subtitleUri;
|
|
33
|
+
|
|
34
|
+
BOOL _paused;
|
|
35
|
+
BOOL _autoplay;
|
|
36
|
+
BOOL _acceptInvalidCertificates;
|
|
37
|
+
BOOL _playInBackground;
|
|
38
|
+
BOOL _playWhenInactive;
|
|
39
|
+
NSString *_sourceURI;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
|
43
|
+
{
|
|
44
|
+
if ((self = [super init])) {
|
|
45
|
+
_eventDispatcher = eventDispatcher;
|
|
46
|
+
|
|
47
|
+
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
48
|
+
selector:@selector(applicationWillResignActive:)
|
|
49
|
+
name:UIApplicationWillResignActiveNotification
|
|
50
|
+
object:nil];
|
|
51
|
+
|
|
52
|
+
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
53
|
+
selector:@selector(applicationDidEnterBackground:)
|
|
54
|
+
name:UIApplicationDidEnterBackgroundNotification
|
|
55
|
+
object:nil];
|
|
56
|
+
|
|
57
|
+
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
58
|
+
selector:@selector(applicationWillEnterForeground:)
|
|
59
|
+
name:UIApplicationWillEnterForegroundNotification
|
|
60
|
+
object:nil];
|
|
61
|
+
|
|
62
|
+
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
63
|
+
selector:@selector(applicationDidBecomeActive:)
|
|
64
|
+
name:UIApplicationDidBecomeActiveNotification
|
|
65
|
+
object:nil];
|
|
66
|
+
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return self;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
- (void)applicationDidEnterBackground:(NSNotification *)notification
|
|
73
|
+
{
|
|
74
|
+
if (!_paused && (_playInBackground || _playWhenInactive)) {
|
|
75
|
+
if (_player) {
|
|
76
|
+
_player.drawable = nil;
|
|
77
|
+
}
|
|
78
|
+
[self configureAudioSession];
|
|
79
|
+
[self play];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
- (void)applicationWillResignActive:(NSNotification *)notification
|
|
84
|
+
{
|
|
85
|
+
if (!_paused && (_playInBackground || _playWhenInactive)) {
|
|
86
|
+
if (_player) {
|
|
87
|
+
_player.drawable = nil;
|
|
88
|
+
}
|
|
89
|
+
[self configureAudioSession];
|
|
90
|
+
[self play];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
- (void)applicationWillEnterForeground:(NSNotification *)notification
|
|
95
|
+
{
|
|
96
|
+
if (_player && _player.drawable != self) {
|
|
97
|
+
_player.drawable = self;
|
|
98
|
+
}
|
|
99
|
+
[self configureAudioSession];
|
|
100
|
+
if (!_paused && (_playInBackground || _playWhenInactive)) {
|
|
101
|
+
[self play];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
- (void)applicationDidBecomeActive:(NSNotification *)notification
|
|
106
|
+
{
|
|
107
|
+
if (_player && _player.drawable != self) {
|
|
108
|
+
_player.drawable = self;
|
|
109
|
+
}
|
|
110
|
+
if (!_paused && (_playInBackground || _playWhenInactive)) {
|
|
111
|
+
[self play];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
- (void)play
|
|
116
|
+
{
|
|
117
|
+
if (_player) {
|
|
118
|
+
[_player play];
|
|
119
|
+
_paused = NO;
|
|
120
|
+
if (_playInBackground || _playWhenInactive) {
|
|
121
|
+
[self configureAudioSession];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
- (void)pause
|
|
127
|
+
{
|
|
128
|
+
if (_player) {
|
|
129
|
+
[_player pause];
|
|
130
|
+
_paused = YES;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
- (void)configureAudioSession
|
|
135
|
+
{
|
|
136
|
+
#if !TARGET_OS_TV
|
|
137
|
+
AVAudioSession *session = [AVAudioSession sharedInstance];
|
|
138
|
+
NSError *error = nil;
|
|
139
|
+
if (_playInBackground || _playWhenInactive) {
|
|
140
|
+
[session setCategory:AVAudioSessionCategoryPlayback
|
|
141
|
+
mode:AVAudioSessionModeDefault
|
|
142
|
+
options:AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryOptionAllowAirPlay
|
|
143
|
+
error:&error];
|
|
144
|
+
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
|
|
145
|
+
} else {
|
|
146
|
+
[session setCategory:AVAudioSessionCategoryPlayback
|
|
147
|
+
mode:AVAudioSessionModeMoviePlayback
|
|
148
|
+
options:AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryOptionAllowAirPlay
|
|
149
|
+
error:&error];
|
|
150
|
+
}
|
|
151
|
+
[session setActive:YES error:&error];
|
|
152
|
+
#endif
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
- (void)setSource:(NSDictionary *)source
|
|
156
|
+
{
|
|
157
|
+
NSString* uriString = [source objectForKey:@"uri"];
|
|
158
|
+
if (_player && uriString.length > 0 && [_sourceURI isEqualToString:uriString]) {
|
|
159
|
+
// React Native can resend an equivalent source dictionary whenever a
|
|
160
|
+
// control prop changes. Keep the existing media item and its position.
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (_player) {
|
|
165
|
+
[self _release];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
_videoInfo = nil;
|
|
169
|
+
|
|
170
|
+
// [bavv edit start]
|
|
171
|
+
_sourceURI = [uriString copy];
|
|
172
|
+
NSURL* uri = [NSURL URLWithString:uriString];
|
|
173
|
+
int initType = [source objectForKey:@"initType"];
|
|
174
|
+
NSDictionary* initOptions = [source objectForKey:@"initOptions"];
|
|
175
|
+
|
|
176
|
+
// Get acceptInvalidCertificates from source
|
|
177
|
+
_acceptInvalidCertificates = [[source objectForKey:@"acceptInvalidCertificates"] boolValue];
|
|
178
|
+
NSLog(@"iOS: Set acceptInvalidCertificates to %@", _acceptInvalidCertificates ? @"YES" : @"NO");
|
|
179
|
+
|
|
180
|
+
if (initType == 1) {
|
|
181
|
+
_player = [[VLCMediaPlayer alloc] init];
|
|
182
|
+
} else {
|
|
183
|
+
_player = [[VLCMediaPlayer alloc] initWithOptions:initOptions];
|
|
184
|
+
}
|
|
185
|
+
_player.delegate = self;
|
|
186
|
+
_player.drawable = self;
|
|
187
|
+
// [bavv edit end]
|
|
188
|
+
|
|
189
|
+
VLCLibrary *library = _player.libraryInstance;
|
|
190
|
+
|
|
191
|
+
VLCConsoleLogger *consoleLogger = [[VLCConsoleLogger alloc] init];
|
|
192
|
+
consoleLogger.level = kVLCLogLevelDebug;
|
|
193
|
+
library.loggers = @[consoleLogger];
|
|
194
|
+
|
|
195
|
+
// Create dialog provider with custom UI to handle dialogs programmatically
|
|
196
|
+
self.dialogProvider = [[VLCDialogProvider alloc] initWithLibrary:library customUI:YES];
|
|
197
|
+
self.dialogProvider.customRenderer = self;
|
|
198
|
+
_player.media = [VLCMedia mediaWithURL:uri];
|
|
199
|
+
|
|
200
|
+
if (_autoplay)
|
|
201
|
+
[_player play];
|
|
202
|
+
|
|
203
|
+
[self configureAudioSession];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
- (void)setPlayInBackground:(BOOL)value
|
|
207
|
+
{
|
|
208
|
+
_playInBackground = value;
|
|
209
|
+
if (value) [self configureAudioSession];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
- (void)setPlayWhenInactive:(BOOL)value
|
|
213
|
+
{
|
|
214
|
+
_playWhenInactive = value;
|
|
215
|
+
if (value) [self configureAudioSession];
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
- (void)setAutoplay:(BOOL)autoplay
|
|
219
|
+
{
|
|
220
|
+
_autoplay = autoplay;
|
|
221
|
+
|
|
222
|
+
if (autoplay)
|
|
223
|
+
[self play];
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
- (void)setPaused:(BOOL)paused
|
|
227
|
+
{
|
|
228
|
+
_paused = paused;
|
|
229
|
+
|
|
230
|
+
if (!paused) {
|
|
231
|
+
[self play];
|
|
232
|
+
} else {
|
|
233
|
+
[self pause];
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
- (void)setResume:(BOOL)resume
|
|
238
|
+
{
|
|
239
|
+
if (resume) {
|
|
240
|
+
[self play];
|
|
241
|
+
} else {
|
|
242
|
+
[self pause];
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
- (void)setSubtitleUri:(NSString *)subtitleUri
|
|
247
|
+
{
|
|
248
|
+
NSURL *url = [NSURL URLWithString:subtitleUri];
|
|
249
|
+
|
|
250
|
+
if (url.absoluteString.length != 0 && _player) {
|
|
251
|
+
_subtitleUri = url;
|
|
252
|
+
[_player addPlaybackSlave:_subtitleUri type:VLCMediaPlaybackSlaveTypeSubtitle enforce:YES];
|
|
253
|
+
} else {
|
|
254
|
+
NSLog(@"Invalid subtitle URI: %@", subtitleUri);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ==== player delegate methods ====
|
|
259
|
+
|
|
260
|
+
- (void)mediaPlayerTimeChanged:(NSNotification *)aNotification
|
|
261
|
+
{
|
|
262
|
+
[self updateVideoProgress];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
- (void)mediaPlayerStateChanged:(NSNotification *)aNotification
|
|
266
|
+
{
|
|
267
|
+
|
|
268
|
+
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
269
|
+
NSLog(@"userInfo %@",[aNotification userInfo]);
|
|
270
|
+
NSLog(@"standardUserDefaults %@",defaults);
|
|
271
|
+
if (_player) {
|
|
272
|
+
VLCMediaPlayerState state = _player.state;
|
|
273
|
+
switch (state) {
|
|
274
|
+
case VLCMediaPlayerStateOpening:
|
|
275
|
+
NSLog(@"VLCMediaPlayerStateOpening %i", _player.numberOfAudioTracks);
|
|
276
|
+
self.onVideoOpen(@{
|
|
277
|
+
@"target": self.reactTag
|
|
278
|
+
});
|
|
279
|
+
self.onVideoLoadStart(@{
|
|
280
|
+
@"target": self.reactTag
|
|
281
|
+
});
|
|
282
|
+
break;
|
|
283
|
+
case VLCMediaPlayerStatePaused:
|
|
284
|
+
_paused = YES;
|
|
285
|
+
NSLog(@"VLCMediaPlayerStatePaused %i", _player.numberOfAudioTracks);
|
|
286
|
+
self.onVideoPaused(@{
|
|
287
|
+
@"target": self.reactTag
|
|
288
|
+
});
|
|
289
|
+
break;
|
|
290
|
+
case VLCMediaPlayerStateStopped:
|
|
291
|
+
NSLog(@"VLCMediaPlayerStateStopped %i", _player.numberOfAudioTracks);
|
|
292
|
+
self.onVideoStopped(@{
|
|
293
|
+
@"target": self.reactTag
|
|
294
|
+
});
|
|
295
|
+
break;
|
|
296
|
+
case VLCMediaPlayerStateBuffering:
|
|
297
|
+
NSLog(@"VLCMediaPlayerStateBuffering %i", _player.numberOfAudioTracks);
|
|
298
|
+
self.onVideoBuffering(@{
|
|
299
|
+
@"target": self.reactTag
|
|
300
|
+
});
|
|
301
|
+
break;
|
|
302
|
+
case VLCMediaPlayerStatePlaying:
|
|
303
|
+
_paused = NO;
|
|
304
|
+
NSLog(@"VLCMediaPlayerStatePlaying %i", _player.numberOfAudioTracks);
|
|
305
|
+
self.onVideoPlaying(@{
|
|
306
|
+
@"target": self.reactTag,
|
|
307
|
+
@"seekable": [NSNumber numberWithBool:[_player isSeekable]],
|
|
308
|
+
@"duration":[NSNumber numberWithInt:[_player.media.length intValue]]
|
|
309
|
+
});
|
|
310
|
+
break;
|
|
311
|
+
case VLCMediaPlayerStateEnded:
|
|
312
|
+
NSLog(@"VLCMediaPlayerStateEnded %i", _player.numberOfAudioTracks);
|
|
313
|
+
int currentTime = [[_player time] intValue];
|
|
314
|
+
int remainingTime = [[_player remainingTime] intValue];
|
|
315
|
+
int duration = [_player.media.length intValue];
|
|
316
|
+
|
|
317
|
+
self.onVideoEnded(@{
|
|
318
|
+
@"target": self.reactTag,
|
|
319
|
+
@"currentTime": [NSNumber numberWithInt:currentTime],
|
|
320
|
+
@"remainingTime": [NSNumber numberWithInt:remainingTime],
|
|
321
|
+
@"duration":[NSNumber numberWithInt:duration],
|
|
322
|
+
@"position":[NSNumber numberWithFloat:_player.position]
|
|
323
|
+
});
|
|
324
|
+
break;
|
|
325
|
+
case VLCMediaPlayerStateError:
|
|
326
|
+
NSLog(@"VLCMediaPlayerStateError %i", _player.numberOfAudioTracks);
|
|
327
|
+
// This callback doesn't have any data about the error, we need to rely on the error dialog
|
|
328
|
+
[self _release];
|
|
329
|
+
break;
|
|
330
|
+
default:
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
// ===== media delegate methods =====
|
|
338
|
+
|
|
339
|
+
- (void)mediaDidFinishParsing:(VLCMedia *)aMedia {
|
|
340
|
+
NSLog(@"VLCMediaDidFinishParsing %i", _player.numberOfAudioTracks);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
- (void)mediaMetaDataDidChange:(VLCMedia *)aMedia{
|
|
344
|
+
NSLog(@"VLCMediaMetaDataDidChange %i", _player.numberOfAudioTracks);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
- (void)mediaPlayer:(VLCMediaPlayer *)player recordingStoppedAtPath:(NSString *)path {
|
|
348
|
+
if (self.onRecordingState) {
|
|
349
|
+
self.onRecordingState(@{
|
|
350
|
+
@"target": self.reactTag,
|
|
351
|
+
@"isRecording": @NO,
|
|
352
|
+
@"recordPath": path ?: [NSNull null]
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// ===================================
|
|
358
|
+
|
|
359
|
+
- (void)updateVideoProgress
|
|
360
|
+
{
|
|
361
|
+
if (_player && !_paused) {
|
|
362
|
+
int currentTime = [[_player time] intValue];
|
|
363
|
+
int remainingTime = [[_player remainingTime] intValue];
|
|
364
|
+
int duration = [_player.media.length intValue];
|
|
365
|
+
[self updateVideoInfo];
|
|
366
|
+
|
|
367
|
+
self.onVideoProgress(@{
|
|
368
|
+
@"target": self.reactTag,
|
|
369
|
+
@"currentTime": [NSNumber numberWithInt:currentTime],
|
|
370
|
+
@"remainingTime": [NSNumber numberWithInt:remainingTime],
|
|
371
|
+
@"duration":[NSNumber numberWithInt:duration],
|
|
372
|
+
@"position":[NSNumber numberWithFloat:_player.position],
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
- (void)updateVideoInfo
|
|
378
|
+
{
|
|
379
|
+
NSMutableDictionary *info = [NSMutableDictionary new];
|
|
380
|
+
info[@"duration"] = _player.media.length.value;
|
|
381
|
+
int i;
|
|
382
|
+
if (_player.videoSize.width > 0) {
|
|
383
|
+
info[@"videoSize"] = @{
|
|
384
|
+
@"width": @(_player.videoSize.width),
|
|
385
|
+
@"height": @(_player.videoSize.height)
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (_player.numberOfAudioTracks > 0) {
|
|
390
|
+
NSMutableArray *tracks = [NSMutableArray new];
|
|
391
|
+
for (i = 0; i < _player.numberOfAudioTracks; i++) {
|
|
392
|
+
if (_player.audioTrackIndexes[i] && _player.audioTrackNames[i]) {
|
|
393
|
+
[tracks addObject: @{
|
|
394
|
+
@"id": _player.audioTrackIndexes[i],
|
|
395
|
+
@"name": _player.audioTrackNames[i]
|
|
396
|
+
}];
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
info[@"audioTracks"] = tracks;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (_player.numberOfSubtitlesTracks > 0) {
|
|
403
|
+
NSMutableArray *tracks = [NSMutableArray new];
|
|
404
|
+
for (i = 0; i < _player.numberOfSubtitlesTracks; i++) {
|
|
405
|
+
if (_player.videoSubTitlesIndexes[i] && _player.videoSubTitlesNames[i]) {
|
|
406
|
+
[tracks addObject: @{
|
|
407
|
+
@"id": _player.videoSubTitlesIndexes[i],
|
|
408
|
+
@"name": _player.videoSubTitlesNames[i]
|
|
409
|
+
}];
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
info[@"textTracks"] = tracks;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (![_videoInfo isEqualToDictionary:info]) {
|
|
416
|
+
self.onVideoLoad(info);
|
|
417
|
+
_videoInfo = info;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
- (void)jumpBackward:(int)interval
|
|
422
|
+
{
|
|
423
|
+
if (interval>=0 && interval <= [_player.media.length intValue])
|
|
424
|
+
[_player jumpBackward:interval];
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
- (void)jumpForward:(int)interval
|
|
428
|
+
{
|
|
429
|
+
if (interval>=0 && interval <= [_player.media.length intValue])
|
|
430
|
+
[_player jumpForward:interval];
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
- (void)setSeek:(float)pos
|
|
434
|
+
{
|
|
435
|
+
if ([_player isSeekable]) {
|
|
436
|
+
if (pos>=0 && pos <= 1) {
|
|
437
|
+
[_player setPosition:pos];
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
- (void)setSnapshotPath:(NSString*)path
|
|
443
|
+
{
|
|
444
|
+
if (_player)
|
|
445
|
+
[_player saveVideoSnapshotAt:path withWidth:0 andHeight:0];
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
- (void)setRate:(float)rate
|
|
449
|
+
{
|
|
450
|
+
[_player setRate:rate];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
- (void)setAudioTrack:(int)track
|
|
454
|
+
{
|
|
455
|
+
[_player setCurrentAudioTrackIndex: track];
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
- (void)setTextTrack:(int)track
|
|
459
|
+
{
|
|
460
|
+
[_player setCurrentVideoSubTitleIndex:track];
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
- (void)startRecording:(NSString*)path
|
|
464
|
+
{
|
|
465
|
+
[_player startRecordingAtPath:path];
|
|
466
|
+
if (self.onRecordingState) {
|
|
467
|
+
self.onRecordingState(@{
|
|
468
|
+
@"target": self.reactTag,
|
|
469
|
+
@"isRecording": @YES
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
- (void)stopRecording
|
|
475
|
+
{
|
|
476
|
+
[_player stopRecording];
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
- (void)stopPlayer
|
|
480
|
+
{
|
|
481
|
+
[_player stop];
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
- (void)snapshot:(NSString*)path
|
|
485
|
+
{
|
|
486
|
+
@try {
|
|
487
|
+
if (_player) {
|
|
488
|
+
[_player saveVideoSnapshotAt:path withWidth:_player.videoSize.width andHeight:_player.videoSize.height];
|
|
489
|
+
self.onSnapshot(@{
|
|
490
|
+
@"success": @YES,
|
|
491
|
+
@"path": path,
|
|
492
|
+
@"error": [NSNull null],
|
|
493
|
+
@"target": self.reactTag
|
|
494
|
+
});
|
|
495
|
+
} else {
|
|
496
|
+
@throw [NSException exceptionWithName:@"PlayerNotInitialized" reason:@"Player is not initialized" userInfo:nil];
|
|
497
|
+
}
|
|
498
|
+
} @catch (NSException *e) {
|
|
499
|
+
NSLog(@"Error in snapshot: %@", e);
|
|
500
|
+
self.onSnapshot(@{
|
|
501
|
+
@"success": @NO,
|
|
502
|
+
@"error": [e description],
|
|
503
|
+
@"target": self.reactTag
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
- (void)setVideoAspectRatio:(NSString *)ratio{
|
|
509
|
+
if (!_player) return;
|
|
510
|
+
char *char_content = [ratio cStringUsingEncoding:NSASCIIStringEncoding];
|
|
511
|
+
// iOS VLC can ignore aspect-ratio changes on an active video output;
|
|
512
|
+
// resetting scaleFactor forces the video output to recompute geometry.
|
|
513
|
+
_player.scaleFactor = 0.0f;
|
|
514
|
+
[_player setVideoAspectRatio:char_content];
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
- (void)setMuted:(BOOL)value
|
|
518
|
+
{
|
|
519
|
+
if (_player) {
|
|
520
|
+
[[_player audio] setMuted:value];
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
#pragma mark - VLCCustomDialogRendererProtocol
|
|
525
|
+
|
|
526
|
+
- (void)showErrorWithTitle:(NSString *)title message:(NSString *)message {
|
|
527
|
+
NSLog(@"VLC Error - Title: %@, Message: %@", title, message);
|
|
528
|
+
if (self.onVideoError) {
|
|
529
|
+
self.onVideoError(@{
|
|
530
|
+
@"target": self.reactTag,
|
|
531
|
+
@"title": title ?: [NSNull null],
|
|
532
|
+
@"message": message ?: [NSNull null]
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
- (void)showLoginWithTitle:(NSString *)title
|
|
538
|
+
message:(NSString *)message
|
|
539
|
+
defaultUsername:(NSString *)username
|
|
540
|
+
askingForStorage:(BOOL)askingForStorage
|
|
541
|
+
withReference:(NSValue *)reference {
|
|
542
|
+
NSLog(@"VLC Login - Title: %@, Message: %@", title, message);
|
|
543
|
+
if (self.onVideoError) {
|
|
544
|
+
self.onVideoError(@{
|
|
545
|
+
@"target": self.reactTag,
|
|
546
|
+
@"title": title ?: [NSNull null],
|
|
547
|
+
@"message": message ?: [NSNull null]
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
- (void)showQuestionWithTitle:(NSString *)title
|
|
553
|
+
message:(NSString *)message
|
|
554
|
+
type:(VLCDialogQuestionType)type
|
|
555
|
+
cancelString:(NSString *)cancel
|
|
556
|
+
action1String:(NSString *)action1
|
|
557
|
+
action2String:(NSString *)action2
|
|
558
|
+
withReference:(NSValue *)reference {
|
|
559
|
+
|
|
560
|
+
NSLog(@"VLC Question - Title: %@, Message: %@", title, message);
|
|
561
|
+
|
|
562
|
+
// Check if this is a certificate-related dialog
|
|
563
|
+
NSString *fullText = [NSString stringWithFormat:@"%@ %@", title ?: @"", message ?: @""];
|
|
564
|
+
BOOL isCertificateDialog = [fullText containsString:@"certificate"] ||
|
|
565
|
+
[fullText containsString:@"SSL"] ||
|
|
566
|
+
[fullText containsString:@"TLS"] ||
|
|
567
|
+
[fullText containsString:@"cert"] ||
|
|
568
|
+
[fullText containsString:@"security"];
|
|
569
|
+
|
|
570
|
+
if (isCertificateDialog) {
|
|
571
|
+
if (_acceptInvalidCertificates) {
|
|
572
|
+
// Accept certificate (usually action1)
|
|
573
|
+
[self.dialogProvider postAction:1 forDialogReference:reference];
|
|
574
|
+
NSLog(@"iOS: Auto-accepted certificate dialog");
|
|
575
|
+
} else {
|
|
576
|
+
// Reject certificate (cancel)
|
|
577
|
+
[self.dialogProvider postAction:3 forDialogReference:reference]; // Cancel
|
|
578
|
+
NSLog(@"iOS: Rejected certificate dialog");
|
|
579
|
+
}
|
|
580
|
+
} else {
|
|
581
|
+
// For other dialogs, default to cancel
|
|
582
|
+
[self.dialogProvider postAction:3 forDialogReference:reference];
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
- (void)showProgressWithTitle:(NSString *)title
|
|
587
|
+
message:(NSString *)message
|
|
588
|
+
isIndeterminate:(BOOL)indeterminate
|
|
589
|
+
position:(float)position
|
|
590
|
+
cancelString:(NSString *)cancel
|
|
591
|
+
withReference:(NSValue *)reference {
|
|
592
|
+
NSLog(@"VLC Progress - Title: %@, Message: %@, Position: %.2f", title, message, position);
|
|
593
|
+
// Handle progress dialog if needed
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
- (void)updateProgressWithReference:(NSValue *)reference
|
|
597
|
+
message:(NSString *)message
|
|
598
|
+
position:(float)position {
|
|
599
|
+
// Update progress dialog
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
- (void)cancelDialogWithReference:(NSValue *)reference {
|
|
603
|
+
NSLog(@"VLC Dialog cancelled");
|
|
604
|
+
// Handle dialog cancellation
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
- (void)setAcceptInvalidCertificates:(BOOL)accept
|
|
608
|
+
{
|
|
609
|
+
_acceptInvalidCertificates = accept;
|
|
610
|
+
NSLog(@"iOS: Set acceptInvalidCertificates to %@", accept ? @"YES" : @"NO");
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
- (void)_release
|
|
614
|
+
{
|
|
615
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
616
|
+
|
|
617
|
+
if (_player.media)
|
|
618
|
+
[_player stop];
|
|
619
|
+
|
|
620
|
+
if (_player)
|
|
621
|
+
_player = nil;
|
|
622
|
+
|
|
623
|
+
_eventDispatcher = nil;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
#pragma mark - Lifecycle
|
|
628
|
+
- (void)removeFromSuperview
|
|
629
|
+
{
|
|
630
|
+
NSLog(@"removeFromSuperview");
|
|
631
|
+
[self _release];
|
|
632
|
+
[super removeFromSuperview];
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
@end
|