VLCPlaybackService.m 43 KB

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