Index: dvdprobe.cpp
===================================================================
--- dvdprobe.cpp	(revision 9740)
+++ dvdprobe.cpp	(working copy)
@@ -7,18 +7,29 @@
     implementation for dvd probing (libdvdread)
 */
 
+#include <mythtv/mythcontext.h>
+#include <mythtv/mythdbcon.h>
+
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/ioctl.h>
+#ifdef Q_WS_MACX
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreServices/CoreServices.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/storage/IOMedia.h>
+#include <IOKit/storage/IOCDMedia.h>
+#include <IOKit/storage/IODVDMedia.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#else
 #include <linux/cdrom.h>
+#endif // __macintosh__
 #include <fcntl.h>
 #include <unistd.h>
 #include "dvdprobe.h"
 
-#include <mythtv/mythcontext.h>
-#include <mythtv/mythdbcon.h>
-
 DVDSubTitle::DVDSubTitle(int subtitle_id, const QString &a_language)
 {
     id = subtitle_id; 
@@ -453,6 +464,249 @@
     volume_name = QObject::tr("Unknown");
 }
 
+#ifdef Q_WS_MACX
+static mach_port_t			sMasterPort;
+
+Boolean IsDVDMedia(io_service_t service)
+{
+    //
+    // Determine if the object passed in represents an IOMedia (or subclass) object.
+    // If it does, retrieve the "Whole" property.
+    // If this is the whole media object, find out if it is a CD, DVD, or something else.
+    // If it isn't the whole media object, iterate across its parents in the IORegistry
+    // until the whole media object is found.
+    //
+    // Note that media types other than CD and DVD are not distinguished by class name
+    // but are generic IOMedia objects.
+    //
+    
+    Boolean 		isDVDMedia = false;
+    Boolean			isWholeMedia = false;
+    io_name_t		className;
+    kern_return_t	kernResult;
+
+    if (IOObjectConformsTo(service, kIOMediaClass)) {
+        
+        CFTypeRef wholeMedia;
+        
+        wholeMedia = IORegistryEntryCreateCFProperty(service, 
+                                                     CFSTR(kIOMediaWholeKey), 
+                                                     kCFAllocatorDefault, 
+                                                     0);
+                                                    
+        if (NULL == wholeMedia) {
+            fprintf(stderr, "IsDVDMedia - Could not retrieve Whole property\n");
+        }
+        else {                                        
+            isWholeMedia = CFBooleanGetValue((CFBooleanRef)wholeMedia);
+            CFRelease(wholeMedia);
+        }
+    }
+            
+    if (isWholeMedia) {
+        if (IOObjectConformsTo(service, kIOCDMediaClass)) {
+            isDVDMedia = false;
+        }
+        else if (IOObjectConformsTo(service, kIODVDMediaClass)) {
+            isDVDMedia = true;
+        }
+        else {
+            kernResult = IOObjectGetClass(service, className);
+            isDVDMedia = false;
+        }            
+    }
+
+    return isDVDMedia;
+}
+
+Boolean FindDVDMedia(io_service_t service)
+{
+    kern_return_t	kernResult;
+    io_iterator_t	iter;
+    Boolean isDVDMedia = false;
+        
+    
+    // Create an iterator across all parents of the service object passed in.
+    kernResult = IORegistryEntryCreateIterator(service,
+                                               kIOServicePlane,
+                                               kIORegistryIterateRecursively | kIORegistryIterateParents,
+                                               &iter);
+    
+    if (KERN_SUCCESS != kernResult) {
+        fprintf(stderr, "FindDVDMedia - IORegistryEntryCreateIterator returned %d\n", kernResult);
+    }
+    else if (NULL == iter) {
+        fprintf(stderr, "FindDVDMedia - IORegistryEntryCreateIterator returned a NULL iterator\n");
+    }
+    else {
+        // A reference on the initial service object is released in the do-while loop below,
+        // so add a reference to balance 
+        IOObjectRetain(service);	
+        
+        do {
+            isDVDMedia = IsDVDMedia(service);
+            IOObjectRelease(service);
+        } while ((service = IOIteratorNext(iter)) && !isDVDMedia);
+                
+        IOObjectRelease(iter);
+    }
+    return isDVDMedia;
+}
+
+Boolean IsDVDMediaForBSDName(const char *bsdName)
+{
+    // The idea is that given the BSD node name corresponding to a volume,
+    // I/O Kit can be used to find the information about the media, drive, bus, and so on
+    // that is maintained in the IORegistry.
+    //
+    // In this sample, we find out if the volume is on a CD, DVD, or some other media.
+    // This is done as follows:
+    // 
+    // 1. 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, (a floppy disk, for example) 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".
+    //
+    // 2. Determine which I/O Kit class the whole media object belongs to.
+    //
+    // For CD media the class name will be "IOCDMedia," and for DVD media the class name will be
+    // "IODVDMedia". Other media will be of the generic "IOMedia" class.
+    //
+    
+    CFMutableDictionaryRef	matchingDict;
+    kern_return_t		kernResult;
+    io_iterator_t 		iter;
+    io_service_t		service;
+    Boolean isDVD = false;
+    
+    matchingDict = IOBSDNameMatching(sMasterPort, 0, bsdName);
+    if (NULL == matchingDict) {
+        fprintf(stderr, "IsDVDMediaForName(%s) - IOBSDNameMatching returned a NULL dictionary.\n", bsdName);
+    }
+    else {
+        // Return an iterator across all objects with the matching BSD node name. Note that there
+        // should only be one match!
+        kernResult = IOServiceGetMatchingServices(sMasterPort, matchingDict, &iter);    
+    
+        if (KERN_SUCCESS != kernResult) {
+            fprintf(stderr, "IsDVDMediaForName - IOServiceGetMatchingServices returned %d\n", kernResult);
+        }
+        else if (NULL == iter) {
+            printf("IOServiceGetMatchingServices returned a NULL iterator\n");
+        }
+        else {
+            service = IOIteratorNext(iter);
+            
+            // Release this now because we only expect the iterator to contain
+            // a single io_service_t.
+            IOObjectRelease(iter);
+            
+            if (NULL == service) {
+                fprintf(stderr, "IsDVDMediaForName - IOIteratorNext returned NULL\n");
+            }
+            else {
+                isDVD = FindDVDMedia(service);
+                IOObjectRelease(service);
+            }
+        }
+    }
+    return isDVD;
+}
+
+QString GetDVDVolumeName()
+{
+    kern_return_t		kernResult; 
+    OSErr			result = noErr;
+    ItemCount			volumeIndex;
+
+    if (!sMasterPort)
+    {
+	    kernResult = IOMasterPort(MACH_PORT_NULL, &sMasterPort);
+	    if (KERN_SUCCESS != kernResult)
+	        fprintf(stderr, "GetDVDVolumeName - IOMasterPort returned %d\n", kernResult);
+    }
+    
+    // Iterate across all mounted volumes using FSGetVolumeInfo. This will return nsvErr
+    // (no such volume) when volumeIndex becomes greater than the number of mounted volumes.
+    for (volumeIndex = 1; result == noErr || result != nsvErr; volumeIndex++)
+    {
+        FSVolumeRefNum	actualVolume;
+        HFSUniStr255	volumeName;
+        FSVolumeInfo	volumeInfo;
+        
+        bzero((void *) &volumeInfo, sizeof(volumeInfo));
+        
+        // We're mostly interested in the volume reference number (actualVolume)
+        result = FSGetVolumeInfo(kFSInvalidVolumeRefNum,
+                                 volumeIndex,
+                                 &actualVolume,
+                                 kFSVolInfoFSInfo,
+                                 &volumeInfo,
+                                 &volumeName,
+                                 NULL); 
+        
+        if (result == noErr)
+        {
+            GetVolParmsInfoBuffer volumeParms;
+            HParamBlockRec pb;
+            
+            // Use the volume reference number to retrieve the volume parameters. See the documentation
+            // on PBHGetVolParmsSync for other possible ways to specify a volume.
+            pb.ioParam.ioNamePtr = NULL;
+            pb.ioParam.ioVRefNum = actualVolume;
+            pb.ioParam.ioBuffer = (Ptr) &volumeParms;
+            pb.ioParam.ioReqCount = sizeof(volumeParms);
+            
+            // 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.
+            result = PBHGetVolParmsSync(&pb);
+            
+            if (result != noErr)
+            {
+                fprintf(stderr, "GetDVDVolumeName - PBHGetVolParmsSync returned %d\n", result);
+            }
+            else {
+                // This code is just to convert the volume name from a HFSUniCharStr to
+                // a plain C string so we can print it with printf. It'd be preferable to
+                // use CoreFoundation to work with the volume name in its Unicode form.
+                CFStringRef	volNameAsCFString;
+                char		volNameAsCString[256];
+                
+                volNameAsCFString = CFStringCreateWithCharacters(kCFAllocatorDefault,
+                                                                 volumeName.unicode,
+                                                                 volumeName.length);
+                                                                 
+                // If the conversion to a C string fails, just treat it as a null string.
+                if (!CFStringGetCString(volNameAsCFString,
+                                        volNameAsCString,
+                                        sizeof(volNameAsCString),
+                                        kCFStringEncodingUTF8))
+                {
+                    volNameAsCString[0] = 0;
+                }
+                
+                // The last parameter of this printf call is the BSD node name from the
+                // GetVolParmsInfoBuffer struct.
+//                printf("Volume \"%s\" (vRefNum %d), BSD node /dev/%s, ", 
+//                        volNameAsCString, actualVolume, (char *) volumeParms.vMDeviceID);
+                        
+                // Use the BSD node name to call I/O Kit and get additional information about the volume
+                if (IsDVDMediaForBSDName((char *) volumeParms.vMDeviceID))
+                {
+                	return volNameAsCString;
+                }
+            }
+        }
+    }
+    return QString();
+}
+
+#endif // Q_WX_MACX
+
 bool DVDProbe::probe()
 {
     //
@@ -474,7 +728,28 @@
         return false;
     }
 
