2022-10-26 13:16:21 +02:00
|
|
|
|
|
|
|
// for systems without O_LARGEFILE
|
|
|
|
#ifndef O_LARGEFILE
|
|
|
|
#define O_LARGEFILE 0
|
|
|
|
#endif
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <linux/dvb/frontend.h>
|
|
|
|
#include <linux/dvb/mod.h>
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
typedef uint8_t u8;
|
|
|
|
typedef uint16_t u16;
|
|
|
|
typedef int16_t s16;
|
|
|
|
typedef uint32_t u32;
|
|
|
|
typedef uint64_t u64;
|
|
|
|
|
|
|
|
#include "../ddbridge/ddbridge-mci.h"
|
|
|
|
#include "../ddbridge/ddbridge-ioctl.h"
|
|
|
|
|
|
|
|
#define NIT_PID 0x0010
|
|
|
|
#define MAXNIT 1024
|
|
|
|
#define DEFAULT_BIT_RATE_C (50870588ULL)
|
|
|
|
#define DEFAULT_BIT_RATE_T (31668449ULL)
|
|
|
|
#define TS_SIZE (188)
|
|
|
|
#define START_FREQ_T (474000000)
|
|
|
|
#define START_FREQ_C (114000000)
|
|
|
|
static int adapt = 1;
|
|
|
|
static uint32_t start_freq = START_FREQ_C;
|
|
|
|
static int dvbt = 0;
|
|
|
|
static int writeNIT = 0;
|
|
|
|
|
|
|
|
int timest = 1;
|
|
|
|
|
|
|
|
typedef struct section_data_t {
|
|
|
|
uint8_t *section;
|
|
|
|
uint16_t sec_length;
|
|
|
|
uint16_t sec_pos;
|
|
|
|
uint8_t pack_num;
|
|
|
|
} section_data;
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct transponder_{
|
|
|
|
uint16_t tpid;
|
|
|
|
uint8_t delsys;
|
|
|
|
uint32_t freq;
|
|
|
|
uint8_t qam;
|
|
|
|
uint32_t symbolrate;
|
|
|
|
uint8_t bandwidth;
|
|
|
|
uint8_t guard;
|
|
|
|
uint8_t code_rate;
|
|
|
|
uint8_t trans_mode;
|
|
|
|
} transponder;
|
|
|
|
|
|
|
|
typedef struct write_data_t {
|
|
|
|
int fd_in;
|
|
|
|
int *fd_out;
|
|
|
|
char *name;
|
|
|
|
int chans;
|
|
|
|
transponder tp[32];
|
|
|
|
} write_data;
|
|
|
|
|
|
|
|
#define ADAPT_FIELD 0x20
|
|
|
|
#define MAX_PCR (2576980377599ULL) // max pcr 2^33*300-1
|
|
|
|
#define PCR_FAC (2048ULL)
|
|
|
|
#define MAXPCR (MAX_PCR*PCR_FAC)
|
|
|
|
#define PCR_FLAG 0x10
|
|
|
|
#define OPCR_FLAG 0x08
|
|
|
|
|
|
|
|
//++++++++++++++++++++++++++++++++CRC+++++++++++++++++++++++++++++++++++++
|
|
|
|
static uint32_t dvb_crc_table[256] = {
|
|
|
|
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
|
|
|
|
0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
|
|
|
|
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
|
|
|
|
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
|
|
|
|
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
|
|
|
|
0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
|
|
|
|
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
|
|
|
|
0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
|
|
|
|
0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
|
|
|
|
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
|
|
|
|
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
|
|
|
|
0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
|
|
|
|
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
|
|
|
|
0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
|
|
|
|
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
|
|
|
|
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
|
|
|
|
0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
|
|
|
|
0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
|
|
|
|
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
|
|
|
|
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
|
|
|
|
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
|
|
|
|
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
|
|
|
|
0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
|
|
|
|
0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
|
|
|
|
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
|
|
|
|
0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
|
|
|
|
0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
|
|
|
|
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
|
|
|
|
0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
|
|
|
|
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
|
|
|
|
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
|
|
|
|
0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
|
|
|
|
0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
|
|
|
|
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
|
|
|
|
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
|
|
|
|
0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
|
|
|
|
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
|
|
|
|
0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
|
|
|
|
0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
|
|
|
|
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
|
|
|
|
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
|
|
|
|
0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
|
|
|
|
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
|
|
|
|
0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
|
|
|
|
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
|
|
|
|
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
|
|
|
|
0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
|
|
|
|
0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
|
|
|
|
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
|
|
|
|
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
|
|
|
|
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
|
|
|
|
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
|
|
|
|
0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
|
|
|
|
0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
|
|
|
|
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
|
|
|
|
0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
|
|
|
|
0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
|
|
|
|
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
|
|
|
|
0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
|
|
|
|
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
|
|
|
|
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
|
|
|
|
0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
|
|
|
|
0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
|
|
|
|
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t dvb_crc32(uint8_t *data, int len)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
uint32_t crc=0xffffffff;
|
|
|
|
|
|
|
|
for (i=0; i<len; i++)
|
|
|
|
crc=(crc<< 8)^dvb_crc_table[((crc>>24)^(data[i]))&0xff];
|
|
|
|
return crc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void dvb_set_crc32(uint8_t *data, int len)
|
|
|
|
{
|
|
|
|
uint32_t crc;
|
|
|
|
|
|
|
|
crc=dvb_crc32(data, len);
|
|
|
|
data[len] = (uint8_t)(crc>>24);
|
|
|
|
data[len+1] = (uint8_t)(crc>>16);
|
|
|
|
data[len+2] = (uint8_t)(crc>>8);
|
|
|
|
data[len+3] = (uint8_t)(crc);
|
|
|
|
}
|
|
|
|
|
|
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
|
|
|
|
//+++++++++++++++++++++++++++++++++++PCR++++++++++++++++++++++++++++++++++
|
|
|
|
|
|
|
|
uint16_t get_pid(uint8_t *pid)
|
|
|
|
{
|
|
|
|
uint16_t pp;
|
|
|
|
|
|
|
|
pp = (pid[0] & 0x1f) << 8;
|
|
|
|
pp |= pid[1] &0xff;
|
|
|
|
return pp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_pid(uint16_t pid, uint8_t *p)
|
|
|
|
{
|
|
|
|
p[0] = (p[0] & 0xE0) | ((uint8_t) (pid >> 8 ) & 0x1F);
|
|
|
|
p[1] = (uint8_t) (pid);
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcr_in_msecs(uint64_t pcr)
|
|
|
|
{
|
|
|
|
uint64_t PCR;
|
|
|
|
|
|
|
|
PCR = pcr/PCR_FAC;
|
|
|
|
|
|
|
|
return PCR/27000;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int check_pcr(uint8_t *buf)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (buf[0] != 0x47) {
|
|
|
|
fprintf(stderr,"Not a TS packet header in check_pcr\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf[1] & 0x80) {
|
|
|
|
fprintf(stderr,"Corrupt packet in check_pcr\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(buf[3] & ADAPT_FIELD)){
|
|
|
|
// fprintf(stderr,"no adaptation field\n");
|
|
|
|
return 0; //no adapt. field
|
|
|
|
}
|
|
|
|
if (!buf[4]){
|
|
|
|
// fprintf(stderr,"no adaptation field length\n");
|
|
|
|
return 0; //No adaptation field length
|
|
|
|
}
|
|
|
|
if (! (buf[5] & PCR_FLAG) ){
|
|
|
|
// fprintf(stderr,"no PCR flag\n");
|
|
|
|
return 0; // no PCR FLAG
|
|
|
|
}
|
|
|
|
// fprintf(stderr,"Found pcr\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t *get_pcr(uint8_t *buf)
|
|
|
|
{
|
|
|
|
return buf+6;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t convert_pcr(uint8_t *b){
|
|
|
|
uint64_t pcr;
|
|
|
|
|
|
|
|
pcr = ((uint64_t) b[0] << 25) | ((uint64_t) b[1] << 17) |
|
|
|
|
((uint64_t) b[2] << 9) | ((uint64_t) b[3] << 1) |
|
|
|
|
((uint64_t) b[4] >> 7);
|
|
|
|
pcr *= 300;
|
|
|
|
pcr += ((b[4] & 1) << 8) | b[5];
|
|
|
|
return pcr*PCR_FAC;
|
|
|
|
}
|
|
|
|
|
|
|
|
int find_pcr(uint8_t *buf, int l, int last)
|
|
|
|
{
|
|
|
|
int c = 0;
|
|
|
|
while (buf[c] != 0x47) c++;
|
|
|
|
|
|
|
|
if (last){
|
|
|
|
c+=((l-c)/TS_SIZE-1)*TS_SIZE;
|
|
|
|
while (buf[c] != 0x47) c--;
|
|
|
|
while (c >=0 && !check_pcr(buf+c)) c-=TS_SIZE;
|
|
|
|
if (check_pcr(buf+c)) return c;
|
|
|
|
else return -1;
|
|
|
|
} else {
|
|
|
|
while (c < l && !check_pcr(buf+c)) c+=TS_SIZE;
|
|
|
|
if (check_pcr(buf+c)) return c;
|
|
|
|
else return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
|
|
|
|
//+++++++++++++++++++++++++++++++++++TS PACKETS+++++++++++++++++++++++++++
|
|
|
|
|
|
|
|
void write_filler(uint8_t *buf)
|
|
|
|
{
|
|
|
|
int j;
|
|
|
|
uint8_t *p;
|
|
|
|
|
|
|
|
p = buf;
|
|
|
|
*p++ = 0x47;
|
|
|
|
*p++ = 0x1F;
|
|
|
|
*p++ = 0xFF;
|
|
|
|
*p++ = 0x00;
|
|
|
|
for(j = 0; j < TS_SIZE-4; j+=1 ) *p++ = 0x00;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setbcd(uint8_t *p, int freq, int b, int e)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int f = freq/e;
|
|
|
|
int r = b%2;
|
|
|
|
|
|
|
|
if (r){
|
|
|
|
p[b/2] = ( (f % 10) >> 4) & 0xF0;
|
|
|
|
f = f / 10;
|
|
|
|
}
|
|
|
|
for ( i= b/2; i > 0; i--){
|
|
|
|
p[i-1] = (f % 10) & 0x0F;
|
|
|
|
f = f / 10;
|
|
|
|
p[i-1] |= ( (f % 10) << 4) & 0xF0;
|
|
|
|
f = f / 10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int write_cable_delsys_descriptor (uint8_t *buf, int blength,
|
|
|
|
transponder *tp)
|
|
|
|
{
|
|
|
|
int length = 0;
|
|
|
|
uint8_t *p;
|
|
|
|
|
|
|
|
p = buf;
|
|
|
|
length = 11;
|
|
|
|
|
|
|
|
if (length > (int)(0xFF) || length+2 >blength){
|
|
|
|
fprintf(stderr,"Not enough space for cable delsys descriptor %d, %d\n",
|
|
|
|
length, blength);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
p[0]= 0x44; // cable delivery system descriptor
|
|
|
|
p[1]= 0x0B; // length always 11
|
|
|
|
setbcd(p+2, tp->freq, 8, 100);
|
|
|
|
p[6] = 0xFF; // reserved
|
|
|
|
p[7]= 0x02; // FEC_outer
|
|
|
|
p[8]= tp->qam; // QAM
|
|
|
|
setbcd(p+9,tp->symbolrate, 7, 100);
|
|
|
|
p[12]|= 0x0F;
|
|
|
|
|
|
|
|
return length+2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int write_terrestrial_delsys_descriptor (uint8_t *buf, int blength,
|
|
|
|
transponder *tp)
|
|
|
|
{
|
|
|
|
int length = 0;
|
|
|
|
uint8_t *p;
|
|
|
|
uint32_t freq = tp->freq/10;
|
|
|
|
p = buf;
|
|
|
|
length = 11;
|
|
|
|
|
|
|
|
if (length > (int)(0xFF) || length+2 >blength){
|
|
|
|
fprintf(stderr,"Not enough space for cable delsys descriptor %d, %d\n",
|
|
|
|
length, blength);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
p[0]= 0x5a; // terretrial delivery system descriptor
|
|
|
|
p[1]= 0x0B; // length always 11
|
|
|
|
p[2]= (freq >> 24) & 0xFF;
|
|
|
|
p[3]= (freq >> 16) & 0xFF;
|
|
|
|
p[4]= (freq >> 8) & 0xFF;
|
|
|
|
p[5]= freq & 0xFF;
|
|
|
|
p[6]= ((tp->bandwidth&0x7)<<5) |0x1f;
|
|
|
|
//bandwidth 8MHz 000,no priority 1,
|
|
|
|
//no time slice 1, no mpe-fce 1
|
|
|
|
p[7]= 0x00 |((tp->qam & 0x03) << 6) | (tp->code_rate &0x07);
|
|
|
|
//64-QAM 10, no hierarchy 000, 7/8 100
|
|
|
|
p[8]= 0x00 | ((tp->guard &0x03) <<3 )|((tp->trans_mode &0x03) << 1);
|
|
|
|
// no LP 000, 1/32 guard 00, 8k mode 01, no other freq 0
|
|
|
|
|
|
|
|
p[9]=0xff;
|
|
|
|
p[10]=0xff;
|
|
|
|
p[11]=0xff;
|
|
|
|
p[12]=0xff;
|
|
|
|
|
|
|
|
return length+2;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CreateNIT(uint8_t *NIT, transponder *tp, int start, int stop,
|
|
|
|
char *nname, uint16_t nid, uint8_t secnum, uint8_t lastsec)
|
|
|
|
{
|
|
|
|
uint8_t *p,*lp;
|
|
|
|
int c = 0;
|
|
|
|
int cc;
|
|
|
|
int i;
|
|
|
|
uint16_t TableLen = 0;
|
|
|
|
int slen;
|
|
|
|
|
|
|
|
p = NIT;
|
|
|
|
|
|
|
|
p[0] = 0x40; // Table Id NIT = 0x40
|
|
|
|
|
|
|
|
p[1] = 0x00; // Table length determined at end
|
|
|
|
p[2] = 0x00;
|
|
|
|
|
|
|
|
p[3] = (nid >> 8) & 0xFF; // network id
|
|
|
|
p[4] = nid & 0xFF;
|
|
|
|
|
|
|
|
p[5] = 0xC3; // seq number
|
|
|
|
p[6] = secnum; // section number
|
|
|
|
p[7] = lastsec; // last section number
|
|
|
|
|
|
|
|
|
|
|
|
// Network descriptor
|
|
|
|
p[8] = 0xF0; // network descriptor length
|
|
|
|
p[9] = 0x00; // set later
|
|
|
|
TableLen = 7;
|
|
|
|
c = 10;
|
|
|
|
p[c] = 0x40; // Network name descriptor
|
|
|
|
|
|
|
|
slen = 0;
|
|
|
|
|
|
|
|
slen = strlen(nname);
|
|
|
|
p[c+1] = slen;
|
|
|
|
memcpy(p+c+2,nname,slen);
|
|
|
|
|
|
|
|
p[8] = 0xF0 | (((2+slen) >> 8) &0x0F);
|
|
|
|
p[9] = ((2+slen) & 0xFF);
|
|
|
|
TableLen += 2+slen;
|
|
|
|
c += 2+slen;
|
|
|
|
|
|
|
|
|
|
|
|
// Transport stream loop
|
|
|
|
p[c] = 0xF0; // transport stream loop length
|
|
|
|
p[c+1] = 0x00; // set later
|
|
|
|
cc = c+1;
|
|
|
|
|
|
|
|
TableLen += 2;
|
|
|
|
c += 2;
|
|
|
|
slen = 0;
|
|
|
|
for (i= start; i < stop; i++){
|
|
|
|
p[c] = (uint8_t)( tp[i].tpid >> 8 ); //transport stream id
|
|
|
|
p[c+1]= (uint8_t)( tp[i].tpid );
|
|
|
|
p[c+2]= (nid >> 8) &0xFF; // orig network id
|
|
|
|
p[c+3]= nid & 0xFF; // id
|
|
|
|
|
|
|
|
lp = p+c+4;
|
|
|
|
p[c+4]= 0xF0; // descriptor loop length
|
|
|
|
p[c+5]= 0x0D; // 13 for cable delivery descriptor
|
|
|
|
c += 6;
|
|
|
|
switch (tp->delsys){
|
|
|
|
case SYS_DVBC_ANNEX_A:
|
|
|
|
slen = write_cable_delsys_descriptor (p+c, MAXNIT-c, &tp[i]);
|
|
|
|
break;
|
|
|
|
case SYS_DVBT:
|
|
|
|
slen = write_terrestrial_delsys_descriptor (p+c, MAXNIT-c, &tp[i]);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
slen = 0;
|
|
|
|
}
|
|
|
|
c+=slen;
|
|
|
|
|
|
|
|
lp[0] = 0xF0 | (((slen) >> 8) &0x0F);
|
|
|
|
lp[1] = ((slen) & 0xFF);
|
|
|
|
|
|
|
|
|
|
|
|
TableLen += 6+slen;
|
|
|
|
}
|
|
|
|
|
|
|
|
p[cc-1]= 0xF0 | (((c-cc-1) >> 8) &0x0F);
|
|
|
|
p[cc] = ((c-cc-1) & 0xFF);
|
|
|
|
|
|
|
|
TableLen += 4;
|
|
|
|
|
|
|
|
p[1] = 0xF0 | ((TableLen >> 8) & 0x0F);
|
|
|
|
p[2] = (uint8_t)(TableLen) & 0xFF;
|
|
|
|
|
|
|
|
|
|
|
|
dvb_set_crc32(NIT, TableLen + 3 -4 );
|
|
|
|
return TableLen+3;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void init_sec_data(section_data *s, uint8_t *sec, uint16_t seclength)
|
|
|
|
{
|
|
|
|
uint16_t seclen = 0;
|
|
|
|
|
|
|
|
s->section = sec;
|
|
|
|
seclen |= ((sec[1] & 0x0F) << 8);
|
|
|
|
seclen |= (sec[2] & 0xFF);
|
|
|
|
seclen += 3;
|
|
|
|
if (seclen > 3){
|
|
|
|
s->sec_length = seclen;
|
|
|
|
if(seclength && seclen != seclength)
|
|
|
|
fprintf(stderr,"Setting seclength (%d) %d\n",seclength, seclen);
|
|
|
|
} else s->sec_length = seclength;
|
|
|
|
|
|
|
|
s->sec_pos = 0;
|
|
|
|
s->pack_num = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void write_sec_pack(section_data *s, uint8_t *ts_pack, uint16_t pid)
|
|
|
|
{
|
|
|
|
uint8_t *p = ts_pack;
|
|
|
|
uint16_t rest = 0;
|
|
|
|
uint16_t len = s->sec_length - s->sec_pos;
|
|
|
|
uint16_t pload = 0;
|
|
|
|
|
|
|
|
*p++ = 0x47;
|
|
|
|
set_pid(pid,p);
|
|
|
|
if (!s->sec_pos){
|
|
|
|
*p |= 0x40;
|
|
|
|
} else {
|
|
|
|
*p &= ~0x40;
|
|
|
|
}
|
|
|
|
p += 2;
|
|
|
|
*p++ = 0x10 | (s->pack_num & 0x0f);
|
|
|
|
s->pack_num += 1;
|
|
|
|
if (!s->sec_pos) *p++=0x00;
|
|
|
|
pload = TS_SIZE - (p - ts_pack);
|
|
|
|
if (pload > len) pload = len;
|
|
|
|
memcpy( p, s->section + s->sec_pos, pload);
|
|
|
|
p += pload;
|
|
|
|
rest = TS_SIZE - (p - ts_pack);
|
|
|
|
if (rest > 0)
|
|
|
|
memset(p, 0xff, rest);
|
|
|
|
s->sec_pos += pload;
|
|
|
|
if (s->sec_pos == s->sec_length){
|
|
|
|
s->sec_pos = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
|
|
|
|
//++++++++++++++++++++++++++++++++MAIN ROUTINE++++++++++++++++++++++++++++
|
|
|
|
|
|
|
|
#define OBSIZE (672*TS_SIZE)
|
|
|
|
#define BSIZE (7*OBSIZE)
|
|
|
|
|
|
|
|
void write_mods(write_data *wd)
|
|
|
|
{
|
|
|
|
uint8_t NIT[MAXNIT];
|
|
|
|
section_data sec;
|
|
|
|
off64_t inflength,c=0;
|
|
|
|
uint8_t buffer[BSIZE];
|
|
|
|
uint8_t obuffer[OBSIZE];
|
|
|
|
int ci=0,w=0;
|
|
|
|
int isdvr=0;
|
|
|
|
int first = 0;
|
|
|
|
int last = 0;
|
|
|
|
uint64_t fpcr = 0;
|
|
|
|
uint64_t lpcr = 0;
|
|
|
|
uint64_t delta_pcr = 0;
|
|
|
|
uint64_t in_prate, out_prate;
|
|
|
|
int slen = 0;
|
|
|
|
|
|
|
|
if (dvbt){
|
|
|
|
out_prate = DEFAULT_BIT_RATE_T / (8*TS_SIZE);
|
|
|
|
} else {
|
|
|
|
out_prate = DEFAULT_BIT_RATE_C / (8*TS_SIZE);
|
|
|
|
}
|
|
|
|
slen = CreateNIT(NIT, wd->tp, 0, wd->chans, "DD", 1, 0, 0);
|
|
|
|
|
|
|
|
init_sec_data(&sec, NIT, slen);
|
|
|
|
in_prate = 0;
|
|
|
|
|
|
|
|
inflength = lseek64(wd->fd_in, 0, SEEK_END);
|
|
|
|
inflength = TS_SIZE*(inflength/TS_SIZE);
|
|
|
|
if (inflength <= 0){
|
|
|
|
if (inflength == 0 || errno == ESPIPE){
|
|
|
|
isdvr = 1;
|
|
|
|
fprintf(stderr,"Non seekable file, handling as dvr\n");
|
|
|
|
} else {
|
|
|
|
fprintf(stderr,"Error in lseek, aborting\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isdvr){
|
|
|
|
fprintf(stderr,"Input file length: %.2f MiB\n",inflength/1024./1024.);
|
|
|
|
lseek64(wd->fd_in,0,SEEK_SET);
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr,"Starting %s\n",wd->name);
|
|
|
|
while (1) {
|
|
|
|
int wc=0;
|
|
|
|
first = 0;
|
|
|
|
last = 0;
|
|
|
|
|
|
|
|
if ((ci=read(wd->fd_in,buffer,BSIZE))<0) return;
|
|
|
|
|
|
|
|
first = find_pcr(buffer, ci, 0);
|
|
|
|
last = find_pcr(buffer, ci, 1);
|
|
|
|
if (first == last){
|
|
|
|
fprintf(stderr,"Input buffer too small\n"
|
|
|
|
"first %d last %d length %d pidf 0x%x\n", first/TS_SIZE,
|
|
|
|
last/TS_SIZE,
|
|
|
|
ci/TS_SIZE, get_pid(buffer+first+1));
|
|
|
|
}
|
|
|
|
fpcr = convert_pcr(get_pcr(buffer+first));
|
|
|
|
lpcr = convert_pcr(get_pcr(buffer+last));
|
|
|
|
if (lpcr > fpcr) delta_pcr = lpcr - fpcr;
|
|
|
|
else delta_pcr = (MAXPCR-fpcr)+lpcr;
|
|
|
|
int packs = (last-first)/TS_SIZE;
|
|
|
|
in_prate = ((packs*10000/pcr_in_msecs(delta_pcr))+5)/10;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
fprintf(stderr,"delta_pcr %d ms npack %d packet_rate in: %d out: %d\n",
|
|
|
|
pcr_in_msecs(delta_pcr),
|
|
|
|
packs, in_prate, out_prate);
|
|
|
|
fprintf(stderr,"Input bitrate %.2f MBit output bitrate %.2f MBit\n",
|
|
|
|
(in_prate*TS_SIZE*8)/1000000.0,
|
|
|
|
(out_prate*TS_SIZE*8)/1000000.0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (in_prate > out_prate){
|
|
|
|
fprintf(stderr,"Input bitrate (%.2f MBit) larger than "
|
|
|
|
"possible output bitrate (%.2f MBit)\n",
|
|
|
|
(in_prate*TS_SIZE*8)/1000000.0,
|
|
|
|
(out_prate*TS_SIZE*8)/1000000.0);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int filler = (out_prate*10/in_prate+5)/10-1;
|
|
|
|
int d=0;
|
|
|
|
int rest=0;
|
|
|
|
for (int s = 0; s < ci; s+=TS_SIZE){
|
|
|
|
memcpy(obuffer+d, buffer+s, TS_SIZE*sizeof(uint8_t));
|
|
|
|
d+=TS_SIZE;
|
|
|
|
int fill = (OBSIZE - d)/TS_SIZE;
|
|
|
|
if (fill < filler) rest = filler - fill;
|
|
|
|
else fill = filler;
|
|
|
|
// fprintf(stderr,"d %d fill %d filler %d %d %d\n", d, fill,filler,s,ci);
|
|
|
|
int nit = 0;
|
|
|
|
for(int i=0;i < fill; i++){
|
|
|
|
if (writeNIT && nit < 4 ) {
|
|
|
|
write_sec_pack(&sec, obuffer+d, NIT_PID);
|
|
|
|
nit++;
|
|
|
|
} else write_filler(obuffer+d);
|
|
|
|
d+=TS_SIZE;
|
|
|
|
}
|
|
|
|
if (d >= OBSIZE){
|
|
|
|
for (int m = 0; m < wd->chans; m++){
|
|
|
|
int lc = 0;
|
|
|
|
while (lc < OBSIZE){
|
|
|
|
if ((w=write(wd->fd_out[m],obuffer+lc,OBSIZE-lc))<0){
|
|
|
|
fprintf(stderr,"Problem writing to modulator from %s (%d %d) %s\n",
|
|
|
|
wd->name, w, ci,strerror(errno));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
wc += w;
|
|
|
|
lc+=w;
|
|
|
|
d = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for(int i=0;i < rest; i++){
|
|
|
|
write_filler(obuffer+d);
|
|
|
|
d+=TS_SIZE;
|
|
|
|
}
|
|
|
|
rest = 0;
|
|
|
|
}
|
|
|
|
if (inflength){
|
|
|
|
c+=ci;
|
|
|
|
fprintf(stderr,"written %d %d%%\n",(int)c,(int)((100*c)/inflength));
|
|
|
|
if (c >=inflength ){
|
|
|
|
fprintf(stderr,"Restarting stream %s\n",wd->name);
|
|
|
|
lseek64(wd->fd_in,0,SEEK_SET);
|
|
|
|
c = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
|
|
|
|
//+++++++++++++++++++++++++++++++++DVB DEVICES++++++++++++++++++++++++++++
|
|
|
|
|
|
|
|
#define DVB_TUNE 0
|
|
|
|
#define DVB_MOD 1
|
|
|
|
#define DVB_CA 2
|
|
|
|
#define MAX_ADAPT 32
|
|
|
|
|
|
|
|
typedef struct dvb_devices_t {
|
|
|
|
int adapters;
|
|
|
|
int dtype[MAX_ADAPT];
|
|
|
|
int ndevs[MAX_ADAPT];
|
|
|
|
uint64_t snr[MAX_ADAPT];
|
|
|
|
uint64_t hwid[MAX_ADAPT];
|
|
|
|
uint64_t devid0[MAX_ADAPT];
|
|
|
|
} dvb_devices;
|
|
|
|
|
|
|
|
|
|
|
|
static int check_tuner(int adapt)
|
|
|
|
{
|
|
|
|
char device[35];
|
|
|
|
int front = 0;
|
|
|
|
int done = 0;
|
|
|
|
|
|
|
|
while (!done) {
|
|
|
|
snprintf(device,34,"/dev/dvb/adapter%d/frontend%d",adapt,front);
|
|
|
|
// fprintf(stderr,"Checking for %s\n", device);
|
|
|
|
if(access(device, F_OK) < 0)
|
|
|
|
done=1;
|
|
|
|
else {
|
|
|
|
front++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return front;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_ca(int adapt)
|
|
|
|
{
|
|
|
|
char device[25];
|
|
|
|
int ca = 0;
|
|
|
|
int done = 0;
|
|
|
|
|
|
|
|
while (!done) {
|
|
|
|
snprintf(device,24,"/dev/dvb/adapter%d/ci%d",adapt,ca);
|
|
|
|
if(access(device, F_OK) < 0)
|
|
|
|
done=1;
|
|
|
|
else {
|
|
|
|
ca++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ca;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_modulator(int adapt)
|
|
|
|
{
|
|
|
|
char device[25];
|
|
|
|
int mod = 0;
|
|
|
|
int done = 0;
|
|
|
|
|
|
|
|
while (!done) {
|
|
|
|
snprintf(device,24,"/dev/dvb/adapter%d/mod%d",adapt,mod);
|
|
|
|
if(access(device, F_OK) < 0)
|
|
|
|
done=1;
|
|
|
|
else {
|
|
|
|
mod++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return mod;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int check_dvb(dvb_devices *ddevices){
|
|
|
|
int done = 0;
|
|
|
|
int nadapt = 0;
|
|
|
|
int maxmod = 0;
|
|
|
|
int maxfront = 0;
|
|
|
|
int maxca = 0;
|
|
|
|
int i;
|
|
|
|
int adapter =0;
|
|
|
|
|
|
|
|
while ( nadapt < MAX_ADAPT && !done){
|
|
|
|
maxca = check_ca(nadapt);
|
|
|
|
maxmod = check_modulator(nadapt);
|
|
|
|
maxfront = check_tuner(nadapt);
|
|
|
|
|
|
|
|
if (maxmod){
|
|
|
|
ddevices->dtype[nadapt] = DVB_MOD;
|
|
|
|
ddevices->ndevs[nadapt] = maxmod;
|
|
|
|
maxmod = 0;
|
|
|
|
nadapt++;
|
|
|
|
} else {
|
|
|
|
if (maxfront) {
|
|
|
|
ddevices->dtype[nadapt] = DVB_TUNE;
|
|
|
|
ddevices->ndevs[nadapt] = maxfront;
|
|
|
|
maxfront = 0;
|
|
|
|
nadapt++;
|
|
|
|
} else {
|
|
|
|
if (maxca) {
|
|
|
|
ddevices->dtype[nadapt] = DVB_CA;
|
|
|
|
ddevices->ndevs[nadapt] = maxca;
|
|
|
|
maxca = 0;
|
|
|
|
nadapt++;
|
|
|
|
} else {
|
|
|
|
done = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ddevices->adapters = nadapt;
|
|
|
|
if (!nadapt) return -1;
|
|
|
|
|
|
|
|
fprintf(stderr,"Found %d dvb adapters\n",nadapt);
|
|
|
|
for (i=0; i<nadapt; i++){
|
|
|
|
/*
|
|
|
|
if (check_sysfs(i,ddevices) < 0){
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
switch(ddevices->dtype[i]){
|
|
|
|
case DVB_TUNE:
|
|
|
|
fprintf(stderr," Adapter %d is a TUNER CARD with %d FRONTENDS\n", i, ddevices->ndevs[i]);
|
|
|
|
break;
|
|
|
|
case DVB_MOD:
|
|
|
|
fprintf(stderr," Adapter %d is a MODULATOR CARD with %d MODULATORS\n", i, ddevices->ndevs[i]);
|
|
|
|
adapter = i;
|
|
|
|
break;
|
|
|
|
case DVB_CA:
|
|
|
|
fprintf(stderr," Adapter %d is a CI CARD with %d CA \n",i,ddevices->ndevs[i]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return adapter;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
|
|
|
|
//++++++++++++++++++++++++++++++++DVBT MOD++++++++++++++++++++++++++++++++
|
|
|
|
|
|
|
|
static int mci_cmd(int dev, struct mci_command *cmd)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct ddb_mci_msg msg;
|
|
|
|
uint8_t status;
|
|
|
|
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
|
|
msg.link = 0;
|
|
|
|
memcpy(&msg.cmd, cmd, sizeof(msg.cmd));
|
|
|
|
//dump((uint8_t *) &msg.cmd, sizeof(msg.cmd));
|
|
|
|
ret = ioctl(dev, IOCTL_DDB_MCI_CMD, &msg);
|
|
|
|
if (ret < 0) {
|
|
|
|
dprintf(2, "mci_cmd error %s\n",strerror(errno));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
status = msg.res.status;
|
|
|
|
if (status == MCI_STATUS_OK)
|
|
|
|
return ret;
|
|
|
|
if (status == MCI_STATUS_UNSUPPORTED) {
|
|
|
|
dprintf(2, "Unsupported MCI command\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
if (status == MCI_STATUS_INVALID_PARAMETER) {
|
|
|
|
dprintf(2, "Invalid MCI parameters\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mci_set_output(int fd, uint8_t connector, uint8_t nchannels, uint8_t unit,
|
|
|
|
int16_t power)
|
|
|
|
{
|
|
|
|
char con[14];
|
|
|
|
char un[6];
|
|
|
|
struct mci_command msg_output = {
|
|
|
|
.mod_command = MOD_SETUP_OUTPUT,
|
|
|
|
.mod_channel = 0,
|
|
|
|
.mod_stream = 0,
|
|
|
|
.mod_setup_output = {
|
|
|
|
.connector = MOD_CONNECTOR_F,
|
|
|
|
.num_channels = 14,
|
|
|
|
.unit = MOD_UNIT_DBUV,
|
|
|
|
.channel_power = 9000,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
msg_output.mod_setup_output.connector = connector;
|
|
|
|
msg_output.mod_setup_output.num_channels = nchannels;
|
|
|
|
msg_output.mod_setup_output.unit = unit;
|
|
|
|
msg_output.mod_setup_output.channel_power = power;
|
|
|
|
|
|
|
|
|
|
|
|
switch (connector){
|
|
|
|
case MOD_CONNECTOR_F:
|
|
|
|
snprintf(con, 14, "F-Connector");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MOD_CONNECTOR_SMA:
|
|
|
|
snprintf(con, 14, "SMA-Connector");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MOD_CONNECTOR_OFF:
|
|
|
|
snprintf(con, 14, "off");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
fprintf(stderr,"unknown connector in modulator setup\n");
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
switch (unit){
|
|
|
|
case MOD_UNIT_DBUV:
|
|
|
|
snprintf(un, 6, " dBuV");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MOD_UNIT_DBM:
|
|
|
|
snprintf(un, 6, " dBm");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
fprintf(stderr,"unknow power unit in modulator setup\n");
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-09-16 14:52:32 +02:00
|
|
|
fprintf(stderr,"Setting DVB Modulator output to %s, %d channels, power %f%s\n",
|
2022-10-26 13:16:21 +02:00
|
|
|
con, nchannels, (double)power/100, un );
|
|
|
|
|
|
|
|
return mci_cmd(fd,&msg_output);
|
|
|
|
}
|
|
|
|
|
|
|
|
int mci_set_output_simple(int adapt, uint8_t nchannels)
|
|
|
|
{
|
|
|
|
char fn[128];
|
|
|
|
int re = 0;
|
|
|
|
|
|
|
|
snprintf(fn, 127, "/dev/dvb/adapter%u/mod0", adapt);
|
|
|
|
int fd = open(fn, O_RDWR);
|
|
|
|
if (fd < 0) {
|
|
|
|
fprintf(stderr, "Could not open %s\n", fn);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
re = mci_set_output(fd, MOD_CONNECTOR_F, nchannels, MOD_UNIT_DBUV, 9000);
|
|
|
|
close(fd);
|
|
|
|
return re;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mci_set_channels(int fd, uint32_t freq, uint8_t nchan, uint8_t standard,
|
|
|
|
uint32_t offset, uint32_t bandw)
|
|
|
|
{
|
|
|
|
char stand[25];
|
|
|
|
struct mci_command msg_channels = {
|
|
|
|
.mod_command = MOD_SETUP_CHANNELS,
|
|
|
|
.mod_channel = 0,
|
|
|
|
.mod_stream = 0,
|
|
|
|
.mod_setup_channels[0] = {
|
|
|
|
.flags = MOD_SETUP_FLAG_FIRST|MOD_SETUP_FLAG_LAST|MOD_SETUP_FLAG_VALID,
|
|
|
|
.standard = MOD_STANDARD_DVBT_8,
|
|
|
|
.num_channels = 25,
|
|
|
|
.frequency = 474000000,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
msg_channels.mod_setup_channels[0].frequency = freq;
|
|
|
|
msg_channels.mod_setup_channels[0].num_channels = nchan;
|
|
|
|
msg_channels.mod_setup_channels[0].standard = standard;
|
|
|
|
if (standard == MOD_STANDARD_GENERIC){
|
|
|
|
msg_channels.mod_setup_channels[0].offset = offset;
|
|
|
|
msg_channels.mod_setup_channels[0].bandwidth = bandw;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(standard){
|
|
|
|
case MOD_STANDARD_GENERIC:
|
|
|
|
snprintf(stand, 24, "MOD_STANDARD_GENERIC");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MOD_STANDARD_DVBT_8:
|
|
|
|
snprintf(stand, 24, "MOD_STANDARD_DVBT_8");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MOD_STANDARD_DVBT_7:
|
|
|
|
snprintf(stand, 24, "MOD_STANDARD_DVBT_7");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MOD_STANDARD_DVBT_6:
|
|
|
|
snprintf(stand, 24, "MOD_STANDARD_DVBT_6");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MOD_STANDARD_DVBT_5:
|
|
|
|
snprintf(stand, 24, "MOD_STANDARD_DVBT_5");
|
|
|
|
break;
|
|
|
|
|
2023-09-16 15:00:27 +02:00
|
|
|
case MOD_STANDARD_DVBC_8:
|
|
|
|
snprintf(stand, 24, "MOD_STANDARD_DVBC_8");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MOD_STANDARD_DVBC_7:
|
|
|
|
snprintf(stand, 24, "MOD_STANDARD_DVBC_7");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MOD_STANDARD_DVBC_6:
|
|
|
|
snprintf(stand, 24, "MOD_STANDARD_DVBC_6");
|
|
|
|
break;
|
|
|
|
|
2022-10-26 13:16:21 +02:00
|
|
|
default:
|
|
|
|
fprintf(stderr,"unknown standard in channels setup\n");
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
2023-09-16 15:08:24 +02:00
|
|
|
fprintf(stderr,"Setting DVB Modulator channels to %d HZ, %d channels, %s\n",
|
2022-10-26 13:16:21 +02:00
|
|
|
freq, nchan, stand);
|
|
|
|
|
|
|
|
return mci_cmd(fd,&msg_channels);
|
|
|
|
}
|
|
|
|
|
2023-09-16 15:12:18 +02:00
|
|
|
int mci_set_channels_simple(int adapt, enum fe_delivery_system delsys,
|
|
|
|
uint32_t freq, uint8_t nchan)
|
2022-10-26 13:16:21 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
char fn[128];
|
|
|
|
int re = 0;
|
|
|
|
|
|
|
|
snprintf(fn, 127, "/dev/dvb/adapter%u/mod0", adapt);
|
|
|
|
int fd = open(fn, O_RDWR);
|
|
|
|
if (fd < 0) {
|
|
|
|
fprintf(stderr, "Could not open %s\n", fn);
|
|
|
|
return -1;
|
|
|
|
}
|
2023-09-16 15:12:18 +02:00
|
|
|
switch(delsys){
|
|
|
|
case SYS_DVBC_ANNEX_A:
|
|
|
|
re = mci_set_channels(fd, freq, nchan, MOD_STANDARD_DVBC_8, 0, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SYS_DVBT:
|
|
|
|
re = mci_set_channels(fd, freq, nchan, MOD_STANDARD_DVBT_8, 0, 0);
|
|
|
|
break;
|
2022-10-26 13:16:21 +02:00
|
|
|
|
2023-09-16 15:12:18 +02:00
|
|
|
default:
|
|
|
|
re = -1;
|
|
|
|
}
|
2022-10-26 13:16:21 +02:00
|
|
|
close(fd);
|
|
|
|
return re;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mci_set_stream( int fd, uint8_t stream, uint8_t channel, uint8_t standard,
|
|
|
|
uint8_t stream_format, uint32_t symbol_rate,
|
|
|
|
uint8_t modulation, uint8_t rolloff,
|
|
|
|
uint8_t fft_size, uint8_t guard_interval,
|
|
|
|
uint8_t puncture_rate, uint8_t constellation,
|
|
|
|
uint16_t cell_identifier)
|
|
|
|
{
|
|
|
|
struct mci_command msg_stream = {
|
|
|
|
.mod_command = MOD_SETUP_STREAM,
|
|
|
|
.mod_channel = 0,
|
|
|
|
.mod_stream = 0,
|
|
|
|
.mod_setup_stream = {
|
|
|
|
.standard = MOD_STANDARD_DVBC_8,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
msg_stream.mod_channel = channel;
|
|
|
|
msg_stream.mod_stream = stream;
|
|
|
|
msg_stream.mod_setup_stream.standard = standard;
|
|
|
|
msg_stream.mod_setup_stream.stream_format = stream_format;
|
|
|
|
if (symbol_rate)
|
|
|
|
msg_stream.mod_setup_stream.symbol_rate = symbol_rate;
|
|
|
|
if (modulation)
|
|
|
|
msg_stream.mod_setup_stream.qam.modulation = modulation;
|
|
|
|
if (rolloff)
|
|
|
|
msg_stream.mod_setup_stream.qam.rolloff = rolloff;
|
|
|
|
msg_stream.mod_setup_stream.ofdm.fft_size = fft_size;
|
|
|
|
msg_stream.mod_setup_stream.ofdm.guard_interval = guard_interval;
|
|
|
|
msg_stream.mod_setup_stream.ofdm.puncture_rate = puncture_rate;
|
|
|
|
msg_stream.mod_setup_stream.ofdm.constellation = constellation;
|
|
|
|
if (cell_identifier)
|
|
|
|
msg_stream.mod_setup_stream.ofdm.cell_identifier = cell_identifier;
|
|
|
|
|
2023-09-16 14:56:27 +02:00
|
|
|
fprintf(stderr,"Setting DVB Stream %d to channel %d\n",stream, channel);
|
2022-10-26 13:16:21 +02:00
|
|
|
|
|
|
|
return mci_cmd(fd,&msg_stream);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-09-16 14:41:46 +02:00
|
|
|
void set_dvb_mods(int adapt, int chans, uint32_t start_freq,
|
|
|
|
enum fe_delivery_system delsys, write_data *wd)
|
2022-10-26 13:16:21 +02:00
|
|
|
{
|
|
|
|
if ((mci_set_output_simple(adapt, chans) < 0)||
|
2023-09-16 15:12:18 +02:00
|
|
|
(mci_set_channels_simple(adapt, delsys, start_freq, chans)< 0))
|
2022-10-26 13:16:21 +02:00
|
|
|
{
|
2023-09-16 14:56:27 +02:00
|
|
|
fprintf(stderr,"Error setting up DVB Modulator\n");
|
2022-10-26 13:16:21 +02:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
wd->chans = chans;
|
|
|
|
wd->fd_out = (int *)malloc(chans*sizeof(int));
|
|
|
|
memset(wd->fd_out,0,chans*sizeof(int));
|
|
|
|
|
|
|
|
for (int i = 0; i < chans; i++){
|
|
|
|
char *device;
|
|
|
|
int fd;
|
|
|
|
|
2023-09-16 14:52:32 +02:00
|
|
|
wd->tp[i].tpid = i;
|
|
|
|
wd->tp[i].delsys = delsys;
|
|
|
|
wd->tp[i].freq = start_freq+8000000*i;
|
|
|
|
|
2023-09-16 14:41:46 +02:00
|
|
|
switch(delsys){
|
|
|
|
case SYS_DVBT:
|
|
|
|
wd->tp[i].qam = 2;
|
|
|
|
wd->tp[i].symbolrate = 0;
|
|
|
|
wd->tp[i].bandwidth = 0;
|
|
|
|
wd->tp[i].guard = 0;
|
|
|
|
wd->tp[i].code_rate = 4;
|
|
|
|
wd->tp[i].trans_mode = MOD_STANDARD_DVBT_8;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SYS_DVBC_ANNEX_A:
|
|
|
|
wd->tp[i].qam = QAM_256;
|
|
|
|
wd->tp[i].symbolrate = 6900000;
|
|
|
|
wd->tp[i].bandwidth = 0;
|
|
|
|
wd->tp[i].guard = 0;
|
|
|
|
wd->tp[i].code_rate = 0;
|
|
|
|
wd->tp[i].trans_mode = 0;
|
|
|
|
break;
|
|
|
|
}
|
2023-02-04 15:19:11 +01:00
|
|
|
device = malloc(sizeof(char)*40);
|
|
|
|
snprintf(device,35,"/dev/dvb/adapter%d/mod%d",adapt,i);
|
2022-10-26 13:16:21 +02:00
|
|
|
fd = open(device, O_RDWR);
|
|
|
|
if( fd < 0 )
|
|
|
|
{
|
|
|
|
fprintf(stderr,"Error opening %s : %s\n",device,strerror(errno));
|
|
|
|
free(device);
|
|
|
|
exit(1);
|
|
|
|
}
|
2023-09-16 14:52:32 +02:00
|
|
|
|
|
|
|
|
|
|
|
int re= 0;
|
|
|
|
uint8_t stream_format = 4; //format is ransport stream
|
|
|
|
uint8_t fft_size = wd->tp[i].trans_mode;
|
|
|
|
|
|
|
|
switch(delsys){
|
|
|
|
case SYS_DVBC_ANNEX_A:
|
|
|
|
re = mci_set_stream( fd, i, i, MOD_STANDARD_DVBC_8,
|
|
|
|
stream_format, wd->tp[i].symbolrate,
|
|
|
|
wd->tp[i].qam, 0, 0, 0, 0, 0, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SYS_DVBT:
|
|
|
|
re = mci_set_stream( fd, i, i, MOD_STANDARD_DVBT_8,
|
|
|
|
stream_format, 0,
|
|
|
|
0, 0, fft_size, wd->tp[i].guard,
|
|
|
|
wd->tp[i].code_rate, wd->tp[i].qam,
|
|
|
|
0);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
re = -1;
|
|
|
|
}
|
2022-10-26 13:16:21 +02:00
|
|
|
|
|
|
|
close(fd);
|
|
|
|
free(device);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//+++++++++++++++++++++++++++++CLI++++++++++++++++++++++++++++++++++++++++
|
|
|
|
|
|
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
|
|
|
|
static void usage(char *progname)
|
|
|
|
{
|
|
|
|
printf ("usage: %s [options] <input files>\n\n",progname);
|
|
|
|
printf ("options:\n");
|
2023-02-04 16:13:10 +01:00
|
|
|
printf (" --adapter <n>, -a <n> : adapter number <n> of modulator card (defaults to first found)\n");
|
|
|
|
printf (" --mods <n>, -m <n> : number <n> of modulators to use (default all)\n");
|
|
|
|
printf (" --file, -i : input filename (default test.ts)\n");
|
|
|
|
printf (" --frequency, -f : start frequency in MHz (default DVB_C 114MHz, DVB-T 474MHz)\n");
|
|
|
|
printf (" --dvbt, -t : modulator is DVB-T\n");
|
|
|
|
printf (" --NIT, -n : write a minimal NIT for faster scan\n");
|
2023-02-04 15:19:11 +01:00
|
|
|
printf (" --help, -h : print help message\n");
|
2022-10-26 13:16:21 +02:00
|
|
|
printf ("\n");
|
|
|
|
printf ("\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-02-04 15:19:11 +01:00
|
|
|
static int parse_cl(int argc, char * const argv[], char **filename, int *chans)
|
2022-10-26 13:16:21 +02:00
|
|
|
{
|
|
|
|
int c;
|
|
|
|
int fset = 0;
|
|
|
|
|
|
|
|
*filename = strdup("test.ts");
|
|
|
|
|
|
|
|
writeNIT = 0;
|
|
|
|
dvbt = 0;
|
|
|
|
while (1){
|
|
|
|
int option_index = 0;
|
|
|
|
static struct option long_options[] = {
|
|
|
|
{"help", no_argument , NULL, 'h'},
|
|
|
|
{"dvbt", no_argument , NULL, 't'},
|
|
|
|
{"NIT", no_argument , NULL, 'n'},
|
|
|
|
{"adapter", required_argument , NULL, 'a'},
|
2023-02-04 15:19:11 +01:00
|
|
|
{"mods", required_argument , NULL, 'm'},
|
2022-10-26 13:16:21 +02:00
|
|
|
{"file", required_argument , NULL, 'i'},
|
|
|
|
{"frequency", required_argument , NULL, 'f'},
|
|
|
|
{NULL, 0, NULL, 0}
|
|
|
|
};
|
|
|
|
|
2023-02-04 15:19:11 +01:00
|
|
|
c = getopt_long (argc, argv, "ha:i:f:ntm:",long_options,
|
2022-10-26 13:16:21 +02:00
|
|
|
&option_index);
|
|
|
|
|
|
|
|
if (c == -1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
switch (c){
|
|
|
|
case 'a':
|
|
|
|
adapt = (int)strtol(optarg,(char **)NULL, 0);
|
|
|
|
break;
|
|
|
|
|
2023-02-04 15:19:11 +01:00
|
|
|
case 'm':
|
|
|
|
*chans = (int)strtol(optarg,(char **)NULL, 0);
|
|
|
|
break;
|
|
|
|
|
2022-10-26 13:16:21 +02:00
|
|
|
case 'f':
|
|
|
|
fset = 1;
|
|
|
|
start_freq = strtoul(optarg, NULL, 0)*1000000;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'i':
|
|
|
|
if (*filename){
|
|
|
|
free(*filename);
|
|
|
|
}
|
|
|
|
*filename = strdup(optarg);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'n':
|
|
|
|
writeNIT = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 't':
|
|
|
|
if (!fset) start_freq = START_FREQ_T;
|
|
|
|
dvbt = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'h':
|
|
|
|
case '?':
|
|
|
|
default:
|
|
|
|
usage(argv[0]);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return adapt;
|
|
|
|
|
|
|
|
}
|
|
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
|
|
|
|
//++++++++++++++++++++++++++++++++++++MAIN++++++++++++++++++++++++++++++++
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
const char *progn = "modtest";
|
|
|
|
|
|
|
|
int maxout = 0;
|
|
|
|
int mods = 0;
|
|
|
|
char *filename;
|
|
|
|
char *device;
|
|
|
|
dvb_devices ddevices;
|
|
|
|
write_data wd;
|
|
|
|
int fd_out;
|
|
|
|
int fd_in;
|
2023-02-04 15:19:11 +01:00
|
|
|
int chans = 0;
|
2022-10-26 13:16:21 +02:00
|
|
|
|
|
|
|
printf("%s \n\n", progn);
|
2023-02-04 15:19:11 +01:00
|
|
|
adapt = parse_cl(argc, argv, &filename, &chans);
|
2022-10-26 13:16:21 +02:00
|
|
|
if (check_dvb(&ddevices)<0){
|
|
|
|
fprintf(stderr,"No DVB devices found\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i=0; i<ddevices.adapters; i++){
|
|
|
|
if ( ddevices.dtype[i] == DVB_MOD ) {
|
|
|
|
mods++;
|
|
|
|
maxout+= ddevices.ndevs[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !mods || !maxout ){
|
|
|
|
fprintf(stdout,"No Modulator device found\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
fprintf(stdout,"Found %d modulator devices with %d mods\n",mods, maxout);
|
|
|
|
|
|
|
|
if (ddevices.dtype[adapt] == DVB_MOD){
|
|
|
|
fprintf(stdout,"Using adapter %d\n",adapt);
|
|
|
|
} else {
|
|
|
|
fprintf(stdout,"Adapter %d is not a modulator\n",adapt);
|
|
|
|
int i=0;
|
|
|
|
while (i< ddevices.adapters && ddevices.dtype[i] != DVB_MOD){
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
adapt = i;
|
|
|
|
fprintf(stdout,"Using adapter %d instead\n",adapt);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((fd_in = open(filename ,O_RDONLY| O_LARGEFILE)) < 0){
|
|
|
|
fprintf(stderr,"\nError opening input file:%s\n",filename);
|
|
|
|
if (!strncmp(filename,"test.ts",7)) {
|
|
|
|
fprintf(stderr,"\nYou can create the default input file test.ts\n");
|
|
|
|
fprintf(stderr,"by executing: \"make test.ts\"\n");
|
|
|
|
}
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2023-02-04 15:19:11 +01:00
|
|
|
if (chans<1 || chans >ddevices.ndevs[adapt] )
|
|
|
|
chans = ddevices.ndevs[adapt];
|
2022-10-26 13:16:21 +02:00
|
|
|
|
|
|
|
if (dvbt){
|
2023-09-16 14:41:46 +02:00
|
|
|
set_dvb_mods(adapt, chans, start_freq, SYS_DVBT, &wd);
|
2022-10-26 13:16:21 +02:00
|
|
|
} else {
|
2023-09-16 14:41:46 +02:00
|
|
|
set_dvb_mods(adapt, chans, start_freq, SYS_DVBC_ANNEX_A, &wd);
|
2022-10-26 13:16:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr,"Reading from %s\n", filename);
|
2023-02-04 15:19:11 +01:00
|
|
|
device = malloc(sizeof(char)*40);
|
2022-10-26 13:16:21 +02:00
|
|
|
for (int m= 0; m < chans; m++){
|
2023-02-04 15:19:11 +01:00
|
|
|
snprintf(device,35,"/dev/dvb/adapter%d/mod%d",adapt,m);
|
2022-10-26 13:16:21 +02:00
|
|
|
fd_out = open(device, O_WRONLY);
|
|
|
|
wd.fd_out[m] = fd_out;
|
|
|
|
wd.fd_in = fd_in;
|
|
|
|
wd.name = filename;
|
|
|
|
}
|
|
|
|
write_mods(&wd);
|
|
|
|
|
|
|
|
exit(0);
|
|
|
|
}
|