VLCMovieViewController.m 25.2 KB
Newer Older
Felix Paul Kühne's avatar
Felix Paul Kühne committed
1
//
2
//  VLCMovieViewController.m
Felix Paul Kühne's avatar
Felix Paul Kühne committed
3 4 5 6 7 8
//  AspenProject
//
//  Created by Felix Paul Kühne on 27.02.13.
//  Copyright (c) 2013 VideoLAN. All rights reserved.
//

9
#import "VLCMovieViewController.h"
10
#import "VLCExternalDisplayController.h"
11
#import <sys/sysctl.h> // for sysctlbyname
Felix Paul Kühne's avatar
Felix Paul Kühne committed
12

13 14
#define INPUT_RATE_DEFAULT  1000.

15
@interface VLCMovieViewController () <UIGestureRecognizerDelegate>
16 17
{
    BOOL _shouldResumePlaying;
18
    BOOL _viewAppeared;
19 20
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
21 22
@property (nonatomic, strong) UIPopoverController *masterPopoverController;
@property (nonatomic, strong) UIWindow *externalWindow;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
23 24
@end

25
@implementation VLCMovieViewController
Felix Paul Kühne's avatar
Felix Paul Kühne committed
26 27 28

- (void)dealloc
{
29
    [[NSNotificationCenter defaultCenter] removeObserver:self];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
30 31
}

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

34
- (void)setMediaItem:(id)newMediaItem
Felix Paul Kühne's avatar
Felix Paul Kühne committed
35
{
36
    if (_mediaItem != newMediaItem)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
37
        _mediaItem = newMediaItem;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
38

39
    if (self.masterPopoverController != nil)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
40 41 42
        [self.masterPopoverController dismissPopoverAnimated:YES];
}

43
- (void)viewDidLoad
Felix Paul Kühne's avatar
Felix Paul Kühne committed
44
{
45
    [super viewDidLoad];
46 47
    self.wantsFullScreenLayout = YES;

48 49
    self.videoFilterView.hidden = YES;
    _videoFiltersHidden = YES;
50 51 52 53 54
    _hueLabel.text = NSLocalizedString(@"VFILTER_HUE", @"");
    _contrastLabel.text = NSLocalizedString(@"VFILTER_CONTRAST", @"");
    _brightnessLabel.text = NSLocalizedString(@"VFILTER_BRIGHTNESS", @"");
    _saturationLabel.text = NSLocalizedString(@"VFILTER_SATURATION", @"");
    _gammaLabel.text = NSLocalizedString(@"VFILTER_GAMMA", @"");
55

56 57
    self.playbackSpeedView.hidden = YES;
    _playbackSpeedViewHidden = YES;
58

59 60 61 62 63
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(handleExternalScreenDidConnect:)
                   name:UIScreenDidConnectNotification object:nil];
    [center addObserver:self selector:@selector(handleExternalScreenDidDisconnect:)
                   name:UIScreenDidDisconnectNotification object:nil];
64 65 66 67 68 69
    [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];
70

71 72
    _playingExternallyTitle.text = NSLocalizedString(@"PLAYING_EXTERNALLY_TITLE", @"");
    _playingExternallyDescription.text = NSLocalizedString(@"PLAYING_EXTERNALLY_DESC", @"");
73
    if ([self hasExternalDisplay])
74
        [self showOnExternalDisplay];
75 76

    _movieView.userInteractionEnabled = NO;
77
    UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleControlsVisible)];
78 79
    recognizer.delegate = self;
    [self.view addGestureRecognizer:recognizer];
80

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
    UISwipeGestureRecognizer *leftSwipeRecognizer = [[VLCHorizontalSwipeGestureRecognizer alloc] initWithTarget:self action:nil];
    leftSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
    leftSwipeRecognizer.delegate = self;
    [self.view addGestureRecognizer:leftSwipeRecognizer];
    UISwipeGestureRecognizer *rightSwipeRecognizer = [[VLCHorizontalSwipeGestureRecognizer alloc] initWithTarget:self action:nil];
    rightSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
    rightSwipeRecognizer.delegate = self;
    [self.view addGestureRecognizer:rightSwipeRecognizer];
    UISwipeGestureRecognizer *upSwipeRecognizer = [[VLCVerticalSwipeGestureRecognizer alloc] initWithTarget:self action:nil];
    upSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionUp;
    upSwipeRecognizer.delegate = self;
    [self.view addGestureRecognizer:upSwipeRecognizer];
    UISwipeGestureRecognizer *downSwipeRecognizer = [[VLCVerticalSwipeGestureRecognizer alloc] initWithTarget:self action:nil];
    downSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionDown;
    downSwipeRecognizer.delegate = self;
    [self.view addGestureRecognizer:downSwipeRecognizer];

