Merge relevant parts of PR#255 to complete the implementation of PAT/PMT passthrough, with the option of rewriting the PAT and PMT so the PAT only contains a reference to the included service, and the PMT only contains references to the included streams. This still needs more work - the PMT PID is being passed to the muxer as zero the first time a channel is streamed after tvh starts, and the PMT rewrite code fails if the PMT spans multiple TS packets
This commit is contained in:
parent
7f11eac58a
commit
6df74d3d75
17 changed files with 213 additions and 258 deletions
|
@ -68,6 +68,7 @@ extern struct dvr_entry_list dvrentries;
|
|||
#define DVR_SKIP_COMMERCIALS 0x400
|
||||
#define DVR_SUBTITLE_IN_TITLE 0x800
|
||||
#define DVR_EPISODE_BEFORE_DATE 0x1000
|
||||
#define DVR_REWRITE_PATPMT 0x2000
|
||||
|
||||
typedef enum {
|
||||
DVR_PRIO_IMPORTANT,
|
||||
|
|
|
@ -1111,6 +1111,9 @@ dvr_init(void)
|
|||
|
||||
cfg->dvr_mc = htsmsg_get_u32_or_default(m, "container", MC_MATROSKA);
|
||||
|
||||
if(!htsmsg_get_u32(m, "rewrite-patpmt", &u32) && u32)
|
||||
cfg->dvr_flags |= DVR_REWRITE_PATPMT;
|
||||
|
||||
htsmsg_get_s32(m, "pre-extra-time", &cfg->dvr_extra_time_pre);
|
||||
htsmsg_get_s32(m, "post-extra-time", &cfg->dvr_extra_time_post);
|
||||
htsmsg_get_u32(m, "retention-days", &cfg->dvr_retention_days);
|
||||
|
@ -1307,6 +1310,7 @@ dvr_save(dvr_config_t *cfg)
|
|||
htsmsg_add_str(m, "config_name", cfg->dvr_config_name);
|
||||
htsmsg_add_str(m, "storage", cfg->dvr_storage);
|
||||
htsmsg_add_u32(m, "container", cfg->dvr_mc);
|
||||
htsmsg_add_u32(m, "rewrite-patpmt", !!(cfg->dvr_flags & DVR_REWRITE_PATPMT));
|
||||
htsmsg_add_u32(m, "retention-days", cfg->dvr_retention_days);
|
||||
htsmsg_add_u32(m, "pre-extra-time", cfg->dvr_extra_time_pre);
|
||||
htsmsg_add_u32(m, "post-extra-time", cfg->dvr_extra_time_post);
|
||||
|
|
|
@ -288,8 +288,13 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
|
|||
const streaming_start_component_t *ssc;
|
||||
int i;
|
||||
dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
|
||||
muxer_container_type_t mc;
|
||||
muxer_config_t m_cfg;
|
||||
|
||||
de->de_mux = muxer_create(de->de_mc);
|
||||
mc = de->de_mc;
|
||||
m_cfg.rewrite_patpmt = !!(cfg->dvr_flags & DVR_REWRITE_PATPMT);
|
||||
|
||||
de->de_mux = muxer_create(mc, &m_cfg);
|
||||
if(!de->de_mux) {
|
||||
dvr_rec_fatal_error(de, "Unable to create muxer");
|
||||
return -1;
|
||||
|
|
|
@ -375,6 +375,7 @@ mpegts_service_create0
|
|||
char buf[256];
|
||||
service_create0((service_t*)s, class, uuid, S_MPEG_TS, conf);
|
||||
|
||||
fprintf(stderr,"Created service - pmt_pid=%d\n",pmt_pid);
|
||||
/* Create */
|
||||
sbuf_init(&s->s_tsbuf);
|
||||
if (!conf) {
|
||||
|
|
|
@ -236,18 +236,18 @@ muxer_container_mime2type(const char *str)
|
|||
* Create a new muxer
|
||||
*/
|
||||
muxer_t*
|
||||
muxer_create(muxer_container_type_t mc)
|
||||
muxer_create(muxer_container_type_t mc, muxer_config_t *m_cfg)
|
||||
{
|
||||
muxer_t *m;
|
||||
|
||||
m = pass_muxer_create(mc);
|
||||
m = pass_muxer_create(mc, m_cfg);
|
||||
|
||||
if(!m)
|
||||
m = tvh_muxer_create(mc);
|
||||
m = tvh_muxer_create(mc, m_cfg);
|
||||
|
||||
#if CONFIG_LIBAV
|
||||
if(!m)
|
||||
m = lav_muxer_create(mc);
|
||||
m = lav_muxer_create(mc, m_cfg);
|
||||
#endif
|
||||
|
||||
if(!m)
|
||||
|
|
|
@ -31,6 +31,12 @@ typedef enum {
|
|||
MC_WEBM = 6,
|
||||
} muxer_container_type_t;
|
||||
|
||||
/* Muxer configuration used when creating a muxer. */
|
||||
typedef struct muxer_config {
|
||||
/* Options only for passthrough muxer */
|
||||
int rewrite_patpmt;
|
||||
|
||||
} muxer_config_t;
|
||||
|
||||
struct muxer;
|
||||
struct streaming_start;
|
||||
|
@ -73,7 +79,7 @@ const char* muxer_container_suffix(muxer_container_type_t mc, int vid
|
|||
int muxer_container_list(htsmsg_t *array);
|
||||
|
||||
// Muxer factory
|
||||
muxer_t *muxer_create(muxer_container_type_t mc);
|
||||
muxer_t *muxer_create(muxer_container_type_t mc, muxer_config_t *m_cfg);
|
||||
|
||||
// Wrapper functions
|
||||
int muxer_open_file (muxer_t *m, const char *filename);
|
||||
|
|
|
@ -493,7 +493,7 @@ lav_muxer_destroy(muxer_t *m)
|
|||
* Create a new libavformat based muxer
|
||||
*/
|
||||
muxer_t*
|
||||
lav_muxer_create(muxer_container_type_t mc)
|
||||
lav_muxer_create(muxer_container_type_t mc, muxer_config_t *m_cfg)
|
||||
{
|
||||
const char *mux_name;
|
||||
lav_muxer_t *lm;
|
||||
|
|
|
@ -21,6 +21,6 @@
|
|||
|
||||
#include "muxer.h"
|
||||
|
||||
muxer_t* lav_muxer_create(muxer_container_type_t mc);
|
||||
muxer_t* lav_muxer_create(muxer_container_type_t mc, muxer_config_t* m_cfg);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -27,17 +27,7 @@
|
|||
#include "service.h"
|
||||
#include "input/mpegts/dvb.h"
|
||||
#include "muxer_pass.h"
|
||||
|
||||
|
||||
#define TS_INJECTION_RATE 1000
|
||||
|
||||
/*
|
||||
TODO: How often do we send the injected packets?
|
||||
Once evry packet? Once every 1000 packets?
|
||||
Or perhaps even once 10k packets?
|
||||
Maby we should messure in seconds instead?
|
||||
For now, every 1000 packets will do.
|
||||
*/
|
||||
#include "dvr/dvr.h"
|
||||
|
||||
typedef struct pass_muxer {
|
||||
muxer_t;
|
||||
|
@ -51,206 +41,142 @@ typedef struct pass_muxer {
|
|||
char *pm_filename;
|
||||
|
||||
/* TS muxing */
|
||||
uint8_t pm_injection;
|
||||
uint8_t *pm_pat;
|
||||
uint8_t *pm_pmt;
|
||||
uint16_t pm_pmt_version;
|
||||
uint32_t pm_ic; // Injection counter
|
||||
uint32_t pm_pc; // Packet counter
|
||||
uint8_t pm_rewrite_patpmt;
|
||||
uint8_t pm_pat_cc;
|
||||
uint16_t pm_pmt_pid;
|
||||
uint16_t pm_service_id;
|
||||
uint32_t pm_streams[256]; /* lookup table identifying which streams to include in the PMT */
|
||||
} pass_muxer_t;
|
||||
|
||||
/**
|
||||
* Append CRC
|
||||
*/
|
||||
static int
|
||||
pass_muxer_append_crc32(uint8_t *buf, int offset, int maxlen)
|
||||
static inline void set_pid_bit(uint32_t* pm_streams, uint16_t pid)
|
||||
{
|
||||
uint32_t crc;
|
||||
|
||||
if(offset + 4 > maxlen)
|
||||
return -1;
|
||||
|
||||
crc = tvh_crc32(buf, offset, 0xffffffff);
|
||||
|
||||
buf[offset + 0] = crc >> 24;
|
||||
buf[offset + 1] = crc >> 16;
|
||||
buf[offset + 2] = crc >> 8;
|
||||
buf[offset + 3] = crc;
|
||||
|
||||
assert(tvh_crc32(buf, offset + 4, 0xffffffff) == 0);
|
||||
|
||||
return offset + 4;
|
||||
pm_streams[pid >> 5] |= 1 << (pid & 31);
|
||||
}
|
||||
|
||||
/**
|
||||
* PAT generator
|
||||
*/
|
||||
|
||||
static int
|
||||
pass_muxer_build_pat(service_t *t, uint8_t *buf, int maxlen, int pmtpid)
|
||||
static inline int check_pid_bit(uint32_t* pm_streams, uint16_t pid)
|
||||
{
|
||||
if(maxlen < 12)
|
||||
return -1;
|
||||
|
||||
buf[0] = 0;
|
||||
buf[1] = 0xb0; /* reserved */
|
||||
buf[2] = 12 + 4 - 3; /* Length */
|
||||
|
||||
buf[3] = 0x00; /* transport stream id */
|
||||
buf[4] = 0x01;
|
||||
|
||||
buf[5] = 0xc1; /* reserved + current_next_indicator + version */
|
||||
buf[6] = 0;
|
||||
buf[7] = 0;
|
||||
|
||||
buf[8] = 0; /* Program number, we only have one program */
|
||||
buf[9] = 1;
|
||||
|
||||
buf[10] = 0xe0 | (pmtpid >> 8);
|
||||
buf[11] = pmtpid;
|
||||
|
||||
return pass_muxer_append_crc32(buf, 12, maxlen);
|
||||
return pm_streams[pid >> 5] & (1 << (pid & 31));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* PMT generator
|
||||
/*
|
||||
* Rewrite a PAT packet to only include the service included in the transport stream.
|
||||
*
|
||||
* This is complicated by the need to deal with PATs that span more than one transport
|
||||
* stream packet. In that scenario, we replace the 2nd and subsequent PAT packets with
|
||||
* NULL packets (PID 0x1fff).
|
||||
*
|
||||
*/
|
||||
static int
|
||||
pass_muxer_build_pmt(const streaming_start_t *ss, uint8_t *buf0, int maxlen,
|
||||
int version, int pcrpid)
|
||||
|
||||
static int pass_muxer_rewrite_pat(pass_muxer_t* pm, unsigned char* buf)
|
||||
{
|
||||
int c, tlen, dlen, l, i;
|
||||
uint8_t *buf, *buf1;
|
||||
int pusi = buf[1] & 0x40;
|
||||
|
||||
buf = buf0;
|
||||
if (pusi) {
|
||||
/* First TS packet, generate our new PAT */
|
||||
|
||||
if(maxlen < 12)
|
||||
return -1;
|
||||
|
||||
buf[0] = 2; /* table id, always 2 */
|
||||
|
||||
buf[3] = 0x00; /* program id */
|
||||
buf[4] = 0x01;
|
||||
|
||||
buf[5] = 0xc1; /* current_next_indicator + version */
|
||||
buf[5] |= (version & 0x1F) << 1;
|
||||
|
||||
buf[6] = 0; /* section number */
|
||||
buf[7] = 0; /* last section number */
|
||||
|
||||
buf[8] = 0xe0 | (pcrpid >> 8);
|
||||
buf[9] = pcrpid;
|
||||
|
||||
buf[10] = 0xf0; /* Program info length */
|
||||
buf[11] = 0x00; /* We dont have any such things atm */
|
||||
|
||||
buf += 12;
|
||||
tlen = 12;
|
||||
|
||||
for(i = 0; i < ss->ss_num_components; i++) {
|
||||
const streaming_start_component_t *ssc = &ss->ss_components[i];
|
||||
|
||||
switch(ssc->ssc_type) {
|
||||
case SCT_MPEG2VIDEO:
|
||||
c = 0x02;
|
||||
break;
|
||||
|
||||
case SCT_MPEG2AUDIO:
|
||||
c = 0x04;
|
||||
break;
|
||||
|
||||
case SCT_EAC3:
|
||||
case SCT_DVBSUB:
|
||||
c = 0x06;
|
||||
break;
|
||||
|
||||
case SCT_MP4A:
|
||||
case SCT_AAC:
|
||||
c = 0x11;
|
||||
break;
|
||||
|
||||
case SCT_H264:
|
||||
c = 0x1b;
|
||||
break;
|
||||
|
||||
case SCT_AC3:
|
||||
c = 0x81;
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
/* Some sanity checks */
|
||||
if (buf[4] != 0) {
|
||||
tvhlog(LOG_ERR, "pass", "Unsupported PAT format - pointer_to_data != 0 (%d) (Please report to developers!)\n",buf[4]);
|
||||
return 1;
|
||||
}
|
||||
int last_section_number = buf[12];
|
||||
if (last_section_number != 0) {
|
||||
tvhlog(LOG_ERR, "pass", "Multi-section PAT not supported (last_section_number = %d) (Please report to developers!)\n",last_section_number);
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
buf[0] = c;
|
||||
buf[1] = 0xe0 | (ssc->ssc_pid >> 8);
|
||||
buf[2] = ssc->ssc_pid;
|
||||
|
||||
buf1 = &buf[3];
|
||||
tlen += 5;
|
||||
buf += 5;
|
||||
dlen = 0;
|
||||
|
||||
switch(ssc->ssc_type) {
|
||||
case SCT_MPEG2AUDIO:
|
||||
case SCT_MP4A:
|
||||
case SCT_AAC:
|
||||
buf[0] = DVB_DESC_LANGUAGE;
|
||||
buf[1] = 4;
|
||||
memcpy(&buf[2],ssc->ssc_lang,3);
|
||||
buf[5] = 0; /* Main audio */
|
||||
dlen = 6;
|
||||
break;
|
||||
case SCT_DVBSUB:
|
||||
buf[0] = DVB_DESC_SUBTITLE;
|
||||
buf[1] = 8;
|
||||
memcpy(&buf[2],ssc->ssc_lang,3);
|
||||
buf[5] = 16; /* Subtitling type */
|
||||
buf[6] = ssc->ssc_composition_id >> 8;
|
||||
buf[7] = ssc->ssc_composition_id;
|
||||
buf[8] = ssc->ssc_ancillary_id >> 8;
|
||||
buf[9] = ssc->ssc_ancillary_id;
|
||||
dlen = 10;
|
||||
break;
|
||||
case SCT_AC3:
|
||||
buf[0] = DVB_DESC_LANGUAGE;
|
||||
buf[1] = 4;
|
||||
memcpy(&buf[2],ssc->ssc_lang,3);
|
||||
buf[5] = 0; /* Main audio */
|
||||
buf[6] = DVB_DESC_AC3;
|
||||
buf[7] = 1;
|
||||
buf[8] = 0; /* XXX: generate real AC3 desc */
|
||||
dlen = 9;
|
||||
break;
|
||||
case SCT_EAC3:
|
||||
buf[0] = DVB_DESC_LANGUAGE;
|
||||
buf[1] = 4;
|
||||
memcpy(&buf[2],ssc->ssc_lang,3);
|
||||
buf[5] = 0; /* Main audio */
|
||||
buf[6] = DVB_DESC_EAC3;
|
||||
buf[7] = 1;
|
||||
buf[8] = 0; /* XXX: generate real EAC3 desc */
|
||||
dlen = 9;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
int current_next_indicator = (buf[10] & 0x1);
|
||||
if (!current_next_indicator) {
|
||||
/* If next version of PAT, do nothing */
|
||||
return 0;
|
||||
}
|
||||
|
||||
tlen += dlen;
|
||||
buf += dlen;
|
||||
/* Rewrite continuity counter, in case this is a multi-packet PAT (we discard all but the first packet) */
|
||||
buf[3] = (buf[3] & 0xf0) | pm->pm_pat_cc;
|
||||
pm->pm_pat_cc = (pm->pm_pat_cc + 1) & 0xf;
|
||||
|
||||
buf1[0] = 0xf0 | (dlen >> 8);
|
||||
buf1[1] = dlen;
|
||||
buf[6] = 0; buf[7] = 13; /* section_length (number of bytes after this field, including CRC) */
|
||||
|
||||
buf[13] = (pm->pm_service_id & 0xff00) >> 8;
|
||||
buf[14] = pm->pm_service_id & 0x00ff;
|
||||
buf[15] = 0xe0 | ((pm->pm_pmt_pid & 0x1f00) >> 8);
|
||||
buf[16] = pm->pm_pmt_pid & 0x00ff;
|
||||
|
||||
uint32_t crc32 = tvh_crc32(buf+5, 12, 0xffffffff);
|
||||
buf[17] = (crc32 & 0xff000000) >> 24;
|
||||
buf[18] = (crc32 & 0x00ff0000) >> 16;
|
||||
buf[19] = (crc32 & 0x0000ff00) >> 8;
|
||||
buf[20] = crc32 & 0x000000ff;
|
||||
|
||||
memset(buf + 21, 0xff, 167); /* Wipe rest of packet */
|
||||
} else {
|
||||
/* The second or subsequent packet of a multi-packet PAT, replace with NULL packet */
|
||||
buf[1] = 0x1f; /* pid 0x1fff */
|
||||
memset(buf+2, 0xff, 186);
|
||||
}
|
||||
|
||||
l = tlen - 3 + 4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf0[1] = 0xb0 | (l >> 8);
|
||||
buf0[2] = l;
|
||||
static int pass_muxer_rewrite_pmt(pass_muxer_t* pm, unsigned char* buf)
|
||||
{
|
||||
int i;
|
||||
|
||||
return pass_muxer_append_crc32(buf0, tlen, maxlen);
|
||||
int pusi = buf[1] & 0x40;
|
||||
|
||||
if (pusi) {
|
||||
/* First TS packet, generate our new PAT */
|
||||
|
||||
int current_next_indicator = (buf[10] & 0x1);
|
||||
if (!current_next_indicator) {
|
||||
/* If next version of PAT, do nothing */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Some sanity checks */
|
||||
if (buf[4] != 0) {
|
||||
tvhlog(LOG_ERR, "pass", "Unsupported PMT format - pointer_to_data != 0 (%d) (Please report to developers!)\n",buf[4]);
|
||||
return 1;
|
||||
}
|
||||
int last_section_number = buf[12];
|
||||
if (last_section_number != 0) {
|
||||
tvhlog(LOG_ERR, "pass", "Multi-section PMT not supported (last_section_number = %d) (Please report to developers!)\n",last_section_number);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int section_length = ((buf[6]&0x0f) << 8) | buf[7]; i += 2;
|
||||
if (section_length > (188 - 4 - 7)) { /* 7 bytes before section_length, plus CRC */
|
||||
tvhlog(LOG_ERR, "pass", "Multi-packet PMT not supported (last_section_number = %d) (Please report to developers!)\n",last_section_number);
|
||||
}
|
||||
|
||||
i = 15;
|
||||
|
||||
int program_info_length = ((buf[i] & 0x0f) << 8) | buf[i+1]; i += 2;
|
||||
i += program_info_length;
|
||||
|
||||
while ( i < 7 + section_length - 4) {
|
||||
//int stream_type = buf[i];
|
||||
int pid = ((buf[i+1]&0x1f) << 8) | buf[i+2];
|
||||
int ES_info_length = ((buf[i+3] & 0x0f) << 8) | buf[i+4];
|
||||
|
||||
if (check_pid_bit(pm->pm_streams,pid)) {
|
||||
i += 5 + ES_info_length;
|
||||
} else {
|
||||
memmove(buf + i, buf + i + 5 + ES_info_length,(188-i-5-ES_info_length));
|
||||
section_length -= (5+ES_info_length);
|
||||
}
|
||||
}
|
||||
|
||||
buf[7] = section_length;
|
||||
|
||||
uint32_t crc32 = tvh_crc32(buf+5, i-5, 0xffffffff);
|
||||
buf[i++] = ((crc32 & 0xff000000) >> 24);
|
||||
buf[i++] = ((crc32 & 0x00ff0000) >> 16);
|
||||
buf[i++] = ((crc32 & 0x0000ff00) >> 8);
|
||||
buf[i++] = (crc32 & 0x000000ff);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -302,36 +228,19 @@ static int
|
|||
pass_muxer_reconfigure(muxer_t* m, const struct streaming_start *ss)
|
||||
{
|
||||
pass_muxer_t *pm = (pass_muxer_t*)m;
|
||||
const source_info_t *si = &ss->ss_si;
|
||||
pm->pm_pmt_pid = ss->ss_pmt_pid;
|
||||
pm->pm_service_id = ss->ss_service_id;
|
||||
|
||||
if(si->si_type == S_MPEG_TS && ss->ss_pmt_pid) {
|
||||
pm->pm_pat = realloc(pm->pm_pat, 188);
|
||||
memset(pm->pm_pat, 0xff, 188);
|
||||
pm->pm_pat[0] = 0x47;
|
||||
pm->pm_pat[1] = 0x40;
|
||||
pm->pm_pat[2] = 0x00;
|
||||
pm->pm_pat[3] = 0x10;
|
||||
pm->pm_pat[4] = 0x00;
|
||||
if(pass_muxer_build_pat(NULL, pm->pm_pat+5, 183, ss->ss_pmt_pid) < 0) {
|
||||
pm->m_errors++;
|
||||
tvhlog(LOG_ERR, "pass", "%s: Unable to build pat", pm->pm_filename);
|
||||
return -1;
|
||||
/* Store the PIDs of all the components */
|
||||
if (pm->pm_rewrite_patpmt) {
|
||||
int i;
|
||||
memset(pm->pm_streams,0,1024);
|
||||
for (i=0;i<ss->ss_num_components;i++) {
|
||||
/* SCT_TEXTSUB (text extracted from teletext) streams are virtual and have an invalid PID */
|
||||
if (ss->ss_components[i].ssc_pid < 8192) {
|
||||
set_pid_bit(pm->pm_streams,ss->ss_components[i].ssc_pid);
|
||||
}
|
||||
}
|
||||
|
||||
pm->pm_pmt = realloc(pm->pm_pmt, 188);
|
||||
memset(pm->pm_pmt, 0xff, 188);
|
||||
pm->pm_pmt[0] = 0x47;
|
||||
pm->pm_pmt[1] = 0x40 | (ss->ss_pmt_pid >> 8);
|
||||
pm->pm_pmt[2] = 0x00 | (ss->ss_pmt_pid >> 0);
|
||||
pm->pm_pmt[3] = 0x10;
|
||||
pm->pm_pmt[4] = 0x00;
|
||||
if(pass_muxer_build_pmt(ss, pm->pm_pmt+5, 183, pm->pm_pmt_version,
|
||||
ss->ss_pcr_pid) < 0) {
|
||||
pm->m_errors++;
|
||||
tvhlog(LOG_ERR, "pass", "%s: Unable to build pmt", pm->pm_filename);
|
||||
return -1;
|
||||
}
|
||||
pm->pm_pmt_version++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -415,25 +324,33 @@ static void
|
|||
pass_muxer_write_ts(muxer_t *m, pktbuf_t *pb)
|
||||
{
|
||||
pass_muxer_t *pm = (pass_muxer_t*)m;
|
||||
int rem;
|
||||
unsigned char* p;
|
||||
|
||||
if(pm->pm_pat != NULL &&
|
||||
pm->pm_pmt != NULL &&
|
||||
pm->pm_injection) {
|
||||
// Inject pmt and pat into the stream
|
||||
rem = pm->pm_pc % TS_INJECTION_RATE;
|
||||
if(!rem) {
|
||||
pm->pm_pat[3] = (pm->pm_pat[3] & 0xf0) | (pm->pm_ic & 0x0f);
|
||||
pm->pm_pmt[3] = (pm->pm_pmt[3] & 0xf0) | (pm->pm_ic & 0x0f);
|
||||
pass_muxer_write(m, pm->pm_pat, 188);
|
||||
pass_muxer_write(m, pm->pm_pmt, 188);
|
||||
pm->pm_ic++;
|
||||
if (pm->pm_rewrite_patpmt) {
|
||||
p = pb->pb_data;
|
||||
while (p < pb->pb_data + pb->pb_size) {
|
||||
int pid = (p[1] & 0x1f) << 8 | p[2];
|
||||
|
||||
if (pid == 0) {
|
||||
/* Remove all PMT references except the one being streamed */
|
||||
if (pass_muxer_rewrite_pat(pm, p)) {
|
||||
tvhlog(LOG_ERR, "pass", "Error rewriting PAT, sending original\n");
|
||||
pm->pm_rewrite_patpmt = 0; /* There was an error, so just give user original PAT from now on */
|
||||
break;
|
||||
}
|
||||
} else if (pid == pm->pm_pmt_pid) {
|
||||
/* Remove all references to streams not being streamed */
|
||||
if (pass_muxer_rewrite_pmt(pm, p)) {
|
||||
tvhlog(LOG_ERR, "pass", "Error rewriting PMT, sending original\n");
|
||||
pm->pm_rewrite_patpmt = 0; /* There was an error, so just give user original PMT from now on */
|
||||
break;
|
||||
}
|
||||
}
|
||||
p += 188;
|
||||
}
|
||||
}
|
||||
|
||||
pass_muxer_write(m, pb->pb_data, pb->pb_size);
|
||||
|
||||
pm->pm_pc += (pb->pb_size / 188);
|
||||
}
|
||||
|
||||
|
||||
|
@ -504,12 +421,6 @@ pass_muxer_destroy(muxer_t *m)
|
|||
if(pm->pm_filename)
|
||||
free(pm->pm_filename);
|
||||
|
||||
if(pm->pm_pmt)
|
||||
free(pm->pm_pmt);
|
||||
|
||||
if(pm->pm_pat)
|
||||
free(pm->pm_pat);
|
||||
|
||||
free(pm);
|
||||
}
|
||||
|
||||
|
@ -518,7 +429,7 @@ pass_muxer_destroy(muxer_t *m)
|
|||
* Create a new passthrough muxer
|
||||
*/
|
||||
muxer_t*
|
||||
pass_muxer_create(muxer_container_type_t mc)
|
||||
pass_muxer_create(muxer_container_type_t mc, muxer_config_t *m_cfg)
|
||||
{
|
||||
pass_muxer_t *pm;
|
||||
|
||||
|
@ -536,7 +447,10 @@ pass_muxer_create(muxer_container_type_t mc)
|
|||
pm->m_close = pass_muxer_close;
|
||||
pm->m_destroy = pass_muxer_destroy;
|
||||
pm->pm_fd = -1;
|
||||
pm->pm_injection = (mc == MC_PASS);
|
||||
/* Copy any configuration values we are interested in */
|
||||
if ((mc == MC_PASS) && (m_cfg)) {
|
||||
pm->pm_rewrite_patpmt = m_cfg->rewrite_patpmt;
|
||||
}
|
||||
|
||||
return (muxer_t *)pm;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,6 @@
|
|||
|
||||
#include "muxer.h"
|
||||
|
||||
muxer_t* pass_muxer_create(muxer_container_type_t mc);
|
||||
muxer_t* pass_muxer_create(muxer_container_type_t mc, muxer_config_t* m_cfg);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -220,7 +220,7 @@ tvh_muxer_destroy(muxer_t *m)
|
|||
* Create a new builtin muxer
|
||||
*/
|
||||
muxer_t*
|
||||
tvh_muxer_create(muxer_container_type_t mc)
|
||||
tvh_muxer_create(muxer_container_type_t mc, muxer_config_t *m_cfg)
|
||||
{
|
||||
tvh_muxer_t *tm;
|
||||
|
||||
|
|
|
@ -21,6 +21,6 @@
|
|||
|
||||
#include "muxer.h"
|
||||
|
||||
muxer_t* tvh_muxer_create(muxer_container_type_t mc);
|
||||
muxer_t* tvh_muxer_create(muxer_container_type_t mc, muxer_config_t* m_cfg);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "htsp_server.h"
|
||||
#include "lang_codes.h"
|
||||
#include "descrambler.h"
|
||||
#include "input/mpegts.h"
|
||||
|
||||
static void service_data_timeout(void *aux);
|
||||
static void service_class_save(struct idnode *self);
|
||||
|
@ -846,6 +847,7 @@ service_restart(service_t *t, int had_components)
|
|||
streaming_start_t *
|
||||
service_build_stream_start(service_t *t)
|
||||
{
|
||||
extern const idclass_t mpegts_service_class;
|
||||
elementary_stream_t *st;
|
||||
int n = 0;
|
||||
streaming_start_t *ss;
|
||||
|
@ -881,6 +883,11 @@ service_build_stream_start(service_t *t)
|
|||
ss->ss_refcount = 1;
|
||||
ss->ss_pcr_pid = t->s_pcr_pid;
|
||||
ss->ss_pmt_pid = t->s_pmt_pid;
|
||||
if (idnode_is_instance(&t->s_id, &mpegts_service_class)) {
|
||||
mpegts_service_t *ts = (mpegts_service_t*)t;
|
||||
ss->ss_service_id = ts->s_dvb_service_id;
|
||||
fprintf(stderr,"Streaming service %d, PMT PID is %d\n",ss->ss_service_id,ss->ss_pmt_pid);
|
||||
}
|
||||
return ss;
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ typedef struct streaming_start {
|
|||
|
||||
uint16_t ss_pcr_pid;
|
||||
uint16_t ss_pmt_pid;
|
||||
uint16_t ss_service_id;
|
||||
|
||||
streaming_start_component_t ss_components[0];
|
||||
|
||||
|
|
|
@ -1084,6 +1084,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
|
|||
r = htsmsg_create_map();
|
||||
htsmsg_add_str(r, "storage", cfg->dvr_storage);
|
||||
htsmsg_add_str(r, "container", muxer_container_type2txt(cfg->dvr_mc));
|
||||
htsmsg_add_u32(r, "rewritePATPMT", !!(cfg->dvr_flags & DVR_REWRITE_PATPMT));
|
||||
if(cfg->dvr_postproc != NULL)
|
||||
htsmsg_add_str(r, "postproc", cfg->dvr_postproc);
|
||||
htsmsg_add_u32(r, "retention", cfg->dvr_retention_days);
|
||||
|
@ -1120,6 +1121,9 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
|
|||
if((s = http_arg_get(&hc->hc_req_args, "container")) != NULL)
|
||||
dvr_container_set(cfg,s);
|
||||
|
||||
if(http_arg_get(&hc->hc_req_args, "rewritePATPMT") != NULL)
|
||||
flags |= DVR_REWRITE_PATPMT;
|
||||
|
||||
if((s = http_arg_get(&hc->hc_req_args, "postproc")) != NULL)
|
||||
dvr_postproc_set(cfg,s);
|
||||
|
||||
|
|
|
@ -781,6 +781,9 @@ tvheadend.dvrsettings = function() {
|
|||
editable : false,
|
||||
width : 200,
|
||||
hiddenName : 'container'
|
||||
}), new Ext.form.Checkbox({
|
||||
fieldLabel : 'Rewrite PAT/PMT in passthrough mode',
|
||||
name : 'rewritePATPMT'
|
||||
}), new Ext.form.NumberField({
|
||||
allowNegative : false,
|
||||
allowDecimals : false,
|
||||
|
|
|
@ -220,7 +220,7 @@ page_static_file(http_connection_t *hc, const char *remain, void *opaque)
|
|||
static void
|
||||
http_stream_run(http_connection_t *hc, streaming_queue_t *sq,
|
||||
const char *name, muxer_container_type_t mc,
|
||||
th_subscription_t *s)
|
||||
th_subscription_t *s, muxer_config_t *mcfg)
|
||||
{
|
||||
streaming_message_t *sm;
|
||||
int run = 1;
|
||||
|
@ -232,7 +232,7 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq,
|
|||
int err = 0;
|
||||
socklen_t errlen = sizeof(err);
|
||||
|
||||
mux = muxer_create(mc);
|
||||
mux = muxer_create(mc, mcfg);
|
||||
if(muxer_open_stream(mux, hc->hc_fd))
|
||||
run = 0;
|
||||
|
||||
|
@ -663,12 +663,16 @@ http_stream_service(http_connection_t *hc, service_t *service, int weight)
|
|||
size_t qsize;
|
||||
const char *name;
|
||||
char addrbuf[50];
|
||||
muxer_config_t m_cfg;
|
||||
|
||||
cfg = dvr_config_find_by_name_default("");
|
||||
|
||||
/* Build muxer config - this takes the defaults from the default dvr config, which is a hack */
|
||||
mc = muxer_container_txt2type(http_arg_get(&hc->hc_req_args, "mux"));
|
||||
if(mc == MC_UNKNOWN) {
|
||||
cfg = dvr_config_find_by_name_default("");
|
||||
mc = cfg->dvr_mc;
|
||||
}
|
||||
m_cfg.rewrite_patpmt = !!(cfg->dvr_flags & DVR_REWRITE_PATPMT);
|
||||
|
||||
if ((str = http_arg_get(&hc->hc_req_args, "qsize")))
|
||||
qsize = atoll(str);
|
||||
|
@ -690,6 +694,7 @@ http_stream_service(http_connection_t *hc, service_t *service, int weight)
|
|||
}
|
||||
|
||||
tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrbuf, 50);
|
||||
|
||||
s = subscription_create_from_service(service, weight ?: 100, "HTTP", st, flags,
|
||||
addrbuf,
|
||||
hc->hc_username,
|
||||
|
@ -697,7 +702,7 @@ http_stream_service(http_connection_t *hc, service_t *service, int weight)
|
|||
if(s) {
|
||||
name = tvh_strdupa(service->s_nicename);
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
http_stream_run(hc, &sq, name, mc, s);
|
||||
http_stream_run(hc, &sq, name, mc, s, &m_cfg);
|
||||
pthread_mutex_lock(&global_lock);
|
||||
subscription_unsubscribe(s);
|
||||
}
|
||||
|
@ -739,7 +744,7 @@ http_stream_mux(http_connection_t *hc, mpegts_mux_t *mm, int weight)
|
|||
return HTTP_STATUS_BAD_REQUEST;
|
||||
name = tvh_strdupa(s->ths_title);
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
http_stream_run(hc, &sq, name, MC_RAW, s);
|
||||
http_stream_run(hc, &sq, name, MC_RAW, s, NULL);
|
||||
pthread_mutex_lock(&global_lock);
|
||||
subscription_unsubscribe(s);
|
||||
|
||||
|
@ -770,12 +775,16 @@ http_stream_channel(http_connection_t *hc, channel_t *ch, int weight)
|
|||
size_t qsize;
|
||||
const char *name;
|
||||
char addrbuf[50];
|
||||
muxer_config_t m_cfg;
|
||||
|
||||
cfg = dvr_config_find_by_name_default("");
|
||||
|
||||
/* Build muxer config - this takes the defaults from the default dvr config, which is a hack */
|
||||
mc = muxer_container_txt2type(http_arg_get(&hc->hc_req_args, "mux"));
|
||||
if(mc == MC_UNKNOWN) {
|
||||
cfg = dvr_config_find_by_name_default("");
|
||||
mc = cfg->dvr_mc;
|
||||
}
|
||||
m_cfg.rewrite_patpmt = !!(cfg->dvr_flags & DVR_REWRITE_PATPMT);
|
||||
|
||||
if ((str = http_arg_get(&hc->hc_req_args, "qsize")))
|
||||
qsize = atoll(str);
|
||||
|
@ -813,7 +822,7 @@ http_stream_channel(http_connection_t *hc, channel_t *ch, int weight)
|
|||
if(s) {
|
||||
name = tvh_strdupa(channel_get_name(ch));
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
http_stream_run(hc, &sq, name, mc, s);
|
||||
http_stream_run(hc, &sq, name, mc, s, &m_cfg);
|
||||
pthread_mutex_lock(&global_lock);
|
||||
subscription_unsubscribe(s);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue