Skip to content
This repository was archived by the owner on Sep 7, 2021. It is now read-only.

Commit 2563d7a

Browse files
committed
[[ Player ]] Update iOS play command implementation to use AVKit
This patch updates the implementation of the `play` command to replace the MVMoviePlayerController based implementation (removed from iOS 13.2) with one based on AVPlayer
1 parent dbd5eba commit 2563d7a

File tree

1 file changed

+107
-68
lines changed

1 file changed

+107
-68
lines changed

engine/src/mbliphonevideo.mm

Lines changed: 107 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@
3939

4040
#import <Foundation/Foundation.h>
4141
#import <UIKit/UIKit.h>
42-
#import <MediaPlayer/MPMoviePlayerController.h>
43-
#import <MediaPlayer/MPMoviePlayerViewController.h>
42+
#import <AVKit/AVKit.h>
4443

4544
////////////////////////////////////////////////////////////////////////////////
4645

@@ -49,56 +48,75 @@
4948

5049
////////////////////////////////////////////////////////////////////////////////
5150

52-
MPMoviePlayerViewController *g_movie_player = nil;
53-
54-
////////////////////////////////////////////////////////////////////////////////
55-
56-
static NSObject *s_movie_player_delegate = nil;
51+
static AVPlayerViewController *s_movie_player = nil;
5752

5853
////////////////////////////////////////////////////////////////////////////////
5954

6055
@interface com_runrev_livecode_MCFullscreenMovieDelegate : NSObject
6156
{
6257
bool m_running;
58+
bool m_looping;
59+
CMTime m_start_time;
60+
AVPlayer *m_player;
6361
UIControl *m_overlay;
6462
}
6563

66-
- (id)initWithPlayer;
64+
- (id)initWithPlayer:(AVPlayer*)player startTime:(CMTime)startTime looping:(BOOL)looping;
6765
- (void)dealloc;
6866

6967
- (void)begin: (bool)p_add_overlay;
7068
- (void)end;
7169
- (void)stop;
7270
- (bool)isRunning;
7371

74-
- (void)movieFinished: (NSNotification *) p_notification;
75-
- (void)moviePreloadFinished: (NSNotification *) p_notification;
72+
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context;
73+
- (void)playerItemDidPlayToEndTime:(NSNotification*)notification;
74+
- (void)playerItemFailedToPlayToEndTime:(NSNotification*)notification;
7675
- (void)movieWindowTouched: (UIControl*) p_sender;
7776

7877
@end
7978

79+
@compatibility_alias MCFullscreenMovieDelegate com_runrev_livecode_MCFullscreenMovieDelegate;
80+
81+
////////////////////////////////////////////////////////////////////////////////
82+
83+
static MCFullscreenMovieDelegate *s_movie_player_delegate = nil;
84+
85+
////////////////////////////////////////////////////////////////////////////////
86+
8087
@implementation com_runrev_livecode_MCFullscreenMovieDelegate
8188

8289
// AL-2014-09-09: [[ Bug 13354 ]] Replace deprecated MPMoviePlayerContentPreloadDidFinishNotification
83-
- (id)initWithPlayer: (MPMoviePlayerController *)p_player
90+
- (id)initWithPlayer:(AVPlayer *)p_player startTime:(CMTime)startTime looping:(BOOL)looping
8491
{
8592
self = [super init];
8693
if (self == nil)
8794
return nil;
8895

89-
[[NSNotificationCenter defaultCenter] addObserver:self
90-
selector:@selector(movieFinished:)
91-
name:MPMoviePlayerPlaybackDidFinishNotification
92-
object:nil];
93-
94-
[[NSNotificationCenter defaultCenter] addObserver:self
95-
selector:@selector(moviePreloadFinished:)
96-
name:MPMoviePlayerLoadStateDidChangeNotification
97-
object:p_player];
96+
m_player = [p_player retain];
9897

98+
[m_player.currentItem
99+
addObserver:self
100+
forKeyPath:@"status"
101+
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
102+
context:nil];
103+
[NSNotificationCenter.defaultCenter
104+
addObserver:self
105+
selector:@selector(playerItemDidPlayToEndTime:)
106+
name:AVPlayerItemDidPlayToEndTimeNotification
107+
object:m_player.currentItem];
108+
[NSNotificationCenter.defaultCenter
109+
addObserver:self
110+
selector:@selector(playerItemFailedToPlayToEndTime:)
111+
name:AVPlayerItemFailedToPlayToEndTimeNotification
112+
object:m_player.currentItem];
113+
99114
m_running = true;
100115
m_overlay = nil;
101116

117+
m_start_time = startTime;
118+
m_looping = looping;
119+
102120
s_movie_player_delegate = self;
103121

104122
return self;
@@ -107,7 +125,8 @@ - (id)initWithPlayer: (MPMoviePlayerController *)p_player
107125
- (void)dealloc
108126
{
109127
s_movie_player_delegate = nil;
110-
[[NSNotificationCenter defaultCenter] removeObserver: self];
128+
[NSNotificationCenter.defaultCenter removeObserver: self];
129+
[m_player release];
111130
[super dealloc];
112131
}
113132

@@ -149,23 +168,37 @@ - (bool)isRunning
149168

150169
//////////
151170

152-
- (void)movieFinished: (NSNotification *) p_notification
171+
- (void)playerItemDidPlayToEndTime:(NSNotification *)notification
153172
{
173+
if (m_looping)
174+
{
175+
[m_player seekToTime:m_start_time];
176+
return;
177+
}
178+
154179
m_running = false;
155180

156181
// MW-2011-08-16: [[ Wait ]] Tell the wait to exit (our wait has anyevent == True).
157182
MCscreen -> pingwait();
158183
}
159184

160-
- (void)moviePreloadFinished: (NSNotification *) p_notification
185+
- (void)playerItemFailedToPlayToEndTime:(NSNotification *)notification
161186
{
162-
// AL-2014-09-09: [[ Bug 13354 ]] Replace deprecated MPMoviePlayerContentPreloadDidFinishNotification
163-
if ([[p_notification object] loadState] & MPMovieLoadStateUnknown)
187+
m_running = false;
188+
MCscreen->pingwait();
189+
}
190+
191+
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
192+
{
193+
if ([keyPath isEqualToString:@"status"])
164194
{
165-
m_running = false;
166-
167-
// MW-2011-08-16: [[ Wait ]] Tell the wait to exit (our wait has anyevent == True).
168-
MCscreen -> pingwait();
195+
if (((AVPlayerItem*)object).status == AVPlayerItemStatusFailed)
196+
{
197+
m_running = false;
198+
199+
// MW-2011-08-16: [[ Wait ]] Tell the wait to exit (our wait has anyevent == True).
200+
MCscreen -> pingwait();
201+
}
169202
}
170203
}
171204

@@ -177,25 +210,29 @@ - (void)movieWindowTouched: (UIControl*) p_sender
177210

178211
@end
179212

180-
static void configure_playback_range(MPMoviePlayerController *p_player)
213+
static void configure_playback_range(AVPlayerViewController *p_player, CMTime &r_start_time)
181214
{
182215
if (!MCtemplateplayer -> getflag(F_PLAY_SELECTION))
216+
{
217+
r_start_time = kCMTimeZero;
183218
return;
219+
}
184220

185221
uint32_t t_start_time, t_end_time;
186222
t_start_time = MCtemplateplayer -> getstarttime();
187223
t_end_time = MCtemplateplayer -> getendtime();
188-
[p_player setInitialPlaybackTime: t_start_time / 1000.0];
189-
[p_player setEndPlaybackTime: t_end_time / 1000.0];
224+
p_player.player.currentItem.forwardPlaybackEndTime = CMTimeMake(t_end_time, 1000);
225+
r_start_time = CMTimeMake(t_start_time, 1000);
190226
}
191227

192228
struct play_fullscreen_t
193229
{
194230
NSURL *url;
195231
bool looping;
196232
bool with_controller;
197-
MPMoviePlayerController *movie_player;
198-
com_runrev_livecode_MCFullscreenMovieDelegate *delegate;
233+
AVPlayerViewController *movie_player;
234+
MCFullscreenMovieDelegate *delegate;
235+
CMTime start_time;
199236
};
200237

201238
static void play_fullscreen_movie_prewait(void *p_context)
@@ -205,41 +242,49 @@ static void play_fullscreen_movie_prewait(void *p_context)
205242

206243
// MM-2011-12-09: [[ Bug 9892 ]] Destroy previous movie player. Fixes bug with iOS 5 where
207244
//showController is ignored on second running.
208-
if (g_movie_player != nil)
245+
if (s_movie_player != nil)
209246
{
210-
[g_movie_player release];
211-
g_movie_player = nil;
247+
[s_movie_player release];
248+
s_movie_player = nil;
212249
}
213250

214251
// Make sure we have a movie player view controller with correct url
215-
g_movie_player = [[MPMoviePlayerViewController alloc] initWithContentURL: ctxt -> url];
252+
s_movie_player = [[AVPlayerViewController alloc] init];
253+
s_movie_player.player = [AVPlayer playerWithURL:ctxt->url];
216254

217-
ctxt -> movie_player = [g_movie_player moviePlayer];
255+
ctxt->movie_player = s_movie_player;
218256

219-
[[ctxt -> movie_player view] setHidden: NO];
220-
[[ctxt -> movie_player view] setOpaque: YES];
221-
[[ctxt -> movie_player view] setAlpha: 1.0f];
222-
[[ctxt -> movie_player backgroundView] setBackgroundColor: [UIColor blackColor]];
223-
[ctxt -> movie_player setFullscreen: YES];
224-
[ctxt -> movie_player setScalingMode: MPMovieScalingModeAspectFit];
225-
[ctxt -> movie_player setControlStyle: (ctxt -> with_controller ? MPMovieControlStyleFullscreen : MPMovieControlStyleNone)];
226-
[ctxt -> movie_player setUseApplicationAudioSession: YES];
227-
[ctxt -> movie_player setInitialPlaybackTime: -1];
228-
[ctxt -> movie_player setEndPlaybackTime: -1];
229-
[ctxt -> movie_player setShouldAutoplay: YES];
257+
ctxt->movie_player.view.hidden = NO;
258+
ctxt->movie_player.view.opaque = YES;
259+
ctxt->movie_player.view.alpha = 1.0f;
260+
ctxt->movie_player.view.backgroundColor = [UIColor blackColor];
261+
262+
ctxt->movie_player.videoGravity = AVLayerVideoGravityResizeAspect;
263+
ctxt->movie_player.showsPlaybackControls = ctxt->with_controller;
230264
if (ctxt -> looping)
231-
[ctxt -> movie_player setRepeatMode: MPMovieRepeatModeOne];
232-
if (MCmajorosversion >= 430)
233-
[ctxt -> movie_player setAllowsAirPlay: NO];
265+
ctxt->movie_player.player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
266+
267+
ctxt->movie_player.allowsPictureInPicturePlayback = NO;
268+
ctxt->movie_player.player.allowsExternalPlayback = NO;
234269

235-
configure_playback_range(ctxt -> movie_player);
270+
CMTime t_start_time;
271+
configure_playback_range(ctxt -> movie_player, t_start_time);
236272

237-
ctxt -> delegate = [[com_runrev_livecode_MCFullscreenMovieDelegate alloc] initWithPlayer:ctxt -> movie_player];
273+
ctxt -> delegate = [[MCFullscreenMovieDelegate alloc]
274+
initWithPlayer:ctxt -> movie_player.player
275+
startTime:t_start_time
276+
looping:ctxt->looping];
238277

239278
// Present the view controller and get the delegate to setup its overlay
240279
// if needed.
241-
[MCIPhoneGetViewController() presentModalViewController: g_movie_player animated: NO];
242-
[ctxt -> delegate begin: !ctxt -> with_controller];
280+
[MCIPhoneGetViewController()
281+
presentViewController:s_movie_player
282+
animated:NO
283+
completion:^{
284+
[s_movie_player.player seekToTime:t_start_time];
285+
[s_movie_player.player play];
286+
}];
287+
[ctxt -> delegate begin: !ctxt -> with_controller];
243288
}
244289

245290
static void play_fullscreen_movie_postwait(void *p_context)
@@ -249,14 +294,14 @@ static void play_fullscreen_movie_postwait(void *p_context)
249294

250295
// Clear up and overlay and dismiss the controller.
251296
[ctxt -> delegate end];
252-
[MCIPhoneGetViewController() dismissModalViewControllerAnimated: NO];
297+
[MCIPhoneGetViewController() dismissViewControllerAnimated:NO completion:^{}];
253298

254299
// Cleanup the delegate
255300
[ctxt -> delegate release];
256301

257302
// Make sure we reset the movie player to nothing.
258-
[ctxt -> movie_player stop];
259-
[ctxt -> movie_player setContentURL: [NSURL URLWithString: @""]];
303+
[ctxt->movie_player.player pause];
304+
[ctxt->movie_player.player replaceCurrentItemWithPlayerItem:[AVPlayerItem playerItemWithURL:[NSURL URLWithString:@""]]];
260305
}
261306

262307
static bool play_fullscreen_movie_new(NSURL *p_movie, bool p_looping, bool p_with_controller)
@@ -291,19 +336,13 @@ bool MCSystemPlayVideo(MCStringRef p_video)
291336
if (MCStringBeginsWithCString(p_video, (const char_t *)"http://", kMCStringOptionCompareExact)
292337
|| MCStringBeginsWithCString(p_video, (const char_t *)"https://", kMCStringOptionCompareExact))
293338
{
294-
CFStringRef cfstrurl = nil;
295-
/* UNCHECKED */ MCStringConvertToCFStringRef(p_video, cfstrurl);
296-
t_url = [NSURL URLWithString: (NSString *)cfstrurl];
297-
CFRelease(cfstrurl);
339+
t_url = [NSURL URLWithString:MCStringConvertToAutoreleasedNSString(p_video)];
298340
}
299341
else
300342
{
301343
MCAutoStringRef t_path;
302-
CFStringRef cfstrpath = nil;
303344
/* UNCHECKED */ MCS_resolvepath(p_video, &t_path);
304-
/* UNCHECKED */ MCStringConvertToCFStringRef(*t_path, cfstrpath);
305-
t_url = [NSURL fileURLWithPath: (NSString *)cfstrpath];
306-
CFRelease(cfstrpath);
345+
t_url = [NSURL fileURLWithPath:MCStringConvertToAutoreleasedNSString(*t_path)];
307346
}
308347

309348
if (t_url == nil)

0 commit comments

Comments
 (0)