2 * Copyright (C) 2005-2008 Team XBMC
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)
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.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
23 #include "XBDateTime.h"
25 #include "utils/URIUtils.h"
26 #include "DllLibCMyth.h"
28 #include "DirectoryCache.h"
29 #include "threads/SingleLock.h"
30 #include "utils/log.h"
31 #include "utils/TimeUtils.h"
34 #include "cmyth/include/cmyth/cmyth.h"
35 #include "cmyth/include/refmem/refmem.h"
38 using namespace XFILE;
41 static void prog_update_callback(cmyth_proginfo_t prog)
43 CLog::Log(LOGDEBUG, "%s - prog_update_callback", __FUNCTION__);
46 void CMythFile::OnEvent(int event, const string& data)
48 CSingleLock lock(m_section);
49 m_events.push(make_pair(event, data));
52 bool CMythFile::HandleEvents()
54 CSingleLock lock(m_section);
59 while(!m_events.empty())
61 int event = m_events.front().first;
62 string data = m_events.front().second;
68 case CMYTH_EVENT_CLOSE:
71 case CMYTH_EVENT_LIVETV_CHAIN_UPDATE:
74 m_dll->livetv_chain_update(m_recorder, (char*)data.c_str(), 4096);
84 bool CMythFile::SetupConnection(const CURL& url, bool control, bool event, bool database)
87 m_session = CMythSession::AquireSession(url);
94 m_dll = m_session->GetLibrary();
99 if(control && !m_control)
101 m_control = m_session->GetControl();
107 if(!m_session->SetListener(this))
110 if(database && !m_database)
112 m_database = m_session->GetDatabase();
120 bool CMythFile::SetupRecording(const CURL& url)
122 if (url.GetFileName().Left(11) != "recordings/" &&
123 url.GetFileName().Left(7) != "movies/" &&
124 url.GetFileName().Left(8) != "tvshows/")
127 if(!SetupConnection(url, true, false, false))
130 m_filename = url.GetFileNameWithoutPath();
132 m_program = m_dll->proginfo_get_from_basename(m_control, m_filename.c_str());
135 CLog::Log(LOGERROR, "%s - unable to get find selected file", __FUNCTION__);
139 m_file = m_dll->conn_connect_file(m_program, m_control, 16*1024, 4096);
142 CLog::Log(LOGERROR, "%s - unable to connect to file", __FUNCTION__);
147 * proginfo_get_from_basename doesn't return the recording status. Hopefully this will be added to
148 * mythbackend eventually.
150 * Since cycling through the recorders to check if the program is recording takes some time
151 * (depending on the MythTV backend configuration), make some assumptions based on the recording
152 * end time since nearly all recordings opened won't be recording.
155 CDateTime start = GetValue(m_dll->proginfo_rec_start(m_program));
156 CDateTime end = GetValue(m_dll->proginfo_rec_end(m_program));
157 if (end > start // Assume could be recording if empty date comes back as the epoch
158 && end < CDateTime::GetCurrentDateTime())
159 CLog::Log(LOGDEBUG, "%s - Assumed not recording since recording end time before current time: %s",
160 __FUNCTION__, end.GetAsLocalizedDateTime().c_str());
163 CLog::Log(LOGDEBUG, "%s - Checking recording status using tuners since recording end time NULL or before current time: %s",
164 __FUNCTION__, end.GetAsLocalizedDateTime().c_str());
165 for(int i=0;i<16 && !m_recording;i++)
167 cmyth_recorder_t recorder = m_dll->conn_get_recorder_from_num(m_control, i);
170 if(m_dll->recorder_is_recording(recorder))
172 cmyth_proginfo_t program = m_dll->recorder_get_cur_proginfo(recorder);
174 if(m_dll->proginfo_compare(program, m_program) == 0)
176 m_dll->ref_release(program);
178 m_dll->ref_release(recorder);
183 CLog::Log(LOGDEBUG, "%s - Currently recording: %s", __FUNCTION__, m_filename.c_str());
188 bool CMythFile::SetupLiveTV(const CURL& url)
190 if (url.GetFileName().Left(9) != "channels/")
193 if(!SetupConnection(url, true, true, true))
196 CStdString channel = url.GetFileNameWithoutPath();
197 if(!URIUtils::GetExtension(channel).Equals(".ts"))
199 CLog::Log(LOGERROR, "%s - invalid channel url %s", __FUNCTION__, channel.c_str());
202 URIUtils::RemoveExtension(channel);
204 for(int i=0;i<16;i++)
206 m_recorder = m_dll->conn_get_recorder_from_num(m_control, i);
210 if(m_dll->recorder_is_recording(m_recorder))
212 /* for now don't allow reuse of tuners, we would have to change tuner on channel *
213 * and make sure we don't stop the tuner when stopping playback as that affects *
216 /* if already recording, check if it is this channel */
217 cmyth_proginfo_t program;
218 program = m_dll->recorder_get_cur_proginfo(m_recorder);
221 if(channel == GetValue(m_dll->proginfo_chanstr(program)))
223 m_dll->ref_release(program);
226 m_dll->ref_release(program);
232 /* not recording, check if it supports this channel */
233 if(m_dll->recorder_check_channel(m_recorder, (char*)channel.c_str()) == 0)
236 m_dll->ref_release(m_recorder);
242 CLog::Log(LOGERROR, "%s - unable to get recorder", __FUNCTION__);
246 m_recording = !!m_dll->recorder_is_recording(m_recorder);
248 CLog::Log(LOGDEBUG, "%s - recorder isn't running, let's start it", __FUNCTION__);
251 if(!(m_recorder = m_dll->spawn_live_tv(m_recorder, 16*1024, 4096, prog_update_callback, &msg, (char*)channel.c_str())))
253 CLog::Log(LOGERROR, "%s - unable to spawn live tv: %s", __FUNCTION__, msg ? msg : "");
257 m_program = m_dll->recorder_get_cur_proginfo(m_recorder);
258 m_timestamp = CTimeUtils::GetTimeMS();
260 m_starttime = m_dll->proginfo_rec_start(m_program);
264 /* recorder was running when we started, seek to last position */
265 if(!m_dll->livetv_seek(m_recorder, 0, SEEK_END))
266 CLog::Log(LOGDEBUG, "%s - failed to seek to last position", __FUNCTION__);
269 m_filename = GetValue(m_dll->recorder_get_filename(m_recorder));
273 bool CMythFile::SetupFile(const CURL& url)
275 if (url.GetFileName().Left(6) != "files/")
278 if(!SetupConnection(url, true, false, false))
281 m_filename = url.GetFileName().Mid(6);
283 m_file = m_dll->conn_connect_path((char*)m_filename.c_str(), m_control, 16*1024, 4096);
286 CLog::Log(LOGERROR, "%s - unable to connect to file", __FUNCTION__);
290 if(m_dll->file_length(m_file) == 0)
292 CLog::Log(LOGERROR, "%s - file is empty, probably doesn't even exist", __FUNCTION__);
299 bool CMythFile::Open(const CURL& url)
303 CStdString path(url.GetFileName());
305 if (path.Left(11) == "recordings/" ||
306 path.Left(7) == "movies/" ||
307 path.Left(8) == "tvshows/")
309 if(!SetupRecording(url))
312 CLog::Log(LOGDEBUG, "%s - file: size %"PRIu64", start %"PRIu64", ", __FUNCTION__, (int64_t)m_dll->file_length(m_file), (int64_t)m_dll->file_start(m_file));
314 else if (path.Left(9) == "channels/")
317 if(!SetupLiveTV(url))
320 CLog::Log(LOGDEBUG, "%s - recorder has started on filename %s", __FUNCTION__, m_filename.c_str());
322 else if (path.Left(6) == "files/")
327 CLog::Log(LOGDEBUG, "%s - file: size %"PRId64", start %"PRId64", ", __FUNCTION__, (int64_t)m_dll->file_length(m_file), (int64_t)m_dll->file_start(m_file));
331 CLog::Log(LOGERROR, "%s - invalid path specified %s", __FUNCTION__, path.c_str());
335 /* check for any events */
342 void CMythFile::Close()
349 m_dll->ref_release(m_starttime);
354 m_dll->ref_release(m_program);
359 m_dll->recorder_stop_livetv(m_recorder);
360 m_dll->ref_release(m_recorder);
365 m_dll->ref_release(m_file);
370 m_session->SetListener(NULL);
371 CMythSession::ReleaseSession(m_session);
376 CMythFile::CMythFile()
390 CMythFile::~CMythFile()
395 bool CMythFile::Exists(const CURL& url)
397 CStdString path(url.GetFileName());
400 * mythbackend provides access to the .mpg or .nuv recordings. The associated thumbnails
401 * (*.mpg.png or *.nuv.png) and channel icons, which are an arbitrary image format, are requested
402 * through the files/ path.
404 if ((path.Left(11) == "recordings/"
405 || path.Left(7) == "movies/"
406 || path.Left(8) == "tvshows/")
407 && (URIUtils::GetExtension(path).Equals(".mpg")
408 || URIUtils::GetExtension(path).Equals(".nuv")))
410 if(!SetupConnection(url, true, false, false))
413 m_filename = url.GetFileNameWithoutPath();
414 m_program = m_dll->proginfo_get_from_basename(m_control, m_filename.c_str());
417 CLog::Log(LOGERROR, "%s - unable to get find %s", __FUNCTION__, m_filename.c_str());
422 else if(path.Left(6) == "files/")
428 bool CMythFile::Delete(const CURL& url)
430 CStdString path(url.GetFileName());
432 if (path.Left(11) == "recordings/" ||
433 path.Left(7) == "movies/" ||
434 path.Left(8) == "tvshows/")
436 /* this will setup all interal variables */
442 if(m_dll->proginfo_delete_recording(m_control, m_program))
444 CLog::Log(LOGDEBUG, "%s - Error deleting recording: %s", __FUNCTION__, url.GetFileName().c_str());
448 if (path.Left(8) == "tvshows/")
451 * Clear the directory cache for the TV Shows folder so the listing is accurate if this was
452 * the last TV Show in the current directory that was deleted.
455 tvshows.SetFileName("tvshows/");
456 g_directoryCache.ClearDirectory(tvshows.Get());
464 int64_t CMythFile::Seek(int64_t pos, int whence)
466 CLog::Log(LOGDEBUG, "%s - seek to pos %"PRId64", whence %d", __FUNCTION__, pos, whence);
468 if(whence == SEEK_POSSIBLE)
478 result = -1; //m_dll->livetv_seek(m_recorder, pos, whence);
480 result = m_dll->file_seek(m_file, pos, whence);
487 int64_t CMythFile::GetPosition()
490 return m_dll->livetv_seek(m_recorder, 0, SEEK_CUR);
492 return m_dll->file_seek(m_file, 0, SEEK_CUR);
496 int64_t CMythFile::GetLength()
499 return m_dll->file_length(m_file);
503 unsigned int CMythFile::Read(void* buffer, int64_t size)
505 /* check for any events */
508 /* file might have gotten closed */
509 if(!m_recorder && !m_file)
514 ret = m_dll->livetv_read(m_recorder, (char*)buffer, (unsigned long)size);
516 ret = m_dll->file_read(m_file, (char*)buffer, (unsigned long)size);
520 CLog::Log(LOGERROR, "%s - cmyth read returned error %d", __FUNCTION__, ret);
526 bool CMythFile::SkipNext()
530 return m_dll->recorder_is_recording(m_recorder) > 0;
535 bool CMythFile::UpdateItem(CFileItem& item)
538 * UpdateItem should only return true if a LiveTV item has changed via a channel change, or the
539 * program being aired on the current channel has changed requiring the UI to update the currently
540 * playing information.
542 * Check by comparing the current title with the new title.
547 if (!m_program || !m_session)
550 CStdString title = item.m_strTitle;
551 m_session->SetFileItemMetaData(item, m_program);
552 return title != item.m_strTitle;
555 int CMythFile::GetTotalTime()
557 if(m_recorder && m_timestamp + 5000 < CTimeUtils::GetTimeMS())
559 m_timestamp = CTimeUtils::GetTimeMS();
561 m_dll->ref_release(m_program);
562 m_program = m_dll->recorder_get_cur_proginfo(m_recorder);
565 if(m_program && m_recorder)
566 return m_dll->proginfo_length_sec(m_program) * 1000;
571 int CMythFile::GetStartTime()
573 if(m_program && m_recorder && m_starttime)
575 cmyth_timestamp_t start = m_dll->proginfo_start(m_program);
577 double diff = difftime(m_dll->timestamp_to_unixtime(start), m_dll->timestamp_to_unixtime(m_starttime));
579 m_dll->ref_release(start);
581 return (int)(diff * 1000);
586 bool CMythFile::ChangeChannel(int direction, const CStdString &channel)
588 CLog::Log(LOGDEBUG, "%s - channel change started", __FUNCTION__);
590 if(direction == CHANNEL_DIRECTION_SAME)
592 if(!m_program || channel != GetValue(m_dll->proginfo_chanstr(m_program)))
594 if(m_dll->recorder_pause(m_recorder) < 0)
596 CLog::Log(LOGDEBUG, "%s - failed to pause recorder", __FUNCTION__);
600 CLog::Log(LOGDEBUG, "%s - chainging channel to %s", __FUNCTION__, channel.c_str());
601 if(m_dll->recorder_set_channel(m_recorder, (char*)channel.c_str()) < 0)
603 CLog::Log(LOGDEBUG, "%s - failed to change channel", __FUNCTION__);
610 if(m_dll->recorder_pause(m_recorder) < 0)
612 CLog::Log(LOGDEBUG, "%s - failed to pause recorder", __FUNCTION__);
616 CLog::Log(LOGDEBUG, "%s - chainging channel direction %d", __FUNCTION__, direction);
617 if(m_dll->recorder_change_channel(m_recorder, (cmyth_channeldir_t)direction) < 0)
619 CLog::Log(LOGDEBUG, "%s - failed to change channel", __FUNCTION__);
624 if(!m_dll->livetv_chain_switch_last(m_recorder))
625 CLog::Log(LOGDEBUG, "%s - failed to change to last item in chain", __FUNCTION__);
628 m_dll->ref_release(m_program);
629 m_program = m_dll->recorder_get_cur_proginfo(m_recorder);
631 CLog::Log(LOGDEBUG, "%s - channel change done", __FUNCTION__);
635 bool CMythFile::NextChannel()
637 return ChangeChannel(CHANNEL_DIRECTION_UP, "");
640 bool CMythFile::PrevChannel()
642 return ChangeChannel(CHANNEL_DIRECTION_DOWN, "");
645 bool CMythFile::SelectChannel(unsigned int channel)
647 return ChangeChannel(CHANNEL_DIRECTION_SAME,""+channel);
650 bool CMythFile::CanRecord()
652 if(m_recorder || m_recording)
658 bool CMythFile::IsRecording()
663 bool CMythFile::Record(bool bOnOff)
672 ret = m_dll->livetv_keep_recording(m_recorder, m_database, 1);
674 ret = m_dll->livetv_keep_recording(m_recorder, m_database, 0);
678 CLog::Log(LOGERROR, "%s - failed to turn on recording", __FUNCTION__);
682 m_recording = bOnOff;
689 if(m_dll->proginfo_stop_recording(m_control, m_program) < 0)
699 bool CMythFile::GetCommBreakList(cmyth_commbreaklist_t& commbreaklist)
703 commbreaklist = m_dll->get_commbreaklist(m_control, m_program);
709 bool CMythFile::GetCutList(cmyth_commbreaklist_t& commbreaklist)
713 commbreaklist = m_dll->get_cutlist(m_control, m_program);