On Saturday 28 Aug 2004 03:35, Andrew de Quincey wrote: > Please try this new patch - I get audio again here! I've also added in code > to output the status of the encoder if something goes wrong in future. > > I still have to reload the module twice to get anything though... dunno > what thats all about. I don't even get anything on /dev/video0 (apart from > I/O errors).
Hi, this later patch now corrects problems when un/replugging the A/V feeds. I found that it would frequently lose audio sync, or acquire strange buzzing noises. Oh, and my test decoder program (kaffeine, based on xine) tended to lock up when the video went missing as well - so the encoder must have been outputting rubbish. It's really annoying: the SAA6752hs encoder chip has all these funky commands like "START", "STOP", "PAUSE" etc - problem is they don't work properly! To fix that, if you unplug the video it will now hard reset the encoder. When you plug it back, it will reconfigure and restart it with the last settings used. It means you won't get any data at all if the video signal is missing, but theres no other way to be 100% reliable with this chip.
--- linux-2.6.8.1.orig//drivers/media/video/saa7134/saa6752hs.c Sat Aug 14 11:56:23 2004 +++ linux-2.6.8.1//drivers/media/video/saa7134/saa6752hs.c Sat Aug 28 06:48:40 2004 @@ -11,6 +11,7 @@ #include <linux/types.h> #include <linux/videodev.h> #include <linux/init.h> +#include <linux/crc32.h> #include <media/id.h> #include <media/saa6752hs.h> @@ -36,77 +37,82 @@ SAA6752HS_COMMAND_RECONFIGURE = 4, SAA6752HS_COMMAND_SLEEP = 5, SAA6752HS_COMMAND_RECONFIGURE_FORCE = 6, - + SAA6752HS_COMMAND_MAX }; - /* ---------------------------------------------------------------------- */ static u8 PAT[] = { 0xc2, // i2c register 0x00, // table number for encoder - + 0x47, // sync 0x40, 0x00, // transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid(0) 0x10, // transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) - + 0x00, // PSI pointer to start of table - + 0x00, // tid(0) 0xb0, 0x0d, // section_syntax_indicator(1), section_length(13) - + 0x00, 0x01, // transport_stream_id(1) - + 0xc1, // version_number(0), current_next_indicator(1) - + 0x00, 0x00, // section_number(0), last_section_number(0) 0x00, 0x01, // program_number(1) - - 0xe0, 0x10, // PMT PID(0x10) - 0x76, 0xf1, 0x44, 0xd1 // CRC32 + 0xe0, 0x00, // PMT PID + + 0x00, 0x00, 0x00, 0x00 // CRC32 }; static u8 PMT[] = { 0xc2, // i2c register 0x01, // table number for encoder - + 0x47, // sync - 0x40, 0x10, // transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid(0x10) + 0x40, 0x00, // transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid 0x10, // transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) - + 0x00, // PSI pointer to start of table - + 0x02, // tid(2) 0xb0, 0x17, // section_syntax_indicator(1), section_length(23) 0x00, 0x01, // program_number(1) - + 0xc1, // version_number(0), current_next_indicator(1) - + 0x00, 0x00, // section_number(0), last_section_number(0) - - 0xe1, 0x04, // PCR_PID (0x104) - + + 0xe0, 0x00, // PCR_PID + 0xf0, 0x00, // program_info_length(0) - - 0x02, 0xe1, 0x00, 0xf0, 0x00, // video stream type(2), pid(0x100) - 0x04, 0xe1, 0x03, 0xf0, 0x00, // audio stream type(4), pid(0x103) - - 0xa1, 0xca, 0x0f, 0x82 // CRC32 + + 0x02, 0xe0, 0x00, 0xf0, 0x00, // video stream type(2), pid + 0x04, 0xe0, 0x00, 0xf0, 0x00, // audio stream type(4), pid + + 0x00, 0x00, 0x00, 0x00 // CRC32 }; static struct mpeg_params mpeg_params_template = { - .bitrate_mode = MPEG_BITRATE_MODE_CBR, - .video_target_bitrate = 5000, + .video_bitrate_mode = MPEG_VIDEO_BITRATE_MODE_VBR, + .video_target_bitrate = 4000, + .video_max_bitrate = 6000, .audio_bitrate = MPEG_AUDIO_BITRATE_256, - .total_bitrate = 6000, + .total_bitrate = 7000, + .pmt_pid = 16, + .video_pid = 260, + .audio_pid = 256, + .pcr_pid = 259, + .video_format = MPEG_VIDEO_FORMAT_D1, }; - + /* ---------------------------------------------------------------------- */ @@ -122,11 +128,11 @@ case SAA6752HS_COMMAND_RESET: buf[0] = 0x00; break; - + case SAA6752HS_COMMAND_STOP: - buf[0] = 0x03; + buf[0] = 0x03; break; - + case SAA6752HS_COMMAND_START: buf[0] = 0x02; break; @@ -134,11 +140,11 @@ case SAA6752HS_COMMAND_PAUSE: buf[0] = 0x04; break; - + case SAA6752HS_COMMAND_RECONFIGURE: buf[0] = 0x05; break; - + case SAA6752HS_COMMAND_SLEEP: buf[0] = 0x06; break; @@ -146,11 +152,11 @@ case SAA6752HS_COMMAND_RECONFIGURE_FORCE: buf[0] = 0x07; break; - + default: - return -EINVAL; + return -EINVAL; } - + // set it and wait for it to be so i2c_master_send(client, buf, 1); timeout = jiffies + HZ * 3; @@ -166,7 +172,7 @@ status = -ETIMEDOUT; break; } - + // wait a bit set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(HZ/100); @@ -175,7 +181,7 @@ // delay a bit to let encoder settle set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(HZ/20); - + // done return status; } @@ -185,14 +191,14 @@ struct mpeg_params* params) { u8 buf[3]; - + // set the bitrate mode buf[0] = 0x71; - buf[1] = params->bitrate_mode; + buf[1] = params->video_bitrate_mode; i2c_master_send(client, buf, 2); - + // set the video bitrate - if (params->bitrate_mode == MPEG_BITRATE_MODE_VBR) { + if (params->video_bitrate_mode == MPEG_VIDEO_BITRATE_MODE_VBR) { // set the target bitrate buf[0] = 0x80; buf[1] = params->video_target_bitrate >> 8; @@ -211,100 +217,184 @@ buf[2] = params->video_target_bitrate & 0xff; i2c_master_send(client, buf, 3); } - + // set the audio bitrate buf[0] = 0x94; buf[1] = params->audio_bitrate; i2c_master_send(client, buf, 2); - + // set the total bitrate buf[0] = 0xb1; buf[1] = params->total_bitrate >> 8; buf[2] = params->total_bitrate & 0xff; i2c_master_send(client, buf, 3); - + return 0; } static int saa6752hs_init(struct i2c_client* client, struct mpeg_params* params) -{ +{ unsigned char buf[3]; - void *data; + struct mpeg_params* cachedParams; + u32 crc; + unsigned char localPAT[256]; + unsigned char localPMT[256]; + + // grab the previous set of parameters + cachedParams = (struct mpeg_params*) i2c_get_clientdata(client); // check the bitrate parameters first if (params != NULL) { - if (params->bitrate_mode >= MPEG_BITRATE_MODE_MAX) + unsigned int pmt_pid = 0, pcr_pid = 0, video_pid = 0 , audio_pid = 0; + + // check supplied params are valid + if (params->video_format >= MPEG_VIDEO_FORMAT_MAX) return -EINVAL; - if (params->video_target_bitrate >= MPEG_VIDEO_TARGET_BITRATE_MAX) + if (params->video_bitrate_mode >= MPEG_VIDEO_BITRATE_MODE_MAX) return -EINVAL; - if (params->video_max_bitrate >= MPEG_VIDEO_MAX_BITRATE_MAX) + if (params->video_target_bitrate >= MPEG_VIDEO_TARGET_BITRATE_MAX) return -EINVAL; if (params->audio_bitrate >= MPEG_AUDIO_BITRATE_MAX) return -EINVAL; if (params->total_bitrate >= MPEG_TOTAL_BITRATE_MAX) return -EINVAL; - if (params->bitrate_mode == MPEG_BITRATE_MODE_MAX && - params->video_target_bitrate <= params->video_max_bitrate) - return -EINVAL; + if (params->video_bitrate_mode == MPEG_VIDEO_BITRATE_MODE_VBR) { + if (params->video_max_bitrate >= MPEG_VIDEO_MAX_BITRATE_MAX) + return -EINVAL; + if (params->video_target_bitrate >= params->video_max_bitrate) + return -EINVAL; + } + if (params->pmt_pid != 0) { + if (params->pmt_pid > MPEG_PID_MAX) + return -EINVAL; + pmt_pid = params->pmt_pid; + } else { + pmt_pid = cachedParams->pmt_pid; + } + if (params->pcr_pid != 0) { + if (params->pcr_pid > MPEG_PID_MAX) + return -EINVAL; + pcr_pid = params->pcr_pid; + } else { + pcr_pid = cachedParams->pcr_pid; + } + if (params->video_pid != 0) { + if (params->video_pid > MPEG_PID_MAX) + return -EINVAL; + video_pid = params->video_pid; + } else { + video_pid = cachedParams->video_pid; + } + if (params->audio_pid != 0) { + if (params->audio_pid > MPEG_PID_MAX) + return -EINVAL; + audio_pid = params->audio_pid; + } else { + audio_pid = cachedParams->audio_pid; + } + + // update cache + memcpy(cachedParams, params, sizeof(struct mpeg_params)); + cachedParams->pmt_pid = pmt_pid; + cachedParams->pcr_pid = pcr_pid; + cachedParams->video_pid = video_pid; + cachedParams->audio_pid = audio_pid; } - - // Set GOP structure {3, 13} + + // set bitrate + saa6752hs_set_bitrate(client, cachedParams); + + // Set GOP structure {3, 13} buf[0] = 0x72; buf[1] = 0x03; buf[2] = 0x0D; i2c_master_send(client,buf,3); - + // Set minimum Q-scale {4} buf[0] = 0x82; buf[1] = 0x04; i2c_master_send(client,buf,2); - + // Set maximum Q-scale {12} buf[0] = 0x83; buf[1] = 0x0C; i2c_master_send(client,buf,2); - + // Set Output Protocol buf[0] = 0xD0; buf[1] = 0x01; i2c_master_send(client,buf,2); - + // Set video output stream format {TS} buf[0] = 0xB0; buf[1] = 0x05; i2c_master_send(client,buf,2); - - // Set Audio PID {0x103} + + /* compute PAT */ + memcpy(localPAT, PAT, sizeof(PAT)); + localPAT[17] = 0xe0 | ((cachedParams->pmt_pid >> 8) & 0x0f); + localPAT[18] = cachedParams->pmt_pid & 0xff; + crc = crc32_be(~0, &localPAT[7], sizeof(PAT) - 7 - 4); + localPAT[sizeof(PAT) - 4] = (crc >> 24) & 0xFF; + localPAT[sizeof(PAT) - 3] = (crc >> 16) & 0xFF; + localPAT[sizeof(PAT) - 2] = (crc >> 8) & 0xFF; + localPAT[sizeof(PAT) - 1] = crc & 0xFF; + + /* compute PMT */ + memcpy(localPMT, PMT, sizeof(PMT)); + localPMT[3] = 0x40 | ((cachedParams->pmt_pid >> 8) & 0x0f); + localPMT[4] = cachedParams->pmt_pid & 0xff; + localPMT[15] = 0xE0 | ((cachedParams->pcr_pid >> 8) & 0x0F); + localPMT[16] = cachedParams->pcr_pid & 0xFF; + localPMT[20] = 0xE0 | ((cachedParams->video_pid >> 8) & 0x0F); + localPMT[21] = cachedParams->video_pid & 0xFF; + localPMT[25] = 0xE0 | ((cachedParams->audio_pid >> 8) & 0x0F); + localPMT[26] = cachedParams->audio_pid & 0xFF; + crc = crc32_be(~0, &localPMT[7], sizeof(PMT) - 7 - 4); + localPMT[sizeof(PMT) - 4] = (crc >> 24) & 0xFF; + localPMT[sizeof(PMT) - 3] = (crc >> 16) & 0xFF; + localPMT[sizeof(PMT) - 2] = (crc >> 8) & 0xFF; + localPMT[sizeof(PMT) - 1] = crc & 0xFF; + + // Set Audio PID buf[0] = 0xC1; - buf[1] = 0x01; - buf[2] = 0x03; + buf[1] = (cachedParams->audio_pid >> 8) & 0xFF; + buf[2] = cachedParams->audio_pid & 0xFF; i2c_master_send(client,buf,3); - - // setup bitrate settings - data = i2c_get_clientdata(client); - if (params) { - saa6752hs_set_bitrate(client, params); - memcpy(data, params, sizeof(struct mpeg_params)); - } else { - // parameters were not supplied. use the previous set - saa6752hs_set_bitrate(client, (struct mpeg_params*) data); - } - + + // Set Video PID + buf[0] = 0xC0; + buf[1] = (cachedParams->video_pid >> 8) & 0xFF; + buf[2] = cachedParams->video_pid & 0xFF; + i2c_master_send(client,buf,3); + + // Set PCR PID + buf[0] = 0xC4; + buf[1] = (cachedParams->pcr_pid >> 8) & 0xFF; + buf[2] = cachedParams->pcr_pid & 0xFF; + i2c_master_send(client,buf,3); + // Send SI tables - i2c_master_send(client,PAT,sizeof(PAT)); - i2c_master_send(client,PMT,sizeof(PMT)); - + i2c_master_send(client,localPAT,sizeof(PAT)); + i2c_master_send(client,localPMT,sizeof(PMT)); + // mute then unmute audio. This removes buzzing artefacts buf[0] = 0xa4; buf[1] = 1; i2c_master_send(client, buf, 2); buf[1] = 0; i2c_master_send(client, buf, 2); - + + // Set video format + buf[0] = 0x41; + buf[1] = cachedParams->video_format; + i2c_master_send(client, buf, 2); + // start it going saa6752hs_chip_command(client, SAA6752HS_COMMAND_START); + // return success return 0; } @@ -322,14 +412,14 @@ return -ENOMEM; memcpy(client,&client_template,sizeof(struct i2c_client)); strlcpy(client->name, "saa6752hs", sizeof(client->name)); - + if (NULL == (params = kmalloc(sizeof(struct mpeg_params), GFP_KERNEL))) return -ENOMEM; - memcpy(params,&mpeg_params_template,sizeof(struct mpeg_params)); + memcpy(params, &mpeg_params_template, sizeof(struct mpeg_params)); i2c_set_clientdata(client, params); i2c_attach_client(client); - + return 0; } @@ -337,6 +427,7 @@ { if (adap->class & I2C_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, saa6752hs_attach); + return 0; } @@ -355,16 +446,20 @@ saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct mpeg_params* init_arg = arg; + int result; switch (cmd) { case MPEG_SETPARAMS: - return saa6752hs_init(client, init_arg); + result = saa6752hs_init(client, init_arg); + if (result != 0) + printk("saa6752hs: MPEG_SETPARAMS failed: %i\n", result); + return result; default: /* nothing */ break; } - + return 0; } --- linux-2.6.8.1.orig//include/media/saa6752hs.h Sat Aug 14 11:56:26 2004 +++ linux-2.6.8.1//include/media/saa6752hs.h Sat Aug 28 06:27:14 2004 @@ -1,4 +1,4 @@ -/* +/* saa6752hs.h - definition for saa6752hs MPEG encoder Copyright (C) 2003 Andrew de Quincey <[EMAIL PROTECTED]> @@ -21,30 +21,47 @@ #ifndef _SAA6752HS_H #define _SAA6752HS_H -enum mpeg_bitrate_mode { - MPEG_BITRATE_MODE_VBR = 0, /* Variable bitrate */ - MPEG_BITRATE_MODE_CBR = 1, /* Constant bitrate */ +enum mpeg_video_bitrate_mode { + MPEG_VIDEO_BITRATE_MODE_VBR = 0, /* Variable bitrate */ + MPEG_VIDEO_BITRATE_MODE_CBR = 1, /* Constant bitrate */ - MPEG_BITRATE_MODE_MAX + MPEG_VIDEO_BITRATE_MODE_MAX }; enum mpeg_audio_bitrate { MPEG_AUDIO_BITRATE_256 = 0, /* 256 kBit/sec */ MPEG_AUDIO_BITRATE_384 = 1, /* 384 kBit/sec */ - + MPEG_AUDIO_BITRATE_MAX }; +enum mpeg_video_format { + MPEG_VIDEO_FORMAT_D1 = 0, + MPEG_VIDEO_FORMAT_2_3_D1 = 1, + MPEG_VIDEO_FORMAT_1_2_D1 = 2, + MPEG_VIDEO_FORMAT_SIF = 3, + + MPEG_VIDEO_FORMAT_MAX +}; + #define MPEG_VIDEO_TARGET_BITRATE_MAX 27000 #define MPEG_VIDEO_MAX_BITRATE_MAX 27000 #define MPEG_TOTAL_BITRATE_MAX 27000 - +#define MPEG_PID_MAX ((1 << 14) - 1) + struct mpeg_params { - enum mpeg_bitrate_mode bitrate_mode; + enum mpeg_video_bitrate_mode video_bitrate_mode; unsigned int video_target_bitrate; unsigned int video_max_bitrate; // only used for VBR enum mpeg_audio_bitrate audio_bitrate; unsigned int total_bitrate; + + unsigned int pmt_pid; + unsigned int video_pid; + unsigned int audio_pid; + unsigned int pcr_pid; + + enum mpeg_video_format video_format; }; #define MPEG_SETPARAMS _IOW('6',100,struct mpeg_params) --- linux-2.6.8.1.orig//drivers/media/video/saa7134/saa7134-ts.c Sat Aug 14 11:54:47 2004 +++ linux-2.6.8.1//drivers/media/video/saa7134/saa7134-ts.c Sat Aug 28 06:41:22 2004 @@ -35,6 +35,8 @@ #define TS_PACKET_SIZE 188 /* TS packets 188 bytes */ +#define THREAD_TIMEOUT (HZ/10) + static unsigned int ts_debug = 0; MODULE_PARM(ts_debug,"i"); MODULE_PARM_DESC(ts_debug,"enable debug messages [ts]"); @@ -171,6 +173,7 @@ /* ------------------------------------------------------------------ */ + static void ts_reset_encoder(struct saa7134_dev* dev) { saa_writeb(SAA7134_SPECIAL_MODE, 0x00); @@ -182,11 +185,57 @@ static int ts_init_encoder(struct saa7134_dev* dev, void* arg) { + saa7134_tvaudio_do_scan(dev); ts_reset_encoder(dev); saa7134_i2c_call_clients(dev, MPEG_SETPARAMS, arg); return 0; } +static void ts_sleep(struct saa7134_dev *dev, int timeout) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&dev->ts.wq, &wait); + if (dev->ts.shutdown) { + set_current_state(TASK_INTERRUPTIBLE); + if (timeout < 0) + schedule(); + else + schedule_timeout(timeout); + } + remove_wait_queue(&dev->ts.wq, &wait); +} + +static int ts_thread(void* data) +{ + struct saa7134_dev* dev = (struct saa7134_dev*) data; + int v1, v2; + + daemonize("%s", dev->name); + allow_signal(SIGTERM); + while(!dev->ts.shutdown) { + ts_sleep(dev,THREAD_TIMEOUT); + if (dev->ts.shutdown || signal_pending(current)) + break; + + v1 = saa_readb(SAA7134_STATUS_VIDEO1) & 0x40; + v2 = saa_readb(SAA7134_STATUS_VIDEO2) & 0x40; + + if ((v1 || v2) && !dev->ts.lost_video) { + printk("saa7134-ts: video signal lost\n"); + ts_reset_encoder(dev); + dev->ts.lost_video = 1; + } else if (dev->ts.started && dev->ts.lost_video && !v1 && !v2) { + printk("saa7134-ts: video signal regained\n"); + ts_init_encoder(dev, NULL); + dev->ts.lost_video = 0; + } + } + + complete_and_exit(&dev->ts.exit, 0); + return 0; +} + /* ------------------------------------------------------------------ */ @@ -212,6 +261,7 @@ goto done; dev->ts.started = 0; + dev->ts.lost_video = 0; dev->ts.users++; file->private_data = dev; err = 0; @@ -233,9 +283,10 @@ dev->ts.users--; /* stop the encoder */ - if (dev->ts.started) + if (dev->ts.started) ts_reset_encoder(dev); - + dev->ts.started = 0; + up(&dev->ts.ts.lock); return 0; } @@ -249,7 +300,7 @@ ts_init_encoder(dev, NULL); dev->ts.started = 1; } - + return videobuf_read_stream(file, &dev->ts.ts, data, count, ppos, 0); } @@ -482,12 +533,29 @@ saa_writeb(SAA7134_TS_DMA0, ((ts_nr_packets-1)&0xff)); saa_writeb(SAA7134_TS_DMA1, (((ts_nr_packets-1)>>8)&0xff)); saa_writeb(SAA7134_TS_DMA2, ((((ts_nr_packets-1)>>16)&0x3f) | 0x00)); /* TSNOPIT=0, TSCOLAP=0 */ - + + + /* start ts thread */ + init_waitqueue_head(&dev->ts.wq); + init_completion(&dev->ts.exit); + dev->ts.pid = kernel_thread(ts_thread,dev,0); + if (dev->ts.pid < 0) + printk(KERN_WARNING "%s: kernel_thread() failed\n", + dev->name); + wake_up_interruptible(&dev->ts.wq); + return 0; } int saa7134_ts_fini(struct saa7134_dev *dev) { + /* shutdown ts thread */ + if (dev->ts.pid >= 0) { + dev->ts.shutdown = 1; + wake_up_interruptible(&dev->ts.wq); + wait_for_completion(&dev->ts.exit); + } + /* nothing */ saa7134_pgtable_free(dev->pci,&dev->ts.pt_ts); return 0; --- linux-2.6.8.1.orig//drivers/media/video/saa7134/saa7134.h Sat Aug 14 11:55:48 2004 +++ linux-2.6.8.1//drivers/media/video/saa7134/saa7134.h Sat Aug 28 06:28:24 2004 @@ -284,6 +284,11 @@ struct videobuf_queue ts; struct saa7134_pgtable pt_ts; int started; + int lost_video; + pid_t pid; + struct completion exit; + wait_queue_head_t wq; + unsigned int shutdown; }; /* oss dsp status */