瀏覽代碼

iOS: Native video refactoring

Moved native video handling to separate view.
Sergey Minakov 5 年之前
父節點
當前提交
6ee74de9ee

+ 1 - 0
platform/iphone/SCsub

@@ -20,6 +20,7 @@ iphone_lib = [
     "godot_view_renderer.mm",
     "godot_view_gesture_recognizer.mm",
     "device_metrics.m",
+    "native_video_view.m",
 ]
 
 env_ios = env.Clone()

+ 5 - 4
platform/iphone/display_server_iphone.mm

@@ -35,6 +35,7 @@
 #import "device_metrics.h"
 #import "godot_view.h"
 #include "ios.h"
+#import "native_video_view.h"
 #include "os_iphone.h"
 #import "view_controller.h"
 
@@ -605,22 +606,22 @@ Error DisplayServerIPhone::native_video_play(String p_path, float p_volume, Stri
 }
 
 bool DisplayServerIPhone::native_video_is_playing() const {
-	return [AppDelegate.viewController isVideoPlaying];
+	return [AppDelegate.viewController.videoView isVideoPlaying];
 }
 
 void DisplayServerIPhone::native_video_pause() {
 	if (native_video_is_playing()) {
-		[AppDelegate.viewController pauseVideo];
+		[AppDelegate.viewController.videoView pauseVideo];
 	}
 }
 
 void DisplayServerIPhone::native_video_unpause() {
-	[AppDelegate.viewController unpauseVideo];
+	[AppDelegate.viewController.videoView unpauseVideo];
 };
 
 void DisplayServerIPhone::native_video_stop() {
 	if (native_video_is_playing()) {
-		[AppDelegate.viewController stopVideo];
+		[AppDelegate.viewController.videoView stopVideo];
 	}
 }
 

+ 41 - 0
platform/iphone/native_video_view.h

@@ -0,0 +1,41 @@
+/*************************************************************************/
+/*  native_video_view.h                                                  */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#import <UIKit/UIKit.h>
+
+@interface GodotNativeVideoView : UIView
+
+- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack;
+- (BOOL)isVideoPlaying;
+- (void)pauseVideo;
+- (void)unpauseVideo;
+- (void)stopVideo;
+
+@end

+ 260 - 0
platform/iphone/native_video_view.m

