VLCMovieViewController.m 16.9 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"
Felix Paul Kühne's avatar
Felix Paul Kühne committed
11

12
13
#define INPUT_RATE_DEFAULT  1000.

14
@interface VLCMovieViewController () <UIGestureRecognizerDelegate>
Felix Paul Kühne's avatar
Felix Paul Kühne committed
15
16
@property (nonatomic, strong) UIPopoverController *masterPopoverController;
@property (nonatomic, strong) UIWindow *externalWindow;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
17
18
@end

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

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

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

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

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

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

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

46
47
    self.videoFilterView.hidden = YES;
    _videoFiltersHidden = YES;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
48
49
50
51
52
    _hueLabel.text = NSLocalizedString(@"Hue", @"");
    _contrastLabel.text = NSLocalizedString(@"Contrast", @"");
    _brightnessLabel.text = NSLocalizedString(@"Brightness", @"");
    _saturationLabel.text = NSLocalizedString(@"Saturation", @"");
    _gammaLabel.text = NSLocalizedString(@"Gamma", @"");
53

54
55
    self.playbackView.hidden = YES;

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

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

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

    [self resetIdleTimer];
74
}
Felix Paul Kühne's avatar
Felix Paul Kühne committed
75

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

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

86
87
88
89
90
91
92
    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
93
94

    [_mediaPlayer play];
95

Felix Paul Kühne's avatar
Felix Paul Kühne committed
96
97
    if (self.mediaItem.lastPosition && [self.mediaItem.lastPosition floatValue] < 0.99)
        [_mediaPlayer setPosition:[self.mediaItem.lastPosition floatValue]];
98
99
    self.playbackSpeedSlider.value = [self _playbackSpeed];
    [self _updatePlaybackSpeedIndicator];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
100
101
}

102
- (void)viewWillDisappear:(BOOL)animated
Felix Paul Kühne's avatar
Felix Paul Kühne committed
103
{
104
105
    if (_idleTimer)
        [_idleTimer invalidate];
106
    [self.navigationController setNavigationBarHidden:NO animated:YES];
107
    [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleBlackOpaque;
108
109
    [_mediaPlayer pause];
    [super viewWillDisappear:animated];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
110
    self.mediaItem.lastPosition = @([_mediaPlayer position]);
111
    [_mediaPlayer stop];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
112
113
114
115
116
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
117
    if (self)
118
        self.title = @"Video Playback";
Felix Paul Kühne's avatar
Felix Paul Kühne committed
119
120
    return self;
}
121

122
123
124
125
#pragma mark - controls visibility

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
126
    if (touch.view != self.view)
127
128
129
130
131
132
133
134
135
136
137
138
        return NO;

    return YES;
}

- (void)toogleControlsVisible
{
    _controlsHidden = !_controlsHidden;
    CGFloat alpha = _controlsHidden? 0.0f: 1.0f;

    if (!_controlsHidden) {
        _controllerPanel.alpha = 0.0f;
139
        _controllerPanel.hidden = !_videoFiltersHidden || !_playbackViewHidden;
140
141
        _toolbar.alpha = 0.0f;
        _toolbar.hidden = NO;
142
143
144
145
        _videoFilterView.alpha = 0.0f;
        _videoFilterView.hidden = _videoFiltersHidden;
        _videoFilterButton.alpha = 0.0f;
        _videoFilterButton.hidden = NO;
146
147
148
149
        _playbackView.alpha = 0.0f;
        _playbackView.hidden = _playbackViewHidden;
        _playbackButton.alpha = 0.0f;
        _playbackButton.hidden = NO;
150
151
152
153
    }

    void (^animationBlock)() = ^() {
        _controllerPanel.alpha = alpha;
154
        _toolbar.alpha = alpha;
155
156
        _videoFilterView.alpha = alpha;
        _videoFilterButton.alpha = alpha;
157
158
        _playbackView.alpha = alpha;
        _playbackButton.alpha = alpha;
159
160
161
    };

    void (^completionBlock)(BOOL finished) = ^(BOOL finished) {
162
        if (_videoFiltersHidden && _playbackViewHidden)
163
164
165
            _controllerPanel.hidden = _controlsHidden;
        else
            _controllerPanel.hidden = YES;
166
        _toolbar.hidden = _controlsHidden;
167
168
        _videoFilterView.hidden = _videoFiltersHidden;
        _videoFilterButton.hidden = _controlsHidden;
169
170
        _playbackView.hidden = _playbackViewHidden;
        _playbackButton.hidden = _controlsHidden;
171
172
173
    };

    [UIView animateWithDuration:0.3f animations:animationBlock completion:completionBlock];
174
    [[UIApplication sharedApplication] setStatusBarHidden:_controlsHidden withAnimation:UIStatusBarAnimationFade];
175
176
}