+#ifdef Q_WS_MACX
+	static QString currDVDVolName;
+	QString newDVDVolName;
+	
+	newDVDVolName = GetDVDVolumeName();		
+
+	if (currDVDVolName && (newDVDVolName.compare(currDVDVolName) == 0))
+	{
+		return (titles.count() > 0);
+	}	
+	currDVDVolName = newDVDVolName;
+	
     //
+    //  Try to open the disc
+    //  (as far libdvdread is concerned, the argument
+    //  could be a path, file, whatever).
+    //
+    
+    wipeClean();
+	dvd = DVDOpen(device);
+#else
+    //
     //  I have no idea if the following code block
     //  is anywhere close to the "right way" of doing
     //  this, but it seems to work.
@@ -512,6 +787,7 @@
     }
 
     status = ioctl(drive_handle, CDROM_MEDIA_CHANGED, NULL);
+
     close(drive_handle);
 
     if(!status)
@@ -547,6 +823,7 @@
     wipeClean();
     first_time = false;
     dvd = DVDOpen(device);
+#endif // Q_WS_MACX
     if(dvd)
     {
         //
@@ -557,6 +834,9 @@
         char volume_name_buffr[arbitrary + 1];
         unsigned char set_name[arbitrary + 1];
         
+#ifdef Q_WS_MACX
+		volume_name = currDVDVolName.section('/', 2);
+#else
         if(DVDUDFVolumeInfo(dvd, volume_name_buffr, arbitrary, set_name, arbitrary) > -1)
         {
             volume_name = volume_name_buffr;
@@ -566,6 +846,7 @@
             VERBOSE(VB_IMPORTANT, "Error getting volume name, setting to"
                     "\"Unknown\"");
         }
+#endif // Q_WS_MACX
         
         ifo_handle_t *ifo_file = ifoOpen(dvd, 0);
         if(ifo_file)
@@ -780,7 +1061,6 @@
                 //
                 //  Add this new title to the container
                 //
-
                 titles.append(new_title);
 
             }