@@ -0,0 +1,260 @@
+/*************************************************************************/
+/*  native_video_view.m                                                  */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#import "native_video_view.h"
+#import <AVFoundation/AVFoundation.h>
+
+@interface GodotNativeVideoView ()
+
+@property(strong, nonatomic) AVAsset *avAsset;
+@property(strong, nonatomic) AVPlayerItem *avPlayerItem;
+@property(strong, nonatomic) AVPlayer *avPlayer;
+@property(strong, nonatomic) AVPlayerLayer *avPlayerLayer;
+@property(assign, nonatomic) CMTime videoCurrentTime;
+@property(assign, nonatomic) BOOL isVideoCurrentlyPlaying;
+
+@end
+
+@implementation GodotNativeVideoView
+
+- (instancetype)initWithFrame:(CGRect)frame {
+	self = [super initWithFrame:frame];
+
+	if (self) {
+		[self godot_commonInit];
+	}
+
+	return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder {
+	self = [super initWithCoder:coder];
+
+	if (self) {
+		[self godot_commonInit];
+	}
+
+	return self;
+}
+
+- (void)godot_commonInit {
+	self.isVideoCurrentlyPlaying = NO;
+	self.videoCurrentTime = kCMTimeZero;
+
+	[self observeVideoAudio];
+}
+
+- (void)observeVideoAudio {
+	printf("******** adding observer for sound routing changes\n");
+	[[NSNotificationCenter defaultCenter]
+			addObserver:self
+			   selector:@selector(audioRouteChangeListenerCallback:)
+				   name:AVAudioSessionRouteChangeNotification
+				 object:nil];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+	if (object == self.avPlayerItem && [keyPath isEqualToString:@"status"]) {
+		[self handleVideoOrPlayerStatus];
+	}
+
+	if (object == self.avPlayer && [keyPath isEqualToString:@"rate"]) {
+		[self handleVideoPlayRate];
+	}
+}
+
+// MARK: Video Audio
+
+- (void)audioRouteChangeListenerCallback:(NSNotification *)notification {
+	printf("*********** route changed!\n");
+	NSDictionary *interuptionDict = notification.userInfo;
+
+	NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
+
+	switch (routeChangeReason) {
+		case AVAudioSessionRouteChangeReasonNewDeviceAvailable: {
+			NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
+			NSLog(@"Headphone/Line plugged in");
+		} break;
+		case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: {
+			NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
+			NSLog(@"Headphone/Line was pulled. Resuming video play....");
+			if ([self isVideoPlaying]) {
+				dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
+					[self.avPlayer play]; // NOTE: change this line according your current player implementation
+					NSLog(@"resumed play");
+				});
+			}
+		} break;
+		case AVAudioSessionRouteChangeReasonCategoryChange: {
+			// called at start - also when other audio wants to play
+			NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
+		} break;
+	}
+}
+
+// MARK: Native Video Player
+
+- (void)handleVideoOrPlayerStatus {
+	if (self.avPlayerItem.status == AVPlayerItemStatusFailed || self.avPlayer.status == AVPlayerStatusFailed) {
+		[self stopVideo];
+	}
+
+	if (self.avPlayer.status == AVPlayerStatusReadyToPlay && self.avPlayerItem.status == AVPlayerItemStatusReadyToPlay && CMTimeCompare(self.videoCurrentTime, kCMTimeZero) == 0) {
+		//        NSLog(@"time: %@", self.video_current_time);
+		[self.avPlayer seekToTime:self.videoCurrentTime];
+		self.videoCurrentTime = kCMTimeZero;
+	}
+}
+
+- (void)handleVideoPlayRate {
+	NSLog(@"Player playback rate changed: %.5f", self.avPlayer.rate);
+	if ([self isVideoPlaying] && self.avPlayer.rate == 0.0 && !self.avPlayer.error) {
+		dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
+			[self.avPlayer play]; // NOTE: change this line according your current player implementation
+			NSLog(@"resumed play");
+		});
+
+		NSLog(@" . . . PAUSED (or just started)");
+	}
+}
+
+- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack {
+	self.avAsset = [AVAsset assetWithURL:[NSURL fileURLWithPath:filePath]];
+
+	self.avPlayerItem = [AVPlayerItem playerItemWithAsset:self.avAsset];
+	[self.avPlayerItem addObserver:self forKeyPath:@"status" options:0 context:nil];
+
+	self.avPlayer = [AVPlayer playerWithPlayerItem:self.avPlayerItem];
+	self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
+
+	[self.avPlayer addObserver:self forKeyPath:@"status" options:0 context:nil];
+	[[NSNotificationCenter defaultCenter]
+			addObserver:self
+			   selector:@selector(playerItemDidReachEnd:)
+				   name:AVPlayerItemDidPlayToEndTimeNotification
+				 object:[self.avPlayer currentItem]];
+
+	[self.avPlayer addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:0];
+
+	[self.avPlayerLayer setFrame:self.bounds];
+	[self.layer addSublayer:self.avPlayerLayer];
+	[self.avPlayer play];
+
+	AVMediaSelectionGroup *audioGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
+
+	NSMutableArray *allAudioParams = [NSMutableArray array];
+	for (id track in audioGroup.options) {
+		NSString *language = [[track locale] localeIdentifier];
+		NSLog(@"subtitle lang: %@", language);
+
+		if ([language isEqualToString:audioTrack]) {
+			AVMutableAudioMixInputParameters *audioInputParams = [AVMutableAudioMixInputParameters audioMixInputParameters];
+			[audioInputParams setVolume:videoVolume atTime:kCMTimeZero];
+			[audioInputParams setTrackID:[track trackID]];
+			[allAudioParams addObject:audioInputParams];
+
+			AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
+			[audioMix setInputParameters:allAudioParams];
+
+			[self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:audioGroup];
+			[self.avPlayer.currentItem setAudioMix:audioMix];
+
+			break;
+		}
+	}
+
+	AVMediaSelectionGroup *subtitlesGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
+	NSArray *useableTracks = [AVMediaSelectionGroup mediaSelectionOptionsFromArray:subtitlesGroup.options withoutMediaCharacteristics:[NSArray arrayWithObject:AVMediaCharacteristicContainsOnlyForcedSubtitles]];
+
+	for (id track in useableTracks) {
+		NSString *language = [[track locale] localeIdentifier];
+		NSLog(@"subtitle lang: %@", language);
+
+		if ([language isEqualToString:subtitleTrack]) {
+			[self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:subtitlesGroup];
+			break;
+		}
+	}
+
+	self.isVideoCurrentlyPlaying = YES;
+
+	return true;
+}
+
+- (BOOL)isVideoPlaying {
+	if (self.avPlayer.error) {
+		printf("Error during playback\n");
+	}
+	return (self.avPlayer.rate > 0 && !self.avPlayer.error);
+}
+
+- (void)pauseVideo {
+	self.videoCurrentTime = self.avPlayer.currentTime;
+	[self.avPlayer pause];
+	self.isVideoCurrentlyPlaying = NO;
+}
+
+- (void)unpauseVideo {
+	[self.avPlayer play];
+	self.isVideoCurrentlyPlaying = YES;
+}
+
+- (void)playerItemDidReachEnd:(NSNotification *)notification {
+	[self stopVideo];
+}
+
+- (void)finishPlayingVideo {
+	[self.avPlayer pause];
+	[self.avPlayerLayer removeFromSuperlayer];
+	self.avPlayerLayer = nil;
+
+	if (self.avPlayerItem) {
+		[self.avPlayerItem removeObserver:self forKeyPath:@"status"];
+		self.avPlayerItem = nil;
+	}
+
+	if (self.avPlayer) {
+		[self.avPlayer removeObserver:self forKeyPath:@"status"];
+		self.avPlayer = nil;
+	}
+
+	self.avAsset = nil;
+
+	self.isVideoCurrentlyPlaying = NO;
+}
+
+- (void)stopVideo {
+	[self finishPlayingVideo];
+
+	[self removeFromSuperview];
+}
+
+@end

