Ticket #1753: patch.mediamon.2

File patch.mediamon.2, 14.4 KB (added by Nigel, 14 years ago)

Last version before I subclass MythediaMonitor?

Line 
1Index: libs/libmyth/mythmediamonitor.h
2===================================================================
3--- libs/libmyth/mythmediamonitor.h     (revision 9903)
4+++ libs/libmyth/mythmediamonitor.h     (working copy)
5@@ -8,6 +8,7 @@
6 #include <qthread.h>
7 #include <qstring.h>
8 
9+#include "mythconfig.h"
10 #include "mythmedia.h"
11 
12 const int kMediaEventType = 30042;
13@@ -37,10 +38,21 @@
14     MonitorThread( MediaMonitor* pMon,  unsigned long interval);
15     void setMonitor(MediaMonitor* pMon) { m_Monitor = pMon; }
16     virtual void run(void);
17+#ifdef CONFIG_DARWIN_DA
18+    bool  registerDiskArbitrationCallbacks();
19+    void  deregisterDiskArbitrationCallbacks();
20+    void  diskInsert(const char *devName,
21+                     const char *volName, void *disk, bool isCDorDVD = 1);
22+    void  diskRemove(QString devName);
23+#endif
24 
25   protected:
26     QGuardedPtr<MediaMonitor> m_Monitor;
27     unsigned long m_Interval;
28+#ifdef CONFIG_DARWIN_DA
29+    void *m_DiskArbitrationSession;   // Actually a DASessionRef, but the void*
30+                                      // lets us avoid Mac Headers in this file
31+#endif
32 };
33 
34 class MediaMonitor : public QObject
35Index: libs/libmyth/libmyth.pro
36===================================================================
37--- libs/libmyth/libmyth.pro    (revision 9903)
38+++ libs/libmyth/libmyth.pro    (working copy)
39@@ -80,6 +80,11 @@
40     # Mac OS X Frameworks
41     FWKS = ApplicationServices AudioUnit Carbon CoreAudio IOKit
42 
43+    # Disk Arbitration code is, sadly, conditional. Only 10.4 (& later?) has it
44+    darwin_da: HEADERS += mythmedia-darwin.h
45+    darwin_da: SOURCES += mythmedia-darwin.cpp
46+    darwin_da: FWKS    += DiskArbitration
47+
48     # The following trick is tidier, and shortens the command line, but it
49     # depends on the shell expanding Csh-style braces. Luckily, Bash & Zsh do.
50     FC = $$join(FWKS,",","{","}")
51Index: libs/libmyth/mythmediamonitor.cpp
52===================================================================
53--- libs/libmyth/mythmediamonitor.cpp   (revision 9903)
54+++ libs/libmyth/mythmediamonitor.cpp   (working copy)
55@@ -26,6 +26,10 @@
56 #include <qfile.h>
57 
58 // MythTV headers
59+#include "mythconfig.h"
60+#ifdef CONFIG_DARWIN_DA
61+#include "mythmedia-darwin.h"
62+#endif
63 #include "mythmediamonitor.h"
64 #include "mythcontext.h"
65 #include "mythdialogs.h"
66@@ -69,11 +73,28 @@
67 // loop and check it's devices.
68 void MonitorThread::run(void)
69 {
70+#ifdef CONFIG_DARWIN_DA
71+    if (!registerDiskArbitrationCallbacks())
72+        return;
73+#endif
74+
75     while (m_Monitor && m_Monitor->IsActive())       
76     {
77         m_Monitor->CheckDevices();
78+
79+#ifdef CONFIG_DARWIN_DA
80+        // Run the run loop for interval (milliseconds) - this will
81+        // handle any disk arbitration appeared/dissappeared events
82+        CFRunLoopRunInMode(kCFRunLoopDefaultMode,
83+                           (float) m_Interval / 1000.0f, false );
84+#else
85         msleep(m_Interval);
86+#endif
87     }
88+
89+#ifdef CONFIG_DARWIN_DA
90+    deregisterDiskArbitrationCallbacks();
91+#endif
92 }
93 
94 ////////////////////////////////////////////////////////////////////////
95Index: libs/libmyth/mythmedia-darwin.h
96===================================================================
97--- libs/libmyth/mythmedia-darwin.h     (revision 0)
98+++ libs/libmyth/mythmedia-darwin.h     (revision 0)
99@@ -0,0 +1,9 @@
100+#ifndef MYTH_MEDIA_DARWIN_H
101+#define MYTH_MEDIA_DARWIN_H
102+
103+#include <DiskArbitration/DiskArbitration.h>
104+
105+extern "C" bool  registerDiskArbitrationCallbacks();
106+extern "C" void  deregisterDiskArbitrationCallbacks();
107+
108+#endif // MYTH_MEDIA_DARWIN_H
109
110Property changes on: libs/libmyth/mythmedia-darwin.h
111___________________________________________________________________
112Name: svn:keywords
113   + Date Revision Author
114Name: svn:eol-style
115   + native
116
117Index: libs/libmyth/mythmedia-darwin.cpp
118===================================================================
119--- libs/libmyth/mythmedia-darwin.cpp   (revision 0)
120+++ libs/libmyth/mythmedia-darwin.cpp   (revision 0)
121@@ -0,0 +1,333 @@
122+/******************************************************************************
123+ * = NAME
124+ * mythmedia-darwin.cpp
125+ *
126+ * = DESCRIPTION
127+ * Helper functions for implementing the MythMediaMonitor on Darwin/Mac OS X.
128+ *
129+ * = BUGS
130+ * Verbose logging should probably have its own flag, but we are close to
131+ * running out of bits in the VB_ masks, so VB_UPNP will have to do for now
132+ *
133+ * = REVISION
134+ * $Id$
135+ *
136+ * = AUTHORS
137+ * Andrew Kimpton, Nigel Pearson
138+ *****************************************************************************/
139+
140+#include "mythconfig.h"
141+#include "mythcontext.h"
142+#include "mythmedia-darwin.h"
143+#include "mythmediamonitor.h"
144+#include "mythcdrom.h"
145+#include "mythhdd.h"
146+
147+#include <IOKit/IOKitLib.h>
148+#include <IOKit/storage/IOMedia.h>
149+#include <IOKit/storage/IOCDMedia.h>
150+#include <IOKit/storage/IODVDMedia.h>
151+
152+static mach_port_t    sMasterPort;
153+
154+MediaType FindMediaType(io_service_t service)
155+{
156+    kern_return_t  kernResult;
157+    io_iterator_t  iter;
158+    MediaType      mediaType = MEDIATYPE_UNKNOWN;
159+    bool           isWholeMedia = false;
160+
161+    // Create an iterator across all parents of the service object passed in.
162+    kernResult = IORegistryEntryCreateIterator(service,
163+                                               kIOServicePlane,
164+                                               kIORegistryIterateRecursively
165+                                               | kIORegistryIterateParents,
166+                                               &iter);
167+
168+    if (KERN_SUCCESS != kernResult)
169+    {
170+        VERBOSE(VB_IMPORTANT, QString("FindMediaType - IORegistryEntryCreateIterator returned %1").arg(kernResult));
171+    }
172+    else if (!iter)
173+    {
174+        VERBOSE(VB_IMPORTANT, "FindMediaType - IORegistryEntryCreateIterator returned a NULL iterator");
175+    }
176+    else
177+    {
178+        // A reference on the initial service object is released in
179+        // the do-while loop below, so add a reference to balance
180+        IOObjectRetain(service);   
181+
182+        do
183+        {
184+            isWholeMedia = false;
185+            if (IOObjectConformsTo(service, kIOMediaClass))
186+            {
187+                CFTypeRef wholeMedia;
188+
189+                wholeMedia = IORegistryEntryCreateCFProperty
190+                             (service,
191+                              CFSTR(kIOMediaWholeKey),
192+                              kCFAllocatorDefault,
193+                              0);
194+
195+                if (NULL == wholeMedia)
196+                {
197+                    VERBOSE(VB_IMPORTANT, "FindMediaType - Could not retrieve Whole property");
198+                }
199+                else
200+                {                                       
201+                    isWholeMedia = CFBooleanGetValue((CFBooleanRef)wholeMedia);
202+                    CFRelease(wholeMedia);
203+                }
204+            }
205+
206+            if (isWholeMedia)
207+            {
208+                if (IOObjectConformsTo(service, kIODVDMediaClass))
209+                    mediaType = MEDIATYPE_DVD;
210+                else if (IOObjectConformsTo(service, kIOCDMediaClass))
211+                    mediaType = MEDIATYPE_AUDIO;
212+            }
213+            else
214+                mediaType = MEDIATYPE_DATA;
215+
216+            IOObjectRelease(service);
217+        } while ((service = IOIteratorNext(iter))
218+                 && (mediaType == MEDIATYPE_UNKNOWN));
219+
220+        IOObjectRelease(iter);
221+    }
222+    return mediaType;
223+}
224+
225+MediaType MediaTypeForBSDName(const char *bsdName)
226+{
227+    CFMutableDictionaryRef  matchingDict;
228+    kern_return_t           kernResult;
229+    io_iterator_t           iter;
230+    io_service_t            service;
231+    QString                 msg = QString("MediaTypeForBSDName(%1)")
232+                                  .arg(bsdName);
233+    MediaType               mediaType;
234+
235+
236+    if (!bsdName || !*bsdName)
237+    {
238+        VERBOSE(VB_IMPORTANT, msg + " - Error. No name supplied?");
239+        return MEDIATYPE_UNKNOWN;
240+    }
241+
242+    VERBOSE(VB_UPNP, msg);
243+
244+    matchingDict = IOBSDNameMatching(sMasterPort, 0, bsdName);
245+    if (NULL == matchingDict)
246+    {
247+        VERBOSE(VB_IMPORTANT, msg +
248+                " - Error. IOBSDNameMatching returned a NULL dictionary.");
249+        return MEDIATYPE_UNKNOWN;
250+    }
251+
252+    // Return an iterator across all objects with the matching
253+    // BSD node name. Note that there should only be one match!
254+    kernResult = IOServiceGetMatchingServices(sMasterPort, matchingDict, &iter);   
255+    if (KERN_SUCCESS != kernResult)
256+    {
257+        VERBOSE(VB_IMPORTANT,
258+                (msg + " - Error. IOServiceGetMatchingServices returned %1")
259+                .arg(kernResult));
260+        return MEDIATYPE_UNKNOWN;
261+    }
262+    else if (!iter)
263+    {
264+        VERBOSE(VB_IMPORTANT, msg + " - Error. IOServiceGetMatchingServices"
265+                                  + " returned a NULL iterator");
266+        return MEDIATYPE_UNKNOWN;
267+    }
268+
269+    service = IOIteratorNext(iter);
270+
271+    // Release this now because we only expect the iterator to contain
272+    // a single io_service_t.
273+    IOObjectRelease(iter);
274+
275+    if (!service)
276+    {
277+        VERBOSE(VB_IMPORTANT, msg + "IOIteratorNext returned a NULL iterator");
278+        return MEDIATYPE_UNKNOWN;
279+    }
280+
281+    mediaType = FindMediaType(service);
282+    IOObjectRelease(service);
283+    return mediaType;
284+}
285+
286+/*
287+ * Callbacks which the Disk Arbitration session invokes
288+ * whenever a disk is mounted/unmounted/attached/detatched
289+ */
290+
291+void diskAppearedCallback(DADiskRef disk, void *context)
292+{
293+    const char      *BSDname = DADiskGetBSDName(disk);
294+    CFDictionaryRef  details;
295+    MediaType        mediaType;
296+    MonitorThread   *monitor;
297+    QString          msg = "diskAppearedCallback() - ";
298+    CFStringRef      name;
299+    CFIndex          size;
300+    char            *volName;
301+
302+
303+    if (!BSDname)
304+    {
305+        VERBOSE(VB_UPNP, msg + "Skipping non-local device");
306+        return;
307+    }
308+
309+    if (!context)
310+    {
311+        VERBOSE(VB_IMPORTANT, msg + "Error. Invoked with a NULL context.");
312+        return;
313+    }
314+
315+    monitor = reinterpret_cast<MonitorThread*>(context);
316+
317+
318+    // We want to monitor CDs/DVDs and USB cameras or flash drives,
319+    // but probably not hard disk or network drives. For now, ignore
320+    // any disk or partitions that are not on removable media.
321+    // Seems OK for hot-plug USB/FireWire disks (i.e. they are removable)
322+
323+    details = DADiskCopyDescription(disk);
324+    if (kCFBooleanFalse ==
325+        CFDictionaryGetValue(details, kDADiskDescriptionMediaRemovableKey))
326+    {
327+        VERBOSE(VB_UPNP, msg + "Skipping non-removable " + BSDname);
328+        CFRelease(details);
329+        return;
330+    }
331+
332+    // Get the volume name for more user-friendly interaction
333+    name = (CFStringRef)
334+           CFDictionaryGetValue(details, kDADiskDescriptionVolumeNameKey);
335+    size = CFStringGetLength(name) + 1;
336+    volName = (char *) malloc(size);
337+    if (!volName)
338+    {
339+        QString  err = QString("Error. Couldn't malloc(%1)?").arg(size);
340+
341+        VERBOSE(VB_IMPORTANT, msg + err);
342+        CFRelease(details);
343+        return;
344+    }
345+    if (!CFStringGetCString(name, volName, size, kCFStringEncodingUTF8))
346+        *volName = '\0';
347+
348+
349+
350+    // We know it is removable, and have guessed the type.
351+    // Call a helper function to create appropriate objects and insert
352+
353+    VERBOSE(VB_UPNP, QString("Found disk %1 - volume name '%2'.")
354+                     .arg(BSDname).arg(volName));
355+
356+    mediaType = MediaTypeForBSDName(BSDname);
357+
358+    if ((mediaType == MEDIATYPE_DVD) || (mediaType == MEDIATYPE_AUDIO))
359+        monitor->diskInsert(BSDname, volName, disk, true);
360+
361+    if (mediaType == MEDIATYPE_DATA)
362+        monitor->diskInsert(BSDname, volName, disk, false);
363+
364+    CFRelease(details);
365+    free(volName);
366+}
367+
368+void diskDisappearedCallback(DADiskRef disk, void *context)
369+{
370+    const char  *BSDname = DADiskGetBSDName(disk);
371+
372+    if (context)
373+        reinterpret_cast<MonitorThread *>(context)->diskRemove(BSDname);
374+}
375+
376+
377+/*
378+ * Extra public methods in a friend class of MythMediaMonitor,
379+ * so that we can add or remove from its list of media objects
380+ */
381+
382+void MonitorThread::diskInsert(const char *devName,
383+                               const char *volName, void *disk, bool isCDorDVD)
384+{
385+    MythMediaDevice  *media;
386+    QString           msg = QString("MonitorThread::diskInsert(%1,%2,%3)")
387+                            .arg(devName).arg(volName).arg(isCDorDVD);
388+
389+    VERBOSE(VB_UPNP, msg);
390+
391+    if (isCDorDVD)
392+        media = MythCDROM::get(m_Monitor, devName, true,
393+                               m_Monitor->m_AllowEject);
394+    else
395+        media = MythHDD::Get(m_Monitor, devName, true,
396+                             m_Monitor->m_AllowEject);
397+
398+    if (!media)
399+        VERBOSE(VB_IMPORTANT,
400+                msg + " - Error. Couldn't create MythMediaDevice.");
401+    else
402+    {
403+        m_Monitor->AddDevice(media);
404+
405+        // Store the volume name for user activities like ChooseAndEjectMedia()
406+        if (volName && *volName)
407+            media->setVolumeID(volName);
408+    }
409+}
410+
411+void MonitorThread::diskRemove(QString devName)
412+{
413+    VERBOSE(VB_UPNP, QString("MonitorThread::diskRemove(%1)").arg(devName));
414+
415+    m_Monitor->RemoveDevice(devName);
416+}
417+
418+/*
419+ * Some convenience functions for setting up the Disk Arbitration stuff.
420+ * Having them here minimises strange Apple code in mythmediamonitor.cpp
421+ */
422+
423+bool MonitorThread::registerDiskArbitrationCallbacks()
424+{
425+    DASessionRef  session = DASessionCreate(kCFAllocatorDefault);
426+
427+
428+    if (!session)
429+    {
430+        VERBOSE(VB_IMPORTANT,
431+                QString("MonitorThread::registerDiskArbitrationCallbacks()")
432+                + " - Error. Cannot create a DASession.");
433+        return false;
434+    }
435+
436+    m_DiskArbitrationSession = (void *) session;  // for later release
437+
438+    DARegisterDiskAppearedCallback(session,
439+                                   kDADiskDescriptionMatchVolumeMountable,
440+                                   diskAppearedCallback, this);
441+    DARegisterDiskDisappearedCallback(session,
442+                                      kDADiskDescriptionMatchVolumeMountable,
443+                                      diskDisappearedCallback, this);
444+
445+    DASessionScheduleWithRunLoop(session,
446+                                 CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
447+
448+    return true;
449+}
450+
451+void MonitorThread::deregisterDiskArbitrationCallbacks()
452+{
453+    CFRelease((DASessionRef) m_DiskArbitrationSession);
454+}
455
456Property changes on: libs/libmyth/mythmedia-darwin.cpp
457___________________________________________________________________
458Name: svn:keywords
459   + Date Revision Author
460Name: svn:eol-style
461   + native
462