1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "DetectDVDType.h"
10 #include "guilib/LocalizeStrings.h"
11 #include "utils/StringUtils.h"
12 #include "utils/log.h"
13 #include "cdioSupport.h"
14 #include "filesystem/File.h"
15 #include "threads/SingleLock.h"
16 #ifdef TARGET_POSIX
17 #include <sys/types.h>
18 #include <sys/ioctl.h>
19 #include <fcntl.h>
20 #if !defined(TARGET_DARWIN) && !(defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY))
21 #include <linux/cdrom.h>
22 #endif
23 #endif
24 #include "settings/AdvancedSettings.h"
25 #include "settings/SettingsComponent.h"
26 #include "GUIUserMessages.h"
27 #include "guilib/GUIComponent.h"
28 #include "guilib/GUIWindowManager.h"
29 #include "Application.h"
30 #include "ServiceBroker.h"
31 #include "storage/MediaManager.h"
32
33
34 using namespace XFILE;
35 using namespace MEDIA_DETECT;
36
37 CCriticalSection CDetectDVDMedia::m_muReadingMedia;
38 CEvent CDetectDVDMedia::m_evAutorun;
39 int CDetectDVDMedia::m_DriveState = DRIVE_CLOSED_NO_MEDIA;
40 CCdInfo* CDetectDVDMedia::m_pCdInfo = NULL;
41 time_t CDetectDVDMedia::m_LastPoll = 0;
42 CDetectDVDMedia* CDetectDVDMedia::m_pInstance = NULL;
43 std::string CDetectDVDMedia::m_diskLabel = "";
44 std::string CDetectDVDMedia::m_diskPath = "";
45
CDetectDVDMedia()46 CDetectDVDMedia::CDetectDVDMedia() : CThread("DetectDVDMedia"),
47 m_cdio(CLibcdio::GetInstance())
48 {
49 m_bStop = false;
50 m_pInstance = this;
51 }
52
53 CDetectDVDMedia::~CDetectDVDMedia() = default;
54
OnStartup()55 void CDetectDVDMedia::OnStartup()
56 {
57 // SetPriority( THREAD_PRIORITY_LOWEST );
58 CLog::Log(LOGDEBUG, "Compiled with libcdio Version 0.%d", LIBCDIO_VERSION_NUM);
59 }
60
Process()61 void CDetectDVDMedia::Process()
62 {
63 // for apple - currently disable this check since cdio will return null if no media is loaded
64 #if !defined(TARGET_DARWIN)
65 //Before entering loop make sure we actually have a CDrom drive
66 CdIo_t *p_cdio = m_cdio->cdio_open(NULL, DRIVER_DEVICE);
67 if (p_cdio == NULL)
68 return;
69 else
70 m_cdio->cdio_destroy(p_cdio);
71 #endif
72
73 while (( !m_bStop ))
74 {
75 if (g_application.GetAppPlayer().IsPlayingVideo())
76 {
77 CThread::Sleep(10000);
78 }
79 else
80 {
81 UpdateDvdrom();
82 m_bStartup = false;
83 CThread::Sleep(2000);
84 if ( m_bAutorun )
85 {
86 CThread::Sleep(
87 1500); // Media in drive, wait 1.5s more to be sure the device is ready for playback
88 m_evAutorun.Set();
89 m_bAutorun = false;
90 }
91 }
92 }
93 }
94
OnExit()95 void CDetectDVDMedia::OnExit()
96 {
97 }
98
99 // Gets state of the DVD drive
UpdateDvdrom()100 void CDetectDVDMedia::UpdateDvdrom()
101 {
102 // Signal for WaitMediaReady()
103 // that we are busy detecting the
104 // newly inserted media.
105 {
106 CSingleLock waitLock(m_muReadingMedia);
107 switch (GetTrayState())
108 {
109 case DRIVE_NONE:
110 //! @todo reduce / stop polling for drive updates
111 break;
112
113 case DRIVE_OPEN:
114 {
115 // Send Message to GUI that disc been ejected
116 SetNewDVDShareUrl("D:\\", false, g_localizeStrings.Get(502));
117 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REMOVED_MEDIA);
118 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage( msg );
119 waitLock.Leave();
120 m_DriveState = DRIVE_OPEN;
121 return;
122 }
123 break;
124
125 case DRIVE_NOT_READY:
126 {
127 // Drive is not ready (closing, opening)
128 SetNewDVDShareUrl("D:\\", false, g_localizeStrings.Get(503));
129 m_DriveState = DRIVE_NOT_READY;
130 // DVD-ROM in undefined state
131 // Better delete old CD Information
132 if ( m_pCdInfo != NULL )
133 {
134 delete m_pCdInfo;
135 m_pCdInfo = NULL;
136 }
137 waitLock.Leave();
138 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_SOURCES);
139 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage( msg );
140 // Do we really need sleep here? This will fix: [ 1530771 ] "Open tray" problem
141 // CThread::Sleep(6000);
142 return ;
143 }
144 break;
145
146 case DRIVE_CLOSED_NO_MEDIA:
147 {
148 // Nothing in there...
149 SetNewDVDShareUrl("D:\\", false, g_localizeStrings.Get(504));
150 m_DriveState = DRIVE_CLOSED_NO_MEDIA;
151 // Send Message to GUI that disc has changed
152 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_SOURCES);
153 waitLock.Leave();
154 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage( msg );
155 return ;
156 }
157 break;
158 case DRIVE_READY:
159 #if !defined(TARGET_DARWIN)
160 return ;
161 #endif
162 case DRIVE_CLOSED_MEDIA_PRESENT:
163 {
164 if ( m_DriveState != DRIVE_CLOSED_MEDIA_PRESENT)
165 {
166 m_DriveState = DRIVE_CLOSED_MEDIA_PRESENT;
167 // Detect ISO9660(mode1/mode2) or CDDA filesystem
168 DetectMediaType();
169 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_SOURCES);
170 waitLock.Leave();
171 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage( msg );
172 // Tell the application object that a new Cd is inserted
173 // So autorun can be started.
174 if ( !m_bStartup )
175 m_bAutorun = true;
176 }
177 return ;
178 }
179 break;
180 }
181
182 // We have finished media detection
183 // Signal for WaitMediaReady()
184 }
185
186
187 }
188
189 // Generates the drive url, (like iso9660://)
190 // from the CCdInfo class
DetectMediaType()191 void CDetectDVDMedia::DetectMediaType()
192 {
193 bool bCDDA(false);
194 CLog::Log(LOGINFO, "Detecting DVD-ROM media filesystem...");
195
196 std::string strNewUrl;
197 CCdIoSupport cdio;
198
199 // Delete old CD-Information
200 if ( m_pCdInfo != NULL )
201 {
202 delete m_pCdInfo;
203 m_pCdInfo = NULL;
204 }
205
206 // Detect new CD-Information
207 m_pCdInfo = cdio.GetCdInfo();
208 if (m_pCdInfo == NULL)
209 {
210 CLog::Log(LOGERROR, "Detection of DVD-ROM media failed.");
211 return ;
212 }
213 CLog::Log(LOGINFO, "Tracks overall:%i; Audio tracks:%i; Data tracks:%i",
214 m_pCdInfo->GetTrackCount(),
215 m_pCdInfo->GetAudioTrackCount(),
216 m_pCdInfo->GetDataTrackCount() );
217
218 // Detect ISO9660(mode1/mode2), CDDA filesystem or UDF
219 if (m_pCdInfo->IsISOHFS(1) || m_pCdInfo->IsIso9660(1) || m_pCdInfo->IsIso9660Interactive(1))
220 {
221 strNewUrl = "iso9660://";
222 }
223 else
224 {
225 if (m_pCdInfo->IsUDF(1))
226 strNewUrl = "D:\\";
227 else if (m_pCdInfo->IsAudio(1))
228 {
229 strNewUrl = "cdda://local/";
230 bCDDA = true;
231 }
232 else
233 strNewUrl = "D:\\";
234 }
235
236 if (m_pCdInfo->IsISOUDF(1))
237 {
238 if (!CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_detectAsUdf)
239 {
240 strNewUrl = "iso9660://";
241 }
242 else
243 {
244 strNewUrl = "D:\\";
245 }
246 }
247
248 CLog::Log(LOGINFO, "Using protocol %s", strNewUrl.c_str());
249
250 if (m_pCdInfo->IsValidFs())
251 {
252 if (!m_pCdInfo->IsAudio(1))
253 CLog::Log(LOGINFO, "Disc label: %s", m_pCdInfo->GetDiscLabel().c_str());
254 }
255 else
256 {
257 CLog::Log(LOGWARNING, "Filesystem is not supported");
258 }
259
260 std::string strLabel;
261 if (bCDDA)
262 {
263 strLabel = "Audio-CD";
264 }
265 else
266 {
267 strLabel = m_pCdInfo->GetDiscLabel();
268 StringUtils::TrimRight(strLabel);
269 }
270
271 SetNewDVDShareUrl( strNewUrl , bCDDA, strLabel);
272 }
273
SetNewDVDShareUrl(const std::string & strNewUrl,bool bCDDA,const std::string & strDiscLabel)274 void CDetectDVDMedia::SetNewDVDShareUrl( const std::string& strNewUrl, bool bCDDA, const std::string& strDiscLabel )
275 {
276 std::string strDescription = "DVD";
277 if (bCDDA) strDescription = "CD";
278
279 if (strDiscLabel != "") strDescription = strDiscLabel;
280
281 // Store it in case others want it
282 m_diskLabel = strDescription;
283 m_diskPath = strNewUrl;
284 }
285
GetTrayState()286 DWORD CDetectDVDMedia::GetTrayState()
287 {
288 #ifdef TARGET_POSIX
289
290 char* dvdDevice = m_cdio->GetDeviceFileName();
291 if (strlen(dvdDevice) == 0)
292 return DRIVE_NONE;
293
294 // The following code works with libcdio >= 0.78
295 // To enable it, download and install the latest version from
296 // http://www.gnu.org/software/libcdio/
297 // -d4rk 06/27/07
298
299
300 m_dwTrayState = TRAY_CLOSED_MEDIA_PRESENT;
301 CdIo_t* cdio = m_cdio->cdio_open(dvdDevice, DRIVER_UNKNOWN);
302 if (cdio)
303 {
304 static discmode_t discmode = CDIO_DISC_MODE_NO_INFO;
305 int status = m_cdio->mmc_get_tray_status(cdio);
306 static int laststatus = -1;
307 // 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)
308 if (status == 0 && (laststatus != status || discmode == CDIO_DISC_MODE_ERROR))
309 discmode = m_cdio->cdio_get_discmode(cdio);
310
311 switch(status)
312 {
313 case 0: //closed
314 if (discmode==CDIO_DISC_MODE_NO_INFO || discmode==CDIO_DISC_MODE_ERROR)
315 m_dwTrayState = TRAY_CLOSED_NO_MEDIA;
316 else
317 m_dwTrayState = TRAY_CLOSED_MEDIA_PRESENT;
318 break;
319
320 case 1: //open
321 m_dwTrayState = TRAY_OPEN;
322 break;
323 }
324 laststatus = status;
325 m_cdio->cdio_destroy(cdio);
326 }
327 else
328 return DRIVE_NOT_READY;
329
330 #endif // TARGET_POSIX
331
332 if (m_dwTrayState == TRAY_CLOSED_MEDIA_PRESENT)
333 {
334 if (m_dwLastTrayState != TRAY_CLOSED_MEDIA_PRESENT)
335 {
336 m_dwLastTrayState = m_dwTrayState;
337 return DRIVE_CLOSED_MEDIA_PRESENT;
338 }
339 else
340 {
341 return DRIVE_READY;
342 }
343 }
344 else if (m_dwTrayState == TRAY_CLOSED_NO_MEDIA)
345 {
346 if ( (m_dwLastTrayState != TRAY_CLOSED_NO_MEDIA) && (m_dwLastTrayState != TRAY_CLOSED_MEDIA_PRESENT) )
347 {
348 m_dwLastTrayState = m_dwTrayState;
349 return DRIVE_CLOSED_NO_MEDIA;
350 }
351 else
352 {
353 return DRIVE_READY;
354 }
355 }
356 else if (m_dwTrayState == TRAY_OPEN)
357 {
358 if (m_dwLastTrayState != TRAY_OPEN)
359 {
360 m_dwLastTrayState = m_dwTrayState;
361 return DRIVE_OPEN;
362 }
363 else
364 {
365 return DRIVE_READY;
366 }
367 }
368 else
369 {
370 m_dwLastTrayState = m_dwTrayState;
371 }
372
373 #ifdef HAS_DVD_DRIVE
374 return DRIVE_NOT_READY;
375 #else
376 return DRIVE_READY;
377 #endif
378 }
379
UpdateState()380 void CDetectDVDMedia::UpdateState()
381 {
382 CSingleLock waitLock(m_muReadingMedia);
383 m_pInstance->DetectMediaType();
384 }
385
386 // Static function
387 // Wait for drive, to finish media detection.
WaitMediaReady()388 void CDetectDVDMedia::WaitMediaReady()
389 {
390 CSingleLock waitLock(m_muReadingMedia);
391 }
392
393 // Static function
394 // Returns status of the DVD Drive
DriveReady()395 int CDetectDVDMedia::DriveReady()
396 {
397 return m_DriveState;
398 }
399
400 // Static function
401 // Whether a disc is in drive
IsDiscInDrive()402 bool CDetectDVDMedia::IsDiscInDrive()
403 {
404 return m_DriveState == DRIVE_CLOSED_MEDIA_PRESENT;
405 }
406
407 // Static function
408 // Returns a CCdInfo class, which contains
409 // Media information of the current inserted CD.
410 // Can be NULL
GetCdInfo()411 CCdInfo* CDetectDVDMedia::GetCdInfo()
412 {
413 CSingleLock waitLock(m_muReadingMedia);
414 CCdInfo* pCdInfo = m_pCdInfo;
415 return pCdInfo;
416 }
417
GetDVDLabel()418 const std::string &CDetectDVDMedia::GetDVDLabel()
419 {
420 return m_diskLabel;
421 }
422
GetDVDPath()423 const std::string &CDetectDVDMedia::GetDVDPath()
424 {
425 return m_diskPath;
426 }
427