VLCMovieViewController.m 91.3 KB
Newer Older
1 2 3 4
/*****************************************************************************
 * VLCMovieViewController.m
 * VLC for iOS
 *****************************************************************************
5
 * Copyright (c) 2013-2015 VideoLAN. All rights reserved.
6 7 8 9 10 11 12 13 14 15 16
 * $Id$
 *
 * Authors: Felix Paul Kühne <fkuehne # videolan.org>
 *          Gleb Pinigin <gpinigin # gmail.com>
 *          Ahmad Harb <harb.dev.leb # gmail.com>
 *          Fabio Ritrovato <sephiroth87 # videolan.org>
 *          Pierre SAGASPE <pierre.sagaspe # me.com>
 *          Jean-Baptiste Kempf <jb # videolan.org>
 *
 * Refer to the COPYING file of the official project for license.
 *****************************************************************************/
Felix Paul Kühne's avatar
Felix Paul Kühne committed
17

18
#import "VLCMovieViewController.h"
19
#import "VLCExternalDisplayController.h"
20
#import <AVFoundation/AVFoundation.h>
21
#import <CommonCrypto/CommonDigest.h>
Felix Paul Kühne's avatar
Felix Paul Kühne committed
22
#import "UIDevice+VLC.h"
23
#import "VLCBugreporter.h"
24
#import "VLCThumbnailsCache.h"
25 26
#import "VLCTrackSelectorTableViewCell.h"
#import "VLCTrackSelectorHeaderView.h"
27
#import "VLCEqualizerView.h"
28
#import "VLCMultiSelectionMenuView.h"
29 30 31

#import "OBSlider.h"
#import "VLCStatusLabel.h"
Felix Paul Kühne's avatar
Felix Paul Kühne committed
32

33 34
#define INPUT_RATE_DEFAULT  1000.

35 36 37
#define TRACK_SELECTOR_TABLEVIEW_CELL @"track selector table view cell"
#define TRACK_SELECTOR_TABLEVIEW_SECTIONHEADER @"track selector table view section header"

38 39 40 41
#define LOCKCHECK \
if (_interfaceIsLocked) \
return

42 43 44 45 46 47 48
typedef NS_ENUM(NSInteger, VLCPanType) {
  VLCPanTypeNone,
  VLCPanTypeBrightness,
  VLCPanTypeSeek,
  VLCPanTypeVolume,
};

