Example app is attached. My build is just "gcc -O2 dvbcapture.c -o dvbcapture". Here's example output and usage of this app - both working and with data lockup. Params mean: DVB adapter 0, frequency 357Mhz, 4 seconds, output to "stuff.ts", QAM256. The app returns exit code 3 if no data is available on the DVR device (as in 2nd run below), which is my trigger to reset the USB (via usbreset: https://gist.github.com/x2q/5124616). Resetting the USB device then enables the capture to work. [trevor@xxx bin]$ ./dvbcapture -c 0 -f 357000000 -t 4 -o stuff.ts -q 256 Frontend type: ATSC DVB card: Auvitek AU8522 QAM/8VSB Frontend Frequency: 357000000 Getting frontend status Locked frequency: 357000000 Locked modulation: 5 Bit error rate: 96 Signal strength: 65535 SNR: 398 FE_STATUS: FE_HAS_SIGNAL FE_HAS_LOCK FE_HAS_CARRIER FE_HAS_VITERBI FE_HAS_SYNC Setting TS filter to capture all PIDs Capturing for 4 seconds Caught timeout DONE - wrote 19415136 bytes! [trevor@xxx bin]$ ./dvbcapture -c 0 -f 357000000 -t 4 -o stuff.ts -q 256 Frontend type: ATSC DVB card: Auvitek AU8522 QAM/8VSB Frontend Frequency: 357000000 Getting frontend status Locked frequency: 357000000 Locked modulation: 5 Bit error rate: 94 Signal strength: 65535 SNR: 398 FE_STATUS: FE_HAS_SIGNAL FE_HAS_LOCK FE_HAS_CARRIER FE_HAS_VITERBI FE_HAS_SYNC Setting TS filter to capture all PIDs Capturing for 4 seconds No data available on DVR device! On Sun, May 11, 2014 at 10:58 AM, Mauro Carvalho Chehab <mchehab@xxxxxxxxxxxxx> wrote: > Hi Trevor, > > Em Fri, 9 May 2014 11:19:49 -0400 > Trevor Anonymous <trevor.forums@xxxxxxxxx> escreveu: > >> Hello all, >> >> I have written a simple application to capture RF QAM transport >> streams with the Hauppauge 950Q, and save to a file. This is >> essentially the same as dvbstream, but with unnecessary stuff removed >> (and I have verified this bug using dvbstream as well): >> - tune using frontend device >> - demux device: DMX_SET_PES_FILTER on pid 8192 with DMX_OUT_TS_TAP output. >> - Read from dvr device, save to file. >> - Interrupt app using alarm() and stop pes filter, close devices. >> >> >> This works as expected. The problem is after running this a bunch of >> times (sometimes 15-20+), the device seems to eventually get into a >> bad state, and nothing is available to read on the dvr device. The >> lockup never seems to happen while reading data (i.e., either data >> comes and the app works completely, or the app reads 0 bytes). When >> this happens, all the tuning/demod locks look good, and everything >> appears to be working -- there just isn't data ready to read from the >> dvr device. >> >> When it gets into a bad state, I have to physically remove/reinsert >> the 950Q device or otherwise reset the device (e.g., usb reset - >> USBDEVFS_RESET ioctl). > > Yes, I noticed a similar issue with last devel Kernel. I suspect > that the culprit could be due to a sheduled work that fixes a > hardware bug. Such scheduled work task should be cancelled when > the device is closed or the channel is changed. This is likely > a partial fix for it (untested): > https://patchwork.linuxtv.org/patch/23860/ > > It makes sure that the thread is canceled when a new set frontend > ioctl is sent. Yet, this patch won't solve your specific problem. > > I suspect that the right approach would be to also call > cancel_work_sync(&dev->restart_streaming) on all other places > where stop_urb_transfer() is called. > > Btw, could you share your small test application? That would > help us to test the bug locally and work on a patch. > >> >> Has anyone seen this issue before? >> >> I am running Fedora 19 with 3.13.9 kernel. Hardware is: >> - au0828, au8522, xc5000 (with dvb-fe-xc5000c-4.1.30.7.fw) >> >> >> Thanks, >> -Trevor >> -- >> To unsubscribe from this list: send the line "unsubscribe linux-media" in >> the body of a message to majordomo@xxxxxxxxxxxxxxx >> More majordomo info at http://vger.kernel.org/majordomo-info.html
/** dvbcapture.c -- Application to capture TS stream from DVB device (currently ATSC (QAM) only) * NOTE: Some of the code (especially tuning) is from dvbstream (available at http://sourceforge.net/projects/dvbtools/), which is GPL licensed software. */ // Linux includes: #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <ctype.h> #include <sys/ioctl.h> #include <sys/time.h> #include <sys/poll.h> #include <sys/stat.h> #include <resolv.h> #include <fcntl.h> #include <unistd.h> #include <signal.h> #include <values.h> #include <string.h> // DVB includes: #include <linux/dvb/dmx.h> #include <linux/dvb/frontend.h> enum ExitStatus { EXIT_OK = 0, EXIT_ERR_ARGS = 1, /* Bad command line */ EXIT_ERR_DEVICE = 2, /* Error opening/reading/ioctl, etc */ EXIT_ERR_DATA = 3, /* No data */ EXIT_ERR_INT = 4 /* User interrupt signal caught */ }; #define TS_SIZE 188 struct config_t { char* frontenddev; char* demuxdev; char* dvrdev; char* outputname; int time; int frequency; int modulation; }; /** * Tuning Functions */ void print_status(FILE* fd, fe_status_t festatus) { fprintf(fd, "FE_STATUS:"); if (festatus & FE_HAS_SIGNAL) fprintf(fd, " FE_HAS_SIGNAL"); if (festatus & FE_TIMEDOUT) fprintf(fd, " FE_TIMEDOUT"); if (festatus & FE_HAS_LOCK) fprintf(fd, " FE_HAS_LOCK"); if (festatus & FE_HAS_CARRIER) fprintf(fd, " FE_HAS_CARRIER"); if (festatus & FE_HAS_VITERBI) fprintf(fd, " FE_HAS_VITERBI"); if (festatus & FE_HAS_SYNC) fprintf(fd, " FE_HAS_SYNC"); fprintf(fd, "\n"); } /* Note: TODO - start_uncorrected, get_uncorrected not working as expected. */ static int uncorrected_start_blocks = 0; void start_uncorrected(int fd_frontend) { int ioret = 0; if (ioctl(fd_frontend, FE_READ_UNCORRECTED_BLOCKS, &ioret) >= 0) fprintf(stderr, "Start UNC: %d\n", ioret); uncorrected_start_blocks = ioret; } int get_uncorrected(int fd_frontend) { int ioret = 0; if (ioctl(fd_frontend, FE_READ_UNCORRECTED_BLOCKS, &ioret) >= 0) fprintf(stderr, "Stop UNC: %d\n", ioret); int unc = ioret - uncorrected_start_blocks; fprintf(stderr, "UNC: %d\n", unc); } int tune(int fd_frontend, int frequency, int modulation) { struct dvb_frontend_parameters feparams = { 0 }; struct dvb_frontend_info feinfo = { 0 }; const char* card_types[] = {"QPSK", "QAM", "OFDM", "ATSC"}; if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) < 0) { perror("FE_GET_INFO: "); return -1; } fprintf(stderr, "Frontend type: %s\n", card_types[feinfo.type]); if (feinfo.type != FE_ATSC) { fprintf(stderr, "Error: Only ATSC cards are currently supported\n"); } fprintf(stderr, "DVB card: %s\n", feinfo.name, frequency); fprintf(stderr, "Frequency: %d\n", frequency); feparams.frequency = frequency; feparams.u.vsb.modulation = modulation; fe_status_t festatus; struct pollfd pfd[1]; int locks = 0, ok = 0; time_t tm1, tm2; if (ioctl(fd_frontend, FE_SET_FRONTEND, &feparams) < 0) { perror("Error tuning channel"); return -1; } pfd[0].fd = fd_frontend; pfd[0].events = POLLPRI; tm1 = tm2 = time((time_t*) NULL); fprintf(stderr, "Getting frontend status\n"); while (!ok) { festatus = 0; if (poll(pfd, 1, 3000) > 0) { if (pfd[0].revents & POLLPRI) { if (ioctl(fd_frontend, FE_READ_STATUS, &festatus) >= 0) if (festatus & FE_HAS_LOCK) locks++; } } usleep(10000); tm2 = time((time_t*) NULL); if ((festatus & FE_TIMEDOUT) || (locks >= 2) || (tm2 - tm1 >= 3)) ok = 1; } if (festatus & FE_HAS_LOCK) { int32_t ioret; if (ioctl(fd_frontend, FE_GET_FRONTEND, &feparams) >= 0) { fprintf(stderr, "Locked frequency: %d\n", feparams.frequency); fprintf(stderr, "Locked modulation: %d\n", feparams.u.vsb.modulation); } ioret = 0; if (ioctl(fd_frontend, FE_READ_BER, &ioret) >= 0) fprintf(stderr, "Bit error rate: %d\n", ioret); ioret = 0; if (ioctl(fd_frontend, FE_READ_SIGNAL_STRENGTH, &ioret) >= 0) fprintf(stderr, "Signal strength: %d\n", ioret); ioret = 0; if (ioctl(fd_frontend, FE_READ_SNR, &ioret) >= 0) fprintf(stderr, "SNR: %d\n", ioret); print_status(stderr, festatus); } else { fprintf(stderr, "Not able to lock to the signal on the given frequency\n"); return -1; } return 0; } /** * Demux Functions */ int set_ts_filt(int fd_demux) { struct dmx_pes_filter_params pesFilterParams; fprintf(stderr, "Setting TS filter to capture all PIDs\n"); pesFilterParams.pid = 8192; pesFilterParams.input = DMX_IN_FRONTEND; pesFilterParams.output = DMX_OUT_TS_TAP; pesFilterParams.pes_type = DMX_PES_OTHER; pesFilterParams.flags = DMX_IMMEDIATE_START; if (ioctl(fd_demux, DMX_SET_PES_FILTER, &pesFilterParams) < 0) { fprintf(stderr, "Failed setting filter"); perror("DMX SET PES FILTER"); return -1; } if (ioctl(fd_demux, DMX_START) < 0) { perror("DMX_STOP"); return -1; } return 0; } int stop_ts_filt(int fd_demux) { if (ioctl(fd_demux, DMX_STOP) < 0) { perror("DMX_STOP"); return -1; } return 0; } /** * DVR/Capture Functions */ int capture(int fd_dvr, FILE* outfile) { int bytes_read; int bytes_written = 0; uint8_t buf[TS_SIZE]; bytes_read = read(fd_dvr, buf, TS_SIZE); if (bytes_read > 0) { if (buf[0] == 0x47) { bytes_written = fwrite(buf, 1, bytes_read, outfile); } else { fprintf(stderr, "NON 0X47\n"); } } else { if (errno != EAGAIN && errno != EINTR) { perror("Error dvrdev read"); } } return bytes_written; } /** * Utility/Initialization */ /* Signal handler to cause completion of app */ volatile int finished = 0; volatile int user_interrupted = 0; void stop_signal_handler(int sig) { if (sig == SIGALRM) { fprintf(stderr, "Caught timeout\n"); } else if (sig == SIGINT) { user_interrupted = 1; fprintf(stderr, "Caught interrupt signal\n"); } finished = 1; } /* Wait for data on file descriptor for duration in milliseconds. Return 1 if data is available, 0 otherwise */ int wait_for_data(int fd, int millis) { struct pollfd pfd[1]; pfd[0].fd = fd; pfd[0].events = POLLIN; if (poll(pfd, 1, millis) > 0) { if (pfd[0].revents & POLLIN) { return 1; } } return 0; } /* Print usage */ void printhelp() { fprintf(stderr, "Usage: dvbcapture -c <card_number> -f <frequency> -t <time> -o <output_file> -q <qam>\n"); fprintf(stderr, "card_number DVB device adapter number\n"); fprintf(stderr, "frequency Frequency to tune to\n"); fprintf(stderr, "time Time to capture in seconds\n"); fprintf(stderr, "output_file File name to store capture\n"); fprintf(stderr, "qam Either 16 or 256 for QAM16 and QAM256\n"); } /* Set config parameters based on command line args */ int initialize(int argc, char* argv[], struct config_t *config) { int card_number = 0; int time = -1; int card_selected_flag = 0; int time_selected_flag = 0; int frequency = 0; int modulation = 0; char* outputname; opterr = 0; int c; while ((c = getopt(argc, argv, "hc:t:o:f:q:")) != -1) switch (c) { case 'h': printhelp(); return EXIT_ERR_ARGS; case 'c': card_number = atoi(optarg); card_selected_flag = 1; break; case 't': time = atoi(optarg); time_selected_flag = 1; break; case 'o': outputname = optarg; break; case 'f': frequency = atoi(optarg); break; case 'q': modulation = atoi(optarg); break; } if (!card_selected_flag || !time_selected_flag || card_number < 0 || time <= 0 || !outputname || !frequency || (modulation != 16 && modulation != 256)) { printhelp(); return EXIT_ERR_ARGS; } asprintf(&config->frontenddev, "/dev/dvb/adapter%d/frontend0", card_number); asprintf(&config->demuxdev, "/dev/dvb/adapter%d/demux0", card_number); asprintf(&config->dvrdev, "/dev/dvb/adapter%d/dvr0", card_number); config->time = time; config->outputname = outputname; config->frequency = frequency; if (modulation == 16) { config->modulation = QAM_16; } else if (modulation == 256) { config->modulation = QAM_256; } return EXIT_OK; } /* MAIN */ int main(int argc, char* argv[]) { FILE* outfile; struct config_t config = { 0 }; int ret; int fd_frontend, fd_demux, fd_dvr; fd_frontend = fd_demux = fd_dvr = -1; ret = initialize(argc, argv, &config); if (ret != 0) { return ret; } if ((fd_frontend = open(config.frontenddev, O_RDWR)) < 0) { fprintf(stderr, "Error opening frontend device %s: %s\n", config.frontenddev, strerror(errno)); ret = EXIT_ERR_DEVICE; goto complete; } if ((fd_demux = open(config.demuxdev, O_RDONLY)) < 0) { fprintf(stderr, "Error opening demux device %s: %s\n", config.demuxdev, strerror(errno)); ret = EXIT_ERR_DEVICE; goto complete; } if ((fd_dvr = open(config.dvrdev, O_RDONLY)) < 0) { fprintf(stderr, "Error opening dvr device %s: %s\n", config.dvrdev, strerror(errno)); ret = EXIT_ERR_DEVICE; goto complete; } if ((outfile = fopen(config.outputname, "wb")) == 0) { perror("Error opening output file"); ret = EXIT_ERR_DEVICE; goto complete; } if (tune(fd_frontend, config.frequency, config.modulation) != 0) { fprintf(stderr, "Error tuning\n"); ret = EXIT_ERR_DEVICE; goto complete; } if (set_ts_filt(fd_demux) != 0) { ret = EXIT_ERR_DEVICE; goto complete; } // start_uncorrected(fd_frontend); /* Install SIGINT handler */ struct sigaction int_action = { 0 }; int_action.sa_handler = stop_signal_handler; sigaction(SIGINT, &int_action, NULL); /* Capture */ int size = 0; fprintf(stderr, "Capturing for %d seconds\n", config.time); /* Ensure data is available to read */ if (wait_for_data(fd_dvr, 2000) != 1) { if (!user_interrupted) { fprintf(stderr, "No data available on DVR device!\n"); ret = EXIT_ERR_DATA; goto complete; } } /* Set up alarm */ struct sigaction alarm_action = { 0 }; alarm_action.sa_handler = stop_signal_handler; sigaction(SIGALRM, &alarm_action, NULL); alarm(config.time); /* Capture data */ while (!finished) { size += capture(fd_dvr, outfile); } // get_uncorrected(fd_frontend); fprintf(stderr, "%sDONE - wrote %d bytes!\n", user_interrupted ? "(User Interrupt) " : "", size); ret = EXIT_OK; complete: if (fd_demux >= 0) stop_ts_filt(fd_demux); if (fd_dvr >= 0) close(fd_dvr); if (fd_demux >= 0) close(fd_demux); if (fd_frontend >= 0) close(fd_frontend); if (outfile) fclose(outfile); return ret; }