diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp new file mode 100644 index 0000000..e564001 --- /dev/null +++ b/libtriple/dmx_td.cpp @@ -0,0 +1,442 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "dmx_td.h" +#include "lt_debug.h" + +cDemux *videoDemux = NULL; +cDemux *audioDemux = NULL; +//cDemux *pcrDemux = NULL; + +static const char *DMX_T[] = { + "", + "DMX_VIDEO_CHANNEL", + "DMX_AUDIO_CHANNEL", + "DMX_PES_CHANNEL", + "DMX_PSI_CHANNEL", + "DMX_PIP_CHANNEL", + "DMX_TP_CHANNEL", + "DMX_PCR_ONLY_CHANNEL" +}; + +/* map the device numbers as used to the TD devices */ +static const char *devname[] = { + "/dev/" DEVICE_NAME_DEMUX "0", + "/dev/" DEVICE_NAME_DEMUX "1", + "/dev/" DEVICE_NAME_DEMUX "2", +}; + +cDemux::cDemux(int n) +{ + if (n < 0 || n > 2) + { + fprintf(stderr, "ERROR: cDemux::cDemux, n invalid (%d)\n", n); + num = 0; + } + else + num = n; + fd = -1; +} + +cDemux::~cDemux() +{ + fprintf(stderr, "cDemux::%s #%d fd: %d\n", __FUNCTION__, num, fd); + Close(); +} + +bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBufferSize) +{ + if (fd > -1) + fprintf(stderr, "cDemux::Open FD ALREADY OPENED? fd = %d\n", fd); + fd = open(devname[num], O_RDWR); + if (fd < 0) + { + fprintf(stderr, "cDemux::Open %s: %m", devname[num]); + return false; + } + lt_debug("cDemux::Open #%d pes_type: %s (%d), uBufferSize: %d devname: %s fd: %d\n", + num, DMX_T[pes_type], pes_type, uBufferSize, devname[num], fd); + + dmx_type = pes_type; + + if (!pesfds.empty()) + { + fprintf(stderr, "ERROR! pesfds not empty!\n"); /* TODO: error handling */ + return false; + } + if (pes_type == DMX_TP_CHANNEL) + { + struct demux_bucket_para bp; + bp.unloader.unloader_type = UNLOADER_TYPE_TRANSPORT; + bp.unloader.threshold = 128; + ioctl(fd, DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0); + ioctl(fd, DEMUX_SET_BUFFER_SIZE, 230400); + ioctl(fd, DEMUX_FILTER_BUCKET_SET, &bp); + return true; + } + if (uBufferSize > 0) + { + /* probably uBufferSize == 0 means "use default size". TODO: find a reasonable default */ + if (ioctl(fd, DEMUX_SET_BUFFER_SIZE, uBufferSize) < 0) + fprintf(stderr, "cDemux::Open DEMUX_SET_BUFFER_SIZE failed (%m)\n"); + } + + return true; +} + +void cDemux::Close(void) +{ + if (fd < 0) + { + fprintf(stderr, "cDemux::%s #%d: not open!\n", __FUNCTION__, num); + return; + } + + for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) + { + fprintf(stderr, "iterator: stopping and closing demux fd %d\n", *i); + if (ioctl(*i, DEMUX_STOP) < 0) + perror("DEMUX_STOP"); + if (close(*i) < 0) + perror("close"); + } + pesfds.clear(); + ioctl(fd, DEMUX_STOP); + close(fd); + fd = -1; +} + +bool cDemux::Start(void) +{ + if (fd < 0) + { + fprintf(stderr, "cDemux::%s #%d: not open!\n", __FUNCTION__, num); + return false; + } + + for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) + { + fprintf(stderr, "iterator: starting demux fd %d\n", *i); + if (ioctl(*i, DEMUX_START) < 0) + perror("DEMUX_START"); + } + ioctl(fd, DEMUX_START); + return true; +} + +bool cDemux::Stop(void) +{ + if (fd < 0) + { + fprintf(stderr, "cDemux::%s #%d: not open!\n", __FUNCTION__, num); + return false; + } + for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) + { + fprintf(stderr, "iterator: stopping demux fd %d\n", *i); + if (ioctl(*i, DEMUX_STOP) < 0) + perror("DEMUX_STOP"); + } + ioctl(fd, DEMUX_STOP); + return true; +} + +int cDemux::Read(unsigned char *buff, int len, int timeout) +{ +#if 0 + if (len != 4095 && timeout != 10) + fprintf(stderr, "cDemux::%s #%d fd: %d type: %s len: %d timeout: %d\n", + __FUNCTION__, num, fd, DMX_T[dmx_type], len, timeout); +#endif + int rc; + struct pollfd ufds; + ufds.fd = fd; + ufds.events = POLLIN; + ufds.revents = 0; + + if (timeout > 0) + { + retry: + rc = ::poll(&ufds, 1, timeout); + if (!rc) + return 0; // timeout + else if (rc < 0) + { + perror("[cDemux::Read] poll"); + /* happens, when running under gdb... */ + if (errno == EINTR) + goto retry; + return -1; + } + if (ufds.revents & POLLERR) /* POLLERR means buffer error, i.e. buffer overflow */ + { + fprintf(stderr, "[cDemux::Read] received POLLERR, fd %d, revents 0x%x\n", fd, ufds.revents); + /* this seems to happen sometimes at recording start, without bad effects */ + return 0; + } + if (ufds.revents & POLLHUP) /* we get POLLHUP if e.g. a too big DMX_BUFFER_SIZE was set */ + { + fprintf(stderr, "[cDemux::Read] received POLLHUP, fd %d\n", fd); + return -1; + } + if (!(ufds.revents & POLLIN)) /* we requested POLLIN but did not get it? */ + { + fprintf(stderr, "cDemux::%s: not ufds.revents&POLLIN, please report! " + "revents: 0x%x fd: %d rc: %d '%m'\n", __FUNCTION__, ufds.revents, fd, rc); + return 0; + } + } + + rc = ::read(fd, buff, len); + //fprintf(stderr, "fd %d ret: %d\n", fd, rc); + if (rc < 0) + perror ("[cDemux::Read] read"); + + return rc; +} + +bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filter, + const unsigned char * const mask, int len, int timeout, + const unsigned char * const negmask) +{ + struct demux_filter_para flt; + memset(&flt, 0, sizeof(flt)); + + if (len > FILTER_LENGTH - 2) + fprintf(stderr, "cDemux::sectionFilter #%d: len too long: %d, FILTER_LENGTH: %d\n", num, len, FILTER_LENGTH); + + flt.pid = pid; + flt.filter_length = len + 2 * (len > 1); /* only add the two bytes if required */ + flt.filter[0] = filter[0]; + flt.mask[0] = mask[0]; + flt.timeout = timeout; + memcpy(&flt.filter[3], &filter[1], len - 1); + memcpy(&flt.mask[3], &mask[1], len - 1); + if (negmask != NULL) + { + flt.positive[0] = negmask[0]; + memcpy(&flt.positive[3], &negmask[1], len - 1); + } + + flt.flags = XPDF_IMMEDIATE_START; + + int to = 0; + switch (filter[0]) { + case 0x00: /* program_association_section */ + to = 2000; + break; + case 0x01: /* conditional_access_section */ + to = 6000; + break; + case 0x02: /* program_map_section */ + to = 1500; + break; + case 0x03: /* transport_stream_description_section */ + to = 10000; + break; + /* 0x04 - 0x3F: reserved */ + case 0x40: /* network_information_section - actual_network */ + to = 10000; + break; + case 0x41: /* network_information_section - other_network */ + to = 15000; + break; + case 0x42: /* service_description_section - actual_transport_stream */ + to = 10000; + break; + /* 0x43 - 0x45: reserved for future use */ + case 0x46: /* service_description_section - other_transport_stream */ + to = 10000; + break; + /* 0x47 - 0x49: reserved for future use */ + case 0x4A: /* bouquet_association_section */ + to = 11000; + break; + /* 0x4B - 0x4D: reserved for future use */ + case 0x4E: /* event_information_section - actual_transport_stream, present/following */ + to = 2000; + break; + case 0x4F: /* event_information_section - other_transport_stream, present/following */ + to = 10000; + break; + /* 0x50 - 0x5F: event_information_section - actual_transport_stream, schedule */ + /* 0x60 - 0x6F: event_information_section - other_transport_stream, schedule */ + case 0x70: /* time_date_section */ + flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ + //flt.pid = 0x0014; + to = 30000; + break; + case 0x71: /* running_status_section */ + flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ + to = 0; + break; + case 0x72: /* stuffing_section */ + flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ + to = 0; + break; + case 0x73: /* time_offset_section */ + //flt.pid = 0x0014; + to = 30000; + break; + /* 0x74 - 0x7D: reserved for future use */ + case 0x7E: /* discontinuity_information_section */ + flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ + to = 0; + break; + case 0x7F: /* selection_information_section */ + to = 0; + break; + /* 0x80 - 0x8F: ca_message_section */ + /* 0x90 - 0xFE: user defined */ + /* 0xFF: reserved */ + default: + break; +// return -1; + } + if (timeout == 0) + flt.timeout = to; +#if 0 + fprintf(stderr, "cDemux::%s #%d pid:0x%04hx fd:%d type:%s len:%d/%d to:%d flags:%x\n", __FUNCTION__, num, pid, fd, DMX_T[dmx_type], len,flt.filter_length, flt.timeout,flt.flags); + fprintf(stderr,"filt: ");for(int i=0;i= 0x0002 && pid <= 0x000f) || pid >= 0x1fff) + return false; + +fprintf(stderr, "cDemux::%s #%d pid: 0x%04hx fd: %d type: %s\n", __FUNCTION__, num, pid, fd, DMX_T[dmx_type]); + + if (dmx_type == DMX_TP_CHANNEL) + { + unsigned int n = pesfds.size(); + addPid(pid); + return (n != pesfds.size()); + } + memset(&flt, 0, sizeof(flt)); + flt.pid = pid; + flt.output = OUT_DECODER; + switch (dmx_type) { + case DMX_PCR_ONLY_CHANNEL: + flt.pesType = DMX_PES_PCR; + break; + case DMX_AUDIO_CHANNEL: + flt.pesType = DMX_PES_AUDIO; + break; + case DMX_VIDEO_CHANNEL: + flt.pesType = DMX_PES_VIDEO; + break; + case DMX_PES_CHANNEL: + flt.unloader.unloader_type = UNLOADER_TYPE_PAYLOAD; + flt.unloader.threshold = 64; + flt.pesType = DMX_PES_OTHER; + flt.output = OUT_MEMORY; + default: + flt.pesType = DMX_PES_OTHER; + } + + return (ioctl(fd, DEMUX_FILTER_PES_SET, &flt) >= 0); +} + +void cDemux::SetSyncMode(AVSYNC_TYPE /*mode*/) +{ + fprintf(stderr, "cDemux::%s #%d\n", __FUNCTION__, num); +} + +void *cDemux::getBuffer() +{ + fprintf(stderr, "cDemux::%s #%d\n", __FUNCTION__, num); + return NULL; +} + +void *cDemux::getChannel() +{ + fprintf(stderr, "cDemux::%s #%d\n", __FUNCTION__, num); + return NULL; +} + +void cDemux::addPid(unsigned short Pid) +{ + int pfd; + int ret; + struct demux_pes_para p; + if (dmx_type != DMX_TP_CHANNEL) + { + fprintf(stderr, "cDemux::%s pes_type!=DMX_TP_CHANNEL (%s) not implemented yet! pid=%hx\n", __FUNCTION__, DMX_T[dmx_type], Pid); + return; + } + if (fd == -1) + fprintf(stderr, "cDemux::%s bucketfd not yet opened? pid=%hx\n", __FUNCTION__, Pid); + pfd = open(devname[num], O_RDWR); + if (pfd < 0) + { + fprintf(stderr, "cDemux::%s #%d Pid = %hx open failed (%m)\n", __FUNCTION__, num, Pid); + return; + } + fprintf(stderr, "cDemux::%s #%d Pid = %hx pfd = %d\n", __FUNCTION__, num, Pid, pfd); + + p.pid = Pid; + p.pesType = DMX_PES_OTHER; + p.output = OUT_NOTHING; + p.flags = 0; + p.unloader.unloader_type = UNLOADER_TYPE_BUCKET; + p.unloader.threshold = 128; + + ioctl(pfd, DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0); + ret = ioctl(pfd, DEMUX_SET_BUFFER_SIZE, 0x10000); // 64k + if (ret == -1) + perror("DEMUX_SET_BUFFER_SIZE"); + else + { + ret = ioctl(pfd, DEMUX_FILTER_PES_SET, &p); + if (ret == -1) + perror("DEMUX_FILTER_PES_SET"); + } + if (ret != -1) + /* success! */ + pesfds.push_back(pfd); + else + /* error! */ + close(pfd); + return; +} + +void cDemux::getSTC(int64_t * STC) +{ + lt_debug("cDemux::%s #%d\n", __FUNCTION__, num); + /* this is a guess, but seems to work... int32_t gives errno 515... */ +#define STC_TYPE uint64_t + STC_TYPE stc; + if (ioctl(fd, DEMUX_GET_CURRENT_STC, &stc)) + perror("cDemux::getSTC DEMUX_GET_CURRENT_STC"); + *STC = (stc >> 32); +} + +int cDemux::getUnit(void) +{ + lt_debug("cDemux::%s #%d\n", __FUNCTION__, num); + /* just guessed that this is the right thing to do. + right now this is only used by the CA code which is stubbed out + anyway */ + return num; +} diff --git a/libtriple/dmx_td.h b/libtriple/dmx_td.h new file mode 100644 index 0000000..aba9ec8 --- /dev/null +++ b/libtriple/dmx_td.h @@ -0,0 +1,56 @@ +#ifndef __DEMUX_TD_H +#define __DEMUX_TD_H + +#include +#include +extern "C" { +#include +#include +} +#if defined DMX_FILTER_SIZE +#undef DMX_FILTER_SIZE +#endif +#define DMX_FILTER_SIZE FILTER_LENGTH + +typedef enum +{ + DMX_VIDEO_CHANNEL = 1, + DMX_AUDIO_CHANNEL, + DMX_PES_CHANNEL, + DMX_PSI_CHANNEL, + DMX_PIP_CHANNEL, + DMX_TP_CHANNEL, + DMX_PCR_ONLY_CHANNEL +} DMX_CHANNEL_TYPE; + +class cDemux +{ + private: + int num; + int fd; + DMX_CHANNEL_TYPE dmx_type; + std::vector pesfds; + public: + + bool Open(DMX_CHANNEL_TYPE pes_type, void * x = NULL, int y = 0); + void Close(void); + bool Start(void); + bool Stop(void); + int Read(unsigned char *buff, int len, int Timeout = 0); + bool sectionFilter(unsigned short pid, const unsigned char * const filter, const unsigned char * const mask, int len, int Timeout = 0, const unsigned char * const negmask = NULL); + bool pesFilter(const unsigned short pid); +#define AVSYNC_TYPE int + void SetSyncMode(AVSYNC_TYPE mode); + void * getBuffer(); + void * getChannel(); + DMX_CHANNEL_TYPE getChannelType(void) { return dmx_type; }; + void addPid(unsigned short pid); + void getSTC(int64_t * STC); + int getUnit(void); + int getFD(void) { return fd; }; /* needed by cPlayback class */ + // + cDemux(int num = 0); + ~cDemux(); +}; + +#endif //__DEMUX_H