VLCMovieViewController.m 20.6 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>
Felix Paul Kühne's avatar
Felix Paul Kühne committed
16 17
@property (nonatomic, strong) UIPopoverController *masterPopoverController;
@property (nonatomic, strong) UIWindow *externalWindow;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
18 19
@end

20
@implementation VLCMovieViewController
Felix Paul Kühne's avatar
Felix Paul Kühne committed
21 22 23

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

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

29
- (void)setMediaItem:(id)newMediaItem
Felix Paul Kühne's avatar
Felix Paul Kühne committed
30
{
31
    if (_mediaItem != newMediaItem)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
32
        _mediaItem = newMediaItem;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
33

34
    if (self.masterPopoverController != nil)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
35 36 37
        [self.masterPopoverController dismissPopoverAnimated:YES];
}

38
- (void)viewDidLoad
Felix Paul Kühne's avatar
Felix Paul Kühne committed
39
{
40
    [super viewDidLoad];
41 42
    self.wantsFullScreenLayout = YES;

43 44 45
    _mediaPlayer = [[VLCMediaPlayer alloc] init];
    [_mediaPlayer setDelegate:self];
    [_mediaPlayer setDrawable:self.movieView];
46

47 48
    self.videoFilterView.hidden = YES;
    _videoFiltersHidden = YES;
49 50 51 52 53
    _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", @"");
54

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

58 59 60 61 62
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(handleExternalScreenDidConnect:)
                   name:UIScreenDidConnectNotification object:nil];
    [center addObserver:self selector:@selector(handleExternalScreenDidDisconnect:)
                   name:UIScreenDidDisconnectNotification object:nil];
63
    [center addObserver:self selector:@selector(appWillResign:) name:UIApplicationWillResignActiveNotification object:nil];
64

65 66
    _playingExternallyTitle.text = NSLocalizedString(@"PLAYING_EXTERNALLY_TITLE", @"");
    _playingExternallyDescription.text = NSLocalizedString(@"PLAYING_EXTERNALLY_DESC", @"");
67
    if ([self hasExternalDisplay])
68
        [self showOnExternalDisplay];
69 70

    _movieView.userInteractionEnabled = NO;
71
    UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleControlsVisible)];
72 73
    recognizer.delegate = self;
    [self.view addGestureRecognizer:recognizer];
74

75 76
    _cropRatios = @[@"Default", @"16:10", @"16:9", @"2:39:1", @"5:3", @"4:3", @"5:4", @"1:1"];
    _aspectRatios = @[@"Default", @"1:1", @"4:3", @"16:9", @"16:10", @"2.21:1", @"2:35:1", @"2.39:1", @"5:4"];
77
}
Felix Paul Kühne's avatar
Felix Paul Kühne committed
78

79 80
- (void)viewWillAppear:(BOOL)animated
{
81
    [self.navigationController setNavigationBarHidden:YES animated:YES];
82 83
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
        [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleBlackTranslucent;
84

85
    if (!self.mediaItem && !self.url)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
86
        return;
87

88 89 90 91 92 93 94
    if (self.mediaItem) {
        self.title = [self.mediaItem title];
        [_mediaPlayer setMedia:[VLCMedia mediaWithURL:[NSURL URLWithString:self.mediaItem.url]]];
    } else {
        [_mediaPlayer setMedia:[VLCMedia mediaWithURL:self.url]];
        self.title = @"Network Stream";
    }
Felix Paul Kühne's avatar
Felix Paul Kühne committed
95

96 97
    self.positionSlider.value = 0.;

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
    [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];
}

- (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
150
    [_mediaPlayer play];
151

Felix Paul Kühne's avatar
Felix Paul Kühne committed
152 153
    if (self.mediaItem.lastPosition && [self.mediaItem.lastPosition floatValue] < 0.99)
        [_mediaPlayer setPosition:[self.mediaItem.lastPosition floatValue]];
154 155
    self.playbackSpeedSlider.value = [self _playbackSpeed];
    [self _updatePlaybackSpeedIndicator];
156

157 158 159
    _currentAspectRatioMask = _currentCropMask = 0;
    _mediaPlayer.videoAspectRatio = _mediaPlayer.videoCropGeometry = NULL;

160
    [self resetIdleTimer];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
161 162
}

163
- (void)viewWillDisappear:(BOOL)animated
Felix Paul Kühne's avatar
Felix Paul Kühne committed
164
{
165 166
    if (_idleTimer)
        [_idleTimer invalidate];
167
    [self.navigationController setNavigationBarHidden:NO animated:YES];
168
    [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleBlackOpaque;
169 170
    [_mediaPlayer pause];
    [super viewWillDisappear:animated];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
171
    self.mediaItem.lastPosition = @([_mediaPlayer position]);
172
    [_mediaPlayer stop];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
173 174 175 176 177
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
178
    if (self)
179
        self.title = @"Video Playback";
Felix Paul Kühne's avatar
Felix Paul Kühne committed
180 181
    return self;
}
182

183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
#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;
210

211 212 213 214 215
        case UIEventSubtypeRemoteControlPause:
            [_mediaPlayer pause];
            break;

        case UIEventSubtypeRemoteControlTogglePlayPause:
216
            [self playPause];
217 218 219 220 221 222 223
            break;

        default:
            break;
    }
}