+ 3 - 5
platform/iphone/view_controller.h

@@ -32,17 +32,15 @@
 #import <UIKit/UIKit.h>
 
 @class GodotView;
+@class GodotNativeVideoView;
 
 @interface ViewController : UIViewController <GKGameCenterControllerDelegate>
 
-- (GodotView *)godotView;
+@property(nonatomic, readonly, strong) GodotView *godotView;
+@property(nonatomic, readonly, strong) GodotNativeVideoView *videoView;
 
 // MARK: Native Video Player
 
 - (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack;
-- (BOOL)isVideoPlaying;
-- (void)pauseVideo;
-- (void)unpauseVideo;
-- (void)stopVideo;
 
 @end

+ 14 - 190
platform/iphone/view_controller.mm

@@ -33,6 +33,7 @@
 #include "display_server_iphone.h"
 #import "godot_view.h"
 #import "godot_view_renderer.h"
+#import "native_video_view.h"
 #include "os_iphone.h"
 
 #import <GameController/GameController.h>
@@ -40,16 +41,7 @@
 @interface ViewController ()
 
 @property(strong, nonatomic) GodotViewRenderer *renderer;
-
-// TODO: separate view to handle video
-// AVPlayer-related properties
-@property(strong, nonatomic) AVAsset *avAsset;
-@property(strong, nonatomic) AVPlayerItem *avPlayerItem;
-@property(strong, nonatomic) AVPlayer *avPlayer;
-@property(strong, nonatomic) AVPlayerLayer *avPlayerLayer;
-@property(assign, nonatomic) CMTime videoCurrentTime;
-@property(assign, nonatomic) BOOL isVideoCurrentlyPlaying;
-@property(assign, nonatomic) BOOL videoHasFoundError;
+@property(strong, nonatomic) GodotNativeVideoView *videoView;
 
 @end
 
@@ -90,9 +82,7 @@
 }
 
 - (void)godot_commonInit {
-	self.isVideoCurrentlyPlaying = NO;
-	self.videoCurrentTime = kCMTimeZero;
-	self.videoHasFoundError = false;
+	// Initialize view controller values.
 }
 
 - (void)didReceiveMemoryWarning {
@@ -104,7 +94,6 @@
 	[super viewDidLoad];
 
 	[self observeKeyboard];
-	[self observeAudio];
 
 	if (@available(iOS 11.0, *)) {
 		[self setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
@@ -125,28 +114,10 @@
 				 object:nil];
 }
 
-- (void)observeAudio {
-	printf("******** adding observer for sound routing changes\n");
-	[[NSNotificationCenter defaultCenter]
-			addObserver:self
-			   selector:@selector(audioRouteChangeListenerCallback:)
-				   name:AVAudioSessionRouteChangeNotification
-				 object:nil];
-}
-
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
-	if (object == self.avPlayerItem && [keyPath isEqualToString:@"status"]) {
-		[self handleVideoOrPlayerStatus];
-	}
-
-	if (object == self.avPlayer && [keyPath isEqualToString:@"rate"]) {
-		[self handleVideoPlayRate];
-	}
-}
-
 - (void)dealloc {
-	[self stopVideo];
+	[self.videoView stopVideo];
 
+	self.videoView = nil;
 	self.renderer = nil;
 
 	[[NSNotificationCenter defaultCenter] removeObserver:self];
@@ -228,166 +199,19 @@
 	}
 }
 
