source: branches/CoreData/SapphireFrappliance/Players/SapphireVideoPlayer.m @ 875

Revision 875, 9.3 KB checked in by gbooker, 5 years ago (diff)

Only attempt to disable audio tracks after the first is detected and abort if an AC3 track is encountered as disabling it breaks AC3 passthrough
Fixes 294

Line 
1/*
2 * SapphireVideoPlayer.m
3 * Sapphire
4 *
5 * Created by Graham Booker on Jun. 25, 2007.
6 * Copyright 2007 Sapphire Development Team and/or www.nanopi.net
7 * All rights reserved.
8 *
9 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
10 * General Public License as published by the Free Software Foundation; either version 3 of the License,
11 * or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
14 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
15 * Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along with this program; if not,
18 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 */
20
21#import "SapphireVideoPlayer.h"
22#import <SapphireCompatClasses/SapphireFrontRowCompat.h>
23#import <QTKit/QTKit.h>
24#import <objc/objc-class.h>
25
26/*This is a private function somewhere, so declare to remove warnings*/
27@interface QTMovie (whoKnows)
28- (BOOL)hasChapters;
29@end
30
31/*These interfaces are to access variables not available*/
32@interface BRQTKitVideoPlayer (privateFunctions)
33- (BRVideo *)gimmieVideo;
34@end
35
36@interface BRVideo (privateFunctions)
37- (Movie)gimmieMovie;
38@end
39
40@implementation BRQTKitVideoPlayer (privateFunctions)
41- (BRVideo *)gimmieVideo
42{
43        Class myClass = [self class];
44        Ivar ret = class_getInstanceVariable(myClass, "_video");
45       
46        return *(BRVideo * *)(((char *)self)+ret->ivar_offset);
47}
48@end
49
50@implementation BRVideo (privateFunctions)
51- (Movie)gimmieMovie
52{
53        Class myClass = [self class];
54        Ivar ret = class_getInstanceVariable(myClass, "_movie");
55       
56        if([SapphireFrontRowCompat usingTakeTwo])
57                return *(Movie *)(((char *)self)+ret->ivar_offset);
58        QTMovie *qtmov = *(QTMovie * *)(((char *)self)+ret->ivar_offset);
59        return [qtmov quickTimeMovie];
60}
61@end
62
63typedef enum
64        {
65                kBRMediaPlayerStateStopped =            0,
66                kBRMediaPlayerStatePaused,
67                kBRMediaPlayerStateLoading,
68                kBRMediaPlayerStatePlaying,
69                kBRMediaPlayerStateFastForwardLevel1,
70                kBRMediaPlayerStateFastForwardLevel2,
71                kBRMediaPlayerStateFastForwardLevel3,
72                kBRMediaPlayerStateRewindLevel1,
73                kBRMediaPlayerStateRewindLevel2,
74                kBRMediaPlayerStateRewindLevel3,
75                kBRMediaPlayerStateSlowForwardLevel1,
76                kBRMediaPlayerStateSlowForwardLevel2,
77                kBRMediaPlayerStateSlowForwardLevel3,
78                kBRMediaPlayerStateSlowRewindLevel1,
79                kBRMediaPlayerStateSlowRewindLevel2,
80                kBRMediaPlayerStateSlowRewindLevel3,
81               
82                kBRMediaPlayerStateRewind = kBRMediaPlayerStateRewindLevel1,    // default
83                kBRMediaPlayerStateFastForward = kBRMediaPlayerStateFastForwardLevel1,  // default
84               
85                kBRMediaPlayerStateRESERVED     =               20,
86               
87                // Individual player subclasses may create their own states beyond the
88                // reserved states. For instance, the DVD player may want to create states
89                // for when it's in menus.
90               
91        } BRMediaPlayerState;
92
93@interface BRQTKitVideoPlayer (compat)
94-(BOOL)setState:(BRMediaPlayerState)state error:(NSError **)error;
95-(BOOL)setMedia:(BRBaseMediaAsset *)asset inTrackList:(NSArray *)tracklist error:(NSError **)error;
96-(BOOL)setMediaAtIndex:(long)index inTrackList:(NSArray *)tracklist error:(NSError **)error;
97-(double)duration;
98-(double)elapsedTime;
99@end
100
101
102#define LOW_SKIP_TIME 5.0f
103
104typedef enum {
105        STATE_COMMAND_RESET,
106        STATE_COMMAND_FORWARD,
107        STATE_COMMAND_BACKWARD,
108} StateCommand;
109
110@implementation SapphireVideoPlayer
111
112- (id)init
113{
114        self = [super init];
115        if(!self)
116                return nil;
117       
118        /* Initial skip times */
119        skipTime = LOW_SKIP_TIME;
120        state = SKIP_STATE_NONE;
121       
122        return self;
123}
124
125- (void)dealloc
126{
127//      [resetTimer invalidate];
128        [super dealloc];
129}
130
131- (BOOL)movieHasChapters:(Movie)mov
132{
133        int tkCount = GetMovieTrackCount(mov);
134        int i;
135        for(i=0; i<tkCount; i++)
136        {
137                Track track = GetMovieIndTrack(mov, i);
138                if(!GetTrackEnabled(track))
139                        continue;
140               
141                if(GetTrackReference(track, 'chap', 1) != NULL)
142                        return YES;
143        }
144       
145        return NO;
146}
147
148- (void)onlyEnableFirstAudioTrack:(Movie)mov
149{
150        int tkCount = GetMovieTrackCount(mov);
151        int i;
152        BOOL enabledFirstAudio = NO;
153        for(i=0; i<tkCount; i++)
154        {
155                Track track = GetMovieIndTrack(mov, i);
156                if(!GetTrackEnabled(track))
157                        continue;
158               
159                OSType trackType;
160                Media media = GetTrackMedia(track);
161                GetMediaHandlerDescription(media, &trackType, NULL, NULL);
162                if(trackType != SoundMediaType)
163                        continue;
164               
165                SoundDescriptionHandle soundDesc = (SoundDescriptionHandle) NewHandle(0);
166                GetMediaSampleDescription(media, 1, (SampleDescriptionHandle)soundDesc);
167               
168                BOOL detectedAC3 = NO;
169                if((*soundDesc)->dataFormat == 'ac-3')
170                        detectedAC3 = YES;
171               
172                DisposeHandle((Handle)soundDesc);
173                if(detectedAC3)
174                        break;
175               
176                if(enabledFirstAudio)
177                        SetTrackEnabled(track, NO);
178                else if(GetTrackEnabled(track))
179                        enabledFirstAudio = YES;
180        }
181}
182
183- (void)checkIfCanEnable
184{
185        /*Check to see if the movie has any chapters by default*/
186        Movie myMovie = [[self gimmieVideo] gimmieMovie];
187       
188        BOOL hasChapters = [self movieHasChapters:myMovie];
189        duration = ((double)GetMovieDuration(myMovie)) / ((double)GetMovieTimeScale(myMovie));
190       
191        if(!hasChapters)
192                enabled = TRUE;
193       
194        /*Turn off multiple audio tracks*/
195        [self onlyEnableFirstAudioTrack:myMovie];
196}
197
198- (BOOL)prerollMedia:(NSError * *)fp8
199{
200        BOOL ret = [super prerollMedia:fp8];
201       
202        if(!ret)
203                return ret;
204       
205        [self checkIfCanEnable];
206
207        return ret;
208}
209
210-(BOOL)setState:(BRMediaPlayerState)playState error:(NSError **)error;
211{
212        BOOL ret = [super setState:playState error:error];
213       
214        if(!ret)
215                return ret;
216
217        if(!enabledChecked && playState != kBRMediaPlayerStateStopped)
218        {
219                [self checkIfCanEnable];
220                enabledChecked = YES;
221        }
222       
223        return ret;
224}
225
226- (void)setNewTimer
227{
228        /*Reset the skip times after 3 seconds of non use*/
229        [resetTimer invalidate];
230        resetTimer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(resetTimes) userInfo:nil repeats:NO];
231}
232
233- (double)offsetForCommand:(StateCommand)command
234{
235        double ret = 0.0f;
236       
237        switch(command)
238        {
239                case STATE_COMMAND_RESET:
240                        state = SKIP_STATE_NONE;
241                        skipTime = LOW_SKIP_TIME;
242                        break;
243
244                case STATE_COMMAND_FORWARD:
245                        switch(state)
246                        {
247                                case SKIP_STATE_NONE:
248                                case SKIP_STATE_FORWARD_INCREASING:
249                                        state = SKIP_STATE_FORWARD_INCREASING;
250                                        ret = skipTime;
251                                        skipTime *= 2;
252                                        break;
253                                case SKIP_STATE_BACKWARD_INCREASING:
254                                        state = SKIP_STATE_DECREASING;
255                                        skipTime /= 4;
256                                case SKIP_STATE_DECREASING:
257                                        ret = skipTime;
258                                        skipTime = MAX(skipTime / 2, LOW_SKIP_TIME);
259                        }
260                        break;
261                       
262                case STATE_COMMAND_BACKWARD:
263                        switch(state)
264                        {
265                                case SKIP_STATE_NONE:
266                                case SKIP_STATE_BACKWARD_INCREASING:
267                                        state = SKIP_STATE_BACKWARD_INCREASING;
268                                        ret = -skipTime;
269                                        skipTime *= 2;
270                                        break;
271                                case SKIP_STATE_FORWARD_INCREASING:
272                                        state = SKIP_STATE_DECREASING;
273                                        skipTime /= 4;
274                                case SKIP_STATE_DECREASING:
275                                        ret = -skipTime;
276                                        skipTime = MAX(skipTime / 2, LOW_SKIP_TIME);
277                        }
278                        break;
279        }
280        return ret;
281}
282
283- (void)resetTimes
284{
285        /*Reset the times from the timer above*/
286        resetTimer = nil;
287        [self offsetForCommand:STATE_COMMAND_RESET];
288}
289
290- (double)_nextChapterMark
291{
292        /*Get the location of the next chapter mark*/
293        if(!enabled)
294                return [super _nextChapterMark];
295        /*Compute our's*/
296        double current = [self elapsedPlaybackTime];
297        double ret = current + [self offsetForCommand:STATE_COMMAND_FORWARD];
298       
299        if(ret > duration + 5.0f)
300        {
301                /*Halve the distance to the end of the file if skipping so much*/
302                ret = (current + duration) / 2;
303                skipTime = duration - current;
304        }
305       
306        /*Start the reset timer*/
307        [self setNewTimer];
308       
309        return ret;
310}
311
312- (double)getPreviousChapterMarkAndUpdate:(BOOL)update
313{
314        /*Compute our previous chapter*/
315        double current = [self elapsedPlaybackTime];
316        double ret;
317        if(update)
318                ret = current - [self offsetForCommand:STATE_COMMAND_BACKWARD];
319        else if(state == SKIP_STATE_DECREASING)
320                ret = current - skipTime * 2;
321        else
322                ret = current - skipTime / 2;
323       
324        /*Make sure we don't go past the beginning of the file*/
325        if(ret < 0.0f)
326                ret = 0.0f;
327       
328        return ret;
329}
330
331- (double)_previousChapterMark
332{
333        /*Get the location of the previous chapter mark*/
334        if(!enabled)
335                return [super _previousChapterMark];
336       
337        /*Compute our's*/
338        double ret = [self getPreviousChapterMarkAndUpdate:YES];
339       
340        /*Start the reset timer*/
341        [self setNewTimer];
342
343        return ret;
344}
345
346- (double)_virtualChapterMark
347{
348        if(!enabled)
349                return [super _virtualChapterMark];
350        /*If we are enabled, disable the virtual chapter marks*/
351        return 0.0f;
352}
353
354- (double)_currentChapterMark
355{
356        if(!enabled)
357                return [super _currentChapterMark];
358       
359        return [self getPreviousChapterMarkAndUpdate:NO];
360}
361
362- (BOOL)setMedia:(BRBaseMediaAsset *)asset error:(NSError **)error
363{
364        if([SapphireFrontRowCompat usingTakeTwoDotTwo])
365        {
366                if([[SapphireVideoPlayer superclass] instancesRespondToSelector:@selector(setMedia:inTrackList:error:)])
367                        return [super setMedia:asset inTrackList:[NSArray arrayWithObject:asset] error:error];
368                else
369                        return [super setMediaAtIndex:0 inTrackList:[NSArray arrayWithObject:asset] error:error];
370        }
371        return [super setMedia:asset error:error];
372}
373
374- (float)elapsedPlaybackTime
375{
376        if([[SapphireVideoPlayer superclass] instancesRespondToSelector:@selector(elapsedPlaybackTime)])
377                return [super elapsedPlaybackTime];
378        return [super elapsedTime];
379}
380
381- (double)trackDuration
382{
383        if([[SapphireVideoPlayer superclass] instancesRespondToSelector:@selector(trackDuration)])
384                return [super trackDuration];
385        return [super duration];
386}
387
388@end
Note: See TracBrowser for help on using the repository browser.