49
@interface VLCMovieViewController () <UIGestureRecognizerDelegate, AVAudioSessionDelegate, VLCMediaDelegate, UITableViewDataSource, UITableViewDelegate, VLCEqualizerViewDelegate, VLCMultiSelectionViewDelegate>
50
{
51
    VLCMediaListPlayer *_listPlayer;
52 53 54 55 56 57 58 59 60 61 62 63 64 65
    VLCMediaPlayer *_mediaPlayer;

    BOOL _controlsHidden;
    BOOL _videoFiltersHidden;
    BOOL _playbackSpeedViewHidden;

    UIActionSheet *_subtitleActionSheet;
    UIActionSheet *_audiotrackActionSheet;

    float _currentPlaybackRate;
    NSArray *_aspectRatios;
    NSUInteger _currentAspectRatioMask;

    NSTimer *_idleTimer;
66
    NSTimer *_sleepTimer;
67

68
    BOOL _shouldResumePlaying;
69
    BOOL _viewAppeared;
70
    BOOL _displayRemainingTime;
71
    BOOL _positionSet;
72
    BOOL _playerIsSetup;
73
    BOOL _isScrubbing;
74
    BOOL _interfaceIsLocked;
75
    BOOL _switchingTracksNotChapters;
Ahmad Harb's avatar
Ahmad Harb committed
76 77

    BOOL _swipeGesturesEnabled;
78
    UIPinchGestureRecognizer *_pinchRecognizer;
79
    VLCPanType _currentPanType;
Ahmad Harb's avatar
Ahmad Harb committed
80 81 82
    UIPanGestureRecognizer *_panRecognizer;
    UISwipeGestureRecognizer *_swipeRecognizerLeft;
    UISwipeGestureRecognizer *_swipeRecognizerRight;
83 84
    UISwipeGestureRecognizer *_swipeRecognizerUp;
    UISwipeGestureRecognizer *_swipeRecognizerDown;
85
    UITapGestureRecognizer *_tapRecognizer;
86
    UITapGestureRecognizer *_tapOnVideoRecognizer;
87 88 89

    UIView *_trackSelectorContainer;
    UITableView *_trackSelectorTableView;
90 91

    VLCEqualizerView *_equalizerView;
92
    VLCMultiSelectionMenuView *_multiSelectionView;
93 94 95 96

    UIView *_sleepTimerContainer;
    UIDatePicker *_sleepTimeDatePicker;

97
    NSInteger _mediaDuration;
98
    BOOL _playbackFailed;
99 100
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
101 102
@property (nonatomic, strong) UIPopoverController *masterPopoverController;
@property (nonatomic, strong) UIWindow *externalWindow;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
103 104
@end

105
@implementation VLCMovieViewController
Felix Paul Kühne's avatar
Felix Paul Kühne committed
106

107 108 109 110 111 112 113
+ (void)initialize
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSDictionary *appDefaults = @{kVLCShowRemainingTime : @(YES)};
    [defaults registerDefaults:appDefaults];
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
114 115
- (void)dealloc
{
116 117
    [[NSNotificationCenter defaultCenter] removeObserver:self];

118
    if (_tapRecognizer)
119
        [self.view removeGestureRecognizer:_tapRecognizer];
Ahmad Harb's avatar
Ahmad Harb committed
120
    if (_swipeRecognizerLeft)
121
        [self.view removeGestureRecognizer:_swipeRecognizerLeft];
Ahmad Harb's avatar
Ahmad Harb committed
122
    if (_swipeRecognizerRight)
123
        [self.view removeGestureRecognizer:_swipeRecognizerRight];
124 125 126 127
    if (_swipeRecognizerUp)
        [self.view removeGestureRecognizer:_swipeRecognizerUp];
    if (_swipeRecognizerDown)
        [self.view removeGestureRecognizer:_swipeRecognizerDown];
Ahmad Harb's avatar
Ahmad Harb committed
128
    if (_panRecognizer)
129
        [self.view removeGestureRecognizer:_panRecognizer];
130 131 132 133
    if (_pinchRecognizer)
        [self.view removeGestureRecognizer:_pinchRecognizer];
    [self.view removeGestureRecognizer:_tapOnVideoRecognizer];

134 135 136
    _tapRecognizer = nil;
    _swipeRecognizerLeft = nil;
    _swipeRecognizerRight = nil;
137 138
    _swipeRecognizerUp = nil;
    _swipeRecognizerDown = nil;
139
    _panRecognizer = nil;
140 141 142 143
    _pinchRecognizer = nil;
    _tapOnVideoRecognizer = nil;

    [[NSNotificationCenter defaultCenter] removeObserver:self];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
144 145
}

146
#pragma mark - Managing the media item
Felix Paul Kühne's avatar
Felix Paul Kühne committed
147

148
- (void)setFileFromMediaLibrary:(id)newFile
Felix Paul Kühne's avatar
Felix Paul Kühne committed
149
{
150
    if (_fileFromMediaLibrary != newFile) {
151
        [self _stopPlayback];
152
        _fileFromMediaLibrary = newFile;
153 154 155
        if (_viewAppeared)
            [self _startPlayback];
    }
Felix Paul Kühne's avatar
Felix Paul Kühne committed
156

157
    if (self.masterPopoverController != nil)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
158 159 160
        [self.masterPopoverController dismissPopoverAnimated:YES];
}

161 162
- (void)setUrl:(NSURL *)url
{
163 164 165 166 167
    [self _stopPlayback];
    _url = url;
    _playerIsSetup = NO;
    if (_viewAppeared)
        [self _startPlayback];
168 169
}

170 171 172 173 174 175 176 177 178
- (void)setMediaList:(VLCMediaList *)mediaList
{
    [self _stopPlayback];
    _mediaList = mediaList;
    _playerIsSetup = NO;
    if (_viewAppeared)
        [self _startPlayback];
}

179
- (void)viewDidLoad
Felix Paul Kühne's avatar
Felix Paul Kühne committed
180
{
181
    [super viewDidLoad];
182 183
    CGRect rect;

184 185
    self.wantsFullScreenLayout = YES;

186 187
    self.videoFilterView.hidden = YES;
    _videoFiltersHidden = YES;
188
    _hueLabel.text = NSLocalizedString(@"VFILTER_HUE", nil);
189 190
    _hueSlider.accessibilityLabel = _hueLabel.text;
    _hueSlider.isAccessibilityElement = YES;
191
    _contrastLabel.text = NSLocalizedString(@"VFILTER_CONTRAST", nil);
192 193
    _contrastSlider.accessibilityLabel = _contrastLabel.text;
    _contrastSlider.isAccessibilityElement = YES;
194
    _brightnessLabel.text = NSLocalizedString(@"VFILTER_BRIGHTNESS", nil);
195 196
    _brightnessSlider.accessibilityLabel = _brightnessLabel.text;
    _brightnessSlider.isAccessibilityElement = YES;
197
    _saturationLabel.text = NSLocalizedString(@"VFILTER_SATURATION", nil);
198 199
    _saturationSlider.accessibilityLabel = _saturationLabel.text;
    _saturationSlider.isAccessibilityElement = YES;
200
    _gammaLabel.text = NSLocalizedString(@"VFILTER_GAMMA", nil);
201 202
    _gammaSlider.accessibilityLabel = _gammaLabel.text;
    _gammaSlider.isAccessibilityElement = YES;
203
    _playbackSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SPEED", nil);
204 205
    _playbackSpeedSlider.accessibilityLabel = _playbackSpeedLabel.text;
    _playbackSpeedSlider.isAccessibilityElement = YES;
206 207 208 209 210 211
    _audioDelayLabel.text = NSLocalizedString(@"AUDIO_DELAY", nil);
    _audioDelaySlider.accessibilityLabel = _audioDelayLabel.text;
    _audioDelaySlider.isAccessibilityElement = YES;
    _spuDelayLabel.text = NSLocalizedString(@"SPU_DELAY", nil);
    _spuDelaySlider.accessibilityLabel = _spuDelayLabel.text;
    _spuDelaySlider.isAccessibilityElement = YES;
212

213
    _positionSlider.accessibilityLabel = NSLocalizedString(@"PLAYBACK_POSITION", nil);
214 215 216
    _positionSlider.isAccessibilityElement = YES;
    _timeDisplay.isAccessibilityElement = YES;

217 218 219 220
    _trackSwitcherButton.accessibilityLabel = NSLocalizedString(@"OPEN_TRACK_PANEL", nil);
    _trackSwitcherButton.isAccessibilityElement = YES;
    _trackSwitcherButtonLandscape.accessibilityLabel = NSLocalizedString(@"OPEN_TRACK_PANEL", nil);
    _trackSwitcherButtonLandscape.isAccessibilityElement = YES;
221 222
    _playbackSpeedButton.accessibilityLabel = _playbackSpeedLabel.text;
    _playbackSpeedButton.isAccessibilityElement = YES;
223 224
    _playbackSpeedButtonLandscape.accessibilityLabel = _playbackSpeedLabel.text;
    _playbackSpeedButtonLandscape.isAccessibilityElement = YES;
225
    _videoFilterButton.accessibilityLabel = NSLocalizedString(@"VIDEO_FILTER", nil);
226
    _videoFilterButton.isAccessibilityElement = YES;
227
    _videoFilterButtonLandscape.accessibilityLabel = NSLocalizedString(@"VIDEO_FILTER", nil);
228
    _videoFilterButtonLandscape.isAccessibilityElement = YES;
229
    _resetVideoFilterButton.accessibilityLabel = NSLocalizedString(@"VIDEO_FILTER_RESET_BUTTON", nil);
230
    _resetVideoFilterButton.isAccessibilityElement = YES;
231
    _aspectRatioButton.accessibilityLabel = NSLocalizedString(@"VIDEO_ASPECT_RATIO_BUTTON", nil);
232
    _aspectRatioButton.isAccessibilityElement = YES;
233
    _playPauseButton.accessibilityLabel = NSLocalizedString(@"PLAY_PAUSE_BUTTON", nil);
234
    _playPauseButton.isAccessibilityElement = YES;
235
    _playPauseButtonLandscape.accessibilityLabel = NSLocalizedString(@"PLAY_PAUSE_BUTTON", nil);
236
    _playPauseButtonLandscape.isAccessibilityElement = YES;
237
    _bwdButton.accessibilityLabel = NSLocalizedString(@"BWD_BUTTON", nil);
238
    _bwdButton.isAccessibilityElement = YES;
239
    _bwdButtonLandscape.accessibilityLabel = NSLocalizedString(@"BWD_BUTTON", nil);
240
    _bwdButtonLandscape.isAccessibilityElement = YES;
241
    _fwdButton.accessibilityLabel = NSLocalizedString(@"FWD_BUTTON", nil);
242
    _fwdButton.isAccessibilityElement = YES;
243
    _fwdButtonLandscape.accessibilityLabel = NSLocalizedString(@"FWD_BUTTON", nil);
244
    _fwdButtonLandscape.isAccessibilityElement = YES;
245 246 247
    _sleepTimerButton.accessibilityLabel = NSLocalizedString(@"BUTTON_SLEEP_TIMER", nil);
    _sleepTimerButton.isAccessibilityElement = YES;
    [_sleepTimerButton setTitle:NSLocalizedString(@"BUTTON_SLEEP_TIMER", nil) forState:UIControlStateNormal];
248 249
    if (!SYSTEM_RUNS_IOS7_OR_LATER)
        _sleepTimerButton.hidden = YES;
250

251 252 253 254 255 256
    _multiSelectionView = [[VLCMultiSelectionMenuView alloc] init];
    _multiSelectionView.delegate = self;
    _multiSelectionView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin;
    _multiSelectionView.hidden = YES;
    [self.view addSubview:_multiSelectionView];

257
    _scrubHelpLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_HELP", nil);
258

259 260
    self.playbackSpeedView.hidden = YES;
    _playbackSpeedViewHidden = YES;
261

262 263 264 265 266
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(handleExternalScreenDidConnect:)
                   name:UIScreenDidConnectNotification object:nil];
    [center addObserver:self selector:@selector(handleExternalScreenDidDisconnect:)
                   name:UIScreenDidDisconnectNotification object:nil];
267 268 269 270 271 272
    [center addObserver:self selector:@selector(applicationWillResignActive:)
                   name:UIApplicationWillResignActiveNotification object:nil];
    [center addObserver:self selector:@selector(applicationDidBecomeActive:)
                   name:UIApplicationDidBecomeActiveNotification object:nil];
    [center addObserver:self selector:@selector(applicationDidEnterBackground:)
                   name:UIApplicationDidEnterBackgroundNotification object:nil];
273 274
    [center addObserver:self selector:@selector(audioSessionRouteChange:)
                   name:AVAudioSessionRouteChangeNotification object:nil];
275

276 277
    _playingExternallyTitle.text = NSLocalizedString(@"PLAYING_EXTERNALLY_TITLE", nil);
    _playingExternallyDescription.text = NSLocalizedString(@"PLAYING_EXTERNALLY_DESC", nil);
278
    if ([self hasExternalDisplay])
279
        [self showOnExternalDisplay];
280

281 282
    self.trackNameLabel.text = self.artistNameLabel.text = self.albumNameLabel.text = @"";

283
    _movieView.userInteractionEnabled = NO;
284 285 286
    _tapOnVideoRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleControlsVisible)];
    _tapOnVideoRecognizer.delegate = self;
    [self.view addGestureRecognizer:_tapOnVideoRecognizer];