224 225 226 227
#pragma mark - controls visibility

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
228
    if (touch.view != self.view)
229 230 231 232 233
        return NO;

    return YES;
}

234
- (void)toggleControlsVisible
235 236 237 238 239 240
{
    _controlsHidden = !_controlsHidden;
    CGFloat alpha = _controlsHidden? 0.0f: 1.0f;

    if (!_controlsHidden) {
        _controllerPanel.alpha = 0.0f;
241
        _controllerPanel.hidden = !_videoFiltersHidden;
242 243
        _toolbar.alpha = 0.0f;
        _toolbar.hidden = NO;
244 245
        _videoFilterView.alpha = 0.0f;
        _videoFilterView.hidden = _videoFiltersHidden;
246 247 248 249
        _playbackSpeedView.alpha = 0.0f;
        _playbackSpeedView.hidden = _playbackSpeedViewHidden;
        _playbackSpeedButton.alpha = 0.0f;
        _playbackSpeedButton.hidden = NO;
250 251
        _videoFilterButton.alpha = 0.0f;
        _videoFilterButton.hidden = NO;
252 253 254 255
        _cropButton.alpha = 0.0f;
        _cropButton.hidden = NO;
        _aspectRatioButton.alpha = 0.0f;
        _aspectRatioButton.hidden = NO;
256 257 258 259
    }

    void (^animationBlock)() = ^() {
        _controllerPanel.alpha = alpha;
260
        _toolbar.alpha = alpha;
261 262
        _videoFilterView.alpha = alpha;
        _videoFilterButton.alpha = alpha;
263 264 265 266 267
        _playbackSpeedView.alpha = alpha;
        _playbackSpeedButton.alpha = alpha;
        _videoFilterButton.alpha = alpha;
        _cropButton.alpha = alpha;
        _aspectRatioButton.alpha = alpha;
268 269 270
    };

    void (^completionBlock)(BOOL finished) = ^(BOOL finished) {
271
        if (_videoFiltersHidden) {
272
            _controllerPanel.hidden = _controlsHidden;
273 274 275 276 277 278 279 280 281 282 283
            _playbackSpeedButton.hidden = _controlsHidden;
            _videoFilterButton.hidden = _controlsHidden;
            _cropButton.hidden = _controlsHidden;
            _aspectRatioButton.hidden = _controlsHidden;
        } else {
            _controllerPanel.hidden = NO;
            _playbackSpeedButton.hidden = NO;
            _videoFilterButton.hidden = NO;
            _cropButton.hidden = NO;
            _aspectRatioButton.hidden = NO;
        }
284
        _toolbar.hidden = _controlsHidden;
285
        _videoFilterView.hidden = _videoFiltersHidden;
286 287 288 289
        if (_controlsHidden)
            _playbackSpeedView.hidden = YES;
        else
            _playbackSpeedView.hidden = _playbackSpeedViewHidden;
290 291 292
    };

    [UIView animateWithDuration:0.3f animations:animationBlock completion:completionBlock];
293
    [[UIApplication sharedApplication] setStatusBarHidden:_controlsHidden withAnimation:UIStatusBarAnimationFade];
294 295
}

