X-Git-Url: https://git.cweiske.de/enigma2.git/blobdiff_plain/a34ef895210161a8820e96829ac87806566e7858..1c0fed5449b5ab880575587803451fc58fee13eb:/lib/dvb/dvb.cpp diff --git a/lib/dvb/dvb.cpp b/lib/dvb/dvb.cpp index 3ad086de..d30be29c 100644 --- a/lib/dvb/dvb.cpp +++ b/lib/dvb/dvb.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -69,19 +70,19 @@ eDVBResourceManager::eDVBResourceManager() if (!instance) instance = this; - + /* search available adapters... */ // add linux devices - + int num_adapter = 0; while (eDVBAdapterLinux::exist(num_adapter)) { addAdapter(new eDVBAdapterLinux(num_adapter)); num_adapter++; } - - eDebug("found %d adapter, %d frontends(%d sim) and %d demux", + + eDebug("found %d adapter, %d frontends(%d sim) and %d demux", m_adapter.size(), m_frontend.size(), m_simulate_frontend.size(), m_demux.size()); eDVBCAService::registerChannelCallback(this); @@ -103,7 +104,7 @@ eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr) { // scan frontends int num_fe = 0; - + eDebug("scanning for frontends.."); while (1) { @@ -132,7 +133,7 @@ eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr) } ++num_fe; } - + // scan demux int num_demux = 0; while (1) @@ -147,10 +148,10 @@ eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr) if (stat(filename, &s)) break; ePtr demux; - + demux = new eDVBDemux(m_nr, num_demux); m_demux.push_back(demux); - + ++num_demux; } } @@ -168,12 +169,12 @@ RESULT eDVBAdapterLinux::getDemux(ePtr &demux, int nr) --nr; ++i; } - + if (i != m_demux.end()) demux = *i; else return -1; - + return 0; } @@ -190,12 +191,12 @@ RESULT eDVBAdapterLinux::getFrontend(ePtr &fe, int nr, bool simula --nr; ++i; } - + if (i != m_frontend.end()) fe = *i; else return -1; - + return 0; } @@ -223,9 +224,9 @@ void eDVBResourceManager::addAdapter(iDVBAdapter *adapter) { int num_fe = adapter->getNumFrontends(); int num_demux = adapter->getNumDemux(); - + m_adapter.push_back(adapter); - + int i; for (i=0; i unused; - - if (m_demux.size() < 5) + + if (m_demux.size() == 3) // dm800 / 500hd + { + for (; i != m_demux.end(); ++i, ++n) + { + if (!i->m_inuse) + { + if (!unused) + unused = i; + } + else + { + if (fe) + { + if (i->m_adapter == fe->m_adapter && + i->m_demux->getSource() == fe->m_frontend->getDVBID()) + { + demux = new eDVBAllocatedDemux(i); + return 0; + } + } + else if (i->m_demux->getSource() == -1) // PVR + { + demux = new eDVBAllocatedDemux(i); + return 0; + } + } + } + } + else if (m_demux.size() < 5) // ATI { /* FIXME: hardware demux policy */ if (!(cap & iDVBChannel::capDecode)) @@ -443,14 +472,14 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtrm_demux->getRefCount() != 2) : i->m_inuse; - + if ((!in_use) && ((!fe) || (i->m_adapter == fe->m_adapter))) { if ((cap & iDVBChannel::capDecode) && !is_decode) continue; - unused = i; + unused = i; break; } } @@ -466,7 +495,7 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtrm_adapter == fe->m_adapter && + else if (i->m_adapter == fe->m_adapter && i->m_demux->getSource() == fe->m_frontend->getDVBID()) { demux = new eDVBAllocatedDemux(i); @@ -572,7 +601,7 @@ RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUse } /* allocate a frontend. */ - + ePtr fe; int err = allocateFrontend(fe, feparm, simulate); @@ -973,7 +1002,7 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s } #endif -#if 1 /* not yet */ +#if 0 if (!m_iframe_search) return len; @@ -982,7 +1011,7 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s // 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))) + while ((d + 3 < data + len) && (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 */ @@ -998,7 +1027,7 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s if (m_iframe_state == 1) { - /* we are allowing data, and stop allowing data on the next frame. + /* 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; @@ -1025,11 +1054,6 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s fts += 188; } - /* force payload only */ - ts[3] &= ~0x30; - ts[3] |= 0x10; - -// memset(ts + 4, 0xFF, (offset % 188) - 4); m_iframe_state = 1; } @@ -1041,7 +1065,7 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s int payload_start = 4; if (ts[3] & 0x20) /* adaptation field present */ payload_start += ts[4] + 1; /* skip AF */ - if (payload_start == (offset%184)) /* the 00 00 01 should be directly at the payload start, otherwise it's not a PES header */ + if (payload_start == (offset%188)) /* the 00 00 01 should be directly at the payload start, otherwise it's not a PES header */ { if (m_pid != pid) { @@ -1054,7 +1078,6 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s d += 4; } else d += 4; /* ignore */ - } if (m_iframe_state == 1) @@ -1073,9 +1096,9 @@ eDVBChannel::eDVBChannel(eDVBResourceManager *mgr, eDVBAllocatedFrontend *fronte m_frontend = frontend; m_pvr_thread = 0; - - m_skipmode_n = m_skipmode_m = 0; - + + m_skipmode_n = m_skipmode_m = m_skipmode_frames = 0; + if (m_frontend) m_frontend->get().connectStateChange(slot(*this, &eDVBChannel::frontendStateChanged), m_conn_frontendStateChanged); } @@ -1091,14 +1114,14 @@ eDVBChannel::~eDVBChannel() void eDVBChannel::frontendStateChanged(iDVBFrontend*fe) { int state, ourstate = 0; - + /* if we are already in shutdown, don't change state. */ if (m_state == state_release) return; - + if (fe->getState(state)) return; - + if (state == iDVBFrontend::stateLock) { eDebug("OURSTATE: ok"); @@ -1127,7 +1150,7 @@ void eDVBChannel::frontendStateChanged(iDVBFrontend*fe) ourstate = state_failed; } else eFatal("state unknown"); - + if (ourstate != m_state) { m_state = ourstate; @@ -1175,6 +1198,8 @@ void eDVBChannel::cueSheetEvent(int event) /* i agree that this might look a bit like black magic. */ m_skipmode_n = 512*1024; /* must be 1 iframe at least. */ m_skipmode_m = bitrate / 8 / 90000 * m_cue->m_skipmode_ratio / 8; + m_skipmode_frames = m_cue->m_skipmode_ratio / 90000; + m_skipmode_frames_remainder = 0; if (m_cue->m_skipmode_ratio < 0) m_skipmode_m -= m_skipmode_n; @@ -1184,12 +1209,12 @@ void eDVBChannel::cueSheetEvent(int event) if (abs(m_skipmode_m) < abs(m_skipmode_n)) { eWarning("something is wrong with this calculation"); - m_skipmode_n = m_skipmode_m = 0; + m_skipmode_frames = m_skipmode_n = m_skipmode_m = 0; } } else { eDebug("skipmode ratio is 0, normal play"); - m_skipmode_n = m_skipmode_m = 0; + m_skipmode_frames = m_skipmode_n = m_skipmode_m = 0; } } m_pvr_thread->setIFrameSearch(m_skipmode_n != 0); @@ -1232,6 +1257,23 @@ static inline long long align(long long x, int align) x -= x % align; + if (sign) + x = -x; + + return x; +} + + /* align toward zero */ +static inline long long align_with_len(long long x, int align, size_t &len) +{ + int sign = x < 0; + + if (sign) + x = -x; + + x -= x % align; + len += x % align; + if (sign) x = -x; @@ -1244,7 +1286,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off const int blocksize = 188; unsigned int max = align(10*1024*1024, blocksize); current_offset = align(current_offset, blocksize); - + if (!m_cue) { eDebug("no cue sheet. forcing normal play"); @@ -1265,13 +1307,55 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off if (m_skipmode_n) { - eDebug("skipmode %d:%d", m_skipmode_m, m_skipmode_n); + eDebug("skipmode %d:%d (x%d)", m_skipmode_m, m_skipmode_n, m_skipmode_frames); max = align(m_skipmode_n, blocksize); } eDebug("getNextSourceSpan, current offset is %08llx, m_skipmode_m = %d!", current_offset, m_skipmode_m); + + int frame_skip_success = 0; - current_offset += align(m_skipmode_m, blocksize); + if (m_skipmode_m) + { + int frames_to_skip = m_skipmode_frames + m_skipmode_frames_remainder; + eDebug("we are at %llx, and we try to skip %d+%d frames from here", current_offset, m_skipmode_frames, m_skipmode_frames_remainder); + size_t iframe_len; + off_t iframe_start = current_offset; + int frames_skipped = frames_to_skip; + if (!m_tstools.findNextPicture(iframe_start, iframe_len, frames_skipped)) + { + m_skipmode_frames_remainder = frames_to_skip - frames_skipped; + eDebug("successfully skipped %d (out of %d, rem now %d) frames.", frames_skipped, frames_to_skip, m_skipmode_frames_remainder); + current_offset = align_with_len(iframe_start, blocksize, iframe_len); + max = align(iframe_len + 187, blocksize); + frame_skip_success = 1; + } else + { + m_skipmode_frames_remainder = 0; + eDebug("frame skipping failed, reverting to byte-skipping"); + } + } + + if (!frame_skip_success) + { + current_offset += align(m_skipmode_m, blocksize); + + if (m_skipmode_m) + { + eDebug("we are at %llx, and we try to find the iframe here:", current_offset); + size_t iframe_len; + off_t iframe_start = current_offset; + + int direction = (m_skipmode_m < 0) ? -1 : +1; + if (m_tstools.findFrame(iframe_start, iframe_len, direction)) + eDebug("failed"); + else + { + current_offset = align_with_len(iframe_start, blocksize, iframe_len); + max = align(iframe_len, blocksize); + } + } + } while (!m_cue->m_seek_requests.empty()) { @@ -1315,7 +1399,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off continue; } } - + if (relative == 1) /* pts relative */ { pts += now; @@ -1326,7 +1410,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off if (relative != 2) if (pts < 0) pts = 0; - + if (relative == 2) /* AP relative */ { eDebug("AP relative seeking: %lld, at %lld", pts, now); @@ -1341,15 +1425,20 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off eDebug("next ap is %llx\n", pts); } } - + off_t offset = 0; if (m_tstools.getOffset(offset, pts, -1)) { eDebug("get offset for pts=%lld failed!", pts); continue; } + + size_t iframe_len; + /* try to align to iframe */ + int direction = pts < 0 ? -1 : 1; + m_tstools.findFrame(offset, iframe_len, direction); - eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx", relative, pts, offset); + eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx (skipped additional %d frames due to iframe re-align)", relative, pts, offset, direction); current_offset = align(offset, blocksize); /* in case tstools return non-aligned offset */ } @@ -1359,7 +1448,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off { 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; @@ -1417,30 +1506,23 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off } } - if (m_source_span.empty()) { - if ((current_offset < -m_skipmode_m) && (m_skipmode_m < 0)) - { - eDebug("reached SOF"); - m_skipmode_m = 0; - m_pvr_thread->sendEvent(eFilePushThread::evtUser); - } + if ((current_offset < -m_skipmode_m) && (m_skipmode_m < 0)) + { + eDebug("reached SOF"); + m_skipmode_m = 0; + m_pvr_thread->sendEvent(eFilePushThread::evtUser); + } + + if (m_source_span.empty()) + { start = current_offset; size = max; - } else { - off_t tmp2, tmp = align(m_source_span.rbegin()->second, blocksize); - pts_t len; - getLength(len); - m_tstools.getOffset(tmp2, len, 1); - if (current_offset == tmp || current_offset == tmp2) { - start = tmp2; - size = max; - } else { - start = tmp - align(512*1024, blocksize); - size = align(512*1024, blocksize); - } + eDebug("NO CUESHEET. (%08llx, %d)", start, size); + } else + { + start = current_offset; + size = 0; } - - eDebug("END OF CUESHEET. (%08llx, %d)", start, size); return; } @@ -1471,7 +1553,7 @@ RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtrremoveChannel(this); - + if (!channelid) return 0; @@ -1480,7 +1562,7 @@ RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtraddChannel(channelid, this); m_state = state_tuning; @@ -1488,14 +1570,14 @@ RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtrget().tune(*feparm); m_current_frontend_parameters = feparm; - + if (res) { m_state = state_release; m_stateChanged(this); return res; } - + return 0; } @@ -1522,24 +1604,79 @@ RESULT eDVBChannel::setCIRouting(const eDVBCIRouting &routing) return -1; } +void eDVBChannel::SDTready(int result) +{ + ePyObject args = PyTuple_New(2), ret; + bool ok=false; + if (!result) + { + for (std::vector::const_iterator i = m_SDT->getSections().begin(); i != m_SDT->getSections().end(); ++i) + { + ok = true; + PyTuple_SET_ITEM(args, 0, PyInt_FromLong((*i)->getTransportStreamId())); + PyTuple_SET_ITEM(args, 1, PyInt_FromLong((*i)->getOriginalNetworkId())); + break; + } + } + if (!ok) + { + PyTuple_SET_ITEM(args, 0, Py_None); + PyTuple_SET_ITEM(args, 1, Py_None); + Py_INCREF(Py_None); + Py_INCREF(Py_None); + } + ret = PyObject_CallObject(m_tsid_onid_callback, args); + if (ret) + Py_DECREF(ret); + Py_DECREF(args); + Py_DECREF(m_tsid_onid_callback); + m_tsid_onid_callback = ePyObject(); + m_tsid_onid_demux = 0; + m_SDT = 0; +} + +RESULT eDVBChannel::requestTsidOnid(ePyObject callback) +{ + if (PyCallable_Check(callback)) + { + if (!getDemux(m_tsid_onid_demux, 0)) + { + m_SDT = new eTable; + CONNECT(m_SDT->tableReady, eDVBChannel::SDTready); + if (m_SDT->start(m_tsid_onid_demux, eDVBSDTSpec())) + { + m_tsid_onid_demux = 0; + m_SDT = 0; + } + else + { + Py_INCREF(callback); + m_tsid_onid_callback = callback; + return 0; + } + } + } + return -1; +} + RESULT eDVBChannel::getDemux(ePtr &demux, int cap) { ePtr &our_demux = (cap & capDecode) ? m_decoder_demux : m_demux; - + if (!our_demux) { demux = 0; - + if (m_mgr->allocateDemux(m_frontend ? (eDVBRegisteredFrontend*)*m_frontend : (eDVBRegisteredFrontend*)0, our_demux, cap)) return -1; } - + demux = *our_demux; /* don't hold a reference to the decoding demux, we don't need it. */ - + /* FIXME: by dropping the 'allocated demux' in favour of the 'iDVBDemux', - the refcount is lost. thus, decoding demuxes are never allocated. - + the refcount is lost. thus, decoding demuxes are never allocated. + this poses a big problem for PiP. */ if (cap & capDecode) our_demux = 0; @@ -1572,12 +1709,12 @@ RESULT eDVBChannel::playFile(const char *file) delete m_pvr_thread; m_pvr_thread = 0; } - + m_tstools.openFile(file); - + /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST, THEN DO A REAL FIX HERE! */ - + /* (this codepath needs to be improved anyway.) */ #if HAVE_DVB_API_VERSION < 3 m_pvr_fd_dst = open("/dev/pvr", O_WRONLY); @@ -1638,11 +1775,11 @@ RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, in { if (!decoding_demux) return -1; - + pts_t now; - + int r; - + if (mode == 0) /* demux */ { r = decoding_demux->getSTC(now, 0); @@ -1653,7 +1790,7 @@ RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, in } } else now = pos; /* fixup supplied */ - + off_t off = 0; /* TODO: fixme */ r = m_tstools.fixupPTS(off, now); if (r) @@ -1661,9 +1798,9 @@ RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, in eDebug("fixup PTS failed"); return -1; } - + pos = now; - + return 0; } @@ -1674,7 +1811,7 @@ void eDVBChannel::flushPVR(iDVBDemux *decoding_demux) a.) the filepush's internal buffer b.) the PVR buffer (before demux) c.) the ratebuffer (after demux) - + it's important to clear them in the correct order, otherwise the ratebuffer (for example) would immediately refill from the not-yet-flushed PVR buffer. @@ -1685,7 +1822,7 @@ void eDVBChannel::flushPVR(iDVBDemux *decoding_demux) m_pvr_thread->flush(); /* HACK: flush PVR buffer */ ::ioctl(m_pvr_fd_dst, 0); - + /* flush ratebuffers (video, audio) */ if (decoding_demux) decoding_demux->flush(); @@ -1709,7 +1846,7 @@ void eCueSheet::seekTo(int relative, const pts_t &pts) m_lock.Unlock(); m_event(evtSeek); } - + void eCueSheet::clear() { m_lock.WrLock(); @@ -1719,7 +1856,7 @@ void eCueSheet::clear() void eCueSheet::addSourceSpan(const pts_t &begin, const pts_t &end) { - assert(begin < end); + ASSERT(begin < end); m_lock.WrLock(); m_spans.push_back(std::pair(begin, end)); m_lock.Unlock();