98
    _aspectRatios = @[@"DEFAULT", @"4:3", @"16:9", @"16:10", @"2.21:1", @"FILL_TO_SCREEN"];
99
}
Felix Paul Kühne's avatar
Felix Paul Kühne committed
100

101 102
- (void)viewWillAppear:(BOOL)animated
{
103
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
104 105
    NSArray *options = @[[[defaults objectForKey:kVLCSettingVerboseOutput] boolValue] ? kVLCSettingVerboseOutputOnValue : kVLCSettingVerboseOutputOffValue,
                         [[defaults objectForKey:kVLCSettingStretchAudio] boolValue] ? kVLCSettingStretchAudioOnValue : kVLCSettingStretchAudioOffValue,
106
                         [NSString stringWithFormat:@"--subsdec-encoding=%@",[defaults objectForKey:kVLCSettingTextEncoding]]];
107 108 109 110 111

    _mediaPlayer = [[VLCMediaPlayer alloc] initWithOptions:options];
    [_mediaPlayer setDelegate:self];
    [_mediaPlayer setDrawable:self.movieView];

112
    [self.navigationController setNavigationBarHidden:YES animated:YES];
113 114
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
        [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleBlackTranslucent;
115

116
    if (!self.mediaItem && !self.url)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
117
        return;
118

119 120 121
    if (self.mediaItem) {
        self.title = [self.mediaItem title];
        [_mediaPlayer setMedia:[VLCMedia mediaWithURL:[NSURL URLWithString:self.mediaItem.url]]];
122
        self.mediaItem.unread = @(NO);
123 124 125 126
    } else {
        [_mediaPlayer setMedia:[VLCMedia mediaWithURL:self.url]];
        self.title = @"Network Stream";
    }
Felix Paul Kühne's avatar
Felix Paul Kühne committed
127

128 129
    self.positionSlider.value = 0.;

130 131 132 133 134 135 136
    [super viewWillAppear:animated];

    if (![self _isMediaSuitableForDevice]) {
        UIAlertView * alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"DEVICE_TOOSLOW_TITLE", @"") message:[NSString stringWithFormat:NSLocalizedString(@"DEVICE_TOOSLOW", @""), [[UIDevice currentDevice] model], self.mediaItem.title] delegate:self cancelButtonTitle:NSLocalizedString(@"BUTTON_CANCEL", @"") otherButtonTitles:NSLocalizedString(@"BUTTON_OPEN", @""), nil];
        [alert show];
    } else
        [self _playNewMedia];
137 138 139

    [self setControlsHidden:NO animated:YES];
    _viewAppeared = YES;
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
}

- (BOOL)_isMediaSuitableForDevice
{
    if (!self.mediaItem)
        return YES;

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

    size_t size;
    sysctlbyname("hw.machine", NULL, &size, NULL, 0);

    char *answer = malloc(size);
    sysctlbyname("hw.machine", answer, &size, NULL, 0);

    NSString *currentMachine = @(answer);
    free(answer);

    if ([currentMachine hasPrefix:@"iPhone2"] || [currentMachine hasPrefix:@"iPhone3"] || [currentMachine hasPrefix:@"iPad1"] || [currentMachine hasPrefix:@"iPod3"] || [currentMachine hasPrefix:@"iPod4"]) {
        // iPhone 3GS, iPhone 4, first gen. iPad, 3rd and 4th generation iPod touch
        APLog(@"this is a cat one device");
        return (totalNumberOfPixels < 600000); // between 480p and 720p
    } else if ([currentMachine hasPrefix:@"iPhone4"] || [currentMachine hasPrefix:@"iPad3,1"] || [currentMachine hasPrefix:@"iPad3,2"] || [currentMachine hasPrefix:@"iPad3,3"] || [currentMachine hasPrefix:@"iPod4"] || [currentMachine hasPrefix:@"iPad2"] || [currentMachine hasPrefix:@"iPod5"]) {
        // iPhone 4S, iPad 2 and 3, iPod 4 and 5
        APLog(@"this is a cat two device");
        return (totalNumberOfPixels < 922000); // 720p
    } else {
        // iPhone 5, iPad 4
        APLog(@"this is a cat three device");
        return (totalNumberOfPixels < 2074000); // 1080p
    }

    return YES;
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 1)
        [self _playNewMedia];
    else
        [self closePlayback:nil];
}

