VLCMovieViewController.m 48.7 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
//  AspenProject
//
//  Created by Felix Paul Kühne on 27.02.13.
//  Copyright (c) 2013 VideoLAN. All rights reserved.
//
8
9
//  Refer to the COPYING file of the official project for license.
//
Felix Paul Kühne's avatar
Felix Paul Kühne committed
10

11
#import "VLCMovieViewController.h"
12
#import "VLCExternalDisplayController.h"
13
#import <AVFoundation/AVFoundation.h>
14
#import <CommonCrypto/CommonDigest.h>
15
#import "UIDevice+SpeedCategory.h"
16
#import "VLCBugreporter.h"
17
18
19
20
21

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

23
24
#define INPUT_RATE_DEFAULT  1000.

25
26
@interface VLCMovieViewController () <UIGestureRecognizerDelegate, AVAudioSessionDelegate,
                                    VLCHorizontalSwipeGestureRecognizer, VLCVerticalSwipeGestureRecognizer>
27
{
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
    VLCMediaPlayer *_mediaPlayer;

    BOOL _controlsHidden;
    BOOL _videoFiltersHidden;
    BOOL _playbackSpeedViewHidden;

    UIActionSheet *_subtitleActionSheet;
    UIActionSheet *_audiotrackActionSheet;

    float _currentPlaybackRate;
    NSArray *_aspectRatios;
    NSUInteger _currentAspectRatioMask;

    NSTimer *_idleTimer;

43
    BOOL _shouldResumePlaying;
44
    BOOL _viewAppeared;
45
    BOOL _displayRemainingTime;
46
    BOOL _positionSet;
47
    BOOL _playerIsSetup;
48
    BOOL _isScrubbing;
Ahmad Harb's avatar
Ahmad Harb committed
49
50
51
52
53
54
55
56
57
58
59
60
61

    BOOL _swipeGesturesEnabled;
    CGFloat _panDirectionX;
    CGFloat _panDirectionY;
    int _forwardDuration;
    int _backwardDuration;
    NSString * panType;
    UIView *_rootView;
    UIView *_splashView;
    UIPanGestureRecognizer *_panRecognizer;
    UISwipeGestureRecognizer *_swipeRecognizerLeft;
    UISwipeGestureRecognizer *_swipeRecognizerRight;
    UITapGestureRecognizer *_tapRecognizer;
62
63
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
64
65
@property (nonatomic, strong) UIPopoverController *masterPopoverController;
@property (nonatomic, strong) UIWindow *externalWindow;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
66
67
@end

68
@implementation VLCMovieViewController
Felix Paul Kühne's avatar
Felix Paul Kühne committed
69

70
71
72
73
74
75
76
+ (void)initialize
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSDictionary *appDefaults = @{kVLCShowRemainingTime : @(YES)};
    [defaults registerDefaults:appDefaults];
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
77
78
- (void)dealloc
{
Ahmad Harb's avatar
Ahmad Harb committed
79
80
81
82
83
84
85
86
87
88
89
    if (_splashView)
        [_splashView removeFromSuperview];
    if (_tapRecognizer)
        [_rootView removeGestureRecognizer:_tapRecognizer];
    if (_swipeRecognizerLeft)
        [_rootView removeGestureRecognizer:_swipeRecognizerLeft];
    if (_swipeRecognizerRight)
        [_rootView removeGestureRecognizer:_swipeRecognizerRight];
    if (_panRecognizer)
        [_rootView removeGestureRecognizer:_panRecognizer];

90
    [[NSNotificationCenter defaultCenter] removeObserver:self];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
91
92
}

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

95
- (void)setMediaItem:(id)newMediaItem
Felix Paul Kühne's avatar
Felix Paul Kühne committed
96
{
97
98
    if (_mediaItem != newMediaItem) {
        [self _stopPlayback];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
99
        _mediaItem = newMediaItem;
100
101
102
        if (_viewAppeared)
            [self _startPlayback];
    }
Felix Paul Kühne's avatar
Felix Paul Kühne committed
103

104
    if (self.masterPopoverController != nil)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
105
106
107
        [self.masterPopoverController dismissPopoverAnimated:YES];
}

108
109
110
111
112
113
114
115
116
117
- (void)setUrl:(NSURL *)url
{
    if (_url != url) {
        [self _stopPlayback];
        _url = url;
        if (_viewAppeared)
            [self _startPlayback];
    }
}

118
- (void)viewDidLoad
Felix Paul Kühne's avatar
Felix Paul Kühne committed
119
{
120
    [super viewDidLoad];
121
122
    self.wantsFullScreenLayout = YES;

123
124
    self.videoFilterView.hidden = YES;
    _videoFiltersHidden = YES;
125
    _hueLabel.text = NSLocalizedString(@"VFILTER_HUE", @"");
126
127
    _hueSlider.accessibilityLabel = _hueLabel.text;
    _hueSlider.isAccessibilityElement = YES;
128
    _contrastLabel.text = NSLocalizedString(@"VFILTER_CONTRAST", @"");
129
130
    _contrastSlider.accessibilityLabel = _contrastLabel.text;
    _contrastSlider.isAccessibilityElement = YES;
131
    _brightnessLabel.text = NSLocalizedString(@"VFILTER_BRIGHTNESS", @"");
132
133
    _brightnessSlider.accessibilityLabel = _brightnessLabel.text;
    _brightnessSlider.isAccessibilityElement = YES;
134
    _saturationLabel.text = NSLocalizedString(@"VFILTER_SATURATION", @"");
135
136
    _saturationSlider.accessibilityLabel = _saturationLabel.text;
    _saturationSlider.isAccessibilityElement = YES;
137
    _gammaLabel.text = NSLocalizedString(@"VFILTER_GAMMA", @"");
138
139
    _gammaSlider.accessibilityLabel = _gammaLabel.text;
    _gammaSlider.isAccessibilityElement = YES;
140
    _playbackSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SPEED", @"");
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
    _playbackSpeedSlider.accessibilityLabel = _playbackSpeedLabel.text;
    _playbackSpeedSlider.isAccessibilityElement = YES;

    _positionSlider.accessibilityLabel = NSLocalizedString(@"PLAYBACK_POSITION", @"");
    _positionSlider.isAccessibilityElement = YES;
    _timeDisplay.isAccessibilityElement = YES;

    _audioSwitcherButton.accessibilityLabel = NSLocalizedString(@"CHOOSE_AUDIO_TRACK", @"");
    _audioSwitcherButton.isAccessibilityElement = YES;
    _subtitleSwitcherButton.accessibilityLabel = NSLocalizedString(@"CHOOSE_SUBTITLE_TRACK", @"");
    _subtitleSwitcherButton.isAccessibilityElement = YES;
    _playbackSpeedButton.accessibilityLabel = _playbackSpeedLabel.text;
    _playbackSpeedButton.isAccessibilityElement = YES;
    _videoFilterButton.accessibilityLabel = NSLocalizedString(@"VIDEO_FILTER", @"");
    _videoFilterButton.isAccessibilityElement = YES;
    _resetVideoFilterButton.accessibilityLabel = NSLocalizedString(@"VIDEO_FILTER_RESET_BUTTON", @"");
    _resetVideoFilterButton.isAccessibilityElement = YES;
    _aspectRatioButton.accessibilityLabel = NSLocalizedString(@"VIDEO_ASPECT_RATIO_BUTTON", @"");
    _aspectRatioButton.isAccessibilityElement = YES;
    _playPauseButton.accessibilityLabel = NSLocalizedString(@"PLAY_PAUSE_BUTTON", @"");
    _playPauseButton.isAccessibilityElement = YES;
    _bwdButton.accessibilityLabel = NSLocalizedString(@"BWD_BUTTON", @"");
    _bwdButton.isAccessibilityElement = YES;
    _fwdButton.accessibilityLabel = NSLocalizedString(@"FWD_BUTTON", @"");
    _fwdButton.isAccessibilityElement = YES;
166

167
168
    _scrubHelpLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_HELP", @"");

169
170
    self.playbackSpeedView.hidden = YES;
    _playbackSpeedViewHidden = YES;
171

172
173
174
175
176
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(handleExternalScreenDidConnect:)
                   name:UIScreenDidConnectNotification object:nil];
    [center addObserver:self selector:@selector(handleExternalScreenDidDisconnect:)
                   name:UIScreenDidDisconnectNotification object:nil];
177
178
179
180
181
182
    [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];
183

184
185
    _playingExternallyTitle.text = NSLocalizedString(@"PLAYING_EXTERNALLY_TITLE", @"");
    _playingExternallyDescription.text = NSLocalizedString(@"PLAYING_EXTERNALLY_DESC", @"");
186
    if ([self hasExternalDisplay])
187
        [self showOnExternalDisplay];
188

189
190
    self.trackNameLabel.text = self.artistNameLabel.text = self.albumNameLabel.text = @"";

191
    _movieView.userInteractionEnabled = NO;
192
193
194
195
196
    UITapGestureRecognizer *tapOnVideoRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleControlsVisible)];
    tapOnVideoRecognizer.delegate = self;
    [self.view addGestureRecognizer:tapOnVideoRecognizer];

    _displayRemainingTime = [[[NSUserDefaults standardUserDefaults] objectForKey:kVLCShowRemainingTime] boolValue];
