[cstdstring] removal of Trim/TrimLeft/TrimRight
[vuplus_xbmc] / xbmc / storage / DetectDVDType.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "system.h"
22
23 #ifdef HAS_DVD_DRIVE
24
25 #include "DetectDVDType.h"
26 #include "guilib/LocalizeStrings.h"
27 #include "utils/StringUtils.h"
28 #include "utils/log.h"
29 #include "cdioSupport.h"
30 #include "filesystem/iso9660.h"
31 #include "threads/SingleLock.h"
32 #ifdef TARGET_POSIX
33 #include <sys/types.h>
34 #include <sys/ioctl.h>
35 #include <fcntl.h>
36 #if !defined(TARGET_DARWIN) && !defined(TARGET_FREEBSD)
37 #include <linux/cdrom.h>
38 #endif
39 #endif
40 #include "settings/AdvancedSettings.h"
41 #include "GUIUserMessages.h"
42 #include "utils/URIUtils.h"
43 #if defined (LIBCDIO_VERSION_NUM) && (LIBCDIO_VERSION_NUM > 77) || defined (TARGET_DARWIN)
44 #define USING_CDIO78
45 #endif
46 #include "guilib/GUIWindowManager.h"
47 #include "FileItem.h"
48 #include "Application.h"
49 #include "IoSupport.h"
50 #include "cdioSupport.h"
51 #include "storage/MediaManager.h"
52
53
54 using namespace XFILE;
55 using namespace MEDIA_DETECT;
56
57 CCriticalSection CDetectDVDMedia::m_muReadingMedia;
58 CEvent CDetectDVDMedia::m_evAutorun;
59 int CDetectDVDMedia::m_DriveState = DRIVE_CLOSED_NO_MEDIA;
60 CCdInfo* CDetectDVDMedia::m_pCdInfo = NULL;
61 time_t CDetectDVDMedia::m_LastPoll = 0;
62 CDetectDVDMedia* CDetectDVDMedia::m_pInstance = NULL;
63 CStdString CDetectDVDMedia::m_diskLabel = "";
64 CStdString CDetectDVDMedia::m_diskPath = "";
65
66 CDetectDVDMedia::CDetectDVDMedia() : CThread("DetectDVDMedia")
67 {
68   m_bAutorun = false;
69   m_bStop = false;
70   m_dwLastTrayState = 0;
71   m_bStartup = true;  // Do not autorun on startup
72   m_pInstance = this;
73   m_cdio = CLibcdio::GetInstance();
74 }
75
76 CDetectDVDMedia::~CDetectDVDMedia()
77 {
78 }
79
80 void CDetectDVDMedia::OnStartup()
81 {
82   // SetPriority( THREAD_PRIORITY_LOWEST );
83   CLog::Log(LOGDEBUG, "Compiled with libcdio Version 0.%d", LIBCDIO_VERSION_NUM);
84 }
85
86 void CDetectDVDMedia::Process()
87 {
88 // for apple - currently disable this check since cdio will return null if no media is loaded
89 #if !defined(TARGET_DARWIN)
90   //Before entering loop make sure we actually have a CDrom drive
91   CdIo_t *p_cdio = m_cdio->cdio_open(NULL, DRIVER_DEVICE);
92   if (p_cdio == NULL)
93     return;
94   else
95     m_cdio->cdio_destroy(p_cdio);
96 #endif
97
98   while (( !m_bStop ))
99   {
100     if (g_application.m_pPlayer->IsPlayingVideo())
101     {
102       Sleep(10000);
103     }
104     else
105     {
106       UpdateDvdrom();
107       m_bStartup = false;
108       Sleep(2000);
109       if ( m_bAutorun )
110       {
111         Sleep(1500); // Media in drive, wait 1.5s more to be sure the device is ready for playback
112         m_evAutorun.Set();
113         m_bAutorun = false;
114       }
115     }
116   }
117 }
118
119 void CDetectDVDMedia::OnExit()
120 {
121 }
122
123 // Gets state of the DVD drive
124 VOID CDetectDVDMedia::UpdateDvdrom()
125 {
126   // Signal for WaitMediaReady()
127   // that we are busy detecting the
128   // newly inserted media.
129   {
130     CSingleLock waitLock(m_muReadingMedia);
131     switch (GetTrayState())
132     {
133       case DRIVE_NONE:
134         // TODO: reduce / stop polling for drive updates
135         break;
136
137       case DRIVE_OPEN:
138         {
139           // Send Message to GUI that disc been ejected
140           SetNewDVDShareUrl("D:\\", false, g_localizeStrings.Get(502));
141           m_isoReader.Reset();
142           CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REMOVED_MEDIA);
143           g_windowManager.SendThreadMessage( msg );
144           waitLock.Leave();
145           m_DriveState = DRIVE_OPEN;
146           return;
147         }
148         break;
149
150       case DRIVE_NOT_READY:
151         {
152           // drive is not ready (closing, opening)
153           m_isoReader.Reset();
154           SetNewDVDShareUrl("D:\\", false, g_localizeStrings.Get(503));
155           m_DriveState = DRIVE_NOT_READY;
156           // DVD-ROM in undefined state
157           // better delete old CD Information
158           if ( m_pCdInfo != NULL )
159           {
160             delete m_pCdInfo;
161             m_pCdInfo = NULL;
162           }
163           waitLock.Leave();
164           CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_SOURCES);
165           g_windowManager.SendThreadMessage( msg );
166           // Do we really need sleep here? This will fix: [ 1530771 ] "Open tray" problem
167           // Sleep(6000);
168           return ;
169         }
170         break;
171
172       case DRIVE_CLOSED_NO_MEDIA:
173         {
174           // nothing in there...
175           m_isoReader.Reset();
176           SetNewDVDShareUrl("D:\\", false, g_localizeStrings.Get(504));
177           m_DriveState = DRIVE_CLOSED_NO_MEDIA;
178           // Send Message to GUI that disc has changed
179           CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_SOURCES);
180           waitLock.Leave();
181           g_windowManager.SendThreadMessage( msg );
182           return ;
183         }
184         break;
185       case DRIVE_READY:
186 #if !defined(TARGET_DARWIN)
187         return ;
188 #endif
189       case DRIVE_CLOSED_MEDIA_PRESENT:
190         {
191           if ( m_DriveState != DRIVE_CLOSED_MEDIA_PRESENT)
192           {
193             m_DriveState = DRIVE_CLOSED_MEDIA_PRESENT;
194             // Detect ISO9660(mode1/mode2) or CDDA filesystem
195             DetectMediaType();
196             CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_SOURCES);
197             waitLock.Leave();
198             g_windowManager.SendThreadMessage( msg );
199             // Tell the application object that a new Cd is inserted
200             // So autorun can be started.
201             if ( !m_bStartup )
202               m_bAutorun = true;
203           }
204           return ;
205         }
206         break;
207     }
208
209     // We have finished media detection
210     // Signal for WaitMediaReady()
211   }
212
213
214 }
215
216 // Generates the drive url, (like iso9660://)
217 // from the CCdInfo class
218 void CDetectDVDMedia::DetectMediaType()
219 {
220   bool bCDDA(false);
221   CLog::Log(LOGINFO, "Detecting DVD-ROM media filesystem...");
222
223   CStdString strNewUrl;
224   CCdIoSupport cdio;
225
226   // Delete old CD-Information
227   if ( m_pCdInfo != NULL )
228   {
229     delete m_pCdInfo;
230     m_pCdInfo = NULL;
231   }
232
233   // Detect new CD-Information
234   m_pCdInfo = cdio.GetCdInfo();
235   if (m_pCdInfo == NULL)
236   {
237     CLog::Log(LOGERROR, "Detection of DVD-ROM media failed.");
238     return ;
239   }
240   CLog::Log(LOGINFO, "Tracks overall:%i; Audio tracks:%i; Data tracks:%i",
241             m_pCdInfo->GetTrackCount(),
242             m_pCdInfo->GetAudioTrackCount(),
243             m_pCdInfo->GetDataTrackCount() );
244
245   // Detect ISO9660(mode1/mode2), CDDA filesystem or UDF
246   if (m_pCdInfo->IsISOHFS(1) || m_pCdInfo->IsIso9660(1) || m_pCdInfo->IsIso9660Interactive(1))
247   {
248     strNewUrl = "iso9660://";
249     m_isoReader.Scan();
250   }
251   else
252   {
253     if (m_pCdInfo->IsUDF(1) || m_pCdInfo->IsUDFX(1))
254       strNewUrl = "D:\\";
255     else if (m_pCdInfo->IsAudio(1))
256     {
257       strNewUrl = "cdda://local/";
258       bCDDA = true;
259     }
260     else
261       strNewUrl = "D:\\";
262   }
263
264   if (m_pCdInfo->IsISOUDF(1))
265   {
266     if (!g_advancedSettings.m_detectAsUdf)
267     {
268       strNewUrl = "iso9660://";
269       m_isoReader.Scan();
270     }
271     else
272     {
273       strNewUrl = "D:\\";
274     }
275   }
276
277   CLog::Log(LOGINFO, "Using protocol %s", strNewUrl.c_str());
278
279   if (m_pCdInfo->IsValidFs())
280   {
281     if (!m_pCdInfo->IsAudio(1))
282       CLog::Log(LOGINFO, "Disc label: %s", m_pCdInfo->GetDiscLabel().c_str());
283   }
284   else
285   {
286     CLog::Log(LOGWARNING, "Filesystem is not supported");
287   }
288
289   CStdString strLabel = "";
290   if (bCDDA)
291   {
292     strLabel = "Audio-CD";
293   }
294   else
295   {
296     strLabel = m_pCdInfo->GetDiscLabel();
297     StringUtils::TrimRight(strLabel);
298   }
299
300   SetNewDVDShareUrl( strNewUrl , bCDDA, strLabel);
301 }
302
303 void CDetectDVDMedia::SetNewDVDShareUrl( const CStdString& strNewUrl, bool bCDDA, const CStdString& strDiscLabel )
304 {
305   CStdString strDescription = "DVD";
306   if (bCDDA) strDescription = "CD";
307
308   if (strDiscLabel != "") strDescription = strDiscLabel;
309
310   // store it in case others want it
311   m_diskLabel = strDescription;
312   m_diskPath = strNewUrl;
313 }
314
315 DWORD CDetectDVDMedia::GetTrayState()
316 {
317 #ifdef TARGET_POSIX
318
319   char* dvdDevice = m_cdio->GetDeviceFileName();
320   if (strlen(dvdDevice) == 0)
321     return DRIVE_NONE;
322
323 #ifndef USING_CDIO78
324
325   int fd = 0;
326
327   fd = open(dvdDevice, O_RDONLY | O_NONBLOCK);
328   if (fd<0)
329   {
330     CLog::Log(LOGERROR, "Unable to open CD-ROM device %s for polling.", dvdDevice);
331     return DRIVE_NOT_READY;
332   }
333
334   int drivestatus = ioctl(fd, CDROM_DRIVE_STATUS, 0);
335
336   switch(drivestatus)
337   {
338   case CDS_NO_INFO:
339     m_dwTrayState = TRAY_CLOSED_NO_MEDIA;
340     break;
341
342   case CDS_NO_DISC:
343     m_dwTrayState = TRAY_CLOSED_NO_MEDIA;
344     break;
345
346   case CDS_TRAY_OPEN:
347     m_dwTrayState = TRAY_OPEN;
348     break;
349
350   case CDS_DISC_OK:
351     m_dwTrayState = TRAY_CLOSED_MEDIA_PRESENT;
352     break;
353
354   case CDS_DRIVE_NOT_READY:
355     close(fd);
356     return DRIVE_NOT_READY;
357
358   default:
359     m_dwTrayState = TRAY_CLOSED_NO_MEDIA;
360   }
361
362   close(fd);
363
364 #else
365
366   // The following code works with libcdio >= 0.78
367   // To enable it, download and install the latest version from
368   // http://www.gnu.org/software/libcdio/
369   // -d4rk 06/27/07
370
371
372   m_dwTrayState = TRAY_CLOSED_MEDIA_PRESENT;
373   CdIo_t* cdio = m_cdio->cdio_open(dvdDevice, DRIVER_UNKNOWN);
374   if (cdio)
375   {
376     static discmode_t discmode = CDIO_DISC_MODE_NO_INFO;
377     int status = m_cdio->mmc_get_tray_status(cdio);
378     static int laststatus = -1;
379     // We only poll for new discmode when status has changed or there have been read errors (The last usually happens when new media is inserted)
380     if (status == 0 && (laststatus != status || discmode == CDIO_DISC_MODE_ERROR))
381       discmode = m_cdio->cdio_get_discmode(cdio);
382
383     switch(status)
384     {
385     case 0: //closed
386       if (discmode==CDIO_DISC_MODE_NO_INFO || discmode==CDIO_DISC_MODE_ERROR)
387         m_dwTrayState = TRAY_CLOSED_NO_MEDIA;
388       else
389         m_dwTrayState = TRAY_CLOSED_MEDIA_PRESENT;
390       break;
391
392     case 1: //open
393       m_dwTrayState = TRAY_OPEN;
394       break;
395     }
396     laststatus = status;
397     m_cdio->cdio_destroy(cdio);
398   }
399   else
400     return DRIVE_NOT_READY;
401
402 #endif // USING_CDIO78
403 #endif // TARGET_POSIX
404
405   if (m_dwTrayState == TRAY_CLOSED_MEDIA_PRESENT)
406   {
407     if (m_dwLastTrayState != TRAY_CLOSED_MEDIA_PRESENT)
408     {
409       m_dwLastTrayState = m_dwTrayState;
410       return DRIVE_CLOSED_MEDIA_PRESENT;
411     }
412     else
413     {
414       return DRIVE_READY;
415     }
416   }
417   else if (m_dwTrayState == TRAY_CLOSED_NO_MEDIA)
418   {
419     if ( (m_dwLastTrayState != TRAY_CLOSED_NO_MEDIA) && (m_dwLastTrayState != TRAY_CLOSED_MEDIA_PRESENT) )
420     {
421       m_dwLastTrayState = m_dwTrayState;
422       return DRIVE_CLOSED_NO_MEDIA;
423     }
424     else
425     {
426       return DRIVE_READY;
427     }
428   }
429   else if (m_dwTrayState == TRAY_OPEN)
430   {
431     if (m_dwLastTrayState != TRAY_OPEN)
432     {
433       m_dwLastTrayState = m_dwTrayState;
434       return DRIVE_OPEN;
435     }
436     else
437     {
438       return DRIVE_READY;
439     }
440   }
441   else
442   {
443     m_dwLastTrayState = m_dwTrayState;
444   }
445
446 #ifdef HAS_DVD_DRIVE
447   return DRIVE_NOT_READY;
448 #else
449   return DRIVE_READY;
450 #endif
451 }
452
453 void CDetectDVDMedia::UpdateState()
454 {
455   CSingleLock waitLock(m_muReadingMedia);
456   m_pInstance->DetectMediaType();
457 }
458
459 // Static function
460 // Wait for drive, to finish media detection.
461 void CDetectDVDMedia::WaitMediaReady()
462 {
463   CSingleLock waitLock(m_muReadingMedia);
464 }
465
466 // Static function
467 // Returns status of the DVD Drive
468 int CDetectDVDMedia::DriveReady()
469 {
470   return m_DriveState;
471 }
472
473 // Static function
474 // Whether a disc is in drive
475 bool CDetectDVDMedia::IsDiscInDrive()
476 {
477   return m_DriveState == DRIVE_CLOSED_MEDIA_PRESENT;
478 }
479
480 // Static function
481 // Returns a CCdInfo class, which contains
482 // Media information of the current
483 // inserted CD.
484 // Can be NULL
485 CCdInfo* CDetectDVDMedia::GetCdInfo()
486 {
487   CSingleLock waitLock(m_muReadingMedia);
488   CCdInfo* pCdInfo = m_pCdInfo;
489   return pCdInfo;
490 }
491
492 const CStdString &CDetectDVDMedia::GetDVDLabel()
493 {
494   return m_diskLabel;
495 }
496
497 const CStdString &CDetectDVDMedia::GetDVDPath()
498 {
499   return m_diskPath;
500 }
501 #endif