- (void)_playNewMedia
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
185
    [_mediaPlayer play];
186

187
    if (self.mediaItem.lastPosition && [self.mediaItem.lastPosition floatValue] < .95)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
188
        [_mediaPlayer setPosition:[self.mediaItem.lastPosition floatValue]];
189 190
    self.playbackSpeedSlider.value = [self _playbackSpeed];
    [self _updatePlaybackSpeedIndicator];
191

192 193
    _currentAspectRatioMask = 0;
    _mediaPlayer.videoAspectRatio =  NULL;
194

195
    [self resetIdleTimer];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
196 197
}

198
- (void)viewWillDisappear:(BOOL)animated
Felix Paul Kühne's avatar
Felix Paul Kühne committed
199
{
200
    _viewAppeared = NO;
201
    if (_idleTimer) {
202
        [_idleTimer invalidate];
203 204
        _idleTimer = nil;
    }
205
    [self.navigationController setNavigationBarHidden:NO animated:YES];
206
    [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleBlackOpaque;
207
    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
208 209
    [_mediaPlayer pause];
    [super viewWillDisappear:animated];
210 211
    if (self.mediaItem)
        self.mediaItem.lastPosition = @([_mediaPlayer position]);
212
    [_mediaPlayer stop];
213
    _mediaPlayer = nil; // save memory and some CPU time
Felix Paul Kühne's avatar
Felix Paul Kühne committed
214 215 216 217 218
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
219
    if (self)
220
        self.title = @"Video Playback";
Felix Paul Kühne's avatar
Felix Paul Kühne committed
221 222
    return self;
}
223

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
#pragma mark - remote events

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder];
}

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    [self resignFirstResponder];
}

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

- (void)remoteControlReceivedWithEvent:(UIEvent *)event
{
    switch (event.subtype) {
        case UIEventSubtypeRemoteControlPlay:
            [_mediaPlayer play];
            break;
251

252 253 254 255 256
        case UIEventSubtypeRemoteControlPause:
            [_mediaPlayer pause];
            break;

        case UIEventSubtypeRemoteControlTogglePlayPause:
257
            [self playPause];
258 259 260 261 262 263 264
            break;

        default:
            break;
    }
}

265 266 267 268
#pragma mark - controls visibility

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
269
    if (touch.view != self.view)
270 271 272 273 274
        return NO;

    return YES;
}

275
- (void)setControlsHidden:(BOOL)hidden animated:(BOOL)animated
276
{
277
    _controlsHidden = hidden;
278
    CGFloat alpha = _controlsHidden? 0.0f: 1.0f;
279

280 281
    if (!_controlsHidden) {
        _controllerPanel.alpha = 0.0f;
282
        _controllerPanel.hidden = !_videoFiltersHidden;
283 284
        _toolbar.alpha = 0.0f;
        _toolbar.hidden = NO;
285 286
        _videoFilterView.alpha = 0.0f;
        _videoFilterView.hidden = _videoFiltersHidden;
287 288 289 290
        _playbackSpeedView.alpha = 0.0f;
        _playbackSpeedView.hidden = _playbackSpeedViewHidden;
        _playbackSpeedButton.alpha = 0.0f;
        _playbackSpeedButton.hidden = NO;
291 292
        _videoFilterButton.alpha = 0.0f;
        _videoFilterButton.hidden = NO;
293 294
        _aspectRatioButton.alpha = 0.0f;
        _aspectRatioButton.hidden = NO;
295
    }
296

297 298
    void (^animationBlock)() = ^() {
        _controllerPanel.alpha = alpha;
299
        _toolbar.alpha = alpha;
300 301
        _videoFilterView.alpha = alpha;
        _videoFilterButton.alpha = alpha;
302 303 304 305
        _playbackSpeedView.alpha = alpha;
        _playbackSpeedButton.alpha = alpha;
        _videoFilterButton.alpha = alpha;
        _aspectRatioButton.alpha = alpha;
306
    };
307

308
    void (^completionBlock)(BOOL finished) = ^(BOOL finished) {
309
        if (_videoFiltersHidden) {
310
            _controllerPanel.hidden = _controlsHidden;
311 312 313 314 315 316 317 318 319
            _playbackSpeedButton.hidden = _controlsHidden;
            _videoFilterButton.hidden = _controlsHidden;
            _aspectRatioButton.hidden = _controlsHidden;
        } else {
            _controllerPanel.hidden = NO;
            _playbackSpeedButton.hidden = NO;
            _videoFilterButton.hidden = NO;
            _aspectRatioButton.hidden = NO;
        }
320
        _toolbar.hidden = _controlsHidden;
321
        _videoFilterView.hidden = _videoFiltersHidden;
322 323 324 325
        if (_controlsHidden)
            _playbackSpeedView.hidden = YES;
        else
            _playbackSpeedView.hidden = _playbackSpeedViewHidden;
326
    };
327

328 329
    UIStatusBarAnimation animationType = animated? UIStatusBarAnimationFade: UIStatusBarAnimationNone;
    NSTimeInterval animationDuration = animated? 0.3: 0.0;
330 331

    [[UIApplication sharedApplication] setStatusBarHidden:_viewAppeared ? _controlsHidden : NO withAnimation:animationType];
332 333
    [UIView animateWithDuration:animationDuration animations:animationBlock completion:completionBlock];
}
334