197

198
199
200
201
    UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchGesture:)];
    pinchRecognizer.delegate = self;
    [self.view addGestureRecognizer:pinchRecognizer];

Ahmad Harb's avatar
Ahmad Harb committed
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
    _swipeGesturesEnabled = YES;
    if (_swipeGesturesEnabled) {
        _forwardDuration = 30;
        _backwardDuration = 10;

        _tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapRecognized)];
        [_tapRecognizer setNumberOfTouchesRequired:2];
        _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;

        UIWindow * window = [[[UIApplication sharedApplication] delegate] window];
        _rootView = window.rootViewController.view;
        [_rootView addGestureRecognizer:_swipeRecognizerLeft];
        [_rootView addGestureRecognizer:_swipeRecognizerRight];
        [_rootView addGestureRecognizer:_panRecognizer];
        [_rootView addGestureRecognizer:_tapRecognizer];
        [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerLeft];
        [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerRight];

        _panRecognizer.delegate = self;
        _swipeRecognizerRight.delegate = self;
        _swipeRecognizerLeft.delegate = self;
        _tapRecognizer.delegate = self;
    }
232

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

235
236
237
    [self.aspectRatioButton setBackgroundImage:[UIImage imageNamed:@"ratioButton"] forState:UIControlStateNormal];
    [self.aspectRatioButton setBackgroundImage:[UIImage imageNamed:@"ratioButtonHighlight"] forState:UIControlStateHighlighted];
    [self.aspectRatioButton setImage:[UIImage imageNamed:@"ratioIcon"] forState:UIControlStateNormal];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
