MythTV master
mediamonitor-darwin.cpp
Go to the documentation of this file.
1
8#include <QDir>
9#include <QMetaType>
10
11#include "libmythbase/mythconfig.h"
13#include "libmythbase/mythhdd.h"
15
16#include "mediamonitor.h"
17#include "mediamonitor-darwin.h"
18
19#include <IOKit/IOKitLib.h>
20#include <IOKit/storage/IOMedia.h>
21#include <IOKit/storage/IOCDMedia.h>
22#include <IOKit/storage/IODVDMedia.h>
23#include <IOKit/storage/IOBlockStorageDevice.h>
24#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
25#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
26#include <DiskArbitration/DiskArbitration.h>
27
28#if !HAVE_IOMAINPORT
29#define IOMainPort IOMasterPort
30#endif
31
32// These aren't external, they are defined in this file.
33// The 'extern "C"' forces them in the C namespace, not the C++
34extern "C" void diskAppearedCallback(DADiskRef disk, void *context);
35extern "C" void diskDisappearedCallback(DADiskRef disk, void *context);
36extern "C" void diskChangedCallback(DADiskRef disk,
37 CFArrayRef keys, void *context);
38extern "C" MythMediaType MediaTypeForBSDName(const char *bsdName);
39
40static mach_port_t sMasterPort;
41
42
46MythMediaType FindMediaType(io_service_t service)
47{
48 kern_return_t kernResult;
49 io_iterator_t iter;
51 QString msg = QString("FindMediaType() - ");
52
53 // Create an iterator across all parents of the service object passed in.
54 kernResult = IORegistryEntryCreateIterator(service,
55 kIOServicePlane,
56 kIORegistryIterateRecursively
57 | kIORegistryIterateParents,
58 &iter);
59
60 if (KERN_SUCCESS != kernResult)
61 {
62 LOG(VB_GENERAL, LOG_CRIT, msg +
63 QString("IORegistryEntryCreateIterator returned %1")
64 .arg(kernResult));
65 }
66 else if (!iter)
67 {
68 LOG(VB_GENERAL, LOG_CRIT, msg +
69 "IORegistryEntryCreateIterator returned NULL iterator");
70 }
71 else
72 {
73 // A reference on the initial service object is released in
74 // the do-while loop below, so add a reference to balance
75 IOObjectRetain(service);
76
77 do
78 {
79 bool isWholeMedia = false;
80 if (IOObjectConformsTo(service, kIOMediaClass))
81 {
82 CFTypeRef wholeMedia;
83
84 wholeMedia = IORegistryEntryCreateCFProperty
85 (service, CFSTR(kIOMediaWholeKey),
86 kCFAllocatorDefault, 0);
87
88 if (!wholeMedia)
89 {
90 LOG(VB_GENERAL, LOG_ALERT, msg +
91 "Could not retrieve Whole property");
92 }
93 else
94 {
95 isWholeMedia = CFBooleanGetValue((CFBooleanRef)wholeMedia);
96 CFRelease(wholeMedia);
97 }
98 }
99
100 if (isWholeMedia)
101 {
102 if (IOObjectConformsTo(service, kIODVDMediaClass))
103 mediaType = MEDIATYPE_DVD;
104 else if (IOObjectConformsTo(service, kIOCDMediaClass))
105 mediaType = MEDIATYPE_AUDIO;
106 }
107
108 IOObjectRelease(service);
109
110 } while ((service = IOIteratorNext(iter))
111 && (mediaType == MEDIATYPE_UNKNOWN));
112
113 IOObjectRelease(iter);
114 }
115 return mediaType;
116}
117
122{
123 CFMutableDictionaryRef matchingDict;
124 kern_return_t kernResult;
125 io_iterator_t iter;
126 io_service_t service;
127 QString msg = QString("MediaTypeForBSDName(%1)")
128 .arg(bsdName);
129 MythMediaType mediaType;
130
131
132 if (!bsdName || !*bsdName)
133 {
134 LOG(VB_GENERAL, LOG_ALERT, msg + " - No name supplied?");
135 return MEDIATYPE_UNKNOWN;
136 }
137
138 matchingDict = IOBSDNameMatching(sMasterPort, 0, bsdName);
139 if (!matchingDict)
140 {
141 LOG(VB_GENERAL, LOG_ALERT,
142 msg + " - IOBSDNameMatching() returned a NULL dictionary.");
143 return MEDIATYPE_UNKNOWN;
144 }
145
146 // Return an iterator across all objects with the matching
147 // BSD node name. Note that there should only be one match!
148 kernResult = IOServiceGetMatchingServices(sMasterPort, matchingDict, &iter);
149
150 if (KERN_SUCCESS != kernResult)
151 {
152 LOG(VB_GENERAL, LOG_ALERT,
153 QString(msg + " - IOServiceGetMatchingServices() returned %2")
154 .arg(kernResult));
155 return MEDIATYPE_UNKNOWN;
156 }
157 if (!iter)
158 {
159 LOG(VB_GENERAL, LOG_ALERT,
160 msg + " - IOServiceGetMatchingServices() returned a NULL "
161 "iterator");
162 return MEDIATYPE_UNKNOWN;
163 }
164
165 service = IOIteratorNext(iter);
166
167 // Release this now because we only expect
168 // the iterator to contain a single io_service_t.
169 IOObjectRelease(iter);
170
171 if (!service)
172 {
173 LOG(VB_GENERAL, LOG_ALERT,
174 msg + " - IOIteratorNext() returned a NULL iterator");
175 return MEDIATYPE_UNKNOWN;
176 }
177 mediaType = FindMediaType(service);
178 IOObjectRelease(service);
179 return mediaType;
180}
181
182
186static char * getVolName(CFDictionaryRef diskDetails)
187{
188 CFStringRef name;
189 CFIndex size;
190 char *volName;
191
192 name = (CFStringRef)
193 CFDictionaryGetValue(diskDetails, kDADiskDescriptionVolumeNameKey);
194
195 if (!name)
196 return nullptr;
197
198 size = CFStringGetLength(name) + 1;
199 volName = (char *) malloc(size);
200 if (!volName)
201 {
202 LOG(VB_GENERAL, LOG_ALERT,
203 QString("getVolName() - Can't malloc(%1)?").arg(size));
204 return nullptr;
205 }
206
207 if (!CFStringGetCString(name, volName, size, kCFStringEncodingUTF8))
208 {
209 free(volName);
210 return nullptr;
211 }
212
213 return volName;
214}
215
216/*
217 * Given a DA description, return a compound description to help identify it.
218 */
219static QString getModel(CFDictionaryRef diskDetails)
220{
221 QString desc;
222 const void *strRef;
223
224 // Location
225 if (kCFBooleanTrue ==
226 CFDictionaryGetValue(diskDetails,
227 kDADiskDescriptionDeviceInternalKey))
228 desc.append("Internal ");
229
230 // Manufacturer
231 strRef = CFDictionaryGetValue(diskDetails,
232 kDADiskDescriptionDeviceVendorKey);
233 if (strRef)
234 {
235 desc.append(CFStringGetCStringPtr((CFStringRef)strRef,
236 kCFStringEncodingMacRoman));
237 desc.append(' ');
238 }
239
240 // Product
241 strRef = CFDictionaryGetValue(diskDetails,
242 kDADiskDescriptionDeviceModelKey);
243 if (strRef)
244 {
245 desc.append(CFStringGetCStringPtr((CFStringRef)strRef,
246 kCFStringEncodingMacRoman));
247 desc.append(' ');
248 }
249
250 // Remove the trailing space
251 desc.truncate(desc.length() - 1);
252
253 // and multiple spaces
254 desc.remove(" ");
255
256 return desc;
257}
258
259
260/*
261 * Callbacks which the Disk Arbitration session invokes
262 * whenever a disk comes or goes, or is renamed
263 */
264
265void diskAppearedCallback(DADiskRef disk, void *context)
266{
267 const char *BSDname = DADiskGetBSDName(disk);
268 CFDictionaryRef details;
269 bool isCDorDVD;
270 MythMediaType mediaType;
271 QString model;
273 QString msg = "diskAppearedCallback() - ";
274 char *volName;
275
276
277 if (!BSDname)
278 {
279 LOG(VB_MEDIA, LOG_INFO, msg + "Skipping non-local device");
280 return;
281 }
282
283 if (!context)
284 {
285 LOG(VB_GENERAL, LOG_ALERT, msg + "Error. Invoked with a NULL context.");
286 return;
287 }
288
289 mtd = reinterpret_cast<MonitorThreadDarwin*>(context);
290
291
292 // We want to monitor CDs/DVDs and USB cameras or flash drives,
293 // but probably not hard disk or network drives. For now, ignore
294 // any disk or partitions that are not on removable media.
295 // Seems OK for hot-plug USB/FireWire disks (i.e. they are removable)
296
297 details = DADiskCopyDescription(disk);
298
299 if (kCFBooleanFalse ==
300 CFDictionaryGetValue(details, kDADiskDescriptionMediaRemovableKey))
301 {
302 LOG(VB_MEDIA, LOG_INFO, msg + QString("Skipping non-removable %1")
303 .arg(BSDname));
304 CFRelease(details);
305 return;
306 }
307
308 // Get the volume and model name for more user-friendly interaction
309 volName = getVolName(details);
310 if (!volName)
311 {
312 LOG(VB_MEDIA, LOG_INFO, msg + QString("No volume name for dev %1")
313 .arg(BSDname));
314 CFRelease(details);
315 return;
316 }
317
318 model = getModel(details);
319
320 if (model.contains("Disk Image"))
321 {
322 LOG(VB_MEDIA, LOG_INFO, msg + QString("DMG %1 mounted, ignoring")
323 .arg(BSDname));
324 CFRelease(details);
325 free(volName);
326 return;
327 }
328
329 mediaType = MediaTypeForBSDName(BSDname);
330 isCDorDVD = (mediaType == MEDIATYPE_DVD) || (mediaType == MEDIATYPE_AUDIO);
331
332
333 // We know it is removable, and have guessed the type.
334 // Call a helper function to create appropriate objects and insert
335
336 LOG(VB_MEDIA, LOG_INFO, QString("Found disk %1 - volume name '%2'.")
337 .arg(BSDname).arg(volName));
338
339 mtd->diskInsert(BSDname, volName, model, isCDorDVD);
340
341 CFRelease(details);
342 free(volName);
343}
344
345void diskDisappearedCallback(DADiskRef disk, void *context)
346{
347 const char *BSDname = DADiskGetBSDName(disk);
348
349 if (context)
350 reinterpret_cast<MonitorThreadDarwin *>(context)->diskRemove(BSDname);
351}
352
353void diskChangedCallback(DADiskRef disk, CFArrayRef keys, void *context)
354{
355 if (CFArrayContainsValue(keys, CFRangeMake(0, CFArrayGetCount(keys)),
356 kDADiskDescriptionVolumeNameKey))
357 {
358 const char *BSDname = DADiskGetBSDName(disk);
359 CFDictionaryRef details = DADiskCopyDescription(disk);
360 char *volName = getVolName(details);
361
362 LOG(VB_MEDIA, LOG_INFO, QString("Disk %1 - changed name to '%2'.")
363 .arg(BSDname).arg(volName));
364
365 reinterpret_cast<MonitorThreadDarwin *>(context)
366 ->diskRename(BSDname, volName);
367 CFRelease(details);
368 free(volName);
369 }
370}
371
372
377{
378 RunProlog();
379 CFDictionaryRef match = kDADiskDescriptionMatchVolumeMountable;
380 DASessionRef daSession = DASessionCreate(kCFAllocatorDefault);
381
382 IOMainPort(MACH_PORT_NULL, &sMasterPort);
383
384 DARegisterDiskAppearedCallback(daSession, match,
386 DARegisterDiskDisappearedCallback(daSession, match,
388 DARegisterDiskDescriptionChangedCallback(daSession, match,
389 kDADiskDescriptionWatchVolumeName,
390 diskChangedCallback, this);
391
392 DASessionScheduleWithRunLoop(daSession,
393 CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
394
395
396 // Nice and simple, as long as our monitor is valid and active,
397 // loop and let daSession check the devices.
398 while (m_monitor && m_monitor->IsActive())
399 {
400 // Run the run loop for interval (milliseconds) - this will
401 // handle any disk arbitration appeared/dissappeared events
402 CFRunLoopRunInMode(kCFRunLoopDefaultMode,
403 (float) m_interval / 1000.0F, false );
404 }
405
406 DAUnregisterCallback(daSession, (void(*))diskChangedCallback, this);
407 DAUnregisterCallback(daSession, (void(*))diskDisappearedCallback, this);
408 DAUnregisterCallback(daSession, (void(*))diskAppearedCallback, this);
409 CFRelease(daSession);
410 RunEpilog();
411}
412
419void MonitorThreadDarwin::diskInsert(const char *devName,
420 const char *volName,
421 QString model, bool isCDorDVD)
422{
423 MythMediaDevice *media;
424 QString msg = "MonitorThreadDarwin::diskInsert";
425
426 LOG(VB_MEDIA, LOG_DEBUG, msg + QString("(%1,%2,'%3',%4)")
427 .arg(devName).arg(volName).arg(model).arg(isCDorDVD));
428
429 if (isCDorDVD)
430 media = MythCDROM::get(nullptr, devName, true, m_monitor->m_allowEject);
431 else
432 media = MythHDD::Get(nullptr, devName, true, false);
433
434 if (!media)
435 {
436 LOG(VB_GENERAL, LOG_ALERT, msg + "Couldn't create MythMediaDevice.");
437 return;
438 }
439
440 // We store the volume name for user activities like ChooseAndEjectMedia().
441 media->setVolumeID(volName);
442 media->setDeviceModel(model.toLatin1()); // Same for the Manufacturer and model
443
444 // Mac OS X devices are pre-mounted here:
445 QString mnt = "/Volumes/"; mnt += volName;
446 media->setMountPath(mnt.toLatin1());
447
448 int attempts = 0;
449 QDir d(mnt);
450 while (!d.exists())
451 {
452 LOG(VB_MEDIA, LOG_WARNING,
453 (msg + "() - Waiting for mount '%1' to become stable.").arg(mnt));
454 usleep(std::chrono::microseconds(120000)); // cppcheck-suppress usleepCalled
455 if ( ++attempts > 4 )
456 usleep(std::chrono::microseconds(200000)); // cppcheck-suppress usleepCalled
457 if ( attempts > 8 )
458 {
459 delete media;
460 LOG(VB_MEDIA, LOG_ALERT, msg + "() - Giving up");
461 return;
462 }
463 }
464
466
467 // This is checked in AddDevice(), but checking earlier means
468 // we can avoid scanning all the files to determine its type
469 if (m_monitor->shouldIgnore(media))
470 return;
471
472 // We want to use MythMedia's code to work out the mediaType.
473 // media->onDeviceMounted() is protected,
474 // so to call it indirectly, we pretend to mount it here.
475 media->mount();
476
477 m_monitor->AddDevice(media);
478}
479
481{
482 LOG(VB_MEDIA, LOG_DEBUG,
483 QString("MonitorThreadDarwin::diskRemove(%1)").arg(devName));
484
485 MythMediaDevice *pDevice = m_monitor->GetMedia(devName);
486
487 if (pDevice) // Probably should ValidateAndLock() here?
488 pDevice->setStatus(MEDIASTAT_NODISK);
489 else
490 LOG(VB_MEDIA, LOG_INFO, "Couldn't find MythMediaDevice: " + devName);
491
492 m_monitor->RemoveDevice(devName);
493}
494
501void MonitorThreadDarwin::diskRename(const char *devName, const char *volName)
502{
503 LOG(VB_MEDIA, LOG_DEBUG,
504 QString("MonitorThreadDarwin::diskRename(%1,%2)")
505 .arg(devName).arg(volName));
506
507 MythMediaDevice *pDevice = m_monitor->GetMedia(devName);
508
509 if (m_monitor->ValidateAndLock(pDevice))
510 {
511 // Send message to plugins to ignore this drive:
512 pDevice->setStatus(MEDIASTAT_NODISK);
513
514 pDevice->setVolumeID(volName);
515 pDevice->setMountPath((QString("/Volumes/") + volName).toLatin1());
516
517 // Plugins can now use it again:
519
520 m_monitor->Unlock(pDevice);
521 }
522 else
523 LOG(VB_MEDIA, LOG_INFO,
524 QString("Couldn't find MythMediaDevice: %1").arg(devName));
525}
526
534{
535 // Sanity check
536 if (m_active)
537 return;
538
539 // If something (like the MythMusic plugin) stops and starts monitoring,
540 // DiskArbitration would re-add the same drives several times over.
541 // So, we make sure the device list is deleted.
542 m_devices.clear();
543
544
545 if (!m_thread)
547
548 qRegisterMetaType<MythMediaStatus>("MythMediaStatus");
549
550 LOG(VB_MEDIA, LOG_NOTICE, "Starting MediaMonitor");
551 m_active = true;
552 m_thread->start();
553}
554
561{
562 if ( !pDevice )
563 {
564 LOG(VB_GENERAL, LOG_ERR, "MediaMonitor::AddDevice(null)");
565 return false;
566 }
567
568 // If the user doesn't want this device to be monitored, stop now:
569 if (shouldIgnore(pDevice))
570 return false;
571
572 m_devices.push_back( pDevice );
573 m_useCount[pDevice] = 0;
574
575
576 // Devices on Mac OS X don't change status the way Linux ones do,
577 // so we force a status change for mediaStatusChanged() to send an event
578 pDevice->setStatus(MEDIASTAT_NODISK);
579 connect(pDevice, &MythMediaDevice::statusChanged,
582
583
584 return true;
585}
586
587/*
588 * Given a device, return a compound description to help identify it.
589 * We try to find out if it is internal, its manufacturer, and model.
590 */
591static QString getModel(io_object_t drive)
592{
593 QString desc;
594 CFMutableDictionaryRef props = nullptr;
595
596 props = (CFMutableDictionaryRef) IORegistryEntrySearchCFProperty(drive, kIOServicePlane, CFSTR(kIOPropertyProtocolCharacteristicsKey), kCFAllocatorDefault, kIORegistryIterateParents | kIORegistryIterateRecursively);
597CFShow(props);
598 if (props)
599 {
600 const void *location = CFDictionaryGetValue(props, CFSTR(kIOPropertyPhysicalInterconnectLocationKey));
601 if (CFEqual(location, CFSTR("Internal")))
602 desc.append("Internal ");
603 }
604
605 props = (CFMutableDictionaryRef) IORegistryEntrySearchCFProperty(drive, kIOServicePlane, CFSTR(kIOPropertyDeviceCharacteristicsKey), kCFAllocatorDefault, kIORegistryIterateParents | kIORegistryIterateRecursively);
606 if (props)
607 {
608 const void *product = CFDictionaryGetValue(props, CFSTR(kIOPropertyProductNameKey));
609 const void *vendor = CFDictionaryGetValue(props, CFSTR(kIOPropertyVendorNameKey));
610 if (vendor)
611 {
612 desc.append(CFStringGetCStringPtr((CFStringRef)vendor, kCFStringEncodingMacRoman));
613 desc.append(" ");
614 }
615 if (product)
616 {
617 desc.append(CFStringGetCStringPtr((CFStringRef)product, kCFStringEncodingMacRoman));
618 desc.append(" ");
619 }
620 }
621
622 // Omit the trailing space
623 desc.truncate(desc.length() - 1);
624
625 return desc;
626}
627
638{
639 kern_return_t kernResult;
640 CFMutableDictionaryRef devices;
641 io_iterator_t iter;
642 QStringList list;
643 QString msg = QString("GetCDRomBlockDevices() - ");
644
645
646 devices = IOServiceMatching(kIOBlockStorageDeviceClass);
647 if (!devices)
648 {
649 LOG(VB_GENERAL, LOG_ALERT, msg + "No Storage Devices? Unlikely!");
650 return list;
651 }
652
653 // Create an iterator across all parents of the service object passed in.
654 kernResult = IOServiceGetMatchingServices(sMasterPort, devices, &iter);
655
656 if (KERN_SUCCESS != kernResult)
657 {
658 LOG(VB_GENERAL, LOG_ALERT, msg +
659 QString("IORegistryEntryCreateIterator returned %1")
660 .arg(kernResult));
661 return list;
662 }
663 if (!iter)
664 {
665 LOG(VB_GENERAL, LOG_ALERT, msg +
666 "IORegistryEntryCreateIterator returned a NULL iterator");
667 return list;
668 }
669
670 io_object_t drive;
671
672 while ((drive = IOIteratorNext(iter)))
673 {
674 CFMutableDictionaryRef p = nullptr; // properties of drive
675
676 IORegistryEntryCreateCFProperties(drive, &p, kCFAllocatorDefault, 0);
677 if (p)
678 {
679 const void *type = CFDictionaryGetValue(p, CFSTR("device-type"));
680
681 if (CFEqual(type, CFSTR("DVD")) || CFEqual(type, CFSTR("CD")))
682 {
683 QString desc = getModel(drive);
684
685 list.append(desc);
686 LOG(VB_MEDIA, LOG_INFO, desc.prepend("Found CD/DVD: "));
687 CFRelease(p);
688 }
689 }
690 else
691 LOG(VB_GENERAL, LOG_ALERT,
692 msg + "Could not retrieve drive properties");
693
694 IOObjectRelease(drive);
695 }
696
697 IOObjectRelease(iter);
698
699 return list;
700}
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:196
static void usleep(std::chrono::microseconds time)
Definition: mthread.cpp:335
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
void StartMonitoring(void) override
Start the monitoring thread if needed.
bool AddDevice(MythMediaDevice *pDevice) override
Simpler version of MediaMonitorUnix::AddDevice()
QStringList GetCDROMBlockDevices(void) override
List of CD/DVD devices.
unsigned long m_monitorPollingInterval
Definition: mediamonitor.h:132
void mediaStatusChanged(MythMediaStatus oldStatus, MythMediaDevice *pMedia) const
Slot which is called when the device status changes and posts a media event to the mainwindow.
MonitorThread * m_thread
Definition: mediamonitor.h:131
bool shouldIgnore(const MythMediaDevice *device)
Check user preferences to see if this device should be monitored.
bool volatile m_active
Was MonitorThread started?
Definition: mediamonitor.h:129
friend class MonitorThreadDarwin
Definition: mediamonitor.h:51
QList< MythMediaDevice * > m_devices
Definition: mediamonitor.h:122
QMap< MythMediaDevice *, int > m_useCount
Definition: mediamonitor.h:124
void diskRemove(QString devName)
void diskRename(const char *devName, const char *volName)
Deal with the user, or another program, renaming a volume.
void diskInsert(const char *devName, const char *volName, QString model, bool isCDorDVD=1)
Create a MythMedia instance and insert in MythMediaMonitor list.
void run(void) override
Use the DiskArbitration Daemon to inform us of media changes.
unsigned long m_interval
Definition: mediamonitor.h:43
QPointer< MediaMonitor > m_monitor
Definition: mediamonitor.h:42
static MythCDROM * get(QObject *par, const QString &devicePath, bool SuperMount, bool AllowEject)
Definition: mythcdrom.cpp:43
static MythHDD * Get(QObject *par, const char *devicePath, bool SuperMount, bool AllowEject)
Helper function used to create a new instance of a hard disk device.
Definition: mythhdd.cpp:15
MythMediaStatus setStatus(MythMediaStatus newStat, bool CloseIt=false)
Definition: mythmedia.cpp:465
void statusChanged(MythMediaStatus oldStatus, MythMediaDevice *pMedia)
void setMountPath(const char *path)
Definition: mythmedia.h:59
void setDeviceModel(const char *model)
Definition: mythmedia.h:68
void setVolumeID(const char *vol)
Definition: mythmedia.h:73
static const iso6937table * d
MythMediaType MediaTypeForBSDName(const char *bsdName)
Given a BSD device node name, guess its media type.
MythMediaType FindMediaType(io_service_t service)
Guess the media that a volume/partition is on.
void diskDisappearedCallback(DADiskRef disk, void *context)
void diskAppearedCallback(DADiskRef disk, void *context)
static char * getVolName(CFDictionaryRef diskDetails)
Given a description of a disk, copy and return the volume name.
static QString getModel(CFDictionaryRef diskDetails)
static mach_port_t sMasterPort
#define IOMainPort
void diskChangedCallback(DADiskRef disk, CFArrayRef keys, void *context)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMediaType
Definition: mythmedia.h:24
@ MEDIATYPE_DVD
Definition: mythmedia.h:29
@ MEDIATYPE_AUDIO
Definition: mythmedia.h:28
@ MEDIATYPE_UNKNOWN
Definition: mythmedia.h:25
@ MEDIASTAT_NODISK
CD/DVD tray closed but empty, device unusable.
Definition: mythmedia.h:17
@ MEDIASTAT_USEABLE
Definition: mythmedia.h:19
@ MEDIASTAT_MOUNTED
Definition: mythmedia.h:21