335
- (void)toggleControlsVisible
336
{
337
    [self setControlsHidden:!_controlsHidden animated:YES];
338 339
}

340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
- (void)resetIdleTimer
{
    if (!_idleTimer)
        _idleTimer = [NSTimer scheduledTimerWithTimeInterval:2.
                                                      target:self
                                                    selector:@selector(idleTimerExceeded)
                                                    userInfo:nil
                                                     repeats:NO];
    else {
        if (fabs([_idleTimer.fireDate timeIntervalSinceNow]) < 2.)
            [_idleTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:2.]];
    }
}

- (void)idleTimerExceeded
{
    _idleTimer = nil;
    if (!_controlsHidden)
358
        [self toggleControlsVisible];
359 360 361 362 363 364 365 366
}

- (UIResponder *)nextResponder
{
    [self resetIdleTimer];
    return [super nextResponder];
}

367 368 369 370
#pragma mark - controls

- (IBAction)closePlayback:(id)sender
{
371
    [self setControlsHidden:NO animated:NO];
372 373 374 375 376 377
    [self.navigationController popViewControllerAnimated:YES];
}

- (IBAction)positionSliderAction:(UISlider *)sender
{
    _mediaPlayer.position = sender.value;
378
    [self resetIdleTimer];
379 380 381 382
}

- (void)mediaPlayerTimeChanged:(NSNotification *)aNotification {
    self.positionSlider.value = [_mediaPlayer position];
383
    self.timeDisplay.text = [[_mediaPlayer remainingTime] stringValue];
384 385
}

386 387
- (void)mediaPlayerStateChanged:(NSNotification *)aNotification
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
388 389 390 391
    VLCMediaPlayerState currentState = _mediaPlayer.state;

    if (currentState == VLCMediaPlayerStateError) {
        [self.statusLabel showStatusMessage:NSLocalizedString(@"PLAYBACK_FAILED", @"")];
392
        [self performSelector:@selector(closePlayback:) withObject:nil afterDelay:2.];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
393 394
    }

395 396 397
    if (currentState == VLCMediaPlayerStateEnded || currentState == VLCMediaPlayerStateStopped)
        [self performSelector:@selector(closePlayback:) withObject:nil afterDelay:2.];

398
    UIImage *playPauseImage = [_mediaPlayer isPlaying]? [UIImage imageNamed:@"pause"] : [UIImage imageNamed:@"play"];
Gleb Pinigin's avatar
Gleb Pinigin committed
399
    [_playPauseButton setImage:playPauseImage forState:UIControlStateNormal];
400 401 402 403 404 405 406 407 408 409

    if ([[_mediaPlayer audioTrackIndexes] count] > 2)
        self.audioSwitcherButton.hidden = NO;
    else
        self.audioSwitcherButton.hidden = YES;

    if ([[_mediaPlayer videoSubTitlesIndexes] count] > 1)
        self.subtitleSwitcherButton.hidden = NO;
    else
        self.subtitleSwitcherButton.hidden = YES;