238
    if (SYSTEM_RUNS_IOS7_OR_LATER) {
239
        self.backButton.tintColor = [UIColor colorWithRed:(190.0f/255.0f) green:(190.0f/255.0f) blue:(190.0f/255.0f) alpha:1.];
240
241
242
        self.toolbar.tintColor = [UIColor whiteColor];
        self.toolbar.barTintColor = [UIColor colorWithWhite:0.f alpha:1.f];

243
        CGRect rect = self.positionSlider.frame;
244
245
246
247
248
249
250
        rect.origin.y = rect.origin.y - 5.;
        self.positionSlider.frame = rect;
        rect = self.resetVideoFilterButton.frame;
        rect.origin.y = rect.origin.y + 5.;
        self.resetVideoFilterButton.frame = rect;
    } else {
        [self.toolbar setBackgroundImage:[UIImage imageNamed:@"seekbarBg"] forBarMetrics:UIBarMetricsDefault];
251
252
253
        [self.backButton setBackgroundImage:[UIImage imageNamed:@"playbackDoneButton"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
        [self.backButton setBackgroundImage:[UIImage imageNamed:@"playbackDoneButtonHighlight"] forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
    }
254

255
256
257
258
259
260
261
262
263
264
    /* this looks a bit weird, but we need to support iOS 5 and should show the same appearance */
    UISlider *volumeSlider = nil;
    for (id aView in self.volumeView.subviews){
        if ([[[aView class] description] isEqualToString:@"MPVolumeSlider"]){
            volumeSlider = (UISlider *)aView;
            break;
        }
    }
    [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];
265
    [volumeSlider setThumbImage:[UIImage imageNamed:@"volumeballslider"] forState:UIControlStateNormal];
266
267
268
    [volumeSlider addTarget:self
                     action:@selector(volumeSliderAction:)
           forControlEvents:UIControlEventValueChanged];
269

270
    [[AVAudioSession sharedInstance] setDelegate:self];
271
272
273

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

    _playerIsSetup = NO;
276
277
278

    [self.movieView setAccessibilityLabel:NSLocalizedString(@"VO_VIDEOPLAYER_TITLE", @"")];
    [self.movieView setAccessibilityHint:NSLocalizedString(@"VO_VIDEOPLAYER_DOUBLETAP", @"")];
279
}
Felix Paul Kühne's avatar
Felix Paul Kühne committed
280

281
282
283
284
285
- (BOOL)_blobCheck
{
    NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *directoryPath = searchPaths[0];

286
    if (![[NSFileManager defaultManager] fileExistsAtPath:[directoryPath stringByAppendingPathComponent:@"blob.bin"]])
287
288
        return NO;

289
    NSData *data = [NSData dataWithContentsOfFile:[directoryPath stringByAppendingPathComponent:@"blob.bin"]];
290
291
292
293
294
295
296
297
298
299
300
301
302
303
    uint8_t digest[CC_SHA1_DIGEST_LENGTH];
    CC_SHA1(data.bytes, data.length, digest);

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

304
305
- (void)viewWillAppear:(BOOL)animated
{
306
    [super viewWillAppear:animated];
307

308
    [self.navigationController setNavigationBarHidden:YES animated:YES];
309

Felix Paul Kühne's avatar
Felix Paul Kühne committed
310
    if (!SYSTEM_RUNS_IOS7_OR_LATER) {
311
312
313
        if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
            [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleBlackTranslucent;
    }
314

315
316
317
318
319
320
321
322
323
324
325
    [self _startPlayback];

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

- (void)_startPlayback
{
    if (_playerIsSetup)
        return;

326
327
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

328
329
    if (!self.mediaItem && !self.url) {
        [self _stopPlayback];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
330
        return;
331
    }
332

333
334
335
336
    _mediaPlayer = [[VLCMediaPlayer alloc] initWithOptions:@[[NSString stringWithFormat:@"--%@=%@", kVLCSettingSubtitlesFont, [defaults objectForKey:kVLCSettingSubtitlesFont]], [NSString stringWithFormat:@"--%@=%@", kVLCSettingSubtitlesFontColor, [defaults objectForKey:kVLCSettingSubtitlesFontColor]], [NSString stringWithFormat:@"--%@=%@", kVLCSettingSubtitlesFontSize, [defaults objectForKey:kVLCSettingSubtitlesFontSize]], [NSString stringWithFormat:@"--%@=%@", kVLCSettingDeinterlace, [defaults objectForKey:kVLCSettingDeinterlace]]]];
    [_mediaPlayer setDelegate:self];
    [_mediaPlayer setDrawable:self.movieView];

337
    VLCMedia *media;
338
339
    if (self.mediaItem) {
        self.title = [self.mediaItem title];
340
341
342
343
344
345
346
347
        MLFile *item = self.mediaItem;
        media = [VLCMedia mediaWithURL:[NSURL URLWithString:item.url]];
        item.unread = @(NO);

        if (item.isAlbumTrack) {
            self.trackNameLabel.text = item.albumTrack.title;
            self.artistNameLabel.text = item.albumTrack.artist;
            self.albumNameLabel.text = item.albumTrack.album.name;
348
349
        } else
            self.trackNameLabel.text = self.artistNameLabel.text = self.albumNameLabel.text = @"";
350
    } else {
351
        media = [VLCMedia mediaWithURL:self.url];
352
353
        self.title = @"Network Stream";
    }
Felix Paul Kühne's avatar
Felix Paul Kühne committed
354

355
356
    [media addOptions:
     @{kVLCSettingStretchAudio :
357
     [[defaults objectForKey:kVLCSettingStretchAudio] boolValue] ? kVLCSettingStretchAudioOnValue : kVLCSettingStretchAudioOffValue, kVLCSettingTextEncoding : [defaults objectForKey:kVLCSettingTextEncoding], kVLCSettingSkipLoopFilter : [defaults objectForKey:kVLCSettingSkipLoopFilter]}];
358

359
360
361
362
    [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"];

363
    if ([tzNames containsObject:tzName] || [[tzName stringByDeletingLastPathComponent] isEqualToString:@"US"]) {
364
365
366
367
368
369
370
371
372
373
374
375
        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:
376
377
                    case 1684566644:
                    case 2126701:
378
                    {
379
380
381
382
                        if (![self _blobCheck]) {
                            [media addOptions:@{@"no-audio" : [NSNull null]}];
                            APLog(@"audio playback disabled because an unsupported codec was found");
                        }
383
384
385
386
387
388
389
390
391
392
                        break;
                    }

                    default:
                        break;
                }
            }
        }
    }

393
394
    [_mediaPlayer setMedia:media];

395
    self.positionSlider.value = 0.;
396
    [self.timeDisplay setTitle:@"" forState:UIControlStateNormal];
397
    self.timeDisplay.accessibilityLabel = @"";
398

399
400
401
402
403
    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];
404

405
406
    if (![self hasExternalDisplay])
        self.brightnessSlider.value = [UIScreen mainScreen].brightness * 2.;
407
408
409
410
411
412
413
414
415
}

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

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

416
    NSInteger speedCategory = [[UIDevice currentDevice] speedCategory];
417

418
    if (speedCategory == 1) {
419
420
        // iPhone 3GS, iPhone 4, first gen. iPad, 3rd and 4th generation iPod touch
        return (totalNumberOfPixels < 600000); // between 480p and 720p
421
    } else if (speedCategory == 2) {
422
423
        // iPhone 4S, iPad 2 and 3, iPod 4 and 5
        return (totalNumberOfPixels < 922000); // 720p
424
    } else if (speedCategory == 3) {
425
426
427
428
429
430
431
432
433
434
435
        // iPhone 5, iPad 4
        return (totalNumberOfPixels < 2074000); // 1080p
    }

    return YES;
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 1)
        [self _playNewMedia];
436
437
    else {
        [self _stopPlayback];
438
        [self closePlayback:nil];
439
    }
440
441
442
443
}

