X-Git-Url: http://code.vuplus.com/gitweb/?p=vuplus_dvbapp;a=blobdiff_plain;f=lib%2Fdvb%2Fdemux.cpp;h=f277468d248b89d0e700a29c17693d81c6e0a011;hp=f50f4bb29a9a4d02ed8f9202f7f69e0bd57df2d9;hb=d8e08fe0a7304ed06b3de0b9ad6d4265ec8063b8;hpb=aa3d1162bef321bd2d0effdafce6eb080660a7ca diff --git a/lib/dvb/demux.cpp b/lib/dvb/demux.cpp index f50f4bb..f2b494a 100644 --- a/lib/dvb/demux.cpp +++ b/lib/dvb/demux.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -6,15 +5,18 @@ #include #include -#include +// #define FUZZING 1 -#if HAVE_DVB_API_VERSION < 3 -#include -#ifndef DMX_SET_NEGFILTER_MASK - #define DMX_SET_NEGFILTER_MASK _IOW('o',48,uint8_t *) +#if FUZZING + /* change every 1:FUZZING_PROPABILITY byte */ +#define FUZZING_PROPABILITY 100 #endif -#else + #include + +#ifndef DMX_ADD_PID +#define DMX_ADD_PID _IOW('o', 51, __u16) +#define DMX_REMOVE_PID _IOW('o', 52, __u16) #endif #include "crc32.h" @@ -34,8 +36,47 @@ eDVBDemux::~eDVBDemux() { } +int eDVBDemux::openDemux(void) +{ + char filename[128]; + snprintf(filename, 128, "/dev/dvb/adapter%d/demux%d", adapter, demux); + return ::open(filename, O_RDWR); +} + +int eDVBDemux::openDVR(int flags) +{ + char filename[128]; + snprintf(filename, 128, "/dev/dvb/adapter%d/dvr%d", adapter, demux); + return ::open(filename, flags); +} + DEFINE_REF(eDVBDemux) +RESULT eDVBDemux::setSourceFrontend(int fenum) +{ + int fd = openDemux(); + if (fd < 0) return -1; + 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; +} + +RESULT eDVBDemux::setSourcePVR(int pvrnum) +{ + int fd = openDemux(); + if (fd < 0) return -1; + int n = DMX_SOURCE_DVR0 + pvrnum; + int res = ::ioctl(fd, DMX_SET_SOURCE, &n); + source = -1; + ::close(fd); + return res; +} + RESULT eDVBDemux::createSectionReader(eMainloop *context, ePtr &reader) { RESULT res; @@ -45,6 +86,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) @@ -53,9 +103,49 @@ RESULT eDVBDemux::createTSRecorder(ePtr &recorder) return 0; } -RESULT eDVBDemux::getMPEGDecoder(ePtr &decoder) +RESULT eDVBDemux::getMPEGDecoder(ePtr &decoder, int index) +{ + decoder = new eTSMPEGDecoder(this, index); + return 0; +} + +RESULT eDVBDemux::getSTC(pts_t &pts, int num) { - decoder = new eTSMPEGDecoder(this, 0); + int fd = openDemux(); + + if (fd < 0) + return -ENODEV; + + struct dmx_stc stc; + stc.num = num; + stc.base = 1; + + 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; +} + +RESULT eDVBDemux::flush() +{ + // FIXME: implement flushing the PVR queue here. + + m_event(evtFlush); + return 0; +} + +RESULT eDVBDemux::connectEvent(const Slot1 &event, ePtr &conn) +{ + conn = new eConnection(this, m_event.connect(event)); return 0; } @@ -64,6 +154,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"); @@ -74,7 +172,10 @@ void eDVBSectionReader::data(int) // this check should never happen unless the driver is crappy! unsigned int c; if ((c = crc32((unsigned)-1, data, r))) - eFatal("crc32 failed! is %x\n", c); + { + eDebug("crc32 failed! is %x\n", c); + return; + } } if (active) read(data); @@ -85,18 +186,11 @@ void eDVBSectionReader::data(int) eDVBSectionReader::eDVBSectionReader(eDVBDemux *demux, eMainloop *context, RESULT &res): demux(demux) { char filename[128]; -#if HAVE_DVB_API_VERSION < 3 - sprintf(filename, "/dev/dvb/card%d/demux%d", demux->adapter, demux->demux); -#else - sprintf(filename, "/dev/dvb/adapter%d/demux%d", demux->adapter, demux->demux); -#endif - fd = ::open(filename, O_RDWR); - - eDebug("eDVBSectionReader has fd %d", fd); + fd = demux->openDemux(); if (fd >= 0) { - notifier=new eSocketNotifier(context, fd, eSocketNotifier::Read); + notifier=eSocketNotifier::create(context, fd, eSocketNotifier::Read, false); CONNECT(notifier->activated, eDVBSectionReader::data); res = 0; } else @@ -110,57 +204,47 @@ DEFINE_REF(eDVBSectionReader) eDVBSectionReader::~eDVBSectionReader() { - if (notifier) - delete notifier; if (fd >= 0) ::close(fd); } +RESULT eDVBSectionReader::setBufferSize(int size) +{ + int res=::ioctl(fd, DMX_SET_BUFFER_SIZE, size); + if (res < 0) + eDebug("eDVBSectionReader DMX_SET_BUFFER_SIZE failed(%m)"); + return res; +} + RESULT eDVBSectionReader::start(const eDVBSectionFilterMask &mask) { RESULT res; if (fd < 0) return -ENODEV; -#if HAVE_DVB_API_VERSION < 3 - dmxSctFilterParams sct; -#else + notifier->start(); dmx_sct_filter_params sct; -#endif sct.pid = mask.pid; sct.timeout = 0; -#if HAVE_DVB_API_VERSION < 3 - sct.flags = 0; -#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); memcpy(sct.filter.mask, mask.mask, DMX_FILTER_SIZE); -#if HAVE_DVB_API_VERSION >= 3 memcpy(sct.filter.mode, mask.mode, DMX_FILTER_SIZE); -#endif + setBufferSize(8192*8); res = ::ioctl(fd, DMX_SET_FILTER, &sct); if (!res) { -#if HAVE_DVB_API_VERSION < 3 - res = ::ioctl(fd, DMX_SET_NEGFILTER_MASK, mask.mode); - if (!res) - { - res = ::ioctl(fd, DMX_START, 0); - if (!res) - active = 1; - } -#else active = 1; -#endif } return res; } @@ -172,7 +256,8 @@ RESULT eDVBSectionReader::stop() active=0; ::ioctl(fd, DMX_STOP); - + notifier->stop(); + return 0; } @@ -182,141 +267,233 @@ RESULT eDVBSectionReader::connectRead(const Slot1 &r, ePtropenDemux(); + + if (m_fd >= 0) + { + setBufferSize(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; + } +} -eDVBTSRecorderThread::eDVBTSRecorderThread() +RESULT eDVBPESReader::setBufferSize(int size) { - m_stop = 0; - m_buf_start = m_buf_end = 0; + int res = ::ioctl(m_fd, DMX_SET_BUFFER_SIZE, size); + if (res < 0) + eDebug("eDVBPESReader DMX_SET_BUFFER_SIZE failed(%m)"); + return res; } -static void signal_handler(int x) +DEFINE_REF(eDVBPESReader) + +eDVBPESReader::~eDVBPESReader() { + if (m_fd >= 0) + ::close(m_fd); } -void eDVBTSRecorderThread::thread() +RESULT eDVBPESReader::start(int pid) { - eDebug("RECORDING THREAD START"); - // this is race. FIXME. + RESULT res; + if (m_fd < 0) + return -ENODEV; + + m_notifier->start(); + + dmx_pes_filter_params flt; - /* we set the signal to not restart syscalls, so we can detect our signal. */ - struct sigaction act; - act.sa_handler = signal_handler; // no, SIG_IGN doesn't do it :/ - act.sa_flags = 0; - sigaction(SIGUSR1, &act, 0); + flt.pes_type = DMX_PES_OTHER; + flt.pid = pid; + flt.input = DMX_IN_FRONTEND; + flt.output = DMX_OUT_TAP; - /* m_stop must be evaluated after each syscall. */ - while (!m_stop) - { - /* first try flushing the bufptr */ - if (m_buf_start != m_buf_end) - { - // TODO: take care of boundaries. - int w = write(m_fd_dest, m_buffer + m_buf_start, m_buf_end - m_buf_start); - if (w <= 0) - { - if (errno == -EINTR) - continue; - eDebug("eDVBTSRecorder *write error* - not yet handled"); - // ... we would stop the thread - } - printf("TSRECORD: wrote %d bytes\n", w); - m_buf_start += w; - continue; - } - - /* now fill our buffer. */ - m_buf_start = 0; - m_buf_end = read(m_fd_source, m_buffer, sizeof(m_buffer)); - if (m_buf_end < 0) - { - m_buf_end = 0; - if (errno == EINTR) - continue; - eDebug("eDVBTSRecorder *read error* - not yet handled"); - } - printf("TSRECORD: read %d bytes\n", m_buf_end); - } + flt.flags = DMX_IMMEDIATE_START; + + res = ::ioctl(m_fd, DMX_SET_PES_FILTER, &flt); - eDebug("RECORDING THREAD STOP"); + 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; +} + +eDVBRecordFileThread::eDVBRecordFileThread() + :eFilePushThread(IOPRIO_CLASS_RT, 7), m_ts_parser(m_stream_info) +{ + m_current_offset = 0; +} + +void eDVBRecordFileThread::setTimingPID(int pid, int type) +{ + m_ts_parser.setPid(pid, type); +} + +void eDVBRecordFileThread::startSaveMetaInformation(const std::string &filename) +{ + m_stream_info.startSave(filename.c_str()); } -void eDVBTSRecorderThread::start(int fd_source, int fd_dest) +void eDVBRecordFileThread::stopSaveMetaInformation() { - m_fd_source = fd_source; - m_fd_dest = fd_dest; - m_stop = 0; - run(); + m_stream_info.stopSave(); } -void eDVBTSRecorderThread::stop() +void eDVBRecordFileThread::enableAccessPoints(bool enable) { - m_stop = 1; - sendSignal(SIGUSR1); - kill(); + m_ts_parser.enableAccessPoints(enable); } +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); + eDVBTSRecorder::eDVBTSRecorder(eDVBDemux *demux): m_demux(demux) { m_running = 0; - m_format = 0; m_target_fd = -1; - m_thread = new eDVBTSRecorderThread(); - m_demux->m_dvr_busy = 1; + m_thread = new eDVBRecordFileThread(); + CONNECT(m_thread->m_event, eDVBTSRecorder::filepushEvent); } eDVBTSRecorder::~eDVBTSRecorder() { stop(); delete m_thread; - m_demux->m_dvr_busy = 0; } 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]; -#if HAVE_DVB_API_VERSION < 3 - snprintf(filename, 128, "/dev/dvb/card%d/dvr%d", m_demux->adapter, m_demux->demux); -#else - snprintf(filename, 128, "/dev/dvb/adapter%d/dvr%d", m_demux->adapter, m_demux->demux); -#endif + 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 dvr (%s) in ts recoder (%m)", filename); + eDebug("FAILED to open demux (%s) in ts recoder (%m)", filename); + return -3; + } + + setBufferSize(1024*1024); + + dmx_pes_filter_params flt; + flt.pes_type = DMX_PES_OTHER; + flt.output = DMX_OUT_TSDEMUX_TAP; + 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); + m_source_fd = -1; return -3; } + ::ioctl(m_source_fd, DMX_START); + + 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; } +RESULT eDVBTSRecorder::setBufferSize(int size) +{ + int res = ::ioctl(m_source_fd, DMX_SET_BUFFER_SIZE, size); + if (res < 0) + eDebug("eDVBTSRecorder DMX_SET_BUFFER_SIZE failed(%m)"); + return res; +} + RESULT eDVBTSRecorder::addPID(int pid) { if (m_pids.find(pid) != m_pids.end()) @@ -340,11 +517,9 @@ RESULT eDVBTSRecorder::removePID(int pid) return 0; } -RESULT eDVBTSRecorder::setFormat(int format) +RESULT eDVBTSRecorder::setTimingPID(int pid, int type) { - if (m_running) - return -1; - m_format = format; + m_thread->setTimingPID(pid, type); return 0; } @@ -354,25 +529,86 @@ RESULT eDVBTSRecorder::setTargetFD(int fd) return 0; } +RESULT eDVBTSRecorder::setTargetFilename(const char *filename) +{ + m_target_filename = filename; + + std::string target_path = m_target_filename; + std::string::size_type filePos = target_path.rfind('/'); + m_thread->setTSPath(target_path.erase(filePos)); + + return 0; +} + +RESULT eDVBTSRecorder::enableAccessPoints(bool enable) +{ + m_thread->enableAccessPoints(enable); + return 0; +} + RESULT eDVBTSRecorder::setBoundary(off_t max) { return -1; // not yet implemented } +RESULT eDVBTSRecorder::setTimeshift(bool enable) +{ + m_thread->setTimeshift(enable); +} + RESULT eDVBTSRecorder::stop() { + int state=3; + for (std::map::iterator i(m_pids.begin()); i != m_pids.end(); ++i) stopPID(i->first); if (!m_running) return -1; + + /* workaround for record thread stop */ + if (m_source_fd >= 0) + { + if (::ioctl(m_source_fd, DMX_STOP) < 0) + perror("DMX_STOP"); + else + state &= ~1; + + if (::close(m_source_fd) < 0) + perror("close"); + else + state &= ~2; + m_source_fd = -1; + } + m_thread->stop(); - - close(m_source_fd); - + + if (state & 3) + { + if (m_source_fd >= 0) + { + ::close(m_source_fd); + m_source_fd = -1; + } + } + + m_running = 0; + 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)); @@ -381,49 +617,46 @@ RESULT eDVBTSRecorder::connectEvent(const Slot1 &event, ePtradapter, m_demux->demux); -#else - snprintf(filename, 128, "/dev/dvb/adapter%d/demux%d", m_demux->adapter, m_demux->demux); -#endif - int fd = ::open(filename, O_RDWR); - if (fd < 0) - { - eDebug("FAILED to open demux (%s) in ts recoder (%m)", filename); - return -1; - } - -#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_TS_TAP; - - flt.flags = DMX_IMMEDIATE_START; - - int res = ::ioctl(fd, DMX_SET_PES_FILTER, &flt); - if (res < 0) - { - eDebug("set pes filter failed!"); - ::close(fd); - return -1; + while(true) { + __u16 p = pid; + if (::ioctl(m_source_fd, DMX_ADD_PID, &p) < 0) { + perror("DMX_ADD_PID"); + if (errno == EAGAIN || errno == EINTR) { + eDebug("retry!"); + continue; + } + } else + m_pids[pid] = 1; + break; } - m_pids[pid] = fd; - return 0; } void eDVBTSRecorder::stopPID(int pid) { - ::close(m_pids[pid]); + if (m_pids[pid] != -1) + { + while(true) { + __u16 p = pid; + if (::ioctl(m_source_fd, DMX_REMOVE_PID, &p) < 0) { + perror("DMX_REMOVE_PID"); + if (errno == EAGAIN || errno == EINTR) { + eDebug("retry!"); + continue; + } + } + break; + } + } m_pids[pid] = -1; } + +void eDVBTSRecorder::filepushEvent(int event) +{ + switch (event) + { + case eFilePushThread::evtWriteError: + m_event(eventWriteError); + break; + } +}