410 411
}

412
- (IBAction)playPause
413
{
414
    if ([_mediaPlayer isPlaying])
415
        [_mediaPlayer pause];
416
    else
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
        [_mediaPlayer play];
}

- (IBAction)forward:(id)sender
{
    [_mediaPlayer mediumJumpForward];
}

- (IBAction)backward:(id)sender
{
    [_mediaPlayer mediumJumpBackward];
}

- (IBAction)switchAudioTrack:(id)sender
{
432
    _audiotrackActionSheet = [[UIActionSheet alloc] initWithTitle:NSLocalizedString(@"CHOOSE_AUDIO_TRACK", @"audio track selector") delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles: nil];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
433
    NSArray *audioTracks = [_mediaPlayer audioTrackNames];
434
    NSUInteger count = [audioTracks count];
435
    for (NSUInteger i = 0; i < count; i++)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
436
        [_audiotrackActionSheet addButtonWithTitle:audioTracks[i]];
437
    [_audiotrackActionSheet addButtonWithTitle:NSLocalizedString(@"BUTTON_CANCEL", @"cancel button")];
438
    [_audiotrackActionSheet setCancelButtonIndex:[_audiotrackActionSheet numberOfButtons] - 1];
439
    [_audiotrackActionSheet showInView:self.audioSwitcherButton];
440 441 442 443
}

- (IBAction)switchSubtitleTrack:(id)sender
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
444
    NSArray *spuTracks = [_mediaPlayer videoSubTitlesNames];
445
    NSUInteger count = [spuTracks count];
446 447
    if (count <= 1)
        return;
448
    _subtitleActionSheet = [[UIActionSheet alloc] initWithTitle:NSLocalizedString(@"CHOOSE_SUBTITLE_TRACK", @"subtitle track selector") delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles: nil];
449
    for (NSUInteger i = 0; i < count; i++)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
450
        [_subtitleActionSheet addButtonWithTitle:spuTracks[i]];
451
    [_subtitleActionSheet addButtonWithTitle:NSLocalizedString(@"BUTTON_CANCEL", @"cancel button")];
452
    [_subtitleActionSheet setCancelButtonIndex:[_subtitleActionSheet numberOfButtons] - 1];
453
    [_subtitleActionSheet showInView: self.subtitleSwitcherButton];
454 455 456
}

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
457
    NSUInteger arrayIndex = 0;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
458 459
    NSArray *indexArray;
    NSArray *namesArray;
460
    if (actionSheet == _subtitleActionSheet) {
461 462
        namesArray = _mediaPlayer.videoSubTitlesNames;
        arrayIndex = [namesArray indexOfObject:[actionSheet buttonTitleAtIndex:buttonIndex]];
463 464
        if (arrayIndex != NSNotFound) {
            indexArray = _mediaPlayer.videoSubTitlesIndexes;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
465
            _mediaPlayer.currentVideoSubTitleIndex = [indexArray[arrayIndex] intValue];
466
        }
467
    } else if (actionSheet == _audiotrackActionSheet) {
468 469
        namesArray = _mediaPlayer.audioTrackNames;
        arrayIndex = [namesArray indexOfObject:[actionSheet buttonTitleAtIndex:buttonIndex]];
470 471
        if (arrayIndex != NSNotFound) {
            indexArray = _mediaPlayer.audioTrackIndexes;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
472
            _mediaPlayer.currentAudioTrackIndex = [indexArray[arrayIndex] intValue];
473
        }
474 475 476
    }
}

477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
#pragma mark - swipe gestures

- (void)horizontalSwipePercentage:(CGFloat)percentage inView:(UIView *)view
{
    if (percentage != 0.) {
        _mediaPlayer.position = _mediaPlayer.position + percentage;
    }
}

- (void)verticalSwipePercentage:(CGFloat)percentage inView:(UIView *)view half:(NSUInteger)half
{
    if (percentage != 0.) {
        if (half > 0) {
            CGFloat currentValue = self.brightnessSlider.value;
            currentValue = currentValue + percentage;
            self.brightnessSlider.value = currentValue;
            if ([self hasExternalDisplay])
                _mediaPlayer.brightness = currentValue;
            else
                [[UIScreen mainScreen] setBrightness:currentValue / 2];
        } else
            NSLog(@"volume setting through swipe not implemented");//_mediaPlayer.audio.volume = percentage * 200;
    }
}

