diff options
Diffstat (limited to 'macosx/HBDriveDetector.m')
-rw-r--r-- | macosx/HBDriveDetector.m | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/macosx/HBDriveDetector.m b/macosx/HBDriveDetector.m new file mode 100644 index 000000000..2b57237d9 --- /dev/null +++ b/macosx/HBDriveDetector.m @@ -0,0 +1,240 @@ +/** + * HBDriveDetector.m + * 8/17/2007 + * + * This file is part of the HandBrake source code. + * Homepage: <http://handbrake.m0k.org/>. + * It may be used under the terms of the GNU General Public License. + */ + +#include <IOKit/IOKitLib.h> +#include <IOKit/storage/IOMedia.h> +#include <IOKit/storage/IODVDMedia.h> + +#import "HBDVDDetector.h" + + +@interface HBDVDDetector (Private) + +- (NSString *)bsdNameForPath; +- (BOOL)pathHasVideoTS; +- (BOOL)deviceIsDVD; +- (io_service_t)getIOKitServiceForBSDName; +- (BOOL)isDVDService: (io_service_t)service; +- (BOOL)isWholeMediaService: (io_service_t)service; + +@end + + +@implementation HBDVDDetector + ++ (HBDVDDetector *)detectorForPath: (NSString *)aPath +{ + return [[[self alloc] initWithPath:aPath] autorelease]; +} + + +- (HBDVDDetector *)initWithPath: (NSString *)aPath +{ + NSAssert(aPath, @"nil string passed to drive detector."); + if( self = [super init] ) + { + path = [aPath retain]; + bsdName = nil; + } + return self; +} + + +- (void)dealloc +{ + [path release]; + path = nil; + [bsdName release]; + bsdName = nil; + [super dealloc]; +} + + +- (BOOL)isVideoDVD +{ + if( !bsdName ) + { + bsdName = [[self bsdNameForPath] retain]; + } + return ( [self pathHasVideoTS] && [self deviceIsDVD] ); +} + + +- (NSString *)devicePath +{ + if( !bsdName ) + { + bsdName = [[self bsdNameForPath] retain]; + } + return [NSString stringWithFormat:@"/dev/%@", bsdName]; +} + +@end + + +@implementation HBDVDDetector (Private) + +- (NSString *)bsdNameForPath +{ + OSStatus err; + FSRef ref; + err = FSPathMakeRef( (const UInt8 *) [path fileSystemRepresentation], + &ref, NULL ); + if( err != noErr ) + { + return nil; + } + + // Get the volume reference number. + FSCatalogInfo catalogInfo; + err = FSGetCatalogInfo( &ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL, + NULL); + if( err != noErr ) + { + return nil; + } + FSVolumeRefNum volRefNum = catalogInfo.volume; + + // Mow let's get the device name + GetVolParmsInfoBuffer volumeParms; + HParamBlockRec pb; + pb.ioParam.ioNamePtr = NULL; + pb.ioParam.ioVRefNum = volRefNum; + pb.ioParam.ioBuffer = (Ptr) &volumeParms; + pb.ioParam.ioReqCount = sizeof( volumeParms ); + err = PBHGetVolParmsSync( &pb ); + if( err != noErr ) + { + return nil; + } + + // A version 4 GetVolParmsInfoBuffer contains the BSD node name in the vMDeviceID field. + // It is actually a char * value. This is mentioned in the header CoreServices/CarbonCore/Files.h. + return [NSString stringWithCString:(char *)volumeParms.vMDeviceID]; +} + + +- (BOOL)pathHasVideoTS +{ + // Check one level under the path + if( [[NSFileManager defaultManager] fileExistsAtPath: + [path stringByAppendingPathComponent:@"VIDEO_TS"]] ) + { + return YES; + } + + // Now check above the path + return [[path pathComponents] containsObject:@"VIDEO_TS"]; +} + + +- (BOOL)deviceIsDVD +{ + io_service_t service = [self getIOKitServiceForBSDName]; + if( service == IO_OBJECT_NULL ) + { + return NO; + } + BOOL result = [self isDVDService:service]; + IOObjectRelease(service); + return result; +} + + +- (io_service_t)getIOKitServiceForBSDName +{ + CFMutableDictionaryRef matchingDict; + matchingDict = IOBSDNameMatching( kIOMasterPortDefault, 0, [bsdName cString] ); + if( matchingDict == NULL ) + { + return IO_OBJECT_NULL; + } + + // Fetch the object with the matching BSD node name. There should only be + // one match, so IOServiceGetMatchingService is used instead of + // IOServiceGetMatchingServices to simplify the code. + return IOServiceGetMatchingService( kIOMasterPortDefault, matchingDict ); +} + + +- (BOOL)isDVDService: (io_service_t)service +{ + // Find the IOMedia object that represents the entire (whole) media that the + // volume is on. + // + // If the volume is on partitioned media, the whole media object will be a + // parent of the volume's media object. If the media is not partitioned, the + // volume's media object will be the whole media object. + // + // The whole media object is indicated in the IORegistry by the presence of + // a property with the key "Whole" and value "Yes". + + // Create an iterator across all parents of the service object passed in. + kern_return_t kernResult; + io_iterator_t iter; + kernResult = IORegistryEntryCreateIterator( service, + kIOServicePlane, + kIORegistryIterateRecursively | kIORegistryIterateParents, + &iter ); + if( kernResult != KERN_SUCCESS ) + { + return NO; + } + if( iter == IO_OBJECT_NULL ) + { + return NO; + } + + + // A reference on the initial service object is released in the do-while loop below, + // so add a reference to balance. + IOObjectRetain( service ); + + BOOL isDVD = NO; + do + { + isDVD = ( [self isWholeMediaService:service] && + IOObjectConformsTo(service, kIODVDMediaClass) ); + IOObjectRelease(service); + } while( !isDVD && (service = IOIteratorNext(iter)) ); + IOObjectRelease( iter ); + + return isDVD; +} + + +- (BOOL)isWholeMediaService: (io_service_t)service +{ + // + // Determine if the object passed in represents an IOMedia (or subclass) object. + // If it does, test the "Whole" property. + // + + Boolean isWholeMedia = NO; + + if( IOObjectConformsTo(service, kIOMediaClass) ) + { + CFTypeRef wholeMedia; + wholeMedia = IORegistryEntryCreateCFProperty( service, + CFSTR(kIOMediaWholeKey), + kCFAllocatorDefault, + 0); + if( !wholeMedia ) + { + return NO; + } + isWholeMedia = CFBooleanGetValue( (CFBooleanRef)wholeMedia ); + CFRelease(wholeMedia); + } + + return isWholeMedia; +} + + +@end
\ No newline at end of file |