22 #include "mythlivetvplayback.h" 23 #include "private/debug.h" 24 #include "private/socket.h" 25 #include "private/os/threads/mutex.h" 26 #include "private/os/threads/timeout.h" 27 #include "private/builtin.h" 33 #define MIN_TUNE_DELAY 5 34 #define MAX_TUNE_DELAY 60 35 #define TICK_USEC 100000 // valid range: 10000 - 999999 36 #define START_TIMEOUT 2000 // millisec 37 #define AHEAD_TIMEOUT 10000 // millisec 38 #define BREAK_TIMEOUT 4000 // millisec 49 , m_eventHandler(handler)
50 , m_eventSubscriberId(0)
51 , m_tuneDelay(MIN_TUNE_DELAY)
52 , m_limitTuneAttempts(true)
56 , m_chunk(MYTH_LIVETV_CHUNK_SIZE)
60 m_buffer.data =
new unsigned char[m_chunk];
61 m_eventSubscriberId = m_eventHandler.CreateSubscription(
this);
62 m_eventHandler.SubscribeForEvent(m_eventSubscriberId, EVENT_SIGNAL);
63 m_eventHandler.SubscribeForEvent(m_eventSubscriberId, EVENT_LIVETV_CHAIN);
64 m_eventHandler.SubscribeForEvent(m_eventSubscriberId, EVENT_LIVETV_WATCH);
65 m_eventHandler.SubscribeForEvent(m_eventSubscriberId, EVENT_DONE_RECORDING);
66 m_eventHandler.SubscribeForEvent(m_eventSubscriberId, EVENT_UPDATE_FILE_SIZE);
70 LiveTVPlayback::LiveTVPlayback(
const std::string& server,
unsigned port)
72 , m_eventHandler(server, port)
73 , m_eventSubscriberId(0)
74 , m_tuneDelay(MIN_TUNE_DELAY)
78 , m_chunk(MYTH_LIVETV_CHUNK_SIZE)
82 m_buffer.data =
new unsigned char[m_chunk];
84 m_eventSubscriberId = m_eventHandler.CreateSubscription(
this);
85 m_eventHandler.SubscribeForEvent(m_eventSubscriberId, EVENT_SIGNAL);
86 m_eventHandler.SubscribeForEvent(m_eventSubscriberId, EVENT_LIVETV_CHAIN);
87 m_eventHandler.SubscribeForEvent(m_eventSubscriberId, EVENT_LIVETV_WATCH);
88 m_eventHandler.SubscribeForEvent(m_eventSubscriberId, EVENT_DONE_RECORDING);
89 m_eventHandler.SubscribeForEvent(m_eventSubscriberId, EVENT_UPDATE_FILE_SIZE);
93 LiveTVPlayback::~LiveTVPlayback()
95 if (m_eventSubscriberId)
96 m_eventHandler.RevokeSubscription(m_eventSubscriberId);
98 delete[] m_buffer.data;
101 bool LiveTVPlayback::Open()
104 OS::CLockGuard lock(*m_mutex);
105 if (ProtoMonitor::IsOpen())
107 if (ProtoMonitor::Open())
109 if (!m_eventHandler.IsRunning())
111 OS::CTimeout timeout(START_TIMEOUT);
112 m_eventHandler.Start();
117 while (!m_eventHandler.IsConnected() && timeout.TimeLeft() > 0);
118 if (!m_eventHandler.IsConnected())
119 DBG(DBG_WARN,
"%s: event handler is not connected in time\n", __FUNCTION__);
121 DBG(DBG_DEBUG,
"%s: event handler is connected\n", __FUNCTION__);
128 void LiveTVPlayback::Close()
131 OS::CLockGuard lock(*m_mutex);
133 ProtoMonitor::Close();
136 void LiveTVPlayback::SetTuneDelay(
unsigned delay)
138 if (delay < MIN_TUNE_DELAY)
139 m_tuneDelay = MIN_TUNE_DELAY;
140 else if (delay > MAX_TUNE_DELAY)
141 m_tuneDelay = MAX_TUNE_DELAY;
146 void LiveTVPlayback::SetLimitTuneAttempts(
bool limit)
150 m_limitTuneAttempts = limit;
153 bool LiveTVPlayback::SpawnLiveTV(
const std::string& chanNum,
const ChannelList& channels)
156 OS::CLockGuard lock(*m_mutex);
157 if (!ProtoMonitor::IsOpen() || !m_eventHandler.IsConnected())
159 DBG(DBG_ERROR,
"%s: not connected\n", __FUNCTION__);
164 preferredCards_t preferredCards = FindTunableCardIds(chanNum, channels);
165 preferredCards_t::const_iterator card = preferredCards.begin();
166 while (card != preferredCards.end())
169 const CardInputPtr& input = card->second.first;
170 const ChannelPtr& channel = card->second.second;
171 DBG(DBG_DEBUG,
"%s: trying recorder num (%" PRIu32
") channum (%s)\n", __FUNCTION__, input->cardId, channel->chanNum.c_str());
172 m_recorder = GetRecorderFromNum((
int) input->cardId);
174 m_chain.switchOnCreate =
true;
175 m_chain.watch =
true;
176 if (m_recorder->SpawnLiveTV(m_chain.UID, channel->chanNum))
179 uint32_t delayMs = m_tuneDelay * 1000;
180 OS::CTimeout timeout(delayMs);
186 if (!m_chain.switchOnCreate)
188 DBG(DBG_DEBUG,
"%s: tune delay (%" PRIu32
"ms)\n", __FUNCTION__, (delayMs - timeout.TimeLeft()));
192 while (timeout.TimeLeft() > 0);
193 DBG(DBG_ERROR,
"%s: tune delay exceeded (%" PRIu32
"ms)\n", __FUNCTION__, delayMs);
194 m_recorder->StopLiveTV();
198 if (m_limitTuneAttempts)
200 DBG(DBG_DEBUG,
"%s: limiting tune attempts to first tunable card\n", __FUNCTION__);
209 bool LiveTVPlayback::SpawnLiveTV(
const ChannelPtr& thisChannel)
212 list.push_back(thisChannel);
213 return SpawnLiveTV(thisChannel->chanNum, list);
216 void LiveTVPlayback::StopLiveTV()
219 OS::CLockGuard lock(*m_mutex);
220 if (m_recorder && m_recorder->IsPlaying())
222 m_recorder->StopLiveTV();
225 if (m_recorder->IsLiveRecording())
230 void LiveTVPlayback::InitChain()
234 OS::CLockGuard lock(*m_mutex);
235 time_to_iso8601(time(NULL), buf);
236 m_chain.UID = m_socket->GetMyHostName();
237 m_chain.UID.append(
"-").append(buf);
238 m_chain.currentSequence = 0;
239 m_chain.lastSequence = 0;
240 m_chain.watch =
false;
241 m_chain.switchOnCreate =
true;
242 m_chain.chained.clear();
243 m_chain.currentTransfer.reset();
246 void LiveTVPlayback::ClearChain()
249 OS::CLockGuard lock(*m_mutex);
250 m_chain.currentSequence = 0;
251 m_chain.lastSequence = 0;
252 m_chain.watch =
false;
253 m_chain.switchOnCreate =
false;
254 m_chain.chained.clear();
255 m_chain.currentTransfer.reset();
258 bool LiveTVPlayback::IsChained(
const Program& program)
260 for (chained_t::const_iterator it = m_chain.chained.begin(); it != m_chain.chained.end(); ++it)
262 if (it->first && it->first->GetPathName() == program.fileName)
268 void LiveTVPlayback::HandleChainUpdate()
270 OS::CLockGuard lock(*m_mutex);
271 ProtoRecorderPtr recorder(m_recorder);
274 ProgramPtr prog = recorder->GetCurrentRecording();
279 if (prog && !prog->fileName.empty() && !IsChained(*prog))
281 DBG(DBG_DEBUG,
"%s: liveTV (%s): adding new transfer %s\n", __FUNCTION__,
282 m_chain.UID.c_str(), prog->fileName.c_str());
283 ProtoTransferPtr transfer(
new ProtoTransfer(recorder->GetServer(), recorder->GetPort(), prog->fileName, prog->recording.storageGroup));
285 if (m_chain.lastSequence && m_chain.chained[m_chain.lastSequence - 1].first->GetSize() == 0)
287 --m_chain.lastSequence;
288 m_chain.chained.pop_back();
290 m_chain.chained.push_back(std::make_pair(transfer, prog));
291 m_chain.lastSequence = m_chain.chained.size();
296 if (m_chain.switchOnCreate && transfer->GetSize() > 0 && SwitchChainLast())
297 m_chain.switchOnCreate =
false;
298 m_chain.watch =
false;
299 DBG(DBG_DEBUG,
"%s: liveTV (%s): chain last (%u), watching (%u)\n", __FUNCTION__,
300 m_chain.UID.c_str(), m_chain.lastSequence, m_chain.currentSequence);
304 bool LiveTVPlayback::SwitchChain(
unsigned sequence)
306 OS::CLockGuard lock(*m_mutex);
308 if (sequence < 1 || sequence > m_chain.lastSequence)
311 if (!m_chain.chained[sequence - 1].first->IsOpen() && !m_chain.chained[sequence - 1].first->Open())
313 m_chain.currentTransfer = m_chain.chained[sequence - 1].first;
314 m_chain.currentSequence = sequence;
315 DBG(DBG_DEBUG,
"%s: switch to file (%u) %s\n", __FUNCTION__,
316 (
unsigned)m_chain.currentTransfer->GetFileId(), m_chain.currentTransfer->GetPathName().c_str());
320 bool LiveTVPlayback::SwitchChainLast()
322 if (SwitchChain(m_chain.lastSequence))
324 ProtoRecorderPtr recorder(m_recorder);
325 ProtoTransferPtr transfer(m_chain.currentTransfer);
326 if (recorder && transfer && recorder->TransferSeek(*transfer, 0, WHENCE_SET) == 0)
332 void LiveTVPlayback::HandleBackendMessage(EventMessagePtr msg)
334 ProtoRecorderPtr recorder(m_recorder);
335 if (!recorder || !recorder->IsPlaying())
347 case EVENT_LIVETV_CHAIN:
348 if (msg->subject.size() >= 3)
350 if (msg->subject[1] ==
"UPDATE" && msg->subject[2] == m_chain.UID)
368 case EVENT_LIVETV_WATCH:
369 if (msg->subject.size() >= 3)
373 if (string_to_int32(msg->subject[1].c_str(), &rnum) == 0 && string_to_int8(msg->subject[2].c_str(), &flag) == 0)
375 if (recorder->GetNum() == (int)rnum)
377 OS::CLockGuard lock(*m_mutex);
378 m_chain.watch =
true;
393 case EVENT_DONE_RECORDING:
394 if (msg->subject.size() >= 2)
397 if (string_to_int32(msg->subject[1].c_str(), &rnum) == 0 && recorder->GetNum() == (int)rnum)
400 recorder->DoneRecordingCallback();
409 OS::CTimeout timeout(BREAK_TIMEOUT);
415 while (m_chain.watch && timeout.TimeLeft() > 0);
420 case EVENT_UPDATE_FILE_SIZE:
421 if (msg->subject.size() >= 3)
423 OS::CLockGuard lock(*m_mutex);
424 if (m_chain.lastSequence > 0)
428 if (msg->subject.size() >= 4)
432 if (string_to_uint32(msg->subject[1].c_str(), &chanid)
433 || string_to_time(msg->subject[2].c_str(), &startts)
434 || m_chain.chained[m_chain.lastSequence -1].second->channel.chanId != chanid
435 || m_chain.chained[m_chain.lastSequence -1].second->recording.startTs != startts
436 || string_to_int64(msg->subject[3].c_str(), &newsize)
437 || m_chain.chained[m_chain.lastSequence - 1].first->GetSize() >= newsize)
444 if (string_to_uint32(msg->subject[1].c_str(), &recordedid)
445 || m_chain.chained[m_chain.lastSequence -1].second->recording.recordedId != recordedid
446 || string_to_int64(msg->subject[2].c_str(), &newsize)
447 || m_chain.chained[m_chain.lastSequence - 1].first->GetSize() >= newsize)
451 m_chain.chained[m_chain.lastSequence - 1].first->SetSize(newsize);
453 if (m_chain.switchOnCreate && SwitchChainLast())
454 m_chain.switchOnCreate =
false;
455 DBG(DBG_DEBUG,
"%s: liveTV (%s): chain last (%u) filesize %" PRIi64
"\n", __FUNCTION__,
456 m_chain.UID.c_str(), m_chain.lastSequence, newsize);
461 if (msg->subject.size() >= 2)
464 if (string_to_int32(msg->subject[1].c_str(), &rnum) == 0 && recorder->GetNum() == (int)rnum)
465 m_signal = msg->signal;
477 void LiveTVPlayback::SetChunk(
unsigned size)
479 if (size < MYTH_LIVETV_CHUNK_MIN)
480 size = MYTH_LIVETV_CHUNK_MIN;
481 else if (size > MYTH_LIVETV_CHUNK_MAX)
482 size = MYTH_LIVETV_CHUNK_MAX;
484 m_buffer.pos = m_buffer.len = 0;
485 delete[] m_buffer.data;
486 m_buffer.data =
new unsigned char[size];
490 int64_t LiveTVPlayback::GetSize()
const 493 OS::CLockGuard lock(*m_mutex);
494 for (chained_t::const_iterator it = m_chain.chained.begin(); it != m_chain.chained.end(); ++it)
495 size += it->first->GetSize();
499 int LiveTVPlayback::Read(
void* buffer,
unsigned n)
506 if (m_buffer.len >= n)
508 memcpy(static_cast<unsigned char*>(buffer) + c, m_buffer.data + m_buffer.pos, n);
515 if (m_buffer.len > 0)
517 memcpy(static_cast<unsigned char*>(buffer) + c, m_buffer.data + m_buffer.pos, m_buffer.len);
525 int r = _read(m_buffer.data, m_chunk);
534 int LiveTVPlayback::_read(
void* buffer,
unsigned n)
542 ProtoRecorderPtr recorder(m_recorder);
543 if (!m_chain.currentTransfer || !recorder)
546 fp = m_chain.currentTransfer->GetPosition();
551 s = m_chain.currentTransfer->GetRemaining();
554 OS::CTimeout timeout(AHEAD_TIMEOUT);
558 if (m_chain.currentSequence == m_chain.lastSequence)
560 int64_t rp = recorder->GetFilePosition();
563 m_chain.currentTransfer->SetSize(rp);
567 if (!timeout.TimeLeft())
569 DBG(DBG_WARN,
"%s: read position is ahead (%" PRIi64
")\n", __FUNCTION__, fp);
577 if (!SwitchChain(m_chain.currentSequence + 1))
579 if (m_chain.currentTransfer->GetPosition() != 0)
580 recorder->TransferSeek(*(m_chain.currentTransfer), 0, WHENCE_SET);
581 DBG(DBG_DEBUG,
"%s: liveTV (%s): chain last (%u), watching (%u)\n", __FUNCTION__,
582 m_chain.UID.c_str(), m_chain.lastSequence, m_chain.currentSequence);
596 r = recorder->TransferRequestBlock(*(m_chain.currentTransfer), buffer, n);
600 int64_t LiveTVPlayback::Seek(int64_t offset, WHENCE_t whence)
602 if (whence == WHENCE_CUR)
606 int64_t p = _seek(offset, whence);
608 return (p >= m_buffer.len ? p - m_buffer.len : p);
611 offset -= m_buffer.len;
614 return _seek(offset, whence);
617 int64_t LiveTVPlayback::_seek(int64_t offset, WHENCE_t whence)
619 OS::CLockGuard lock(*m_mutex);
620 if (!m_recorder || !m_chain.currentSequence)
623 unsigned ci = m_chain.currentSequence - 1;
624 int64_t size = GetSize();
625 int64_t position = GetPosition();
636 p = position + offset;
641 if (p > size || p < 0)
643 DBG(DBG_WARN,
"%s: invalid seek (%" PRId64
")\n", __FUNCTION__, p);
650 if (position + m_chain.chained[ci].first->GetRemaining() >= p)
653 if (m_recorder->TransferSeek(*(m_chain.chained[ci].first), p - position, WHENCE_CUR) < 0 ||
658 position += m_chain.chained[ci].first->GetRemaining();
660 if (ci < m_chain.lastSequence)
661 position += m_chain.chained[ci].first->GetPosition();
670 if (position - m_chain.chained[ci].first->GetPosition() <= p)
673 if (m_recorder->TransferSeek(*(m_chain.chained[ci].first), p - position, WHENCE_CUR) < 0 ||
678 position -= m_chain.chained[ci].first->GetPosition();
682 position -= m_chain.chained[ci].first->GetRemaining();
692 int64_t LiveTVPlayback::GetPosition()
const 695 OS::CLockGuard lock(*m_mutex);
696 if (m_chain.currentSequence)
698 unsigned s = m_chain.currentSequence - 1;
699 for (
unsigned i = 0; i < s; ++i)
700 pos += m_chain.chained[i].first->GetSize();
701 pos += m_chain.currentTransfer->GetPosition();
704 return pos - m_buffer.len;
707 bool LiveTVPlayback::IsPlaying()
const 709 ProtoRecorderPtr recorder(m_recorder);
710 return (recorder ? recorder->IsPlaying() :
false);
713 bool LiveTVPlayback::IsLiveRecording()
const 715 ProtoRecorderPtr recorder(m_recorder);
716 return (recorder ? recorder->IsLiveRecording() :
false);
719 bool LiveTVPlayback::KeepLiveRecording(
bool keep)
721 ProtoRecorderPtr recorder(m_recorder);
723 OS::CLockGuard lock(*m_mutex);
724 if (recorder && recorder->IsPlaying())
726 ProgramPtr prog = recorder->GetCurrentRecording();
731 if (UndeleteRecording(*prog) && recorder->SetLiveRecording(keep))
733 QueryGenpixmap(*prog);
739 if (recorder->SetLiveRecording(keep) && recorder->FinishRecording())
747 ProgramPtr LiveTVPlayback::GetPlayedProgram()
const 749 OS::CLockGuard lock(*m_mutex);
750 if (m_chain.currentSequence > 0)
751 return m_chain.chained[m_chain.currentSequence - 1].second;
755 time_t LiveTVPlayback::GetLiveTimeStart()
const 757 OS::CLockGuard lock(*m_mutex);
758 if (m_chain.lastSequence)
759 return m_chain.chained[0].second->recording.startTs;
763 unsigned LiveTVPlayback::GetChainedCount()
const 765 OS::CLockGuard lock(*m_mutex);
766 return m_chain.lastSequence;
769 ProgramPtr LiveTVPlayback::GetChainedProgram(
unsigned sequence)
const 771 OS::CLockGuard lock(*m_mutex);
772 if (sequence > 0 && sequence <= m_chain.lastSequence)
773 return m_chain.chained[sequence - 1].second;
777 uint32_t LiveTVPlayback::GetCardId()
const 779 ProtoRecorderPtr recorder(m_recorder);
780 return (recorder ? recorder->GetNum() : 0);
783 SignalStatusPtr LiveTVPlayback::GetSignal()
const 785 return (m_recorder ? m_signal : SignalStatusPtr());
788 LiveTVPlayback::preferredCards_t LiveTVPlayback::FindTunableCardIds(
const std::string& chanNum,
const ChannelList& channels)
792 for (ChannelList::const_iterator it = channels.begin(); it != channels.end(); ++it)
794 if ((*it)->chanNum == chanNum)
795 chanset.push_back(*it);
800 preferredCards_t preferredCards;
801 CardInputListPtr inputs = GetFreeInputs(0);
802 for (CardInputList::const_iterator iti = inputs->begin(); iti != inputs->end(); ++iti)
804 for (ChannelList::const_iterator itchan = chanset.begin(); itchan != chanset.end(); ++itchan)
806 if ((*itchan)->sourceId == (*iti)->sourceId && ( (*iti)->mplexId == 0 || (*iti)->mplexId == (*itchan)->mplexId ))
808 preferredCards.insert(std::make_pair((*iti)->liveTVOrder, std::make_pair(*iti, *itchan)));
809 DBG(DBG_DEBUG,
"%s: [%u] channel=%s(%" PRIu32
") card=%" PRIu32
" input=%s(%" PRIu32
") mplex=%" PRIu32
" source=%" PRIu32
"\n",
810 __FUNCTION__, (*iti)->liveTVOrder, (*itchan)->callSign.c_str(), (*itchan)->chanId,
811 (*iti)->cardId, (*iti)->inputName.c_str(), (*iti)->inputId, (*iti)->mplexId, (*iti)->sourceId);
816 return preferredCards;
This is the main namespace that encloses all public classes.