Re: Hauppauge 950Q TS capture intermittent lock up

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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;
}

[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux