/* * Copyright (C) 2005-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with XBMC; see the file COPYING. If not, see * . * */ #include "DVDCodecs/DVDCodecs.h" #include "DVDInputStreams/DVDInputStream.h" #include "DVDInputStreams/DVDInputStreamHTSP.h" #include "DVDDemuxHTSP.h" #include "DVDDemuxUtils.h" #include "DVDClock.h" #include "dialogs/GUIDialogKaiToast.h" #include "utils/log.h" #include "utils/StringUtils.h" #include extern "C" { #include "lib/libhts/net.h" #include "lib/libhts/htsmsg.h" #include "lib/libhts/htsmsg_binary.h" } using namespace std; using namespace HTSP; class CDemuxStreamVideoHTSP : public CDemuxStreamVideo { CDVDDemuxHTSP *m_parent; string m_codec; public: CDemuxStreamVideoHTSP(CDVDDemuxHTSP *parent, const string& codec) : m_parent(parent) , m_codec(codec) {} void GetStreamInfo(std::string& strInfo) { strInfo = StringUtils::Format("%s, delay: %u, drops: %ub %up %ui" , m_codec.c_str() , m_parent->m_QueueStatus.delay , m_parent->m_QueueStatus.bdrops , m_parent->m_QueueStatus.pdrops , m_parent->m_QueueStatus.idrops); } }; class CDemuxStreamAudioHTSP : public CDemuxStreamAudio { CDVDDemuxHTSP *m_parent; string m_codec; public: CDemuxStreamAudioHTSP(CDVDDemuxHTSP *parent, const string& codec) : m_parent(parent) , m_codec(codec) {} void GetStreamInfo(string& strInfo) { strInfo = StringUtils::Format("%s", m_codec.c_str()); } }; CDVDDemuxHTSP::CDVDDemuxHTSP() : CDVDDemux() , m_Input(NULL) , m_StatusCount(0) { } CDVDDemuxHTSP::~CDVDDemuxHTSP() { Dispose(); } bool CDVDDemuxHTSP::Open(CDVDInputStream* input) { Dispose(); if(!input->IsStreamType(DVDSTREAM_TYPE_HTSP)) return false; m_Input = (CDVDInputStreamHTSP*)input; m_StatusCount = 0; while(m_Streams.empty() && m_StatusCount == 0) { DemuxPacket* pkg = Read(); if(!pkg) return false; CDVDDemuxUtils::FreeDemuxPacket(pkg); } return true; } void CDVDDemuxHTSP::Dispose() { } void CDVDDemuxHTSP::Reset() { } void CDVDDemuxHTSP::Flush() { } bool CDVDDemuxHTSP::ReadStream(uint8_t* buf, int len) { while(len > 0) { int ret = m_Input->Read(buf, len); if(ret <= 0) return false; len -= ret; buf += ret; } return true; } htsmsg_t* CDVDDemuxHTSP::ReadStream() { if(m_Input->IsStreamType(DVDSTREAM_TYPE_HTSP)) return ((CDVDInputStreamHTSP*)m_Input)->ReadStream(); uint32_t l; if(!ReadStream((uint8_t*)&l, 4)) return NULL; l = ntohl(l); if(l == 0) return htsmsg_create_map(); uint8_t* buf = (uint8_t*)malloc(l); if(!buf) return NULL; if(!ReadStream(buf, l)) return NULL; return htsmsg_binary_deserialize(buf, l, buf); /* consumes 'buf' */ } DemuxPacket* CDVDDemuxHTSP::Read() { htsmsg_t * msg; while((msg = ReadStream())) { const char* method = htsmsg_get_str(msg, "method"); if(method == NULL) break; if (strcmp("subscriptionStart", method) == 0) SubscriptionStart(msg); else if(strcmp("subscriptionStop", method) == 0) SubscriptionStop (msg); else if(strcmp("subscriptionStatus", method) == 0) SubscriptionStatus(msg); else if(strcmp("queueStatus" , method) == 0) CHTSPSession::ParseQueueStatus(msg, m_QueueStatus); else if(strcmp("muxpkt" , method) == 0) { uint32_t index, duration; const void* bin; size_t binlen; int64_t ts; if(htsmsg_get_u32(msg, "stream" , &index) || htsmsg_get_bin(msg, "payload", &bin, &binlen)) break; DemuxPacket* pkt = CDVDDemuxUtils::AllocateDemuxPacket(binlen); memcpy(pkt->pData, bin, binlen); pkt->iSize = binlen; if(!htsmsg_get_u32(msg, "duration", &duration)) pkt->duration = (double)duration * DVD_TIME_BASE / 1000000; if(!htsmsg_get_s64(msg, "dts", &ts)) pkt->dts = (double)ts * DVD_TIME_BASE / 1000000; else pkt->dts = DVD_NOPTS_VALUE; if(!htsmsg_get_s64(msg, "pts", &ts)) pkt->pts = (double)ts * DVD_TIME_BASE / 1000000; else pkt->pts = DVD_NOPTS_VALUE; pkt->iStreamId = -1; for(int i = 0; i < (int)m_Streams.size(); i++) { if(m_Streams[i]->iPhysicalId == (int)index) { pkt->iStreamId = i; break; } } htsmsg_destroy(msg); return pkt; } break; } if(msg) { htsmsg_destroy(msg); return CDVDDemuxUtils::AllocateDemuxPacket(0); } return NULL; } void CDVDDemuxHTSP::SubscriptionStart (htsmsg_t *m) { htsmsg_t *streams; htsmsg_t *info; htsmsg_field_t *f; if((info = htsmsg_get_map(m, "sourceinfo"))) { HTSMSG_FOREACH(f, info) { if(f->hmf_type != HMF_STR) continue; CLog::Log(LOGDEBUG, "CDVDDemuxHTSP::SubscriptionStart - %s: %s", f->hmf_name, htsmsg_field_get_string(f)); } } if((streams = htsmsg_get_list(m, "streams")) == NULL) { CLog::Log(LOGERROR, "CDVDDemuxHTSP::SubscriptionStart - malformed message"); return; } for(int i = 0; i < (int)m_Streams.size(); i++) delete m_Streams[i]; m_Streams.clear(); HTSMSG_FOREACH(f, streams) { uint32_t index; const char* type; const char* lang; htsmsg_t* sub; if(f->hmf_type != HMF_MAP) continue; sub = &f->hmf_msg; if((type = htsmsg_get_str(sub, "type")) == NULL) continue; if(htsmsg_get_u32(sub, "index", &index)) continue; union { CDemuxStream* g; CDemuxStreamAudio* a; CDemuxStreamVideo* v; CDemuxStreamSubtitle* s; CDemuxStreamTeletext* t; } st; CLog::Log(LOGDEBUG, "CDVDDemuxHTSP::SubscriptionStart - id: %d, type: %s", index, type); if(!strcmp(type, "AC3")) { st.a = new CDemuxStreamAudioHTSP(this, type); st.a->codec = AV_CODEC_ID_AC3; } else if(!strcmp(type, "EAC3")) { st.a = new CDemuxStreamAudioHTSP(this, type); st.a->codec = AV_CODEC_ID_EAC3; } else if(!strcmp(type, "MPEG2AUDIO")) { st.a = new CDemuxStreamAudioHTSP(this, type); st.a->codec = AV_CODEC_ID_MP2; } else if(!strcmp(type, "AAC")) { st.a = new CDemuxStreamAudioHTSP(this, type); st.a->codec = AV_CODEC_ID_AAC; } else if(!strcmp(type, "MPEG2VIDEO")) { st.v = new CDemuxStreamVideoHTSP(this, type); st.v->codec = AV_CODEC_ID_MPEG2VIDEO; st.v->iWidth = htsmsg_get_u32_or_default(sub, "width" , 0); st.v->iHeight = htsmsg_get_u32_or_default(sub, "height", 0); } else if(!strcmp(type, "H264")) { st.v = new CDemuxStreamVideoHTSP(this, type); st.v->codec = AV_CODEC_ID_H264; st.v->iWidth = htsmsg_get_u32_or_default(sub, "width" , 0); st.v->iHeight = htsmsg_get_u32_or_default(sub, "height", 0); } else if(!strcmp(type, "DVBSUB")) { st.s = new CDemuxStreamSubtitle(); st.s->codec = AV_CODEC_ID_DVB_SUBTITLE; uint32_t composition_id = 0, ancillary_id = 0; htsmsg_get_u32(sub, "composition_id", &composition_id); htsmsg_get_u32(sub, "ancillary_id" , &ancillary_id); if(composition_id || ancillary_id) { st.s->ExtraData = new uint8_t[4]; st.s->ExtraSize = 4; st.s->ExtraData[0] = (composition_id >> 8) & 0xff; st.s->ExtraData[1] = (composition_id >> 0) & 0xff; st.s->ExtraData[2] = (ancillary_id >> 8) & 0xff; st.s->ExtraData[3] = (ancillary_id >> 0) & 0xff; } } else if(!strcmp(type, "TEXTSUB")) { st.s = new CDemuxStreamSubtitle(); st.s->codec = AV_CODEC_ID_TEXT; } else if(!strcmp(type, "TELETEXT")) { st.t = new CDemuxStreamTeletext(); st.t->codec = AV_CODEC_ID_DVB_TELETEXT; } else { continue; } if((lang = htsmsg_get_str(sub, "language"))) { strncpy(st.g->language, lang, sizeof(st.g->language)); st.g->language[sizeof(st.g->language) - 1] = '\0'; } st.g->iId = m_Streams.size(); st.g->iPhysicalId = index; m_Streams.push_back(st.g); } } void CDVDDemuxHTSP::SubscriptionStop (htsmsg_t *m) { for(int i = 0; i < (int)m_Streams.size(); i++) delete m_Streams[i]; m_Streams.clear(); } void CDVDDemuxHTSP::SubscriptionStatus(htsmsg_t *m) { const char* status; status = htsmsg_get_str(m, "status"); if(status == NULL) m_Status = ""; else { m_StatusCount++; m_Status = status; CLog::Log(LOGDEBUG, "CDVDDemuxHTSP::SubscriptionStatus - %s", status); CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, "TVHeadend Status", status, TOAST_DISPLAY_TIME, false); } } CDemuxStream* CDVDDemuxHTSP::GetStream(int iStreamId) { if(iStreamId >= 0 && iStreamId < (int)m_Streams.size()) return m_Streams[iStreamId]; return NULL; } int CDVDDemuxHTSP::GetNrOfStreams() { return m_Streams.size(); } std::string CDVDDemuxHTSP::GetFileName() { if(m_Input) return m_Input->GetFileName(); else return ""; } void CDVDDemuxHTSP::Abort() { if(m_Input) return m_Input->Abort(); }