#include <lib/dvb/dvb.h>
#include <lib/dvb/pmt.h>
#include <lib/dvb/sec.h>
+#include <lib/dvb/specs.h>
#include <errno.h>
#include <sys/types.h>
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",
- m_adapter.size(), m_frontend.size(), m_simulate_frontend.size(), m_demux.size());
+
+ int fd = open("/proc/stb/info/model", O_RDONLY);
+ char tmp[255];
+ int rd = fd >= 0 ? read(fd, tmp, 255) : 0;
+ if (fd >= 0)
+ close(fd);
+
+ if (!strncmp(tmp, "dm7025\n", rd))
+ m_boxtype = DM7025;
+ else if (!strncmp(tmp, "dm8000\n", rd))
+ m_boxtype = DM8000;
+ else if (!strncmp(tmp, "dm800\n", rd))
+ m_boxtype = DM800;
+ else if (!strncmp(tmp, "dm500hd\n", rd))
+ m_boxtype = DM500HD;
+ else {
+ eDebug("boxtype detection via /proc/stb/info not possible... use fallback via demux count!\n");
+ if (m_demux.size() == 3)
+ m_boxtype = DM800;
+ else if (m_demux.size() < 5)
+ m_boxtype = DM7025;
+ else
+ m_boxtype = DM8000;
+ }
+
+ eDebug("found %d adapter, %d frontends(%d sim) and %d demux, boxtype %d",
+ m_adapter.size(), m_frontend.size(), m_simulate_frontend.size(), m_demux.size(), m_boxtype);
eDVBCAService::registerChannelCallback(this);
{
// scan frontends
int num_fe = 0;
-
+
eDebug("scanning for frontends..");
while (1)
{
}
++num_fe;
}
-
+
// scan demux
int num_demux = 0;
while (1)
if (stat(filename, &s))
break;
ePtr<eDVBDemux> demux;
-
+
demux = new eDVBDemux(m_nr, num_demux);
m_demux.push_back(demux);
-
+
++num_demux;
}
}
--nr;
++i;
}
-
+
if (i != m_demux.end())
demux = *i;
else
return -1;
-
+
return 0;
}
--nr;
++i;
}
-
+
if (i != m_frontend.end())
fe = *i;
else
return -1;
-
+
return 0;
}
{
int num_fe = adapter->getNumFrontends();
int num_demux = adapter->getNumDemux();
-
+
m_adapter.push_back(adapter);
-
+
int i;
for (i=0; i<num_demux; ++i)
{
return err;
}
-RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBAllocatedDemux> &demux, int cap)
+#define capHoldDecodeReference 64
+
+RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBAllocatedDemux> &demux, int &cap)
{
/* find first unused demux which is on same adapter as frontend (or any, if PVR)
never use the first one unless we need a decoding demux. */
if (i == m_demux.end())
return -1;
-
+
ePtr<eDVBRegisteredDemux> unused;
-
- if (m_demux.size() < 5)
+
+ if (m_boxtype == DM800 || m_boxtype == DM500HD) // dm800 / 500hd
+ {
+ cap |= capHoldDecodeReference; // this is checked in eDVBChannel::getDemux
+ 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_boxtype == DM7025) // ATI
{
/* FIXME: hardware demux policy */
if (!(cap & iDVBChannel::capDecode))
for (; i != m_demux.end(); ++i, ++n)
{
int is_decode = n < 2;
-
+
int in_use = is_decode ? (i->m_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;
}
}
}
- else // we asume dm8000
+ else if (m_boxtype == DM8000)
{
+ cap |= capHoldDecodeReference; // this is checked in eDVBChannel::getDemux
for (; i != m_demux.end(); ++i, ++n)
{
if (fe)
if (!unused)
unused = i;
}
- else if (i->m_adapter == fe->m_adapter &&
+ else if (i->m_adapter == fe->m_adapter &&
i->m_demux->getSource() == fe->m_frontend->getDVBID())
{
demux = new eDVBAllocatedDemux(i);
}
/* allocate a frontend. */
-
+
ePtr<eDVBAllocatedFrontend> fe;
int err = allocateFrontend(fe, feparm, simulate);
}
#endif
-#if 1 /* This codepath is required on Broadcom-based Dreamboxes (DM800, DM8000) and strips away non-I-frames. */
+#if 0
if (!m_iframe_search)
return len;
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;
fts += 188;
}
- /* force payload only */
- ts[3] &= ~0x30;
- ts[3] |= 0x10;
-
-// memset(ts + 4, 0xFF, (offset % 188) - 4);
m_iframe_state = 1;
}
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)
{
m_frontend = frontend;
m_pvr_thread = 0;
-
- m_skipmode_n = m_skipmode_m = 0;
-
+ m_pvr_fd_dst = -1;
+
+ m_skipmode_n = m_skipmode_m = m_skipmode_frames = 0;
+
if (m_frontend)
m_frontend->get().connectStateChange(slot(*this, &eDVBChannel::frontendStateChanged), m_conn_frontendStateChanged);
}
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");
ourstate = state_failed;
} else
eFatal("state unknown");
-
+
if (ourstate != m_state)
{
m_state = ourstate;
/* 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;
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);
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;
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");
return;
}
- 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);
+ 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);
+ }
+ }
+ }
+
+ m_cue->m_lock.RdLock();
while (!m_cue->m_seek_requests.empty())
{
eDebug("decoder getPTS failed, can't seek relative");
continue;
}
+ if (!m_cue->m_decoding_demux)
+ {
+ eDebug("getNextSourceSpan, no decoding demux. couldn't seek to %llx... ignore request!", pts);
+ start = current_offset;
+ size = max;
+ continue;
+ }
if (getCurrentPosition(m_cue->m_decoding_demux, now, 1))
{
eDebug("seekTo: getCurrentPosition failed!");
continue;
}
}
-
+
if (relative == 1) /* pts relative */
{
pts += now;
if (relative != 2)
if (pts < 0)
pts = 0;
-
+
if (relative == 2) /* AP relative */
{
eDebug("AP relative seeking: %lld, at %lld", pts, now);
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;
}
-
+
eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx", relative, pts, offset);
current_offset = align(offset, blocksize); /* in case tstools return non-aligned offset */
}
{
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;
}
}
- 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;
}
{
if (m_channel_id)
m_mgr->removeChannel(this);
-
+
if (!channelid)
return 0;
eDebug("no frontend to tune!");
return -ENODEV;
}
-
+
m_channel_id = channelid;
m_mgr->addChannel(channelid, this);
m_state = state_tuning;
int res;
res = m_frontend->get().tune(*feparm);
m_current_frontend_parameters = feparm;
-
+
if (res)
{
m_state = state_release;
m_stateChanged(this);
return res;
}
-
+
return 0;
}
return -1;
}
+void eDVBChannel::SDTready(int result)
+{
+ ePyObject args = PyTuple_New(2), ret;
+ bool ok=false;
+ if (!result)
+ {
+ for (std::vector<ServiceDescriptionSection*>::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<ServiceDescriptionSection>;
+ 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<iDVBDemux> &demux, int cap)
{
ePtr<eDVBAllocatedDemux> &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;
+
+ 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;
+
+ if (cap & capHoldDecodeReference) // this is set in eDVBResourceManager::allocateDemux for Dm500HD/DM800 and DM8000
+ ;
+ else if (cap & capDecode)
+ our_demux = 0;
+ }
+ else
+ demux = *our_demux;
+
return 0;
}
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! */
-
+
+ if (m_pvr_fd_dst < 0)
+ {
/* (this codepath needs to be improved anyway.) */
#if HAVE_DVB_API_VERSION < 3
- m_pvr_fd_dst = open("/dev/pvr", O_WRONLY);
+ m_pvr_fd_dst = open("/dev/pvr", O_WRONLY);
#else
- m_pvr_fd_dst = open("/dev/misc/pvr", O_WRONLY);
+ m_pvr_fd_dst = open("/dev/misc/pvr", O_WRONLY);
#endif
- if (m_pvr_fd_dst < 0)
- {
- eDebug("can't open /dev/misc/pvr - you need to buy the new(!) $$$ box! (%m)"); // or wait for the driver to be improved.
- return -ENODEV;
+ if (m_pvr_fd_dst < 0)
+ {
+ eDebug("can't open /dev/misc/pvr - you need to buy the new(!) $$$ box! (%m)"); // or wait for the driver to be improved.
+ return -ENODEV;
+ }
}
m_pvr_thread = new eDVBChannelFilePush();
m_pvr_thread->setStreamMode(1);
m_pvr_thread->setScatterGather(this);
+ m_event(this, evtPreStart);
+
if (m_pvr_thread->start(file, m_pvr_fd_dst))
{
delete m_pvr_thread;
m_pvr_thread = 0;
+ ::close(m_pvr_fd_dst);
+ m_pvr_fd_dst = -1;
eDebug("can't open PVR file %s (%m)", file);
return -ENOENT;
}
if (m_pvr_thread)
{
m_pvr_thread->stop();
- ::close(m_pvr_fd_dst);
delete m_pvr_thread;
m_pvr_thread = 0;
}
+ if (m_pvr_fd_dst >= 0)
+ ::close(m_pvr_fd_dst);
}
void eDVBChannel::setCueSheet(eCueSheet *cuesheet)
{
if (!decoding_demux)
return -1;
-
+
pts_t now;
-
+
int r;
-
+
if (mode == 0) /* demux */
{
r = decoding_demux->getSTC(now, 0);
}
} else
now = pos; /* fixup supplied */
-
+
off_t off = 0; /* TODO: fixme */
r = m_tstools.fixupPTS(off, now);
if (r)
eDebug("fixup PTS failed");
return -1;
}
-
+
pos = now;
-
+
return 0;
}
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.
m_pvr_thread->flush();
/* HACK: flush PVR buffer */
::ioctl(m_pvr_fd_dst, 0);
-
+
/* flush ratebuffers (video, audio) */
if (decoding_demux)
decoding_demux->flush();
m_lock.Unlock();
m_event(evtSeek);
}
-
+
void eCueSheet::clear()
{
m_lock.WrLock();
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<pts_t, pts_t>(begin, end));
m_lock.Unlock();