-// MARK: Audio
-
-- (void)audioRouteChangeListenerCallback:(NSNotification *)notification {
-	printf("*********** route changed!\n");
-	NSDictionary *interuptionDict = notification.userInfo;
-
-	NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
-
-	switch (routeChangeReason) {
-		case AVAudioSessionRouteChangeReasonNewDeviceAvailable: {
-			NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
-			NSLog(@"Headphone/Line plugged in");
-		} break;
-		case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: {
-			NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
-			NSLog(@"Headphone/Line was pulled. Resuming video play....");
-			if ([self isVideoPlaying]) {
-				dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
-					[self.avPlayer play]; // NOTE: change this line according your current player implementation
-					NSLog(@"resumed play");
-				});
-			}
-		} break;
-		case AVAudioSessionRouteChangeReasonCategoryChange: {
-			// called at start - also when other audio wants to play
-			NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
-		} break;
-	}
-}
-
 // MARK: Native Video Player
 
-- (void)handleVideoOrPlayerStatus {
-	if (self.avPlayerItem.status == AVPlayerItemStatusFailed || self.avPlayer.status == AVPlayerStatusFailed) {
-		[self stopVideo];
-		self.videoHasFoundError = true;
-	}
-
-	if (self.avPlayer.status == AVPlayerStatusReadyToPlay && self.avPlayerItem.status == AVPlayerItemStatusReadyToPlay && CMTimeCompare(self.videoCurrentTime, kCMTimeZero) == 0) {
-		//        NSLog(@"time: %@", self.video_current_time);
-		[self.avPlayer seekToTime:self.videoCurrentTime];
-		self.videoCurrentTime = kCMTimeZero;
-	}
-}
-
-- (void)handleVideoPlayRate {
-	NSLog(@"Player playback rate changed: %.5f", self.avPlayer.rate);
-	if ([self isVideoPlaying] && self.avPlayer.rate == 0.0 && !self.avPlayer.error) {
-		dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
-			[self.avPlayer play]; // NOTE: change this line according your current player implementation
-			NSLog(@"resumed play");
-		});
-
-		NSLog(@" . . . PAUSED (or just started)");
-	}
-}
-
 - (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack {
-	self.avAsset = [AVAsset assetWithURL:[NSURL fileURLWithPath:filePath]];
-
-	self.avPlayerItem = [AVPlayerItem playerItemWithAsset:self.avAsset];
-	[self.avPlayerItem addObserver:self forKeyPath:@"status" options:0 context:nil];
-
-	self.avPlayer = [AVPlayer playerWithPlayerItem:self.avPlayerItem];
-	self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
-
-	[self.avPlayer addObserver:self forKeyPath:@"status" options:0 context:nil];
-	[[NSNotificationCenter defaultCenter]
-			addObserver:self
-			   selector:@selector(playerItemDidReachEnd:)
-				   name:AVPlayerItemDidPlayToEndTimeNotification
-				 object:[self.avPlayer currentItem]];
-
-	[self.avPlayer addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:0];
-
-	[self.avPlayerLayer setFrame:self.view.bounds];
-	[self.view.layer addSublayer:self.avPlayerLayer];
-	[self.avPlayer play];
-
-	AVMediaSelectionGroup *audioGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
-
-	NSMutableArray *allAudioParams = [NSMutableArray array];
-	for (id track in audioGroup.options) {
-		NSString *language = [[track locale] localeIdentifier];
-		NSLog(@"subtitle lang: %@", language);
-
-		if ([language isEqualToString:audioTrack]) {
-			AVMutableAudioMixInputParameters *audioInputParams = [AVMutableAudioMixInputParameters audioMixInputParameters];
-			[audioInputParams setVolume:videoVolume atTime:kCMTimeZero];
-			[audioInputParams setTrackID:[track trackID]];
-			[allAudioParams addObject:audioInputParams];
-
-			AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
-			[audioMix setInputParameters:allAudioParams];
-
-			[self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:audioGroup];
-			[self.avPlayer.currentItem setAudioMix:audioMix];
-
-			break;
-		}
-	}
-
-	AVMediaSelectionGroup *subtitlesGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
-	NSArray *useableTracks = [AVMediaSelectionGroup mediaSelectionOptionsFromArray:subtitlesGroup.options withoutMediaCharacteristics:[NSArray arrayWithObject:AVMediaCharacteristicContainsOnlyForcedSubtitles]];
-
-	for (id track in useableTracks) {
-		NSString *language = [[track locale] localeIdentifier];
-		NSLog(@"subtitle lang: %@", language);
-
-		if ([language isEqualToString:subtitleTrack]) {
-			[self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:subtitlesGroup];
-			break;
-		}
-	}
-
-	self.isVideoCurrentlyPlaying = YES;
-
-	return true;
-}
-
-- (BOOL)isVideoPlaying {
-	if (self.avPlayer.error) {
-		printf("Error during playback\n");
-	}
-	return (self.avPlayer.rate > 0 && !self.avPlayer.error);
-}
-
-- (void)pauseVideo {
-	self.videoCurrentTime = self.avPlayer.currentTime;
-	[self.avPlayer pause];
-	self.isVideoCurrentlyPlaying = NO;
-}
-
-- (void)unpauseVideo {
-	[self.avPlayer play];
-	self.isVideoCurrentlyPlaying = YES;
-}
-
-- (void)playerItemDidReachEnd:(NSNotification *)notification {
-	[self stopVideo];
-}
-
-- (void)stopVideo {
-	[self.avPlayer pause];
-	[self.avPlayerLayer removeFromSuperlayer];
-	self.avPlayerLayer = nil;
-
-	if (self.avPlayerItem) {
-		[self.avPlayerItem removeObserver:self forKeyPath:@"status"];
-		self.avPlayerItem = nil;
-	}
-
-	if (self.avPlayer) {
-		[self.avPlayer removeObserver:self forKeyPath:@"status"];
-		self.avPlayer = nil;
+	// If we are showing some video already, reuse existing view for new video.
+	if (self.videoView) {
+		return [self.videoView playVideoAtPath:filePath volume:videoVolume audio:audioTrack subtitle:subtitleTrack];
+	} else {
+		// Create autoresizing view for video playback.
+		GodotNativeVideoView *videoView = [[GodotNativeVideoView alloc] initWithFrame:self.view.bounds];
+		videoView.autoresizingMask = UIViewAutoresizingFlexibleWidth & UIViewAutoresizingFlexibleHeight;
+		[self.view addSubview:videoView];
+		return [self.videoView playVideoAtPath:filePath volume:videoVolume audio:audioTrack subtitle:subtitleTrack];
 	}
-
-	self.avAsset = nil;
-
-	self.isVideoCurrentlyPlaying = NO;
 }
 
 // MARK: Delegates