VLCPlaybackController.m 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360
  1. /*****************************************************************************
  2. * VLCPlaybackController.m
  3. * VLC for iOS
  4. *****************************************************************************
  5. * Copyright (c) 2013-2018 VideoLAN. All rights reserved.
  6. * $Id$
  7. *
  8. * Authors: Felix Paul Kühne <fkuehne # videolan.org>
  9. * Carola Nitz <caro # videolan.org>
  10. * Gleb Pinigin <gpinigin # gmail.com>
  11. * Pierre Sagaspe <pierre.sagaspe # me.com>
  12. * Tobias Conradi <videolan # tobias-conradi.de>
  13. * Sylver Bruneau <sylver.bruneau # gmail dot com>
  14. * Winston Weinert <winston # ml1 dot net>
  15. *
  16. * Refer to the COPYING file of the official project for license.
  17. *****************************************************************************/
  18. #import "VLCPlaybackController.h"
  19. #import "UIDevice+VLC.h"
  20. #import <AVFoundation/AVFoundation.h>
  21. #import "VLCPlayerDisplayController.h"
  22. #import "VLCConstants.h"
  23. #import "VLCRemoteControlService.h"
  24. #import "VLCMetadata.h"
  25. #if TARGET_OS_IOS
  26. #import "VLC-Swift.h"
  27. #endif
  28. NSString *const VLCPlaybackControllerPlaybackDidStart = @"VLCPlaybackControllerPlaybackDidStart";
  29. NSString *const VLCPlaybackControllerPlaybackDidPause = @"VLCPlaybackControllerPlaybackDidPause";
  30. NSString *const VLCPlaybackControllerPlaybackDidResume = @"VLCPlaybackControllerPlaybackDidResume";
  31. NSString *const VLCPlaybackControllerPlaybackDidStop = @"VLCPlaybackControllerPlaybackDidStop";
  32. NSString *const VLCPlaybackControllerPlaybackMetadataDidChange = @"VLCPlaybackControllerPlaybackMetadataDidChange";
  33. NSString *const VLCPlaybackControllerPlaybackDidFail = @"VLCPlaybackControllerPlaybackDidFail";
  34. NSString *const VLCPlaybackControllerPlaybackPositionUpdated = @"VLCPlaybackControllerPlaybackPositionUpdated";
  35. typedef NS_ENUM(NSUInteger, VLCAspectRatio) {
  36. VLCAspectRatioDefault = 0,
  37. VLCAspectRatioFillToScreen,
  38. VLCAspectRatioFourToThree,
  39. VLCAspectRatioSixteenToNine,
  40. VLCAspectRatioSixteenToTen,
  41. };
  42. @interface VLCPlaybackController () <VLCMediaPlayerDelegate, VLCMediaDelegate, VLCRemoteControlServiceDelegate>
  43. {
  44. VLCRemoteControlService *_remoteControlService;
  45. VLCMediaPlayer *_mediaPlayer;
  46. VLCMediaListPlayer *_listPlayer;
  47. BOOL _playerIsSetup;
  48. BOOL _shouldResumePlaying;
  49. BOOL _sessionWillRestart;
  50. NSString *_pathToExternalSubtitlesFile;
  51. int _itemInMediaListToBePlayedFirst;
  52. NSTimer *_sleepTimer;
  53. NSUInteger _currentAspectRatio;
  54. BOOL _isInFillToScreen;
  55. UIView *_videoOutputViewWrapper;
  56. UIView *_actualVideoOutputView;
  57. UIView *_preBackgroundWrapperView;
  58. BOOL _needsMetadataUpdate;
  59. BOOL _mediaWasJustStarted;
  60. BOOL _recheckForExistingThumbnail;
  61. BOOL _externalAudioPlaybackDevicePlugged;
  62. NSLock *_playbackSessionManagementLock;
  63. NSMutableArray *_shuffleStack;
  64. void (^_playbackCompletion)(BOOL success);
  65. }
  66. @end
  67. @implementation VLCPlaybackController
  68. #pragma mark instance management
  69. + (VLCPlaybackController *)sharedInstance
  70. {
  71. static VLCPlaybackController *sharedInstance = nil;
  72. static dispatch_once_t pred;
  73. dispatch_once(&pred, ^{
  74. sharedInstance = [VLCPlaybackController new];
  75. });
  76. return sharedInstance;
  77. }
  78. - (void)dealloc
  79. {
  80. _dialogProvider = nil;
  81. }
  82. - (instancetype)init
  83. {
  84. self = [super init];
  85. if (self) {
  86. // listen to audiosessions and appkit callback
  87. _externalAudioPlaybackDevicePlugged = [self isExternalAudioPlaybackDevicePlugged];
  88. NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
  89. [defaultCenter addObserver:self selector:@selector(audioSessionRouteChange:)
  90. name:AVAudioSessionRouteChangeNotification object:nil];
  91. [defaultCenter addObserver:self selector:@selector(handleInterruption:)
  92. name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
  93. // appkit because we neeed to know when we go to background in order to stop the video, so that we don't crash
  94. [defaultCenter addObserver:self selector:@selector(applicationWillResignActive:)
  95. name:UIApplicationWillResignActiveNotification object:nil];
  96. [defaultCenter addObserver:self selector:@selector(applicationDidBecomeActive:)
  97. name:UIApplicationDidBecomeActiveNotification object:nil];
  98. [defaultCenter addObserver:self selector:@selector(applicationDidEnterBackground:)
  99. name:UIApplicationDidEnterBackgroundNotification object:nil];
  100. _metadata = [VLCMetaData new];
  101. _dialogProvider = [[VLCDialogProvider alloc] initWithLibrary:[VLCLibrary sharedLibrary] customUI:YES];
  102. _dialogProvider.customRenderer = self;
  103. _playbackSessionManagementLock = [[NSLock alloc] init];
  104. _shuffleMode = NO;
  105. _shuffleStack = [[NSMutableArray alloc] init];
  106. }
  107. return self;
  108. }
  109. - (VLCRemoteControlService *)remoteControlService
  110. {
  111. if (!_remoteControlService) {
  112. _remoteControlService = [[VLCRemoteControlService alloc] init];
  113. _remoteControlService.remoteControlServiceDelegate = self;
  114. }
  115. return _remoteControlService;
  116. }
  117. #pragma mark - playback management
  118. - (void)openVideoSubTitlesFromFile:(NSString *)pathToFile
  119. {
  120. [_mediaPlayer addPlaybackSlave:[NSURL fileURLWithPath:pathToFile] type:VLCMediaPlaybackSlaveTypeSubtitle enforce:YES];
  121. }
  122. - (void)playMediaList:(VLCMediaList *)mediaList firstIndex:(NSInteger)index subtitlesFilePath:(NSString * _Nullable)subsFilePath
  123. {
  124. [self playMediaList: mediaList firstIndex: index subtitlesFilePath: subsFilePath completion: nil];
  125. }
  126. - (void)playMediaList:(VLCMediaList *)mediaList firstIndex:(NSInteger)index subtitlesFilePath:(NSString * _Nullable)subsFilePath completion:(void (^ __nullable)(BOOL success))completion
  127. {
  128. _playbackCompletion = completion;
  129. self.mediaList = mediaList;
  130. _itemInMediaListToBePlayedFirst = (int)index;
  131. _pathToExternalSubtitlesFile = subsFilePath;
  132. _sessionWillRestart = _playerIsSetup;
  133. _playerIsSetup ? [self stopPlayback] : [self startPlayback];
  134. }
  135. - (VLCTime *)playedTime
  136. {
  137. return [_mediaPlayer time];
  138. }
  139. - (void)startPlayback
  140. {
  141. if (_playerIsSetup) {
  142. APLog(@"%s: player is already setup, bailing out", __PRETTY_FUNCTION__);
  143. return;
  144. }
  145. BOOL ret = [_playbackSessionManagementLock tryLock];
  146. if (!ret) {
  147. APLog(@"%s: locking failed", __PRETTY_FUNCTION__);
  148. return;
  149. }
  150. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  151. if (!self.mediaList) {
  152. APLog(@"%s: no URL and no media list set, stopping playback", __PRETTY_FUNCTION__);
  153. [_playbackSessionManagementLock unlock];
  154. [self stopPlayback];
  155. return;
  156. }
  157. /* video decoding permanently fails if we don't provide a UIView to draw into on init
  158. * hence we provide one which is not attached to any view controller for off-screen drawing
  159. * and disable video decoding once playback started */
  160. _actualVideoOutputView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
  161. _actualVideoOutputView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  162. _actualVideoOutputView.autoresizesSubviews = YES;
  163. _listPlayer = [[VLCMediaListPlayer alloc] initWithDrawable:_actualVideoOutputView];
  164. /* to enable debug logging for the playback library instance, switch the boolean below
  165. * note that the library instance used for playback may not necessarily match the instance
  166. * used for media discovery or thumbnailing */
  167. _listPlayer.mediaPlayer.libraryInstance.debugLogging = NO;
  168. _mediaPlayer = _listPlayer.mediaPlayer;
  169. [_mediaPlayer setDelegate:self];
  170. if ([[defaults objectForKey:kVLCSettingPlaybackSpeedDefaultValue] floatValue] != 0)
  171. [_mediaPlayer setRate: [[defaults objectForKey:kVLCSettingPlaybackSpeedDefaultValue] floatValue]];
  172. if ([[defaults objectForKey:kVLCSettingDeinterlace] intValue] != 0)
  173. [_mediaPlayer setDeinterlaceFilter:@"blend"];
  174. else
  175. [_mediaPlayer setDeinterlaceFilter:nil];
  176. VLCMedia *media = [_mediaList mediaAtIndex:_itemInMediaListToBePlayedFirst];
  177. [media parseWithOptions:VLCMediaParseLocal];
  178. media.delegate = self;
  179. [media addOptions:self.mediaOptionsDictionary];
  180. [_listPlayer setMediaList:self.mediaList];
  181. [_listPlayer setRepeatMode:VLCDoNotRepeat];
  182. [_playbackSessionManagementLock unlock];
  183. [self _playNewMedia];
  184. }
  185. - (void)_playNewMedia
  186. {
  187. BOOL ret = [_playbackSessionManagementLock tryLock];
  188. if (!ret) {
  189. APLog(@"%s: locking failed", __PRETTY_FUNCTION__);
  190. return;
  191. }
  192. // Set last selected equalizer profile
  193. unsigned int profile = (unsigned int)[[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingEqualizerProfile] integerValue];
  194. [_mediaPlayer resetEqualizerFromProfile:profile];
  195. [_mediaPlayer setPreAmplification:[_mediaPlayer preAmplification]];
  196. _mediaWasJustStarted = YES;
  197. [_mediaPlayer addObserver:self forKeyPath:@"time" options:0 context:nil];
  198. [_mediaPlayer addObserver:self forKeyPath:@"remainingTime" options:0 context:nil];
  199. [_mediaPlayer setRendererItem:_renderer];
  200. [_listPlayer playItemAtNumber:@(_itemInMediaListToBePlayedFirst)];
  201. if ([self.delegate respondsToSelector:@selector(prepareForMediaPlayback:)])
  202. [self.delegate prepareForMediaPlayback:self];
  203. _currentAspectRatio = VLCAspectRatioDefault;
  204. _mediaPlayer.videoAspectRatio = NULL;
  205. _mediaPlayer.videoCropGeometry = NULL;
  206. [[self remoteControlService] subscribeToRemoteCommands];
  207. if (_pathToExternalSubtitlesFile) {
  208. /* this could be a path or an absolute string - let's see */
  209. NSURL *subtitleURL = [NSURL URLWithString:_pathToExternalSubtitlesFile];
  210. if (!subtitleURL) {
  211. subtitleURL = [NSURL fileURLWithPath:_pathToExternalSubtitlesFile];
  212. }
  213. if (subtitleURL) {
  214. [_mediaPlayer addPlaybackSlave:subtitleURL type:VLCMediaPlaybackSlaveTypeSubtitle enforce:YES];
  215. }
  216. }
  217. _playerIsSetup = YES;
  218. [[NSNotificationCenter defaultCenter] postNotificationName:VLCPlaybackControllerPlaybackDidStart object:self];
  219. [_playbackSessionManagementLock unlock];
  220. }
  221. - (void)stopPlayback
  222. {
  223. BOOL ret = [_playbackSessionManagementLock tryLock];
  224. if (!ret) {
  225. APLog(@"%s: locking failed", __PRETTY_FUNCTION__);
  226. return;
  227. }
  228. if (_mediaPlayer) {
  229. @try {
  230. [_mediaPlayer removeObserver:self forKeyPath:@"time"];
  231. [_mediaPlayer removeObserver:self forKeyPath:@"remainingTime"];
  232. }
  233. @catch (NSException *exception) {
  234. APLog(@"we weren't an observer yet");
  235. }
  236. if (_mediaPlayer.media) {
  237. [_mediaPlayer pause];
  238. #if TARGET_OS_IOS
  239. [self _savePlaybackState];
  240. #endif
  241. [_mediaPlayer stop];
  242. }
  243. _mediaPlayer = nil;
  244. _listPlayer = nil;
  245. }
  246. if (!_sessionWillRestart) {
  247. _mediaList = nil;
  248. }
  249. _playerIsSetup = NO;
  250. [_shuffleStack removeAllObjects];
  251. if (_playbackCompletion) {
  252. BOOL finishedPlaybackWithError = _mediaPlayer.state == VLCMediaPlayerStateError && !_sessionWillRestart;
  253. _playbackCompletion(!finishedPlaybackWithError);
  254. }
  255. [[self remoteControlService] unsubscribeFromRemoteCommands];
  256. [_playbackSessionManagementLock unlock];
  257. if (!_sessionWillRestart) {
  258. [[NSNotificationCenter defaultCenter] postNotificationName:VLCPlaybackControllerPlaybackDidStop object:self];
  259. } else {
  260. _sessionWillRestart = NO;
  261. [self startPlayback];
  262. }
  263. }
  264. #if TARGET_OS_IOS
  265. - (void)restoreAudioAndSubtitleTrack
  266. {
  267. MLFile *item = [MLFile fileForURL:_mediaPlayer.media.url].firstObject;
  268. if (item) {
  269. _mediaPlayer.currentAudioTrackIndex = item.lastAudioTrack.intValue;
  270. _mediaPlayer.currentVideoSubTitleIndex = item.lastSubtitleTrack.intValue;
  271. }
  272. }
  273. - (void)_savePlaybackState
  274. {
  275. @try {
  276. [[MLMediaLibrary sharedMediaLibrary] save];
  277. }
  278. @catch (NSException *exception) {
  279. APLog(@"saving playback state failed");
  280. }
  281. NSArray *files = [MLFile fileForURL:_mediaPlayer.media.url];
  282. MLFile *fileItem = files.firstObject;
  283. if (!fileItem) {
  284. APLog(@"couldn't find file, not saving playback progress");
  285. return;
  286. }
  287. @try {
  288. float position = _mediaPlayer.position;
  289. fileItem.lastPosition = @(position);
  290. fileItem.lastAudioTrack = @(_mediaPlayer.currentAudioTrackIndex);
  291. fileItem.lastSubtitleTrack = @(_mediaPlayer.currentVideoSubTitleIndex);
  292. if (position > .95)
  293. return;
  294. if (_mediaPlayer.hasVideoOut) {
  295. NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
  296. NSString *newThumbnailPath = [searchPaths.firstObject stringByAppendingPathComponent:@"VideoSnapshots"];
  297. NSError *error;
  298. [[NSFileManager defaultManager] createDirectoryAtPath:newThumbnailPath withIntermediateDirectories:YES attributes:nil error:&error];
  299. if (error == nil) {
  300. newThumbnailPath = [newThumbnailPath stringByAppendingPathComponent:fileItem.objectID.URIRepresentation.lastPathComponent];
  301. [_mediaPlayer saveVideoSnapshotAt:newThumbnailPath withWidth:0 andHeight:0];
  302. _recheckForExistingThumbnail = YES;
  303. [self performSelector:@selector(_updateStoredThumbnailForFile:) withObject:fileItem afterDelay:.25];
  304. }
  305. }
  306. }
  307. @catch (NSException *exception) {
  308. APLog(@"failed to save current media state - file removed?");
  309. }
  310. }
  311. #endif
  312. #if TARGET_OS_IOS
  313. - (void)_updateStoredThumbnailForFile:(MLFile *)fileItem
  314. {
  315. NSFileManager *fileManager = [NSFileManager defaultManager];
  316. NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
  317. NSString* newThumbnailPath = [searchPaths[0] stringByAppendingPathComponent:@"VideoSnapshots"];
  318. newThumbnailPath = [newThumbnailPath stringByAppendingPathComponent:fileItem.objectID.URIRepresentation.lastPathComponent];
  319. if (![fileManager fileExistsAtPath:newThumbnailPath]) {
  320. if (_recheckForExistingThumbnail) {
  321. [self performSelector:@selector(_updateStoredThumbnailForFile:) withObject:fileItem afterDelay:1.];
  322. _recheckForExistingThumbnail = NO;
  323. } else
  324. return;
  325. }
  326. UIImage *newThumbnail = [UIImage imageWithContentsOfFile:newThumbnailPath];
  327. if (!newThumbnail) {
  328. if (_recheckForExistingThumbnail) {
  329. [self performSelector:@selector(_updateStoredThumbnailForFile:) withObject:fileItem afterDelay:1.];
  330. _recheckForExistingThumbnail = NO;
  331. } else
  332. return;
  333. }
  334. @try {
  335. [fileItem setComputedThumbnailScaledForDevice:newThumbnail];
  336. }
  337. @catch (NSException *exception) {
  338. APLog(@"updating thumbnail failed");
  339. }
  340. [fileManager removeItemAtPath:newThumbnailPath error:nil];
  341. }
  342. #endif
  343. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
  344. {
  345. if (_mediaWasJustStarted) {
  346. _mediaWasJustStarted = NO;
  347. #if TARGET_OS_IOS
  348. if (self.mediaList) {
  349. NSArray *matches = [MLFile fileForURL:_mediaPlayer.media.url];
  350. MLFile *item = matches.firstObject;
  351. [self _recoverLastPlaybackStateOfItem:item];
  352. }
  353. #else
  354. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  355. BOOL bValue = [defaults boolForKey:kVLCSettingUseSPDIF];
  356. if (bValue) {
  357. _mediaPlayer.audio.passthrough = bValue;
  358. }
  359. #endif
  360. }
  361. if ([self.delegate respondsToSelector:@selector(playbackPositionUpdated:)])
  362. [self.delegate playbackPositionUpdated:self];
  363. [[NSNotificationCenter defaultCenter] postNotificationName:VLCPlaybackControllerPlaybackPositionUpdated
  364. object:self];
  365. }
  366. - (NSInteger)mediaDuration
  367. {
  368. return _mediaPlayer.media.length.intValue;;
  369. }
  370. - (BOOL)isPlaying
  371. {
  372. return _mediaPlayer.isPlaying;
  373. }
  374. - (BOOL)willPlay
  375. {
  376. return _mediaPlayer.willPlay;
  377. }
  378. - (VLCRepeatMode)repeatMode
  379. {
  380. return _listPlayer.repeatMode;
  381. }
  382. - (void)setRepeatMode:(VLCRepeatMode)repeatMode
  383. {
  384. _listPlayer.repeatMode = repeatMode;
  385. }
  386. - (BOOL)currentMediaHasChapters
  387. {
  388. return [_mediaPlayer numberOfTitles] > 1 || [_mediaPlayer numberOfChaptersForTitle:_mediaPlayer.currentTitleIndex] > 1;
  389. }
  390. - (BOOL)currentMediaHasTrackToChooseFrom
  391. {
  392. return [[_mediaPlayer audioTrackIndexes] count] > 2 || [[_mediaPlayer videoSubTitlesIndexes] count] > 1;
  393. }
  394. - (BOOL) isSeekable
  395. {
  396. return _mediaPlayer.isSeekable;
  397. }
  398. - (NSNumber *)playbackTime
  399. {
  400. return _mediaPlayer.time.value;
  401. }
  402. - (float)playbackRate
  403. {
  404. return _mediaPlayer.rate;
  405. }
  406. - (void)setPlaybackRate:(float)playbackRate
  407. {
  408. [_mediaPlayer setRate:playbackRate];
  409. _metadata.playbackRate = @(_mediaPlayer.rate);
  410. }
  411. - (void)setAudioDelay:(float)audioDelay
  412. {
  413. _mediaPlayer.currentAudioPlaybackDelay = 1000.*audioDelay;
  414. }
  415. - (float)audioDelay
  416. {
  417. return _mediaPlayer.currentAudioPlaybackDelay/1000.;
  418. }
  419. - (float)playbackPosition
  420. {
  421. return [_mediaPlayer position];
  422. }
  423. - (void)setPlaybackPosition:(float)position
  424. {
  425. _mediaPlayer.position = position;
  426. }
  427. - (void)setSubtitleDelay:(float)subtitleDeleay
  428. {
  429. _mediaPlayer.currentVideoSubTitleDelay = 1000.*subtitleDeleay;
  430. }
  431. - (float)subtitleDelay
  432. {
  433. return _mediaPlayer.currentVideoSubTitleDelay/1000.;
  434. }
  435. - (float)hue
  436. {
  437. return _mediaPlayer.hue;
  438. }
  439. - (void)setHue:(float)hue
  440. {
  441. _mediaPlayer.hue = hue;
  442. }
  443. - (float)contrast
  444. {
  445. return _mediaPlayer.contrast;
  446. }
  447. - (void)setContrast:(float)contrast
  448. {
  449. _mediaPlayer.contrast = contrast;
  450. }
  451. - (float)brightness
  452. {
  453. return _mediaPlayer.brightness;
  454. }
  455. - (void)setBrightness:(float)brightness
  456. {
  457. _mediaPlayer.brightness = brightness;
  458. }
  459. - (float)saturation
  460. {
  461. return _mediaPlayer.saturation;
  462. }
  463. - (void)setSaturation:(float)saturation
  464. {
  465. _mediaPlayer.saturation = saturation;
  466. }
  467. - (void)setGamma:(float)gamma
  468. {
  469. _mediaPlayer.gamma = gamma;
  470. }
  471. - (float)gamma
  472. {
  473. return _mediaPlayer.gamma;
  474. }
  475. - (void)resetFilters
  476. {
  477. _mediaPlayer.hue = 0.;
  478. _mediaPlayer.contrast = 1.;
  479. _mediaPlayer.brightness = 1.;
  480. _mediaPlayer.saturation = 1.;
  481. _mediaPlayer.gamma = 1.;
  482. }
  483. - (void)toggleRepeatMode
  484. {
  485. if (_listPlayer.repeatMode == VLCRepeatAllItems) {
  486. _listPlayer.repeatMode = VLCDoNotRepeat;
  487. } else {
  488. _listPlayer.repeatMode += 1;
  489. }
  490. }
  491. - (NSInteger)indexOfCurrentAudioTrack
  492. {
  493. return [_mediaPlayer.audioTrackIndexes indexOfObject:@(_mediaPlayer.currentAudioTrackIndex)];
  494. }
  495. - (NSInteger)indexOfCurrentSubtitleTrack
  496. {
  497. return [_mediaPlayer.videoSubTitlesIndexes indexOfObject:@(_mediaPlayer.currentVideoSubTitleIndex)];
  498. }
  499. - (NSInteger)indexOfCurrentChapter
  500. {
  501. return _mediaPlayer.currentChapterIndex;
  502. }
  503. - (NSInteger)indexOfCurrentTitle
  504. {
  505. return _mediaPlayer.currentTitleIndex;
  506. }
  507. - (NSInteger)numberOfAudioTracks
  508. {
  509. return _mediaPlayer.audioTrackIndexes.count;
  510. }
  511. - (NSInteger)numberOfVideoSubtitlesIndexes
  512. {
  513. return _mediaPlayer.videoSubTitlesIndexes.count;
  514. }
  515. - (NSInteger)numberOfTitles
  516. {
  517. return [_mediaPlayer numberOfTitles];
  518. }
  519. - (NSInteger)numberOfChaptersForCurrentTitle
  520. {
  521. return [_mediaPlayer numberOfChaptersForTitle:_mediaPlayer.currentTitleIndex];
  522. }
  523. - (NSString *)videoSubtitleNameAtIndex:(NSInteger)index
  524. {
  525. if (index >= 0 && index < _mediaPlayer.videoSubTitlesNames.count)
  526. return _mediaPlayer.videoSubTitlesNames[index];
  527. return nil;
  528. }
  529. - (NSString *)audioTrackNameAtIndex:(NSInteger)index
  530. {
  531. if (index >= 0 && index < _mediaPlayer.audioTrackNames.count)
  532. return _mediaPlayer.audioTrackNames[index];
  533. return nil;
  534. }
  535. - (NSDictionary *)titleDescriptionsDictAtIndex:(NSInteger)index
  536. {
  537. if (index >= 0 && index < _mediaPlayer.titleDescriptions.count)
  538. return _mediaPlayer.titleDescriptions[index];
  539. return nil;
  540. }
  541. - (NSDictionary *)chapterDescriptionsDictAtIndex:(NSInteger)index
  542. {
  543. NSArray *chapterDescriptions = [_mediaPlayer chapterDescriptionsOfTitle:_mediaPlayer.currentTitleIndex];
  544. if (index >= 0 && index < chapterDescriptions.count)
  545. return chapterDescriptions[index];
  546. return nil;
  547. }
  548. - (void)selectAudioTrackAtIndex:(NSInteger)index
  549. {
  550. if (index >= 0 && index < _mediaPlayer.audioTrackIndexes.count) {
  551. //we can cast this cause we won't have more than 2 million audiotracks
  552. _mediaPlayer.currentAudioTrackIndex = [_mediaPlayer.audioTrackIndexes[index] intValue];
  553. }
  554. }
  555. - (void)selectVideoSubtitleAtIndex:(NSInteger)index
  556. {
  557. if (index >= 0 && index < _mediaPlayer.videoSubTitlesIndexes.count) {
  558. _mediaPlayer.currentVideoSubTitleIndex = [_mediaPlayer.videoSubTitlesIndexes[index] intValue];
  559. }
  560. }
  561. - (void)selectTitleAtIndex:(NSInteger)index
  562. {
  563. if (index >= 0 && index < [_mediaPlayer numberOfTitles]) {
  564. //we can cast this cause we won't have more than 2 million titles
  565. _mediaPlayer.currentTitleIndex = (int)index;
  566. }
  567. }
  568. - (void)selectChapterAtIndex:(NSInteger)index
  569. {
  570. if (index >= 0 && index < [self numberOfChaptersForCurrentTitle]) {
  571. //we can cast this cause we won't have more than 2 million chapters
  572. _mediaPlayer.currentChapterIndex = (int)index;
  573. }
  574. }
  575. - (void)shortJumpForward
  576. {
  577. [_mediaPlayer shortJumpForward];
  578. }
  579. - (void)shortJumpBackward
  580. {
  581. [_mediaPlayer shortJumpBackward];
  582. }
  583. - (VLCTime *)remainingTime
  584. {
  585. return [_mediaPlayer remainingTime];
  586. }
  587. - (void)setAudioPassthrough:(BOOL)shouldPass
  588. {
  589. _mediaPlayer.audio.passthrough = shouldPass;
  590. }
  591. - (void)mediaPlayerStateChanged:(NSNotification *)aNotification
  592. {
  593. VLCMediaPlayerState currentState = _mediaPlayer.state;
  594. switch (currentState) {
  595. case VLCMediaPlayerStateBuffering: {
  596. /* attach delegate */
  597. _mediaPlayer.media.delegate = self;
  598. /* on-the-fly values through hidden API */
  599. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  600. #pragma clang diagnostic push
  601. #pragma clang diagnostic ignored "-Wundeclared-selector"
  602. [_mediaPlayer performSelector:@selector(setTextRendererFont:) withObject:[defaults objectForKey:kVLCSettingSubtitlesFont]];
  603. [_mediaPlayer performSelector:@selector(setTextRendererFontSize:) withObject:[defaults objectForKey:kVLCSettingSubtitlesFontSize]];
  604. [_mediaPlayer performSelector:@selector(setTextRendererFontColor:) withObject:[defaults objectForKey:kVLCSettingSubtitlesFontColor]];
  605. [_mediaPlayer performSelector:@selector(setTextRendererFontForceBold:) withObject:[defaults objectForKey:kVLCSettingSubtitlesBoldFont]];
  606. #pragma clang diagnostic pop
  607. } break;
  608. case VLCMediaPlayerStateError: {
  609. APLog(@"Playback failed");
  610. dispatch_async(dispatch_get_main_queue(),^{
  611. [[NSNotificationCenter defaultCenter] postNotificationName:VLCPlaybackControllerPlaybackDidFail object:self];
  612. });
  613. _sessionWillRestart = NO;
  614. [self stopPlayback];
  615. } break;
  616. case VLCMediaPlayerStateEnded:
  617. case VLCMediaPlayerStateStopped: {
  618. [_listPlayer.mediaList lock];
  619. NSUInteger listCount = _listPlayer.mediaList.count;
  620. [_listPlayer.mediaList unlock];
  621. if ([_listPlayer.mediaList indexOfMedia:_mediaPlayer.media] == listCount - 1 && self.repeatMode == VLCDoNotRepeat) {
  622. _sessionWillRestart = NO;
  623. [self stopPlayback];
  624. return;
  625. }
  626. } break;
  627. case VLCMediaPlayerStateESAdded: {
  628. #if TARGET_OS_IOS
  629. [self restoreAudioAndSubtitleTrack];
  630. #endif
  631. } break;
  632. default:
  633. break;
  634. }
  635. if ([self.delegate respondsToSelector:@selector(mediaPlayerStateChanged:isPlaying:currentMediaHasTrackToChooseFrom:currentMediaHasChapters:forPlaybackController:)])
  636. [self.delegate mediaPlayerStateChanged:currentState
  637. isPlaying:_mediaPlayer.isPlaying
  638. currentMediaHasTrackToChooseFrom:self.currentMediaHasTrackToChooseFrom
  639. currentMediaHasChapters:self.currentMediaHasChapters
  640. forPlaybackController:self];
  641. [self setNeedsMetadataUpdate];
  642. }
  643. #pragma mark - playback controls
  644. - (void)playPause
  645. {
  646. [_mediaPlayer isPlaying] ? [self pause] : [self play];
  647. }
  648. - (void)play
  649. {
  650. [_listPlayer play];
  651. [[NSNotificationCenter defaultCenter] postNotificationName:VLCPlaybackControllerPlaybackDidResume object:self];
  652. }
  653. - (void)pause
  654. {
  655. [_listPlayer pause];
  656. #if TARGET_OS_IOS
  657. [self _savePlaybackState];
  658. #endif
  659. [[NSNotificationCenter defaultCenter] postNotificationName:VLCPlaybackControllerPlaybackDidPause object:self];
  660. }
  661. - (void)next
  662. {
  663. NSInteger mediaListCount = _mediaList.count;
  664. #if TARGET_OS_IOS
  665. if (self.repeatMode != VLCRepeatCurrentItem && mediaListCount > 2 && _shuffleMode) {
  666. NSNumber *nextIndex;
  667. NSUInteger currentIndex = [_mediaList indexOfMedia:_listPlayer.mediaPlayer.media];
  668. //Reached end of playlist
  669. if (_shuffleStack.count + 1 == mediaListCount) {
  670. if ([self repeatMode] == VLCDoNotRepeat)
  671. return;
  672. [_shuffleStack removeAllObjects];
  673. }
  674. [_shuffleStack addObject:[NSNumber numberWithUnsignedInteger:currentIndex]];
  675. do {
  676. nextIndex = [NSNumber numberWithUnsignedInt:arc4random_uniform((uint32_t)mediaListCount)];
  677. } while (currentIndex == nextIndex.unsignedIntegerValue || [_shuffleStack containsObject:nextIndex]);
  678. [_listPlayer playItemAtNumber:[NSNumber numberWithUnsignedInteger:nextIndex.unsignedIntegerValue]];
  679. [[NSNotificationCenter defaultCenter] postNotificationName:VLCPlaybackControllerPlaybackMetadataDidChange object:self];
  680. return;
  681. }
  682. #endif
  683. if (mediaListCount > 1) {
  684. [_listPlayer next];
  685. [[NSNotificationCenter defaultCenter] postNotificationName:VLCPlaybackControllerPlaybackMetadataDidChange object:self];
  686. } else {
  687. NSNumber *skipLength = [[NSUserDefaults standardUserDefaults] valueForKey:kVLCSettingPlaybackForwardSkipLength];
  688. [_mediaPlayer jumpForward:skipLength.intValue];
  689. }
  690. }
  691. - (void)previous
  692. {
  693. if (_mediaList.count > 1) {
  694. [_listPlayer previous];
  695. [[NSNotificationCenter defaultCenter] postNotificationName:VLCPlaybackControllerPlaybackMetadataDidChange object:self];
  696. }
  697. else {
  698. NSNumber *skipLength = [[NSUserDefaults standardUserDefaults] valueForKey:kVLCSettingPlaybackBackwardSkipLength];
  699. [_mediaPlayer jumpBackward:skipLength.intValue];
  700. }
  701. }
  702. - (void)jumpForward:(int)interval
  703. {
  704. [_mediaPlayer jumpForward:interval];
  705. }
  706. - (void)jumpBackward:(int)interval
  707. {
  708. [_mediaPlayer jumpBackward:interval];
  709. }
  710. - (UIScreen *)currentScreen
  711. {
  712. return [[UIDevice currentDevice] VLCHasExternalDisplay] ? [UIScreen screens][1] : [UIScreen mainScreen];
  713. }
  714. - (void)switchToFillToScreen
  715. {
  716. UIScreen *screen = [self currentScreen];
  717. CGSize screenSize = screen.bounds.size;
  718. CGSize videoSize = _mediaPlayer.videoSize;
  719. CGFloat ar = videoSize.width / (float)videoSize.height;
  720. CGFloat dar = screenSize.width / (float)screenSize.height;
  721. CGFloat scale;
  722. if (dar >= ar) {
  723. scale = screenSize.width / (float)videoSize.width;
  724. } else {
  725. scale = screenSize.height / (float)videoSize.height;
  726. }
  727. // Multiplied by screen.scale in consideration of pt to px
  728. _mediaPlayer.scaleFactor = scale * screen.scale;
  729. _isInFillToScreen = YES;
  730. }
  731. - (void)switchIPhoneXFullScreen
  732. {
  733. if (_isInFillToScreen) {
  734. const char *previousAspectRatio = _currentAspectRatio == VLCAspectRatioDefault ? NULL : [[self stringForAspectRatio:_currentAspectRatio] UTF8String];
  735. _mediaPlayer.videoAspectRatio = (char *)previousAspectRatio;
  736. _mediaPlayer.scaleFactor = 0;
  737. _isInFillToScreen = NO;
  738. } else {
  739. [self switchToFillToScreen];
  740. }
  741. }
  742. - (void)switchAspectRatio
  743. {
  744. _currentAspectRatio = _currentAspectRatio == VLCAspectRatioSixteenToTen ? VLCAspectRatioDefault : _currentAspectRatio + 1;
  745. switch (_currentAspectRatio) {
  746. case VLCAspectRatioDefault:
  747. _mediaPlayer.scaleFactor = 0;
  748. _mediaPlayer.videoAspectRatio = NULL;
  749. _mediaPlayer.videoCropGeometry = NULL;
  750. break;
  751. case VLCAspectRatioFillToScreen:
  752. // Reset aspect ratio only with aspectRatio button since we want to keep
  753. // the user ratio with double tap.
  754. _mediaPlayer.videoAspectRatio = NULL;
  755. [self switchToFillToScreen];
  756. break;
  757. case VLCAspectRatioFourToThree:
  758. case VLCAspectRatioSixteenToTen:
  759. case VLCAspectRatioSixteenToNine:
  760. _mediaPlayer.scaleFactor = 0;
  761. _mediaPlayer.videoCropGeometry = NULL;
  762. _mediaPlayer.videoAspectRatio = (char *)[[self stringForAspectRatio:_currentAspectRatio] UTF8String];
  763. }
  764. if ([self.delegate respondsToSelector:@selector(showStatusMessage:)]) {
  765. [self.delegate showStatusMessage:[NSString stringWithFormat:NSLocalizedString(@"AR_CHANGED", nil), [self stringForAspectRatio:_currentAspectRatio]]];
  766. }
  767. }
  768. - (NSString *)stringForAspectRatio:(VLCAspectRatio)ratio
  769. {
  770. switch (ratio) {
  771. case VLCAspectRatioFillToScreen:
  772. return NSLocalizedString(@"FILL_TO_SCREEN", nil);
  773. case VLCAspectRatioDefault:
  774. return NSLocalizedString(@"DEFAULT", nil);
  775. case VLCAspectRatioFourToThree:
  776. return @"4:3";
  777. case VLCAspectRatioSixteenToTen:
  778. return @"16:10";
  779. case VLCAspectRatioSixteenToNine:
  780. return @"16:9";
  781. default:
  782. NSAssert(NO, @"this shouldn't happen");
  783. }
  784. }
  785. - (void)setVideoTrackEnabled:(BOOL)enabled
  786. {
  787. if (!enabled)
  788. _mediaPlayer.currentVideoTrackIndex = -1;
  789. else if (_mediaPlayer.currentVideoTrackIndex == -1) {
  790. for (NSNumber *trackId in _mediaPlayer.videoTrackIndexes) {
  791. if ([trackId intValue] != -1) {
  792. _mediaPlayer.currentVideoTrackIndex = [trackId intValue];
  793. break;
  794. }
  795. }
  796. }
  797. }
  798. - (void)setVideoOutputView:(UIView *)videoOutputView
  799. {
  800. if (videoOutputView) {
  801. if ([_actualVideoOutputView superview] != nil)
  802. [_actualVideoOutputView removeFromSuperview];
  803. _actualVideoOutputView.frame = (CGRect){CGPointZero, videoOutputView.frame.size};
  804. [self setVideoTrackEnabled:true];
  805. [videoOutputView addSubview:_actualVideoOutputView];
  806. [_actualVideoOutputView layoutSubviews];
  807. [_actualVideoOutputView updateConstraints];
  808. [_actualVideoOutputView setNeedsLayout];
  809. } else
  810. [_actualVideoOutputView removeFromSuperview];
  811. _videoOutputViewWrapper = videoOutputView;
  812. }
  813. - (UIView *)videoOutputView
  814. {
  815. return _videoOutputViewWrapper;
  816. }
  817. #pragma mark - 360 Support
  818. #if !TARGET_OS_TV
  819. - (BOOL)updateViewpoint:(CGFloat)yaw pitch:(CGFloat)pitch roll:(CGFloat)roll fov:(CGFloat)fov absolute:(BOOL)absolute
  820. {
  821. //adjusting the values
  822. if (fabs(yaw) > 180) {
  823. yaw = yaw > 0 ? yaw - 360 : yaw + 360;
  824. }
  825. if (fabs(roll) > 180) {
  826. roll = roll > 0 ? roll - 360 : roll + 360;
  827. }
  828. if (fabs(pitch) > 90) {
  829. pitch = pitch > 0 ? pitch - 180 : pitch + 180;
  830. }
  831. return [_mediaPlayer updateViewpoint:yaw pitch:pitch roll:roll fov:fov absolute:absolute];
  832. }
  833. - (CGFloat)yaw
  834. {
  835. return _mediaPlayer.yaw;
  836. }
  837. - (CGFloat)pitch
  838. {
  839. return _mediaPlayer.pitch;
  840. }
  841. - (CGFloat)roll
  842. {
  843. return _mediaPlayer.roll;
  844. }
  845. - (CGFloat)fov
  846. {
  847. return _mediaPlayer.fov;
  848. }
  849. - (BOOL)currentMediaIs360Video
  850. {
  851. return [self currentMediaProjection] == VLCMediaProjectionEquiRectangular;
  852. }
  853. - (NSInteger)currentMediaProjection
  854. {
  855. VLCMedia *media = [_mediaPlayer media];
  856. NSInteger currentVideoTrackIndex = [_mediaPlayer currentVideoTrackIndex];
  857. if (media && currentVideoTrackIndex >= 0) {
  858. NSArray *tracksInfo = media.tracksInformation;
  859. for (NSDictionary *track in tracksInfo) {
  860. if ([track[VLCMediaTracksInformationType] isEqualToString:VLCMediaTracksInformationTypeVideo]) {
  861. return [track[VLCMediaTracksInformationVideoProjection] integerValue];
  862. }
  863. }
  864. }
  865. return -1;
  866. }
  867. #endif
  868. #pragma mark - equalizer
  869. - (void)setAmplification:(CGFloat)amplification forBand:(unsigned int)index
  870. {
  871. if (!_mediaPlayer.equalizerEnabled)
  872. [_mediaPlayer setEqualizerEnabled:YES];
  873. [_mediaPlayer setAmplification:amplification forBand:index];
  874. // For some reason we have to apply again preamp to apply change
  875. [_mediaPlayer setPreAmplification:[_mediaPlayer preAmplification]];
  876. }
  877. - (CGFloat)amplificationOfBand:(unsigned int)index
  878. {
  879. return [_mediaPlayer amplificationOfBand:index];
  880. }
  881. - (NSArray *)equalizerProfiles
  882. {
  883. return _mediaPlayer.equalizerProfiles;
  884. }
  885. - (void)resetEqualizerFromProfile:(unsigned int)profile
  886. {
  887. [[NSUserDefaults standardUserDefaults] setObject:@(profile) forKey:kVLCSettingEqualizerProfile];
  888. [_mediaPlayer resetEqualizerFromProfile:profile];
  889. }
  890. - (void)setPreAmplification:(CGFloat)preAmplification
  891. {
  892. if (!_mediaPlayer.equalizerEnabled)
  893. [_mediaPlayer setEqualizerEnabled:YES];
  894. [_mediaPlayer setPreAmplification:preAmplification];
  895. }
  896. - (CGFloat)preAmplification
  897. {
  898. return [_mediaPlayer preAmplification];
  899. }
  900. #pragma mark - AVAudioSession Notification Observers
  901. - (void)handleInterruption:(NSNotification *)notification
  902. {
  903. NSDictionary *userInfo = notification.userInfo;
  904. if (!userInfo || !userInfo[AVAudioSessionInterruptionTypeKey]) {
  905. return;
  906. }
  907. NSUInteger interruptionType = [userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
  908. if (interruptionType == AVAudioSessionInterruptionTypeBegan) {
  909. [_mediaPlayer pause];
  910. } else if (interruptionType == AVAudioSessionInterruptionTypeEnded
  911. && [userInfo[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue] == AVAudioSessionInterruptionOptionShouldResume) {
  912. [_mediaPlayer play];
  913. }
  914. }
  915. - (BOOL)isExternalAudioPlaybackDevicePlugged
  916. {
  917. /* check what output device is currently connected
  918. * this code assumes that everything which is not a builtin speaker, must be external */
  919. NSArray *outputs = [[AVAudioSession sharedInstance] currentRoute].outputs;
  920. AVAudioSessionPortDescription *outputDescription = outputs.firstObject;
  921. return ![outputDescription.portType isEqualToString:AVAudioSessionPortBuiltInSpeaker];
  922. }
  923. - (void)audioSessionRouteChange:(NSNotification *)notification
  924. {
  925. NSDictionary *userInfo = notification.userInfo;
  926. NSInteger routeChangeReason = [[userInfo valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
  927. if (routeChangeReason == AVAudioSessionRouteChangeReasonRouteConfigurationChange)
  928. return;
  929. BOOL externalAudioPlaybackDevicePlugged = [self isExternalAudioPlaybackDevicePlugged];
  930. if (_externalAudioPlaybackDevicePlugged && !externalAudioPlaybackDevicePlugged && [_mediaPlayer isPlaying]) {
  931. APLog(@"Pausing playback as previously plugged external audio playback device was removed");
  932. [_mediaPlayer pause];
  933. #if TARGET_OS_IOS
  934. [self _savePlaybackState];
  935. #endif
  936. [[NSNotificationCenter defaultCenter] postNotificationName:VLCPlaybackControllerPlaybackDidPause object:self];
  937. }
  938. _externalAudioPlaybackDevicePlugged = externalAudioPlaybackDevicePlugged;
  939. }
  940. #pragma mark - Managing the media item
  941. - (VLCMedia *)currentlyPlayingMedia
  942. {
  943. return _mediaPlayer.media;
  944. }
  945. #pragma mark - metadata handling
  946. - (void)performNavigationAction:(VLCMediaPlaybackNavigationAction)action
  947. {
  948. [_mediaPlayer performNavigationAction:action];
  949. }
  950. - (void)mediaDidFinishParsing:(VLCMedia *)aMedia
  951. {
  952. [self setNeedsMetadataUpdate];
  953. }
  954. - (void)mediaMetaDataDidChange:(VLCMedia*)aMedia
  955. {
  956. [self setNeedsMetadataUpdate];
  957. }
  958. - (void)setNeedsMetadataUpdate
  959. {
  960. if (_needsMetadataUpdate == NO) {
  961. _needsMetadataUpdate = YES;
  962. dispatch_async(dispatch_get_main_queue(), ^{
  963. [self->_metadata updateMetadataFromMediaPlayer:self->_mediaPlayer];
  964. self->_needsMetadataUpdate = NO;
  965. if ([self.delegate respondsToSelector:@selector(displayMetadataForPlaybackController:metadata:)])
  966. [self.delegate displayMetadataForPlaybackController:self metadata:self->_metadata];
  967. });
  968. }
  969. }
  970. #if TARGET_OS_IOS
  971. - (void)_recoverLastPlaybackStateOfItem:(MLFile *)item
  972. {
  973. if (item) {
  974. CGFloat lastPosition = .0;
  975. NSInteger duration = 0;
  976. if (item.lastPosition) {
  977. lastPosition = item.lastPosition.floatValue;
  978. }
  979. duration = item.duration.intValue;
  980. if (lastPosition < .95 && _mediaPlayer.position < lastPosition) {
  981. NSInteger continuePlayback;
  982. if ([item isAlbumTrack] || [item isSupportedAudioFile])
  983. continuePlayback = [[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingContinueAudioPlayback] integerValue];
  984. else
  985. continuePlayback = [[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingContinuePlayback] integerValue];
  986. if (continuePlayback == 1) {
  987. [self setPlaybackPosition:lastPosition];
  988. } else if (continuePlayback == 0) {
  989. NSArray<VLCAlertButton *> *buttonsAction = @[[[VLCAlertButton alloc] initWithTitle: NSLocalizedString(@"BUTTON_CANCEL", nil)
  990. style: UIAlertActionStyleCancel
  991. action: nil],
  992. [[VLCAlertButton alloc] initWithTitle: NSLocalizedString(@"BUTTON_CONTINUE", nil)
  993. action: ^(UIAlertAction *action) {
  994. [self setPlaybackPosition:lastPosition];
  995. }]
  996. ];
  997. UIViewController *presentingVC = [UIApplication sharedApplication].delegate.window.rootViewController;
  998. presentingVC = presentingVC.presentedViewController ?: presentingVC;
  999. [VLCAlertViewController alertViewManagerWithTitle:NSLocalizedString(@"CONTINUE_PLAYBACK", nil)
  1000. errorMessage:[NSString stringWithFormat:NSLocalizedString(@"CONTINUE_PLAYBACK_LONG", nil), item.title]
  1001. viewController:presentingVC
  1002. buttonsAction:buttonsAction];
  1003. }
  1004. }
  1005. }
  1006. }
  1007. #endif
  1008. - (void)recoverDisplayedMetadata
  1009. {
  1010. if ([self.delegate respondsToSelector:@selector(displayMetadataForPlaybackController:metadata:)])
  1011. [self.delegate displayMetadataForPlaybackController:self metadata:_metadata];
  1012. }
  1013. - (void)recoverPlaybackState
  1014. {
  1015. if ([self.delegate respondsToSelector:@selector(mediaPlayerStateChanged:isPlaying:currentMediaHasTrackToChooseFrom:currentMediaHasChapters:forPlaybackController:)])
  1016. [self.delegate mediaPlayerStateChanged:_mediaPlayer.state
  1017. isPlaying:self.isPlaying
  1018. currentMediaHasTrackToChooseFrom:self.currentMediaHasTrackToChooseFrom
  1019. currentMediaHasChapters:self.currentMediaHasChapters
  1020. forPlaybackController:self];
  1021. if ([self.delegate respondsToSelector:@selector(prepareForMediaPlayback:)])
  1022. [self.delegate prepareForMediaPlayback:self];
  1023. }
  1024. - (void)scheduleSleepTimerWithInterval:(NSTimeInterval)timeInterval
  1025. {
  1026. if (_sleepTimer) {
  1027. [_sleepTimer invalidate];
  1028. _sleepTimer = nil;
  1029. }
  1030. _sleepTimer = [NSTimer scheduledTimerWithTimeInterval:timeInterval target:self selector:@selector(stopPlayback) userInfo:nil repeats:NO];
  1031. }
  1032. #pragma mark - background interaction
  1033. - (void)applicationWillResignActive:(NSNotification *)aNotification
  1034. {
  1035. #if TARGET_OS_IOS
  1036. [self _savePlaybackState];
  1037. #endif
  1038. if (![[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingContinueAudioInBackgroundKey] boolValue]) {
  1039. if ([_mediaPlayer isPlaying]) {
  1040. [_mediaPlayer pause];
  1041. _shouldResumePlaying = YES;
  1042. }
  1043. }
  1044. }
  1045. - (void)applicationDidEnterBackground:(NSNotification *)notification
  1046. {
  1047. _preBackgroundWrapperView = _videoOutputViewWrapper;
  1048. if (!_renderer && _mediaPlayer.audioTrackIndexes.count > 0)
  1049. [self setVideoTrackEnabled:false];
  1050. }
  1051. - (void)applicationDidBecomeActive:(NSNotification *)notification
  1052. {
  1053. if (_preBackgroundWrapperView) {
  1054. [self setVideoOutputView:_preBackgroundWrapperView];
  1055. _preBackgroundWrapperView = nil;
  1056. }
  1057. [self setVideoTrackEnabled:true];
  1058. if (_shouldResumePlaying) {
  1059. _shouldResumePlaying = NO;
  1060. [_listPlayer play];
  1061. }
  1062. }
  1063. #pragma mark - remoteControlDelegate
  1064. - (void)remoteControlServiceHitPause:(VLCRemoteControlService *)rcs
  1065. {
  1066. [_listPlayer pause];
  1067. }
  1068. - (void)remoteControlServiceHitPlay:(VLCRemoteControlService *)rcs
  1069. {
  1070. [_listPlayer play];
  1071. }
  1072. - (void)remoteControlServiceTogglePlayPause:(VLCRemoteControlService *)rcs
  1073. {
  1074. [self playPause];
  1075. }
  1076. - (void)remoteControlServiceHitStop:(VLCRemoteControlService *)rcs
  1077. {
  1078. //TODO handle stop playback entirely
  1079. [_listPlayer stop];
  1080. }
  1081. - (BOOL)remoteControlServiceHitPlayNextIfPossible:(VLCRemoteControlService *)rcs
  1082. {
  1083. //TODO This doesn't handle shuffle or repeat yet
  1084. return [_listPlayer next];
  1085. }
  1086. - (BOOL)remoteControlServiceHitPlayPreviousIfPossible:(VLCRemoteControlService *)rcs
  1087. {
  1088. //TODO This doesn't handle shuffle or repeat yet
  1089. return [_listPlayer previous];
  1090. }
  1091. - (void)remoteControlService:(VLCRemoteControlService *)rcs jumpForwardInSeconds:(NSTimeInterval)seconds
  1092. {
  1093. [_mediaPlayer jumpForward:seconds];
  1094. }
  1095. - (void)remoteControlService:(VLCRemoteControlService *)rcs jumpBackwardInSeconds:(NSTimeInterval)seconds
  1096. {
  1097. [_mediaPlayer jumpBackward:seconds];
  1098. }
  1099. - (NSInteger)remoteControlServiceNumberOfMediaItemsinList:(VLCRemoteControlService *)rcs
  1100. {
  1101. return _mediaList.count;
  1102. }
  1103. - (void)remoteControlService:(VLCRemoteControlService *)rcs setPlaybackRate:(CGFloat)playbackRate
  1104. {
  1105. self.playbackRate = playbackRate;
  1106. }
  1107. - (void)remoteControlService:(VLCRemoteControlService *)rcs setCurrentPlaybackTime:(NSTimeInterval)playbackTime
  1108. {
  1109. float positionDiff = playbackTime - [self.metadata.elapsedPlaybackTime floatValue];
  1110. [_mediaPlayer jumpForward:positionDiff];
  1111. }
  1112. #pragma mark - helpers
  1113. - (NSDictionary *)mediaOptionsDictionary
  1114. {
  1115. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  1116. return @{ kVLCSettingNetworkCaching : [defaults objectForKey:kVLCSettingNetworkCaching],
  1117. kVLCSettingStretchAudio : [[defaults objectForKey:kVLCSettingStretchAudio] boolValue] ? kVLCSettingStretchAudioOnValue : kVLCSettingStretchAudioOffValue,
  1118. kVLCSettingTextEncoding : [defaults objectForKey:kVLCSettingTextEncoding],
  1119. kVLCSettingSkipLoopFilter : [defaults objectForKey:kVLCSettingSkipLoopFilter],
  1120. kVLCSettingHardwareDecoding : [defaults objectForKey:kVLCSettingHardwareDecoding]};
  1121. }
  1122. #pragma mark - Renderer
  1123. - (void)setRenderer:(VLCRendererItem * __nullable)renderer
  1124. {
  1125. _renderer = renderer;
  1126. [_mediaPlayer setRendererItem:_renderer];
  1127. }
  1128. @end