287

288 289 290
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    _displayRemainingTime = [[defaults objectForKey:kVLCShowRemainingTime] boolValue];
    _swipeGesturesEnabled = [[defaults objectForKey:kVLCSettingPlaybackGestures] boolValue];
291

292 293 294 295 296 297
    _pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchGesture:)];
    _pinchRecognizer.delegate = self;
    [self.view addGestureRecognizer:_pinchRecognizer];

    _tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapRecognized)];
    [_tapRecognizer setNumberOfTouchesRequired:2];
298 299

    _currentPanType = VLCPanTypeNone;
300 301 302 303 304 305 306 307
    _panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panRecognized:)];
    [_panRecognizer setMinimumNumberOfTouches:1];
    [_panRecognizer setMaximumNumberOfTouches:1];

    _swipeRecognizerLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRecognized:)];
    _swipeRecognizerLeft.direction = UISwipeGestureRecognizerDirectionLeft;
    _swipeRecognizerRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRecognized:)];
    _swipeRecognizerRight.direction = UISwipeGestureRecognizerDirectionRight;
308 309 310 311 312 313
    _swipeRecognizerUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRecognized:)];
    _swipeRecognizerUp.direction = UISwipeGestureRecognizerDirectionUp;
    _swipeRecognizerUp.numberOfTouchesRequired = 2;
    _swipeRecognizerDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRecognized:)];
    _swipeRecognizerDown.direction = UISwipeGestureRecognizerDirectionDown;
    _swipeRecognizerDown.numberOfTouchesRequired = 2;
314 315 316

    [self.view addGestureRecognizer:_swipeRecognizerLeft];
    [self.view addGestureRecognizer:_swipeRecognizerRight];
317 318
    [self.view addGestureRecognizer:_swipeRecognizerUp];
    [self.view addGestureRecognizer:_swipeRecognizerDown];