296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
- (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)
314
        [self toggleControlsVisible];
315 316 317 318 319 320 321 322
}

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

323 324 325 326 327 328 329 330 331 332
#pragma mark - controls

- (IBAction)closePlayback:(id)sender
{
    [self.navigationController popViewControllerAnimated:YES];
}

- (IBAction)positionSliderAction:(UISlider *)sender
{
    _mediaPlayer.position = sender.value;
333
    [self resetIdleTimer];
334 335 336 337
}

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

341 342
- (void)mediaPlayerStateChanged:(NSNotification *)aNotification
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
343 344 345 346
    VLCMediaPlayerState currentState = _mediaPlayer.state;

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

350
    UIImage *playPauseImage = [_mediaPlayer isPlaying]? [UIImage imageNamed:@"pause"] : [UIImage imageNamed:@"play"];
Gleb Pinigin's avatar
Gleb Pinigin committed
351
    [_playPauseButton setImage:playPauseImage forState:UIControlStateNormal];
352 353 354 355 356 357 358 359 360 361

    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;
362 363
}

364
- (IBAction)playPause
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
{
    if ([_mediaPlayer isPlaying]) {
        [_mediaPlayer pause];
    } else {
        [_mediaPlayer play];
    }
}

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

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

- (IBAction)switchAudioTrack:(id)sender
{
385
    _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
386
    NSArray *audioTracks = [_mediaPlayer audioTrackNames];
387
    NSUInteger count = [audioTracks count];
388
    for (NSUInteger i = 0; i < count; i++)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
389
        [_audiotrackActionSheet addButtonWithTitle:audioTracks[i]];
390
    [_audiotrackActionSheet addButtonWithTitle:NSLocalizedString(@"BUTTON_CANCEL", @"cancel button")];
391
    [_audiotrackActionSheet setCancelButtonIndex:[_audiotrackActionSheet numberOfButtons] - 1];
392
    [_audiotrackActionSheet showInView:self.audioSwitcherButton];
393 394 395 396
}

- (IBAction)switchSubtitleTrack:(id)sender
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
397
    NSArray *spuTracks = [_mediaPlayer videoSubTitlesNames];
398
    NSUInteger count = [spuTracks count];
399 400
    if (count <= 1)
        return;
401
    _subtitleActionSheet = [[UIActionSheet alloc] initWithTitle:NSLocalizedString(@"CHOOSE_SUBTITLE_TRACK", @"subtitle track selector") delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles: nil];
402
    for (NSUInteger i = 0; i < count; i++)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
403
        [_subtitleActionSheet addButtonWithTitle:spuTracks[i]];
404
    [_subtitleActionSheet addButtonWithTitle:NSLocalizedString(@"BUTTON_CANCEL", @"cancel button")];
405
    [_subtitleActionSheet setCancelButtonIndex:[_subtitleActionSheet numberOfButtons] - 1];
406
    [_subtitleActionSheet showInView: self.subtitleSwitcherButton];
407 408 409
}

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
410
    NSUInteger arrayIndex = 0;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
411 412
    NSArray *indexArray;
    NSArray *namesArray;
413
    if (actionSheet == _subtitleActionSheet) {
414 415
        namesArray = _mediaPlayer.videoSubTitlesNames;
        arrayIndex = [namesArray indexOfObject:[actionSheet buttonTitleAtIndex:buttonIndex]];
416 417
        if (arrayIndex != NSNotFound) {
            indexArray = _mediaPlayer.videoSubTitlesIndexes;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
418
            _mediaPlayer.currentVideoSubTitleIndex = [indexArray[arrayIndex] intValue];
419
        }
420
    } else if (actionSheet == _audiotrackActionSheet) {
421 422
        namesArray = _mediaPlayer.audioTrackNames;
        arrayIndex = [namesArray indexOfObject:[actionSheet buttonTitleAtIndex:buttonIndex]];
423 424
        if (arrayIndex != NSNotFound) {
            indexArray = _mediaPlayer.audioTrackIndexes;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
425
            _mediaPlayer.currentAudioTrackIndex = [indexArray[arrayIndex] intValue];
426
        }
427 428 429
    }
}

