Ticket #1737: dvdprobe.cpp.patch

File dvdprobe.cpp.patch, 12.4 KB (added by awk@…, 14 years ago)

Patch to mythdvd/mtd/dvdprobe.cpp supports DVD media detection and changes.

  • dvdprobe.cpp

     
    77    implementation for dvd probing (libdvdread)
    88*/
    99
     10#include <mythtv/mythcontext.h>
     11#include <mythtv/mythdbcon.h>
     12
    1013#include <stdio.h>
    1114#include <sys/types.h>
    1215#include <sys/stat.h>
    1316#include <sys/ioctl.h>
     17#ifdef Q_WS_MACX
     18#include <CoreFoundation/CoreFoundation.h>
     19#include <CoreServices/CoreServices.h>
     20#include <IOKit/IOKitLib.h>
     21#include <IOKit/storage/IOMedia.h>
     22#include <IOKit/storage/IOCDMedia.h>
     23#include <IOKit/storage/IODVDMedia.h>
     24#include <sys/param.h>
     25#include <sys/mount.h>
     26#else
    1427#include <linux/cdrom.h>
     28#endif // __macintosh__
    1529#include <fcntl.h>
    1630#include <unistd.h>
    1731#include "dvdprobe.h"
    1832
    19 #include <mythtv/mythcontext.h>
    20 #include <mythtv/mythdbcon.h>
    21 
    2233DVDSubTitle::DVDSubTitle(int subtitle_id, const QString &a_language)
    2334{
    2435    id = subtitle_id;
     
    453464    volume_name = QObject::tr("Unknown");
    454465}
    455466
     467#ifdef Q_WS_MACX
     468static mach_port_t                      sMasterPort;
     469
     470Boolean IsDVDMedia(io_service_t service)
     471{
     472    //
     473    // Determine if the object passed in represents an IOMedia (or subclass) object.
     474    // If it does, retrieve the "Whole" property.
     475    // If this is the whole media object, find out if it is a CD, DVD, or something else.
     476    // If it isn't the whole media object, iterate across its parents in the IORegistry
     477    // until the whole media object is found.
     478    //
     479    // Note that media types other than CD and DVD are not distinguished by class name
     480    // but are generic IOMedia objects.
     481    //
     482   
     483    Boolean             isDVDMedia = false;
     484    Boolean                     isWholeMedia = false;
     485    io_name_t           className;
     486    kern_return_t       kernResult;
     487
     488    if (IOObjectConformsTo(service, kIOMediaClass)) {
     489       
     490        CFTypeRef wholeMedia;
     491       
     492        wholeMedia = IORegistryEntryCreateCFProperty(service,
     493                                                     CFSTR(kIOMediaWholeKey),
     494                                                     kCFAllocatorDefault,
     495                                                     0);
     496                                                   
     497        if (NULL == wholeMedia) {
     498            fprintf(stderr, "IsDVDMedia - Could not retrieve Whole property\n");
     499        }
     500        else {                                       
     501            isWholeMedia = CFBooleanGetValue((CFBooleanRef)wholeMedia);
     502            CFRelease(wholeMedia);
     503        }
     504    }
     505           
     506    if (isWholeMedia) {
     507        if (IOObjectConformsTo(service, kIOCDMediaClass)) {
     508            isDVDMedia = false;
     509        }
     510        else if (IOObjectConformsTo(service, kIODVDMediaClass)) {
     511            isDVDMedia = true;
     512        }
     513        else {
     514            kernResult = IOObjectGetClass(service, className);
     515            isDVDMedia = false;
     516        }           
     517    }
     518
     519    return isDVDMedia;
     520}
     521
     522Boolean FindDVDMedia(io_service_t service)
     523{
     524    kern_return_t       kernResult;
     525    io_iterator_t       iter;
     526    Boolean isDVDMedia = false;
     527       
     528   
     529    // Create an iterator across all parents of the service object passed in.
     530    kernResult = IORegistryEntryCreateIterator(service,
     531                                               kIOServicePlane,
     532                                               kIORegistryIterateRecursively | kIORegistryIterateParents,
     533                                               &iter);
     534   
     535    if (KERN_SUCCESS != kernResult) {
     536        fprintf(stderr, "FindDVDMedia - IORegistryEntryCreateIterator returned %d\n", kernResult);
     537    }
     538    else if (NULL == iter) {
     539        fprintf(stderr, "FindDVDMedia - IORegistryEntryCreateIterator returned a NULL iterator\n");
     540    }
     541    else {
     542        // A reference on the initial service object is released in the do-while loop below,
     543        // so add a reference to balance
     544        IOObjectRetain(service);       
     545       
     546        do {
     547            isDVDMedia = IsDVDMedia(service);
     548            IOObjectRelease(service);
     549        } while ((service = IOIteratorNext(iter)) && !isDVDMedia);
     550               
     551        IOObjectRelease(iter);
     552    }
     553    return isDVDMedia;
     554}
     555
     556Boolean IsDVDMediaForBSDName(const char *bsdName)
     557{
     558    // The idea is that given the BSD node name corresponding to a volume,
     559    // I/O Kit can be used to find the information about the media, drive, bus, and so on
     560    // that is maintained in the IORegistry.
     561    //
     562    // In this sample, we find out if the volume is on a CD, DVD, or some other media.
     563    // This is done as follows:
     564    //
     565    // 1. Find the IOMedia object that represents the entire (whole) media that the volume is on.
     566    //
     567    // If the volume is on partitioned media, the whole media object will be a parent of the volume's
     568    // media object. If the media is not partitioned, (a floppy disk, for example) the volume's media
     569    // object will be the whole media object.
     570    //
     571    // The whole media object is indicated in the IORegistry by the presence of a property with the key
     572    // "Whole" and value "Yes".
     573    //
     574    // 2. Determine which I/O Kit class the whole media object belongs to.
     575    //
     576    // For CD media the class name will be "IOCDMedia," and for DVD media the class name will be
     577    // "IODVDMedia". Other media will be of the generic "IOMedia" class.
     578    //
     579   
     580    CFMutableDictionaryRef      matchingDict;
     581    kern_return_t               kernResult;
     582    io_iterator_t               iter;
     583    io_service_t                service;
     584    Boolean isDVD = false;
     585   
     586    matchingDict = IOBSDNameMatching(sMasterPort, 0, bsdName);
     587    if (NULL == matchingDict) {
     588        fprintf(stderr, "IsDVDMediaForName(%s) - IOBSDNameMatching returned a NULL dictionary.\n", bsdName);
     589    }
     590    else {
     591        // Return an iterator across all objects with the matching BSD node name. Note that there
     592        // should only be one match!
     593        kernResult = IOServiceGetMatchingServices(sMasterPort, matchingDict, &iter);   
     594   
     595        if (KERN_SUCCESS != kernResult) {
     596            fprintf(stderr, "IsDVDMediaForName - IOServiceGetMatchingServices returned %d\n", kernResult);
     597        }
     598        else if (NULL == iter) {
     599            printf("IOServiceGetMatchingServices returned a NULL iterator\n");
     600        }
     601        else {
     602            service = IOIteratorNext(iter);
     603           
     604            // Release this now because we only expect the iterator to contain
     605            // a single io_service_t.
     606            IOObjectRelease(iter);
     607           
     608            if (NULL == service) {
     609                fprintf(stderr, "IsDVDMediaForName - IOIteratorNext returned NULL\n");
     610            }
     611            else {
     612                isDVD = FindDVDMedia(service);
     613                IOObjectRelease(service);
     614            }
     615        }
     616    }
     617    return isDVD;
     618}
     619
     620QString GetDVDVolumeName()
     621{
     622    kern_return_t               kernResult;
     623    OSErr                       result = noErr;
     624    ItemCount                   volumeIndex;
     625
     626    if (!sMasterPort)
     627    {
     628            kernResult = IOMasterPort(MACH_PORT_NULL, &sMasterPort);
     629            if (KERN_SUCCESS != kernResult)
     630                fprintf(stderr, "GetDVDVolumeName - IOMasterPort returned %d\n", kernResult);
     631    }
     632   
     633    // Iterate across all mounted volumes using FSGetVolumeInfo. This will return nsvErr
     634    // (no such volume) when volumeIndex becomes greater than the number of mounted volumes.
     635    for (volumeIndex = 1; result == noErr || result != nsvErr; volumeIndex++)
     636    {
     637        FSVolumeRefNum  actualVolume;
     638        HFSUniStr255    volumeName;
     639        FSVolumeInfo    volumeInfo;
     640       
     641        bzero((void *) &volumeInfo, sizeof(volumeInfo));
     642       
     643        // We're mostly interested in the volume reference number (actualVolume)
     644        result = FSGetVolumeInfo(kFSInvalidVolumeRefNum,
     645                                 volumeIndex,
     646                                 &actualVolume,
     647                                 kFSVolInfoFSInfo,
     648                                 &volumeInfo,
     649                                 &volumeName,
     650                                 NULL);
     651       
     652        if (result == noErr)
     653        {
     654            GetVolParmsInfoBuffer volumeParms;
     655            HParamBlockRec pb;
     656           
     657            // Use the volume reference number to retrieve the volume parameters. See the documentation
     658            // on PBHGetVolParmsSync for other possible ways to specify a volume.
     659            pb.ioParam.ioNamePtr = NULL;
     660            pb.ioParam.ioVRefNum = actualVolume;
     661            pb.ioParam.ioBuffer = (Ptr) &volumeParms;
     662            pb.ioParam.ioReqCount = sizeof(volumeParms);
     663           
     664            // A version 4 GetVolParmsInfoBuffer contains the BSD node name in the vMDeviceID field.
     665            // It is actually a char * value. This is mentioned in the header CoreServices/CarbonCore/Files.h.
     666            result = PBHGetVolParmsSync(&pb);
     667           
     668            if (result != noErr)
     669            {
     670                fprintf(stderr, "GetDVDVolumeName - PBHGetVolParmsSync returned %d\n", result);
     671            }
     672            else {
     673                // This code is just to convert the volume name from a HFSUniCharStr to
     674                // a plain C string so we can print it with printf. It'd be preferable to
     675                // use CoreFoundation to work with the volume name in its Unicode form.
     676                CFStringRef     volNameAsCFString;
     677                char            volNameAsCString[256];
     678               
     679                volNameAsCFString = CFStringCreateWithCharacters(kCFAllocatorDefault,
     680                                                                 volumeName.unicode,
     681                                                                 volumeName.length);
     682                                                                 
     683                // If the conversion to a C string fails, just treat it as a null string.
     684                if (!CFStringGetCString(volNameAsCFString,
     685                                        volNameAsCString,
     686                                        sizeof(volNameAsCString),
     687                                        kCFStringEncodingUTF8))
     688                {
     689                    volNameAsCString[0] = 0;
     690                }
     691               
     692                // The last parameter of this printf call is the BSD node name from the
     693                // GetVolParmsInfoBuffer struct.
     694//                printf("Volume \"%s\" (vRefNum %d), BSD node /dev/%s, ",
     695//                        volNameAsCString, actualVolume, (char *) volumeParms.vMDeviceID);
     696                       
     697                // Use the BSD node name to call I/O Kit and get additional information about the volume
     698                if (IsDVDMediaForBSDName((char *) volumeParms.vMDeviceID))
     699                {
     700                        return volNameAsCString;
     701                }
     702            }
     703        }
     704    }
     705    return QString();
     706}
     707
     708#endif // Q_WX_MACX
     709
    456710bool DVDProbe::probe()
    457711{
    458712    //
     
    474728        return false;
    475729    }
    476730
     731#ifdef Q_WS_MACX
     732        static QString currDVDVolName;
     733        QString newDVDVolName;
     734       
     735        newDVDVolName = GetDVDVolumeName();             
     736
     737        if (currDVDVolName && (newDVDVolName.compare(currDVDVolName) == 0))
     738        {
     739                return (titles.count() > 0);
     740        }       
     741        currDVDVolName = newDVDVolName;
     742       
    477743    //
     744    //  Try to open the disc
     745    //  (as far libdvdread is concerned, the argument
     746    //  could be a path, file, whatever).
     747    //
     748   
     749    wipeClean();
     750        dvd = DVDOpen(device);
     751#else
     752    //
    478753    //  I have no idea if the following code block
    479754    //  is anywhere close to the "right way" of doing
    480755    //  this, but it seems to work.
     
    512787    }
    513788
    514789    status = ioctl(drive_handle, CDROM_MEDIA_CHANGED, NULL);
     790
    515791    close(drive_handle);
    516792
    517793    if(!status)
     
    547823    wipeClean();
    548824    first_time = false;
    549825    dvd = DVDOpen(device);
     826#endif // Q_WS_MACX
    550827    if(dvd)
    551828    {
    552829        //
     
    557834        char volume_name_buffr[arbitrary + 1];
    558835        unsigned char set_name[arbitrary + 1];
    559836       
     837#ifdef Q_WS_MACX
     838                volume_name = currDVDVolName.section('/', 2);
     839#else
    560840        if(DVDUDFVolumeInfo(dvd, volume_name_buffr, arbitrary, set_name, arbitrary) > -1)
    561841        {
    562842            volume_name = volume_name_buffr;
     
    566846            VERBOSE(VB_IMPORTANT, "Error getting volume name, setting to"
    567847                    "\"Unknown\"");
    568848        }
     849#endif // Q_WS_MACX
    569850       
    570851        ifo_handle_t *ifo_file = ifoOpen(dvd, 0);
    571852        if(ifo_file)
     
    7801061                //
    7811062                //  Add this new title to the container
    7821063                //
    783 
    7841064                titles.append(new_title);
    7851065
    7861066            }