- (void)_playNewMedia
{
444
    NSNumber *playbackPositionInTime = @(0);
445
    if (self.mediaItem.lastPosition && [self.mediaItem.lastPosition floatValue] < .95) {
446
447
        if (self.mediaItem.duration.intValue != 0)
            playbackPositionInTime = @(self.mediaItem.lastPosition.floatValue * (self.mediaItem.duration.intValue / 1000.));
448
    }
449
450
451
452
    if (playbackPositionInTime.intValue > 0) {
        [_mediaPlayer.media addOptions:@{@"start-time": playbackPositionInTime}];
        APLog(@"set starttime to %i", playbackPositionInTime.intValue);
    }
453

454
455
456
    [_mediaPlayer addObserver:self forKeyPath:@"time" options:0 context:nil];
    [_mediaPlayer addObserver:self forKeyPath:@"remainingTime" options:0 context:nil];

Felix Paul Kühne's avatar
Felix Paul Kühne committed
457
    [_mediaPlayer play];
458

459
460
461
462
463
464
    if (self.mediaItem) {
        if (self.mediaItem.lastAudioTrack.intValue > 0)
            _mediaPlayer.currentAudioTrackIndex = self.mediaItem.lastAudioTrack.intValue;
        if (self.mediaItem.lastSubtitleTrack.intValue > 0)
            _mediaPlayer.currentVideoSubTitleIndex = self.mediaItem.lastSubtitleTrack.intValue;
    }
465

466
467
    self.playbackSpeedSlider.value = [self _playbackSpeed];
    [self _updatePlaybackSpeedIndicator];
468

469
470
    [self performSelectorInBackground:@selector(_updateExportedPlaybackInformation) withObject:nil];

471
472
    _currentAspectRatioMask = 0;
    _mediaPlayer.videoAspectRatio =  NULL;
473

474
    [self _resetIdleTimer];
475
    _playerIsSetup = YES;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
476
477
}

