5 * This file is part of the HandBrake source code.
6 * Homepage: <http://handbrake.fr/>.
7 * It may be used under the terms of the GNU General Public License.
10 #include <IOKit/IOKitLib.h>
11 #include <IOKit/storage/IOMedia.h>
12 #include <IOKit/storage/IODVDMedia.h>
14 #import "HBDVDDetector.h"
17 @interface HBDVDDetector (Private)
19 - (NSString *)bsdNameForPath;
20 - (BOOL)pathHasVideoTS;
22 - (io_service_t)getIOKitServiceForBSDName;
23 - (BOOL)isDVDService: (io_service_t)service;
24 - (BOOL)isWholeMediaService: (io_service_t)service;
29 @implementation HBDVDDetector
31 + (HBDVDDetector *)detectorForPath: (NSString *)aPath
33 return [[[self alloc] initWithPath:aPath] autorelease];
37 - (HBDVDDetector *)initWithPath: (NSString *)aPath
39 NSAssert(aPath, @"nil string passed to drive detector.");
40 if( self = [super init] )
42 path = [aPath retain];
63 bsdName = [[self bsdNameForPath] retain];
65 return ( [self pathHasVideoTS] && [self deviceIsDVD] );
69 - (NSString *)devicePath
73 bsdName = [[self bsdNameForPath] retain];
75 return [NSString stringWithFormat:@"/dev/%@", bsdName];
81 @implementation HBDVDDetector (Private)
83 - (NSString *)bsdNameForPath
87 err = FSPathMakeRef( (const UInt8 *) [path fileSystemRepresentation],
94 // Get the volume reference number.
95 FSCatalogInfo catalogInfo;
96 err = FSGetCatalogInfo( &ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL,
102 FSVolumeRefNum volRefNum = catalogInfo.volume;
104 // Now let's get the device name
105 GetVolParmsInfoBuffer volumeParms;
106 err = FSGetVolumeParms ( volRefNum, &volumeParms, sizeof( volumeParms ) );
113 // A version 4 GetVolParmsInfoBuffer contains the BSD node name in the vMDeviceID field.
114 // It is actually a char * value. This is mentioned in the header CoreServices/CarbonCore/Files.h.
115 return [NSString stringWithCString:(char *)volumeParms.vMDeviceID];
119 - (BOOL)pathHasVideoTS
121 // Check one level under the path
122 if( [[NSFileManager defaultManager] fileExistsAtPath:
123 [path stringByAppendingPathComponent:@"VIDEO_TS"]] )
128 // Now check above the path
129 return [[path pathComponents] containsObject:@"VIDEO_TS"];
135 io_service_t service = [self getIOKitServiceForBSDName];
136 if( service == IO_OBJECT_NULL )
140 BOOL result = [self isDVDService:service];
141 IOObjectRelease(service);
146 - (io_service_t)getIOKitServiceForBSDName
148 CFMutableDictionaryRef matchingDict;
149 matchingDict = IOBSDNameMatching( kIOMasterPortDefault, 0, [bsdName UTF8String] );
150 if( matchingDict == NULL )
152 return IO_OBJECT_NULL;
155 // Fetch the object with the matching BSD node name. There should only be
156 // one match, so IOServiceGetMatchingService is used instead of
157 // IOServiceGetMatchingServices to simplify the code.
158 return IOServiceGetMatchingService( kIOMasterPortDefault, matchingDict );
162 - (BOOL)isDVDService: (io_service_t)service
164 // Find the IOMedia object that represents the entire (whole) media that the
167 // If the volume is on partitioned media, the whole media object will be a
168 // parent of the volume's media object. If the media is not partitioned, the
169 // volume's media object will be the whole media object.
171 // The whole media object is indicated in the IORegistry by the presence of
172 // a property with the key "Whole" and value "Yes".
174 // Create an iterator across all parents of the service object passed in.
175 kern_return_t kernResult;
177 kernResult = IORegistryEntryCreateIterator( service,
179 kIORegistryIterateRecursively | kIORegistryIterateParents,
181 if( kernResult != KERN_SUCCESS )
185 if( iter == IO_OBJECT_NULL )
191 // A reference on the initial service object is released in the do-while loop below,
192 // so add a reference to balance.
193 IOObjectRetain( service );
198 isDVD = ( [self isWholeMediaService:service] &&
199 IOObjectConformsTo(service, kIODVDMediaClass) );
200 IOObjectRelease(service);
201 } while( !isDVD && (service = IOIteratorNext(iter)) );
202 IOObjectRelease( iter );
208 - (BOOL)isWholeMediaService: (io_service_t)service
211 // Determine if the object passed in represents an IOMedia (or subclass) object.
212 // If it does, test the "Whole" property.
215 Boolean isWholeMedia = NO;
217 if( IOObjectConformsTo(service, kIOMediaClass) )
219 CFTypeRef wholeMedia;
220 wholeMedia = IORegistryEntryCreateCFProperty( service,
221 CFSTR(kIOMediaWholeKey),
228 isWholeMedia = CFBooleanGetValue( (CFBooleanRef)wholeMedia );
229 CFRelease(wholeMedia);