319 320 321 322
    [self.view addGestureRecognizer:_panRecognizer];
    [self.view addGestureRecognizer:_tapRecognizer];
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerLeft];
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerRight];
323 324
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerUp];
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerDown];
325 326 327 328

    _panRecognizer.delegate = self;
    _swipeRecognizerRight.delegate = self;
    _swipeRecognizerLeft.delegate = self;
329 330
    _swipeRecognizerUp.delegate = self;
    _swipeRecognizerDown.delegate = self;
331
    _tapRecognizer.delegate = self;
332

333
    _aspectRatios = @[@"DEFAULT", @"FILL_TO_SCREEN", @"4:3", @"16:9", @"16:10", @"2.21:1"];
334

335
    [self.aspectRatioButton setImage:[UIImage imageNamed:@"ratioIcon"] forState:UIControlStateNormal];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
336
    if (SYSTEM_RUNS_IOS7_OR_LATER) {
337
        self.backButton.tintColor = [UIColor colorWithRed:(190.0f/255.0f) green:(190.0f/255.0f) blue:(190.0f/255.0f) alpha:1.];
338
        self.toolbar.tintColor = [UIColor whiteColor];
339
        self.toolbar.barStyle = UIBarStyleBlack;
340

341
        rect = self.resetVideoFilterButton.frame;
342 343
        rect.origin.y = rect.origin.y + 5.;
        self.resetVideoFilterButton.frame = rect;
344 345 346 347
        rect = self.toolbar.frame;
        rect.size.height = rect.size.height + rect.origin.y;
        rect.origin.y = 0;
        self.toolbar.frame = rect;
348 349 350 351 352 353 354 355 356 357
        rect = self.aspectRatioButton.frame;
        rect.size.width -= 19.;
        rect.origin.x += 19.;
        self.aspectRatioButton.frame = rect;
        rect = self.timeDisplay.frame;
        rect.origin.x += 19.;
        self.timeDisplay.frame = rect;
        rect = self.positionSlider.frame;
        rect.size.width += 19.;
        self.positionSlider.frame = rect;
358
    } else {
359
        rect = self.positionSlider.frame;
360 361
        rect.origin.y = rect.origin.y + 3.;
        self.positionSlider.frame = rect;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
362 363
        [self.aspectRatioButton setBackgroundImage:[UIImage imageNamed:@"ratioButton"] forState:UIControlStateNormal];
        [self.aspectRatioButton setBackgroundImage:[UIImage imageNamed:@"ratioButtonHighlight"] forState:UIControlStateHighlighted];
364
        [self.toolbar setBackgroundImage:[UIImage imageNamed:@"seekbarBg"] forBarMetrics:UIBarMetricsDefault];
365 366 367
        [self.backButton setBackgroundImage:[UIImage imageNamed:@"playbackDoneButton"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
        [self.backButton setBackgroundImage:[UIImage imageNamed:@"playbackDoneButtonHighlight"] forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
    }
368

369
    /* FIXME: there is a saner iOS 6+ API for this! */
370
    /* this looks a bit weird, but we need to support iOS 5 and should show the same appearance */
371 372 373 374 375 376 377
    void (^initVolumeSlider)(MPVolumeView *) = ^(MPVolumeView *volumeView){
        UISlider *volumeSlider = nil;
        for (id aView in volumeView.subviews){
            if ([[[aView class] description] isEqualToString:@"MPVolumeSlider"]){
                volumeSlider = (UISlider *)aView;
                break;
            }
378
        }
379 380 381 382 383 384 385 386 387 388 389 390 391
        if (!SYSTEM_RUNS_IOS7_OR_LATER) {
            [volumeSlider setMinimumTrackImage:[[UIImage imageNamed:@"sliderminiValue"]resizableImageWithCapInsets:UIEdgeInsetsMake(0, 4, 0, 0)] forState:UIControlStateNormal];
            [volumeSlider setMaximumTrackImage:[[UIImage imageNamed:@"slidermaxValue"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 4)] forState:UIControlStateNormal];
            [volumeSlider setThumbImage:[UIImage imageNamed:@"volumeballslider"] forState:UIControlStateNormal];
        } else
            [volumeView setVolumeThumbImage:[UIImage imageNamed:@"modernSliderKnob"] forState:UIControlStateNormal];
        [volumeSlider addTarget:self
                         action:@selector(volumeSliderAction:)
               forControlEvents:UIControlEventValueChanged];
    };

    initVolumeSlider(self.volumeView);
    initVolumeSlider(self.volumeViewLandscape);
392

393
    [[AVAudioSession sharedInstance] setDelegate:self];
394 395 396

    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
        self.positionSlider.scrubbingSpeedChangePositions = @[@(0.), @(100.), @(200.), @(300)];
397 398

    _playerIsSetup = NO;
399

400 401
    [self.movieView setAccessibilityLabel:NSLocalizedString(@"VO_VIDEOPLAYER_TITLE", nil)];
    [self.movieView setAccessibilityHint:NSLocalizedString(@"VO_VIDEOPLAYER_DOUBLETAP", nil)];
402 403

    rect = self.view.frame;
404 405 406 407 408 409 410 411 412
    CGFloat width;
    CGFloat height;
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        width = 300.;
        height = 320.;
    } else {
        width = 450.;
        height = 470.;
    }
413

414
    _trackSelectorTableView = [[UITableView alloc] initWithFrame:CGRectMake(0., 0., width, height) style:UITableViewStylePlain];
415 416
    _trackSelectorTableView.delegate = self;
    _trackSelectorTableView.dataSource = self;
417
    _trackSelectorTableView.separatorColor = [UIColor clearColor];
418 419 420
    _trackSelectorTableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
    [_trackSelectorTableView registerClass:[VLCTrackSelectorTableViewCell class] forCellReuseIdentifier:TRACK_SELECTOR_TABLEVIEW_CELL];
    [_trackSelectorTableView registerClass:[VLCTrackSelectorHeaderView class] forHeaderFooterViewReuseIdentifier:TRACK_SELECTOR_TABLEVIEW_SECTIONHEADER];
421
    _trackSelectorTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
422

423
    _trackSelectorContainer = [[VLCFrostedGlasView alloc] initWithFrame:CGRectMake((rect.size.width - width) / 2., (rect.size.height - height) / 2., width, height)];
424
    [_trackSelectorContainer addSubview:_trackSelectorTableView];
425
    _trackSelectorContainer.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleHeight;
426 427 428 429 430 431
    _trackSelectorContainer.hidden = YES;

    if ([[UIDevice currentDevice] speedCategory] >= 3) {
        _trackSelectorTableView.opaque = NO;
        _trackSelectorTableView.backgroundColor = [UIColor clearColor];
    } else
432
        _trackSelectorTableView.backgroundColor = [UIColor blackColor];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
433
    _trackSelectorTableView.allowsMultipleSelection = YES;
434 435

    [self.view addSubview:_trackSelectorContainer];
436

437 438 439 440 441
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        _equalizerView = [[VLCEqualizerView alloc] initWithFrame:CGRectMake((rect.size.width - 450.) / 2., self.controllerPanel.frame.origin.y - 200., 450., 240.)];
    } else {
        _equalizerView = [[VLCEqualizerView alloc] initWithFrame:CGRectMake((rect.size.width - 450.) / 2., self.controllerPanel.frame.origin.y - 240., 450., 240.)];
    }
442 443 444 445
    _equalizerView.delegate = self;
    _equalizerView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin;
    _equalizerView.hidden = YES;
    [self.view addSubview:_equalizerView];
446

447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
    /* add sleep timer UI, requires iOS 7 or later */
    if (SYSTEM_RUNS_IOS7_OR_LATER) {
        _sleepTimerContainer = [[VLCFrostedGlasView alloc] initWithFrame:CGRectMake(0., 0., 300., 162.)];
        _sleepTimerContainer.center = self.view.center;
        _sleepTimerContainer.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin;

        _sleepTimeDatePicker = [[UIDatePicker alloc] init];
        if ([[UIDevice currentDevice] speedCategory] >= 3) {
            _sleepTimeDatePicker.opaque = NO;
            _sleepTimeDatePicker.backgroundColor = [UIColor clearColor];
        } else
            _sleepTimeDatePicker.backgroundColor = [UIColor blackColor];
        _sleepTimeDatePicker.tintColor = [UIColor VLCLightTextColor];
        _sleepTimeDatePicker.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin;
        [_sleepTimerContainer addSubview:_sleepTimeDatePicker];

        /* adapt the date picker style to suit our needs */
        [_sleepTimeDatePicker setValue:[UIColor whiteColor] forKeyPath:@"textColor"];
        SEL selector = NSSelectorFromString(@"setHighlightsToday:");
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDatePicker instanceMethodSignatureForSelector:selector]];
        BOOL no = NO;
        [invocation setSelector:selector];
        [invocation setArgument:&no atIndex:2];
        [invocation invokeWithTarget:_sleepTimeDatePicker];

        if (_sleepTimerContainer.subviews.count > 0) {
            NSArray *subviewsOfSubview = [_sleepTimeDatePicker.subviews[0] subviews];
            NSUInteger subviewCount = subviewsOfSubview.count;
            for (NSUInteger x = 0; x < subviewCount; x++) {
                if ([subviewsOfSubview[x] isKindOfClass:[UILabel class]])
                    [subviewsOfSubview[x] setTextColor:[UIColor VLCLightTextColor]];
            }
479
        }
480 481 482 483 484
        _sleepTimeDatePicker.datePickerMode = UIDatePickerModeCountDownTimer;
        _sleepTimeDatePicker.minuteInterval = 5;
        _sleepTimeDatePicker.minimumDate = [NSDate date];
        _sleepTimeDatePicker.countDownDuration = 1200.;
        [_sleepTimeDatePicker addTarget:self action:@selector(sleepTimerAction:) forControlEvents:UIControlEventValueChanged];
485

486 487
        [self.view addSubview:_sleepTimerContainer];
    }
488
}
Felix Paul Kühne's avatar
Felix Paul Kühne committed
489

490 491 492 493 494
- (BOOL)_blobCheck
{
    NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *directoryPath = searchPaths[0];

495
    if (![[NSFileManager defaultManager] fileExistsAtPath:[directoryPath stringByAppendingPathComponent:@"blob.bin"]])
496 497
        return NO;

498
    NSData *data = [NSData dataWithContentsOfFile:[directoryPath stringByAppendingPathComponent:@"blob.bin"]];
499
    uint8_t digest[CC_SHA1_DIGEST_LENGTH];
500
    CC_SHA1(data.bytes, (unsigned int)data.length, digest);
501 502 503 504 505 506 507 508 509 510 511 512

    NSMutableString *hash = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];

    for (unsigned int u = 0; u < CC_SHA1_DIGEST_LENGTH; u++)
        [hash appendFormat:@"%02x", digest[u]];

    if ([hash isEqualToString:kBlobHash])
        return YES;
    else
        return NO;
}

513 514
- (void)viewWillAppear:(BOOL)animated
{
515
    [super viewWillAppear:animated];
516

517 518 519 520 521 522
    /* reset audio meta data views */
    self.artworkImageView.image = nil;
    self.trackNameLabel.text = nil;
    self.artistNameLabel.text = nil;
    self.albumNameLabel.text = nil;

523 524
    _swipeGesturesEnabled = [[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingPlaybackGestures] boolValue];

525
    [self.navigationController setNavigationBarHidden:YES animated:YES];
526

Felix Paul Kühne's avatar
Felix Paul Kühne committed
527
    if (!SYSTEM_RUNS_IOS7_OR_LATER) {
528 529 530
        if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
            [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleBlackTranslucent;
    }
531

532 533 534 535 536 537
    [self _startPlayback];

    [self setControlsHidden:NO animated:YES];
    _viewAppeared = YES;
}

538 539
- (void)viewWillLayoutSubviews
{
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572
    CGRect multiSelectionFrame;
    CGRect controllerPanelFrame;

    if (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPhone) {
        controllerPanelFrame = _controllerPanel.frame;
        multiSelectionFrame = (CGRect){CGPointMake(0., 0.), [_multiSelectionView proposedDisplaySize]};
        multiSelectionFrame.origin.x = controllerPanelFrame.size.width - multiSelectionFrame.size.width;
        multiSelectionFrame.origin.y = controllerPanelFrame.origin.y - multiSelectionFrame.size.height;
        _multiSelectionView.frame = multiSelectionFrame;
        _multiSelectionView.showsEqualizer = YES;
        return;
    }

    CGSize viewSize = self.view.frame.size;

    if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
        [_controllerPanel removeFromSuperview];
        controllerPanelFrame = (CGRect){CGPointMake(0, viewSize.height - _controllerPanelLandscape.frame.size.height), CGSizeMake(viewSize.width, _controllerPanelLandscape.frame.size.height)};
        _controllerPanelLandscape.frame = controllerPanelFrame;
        [self.view addSubview:_controllerPanelLandscape];
        _multiSelectionView.showsEqualizer = YES;
        multiSelectionFrame = (CGRect){CGPointMake(0., 0.), [_multiSelectionView proposedDisplaySize]};
        multiSelectionFrame.origin.x = controllerPanelFrame.size.width - multiSelectionFrame.size.width;
        multiSelectionFrame.origin.y = controllerPanelFrame.origin.y - multiSelectionFrame.size.height;
    } else {
        [_controllerPanelLandscape removeFromSuperview];
        controllerPanelFrame = (CGRect){CGPointMake(0, viewSize.height - _controllerPanel.frame.size.height), CGSizeMake(viewSize.width, _controllerPanel.frame.size.height)};
        _controllerPanel.frame = controllerPanelFrame;
        [self.view addSubview:_controllerPanel];
        _multiSelectionView.showsEqualizer = NO;
        multiSelectionFrame = (CGRect){CGPointMake(0., 0.), [_multiSelectionView proposedDisplaySize]};
        multiSelectionFrame.origin.x = controllerPanelFrame.size.width - multiSelectionFrame.size.width;
        multiSelectionFrame.origin.y = controllerPanelFrame.origin.y - multiSelectionFrame.size.height;
573
    }
574 575

    _multiSelectionView.frame = multiSelectionFrame;
576 577
}

578 579 580 581 582
- (void)_startPlayback
{
    if (_playerIsSetup)
        return;

583 584
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

585
    if (!self.fileFromMediaLibrary && !self.url && !self.mediaList) {
586
        [self _stopPlayback];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
587
        return;
588
    }
589 590 591
    if (self.pathToExternalSubtitlesFile)
        _listPlayer = [[VLCMediaListPlayer alloc] initWithOptions:@[[NSString stringWithFormat:@"--%@=%@", kVLCSettingSubtitlesFilePath, self.pathToExternalSubtitlesFile]]];
    else
Felix Paul Kühne's avatar
Felix Paul Kühne committed
592
        _listPlayer = [[VLCMediaListPlayer alloc] init];
593

594
    _mediaPlayer = _listPlayer.mediaPlayer;
595 596
    [_mediaPlayer setDelegate:self];
    [_mediaPlayer setDrawable:self.movieView];
597 598
    if ([[defaults objectForKey:kVLCSettingPlaybackSpeedDefaultValue] floatValue] != 0)
        [_mediaPlayer setRate: [[defaults objectForKey:kVLCSettingPlaybackSpeedDefaultValue] floatValue]];
599 600 601 602
    if ([[defaults objectForKey:kVLCSettingDeinterlace] intValue] != 0)
        [_mediaPlayer setDeinterlaceFilter:@"blend"];
    else
        [_mediaPlayer setDeinterlaceFilter:nil];
603 604 605
    if (self.pathToExternalSubtitlesFile)
        [_mediaPlayer openVideoSubTitlesFromFile:self.pathToExternalSubtitlesFile];

606
    self.trackNameLabel.text = self.artistNameLabel.text = self.albumNameLabel.text = @"";
607

608
    VLCMedia *media;
609 610
    if (self.fileFromMediaLibrary) {
        MLFile *item = self.fileFromMediaLibrary;
611
        media = [VLCMedia mediaWithURL:[NSURL URLWithString:item.url]];
612 613 614 615
    } else if (self.mediaList) {
        media = [self.mediaList mediaAtIndex:self.itemInMediaListToBePlayedFirst];
        [media parse];
    } else {
616
        media = [VLCMedia mediaWithURL:self.url];
617 618
        [media parse];
    }
Felix Paul Kühne's avatar
Felix Paul Kühne committed
619

620 621
    NSMutableDictionary *mediaDictionary = [[NSMutableDictionary alloc] init];
    [mediaDictionary setObject:[defaults objectForKey:kVLCSettingNetworkCaching] forKey:kVLCSettingNetworkCaching];
622 623 624
    [mediaDictionary setObject:[[defaults objectForKey:kVLCSettingStretchAudio] boolValue] ? kVLCSettingStretchAudioOnValue : kVLCSettingStretchAudioOffValue forKey:kVLCSettingStretchAudio];
    [mediaDictionary setObject:[defaults objectForKey:kVLCSettingTextEncoding] forKey:kVLCSettingTextEncoding];
    [mediaDictionary setObject:[defaults objectForKey:kVLCSettingSkipLoopFilter] forKey:kVLCSettingSkipLoopFilter];
625

626 627 628 629
    [NSTimeZone resetSystemTimeZone];
    NSString *tzName = [[NSTimeZone systemTimeZone] name];
    NSArray *tzNames = @[@"America/Adak", @"America/Anchorage", @"America/Boise", @"America/Chicago", @"America/Denver", @"America/Detroit", @"America/Indiana/Indianapolis", @"America/Indiana/Knox", @"America/Indiana/Marengo", @"America/Indiana/Petersburg", @"America/Indiana/Tell_City", @"America/Indiana/Vevay", @"America/Indiana/Vincennes", @"America/Indiana/Winamac", @"America/Juneau", @"America/Kentucky/Louisville", @"America/Kentucky/Monticello", @"America/Los_Angeles", @"America/Menominee", @"America/Metlakatla", @"America/New_York", @"America/Nome", @"America/North_Dakota/Beulah", @"America/North_Dakota/Center", @"America/North_Dakota/New_Salem", @"America/Phoenix", @"America/Puerto_Rico", @"America/Shiprock", @"America/Sitka", @"America/St_Thomas", @"America/Thule", @"America/Yakutat", @"Pacific/Guam", @"Pacific/Honolulu", @"Pacific/Johnston", @"Pacific/Kwajalein", @"Pacific/Midway", @"Pacific/Pago_Pago", @"Pacific/Saipan", @"Pacific/Wake"];

630
    if ([tzNames containsObject:tzName] || [[tzName stringByDeletingLastPathComponent] isEqualToString:@"US"]) {
631 632 633 634 635 636 637 638 639 640 641 642
        NSArray *tracksInfo = media.tracksInformation;
        for (NSUInteger x = 0; x < tracksInfo.count; x++) {
            if ([[tracksInfo[x] objectForKey:VLCMediaTracksInformationType] isEqualToString:VLCMediaTracksInformationTypeAudio])
            {
                NSInteger fourcc = [[tracksInfo[x] objectForKey:VLCMediaTracksInformationCodec] integerValue];

                switch (fourcc) {
                    case 540161377:
                    case 1647457633:
                    case 858612577:
                    case 862151027:
                    case 862151013:
643 644
                    case 1684566644:
                    case 2126701:
645
                    {
646
                        if (![self _blobCheck]) {
647
                            [mediaDictionary setObject:[NSNull null] forKey:@"no-audio"];
648 649
                            APLog(@"audio playback disabled because an unsupported codec was found");
                        }
650 651 652 653 654 655 656 657 658 659
                        break;
                    }

                    default:
                        break;
                }
            }
        }
    }

660 661 662 663 664 665 666 667 668 669
    if (self.mediaList) {
        VLCMediaList *list = self.mediaList;
        NSUInteger count = list.count;
        for (NSUInteger x = 0; x < count; x++)
            [[list mediaAtIndex:x] addOptions:mediaDictionary];
        [_listPlayer setMediaList:self.mediaList];
    } else {
        [media addOptions:mediaDictionary];
        [_listPlayer setRootMedia:media];
    }
670
    [_listPlayer setRepeatMode:VLCDoNotRepeat];
671

672
    self.positionSlider.value = 0.;
673
    [self.timeDisplay setTitle:@"" forState:UIControlStateNormal];
674
    self.timeDisplay.accessibilityLabel = @"";
675

676
    if (![self _isMediaSuitableForDevice]) {
677
        UIAlertView * alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"DEVICE_TOOSLOW_TITLE", nil) message:[NSString stringWithFormat:NSLocalizedString(@"DEVICE_TOOSLOW", nil), [[UIDevice currentDevice] model], self.fileFromMediaLibrary.title] delegate:self cancelButtonTitle:NSLocalizedString(@"BUTTON_CANCEL", nil) otherButtonTitles:NSLocalizedString(@"BUTTON_OPEN", nil), nil];
678 679 680
        [alert show];
    } else
        [self _playNewMedia];
681

682 683
    if (![self hasExternalDisplay])
        self.brightnessSlider.value = [UIScreen mainScreen].brightness * 2.;
684 685 686 687
}

- (BOOL)_isMediaSuitableForDevice
{
688
    if (!self.fileFromMediaLibrary)
689 690
        return YES;

691
    NSUInteger totalNumberOfPixels = [[[self.fileFromMediaLibrary videoTrack] valueForKey:@"width"] doubleValue] * [[[self.fileFromMediaLibrary videoTrack] valueForKey:@"height"] doubleValue];
692

693
    NSInteger speedCategory = [[UIDevice currentDevice] speedCategory];
694

695
    if (speedCategory == 1) {
696 697
        // iPhone 3GS, iPhone 4, first gen. iPad, 3rd and 4th generation iPod touch
        return (totalNumberOfPixels < 600000); // between 480p and 720p
698
    } else if (speedCategory == 2) {
699 700
        // iPhone 4S, iPad 2 and 3, iPod 4 and 5
        return (totalNumberOfPixels < 922000); // 720p
701
    } else if (speedCategory == 3) {
702 703
        // iPhone 5, iPad 4
        return (totalNumberOfPixels < 2074000); // 1080p
704 705 706
    } else if (speedCategory == 4) {
        // iPhone 6, 2014 iPads
        return (totalNumberOfPixels < 8850000); // 4K
707 708 709 710 711 712 713 714 715
    }

    return YES;
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 1)
        [self _playNewMedia];
716 717
    else {
        [self _stopPlayback];
718
        [self closePlayback:nil];
719
    }
720 721 722 723
}