478
- (void)viewWillDisappear:(BOOL)animated
Felix Paul Kühne's avatar
Felix Paul Kühne committed
479
{
480
    [self _stopPlayback];
481
    _viewAppeared = NO;
482
    if (_idleTimer) {
483
        [_idleTimer invalidate];
484
485
        _idleTimer = nil;
    }
486
    [self.navigationController setNavigationBarHidden:NO animated:YES];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
487
    if (!SYSTEM_RUNS_IOS7_OR_LATER)
488
        [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleBlackOpaque;
489
    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
490
    [super viewWillDisappear:animated];
491

Felix Paul Kühne's avatar
Felix Paul Kühne committed
492
493
494
495
496
497
    // 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
498
499
}

500
501
502
- (void)_stopPlayback
{
    if (_mediaPlayer) {
503
504
505
506
507
508
509
510
        @try {
            [_mediaPlayer removeObserver:self forKeyPath:@"time"];
            [_mediaPlayer removeObserver:self forKeyPath:@"remainingTime"];
        }
        @catch (NSException *exception) {
            APLog(@"we weren't an observer yet");
        }

511
        if (_mediaPlayer.media) {
512
513
514
            [_mediaPlayer pause];
            [self _saveCurrentState];
            [_mediaPlayer stop];
515
        }
516
517
        if (_mediaPlayer)
            _mediaPlayer = nil;
518
519
520
521
522
523
524
    }
    if (_mediaItem)
        _mediaItem = nil;

    _playerIsSetup = NO;
}

525
526
527
- (void)_saveCurrentState
{
    if (self.mediaItem) {
528
529
530
531
532
533
534
535
536
        @try {
            MLFile *item = self.mediaItem;
            item.lastPosition = @([_mediaPlayer position]);
            item.lastAudioTrack = @(_mediaPlayer.currentAudioTrackIndex);
            item.lastSubtitleTrack = @(_mediaPlayer.currentVideoSubTitleIndex);
        }
        @catch (NSException *exception) {
            APLog(@"failed to save current media state - file removed?");
        }
537
538
539
    }
}

540
541
542
543
544
545
546
547
548
549
550
551
552
553
#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];
554
555

    [[NSUserDefaults standardUserDefaults] setBool:_displayRemainingTime forKey:kVLCShowRemainingTime];
556
557
558
559
560
561
562
563
564
565
566
567
568
}

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

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

570
571
572
573
574
        case UIEventSubtypeRemoteControlPause:
            [_mediaPlayer pause];
            break;

        case UIEventSubtypeRemoteControlTogglePlayPause:
575
            [self playPause];
576
577
578
579
580
581
582
            break;

        default:
            break;
    }
}

583
584
#pragma mark - controls visibility

585
586
587
588
589
590
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer
{
    if (recognizer.velocity < 0.)
        [self closePlayback:nil];
}

591
592
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
593
    if (touch.view != self.view)
594
595
596
597
598
        return NO;

    return YES;
}

Ahmad Harb's avatar
Ahmad Harb committed
599
600
601
602
603
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}

