/** * This listens to the DVB stream for the PAT/PMT and time, * which is unfortunately needed as input for the CA. * * You call either ca_start_in_new_thread() or * (if you create the thread yourself) ca_start() and later * ca_stop_thread() / ca_stop(). * * Uses open()/close(), poll() and ioctl() system calls. * * Based on zap_dvb.c and zap_ca.c * * Copyright (C) 2007 Ben Bucksch * Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com) * Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "poll.h" #include "zap_ca.h" #include #include #include #include #include #include #include #include #include #include static void process_pat(int pat_fd, const char* demuxDeviceFilename, int channel_service_id, int *pmt_fd, struct pollfd *pollfd); static void process_tdt(int tdt_fd); static void process_pmt(int pmt_fd, int channel_service_id); static void* ca_start_run(void* params); static int create_filter(const char* demuxDeviceFilename, uint16_t pid, uint8_t table_id); ///////////////////////////////////////////////////////////// // Thread ///////////////////////////////////////////////////////////// class CAThread { public: int myAdapterNo; int myChannelServiceID; bool myStopSignal; JNIEnv* myEnv; // only temporary pthread_t myThreadID; }; static int pat_version = -1; // TODO move to CAThread? static int ca_pmt_version = -1; /** * Creates a thread and starts ca_start() */ CAThread* ca_start_in_new_thread(int adapterNo, int channelServiceID, JNIEnv* env) { CAThread* thread = new CAThread(); thread->myAdapterNo = adapterNo; thread->myChannelServiceID = channelServiceID; thread->myEnv = env; thread->myStopSignal = false; pthread_create(&(thread->myThreadID), NULL, ca_start_run, (void*) thread); return thread; } // callback, for use in ca_start_in_new_thread() only // starting point of thread static void* ca_start_run(void* param) { CAThread* thread = (CAThread*) param; ca_start(thread->myAdapterNo, thread->myChannelServiceID, &(thread->myStopSignal), thread->myEnv); thread->myEnv = 0; return 0; } void ca_stop_thread(CAThread* thread) { ca_stop(&(thread->myStopSignal)); pthread_join(thread->myThreadID, NULL); delete thread; } ///////////////////////////////////////////////////////////// // ca_start(), incl. setup and poll() ///////////////////////////////////////////////////////////// void ca_stop(bool* stopSignal) { (*stopSignal) = true; zap_ca_stop(); } #define ERROR_INCASTART(test, message) \ { \ if (test) \ { \ delete[] demuxDeviceFilename; \ if (pat_fd > 0) close(pat_fd); \ if (pmt_fd > 0) close(pmt_fd); \ if (tdt_fd > 0) close(tdt_fd); \ jclass eClass = env->FindClass("org/bucksch/zeipis/recorder/CantUseTVCard"); \ if (eClass == 0) \ return; \ env->ThrowNew(eClass, message); \ return; \ } \ } /** * Sets up the CA for the tuned transponder * Call XXX before/after tuning * * You must call this in a thread, because it will block, * in fact it will loop infinitely until you call ca_stop(). * * @param adapterNo E.g. 0 for "/dev/dvb/adapter0/" * @param channel_service_id ID for channel */ void ca_start(int adapterNo, int channelServiceID, bool* stopSignal, JNIEnv* env) { std::string adapter = "/dev/dvb/adapter0/"; adapter[adapter.size() - 2] = 48 + adapterNo; std::string demuxDeviceFilenameStr = adapter + "demux0"; const char* demuxDeviceFilename = demuxDeviceFilenameStr.c_str(); //std::cout << "Using adapter " << adapterNo << " " << adapter << " with " << demuxDeviceFilename // << " . Service ID " << channelServiceID << std::endl; struct zap_ca_params zap_ca_params1; zap_ca_params1.adapter_id = adapterNo; zap_ca_params1.caslot_num = 0; // TODO support several CAMs per DVB card zap_ca_params1.moveca = 0; zap_ca_start(&zap_ca_params1); // TODO creates yet another pthread :-( struct pollfd pollfds[3]; int pat_fd = -1; int pmt_fd = -1; int tdt_fd = -1; // Create PAT filter pat_fd = create_filter(demuxDeviceFilename, TRANSPORT_PAT_PID, 0x00); // stag_mpeg_program_association ERROR_INCASTART(pat_fd < 0, "Failed to create PAT section filter"); pollfds[0].fd = pat_fd; pollfds[0].events = POLLIN|POLLPRI|POLLERR; // Create TDT filter tdt_fd = create_filter(demuxDeviceFilename, TRANSPORT_TDT_PID, 0x70); // stag_dvb_time_date ERROR_INCASTART(tdt_fd < 0, "Failed to create TDT section filter"); pollfds[1].fd = tdt_fd; pollfds[1].events = POLLIN|POLLPRI|POLLERR; // Zero PMT filter for now, will be filled in later by process_pat() pollfds[2].fd = 0; pollfds[2].events = 0; // Infinite loop until ca_stop is called // to get PMT/PAT for CA while(!(*stopSignal)) { // is there SI data? int count = poll(pollfds, 3, 100); // system call to efficiently wait for new data ERROR_INCASTART(count < 0, "Poll error"); if (count == 0) continue; if (pollfds[0].revents & (POLLIN|POLLPRI)) // PAT process_pat(pat_fd, demuxDeviceFilename, channelServiceID, &pmt_fd, &pollfds[2]); if (pollfds[1].revents & (POLLIN|POLLPRI)) // TDT process_tdt(tdt_fd); if (pollfds[2].revents & (POLLIN|POLLPRI)) // PMT process_pmt(pmt_fd, channelServiceID); } close(pat_fd); close(pmt_fd); close(tdt_fd); } static int create_filter(const char* demuxDeviceFilename, uint16_t pid, uint8_t table_id) { int demux_fd = open(demuxDeviceFilename, O_RDWR|O_NONBLOCK); if (demux_fd < 0) { return -1; } struct dmx_sct_filter_params sctfilter; memset(&sctfilter, 0, sizeof(sctfilter)); sctfilter.pid = pid; sctfilter.filter.filter[0] = table_id; sctfilter.filter.mask[0] = 0xFF; sctfilter.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC; int rv = ioctl(demux_fd, DMX_SET_FILTER, &sctfilter); if (rv < 0) { close(demux_fd); return -1; } return demux_fd; } ///////////////////////////////////////////////////////////// // Process ///////////////////////////////////////////////////////////// static void process_pat(int pat_fd, const char* demuxDeviceFilename, int channel_service_id, int *pmt_fd, struct pollfd *pollfd) { int size; uint8_t sibuf[4096]; // read the section if ((size = read(pat_fd, sibuf, sizeof(sibuf))) < 0) { return; } // parse section struct section *section = section_codec(sibuf, size); if (section == NULL) { return; } // parse section_ext struct section_ext *section_ext = section_ext_decode(section, 0); if (section_ext == NULL) { return; } if (pat_version == section_ext->version_number) { return; } // parse PAT struct mpeg_pat_section *pat = mpeg_pat_section_codec(section_ext); if (pat == NULL) { return; } // try and find the requested program struct mpeg_pat_program *cur_program; mpeg_pat_section_programs_for_each(pat, cur_program) { if (cur_program->program_number == channel_service_id) { // close old PMT fd if (*pmt_fd != -1) close(*pmt_fd); // create PMT filter if ((*pmt_fd = create_filter(demuxDeviceFilename, cur_program->pid, stag_mpeg_program_map)) < 0) { return; } pollfd->fd = *pmt_fd; pollfd->events = POLLIN|POLLPRI|POLLERR; // we have a new PMT pid ca_pmt_version = -1; break; } } // remember the PAT version pat_version = section_ext->version_number; } static void process_tdt(int tdt_fd) { int size; uint8_t sibuf[4096]; // read the section if ((size = read(tdt_fd, sibuf, sizeof(sibuf))) < 0) { return; } // parse section struct section *section = section_codec(sibuf, size); if (section == NULL) { return; } // parse TDT struct dvb_tdt_section *tdt = dvb_tdt_section_codec(section); if (tdt == NULL) { return; } // done zap_ca_new_dvbtime(dvbdate_to_unixtime(tdt->utc_time)); } static void process_pmt(int pmt_fd, int channel_service_id) { int size; uint8_t sibuf[4096]; // read the section if ((size = read(pmt_fd, sibuf, sizeof(sibuf))) < 0) { return; } // parse section struct section *section = section_codec(sibuf, size); if (section == NULL) { return; } // parse section_ext struct section_ext *section_ext = section_ext_decode(section, 0); if (section_ext == NULL) { return; } if ((section_ext->table_id_ext != channel_service_id) || (section_ext->version_number == ca_pmt_version)) { return; } // parse PMT struct mpeg_pmt_section *pmt = mpeg_pmt_section_codec(section_ext); if (pmt == NULL) { return; } //std::cout << "Send new PMT to zap_ca" << std::endl; // do ca handling if (zap_ca_new_pmt(pmt) == 1) ca_pmt_version = pmt->head.version_number; }