502 503 504 505
#pragma mark - Video Filter UI

- (IBAction)videoFilterToggle:(id)sender
{
506 507
    if (!_playbackSpeedViewHidden)
        self.playbackSpeedView.hidden = _playbackSpeedViewHidden = YES;
508

509 510 511 512 513 514 515 516 517 518 519
    self.videoFilterView.hidden = !_videoFiltersHidden;
    _videoFiltersHidden = self.videoFilterView.hidden;
    self.controllerPanel.hidden = !_videoFiltersHidden;
}

- (IBAction)videoFilterSliderAction:(id)sender
{
    if (sender == self.hueSlider)
        _mediaPlayer.hue = (int)self.hueSlider.value;
    else if (sender == self.contrastSlider)
        _mediaPlayer.contrast = self.contrastSlider.value;
520 521 522 523 524 525
    else if (sender == self.brightnessSlider) {
        if ([self hasExternalDisplay])
            _mediaPlayer.brightness = self.brightnessSlider.value;
        else
            [[UIScreen mainScreen] setBrightness:(self.brightnessSlider.value / 2.)];
    } else if (sender == self.saturationSlider)
526 527 528
        _mediaPlayer.saturation = self.saturationSlider.value;
    else if (sender == self.gammaSlider)
        _mediaPlayer.gamma = self.gammaSlider.value;
529 530 531 532 533 534 535
    else if (sender == self.resetVideoFilterButton) {
        _mediaPlayer.hue = self.hueSlider.value = 0.;
        _mediaPlayer.contrast = self.contrastSlider.value = 1.;
        _mediaPlayer.brightness = self.brightnessSlider.value = 1.;
        _mediaPlayer.saturation = self.saturationSlider.value = 1.;
        _mediaPlayer.gamma = self.gammaSlider.value = 1.;
    } else
536
        APLog(@"unknown sender for videoFilterSliderAction");
537
    [self resetIdleTimer];
538 539
}

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 573 574 575 576
#pragma mark - playback view
- (IBAction)playbackSpeedSliderAction:(UISlider *)sender
{
    double speed = pow(2, sender.value / 17.);
    float rate = INPUT_RATE_DEFAULT / speed;
    if (_currentPlaybackRate != rate)
        [_mediaPlayer setRate:INPUT_RATE_DEFAULT / rate];
    _currentPlaybackRate = rate;
    [self _updatePlaybackSpeedIndicator];
    [self resetIdleTimer];
}

- (void)_updatePlaybackSpeedIndicator
{
    float f_value = self.playbackSpeedSlider.value;
    double speed =  pow(2, f_value / 17.);
    self.playbackSpeedIndicator.text = [NSString stringWithFormat:@"%.2fx", speed];
}

- (float)_playbackSpeed
{
    float f_rate = _mediaPlayer.rate;

    double value = 17 * log(f_rate) / log(2.);
    float returnValue = (int) ((value > 0) ? value + .5 : value - .5);

    if (returnValue < -34.)
        returnValue = -34.;
    else if (returnValue > 34.)
        returnValue = 34.;

    _currentPlaybackRate = returnValue;
    return returnValue;
}