604
- (void)setControlsHidden:(BOOL)hidden animated:(BOOL)animated
605
{
606
    _controlsHidden = hidden;
607
    CGFloat alpha = _controlsHidden? 0.0f: 1.0f;
608

609
610
    if (!_controlsHidden) {
        _controllerPanel.alpha = 0.0f;
611
        _controllerPanel.hidden = !_videoFiltersHidden;
612
613
        _toolbar.alpha = 0.0f;
        _toolbar.hidden = NO;
614
615
        _videoFilterView.alpha = 0.0f;
        _videoFilterView.hidden = _videoFiltersHidden;
616
617
        _playbackSpeedView.alpha = 0.0f;
        _playbackSpeedView.hidden = _playbackSpeedViewHidden;
618
    }
619

620
621
    void (^animationBlock)() = ^() {
        _controllerPanel.alpha = alpha;
622
        _toolbar.alpha = alpha;
623
        _videoFilterView.alpha = alpha;
624
        _playbackSpeedView.alpha = alpha;
625
    };
626

627
    void (^completionBlock)(BOOL finished) = ^(BOOL finished) {
628
        if (_videoFiltersHidden)
629
            _controllerPanel.hidden = _controlsHidden;
630
        else
631
            _controllerPanel.hidden = NO;
632
        _toolbar.hidden = _controlsHidden;
633
        _videoFilterView.hidden = _videoFiltersHidden;
634
        _playbackSpeedView.hidden = _playbackSpeedViewHidden;
635
    };
636

637
638
    UIStatusBarAnimation animationType = animated? UIStatusBarAnimationFade: UIStatusBarAnimationNone;
    NSTimeInterval animationDuration = animated? 0.3: 0.0;
639
640

    [[UIApplication sharedApplication] setStatusBarHidden:_viewAppeared ? _controlsHidden : NO withAnimation:animationType];
641
    [UIView animateWithDuration:animationDuration animations:animationBlock completion:completionBlock];
642
643

    _volumeView.hidden = _controllerPanel.hidden;
644
}
645

646
- (void)toggleControlsVisible
647
{
648
649
650
    if (_controlsHidden && !_videoFiltersHidden)
        _videoFiltersHidden = YES;

651
    [self setControlsHidden:!_controlsHidden animated:YES];
652
653
}

654
- (void)_resetIdleTimer
655
656
{
    if (!_idleTimer)
657
        _idleTimer = [NSTimer scheduledTimerWithTimeInterval:4.
658
659
660
661
662
                                                      target:self
                                                    selector:@selector(idleTimerExceeded)
                                                    userInfo:nil
                                                     repeats:NO];
    else {
663
664
        if (fabs([_idleTimer.fireDate timeIntervalSinceNow]) < 4.)
            [_idleTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:4.]];
665
666
667
668
669
670
671
    }
}

- (void)idleTimerExceeded
{
    _idleTimer = nil;
    if (!_controlsHidden)
672
        [self toggleControlsVisible];
673

674
675
676
677
678
679
    if (!_videoFiltersHidden)
        _videoFiltersHidden = YES;

    if (!_playbackSpeedViewHidden)
        _playbackSpeedViewHidden = YES;

Felix Paul Kühne's avatar
Felix Paul Kühne committed
680
681
    if (self.scrubIndicatorView.hidden == NO)
        self.scrubIndicatorView.hidden = YES;
682
683
}

684
685
- (UIResponder *)nextResponder
{
686
    [self _resetIdleTimer];
687
688
689
    return [super nextResponder];
}

690
691
692
693
#pragma mark - controls

- (IBAction)closePlayback:(id)sender
{
694
    [self setControlsHidden:NO animated:NO];
695
696
697
698
699
    [self.navigationController popViewControllerAnimated:YES];
}

- (IBAction)positionSliderAction:(UISlider *)sender
{
700
701
702
    /* we need to limit the number of events sent by the slider, since otherwise, the user
     * wouldn't see the I-frames when seeking on current mobile devices. This isn't a problem
     * within the Simulator, but especially on older ARMv7 devices, it's clearly noticeable. */
703
    [self performSelector:@selector(_setPositionForReal) withObject:nil afterDelay:0.3];
704
    VLCTime *newPosition = [VLCTime timeWithInt:(int)(_positionSlider.value * self.mediaItem.duration.intValue)];
705
    [self.timeDisplay setTitle:newPosition.stringValue forState:UIControlStateNormal];
706
    self.timeDisplay.accessibilityLabel = [NSString stringWithFormat:@"%@: %@", NSLocalizedString(@"PLAYBACK_POSITION", @""), newPosition.stringValue];
707
    _positionSet = NO;
708
    [self _resetIdleTimer];
709
710
}

711
712
713
714
715
716
717
718
- (void)_setPositionForReal
{
    if (!_positionSet) {
        _mediaPlayer.position = _positionSlider.value;
        _positionSet = YES;
    }
}

719
720
721
722
- (IBAction)positionSliderTouchDown:(id)sender
{
    [self _updateScrubLabel];
    self.scrubIndicatorView.hidden = NO;
723
    _isScrubbing = YES;
724
725
726
727
728
}

- (IBAction)positionSliderTouchUp:(id)sender
{
    self.scrubIndicatorView.hidden = YES;
729
    _isScrubbing = NO;
730
731
732
733
734
735
736
737
738
739
740
741
742
}

- (void)_updateScrubLabel
{
    float speed = self.positionSlider.scrubbingSpeed;
    if (speed == 1.)
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_HIGH", @"");
    else if (speed == .5)
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_HALF", @"");
    else if (speed == .25)
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_QUARTER", @"");
    else
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_FINE", @"");
743
744

    [self _resetIdleTimer];
745
746
747
748
749
750
751
}

- (IBAction)positionSliderDrag:(id)sender
{
    [self _updateScrubLabel];
}

