X-Git-Url: https://git.cweiske.de/enigma2.git/blobdiff_plain/1c4afca574ce3b3af434b458e52b61871eba5d31..9e4a418c7ce0e0281ca8c06be4ee92a48eaab87a:/lib/dvb/dvb.cpp diff --git a/lib/dvb/dvb.cpp b/lib/dvb/dvb.cpp index 09c9f5f6..0eb614db 100644 --- a/lib/dvb/dvb.cpp +++ b/lib/dvb/dvb.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -52,6 +53,13 @@ RESULT eDVBResourceManager::getInstance(ePtr &ptr) return -1; } +ePtr NewResourceManagerPtr(void) +{ + ePtr ptr; + eDVBResourceManager::getInstance(ptr); + return ptr; +} + eDVBResourceManager::eDVBResourceManager() :m_releaseCachedChannelTimer(eApp) { @@ -75,9 +83,19 @@ eDVBResourceManager::eDVBResourceManager() eDebug("found %d adapter, %d frontends and %d demux", m_adapter.size(), m_frontend.size(), m_demux.size()); + eDVBCAService::registerChannelCallback(this); + CONNECT(m_releaseCachedChannelTimer.timeout, eDVBResourceManager::releaseCachedChannel); } +void eDVBResourceManager::feStateChanged() +{ + int mask=0; + for (eSmartPtrList::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i) + if (i->m_inuse) + mask |= ( 1 << i->m_frontend->getSlotID() ); + /* emit */ frontendUseMaskChanged(mask); +} DEFINE_REF(eDVBAdapterLinux); eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr) @@ -207,56 +225,145 @@ void eDVBResourceManager::addAdapter(iDVBAdapter *adapter) m_demux.push_back(new eDVBRegisteredDemux(demux, adapter)); } + ePtr prev_dvbt_frontend; for (i=0; i frontend; - if (!adapter->getFrontend(frontend, i)) { + int frontendType=0; + frontend->getFrontendType(frontendType); + eDVBRegisteredFrontend *new_fe = new eDVBRegisteredFrontend(frontend, adapter); + CONNECT(new_fe->stateChanged, eDVBResourceManager::feStateChanged); + m_frontend.push_back(new_fe); frontend->setSEC(m_sec); - m_frontend.push_back(new eDVBRegisteredFrontend(frontend, adapter)); + // we must link all dvb-t frontends ( for active antenna voltage ) + if (frontendType == iDVBFrontend::feTerrestrial) + { + if (prev_dvbt_frontend) + { + prev_dvbt_frontend->m_frontend->setData(eDVBFrontend::LINKED_NEXT_PTR, (int)new_fe); + frontend->setData(eDVBFrontend::LINKED_PREV_PTR, (int)&(*prev_dvbt_frontend)); + } + prev_dvbt_frontend = new_fe; + } } } } +PyObject *eDVBResourceManager::setFrontendSlotInformations(ePyObject list) +{ + if (!PyList_Check(list)) + { + PyErr_SetString(PyExc_StandardError, "eDVBResourceManager::setFrontendSlotInformations argument should be a python list"); + return NULL; + } + if ((unsigned int)PyList_Size(list) != m_frontend.size()) + { + char blasel[256]; + sprintf(blasel, "eDVBResourceManager::setFrontendSlotInformations list size incorrect %d frontends avail, but %d entries in slotlist", + m_frontend.size(), PyList_Size(list)); + PyErr_SetString(PyExc_StandardError, blasel); + return NULL; + } + int pos=0; + for (eSmartPtrList::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i) + { + ePyObject obj = PyList_GET_ITEM(list, pos++); + if (!i->m_frontend->setSlotInfo(obj)) + return NULL; + } + Py_RETURN_NONE; +} + RESULT eDVBResourceManager::allocateFrontend(ePtr &fe, ePtr &feparm) { ePtr best; int bestval = 0; + int foundone = 0; for (eSmartPtrList::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i) + { + int c = i->m_frontend->isCompatibleWith(feparm); + + if (c) /* if we have at least one frontend which is compatible with the source, flag this. */ + foundone = 1; + if (!i->m_inuse) { - int c = i->m_frontend->isCompatibleWith(feparm); if (c > bestval) { bestval = c; best = i; } } + } if (best) { fe = new eDVBAllocatedFrontend(best); return 0; } - + fe = 0; - - return -1; + + if (foundone) + return errAllSourcesBusy; + else + return errNoSourceFound; } -RESULT eDVBResourceManager::allocateFrontendByIndex(ePtr &fe, int nr) +RESULT eDVBResourceManager::allocateFrontendByIndex(ePtr &fe, int slot_index) { - for (eSmartPtrList::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i, --nr) - if ((!nr) && !i->m_inuse) + int err = errNoSourceFound; + for (eSmartPtrList::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i) + if (!i->m_inuse && i->m_frontend->getSlotID() == slot_index) { + // check if another slot linked to this is in use + eDVBRegisteredFrontend *satpos_depends_to_fe = + (eDVBRegisteredFrontend*) i->m_frontend->m_data[eDVBFrontend::SATPOS_DEPENDS_PTR]; + if ( (int)satpos_depends_to_fe != -1 ) + { + if (satpos_depends_to_fe->m_inuse) + { + eDebug("another satpos depending frontend is in use.. so allocateFrontendByIndex not possible!"); + err = errAllSourcesBusy; + goto alloc_fe_by_id_not_possible; + } + } + else // check linked tuners + { + eDVBRegisteredFrontend *next = + (eDVBRegisteredFrontend *) i->m_frontend->m_data[eDVBFrontend::LINKED_NEXT_PTR]; + while ( (int)next != -1 ) + { + if (next->m_inuse) + { + eDebug("another linked frontend is in use.. so allocateFrontendByIndex not possible!"); + err = errAllSourcesBusy; + goto alloc_fe_by_id_not_possible; + } + next = (eDVBRegisteredFrontend *)next->m_frontend->m_data[eDVBFrontend::LINKED_NEXT_PTR]; + } + eDVBRegisteredFrontend *prev = (eDVBRegisteredFrontend *) + i->m_frontend->m_data[eDVBFrontend::LINKED_PREV_PTR]; + while ( (int)prev != -1 ) + { + if (prev->m_inuse) + { + eDebug("another linked frontend is in use.. so allocateFrontendByIndex not possible!"); + err = errAllSourcesBusy; + goto alloc_fe_by_id_not_possible; + } + prev = (eDVBRegisteredFrontend *)prev->m_frontend->m_data[eDVBFrontend::LINKED_PREV_PTR]; + } + } fe = new eDVBAllocatedFrontend(i); return 0; } - +alloc_fe_by_id_not_possible: fe = 0; - return -1; + return err; } RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr &demux, int cap) @@ -294,7 +401,7 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtrget().setSourceFrontend(fe->m_frontend->getID()); + demux->get().setSourceFrontend(fe->m_frontend->getDVBID()); else demux->get().setSourcePVR(0); return 0; @@ -354,22 +461,23 @@ RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUse if (!m_list) { eDebug("no channel list set!"); - return -ENOENT; + return errNoChannelList; } ePtr feparm; if (m_list->getChannelFrontendData(channelid, feparm)) { eDebug("channel not found!"); - return -ENOENT; + return errChannelNotInList; } /* allocate a frontend. */ ePtr fe; - - if (allocateFrontend(fe, feparm)) - return errNoFrontend; + + int err = allocateFrontend(fe, feparm); + if (err) + return err; RESULT res; ePtr ch; @@ -418,7 +526,7 @@ void eDVBResourceManager::releaseCachedChannel() m_cached_channel=0; } -RESULT eDVBResourceManager::allocateRawChannel(eUsePtr &channel, int frontend_index) +RESULT eDVBResourceManager::allocateRawChannel(eUsePtr &channel, int slot_index) { ePtr fe; @@ -429,9 +537,10 @@ RESULT eDVBResourceManager::allocateRawChannel(eUsePtr &channel, in m_releaseCachedChannelTimer.stop(); } - if (allocateFrontendByIndex(fe, frontend_index)) - return errNoFrontend; - + int err = allocateFrontendByIndex(fe, slot_index); + if (err) + return err; + eDVBChannel *ch; ch = new eDVBChannel(this, fe); @@ -489,7 +598,7 @@ RESULT eDVBResourceManager::connectChannelAdded(const Slot1 & return 0; } -bool eDVBResourceManager::canAllocateFrontend(ePtr &feparm) +int eDVBResourceManager::canAllocateFrontend(ePtr &feparm) { ePtr best; int bestval = 0; @@ -501,18 +610,44 @@ bool eDVBResourceManager::canAllocateFrontend(ePtr &fepa if (c > bestval) bestval = c; } + return bestval; +} - return bestval>0; +int tuner_type_channel_default(ePtr &channellist, const eDVBChannelID &chid) +{ + if (channellist) + { + ePtr feparm; + if (!channellist->getChannelFrontendData(chid, feparm)) + { + int system; + if (!feparm->getSystem(system)) + { + switch(system) + { + case iDVBFrontend::feSatellite: + return 50000; + case iDVBFrontend::feCable: + return 40000; + case iDVBFrontend::feTerrestrial: + return 30000; + default: + break; + } + } + } + } + return 0; } -bool eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, const eDVBChannelID& ignore) +int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, const eDVBChannelID& ignore) { - bool ret=true; + int ret=0; if (m_cached_channel) { eDVBChannel *cache_chan = (eDVBChannel*)&(*m_cached_channel); if(channelid==cache_chan->getChannelID()) - return ret; + return tuner_type_channel_default(m_list, channelid); } /* first, check if a channel is already existing. */ @@ -523,7 +658,7 @@ bool eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, con if (i->m_channel_id == channelid) { // eDebug("found shared channel.."); - return ret; + return tuner_type_channel_default(m_list, channelid); } } @@ -593,14 +728,12 @@ bool eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, con if (!m_list) { eDebug("no channel list set!"); - ret = false; goto error; } if (m_list->getChannelFrontendData(channelid, feparm)) { eDebug("channel not found!"); - ret = false; goto error; } @@ -615,6 +748,100 @@ error: return ret; } +class eDVBChannelFilePush: public eFilePushThread +{ +public: + void setIFrameSearch(int enabled) { m_iframe_search = enabled; m_iframe_state = 0; } +protected: + int m_iframe_search, m_iframe_state, m_pid; + int filterRecordData(const unsigned char *data, int len, size_t ¤t_span_remaining); +}; + +int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, size_t ¤t_span_remaining) +{ +#if 0 /* not yet */ + if (!m_iframe_search) + return len; + + unsigned char *data = (unsigned char*)_data; /* remove that const. we know what we are doing. */ + + eDebug("filterRecordData, size=%d (mod 188=%d), first byte is %02x", len, len %188, data[0]); + + unsigned char *d = data; + while ((d = (unsigned char*)memmem(d, data + len - d, "\x00\x00\x01", 3))) + { + int offset = d - data; + int ts_offset = offset - offset % 188; /* offset to the start of TS packet */ + unsigned char *ts = data + ts_offset; + int pid = ((ts[1] << 8) | ts[2]) & 0x1FFF; + + if ((d[3] == 0) && (m_pid == pid)) /* picture start */ + { + int picture_type = (d[5] >> 3) & 7; + d += 4; + + eDebug("%d-frame at %d, offset in TS packet: %d, pid=%04x", picture_type, offset, offset % 188, pid); + + if (m_iframe_state == 1) + { + /* we are allowing data, and stop allowing data on the next frame. + we now found a frame. so stop here. */ + memset(data + offset, 0, 188 - (offset%188)); /* zero out rest of TS packet */ + current_span_remaining = 0; + m_iframe_state = 0; + unsigned char *fts = ts + 188; + while (fts < (data + len)) + { + fts[1] |= 0x1f; + fts[2] |= 0xff; /* drop packet */ + fts += 188; + } + + return len; // ts_offset + 188; /* deliver this packet, but not more. */ + } else + { + if (picture_type != 1) /* we are only interested in I frames */ + continue; + + unsigned char *fts = data; + while (fts < ts) + { + fts[1] |= 0x1f; + fts[2] |= 0xff; /* drop packet */ + + fts += 188; + } + /* force payload only */ + ts[3] &= ~0x30; + ts[3] |= 0x10; + +// memset(ts + 4, 0xFF, (offset % 188) - 4); + + m_iframe_state = 1; + } + } else if ((d[3] & 0xF0) == 0xE0) /* video stream */ + { + if (m_pid != pid) + { + eDebug("now locked to pid %04x", pid); + m_pid = pid; + } +// m_pid = 0x6e; + d += 4; + } else + d += 4; /* ignore */ + + } + + if (m_iframe_state == 1) + return len; + else + return 0; /* we need find an iframe first */ +#else + return len; +#endif +} + DEFINE_REF(eDVBChannel); eDVBChannel::eDVBChannel(eDVBResourceManager *mgr, eDVBAllocatedFrontend *frontend): m_state(state_idle), m_mgr(mgr) @@ -710,8 +937,10 @@ void eDVBChannel::cueSheetEvent(int event) case eCueSheet::evtSkipmode: { { - eSingleLocker l(m_cue->m_lock); + m_cue->m_lock.WrLock(); m_cue->m_seek_requests.push_back(std::pair(1, 0)); /* resync */ + m_cue->m_lock.Unlock(); + eRdLocker l(m_cue->m_lock); if (m_cue->m_skipmode_ratio) { int bitrate = m_tstools.calcBitrate(); /* in bits/s */ @@ -737,7 +966,11 @@ void eDVBChannel::cueSheetEvent(int event) m_skipmode_n = m_skipmode_m = 0; } } + ASSERT(m_pvr_thread); + m_pvr_thread->setIFrameSearch(m_skipmode_n != 0); + eDebug("flush pvr"); flushPVR(m_cue->m_decoding_demux); + eDebug("done"); break; } case eCueSheet::evtSpanChanged: @@ -760,10 +993,28 @@ void eDVBChannel::cueSheetEvent(int event) } } + /* align toward zero */ +static inline long long align(long long x, int align) +{ + int sign = x < 0; + + if (sign) + x = -x; + + x -= x % align; + + if (sign) + x = -x; + + return x; +} + /* remember, this gets called from another thread. */ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off_t &start, size_t &size) { - unsigned int max = 10*1024*1024; + const int blocksize = 188; + unsigned int max = align(10*1024*1024, blocksize); + current_offset = align(current_offset, blocksize); if (!m_cue) { @@ -773,30 +1024,34 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off return; } - eSingleLocker l(m_cue->m_lock); - + m_cue->m_lock.RdLock(); if (!m_cue->m_decoding_demux) { start = current_offset; size = max; eDebug("getNextSourceSpan, no decoding demux. forcing normal play"); + m_cue->m_lock.Unlock(); return; } - + if (m_skipmode_n) { eDebug("skipmode %d:%d", m_skipmode_m, m_skipmode_n); - max = m_skipmode_n; + max = align(m_skipmode_n, blocksize); } - - eDebug("getNextSourceSpan, current offset is %08llx!", current_offset); - - current_offset += m_skipmode_m; - + + eDebug("getNextSourceSpan, current offset is %08llx, m_skipmode_m = %d!", current_offset, m_skipmode_m); + + current_offset += align(m_skipmode_m, blocksize); + while (!m_cue->m_seek_requests.empty()) { std::pair seek = m_cue->m_seek_requests.front(); + m_cue->m_lock.Unlock(); + m_cue->m_lock.WrLock(); m_cue->m_seek_requests.pop_front(); + m_cue->m_lock.Unlock(); + m_cue->m_lock.RdLock(); int relative = seek.first; pts_t pts = seek.second; @@ -818,6 +1073,18 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off eDebug("seekTo: getCurrentPosition failed!"); continue; } + } else if (pts < 0) /* seek relative to end */ + { + pts_t len; + if (!getLength(len)) + { + eDebug("seeking relative to end. len=%lld, seek = %lld", len, pts); + pts += len; + } else + { + eWarning("getLength failed - can't seek relative to end!"); + continue; + } } if (relative == 1) /* pts relative */ @@ -837,7 +1104,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off pts_t nextap; if (m_tstools.getNextAccessPoint(nextap, now, pts)) { - pts = now; + pts = now - 90000; /* approx. 1s */ eDebug("AP relative seeking failed!"); } else { @@ -848,38 +1115,46 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off off_t offset = 0; if (m_tstools.getOffset(offset, pts)) + { + eDebug("get offset for pts=%lld failed!", pts); continue; + } eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx", relative, pts, offset); - current_offset = offset; + current_offset = align(offset, blocksize); /* in case tstools return non-aligned offset */ } - + + m_cue->m_lock.Unlock(); + for (std::list >::const_iterator i(m_source_span.begin()); i != m_source_span.end(); ++i) { - if ((current_offset >= i->first) && (current_offset < i->second)) + long long aligned_start = align(i->first, blocksize); + long long aligned_end = align(i->second, blocksize); + + if ((current_offset >= aligned_start) && (current_offset < aligned_end)) { start = current_offset; - /* max can not exceed max(size_t). i->second - current_offset, however, can. */ - if ((i->second - current_offset) > max) + /* max can not exceed max(size_t). aligned_end - current_offset, however, can. */ + if ((aligned_end - current_offset) > max) size = max; else - size = i->second - current_offset; + size = aligned_end - current_offset; eDebug("HIT, %lld < %lld < %lld, size: %d", i->first, current_offset, i->second, size); return; } - if (current_offset < i->first) + if (current_offset < aligned_start) { /* ok, our current offset is in an 'out' zone. */ if ((m_skipmode_m >= 0) || (i == m_source_span.begin())) { /* in normal playback, just start at the next zone. */ start = i->first; - + /* size is not 64bit! */ if ((i->second - i->first) > max) size = max; else - size = i->second - i->first; + size = aligned_end - aligned_start; eDebug("skip"); if (m_skipmode_m < 0) @@ -895,28 +1170,34 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off --i; eDebug("skip to previous block, which is %llx..%llx", i->first, i->second); size_t len; - - if ((i->second - i->first) > max) + + aligned_start = align(i->first, blocksize); + aligned_end = align(i->second, blocksize); + + if ((aligned_end - aligned_start) > max) len = max; else - len = i->second - i->first; + len = aligned_end - aligned_start; - start = i->second - len; + start = aligned_end - len; eDebug("skipping to %llx, %d", start, len); } + + eDebug("result: %llx, %x (%llx %llx)", start, size, aligned_start, aligned_end); return; } } - + if ((current_offset < -m_skipmode_m) && (m_skipmode_m < 0)) { eDebug("reached SOF"); m_skipmode_m = 0; m_pvr_thread->sendEvent(eFilePushThread::evtUser); } - + start = current_offset; size = max; + eDebug("END OF CUESHEET. (%08llx, %d)", start, size); return; } @@ -1067,8 +1348,9 @@ RESULT eDVBChannel::playFile(const char *file) return -ENODEV; } - m_pvr_thread = new eFilePushThread(); + m_pvr_thread = new eDVBChannelFilePush(); m_pvr_thread->enablePVRCommit(1); + m_pvr_thread->setStreamMode(1); m_pvr_thread->setScatterGather(this); if (m_pvr_thread->start(file, m_pvr_fd_dst)) @@ -1180,25 +1462,24 @@ eCueSheet::eCueSheet() void eCueSheet::seekTo(int relative, const pts_t &pts) { - { - eSingleLock l(m_lock); - m_seek_requests.push_back(std::pair(relative, pts)); - } + m_lock.WrLock(); + m_seek_requests.push_back(std::pair(relative, pts)); + m_lock.Unlock(); m_event(evtSeek); } void eCueSheet::clear() { - eSingleLock l(m_lock); + m_lock.WrLock(); m_spans.clear(); + m_lock.Unlock(); } void eCueSheet::addSourceSpan(const pts_t &begin, const pts_t &end) { - { - eSingleLock l(m_lock); - m_spans.push_back(std::pair(begin, end)); - } + m_lock.WrLock(); + m_spans.push_back(std::pair(begin, end)); + m_lock.Unlock(); } void eCueSheet::commitSpans() @@ -1208,10 +1489,9 @@ void eCueSheet::commitSpans() void eCueSheet::setSkipmode(const pts_t &ratio) { - { - eSingleLock l(m_lock); - m_skipmode_ratio = ratio; - } + m_lock.WrLock(); + m_skipmode_ratio = ratio; + m_lock.Unlock(); m_event(evtSkipmode); }