- (void)_playNewMedia
{
724
    NSNumber *playbackPositionInTime = @(0);
725
    CGFloat lastPosition = .0;
726 727 728
    NSInteger duration = 0;
    MLFile *matchedFile;

729 730 731 732 733 734
    // Set last selected equalizer profile
    unsigned int profile = (unsigned int)[[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingEqualizerProfile] integerValue];
    [_mediaPlayer resetEqualizerFromProfile:profile];
    [_mediaPlayer setPreAmplification:[_mediaPlayer preAmplification]];
    [_equalizerView reloadData];

735 736 737
    if (self.fileFromMediaLibrary)
        matchedFile = self.fileFromMediaLibrary;
    else if (self.mediaList) {
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
        /* TODO: move this code to MLKit */
        NSString *path = [[[self.mediaList mediaAtIndex:self.itemInMediaListToBePlayedFirst] url] absoluteString];
        NSString *componentString = @"";
        NSArray *pathComponents = [path componentsSeparatedByString:@"/"];
        NSUInteger componentCount = pathComponents.count;

        if ([pathComponents[componentCount - 2] isEqualToString:@"Documents"])
            componentString = [path lastPathComponent];
        else {
            NSUInteger firstElement = [pathComponents indexOfObject:@"Documents"] + 1;
            for (NSUInteger x = 0; x < componentCount - firstElement; x++) {
                if (x == 0)
                componentString = [componentString stringByAppendingFormat:@"%@", pathComponents[firstElement + x]];
                else
                componentString = [componentString stringByAppendingFormat:@"/%@", pathComponents[firstElement + x]];
            }
754
        }
755 756 757 758 759 760 761 762 763 764 765 766 767

        NSFetchRequest *request = [[NSFetchRequest alloc] init];
        NSManagedObjectContext *moc = [[MLMediaLibrary sharedMediaLibrary] managedObjectContext];
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"File" inManagedObjectContext:moc];
        [request setEntity:entity];
        [request setPredicate:[NSPredicate predicateWithFormat:@"url CONTAINS %@", componentString]];

        NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)];
        [request setSortDescriptors:@[descriptor]];

        NSArray *matches = [moc executeFetchRequest:request error:nil];
        if (matches.count > 0)
            matchedFile = matches[0];
768 769 770 771
    }
    if (matchedFile.lastPosition)
        lastPosition = matchedFile.lastPosition.floatValue;
    duration = matchedFile.duration.intValue;
772 773 774
    if (lastPosition < .95) {
        if (duration != 0)
            playbackPositionInTime = @(lastPosition * (duration / 1000.));
775
    }
776
    if (playbackPositionInTime.intValue > 0 && (duration * lastPosition - duration) < -60000) {
777 778 779
        [_mediaPlayer.media addOptions:@{@"start-time": playbackPositionInTime}];
        APLog(@"set starttime to %i", playbackPositionInTime.intValue);
    }
780

781 782 783
    [_mediaPlayer addObserver:self forKeyPath:@"time" options:0 context:nil];
    [_mediaPlayer addObserver:self forKeyPath:@"remainingTime" options:0 context:nil];

784
    if (self.mediaList)
785
        [_listPlayer playItemAtIndex:self.itemInMediaListToBePlayedFirst];
786
    else
787
        [_listPlayer playMedia:_listPlayer.rootMedia];
788

789 790 791 792 793
    if (matchedFile) {
        if (matchedFile.lastAudioTrack.intValue > 0)
            _mediaPlayer.currentAudioTrackIndex = matchedFile.lastAudioTrack.intValue;
        if (matchedFile.lastSubtitleTrack.intValue > 0)
            _mediaPlayer.currentVideoSubTitleIndex = matchedFile.lastSubtitleTrack.intValue;
794
    }
795

796 797
    self.playbackSpeedSlider.value = [self _playbackSpeed];
    [self _updatePlaybackSpeedIndicator];
798

799 800 801 802 803 804
    self.audioDelaySlider.value = _mediaPlayer.currentAudioPlaybackDelay / 1000000;
    self.audioDelayIndicator.text = [NSString stringWithFormat:@"%1.00f s", self.audioDelaySlider.value];

    self.spuDelaySlider.value = _mediaPlayer.currentVideoSubTitleDelay / 1000000;
    self.spuDelayIndicator.text = [NSString stringWithFormat:@"%1.00f s", self.spuDelaySlider.value];

805
    _currentAspectRatioMask = 0;
806 807 808
    _mediaPlayer.videoAspectRatio = NULL;

    /* some demuxers don't respect :start-time, so re-try here */
809
    if (lastPosition < .95 && _mediaPlayer.position < lastPosition && (duration * lastPosition - duration) < -60000)
810
        _mediaPlayer.position = lastPosition;
811

812
    [self _resetIdleTimer];
813
    _playerIsSetup = YES;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
814 815
}

816
- (void)viewWillDisappear:(BOOL)animated
Felix Paul Kühne's avatar
Felix Paul Kühne committed
817
{
818
    [self _stopPlayback];
819
    _viewAppeared = NO;
820
    if (_idleTimer) {
821
        [_idleTimer invalidate];
822 823
        _idleTimer = nil;
    }
824
    [self.navigationController setNavigationBarHidden:NO animated:YES];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
825
    if (!SYSTEM_RUNS_IOS7_OR_LATER)
826
        [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleBlackOpaque;
827
    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
828
    [super viewWillDisappear:animated];
829

Felix Paul Kühne's avatar
Felix Paul Kühne committed
830 831 832 833 834 835
    // hide filter UI for next run
    if (!_videoFiltersHidden)
        _videoFiltersHidden = YES;

    if (!_playbackSpeedViewHidden)
        _playbackSpeedViewHidden = YES;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
836 837
}

838 839 840
- (void)_stopPlayback
{
    if (_mediaPlayer) {
841 842 843 844 845 846 847 848
        @try {
            [_mediaPlayer removeObserver:self forKeyPath:@"time"];
            [_mediaPlayer removeObserver:self forKeyPath:@"remainingTime"];
        }
        @catch (NSException *exception) {
            APLog(@"we weren't an observer yet");
        }

849
        if (_mediaPlayer.media) {
850 851 852
            [_mediaPlayer pause];
            [self _saveCurrentState];
            [_mediaPlayer stop];
853
        }
854 855
        if (_mediaPlayer)
            _mediaPlayer = nil;
856 857
        if (_listPlayer)
            _listPlayer = nil;
858
    }
859 860
    if (_fileFromMediaLibrary)
        _fileFromMediaLibrary = nil;
861 862 863 864
    if (_mediaList)
        _mediaList = nil;
    if (_url)
        _url = nil;
865
    if (_pathToExternalSubtitlesFile) {
866
            NSFileManager *fileManager = [NSFileManager defaultManager];
867 868 869
            if ([fileManager fileExistsAtPath:_pathToExternalSubtitlesFile])
                [fileManager removeItemAtPath:_pathToExternalSubtitlesFile error:nil];
            _pathToExternalSubtitlesFile = nil;
870
    }