752
753
754
755
756
- (IBAction)volumeSliderAction:(id)sender
{
    [self _resetIdleTimer];
}

757
758
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
759
760
761
762
    if (!_isScrubbing) {
        self.positionSlider.value = [_mediaPlayer position];
    }

763
    if (_displayRemainingTime)
764
        [self.timeDisplay setTitle:[[_mediaPlayer remainingTime] stringValue] forState:UIControlStateNormal];
765
    else
766
        [self.timeDisplay setTitle:[[_mediaPlayer time] stringValue] forState:UIControlStateNormal];
767
768
}

769
770
- (void)mediaPlayerStateChanged:(NSNotification *)aNotification
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
771
772
773
774
    VLCMediaPlayerState currentState = _mediaPlayer.state;

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

778
779
780
    if (currentState == VLCMediaPlayerStateEnded || currentState == VLCMediaPlayerStateStopped)
        [self performSelector:@selector(closePlayback:) withObject:nil afterDelay:2.];

781
    UIImage *playPauseImage = [_mediaPlayer isPlaying]? [UIImage imageNamed:@"pauseIcon"] : [UIImage imageNamed:@"playIcon"];
Gleb Pinigin's avatar
Gleb Pinigin committed
782
    [_playPauseButton setImage:playPauseImage forState:UIControlStateNormal];
783
784
785
786
787
788
789
790
791
792

    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;
793
794
}

795
- (IBAction)playPause
796
{
797
    if ([_mediaPlayer isPlaying])
798
        [_mediaPlayer pause];
799
    else
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
        [_mediaPlayer play];
}

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

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

- (IBAction)switchAudioTrack:(id)sender
{
815
    _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
816
    NSArray *audioTracks = [_mediaPlayer audioTrackNames];
817
818
    NSArray *audioTrackIndexes = [_mediaPlayer audioTrackIndexes];

819
    NSUInteger count = [audioTracks count];
820
821
822
823
824
    for (NSUInteger i = 0; i < count; i++) {
        NSString *indexIndicator = ([audioTrackIndexes[i] intValue] == [_mediaPlayer currentAudioTrackIndex])? @"\u2713": @"";
        NSString *buttonTitle = [NSString stringWithFormat:@"%@ %@", indexIndicator, audioTracks[i]];
        [_audiotrackActionSheet addButtonWithTitle:buttonTitle];
    }
Felix Paul Kühne's avatar
Felix Paul Kühne committed
825

826
    [_audiotrackActionSheet addButtonWithTitle:NSLocalizedString(@"BUTTON_CANCEL", @"cancel button")];
827
    [_audiotrackActionSheet setCancelButtonIndex:[_audiotrackActionSheet numberOfButtons] - 1];
828
    [_audiotrackActionSheet showInView:self.audioSwitcherButton];
829
830
831
832
}

- (IBAction)switchSubtitleTrack:(id)sender
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
833
    NSArray *spuTracks = [_mediaPlayer videoSubTitlesNames];
834
    NSArray *spuTrackIndexes = [_mediaPlayer videoSubTitlesIndexes];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
835

836
    NSUInteger count = [spuTracks count];
837
838
    if (count <= 1)
        return;
839
    _subtitleActionSheet = [[UIActionSheet alloc] initWithTitle:NSLocalizedString(@"CHOOSE_SUBTITLE_TRACK", @"subtitle track selector") delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles: nil];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
840

841
842
843
844
845
846
    for (NSUInteger i = 0; i < count; i++) {
        NSString *indexIndicator = ([spuTrackIndexes[i] intValue] == [_mediaPlayer currentVideoSubTitleIndex])? @"\u2713": @"";
        NSString *buttonTitle = [NSString stringWithFormat:@"%@ %@", indexIndicator, spuTracks[i]];
        [_subtitleActionSheet addButtonWithTitle:buttonTitle];
    }

847
    [_subtitleActionSheet addButtonWithTitle:NSLocalizedString(@"BUTTON_CANCEL", @"cancel button")];
848
    [_subtitleActionSheet setCancelButtonIndex:[_subtitleActionSheet numberOfButtons] - 1];
849
    [_subtitleActionSheet showInView: self.subtitleSwitcherButton];
850
851
852
}

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
853
854
    if (buttonIndex == [actionSheet cancelButtonIndex])
        return;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
855

Felix Paul Kühne's avatar
Felix Paul Kühne committed
856
    NSArray *indexArray;
857
    if (actionSheet == _subtitleActionSheet) {
858
859
860
        indexArray = _mediaPlayer.videoSubTitlesIndexes;
        if (buttonIndex <= indexArray.count) {
            _mediaPlayer.currentVideoSubTitleIndex = [indexArray[buttonIndex] intValue];
861
        }
862
    } else if (actionSheet == _audiotrackActionSheet) {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
863
        indexArray = _mediaPlayer.audioTrackIndexes;
864
865
        if (buttonIndex <= indexArray.count) {
            _mediaPlayer.currentAudioTrackIndex = [indexArray[buttonIndex] intValue];
866
        }
867
868
869
    }
}

