* Re: Hauppauge 950Q TS capture intermittent lock up
2014-05-11 14:58 ` Mauro Carvalho Chehab
2014-05-11 16:45 ` Trevor Graffa
@ 2014-05-13 15:53 ` Trevor G
2014-05-21 13:22 ` Mauro Carvalho Chehab
1 sibling, 1 reply; 5+ messages in thread
From: Trevor G @ 2014-05-13 15:53 UTC (permalink / raw)
To: Mauro Carvalho Chehab; +Cc: linux-media, Devin Heitmueller, cb.xiong
[-- Attachment #1: Type: text/plain, Size: 3975 bytes --]
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@infradead.org> wrote:
> Hi Trevor,
>
> Em Fri, 9 May 2014 11:19:49 -0400
> Trevor Anonymous <trevor.forums@gmail.com> 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@vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
[-- Attachment #2: dvbcapture.c --]
[-- Type: text/x-csrc, Size: 10343 bytes --]
/** 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;
}
^ permalink raw reply [flat|nested] 5+ messages in thread