X-Git-Url: https://git.cweiske.de/enigma2.git/blobdiff_plain/56f5fa20371cd5a761dc37a6dd0dd730c38e0cbc..e03708b0a361c236d7034f35e4585edd4a91bb34:/lib/dvb/demux.cpp diff --git a/lib/dvb/demux.cpp b/lib/dvb/demux.cpp index 6753f2f2..4fba8fa8 100644 --- a/lib/dvb/demux.cpp +++ b/lib/dvb/demux.cpp @@ -5,6 +5,13 @@ #include #include +// #define FUZZING 1 + +#if FUZZING + /* change every 1:FUZZING_PROPABILITY byte */ +#define FUZZING_PROPABILITY 100 +#endif + #if HAVE_DVB_API_VERSION < 3 #include @@ -24,6 +31,28 @@ #else #include + +#define HAVE_ADD_PID + +#ifdef HAVE_ADD_PID + +#if HAVE_DVB_API_VERSION > 3 +#ifndef DMX_ADD_PID +#define DMX_ADD_PID _IOW('o', 51, __u16) +#define DMX_REMOVE_PID _IOW('o', 52, __u16) +#endif +#else +#define DMX_ADD_PID _IO('o', 51) +#define DMX_REMOVE_PID _IO('o', 52) + +typedef enum { + DMX_TAP_TS = 0, + DMX_TAP_PES = DMX_PES_OTHER, /* for backward binary compat. */ +} dmx_tap_type_t; +#endif + +#endif + #endif #include "crc32.h" @@ -62,11 +91,12 @@ RESULT eDVBDemux::setSourceFrontend(int fenum) { #if HAVE_DVB_API_VERSION >= 3 int fd = openDemux(); - int n = DMX_SOURCE_FRONT0 + fenum; int res = ::ioctl(fd, DMX_SET_SOURCE, &n); if (res) eDebug("DMX_SET_SOURCE failed! - %m"); + else + source = fenum; ::close(fd); return res; #endif @@ -79,6 +109,7 @@ RESULT eDVBDemux::setSourcePVR(int pvrnum) int fd = openDemux(); int n = DMX_SOURCE_DVR0 + pvrnum; int res = ::ioctl(fd, DMX_SET_SOURCE, &n); + source = -1; ::close(fd); return res; #endif @@ -94,6 +125,15 @@ RESULT eDVBDemux::createSectionReader(eMainloop *context, ePtr &reader) +{ + RESULT res; + reader = new eDVBPESReader(this, context, res); + if (res) + reader = 0; + return res; +} + RESULT eDVBDemux::createTSRecorder(ePtr &recorder) { if (m_dvr_busy) @@ -102,9 +142,9 @@ RESULT eDVBDemux::createTSRecorder(ePtr &recorder) return 0; } -RESULT eDVBDemux::getMPEGDecoder(ePtr &decoder) +RESULT eDVBDemux::getMPEGDecoder(ePtr &decoder, int primary) { - decoder = new eTSMPEGDecoder(this, 0); + decoder = new eTSMPEGDecoder(this, primary ? 0 : 1); return 0; } @@ -121,12 +161,15 @@ RESULT eDVBDemux::getSTC(pts_t &pts, int num) if (ioctl(fd, DMX_GET_STC, &stc) < 0) { + eDebug("DMX_GET_STC failed!"); ::close(fd); return -1; } pts = stc.stc; + eDebug("DMX_GET_STC - %lld", pts); + ::close(fd); return 0; } @@ -150,6 +193,14 @@ void eDVBSectionReader::data(int) __u8 data[4096]; // max. section size int r; r = ::read(fd, data, 4096); +#if FUZZING + int j; + for (j = 0; j < r; ++j) + { + if (!(rand()%FUZZING_PROPABILITY)) + data[j] ^= rand(); + } +#endif if(r < 0) { eWarning("ERROR reading section - %m\n"); @@ -178,7 +229,7 @@ eDVBSectionReader::eDVBSectionReader(eDVBDemux *demux, eMainloop *context, RESUL if (fd >= 0) { - notifier=new eSocketNotifier(context, fd, eSocketNotifier::Read, false); + notifier=eSocketNotifier::create(context, fd, eSocketNotifier::Read, false); CONNECT(notifier->activated, eDVBSectionReader::data); res = 0; } else @@ -192,8 +243,6 @@ DEFINE_REF(eDVBSectionReader) eDVBSectionReader::~eDVBSectionReader() { - if (notifier) - delete notifier; if (fd >= 0) ::close(fd); } @@ -217,11 +266,13 @@ RESULT eDVBSectionReader::start(const eDVBSectionFilterMask &mask) #else sct.flags = DMX_IMMEDIATE_START; #endif +#if !FUZZING if (mask.flags & eDVBSectionFilterMask::rfCRC) { sct.flags |= DMX_CHECK_CRC; checkcrc = 1; } else +#endif checkcrc = 0; memcpy(sct.filter.filter, mask.data, DMX_FILTER_SIZE); @@ -268,43 +319,162 @@ RESULT eDVBSectionReader::connectRead(const Slot1 &r, ePtropenDemux(); + + if (m_fd >= 0) + { + ::ioctl(m_fd, DMX_SET_BUFFER_SIZE, 64*1024); + ::fcntl(m_fd, F_SETFL, O_NONBLOCK); + m_notifier = eSocketNotifier::create(context, m_fd, eSocketNotifier::Read, false); + CONNECT(m_notifier->activated, eDVBPESReader::data); + res = 0; + } else + { + perror(filename); + res = errno; + } +} + +DEFINE_REF(eDVBPESReader) + +eDVBPESReader::~eDVBPESReader() +{ + if (m_fd >= 0) + ::close(m_fd); +} + +RESULT eDVBPESReader::start(int pid) +{ + RESULT res; + if (m_fd < 0) + return -ENODEV; + + m_notifier->start(); + +#if HAVE_DVB_API_VERSION < 3 + dmxPesFilterParams flt; + + flt.pesType = DMX_PES_OTHER; +#else + dmx_pes_filter_params flt; + + flt.pes_type = DMX_PES_OTHER; +#endif + + flt.pid = pid; + flt.input = DMX_IN_FRONTEND; + flt.output = DMX_OUT_TAP; + + flt.flags = DMX_IMMEDIATE_START; + + res = ::ioctl(m_fd, DMX_SET_PES_FILTER, &flt); + + if (res) + eWarning("PES filter: DMX_SET_PES_FILTER - %m"); + if (!res) + m_active = 1; + return res; +} + +RESULT eDVBPESReader::stop() +{ + if (!m_active) + return -1; + + m_active=0; + ::ioctl(m_fd, DMX_STOP); + m_notifier->stop(); + + return 0; +} + +RESULT eDVBPESReader::connectRead(const Slot2 &r, ePtr &conn) +{ + conn = new eConnection(this, m_read.connect(r)); + return 0; +} + class eDVBRecordFileThread: public eFilePushThread { public: eDVBRecordFileThread(); - void setTimingPID(int pid); + void setTimingPID(int pid, int type); - void saveTimingInformation(const std::string &filename); + void startSaveMetaInformation(const std::string &filename); + void stopSaveMetaInformation(); + int getLastPTS(pts_t &pts); protected: - void filterRecordData(const unsigned char *data, int len); + int filterRecordData(const unsigned char *data, int len, size_t ¤t_span_remaining); private: eMPEGStreamParserTS m_ts_parser; eMPEGStreamInformation m_stream_info; off_t m_current_offset; + pts_t m_last_pcr; /* very approximate.. */ int m_pid; }; eDVBRecordFileThread::eDVBRecordFileThread() - : m_ts_parser(m_stream_info) + :eFilePushThread(IOPRIO_CLASS_RT, 7), m_ts_parser(m_stream_info) { m_current_offset = 0; } -void eDVBRecordFileThread::setTimingPID(int pid) +void eDVBRecordFileThread::setTimingPID(int pid, int type) +{ + m_ts_parser.setPid(pid, type); +} + +void eDVBRecordFileThread::startSaveMetaInformation(const std::string &filename) { - m_ts_parser.setPid(pid); + m_stream_info.startSave(filename.c_str()); } -void eDVBRecordFileThread::saveTimingInformation(const std::string &filename) +void eDVBRecordFileThread::stopSaveMetaInformation() { - m_stream_info.save(filename.c_str()); + m_stream_info.stopSave(); } -void eDVBRecordFileThread::filterRecordData(const unsigned char *data, int len) +int eDVBRecordFileThread::getLastPTS(pts_t &pts) +{ + return m_ts_parser.getLastPTS(pts); +} + +int eDVBRecordFileThread::filterRecordData(const unsigned char *data, int len, size_t ¤t_span_remaining) { m_ts_parser.parseData(m_current_offset, data, len); m_current_offset += len; + + return len; } DEFINE_REF(eDVBTSRecorder); @@ -314,25 +484,36 @@ eDVBTSRecorder::eDVBTSRecorder(eDVBDemux *demux): m_demux(demux) m_running = 0; m_target_fd = -1; m_thread = new eDVBRecordFileThread(); + CONNECT(m_thread->m_event, eDVBTSRecorder::filepushEvent); +#ifndef HAVE_ADD_PID m_demux->m_dvr_busy = 1; +#endif } eDVBTSRecorder::~eDVBTSRecorder() { stop(); delete m_thread; +#ifndef HAVE_ADD_PID m_demux->m_dvr_busy = 0; +#endif } RESULT eDVBTSRecorder::start() { + std::map::iterator i(m_pids.begin()); + if (m_running) return -1; if (m_target_fd == -1) return -2; - + + if (i == m_pids.end()) + return -3; + char filename[128]; +#ifndef HAVE_ADD_PID #if HAVE_DVB_API_VERSION < 3 snprintf(filename, 128, "/dev/dvb/card%d/dvr%d", m_demux->adapter, m_demux->demux); #else @@ -345,13 +526,54 @@ RESULT eDVBTSRecorder::start() eDebug("FAILED to open dvr (%s) in ts recoder (%m)", filename); return -3; } +#else + snprintf(filename, 128, "/dev/dvb/adapter%d/demux%d", m_demux->adapter, m_demux->demux); + + m_source_fd = ::open(filename, O_RDONLY); + + if (m_source_fd < 0) + { + eDebug("FAILED to open demux (%s) in ts recoder (%m)", filename); + return -3; + } + + ::ioctl(m_source_fd, DMX_SET_BUFFER_SIZE, 1024*1024); + + dmx_pes_filter_params flt; +#if HAVE_DVB_API_VERSION > 3 + flt.pes_type = DMX_PES_OTHER; + flt.output = DMX_OUT_TSDEMUX_TAP; +#else + flt.pes_type = (dmx_pes_type_t)DMX_TAP_TS; + flt.output = DMX_OUT_TAP; +#endif + flt.pid = i->first; + ++i; + flt.input = DMX_IN_FRONTEND; + flt.flags = 0; + int res = ::ioctl(m_source_fd, DMX_SET_PES_FILTER, &flt); + if (res) + { + eDebug("DMX_SET_PES_FILTER: %m"); + ::close(m_source_fd); + return -3; + } + + ::ioctl(m_source_fd, DMX_START); + +#endif + + if (m_target_filename != "") + m_thread->startSaveMetaInformation(m_target_filename); m_thread->start(m_source_fd, m_target_fd); m_running = 1; - - for (std::map::iterator i(m_pids.begin()); i != m_pids.end(); ++i) + + while (i != m_pids.end()) { startPID(i->first); - + ++i; + } + return 0; } @@ -378,11 +600,9 @@ RESULT eDVBTSRecorder::removePID(int pid) return 0; } -RESULT eDVBTSRecorder::setTimingPID(int pid) +RESULT eDVBTSRecorder::setTimingPID(int pid, int type) { - if (m_running) - return -1; - m_thread->setTimingPID(pid); + m_thread->setTimingPID(pid, type); return 0; } @@ -395,6 +615,7 @@ RESULT eDVBTSRecorder::setTargetFD(int fd) RESULT eDVBTSRecorder::setTargetFilename(const char *filename) { m_target_filename = filename; + return 0; } RESULT eDVBTSRecorder::setBoundary(off_t max) @@ -412,13 +633,25 @@ RESULT eDVBTSRecorder::stop() m_thread->stop(); close(m_source_fd); + m_source_fd = -1; - if (m_target_filename != "") - m_thread->saveTimingInformation(m_target_filename + ".ap"); + m_thread->stopSaveMetaInformation(); return 0; } +RESULT eDVBTSRecorder::getCurrentPCR(pts_t &pcr) +{ + if (!m_running) + return 0; + if (!m_thread) + return 0; + /* XXX: we need a lock here */ + + /* we don't filter PCR data, so just use the last received PTS, which is not accurate, but better than nothing */ + return m_thread->getLastPTS(pcr); +} + RESULT eDVBTSRecorder::connectEvent(const Slot1 &event, ePtr &conn) { conn = new eConnection(this, m_event.connect(event)); @@ -427,6 +660,7 @@ RESULT eDVBTSRecorder::connectEvent(const Slot1 &event, ePtropenDemux(); if (fd < 0) { @@ -458,13 +692,61 @@ RESULT eDVBTSRecorder::startPID(int pid) return -1; } m_pids[pid] = fd; - +#else + while(true) { +#if HAVE_DVB_API_VERSION > 3 + __u16 p = pid; + if (::ioctl(m_source_fd, DMX_ADD_PID, &p) < 0) { +#else + if (::ioctl(m_source_fd, DMX_ADD_PID, pid) < 0) { +#endif + perror("DMX_ADD_PID"); + if (errno == EAGAIN || errno == EINTR) { + eDebug("retry!"); + continue; + } + } else + m_pids[pid] = 1; + break; + } +#endif return 0; } void eDVBTSRecorder::stopPID(int pid) { +#ifndef HAVE_ADD_PID if (m_pids[pid] != -1) ::close(m_pids[pid]); +#else + if (m_pids[pid] != -1) + { + while(true) { +#if HAVE_DVB_API_VERSION > 3 + __u16 p = pid; + if (::ioctl(m_source_fd, DMX_REMOVE_PID, &p) < 0) { +#else + if (::ioctl(m_source_fd, DMX_REMOVE_PID, pid) < 0) { +#endif + perror("DMX_REMOVE_PID"); + if (errno == EAGAIN || errno == EINTR) { + eDebug("retry!"); + continue; + } + } + break; + } + } +#endif m_pids[pid] = -1; } + +void eDVBTSRecorder::filepushEvent(int event) +{ + switch (event) + { + case eFilePushThread::evtWriteError: + m_event(eventWriteError); + break; + } +}