870
- (IBAction)toggleTimeDisplay:(id)sender
871
872
{
    _displayRemainingTime = !_displayRemainingTime;
873
874

    [self _resetIdleTimer];
875
876
}

Ahmad Harb's avatar
Ahmad Harb committed
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
#pragma mark - multi-touch gestures
-(CGSize)_screenSize
{
    UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
    CGRect screenRect = [[UIScreen mainScreen] bounds];

    CGFloat screenHeight, screenWidth = .0;

    if (orientation == UIDeviceOrientationPortrait)
    {
        screenHeight = screenRect.size.height;
        screenWidth = screenRect.size.width;
    }
    else
    {
        screenHeight = screenRect.size.width;
        screenWidth = screenRect.size.height;
    }

    return CGSizeMake(screenWidth, screenHeight);
}
898

Ahmad Harb's avatar
Ahmad Harb committed
899
-(void)changePlaybackStatus
900
{
Ahmad Harb's avatar
Ahmad Harb committed
901
902
903
904
905
906
    if ([_mediaPlayer isPlaying]) {
        [_mediaPlayer pause];
        [self displayHUDwithText:@"  ▌▌"];
    } else {
        [_mediaPlayer play];
        [self displayHUDwithText:@"  ►"];
907
    }
Ahmad Harb's avatar
Ahmad Harb committed
908
909
910
911
    [_rootView addSubview:_splashView];
    [UIView animateWithDuration:1
                     animations:^{ _splashView.alpha = 0.0f;}
                     completion:^(BOOL finished){ [_splashView removeFromSuperview]; }];
912
913
}

Ahmad Harb's avatar
Ahmad Harb committed
914
-(void)tapRecognized
915
{
Ahmad Harb's avatar
Ahmad Harb committed
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
    [self changePlaybackStatus];
}

-(NSString*)detectPanTypeForPan:(UIPanGestureRecognizer*)panRecognizer
{
    NSString * type;
    NSString * deviceType = [[UIDevice currentDevice] model];
    type = @"Volume"; // default in case of error
    CGPoint location = [panRecognizer locationInView:_rootView];
    CGFloat position = location.x;
    CGSize screenSize = [self _screenSize];

    if ( position < screenSize.width /2 )
        type = @"Brightness";

    if (position > screenSize.width /2 )
        type = @"Volume";

    // only check for seeking gesture if on iPad , will overwrite last statements if true
    if ([deviceType isEqualToString:@"iPad"]) {
        if (location.y < 110)
            type = @"Seek";
    }

    return type;
}

-(CGFloat)getHorizontalDirectionForPan:(UIPanGestureRecognizer*)panRecognizer
{
    CGPoint velocity = [panRecognizer velocityInView:_rootView];
    CGFloat direction = velocity.x;
    return direction;
}

-(CGFloat)getVerticalDirectionForPan:(UIPanGestureRecognizer*)panRecognizer
{
    CGPoint velocity = [panRecognizer velocityInView:_rootView];
    CGFloat Direction = velocity.y;
    return Direction;
}

-(void)displayHUDwithText:(NSString*)text
{
    CGSize screenSize = [self _screenSize];
    UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake( (screenSize.width/2 - text.length*8.5/2), 72, (text.length * 8.5), 20)];
    [label setText:text];
    [label setAdjustsFontSizeToFitWidth:YES];
    [label setBackgroundColor:[UIColor blackColor]];
    [label setTextColor:[UIColor whiteColor]];
    [label setAlpha:0.55];
    [_rootView addSubview:label];

    [UIView animateWithDuration:1 animations:^{ label.alpha = 0.0f;} completion:^(BOOL finished){ [label removeFromSuperview]; }];
}

-(void)adjustVolume
{
    MPMusicPlayerController *musicPlayer = [MPMusicPlayerController applicationMusicPlayer];
    if (_panDirectionY >0)
    {
        musicPlayer.volume -= 0.01;
    }
    else
    {
        musicPlayer.volume += 0.01;
    }
    NSString *volume =[NSString stringWithFormat:@" Volume : %@ %%", [[[NSString stringWithFormat:@"%f",(musicPlayer.volume*100)] componentsSeparatedByString:@"."] objectAtIndex:0]];
    [self displayHUDwithText:volume];
}

-(void)adjustBrightness
{
    CGFloat brightness = [UIScreen mainScreen].brightness;
    if (_panDirectionY >0)
    {
        [[UIScreen mainScreen]setBrightness:(brightness-0.01)];
    }
    else
    {
        [[UIScreen mainScreen]setBrightness:(brightness+0.01)];
    }

    NSString *brightnessHUD =[NSString stringWithFormat:@" Brightness : %@ %%", [[[NSString stringWithFormat:@"%f",(brightness*100)] componentsSeparatedByString:@"."] objectAtIndex:0]];
    [self displayHUDwithText:brightnessHUD];