VLCPlaybackController.m 43 KB

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