177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
- (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)
        [self toogleControlsVisible];
}

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

204
205
206
207
208
209
210
211
212
213
#pragma mark - controls

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

- (IBAction)positionSliderAction:(UISlider *)sender
{
    _mediaPlayer.position = sender.value;
214
    [self resetIdleTimer];
215
216
217
218
219
220
221
}

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

222
223
224
225
226
- (void)mediaPlayerStateChanged:(NSNotification *)aNotification
{
    // TODO
}

227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
- (IBAction)play:(id)sender
{
    if ([_mediaPlayer isPlaying]) {
        [_mediaPlayer pause];
        _playPauseButton.titleLabel.text = @"Pse";
    } else {
        [_mediaPlayer play];
        _playPauseButton.titleLabel.text = @"Play";
    }
}

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

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

- (IBAction)switchAudioTrack:(id)sender
{
    _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
251
    NSArray *audioTracks = [_mediaPlayer audioTrackNames];
252
    NSUInteger count = [audioTracks count];
253
    for (NSUInteger i = 0; i < count; i++)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
254
        [_audiotrackActionSheet addButtonWithTitle:audioTracks[i]];
255
256
257
258
259
260
261
    [_audiotrackActionSheet addButtonWithTitle:NSLocalizedString(@"Cancel", @"audio track selector")];
    [_audiotrackActionSheet setCancelButtonIndex:[_audiotrackActionSheet numberOfButtons] - 1];
    [_audiotrackActionSheet showFromRect:[self.audioSwitcherButton frame] inView:self.audioSwitcherButton animated:YES];
}

- (IBAction)switchSubtitleTrack:(id)sender
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
262
    NSArray *spuTracks = [_mediaPlayer videoSubTitlesNames];
263
    NSUInteger count = [spuTracks count];
264
265
266
    if (count <= 1)
        return;
    _subtitleActionSheet = [[UIActionSheet alloc] initWithTitle:NSLocalizedString(@"Choose Subtitle Track", @"subtitle track selector") delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles: nil];
267
    for (NSUInteger i = 0; i < count; i++)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
268
        [_subtitleActionSheet addButtonWithTitle:spuTracks[i]];
269
270
271
272
273
274
    [_subtitleActionSheet addButtonWithTitle:NSLocalizedString(@"Cancel", @"subtitle track selector")];
    [_subtitleActionSheet setCancelButtonIndex:[_subtitleActionSheet numberOfButtons] - 1];
    [_subtitleActionSheet showFromRect:[self.subtitleSwitcherButton frame] inView:self.subtitleSwitcherButton animated:YES];
}

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
275
    NSUInteger arrayIndex = 0;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
276
277
    NSArray *indexArray;
    NSArray *namesArray;
278
    if (actionSheet == _subtitleActionSheet) {
279
280
        namesArray = _mediaPlayer.videoSubTitlesNames;
        arrayIndex = [namesArray indexOfObject:[actionSheet buttonTitleAtIndex:buttonIndex]];
281
282
        if (arrayIndex != NSNotFound) {
            indexArray = _mediaPlayer.videoSubTitlesIndexes;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
283
            _mediaPlayer.currentVideoSubTitleIndex = [indexArray[arrayIndex] intValue];
284
        }
285
    } else if (actionSheet == _audiotrackActionSheet) {
286
287
        namesArray = _mediaPlayer.audioTrackNames;
        arrayIndex = [namesArray indexOfObject:[actionSheet buttonTitleAtIndex:buttonIndex]];
288
289
        if (arrayIndex != NSNotFound) {
            indexArray = _mediaPlayer.audioTrackIndexes;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
290
            _mediaPlayer.currentAudioTrackIndex = [indexArray[arrayIndex] intValue];
291
        }
292
293
294
295
296
297
298
299
300
301
302
303
304
305
    } else if (actionSheet == _aspectRatioActionSheet) {
        if (actionSheet.cancelButtonIndex != buttonIndex) {
            if ([[actionSheet buttonTitleAtIndex:buttonIndex] isEqualToString:@"Default"])
                _mediaPlayer.videoAspectRatio = NULL;
            else
                _mediaPlayer.videoAspectRatio = (char *)[[actionSheet buttonTitleAtIndex:buttonIndex] UTF8String];
        }
    } else if (actionSheet == _cropActionSheet) {
        if (actionSheet.cancelButtonIndex != buttonIndex) {
            if ([[actionSheet buttonTitleAtIndex:buttonIndex] isEqualToString:@"Default"])
                _mediaPlayer.videoCropGeometry = NULL;
            else
                _mediaPlayer.videoCropGeometry = (char *)[[actionSheet buttonTitleAtIndex:buttonIndex] UTF8String];
        }
306
307
308
    }
}

309
310
311
312
#pragma mark - Video Filter UI

- (IBAction)videoFilterToggle:(id)sender
{
313
314
315
    if (!_playbackViewHidden)
        self.playbackView.hidden = _playbackViewHidden = YES;

316
317
318
319
320
321
322
323
324
325
326
    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;
327
328
329
330
331
332
    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)
333
334
335
        _mediaPlayer.saturation = self.saturationSlider.value;
    else if (sender == self.gammaSlider)
        _mediaPlayer.gamma = self.gammaSlider.value;
336
337
338
339
340
341
342
    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
343
        APLog(@"unknown sender for videoFilterSliderAction");
344
    [self resetIdleTimer];
345
346
}

347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
#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
{
    if (sender == self.playbackButton) {
        if (!_videoFiltersHidden)
            self.videoFilterButton.hidden = _videoFiltersHidden = YES;

        self.playbackView.hidden = !_playbackViewHidden;
        _playbackViewHidden = self.playbackView.hidden;
        self.controllerPanel.hidden = !_playbackViewHidden;
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
    } else if (sender == self.aspectRatioButton) {
        NSArray *ratios = @[@"Default", @"1:1", @"4:3", @"16:9", @"16:10", @"2.21:1", @"2:35:1", @"2.39:1", @"5:4"];
        NSUInteger count = [ratios count];

        _aspectRatioActionSheet = [[UIActionSheet alloc] initWithTitle:@"Choose Aspect Ratio" delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles: nil];
        for (NSUInteger i = 0; i < count; i++)
            [_aspectRatioActionSheet addButtonWithTitle:ratios[i]];
        [_aspectRatioActionSheet addButtonWithTitle:NSLocalizedString(@"Cancel", @"subtitle track selector")];
        [_aspectRatioActionSheet setCancelButtonIndex:[_aspectRatioActionSheet numberOfButtons] - 1];
        [_aspectRatioActionSheet showFromRect:[self.aspectRatioButton frame] inView:self.aspectRatioButton animated:YES];
    } else if (sender == self.cropButton) {
        NSArray *ratios = @[@"Default", @"16:10", @"16:9", @"1.85:1", @"2.21:1", @"2.35:1", @"2:39:1", @"5:3", @"4:3", @"5:4", @"1:1"];
        NSUInteger count = [ratios count];

        _cropActionSheet = [[UIActionSheet alloc] initWithTitle:@"Choose Aspect Ratio" delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles: nil];
        for (NSUInteger i = 0; i < count; i++)
            [_cropActionSheet addButtonWithTitle:ratios[i]];
        [_cropActionSheet addButtonWithTitle:NSLocalizedString(@"Cancel", @"subtitle track selector")];
        [_cropActionSheet setCancelButtonIndex:[_cropActionSheet numberOfButtons] - 1];
        [_cropActionSheet showFromRect:[self.cropButton frame] inView:self.cropButton animated:YES];
411
412
413
    }
}

414
#pragma mark -
Felix Paul Kühne's avatar
Felix Paul Kühne committed
415

416
417
- (void)appWillResign:(NSNotification *)aNotification
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
418
    self.mediaItem.lastPosition = @([_mediaPlayer position]);
419
420
}

421
422
423
424
425
426
427
#pragma mark - autorotation

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

428
429
430
431
432
433
434
435
436
#pragma mark - External Display

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

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

Felix Paul Kühne's avatar
Felix Paul Kühne committed
440
    self.externalWindow = [[UIWindow alloc] initWithFrame:screen.bounds];
441
442
443
444
445
446
447

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

448
    self.playingExternallyView.hidden = NO;
449
450
451
452
453
454
455
456
457
458
    self.externalWindow.screen = screen;
    self.externalWindow.hidden = NO;
}

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

459
    self.playingExternallyView.hidden = YES;
460
461
462
463
464
465
466
467
468
469
470
471
472
473
    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
474
@end