430 431 432 433
#pragma mark - Video Filter UI

- (IBAction)videoFilterToggle:(id)sender
{
434 435
    if (!_playbackSpeedViewHidden)
        self.playbackSpeedView.hidden = _playbackSpeedViewHidden = YES;
436

437 438 439 440 441 442 443 444 445 446 447
    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;
448 449 450 451 452 453
    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)
454 455 456
        _mediaPlayer.saturation = self.saturationSlider.value;
    else if (sender == self.gammaSlider)
        _mediaPlayer.gamma = self.gammaSlider.value;
457 458 459 460 461 462 463
    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
464
        APLog(@"unknown sender for videoFilterSliderAction");
465
    [self resetIdleTimer];
466 467
}

468 469 470 471 472 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 502 503 504
#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
{
505
    if (sender == self.playbackSpeedButton) {
506
        if (!_videoFiltersHidden)
507
            self.videoFilterView.hidden = _videoFiltersHidden = YES;
508

509 510
        self.playbackSpeedView.hidden = !_playbackSpeedViewHidden;
        _playbackSpeedViewHidden = self.playbackSpeedView.hidden;
511
    } else if (sender == self.aspectRatioButton) {
512 513 514 515 516
        NSUInteger count = [_aspectRatios count];

        if (_currentAspectRatioMask + 1 > count - 1) {
            _mediaPlayer.videoAspectRatio = NULL;
            _currentAspectRatioMask = 0;
517
            [self.statusLabel showStatusMessage:[NSString stringWithFormat:NSLocalizedString(@"AR_CHANGED", @""), NSLocalizedString(@"DEFAULT", @"")]];
518 519 520
        } else {
            _currentAspectRatioMask++;
            _mediaPlayer.videoAspectRatio = (char *)[_aspectRatios[_currentAspectRatioMask] UTF8String];
521
            [self.statusLabel showStatusMessage:[NSString stringWithFormat:NSLocalizedString(@"AR_CHANGED", @""), _aspectRatios[_currentAspectRatioMask]]];
522
        }
523
    } else if (sender == self.cropButton) {
524 525 526 527 528
        NSUInteger count = [_cropRatios count];

        if (_currentCropMask + 1 > count - 1) {
            _mediaPlayer.videoCropGeometry = NULL;
            _currentCropMask = 0;
529
            [self.statusLabel showStatusMessage:[NSString stringWithFormat:NSLocalizedString(@"CROP_CHANGED", @""), NSLocalizedString(@"DEFAULT", @"")]];
530 531 532
        } else {
            _currentCropMask++;
            _mediaPlayer.videoCropGeometry = (char *)[_cropRatios[_currentCropMask] UTF8String];
533
            [self.statusLabel showStatusMessage:[NSString stringWithFormat:NSLocalizedString(@"CROP_CHANGED", @""), _cropRatios[_currentCropMask]]];
534
        }
535 536 537
    }
}

538
#pragma mark -
Felix Paul Kühne's avatar
Felix Paul Kühne committed
539

540 541
- (void)appWillResign:(NSNotification *)aNotification
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
542
    self.mediaItem.lastPosition = @([_mediaPlayer position]);
543 544
}

545 546 547 548 549 550 551
#pragma mark - autorotation

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad
           || toInterfaceOrientation != UIInterfaceOrientationMaskPortraitUpsideDown;
}

552 553 554 555 556 557 558 559 560
#pragma mark - External Display

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

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

Felix Paul Kühne's avatar
Felix Paul Kühne committed
564
    self.externalWindow = [[UIWindow alloc] initWithFrame:screen.bounds];
565 566 567 568 569 570 571

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

572
    self.playingExternallyView.hidden = NO;
573 574 575 576 577 578 579 580 581 582
    self.externalWindow.screen = screen;
    self.externalWindow.hidden = NO;
}

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

583
    self.playingExternallyView.hidden = YES;
584 585 586 587 588 589 590 591 592 593 594 595 596 597
    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
598
@end