| 1 | /* |
|---|
| 2 | * SapphireVideoPlayerController.m |
|---|
| 3 | * Sapphire |
|---|
| 4 | * |
|---|
| 5 | * Created by pnmerrill on Apr. 26, 2008. |
|---|
| 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 <AudioUnit/AudioUnit.h> |
|---|
| 22 | #import <SapphireCompatClasses/SapphireFrontRowCompat.h> |
|---|
| 23 | |
|---|
| 24 | #import "SapphireVideoPlayerController.h" |
|---|
| 25 | #import "SapphireFileMetaData.h" |
|---|
| 26 | #import "SapphireSettings.h" |
|---|
| 27 | #import "SapphireMetaDataSupport.h" |
|---|
| 28 | |
|---|
| 29 | #define PASSTHROUGH_KEY (CFStringRef)@"attemptPassthrough" |
|---|
| 30 | #define A52_DOMIAN (CFStringRef)@"com.cod3r.a52codec" |
|---|
| 31 | |
|---|
| 32 | #define DTS_PASSTHROUGH_KEY CFSTR("attemptDTSPassthrough") |
|---|
| 33 | #define PERIAN_DOMAIN CFSTR("org.perian.Perian") |
|---|
| 34 | |
|---|
| 35 | #define SOUND_STATE_SOUND_ENABLED 1 |
|---|
| 36 | #define SOUND_STATE_AC3_PASSTHROUGH_WAS_ENABLED 2 |
|---|
| 37 | #define SOUND_STATE_PLAYER_TORNDOWN 4 |
|---|
| 38 | #define SOUND_STATE_DTS_PASSTHROUGH_WAS_ENABLED 8 |
|---|
| 39 | |
|---|
| 40 | @interface BRVideoPlayerController (compat) |
|---|
| 41 | - (id)initWithPlayer:(SapphireVideoPlayer *)player; |
|---|
| 42 | @end |
|---|
| 43 | |
|---|
| 44 | |
|---|
| 45 | @implementation SapphireVideoPlayerController |
|---|
| 46 | |
|---|
| 47 | - (id)initWithScene:(BRRenderScene *)scene player:(SapphireVideoPlayer *)player; |
|---|
| 48 | { |
|---|
| 49 | if([[BRVideoPlayerController class] instancesRespondToSelector:@selector(initWithScene:)]) |
|---|
| 50 | self = [super initWithScene:scene]; |
|---|
| 51 | else if([[BRVideoPlayerController class] instancesRespondToSelector:@selector(initWithPlayer:)]) |
|---|
| 52 | self = [super initWithPlayer:player]; |
|---|
| 53 | else |
|---|
| 54 | self = [super init]; |
|---|
| 55 | |
|---|
| 56 | [self setVideoPlayer:player]; |
|---|
| 57 | return self; |
|---|
| 58 | } |
|---|
| 59 | |
|---|
| 60 | - (void) dealloc |
|---|
| 61 | { |
|---|
| 62 | [currentPlayFile release]; |
|---|
| 63 | [super dealloc]; |
|---|
| 64 | } |
|---|
| 65 | |
|---|
| 66 | static BOOL findCorrectDescriptionForStream(AudioStreamID streamID, int sampleRate) |
|---|
| 67 | { |
|---|
| 68 | OSStatus err; |
|---|
| 69 | UInt32 propertySize = 0; |
|---|
| 70 | err = AudioStreamGetPropertyInfo(streamID, 0, kAudioStreamPropertyPhysicalFormats, &propertySize, NULL); |
|---|
| 71 | |
|---|
| 72 | if(err != noErr || propertySize == 0) |
|---|
| 73 | return NO; |
|---|
| 74 | |
|---|
| 75 | AudioStreamBasicDescription *descs = malloc(propertySize); |
|---|
| 76 | if(descs == NULL) |
|---|
| 77 | return NO; |
|---|
| 78 | |
|---|
| 79 | int formatCount = propertySize / sizeof(AudioStreamBasicDescription); |
|---|
| 80 | err = AudioStreamGetProperty(streamID, 0, kAudioStreamPropertyPhysicalFormats, &propertySize, descs); |
|---|
| 81 | |
|---|
| 82 | if(err != noErr) |
|---|
| 83 | { |
|---|
| 84 | free(descs); |
|---|
| 85 | return NO; |
|---|
| 86 | } |
|---|
| 87 | |
|---|
| 88 | int i; |
|---|
| 89 | BOOL ret = NO; |
|---|
| 90 | for(i=0; i<formatCount; i++) |
|---|
| 91 | { |
|---|
| 92 | if (descs[i].mBitsPerChannel == 16 && descs[i].mFormatID == kAudioFormatLinearPCM) |
|---|
| 93 | { |
|---|
| 94 | if(descs[i].mSampleRate == sampleRate) |
|---|
| 95 | { |
|---|
| 96 | err = AudioStreamSetProperty(streamID, NULL, 0, kAudioStreamPropertyPhysicalFormat, sizeof(AudioStreamBasicDescription), descs + i); |
|---|
| 97 | if(err != noErr) |
|---|
| 98 | continue; |
|---|
| 99 | ret = YES; |
|---|
| 100 | break; |
|---|
| 101 | } |
|---|
| 102 | } |
|---|
| 103 | } |
|---|
| 104 | free(descs); |
|---|
| 105 | return ret; |
|---|
| 106 | } |
|---|
| 107 | |
|---|
| 108 | static BOOL setupDevice(AudioDeviceID devID, int sampleRate) |
|---|
| 109 | { |
|---|
| 110 | OSStatus err; |
|---|
| 111 | UInt32 propertySize = 0; |
|---|
| 112 | err = AudioDeviceGetPropertyInfo(devID, 0, FALSE, kAudioDevicePropertyStreams, &propertySize, NULL); |
|---|
| 113 | |
|---|
| 114 | if(err != noErr || propertySize == 0) |
|---|
| 115 | return NO; |
|---|
| 116 | |
|---|
| 117 | AudioStreamID *streams = malloc(propertySize); |
|---|
| 118 | if(streams == NULL) |
|---|
| 119 | return NO; |
|---|
| 120 | |
|---|
| 121 | int streamCount = propertySize / sizeof(AudioStreamID); |
|---|
| 122 | err = AudioDeviceGetProperty(devID, 0, FALSE, kAudioDevicePropertyStreams, &propertySize, streams); |
|---|
| 123 | if(err != noErr) |
|---|
| 124 | { |
|---|
| 125 | free(streams); |
|---|
| 126 | return NO; |
|---|
| 127 | } |
|---|
| 128 | |
|---|
| 129 | int i; |
|---|
| 130 | BOOL ret = NO; |
|---|
| 131 | for(i=0; i<streamCount; i++) |
|---|
| 132 | { |
|---|
| 133 | if(findCorrectDescriptionForStream(streams[i], sampleRate)) |
|---|
| 134 | { |
|---|
| 135 | ret = YES; |
|---|
| 136 | break; |
|---|
| 137 | } |
|---|
| 138 | } |
|---|
| 139 | free(streams); |
|---|
| 140 | return ret; |
|---|
| 141 | } |
|---|
| 142 | |
|---|
| 143 | static BOOL setupAudioOutput(int sampleRate) |
|---|
| 144 | { |
|---|
| 145 | OSErr err; |
|---|
| 146 | UInt32 propertySize = 0; |
|---|
| 147 | |
|---|
| 148 | err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL); |
|---|
| 149 | if(err != noErr || propertySize == 0) |
|---|
| 150 | return NO; |
|---|
| 151 | |
|---|
| 152 | AudioDeviceID *devs = malloc(propertySize); |
|---|
| 153 | if(devs == NULL) |
|---|
| 154 | return NO; |
|---|
| 155 | |
|---|
| 156 | err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propertySize, devs); |
|---|
| 157 | if(err != noErr) |
|---|
| 158 | { |
|---|
| 159 | free(devs); |
|---|
| 160 | return NO; |
|---|
| 161 | } |
|---|
| 162 | |
|---|
| 163 | int i, devCount = propertySize/sizeof(AudioDeviceID); |
|---|
| 164 | BOOL ret = NO; |
|---|
| 165 | for(i=0; i<devCount; i++) |
|---|
| 166 | { |
|---|
| 167 | if(setupDevice(devs[i], sampleRate)) |
|---|
| 168 | { |
|---|
| 169 | err = AudioHardwareSetProperty(kAudioHardwarePropertyDefaultOutputDevice, sizeof(AudioDeviceID), devs + i); |
|---|
| 170 | if(err != noErr) |
|---|
| 171 | continue; |
|---|
| 172 | ret = YES; |
|---|
| 173 | break; |
|---|
| 174 | } |
|---|
| 175 | } |
|---|
| 176 | free(devs); |
|---|
| 177 | return ret; |
|---|
| 178 | } |
|---|
| 179 | |
|---|
| 180 | int enablePassthrough(SapphireFileMetaData *currentPlayFile) |
|---|
| 181 | { |
|---|
| 182 | int soundState; |
|---|
| 183 | SapphireSettings *settings = [SapphireSettings sharedSettings]; |
|---|
| 184 | BOOL useAC3Passthrough = NO; |
|---|
| 185 | BOOL useDTSPassthrough = NO; |
|---|
| 186 | |
|---|
| 187 | if([settings useAC3Passthrough]) |
|---|
| 188 | { |
|---|
| 189 | Float64 sampleRate = [currentPlayFile sampleRateValue]; |
|---|
| 190 | UInt32 type = [currentPlayFile audioFormatIDValue]; |
|---|
| 191 | |
|---|
| 192 | if((type == 'ac-3' || type == 0x6D732000) && setupAudioOutput((int)sampleRate)) |
|---|
| 193 | useAC3Passthrough = YES; |
|---|
| 194 | else if((type == 'DTS ') && setupAudioOutput((int)sampleRate)) |
|---|
| 195 | useDTSPassthrough = YES; |
|---|
| 196 | } |
|---|
| 197 | |
|---|
| 198 | Boolean temp; |
|---|
| 199 | BOOL AC3PassthroughEnabled = CFPreferencesGetAppBooleanValue(PASSTHROUGH_KEY, A52_DOMIAN, &temp); |
|---|
| 200 | BOOL DTSPassthroughEnabled = CFPreferencesGetAppBooleanValue(DTS_PASSTHROUGH_KEY, PERIAN_DOMAIN, &temp); |
|---|
| 201 | BOOL soundsWereEnabled = NO; |
|---|
| 202 | if(useAC3Passthrough || useDTSPassthrough) |
|---|
| 203 | { |
|---|
| 204 | RUIPreferences *prefs = [SapphireFrontRowCompat sharedFrontRowPreferences]; |
|---|
| 205 | soundsWereEnabled = [prefs boolForKey:@"PlayFrontRowSounds"]; |
|---|
| 206 | if(soundsWereEnabled) |
|---|
| 207 | [prefs setBool:NO forKey:@"PlayFrontRowSounds"]; |
|---|
| 208 | } |
|---|
| 209 | if(useAC3Passthrough) |
|---|
| 210 | CFPreferencesSetAppValue(PASSTHROUGH_KEY, (CFNumberRef)[NSNumber numberWithInt:1], A52_DOMIAN); |
|---|
| 211 | else |
|---|
| 212 | CFPreferencesSetAppValue(PASSTHROUGH_KEY, (CFNumberRef)[NSNumber numberWithInt:0], A52_DOMIAN); |
|---|
| 213 | if(useDTSPassthrough) |
|---|
| 214 | CFPreferencesSetAppValue(DTS_PASSTHROUGH_KEY, (CFNumberRef)[NSNumber numberWithInt:1], PERIAN_DOMAIN); |
|---|
| 215 | else |
|---|
| 216 | CFPreferencesSetAppValue(DTS_PASSTHROUGH_KEY, (CFNumberRef)[NSNumber numberWithInt:0], PERIAN_DOMAIN); |
|---|
| 217 | soundState = (AC3PassthroughEnabled ? SOUND_STATE_AC3_PASSTHROUGH_WAS_ENABLED : 0) | |
|---|
| 218 | (soundsWereEnabled ? SOUND_STATE_SOUND_ENABLED : 0) | |
|---|
| 219 | (DTSPassthroughEnabled ? SOUND_STATE_DTS_PASSTHROUGH_WAS_ENABLED : 0); |
|---|
| 220 | CFPreferencesAppSynchronize(A52_DOMIAN); |
|---|
| 221 | CFPreferencesAppSynchronize(PERIAN_DOMAIN); |
|---|
| 222 | return soundState; |
|---|
| 223 | } |
|---|
| 224 | |
|---|
| 225 | void teardownPassthrough(int soundState) |
|---|
| 226 | { |
|---|
| 227 | if(soundState & SOUND_STATE_PLAYER_TORNDOWN) |
|---|
| 228 | return; |
|---|
| 229 | |
|---|
| 230 | soundState |= SOUND_STATE_PLAYER_TORNDOWN; |
|---|
| 231 | //Turn off the AC3 Passthrough hack |
|---|
| 232 | CFPreferencesSetAppValue(PASSTHROUGH_KEY, (CFNumberRef)[NSNumber numberWithInt:((soundState & SOUND_STATE_AC3_PASSTHROUGH_WAS_ENABLED)? 1 : 0)], A52_DOMIAN); |
|---|
| 233 | CFPreferencesAppSynchronize(A52_DOMIAN); |
|---|
| 234 | CFPreferencesSetAppValue(DTS_PASSTHROUGH_KEY, (CFNumberRef)[NSNumber numberWithInt:((soundState & SOUND_STATE_DTS_PASSTHROUGH_WAS_ENABLED)? 1 : 0)], PERIAN_DOMAIN); |
|---|
| 235 | CFPreferencesAppSynchronize(PERIAN_DOMAIN); |
|---|
| 236 | if(soundState & SOUND_STATE_SOUND_ENABLED) |
|---|
| 237 | [[SapphireFrontRowCompat sharedFrontRowPreferences] setBool:YES forKey:@"PlayFrontRowSounds"]; |
|---|
| 238 | |
|---|
| 239 | } |
|---|
| 240 | |
|---|
| 241 | - (void)setPlayFile:(SapphireFileMetaData *)file |
|---|
| 242 | { |
|---|
| 243 | currentPlayFile = [file retain]; |
|---|
| 244 | soundState = enablePassthrough(currentPlayFile); |
|---|
| 245 | } |
|---|
| 246 | |
|---|
| 247 | - (void)teardownPlayback |
|---|
| 248 | { |
|---|
| 249 | teardownPassthrough(soundState); |
|---|
| 250 | /*resume time*/ |
|---|
| 251 | BRVideoPlayer *player = [self player]; |
|---|
| 252 | float elapsed = [player elapsedPlaybackTime]; |
|---|
| 253 | float duration = [player trackDuration]; |
|---|
| 254 | if(duration == 0.0f) |
|---|
| 255 | elapsed = duration = 1.0f; |
|---|
| 256 | |
|---|
| 257 | /*Get the resume time to save*/ |
|---|
| 258 | if(elapsed < duration - 2) |
|---|
| 259 | [currentPlayFile setResumeTimeValue:elapsed]; |
|---|
| 260 | else |
|---|
| 261 | [currentPlayFile setResumeTime:nil]; |
|---|
| 262 | |
|---|
| 263 | if(elapsed / duration > 0.9f) |
|---|
| 264 | /*Mark as watched and reload info*/ |
|---|
| 265 | [currentPlayFile setWatchedValue:YES]; |
|---|
| 266 | [SapphireMetaDataSupport save:[currentPlayFile managedObjectContext]]; |
|---|
| 267 | } |
|---|
| 268 | |
|---|
| 269 | - (void)willBePopped |
|---|
| 270 | { |
|---|
| 271 | [super willBePopped]; |
|---|
| 272 | [self teardownPlayback]; |
|---|
| 273 | } |
|---|
| 274 | |
|---|
| 275 | - (void)wasPopped |
|---|
| 276 | { |
|---|
| 277 | [super wasPopped]; |
|---|
| 278 | [self teardownPlayback]; |
|---|
| 279 | } |
|---|
| 280 | |
|---|
| 281 | @end |
|---|