- (IBAction)videoDimensionAction:(id)sender
{
577
    if (sender == self.playbackSpeedButton) {
578
        if (!_videoFiltersHidden)
579
            self.videoFilterView.hidden = _videoFiltersHidden = YES;
580

581 582
        self.playbackSpeedView.hidden = !_playbackSpeedViewHidden;
        _playbackSpeedViewHidden = self.playbackSpeedView.hidden;
583
    } else if (sender == self.aspectRatioButton) {
584 585 586 587
        NSUInteger count = [_aspectRatios count];

        if (_currentAspectRatioMask + 1 > count - 1) {
            _mediaPlayer.videoAspectRatio = NULL;
588
            _mediaPlayer.videoCropGeometry = NULL;
589
            _currentAspectRatioMask = 0;
590
            [self.statusLabel showStatusMessage:[NSString stringWithFormat:NSLocalizedString(@"AR_CHANGED", @""), NSLocalizedString(@"DEFAULT", @"")]];
591 592
        } else {
            _currentAspectRatioMask++;
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618

            if ([_aspectRatios[_currentAspectRatioMask] isEqualToString:@"FILL_TO_SCREEN"]) {
                UIScreen *screen;
                if (![self hasExternalDisplay])
                    screen = [UIScreen mainScreen];
                else
                    screen = [UIScreen screens][1];

                float f_ar = screen.bounds.size.width / screen.bounds.size.height;

                if (f_ar == (float)(640./1136.)) // iPhone 5 aka 16:9.01
                    _mediaPlayer.videoCropGeometry = "16:9";
                else if (f_ar == (float)(2./3.)) // all other iPhones
                    _mediaPlayer.videoCropGeometry = "16:10"; // libvlc doesn't support 2:3 crop
                else if (f_ar == .75) // all iPads
                    _mediaPlayer.videoCropGeometry = "4:3";
                else if (f_ar == .5625) // AirPlay
                    _mediaPlayer.videoCropGeometry = "16:9";
                else
                    APLog(@"unknown screen format %f, can't crop", f_ar);

                [self.statusLabel showStatusMessage:NSLocalizedString(@"FILL_TO_SCREEN", @"")];
                return;
            }

            _mediaPlayer.videoCropGeometry = NULL;
619
            _mediaPlayer.videoAspectRatio = (char *)[_aspectRatios[_currentAspectRatioMask] UTF8String];
620
            [self.statusLabel showStatusMessage:[NSString stringWithFormat:NSLocalizedString(@"AR_CHANGED", @""), _aspectRatios[_currentAspectRatioMask]]];
621
        }
622 623 624
    }
}

625
#pragma mark - background interaction
Felix Paul Kühne's avatar
Felix Paul Kühne committed
626

627
- (void)applicationWillResignActive:(NSNotification *)aNotification
628
{
629 630
    if (self.mediaItem)
        self.mediaItem.lastPosition = @([_mediaPlayer position]);
631

632
    if (![[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingContinueAudioInBackgroundKey] boolValue]) {
633 634
        [_mediaPlayer pause];
        _shouldResumePlaying = YES;
635 636
    } else
        _mediaPlayer.currentVideoTrackIndex = 0;
637 638 639 640 641 642 643 644 645 646 647 648
}

- (void)applicationDidEnterBackground:(NSNotification *)notification
{
    _shouldResumePlaying = NO;
}

- (void)applicationDidBecomeActive:(NSNotification *)notification
{
    if (_shouldResumePlaying) {
        _shouldResumePlaying = NO;
        [_mediaPlayer play];
649 650
    } else
        _mediaPlayer.currentVideoTrackIndex = 1;
651 652
}

653 654 655 656
#pragma mark - autorotation

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad
657
           || toInterfaceOrientation != UIInterfaceOrientationPortraitUpsideDown;
658 659
}

660 661 662 663 664 665 666 667 668
#pragma mark - External Display

- (BOOL)hasExternalDisplay
{
    return ([[UIScreen screens] count] > 1);
}

- (void)showOnExternalDisplay
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
669
    UIScreen *screen = [UIScreen screens][1];
670 671
    screen.overscanCompensation = UIScreenOverscanCompensationInsetApplicationFrame;

Felix Paul Kühne's avatar
Felix Paul Kühne committed
672
    self.externalWindow = [[UIWindow alloc] initWithFrame:screen.bounds];
673 674 675 676 677 678 679

    UIViewController *controller = [[VLCExternalDisplayController alloc] init];
    self.externalWindow.rootViewController = controller;
    [controller.view addSubview:_movieView];
    controller.view.frame = screen.bounds;
    _movieView.frame = screen.bounds;

680
    self.playingExternallyView.hidden = NO;
681 682 683 684 685 686 687 688 689 690
    self.externalWindow.screen = screen;
    self.externalWindow.hidden = NO;
}

- (void)hideFromExternalDisplay
{
    [self.view addSubview:_movieView];
    [self.view sendSubviewToBack:_movieView];
    _movieView.frame = self.view.frame;

691
    self.playingExternallyView.hidden = YES;
692 693 694 695 696 697 698 699 700 701 702 703 704 705
    self.externalWindow.hidden = YES;
    self.externalWindow = nil;
}

- (void)handleExternalScreenDidConnect:(NSNotification *)notification
{
    [self showOnExternalDisplay];
}

- (void)handleExternalScreenDidDisconnect:(NSNotification *)notification
{
    [self hideFromExternalDisplay];
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
706
@end