diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 09d8d397..df043689 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -31,6 +31,7 @@ typedef struct dvr_config { char *dvr_storage; uint32_t dvr_retention_days; int dvr_flags; + int dvr_mux_flags; char *dvr_postproc; int dvr_extra_time_pre; int dvr_extra_time_post; @@ -68,7 +69,6 @@ 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, @@ -334,6 +334,8 @@ void dvr_retention_set(dvr_config_t *cfg, int days); void dvr_flags_set(dvr_config_t *cfg, int flags); +void dvr_mux_flags_set(dvr_config_t *cfg, int flags); + void dvr_extra_time_pre_set(dvr_config_t *cfg, int d); void dvr_extra_time_post_set(dvr_config_t *cfg, int d); diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index fc3eb2e0..54599054 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -1111,8 +1111,18 @@ 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; + if(!htsmsg_get_u32(m, "rewrite-pat", &u32)) { + if (u32) + cfg->dvr_mux_flags |= MUX_REWRITE_PAT; + else + cfg->dvr_mux_flags &= ~MUX_REWRITE_PAT; + } + if(!htsmsg_get_u32(m, "rewrite-pmt", &u32)) { + if (u32) + cfg->dvr_mux_flags |= MUX_REWRITE_PMT; + else + cfg->dvr_mux_flags &= ~MUX_REWRITE_PMT; + } htsmsg_get_s32(m, "pre-extra-time", &cfg->dvr_extra_time_pre); htsmsg_get_s32(m, "post-extra-time", &cfg->dvr_extra_time_post); @@ -1266,6 +1276,9 @@ dvr_config_create(const char *name) cfg->dvr_sl_more_recent = 1; // Only record more reason episodes cfg->dvr_sl_quality_lock = 1; // Don't attempt to ajust quality + /* PAT/PMT rewrite support */ + cfg->dvr_mux_flags |= MUX_REWRITE_PAT; + /* dup detect */ cfg->dvr_dup_detect_episode = 1; // detect dup episodes @@ -1310,7 +1323,8 @@ 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, "rewrite-pat", !!(cfg->dvr_mux_flags & MUX_REWRITE_PAT)); + htsmsg_add_u32(m, "rewrite-pmt", !!(cfg->dvr_mux_flags & MUX_REWRITE_PMT)); 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); @@ -1419,6 +1433,19 @@ dvr_flags_set(dvr_config_t *cfg, int flags) dvr_save(cfg); } +/** + * + */ +void +dvr_mux_flags_set(dvr_config_t *cfg, int flags) +{ + if(cfg->dvr_mux_flags == flags) + return; + + cfg->dvr_mux_flags = flags; + dvr_save(cfg); +} + /** * diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 7daf3218..0075a3b3 100644 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -292,7 +292,7 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss) muxer_config_t m_cfg; mc = de->de_mc; - m_cfg.rewrite_patpmt = !!(cfg->dvr_flags & DVR_REWRITE_PATPMT); + m_cfg.dvr_flags = cfg->dvr_mux_flags; de->de_mux = muxer_create(mc, &m_cfg); if(!de->de_mux) { diff --git a/src/muxer.h b/src/muxer.h index c01cb8eb..8215df69 100644 --- a/src/muxer.h +++ b/src/muxer.h @@ -21,6 +21,9 @@ #include "htsmsg.h" +#define MUX_REWRITE_PAT 0x0001 +#define MUX_REWRITE_PMT 0x0002 + typedef enum { MC_UNKNOWN = 0, MC_MATROSKA = 1, @@ -33,9 +36,7 @@ typedef enum { /* Muxer configuration used when creating a muxer. */ typedef struct muxer_config { - /* Options only for passthrough muxer */ - int rewrite_patpmt; - + int dvr_flags; } muxer_config_t; struct muxer; diff --git a/src/muxer/muxer_pass.c b/src/muxer/muxer_pass.c index d4a8c002..4a85dedf 100644 --- a/src/muxer/muxer_pass.c +++ b/src/muxer/muxer_pass.c @@ -41,19 +41,21 @@ typedef struct pass_muxer { char *pm_filename; /* TS muxing */ - uint8_t pm_rewrite_patpmt; + uint8_t pm_flags; 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; -static inline void set_pid_bit(uint32_t* pm_streams, uint16_t pid) +static inline void +set_pid_bit(uint32_t* pm_streams, uint16_t pid) { pm_streams[pid >> 5] |= 1 << (pid & 31); } -static inline int check_pid_bit(uint32_t* pm_streams, uint16_t pid) +static inline int +check_pid_bit(uint32_t* pm_streams, uint16_t pid) { return pm_streams[pid >> 5] & (1 << (pid & 31)); } @@ -67,115 +69,126 @@ static inline int check_pid_bit(uint32_t* pm_streams, uint16_t pid) * */ -static int pass_muxer_rewrite_pat(pass_muxer_t* pm, unsigned char* buf) +static int +pass_muxer_rewrite_pat(pass_muxer_t* pm, unsigned char* tsb) { - int pusi = buf[1] & 0x40; + uint32_t crc32; + int pusi = tsb[1] & 0x40; - if (pusi) { - /* First TS packet, generate our new PAT */ - - /* 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; - } - - int current_next_indicator = (buf[10] & 0x1); - if (!current_next_indicator) { - /* If next version of PAT, do nothing */ - return 0; - } - - /* 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; - - 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); + /* NULL packet */ + if (!pusi) { + printf("NULL PAT\n"); + tsb[1] = 0x1f; + memset(tsb+2, 0xff, 186); + return 0; } + /* Ignore Next (TODO: should we wipe it?) */ + if (!(tsb[10] & 0x1)) { + printf("NEXT PAT\n"); + return 0; + } + + /* Some sanity checks */ + if (tsb[4]) { + tvherror("pass", "Unsupported PAT format - pointer_to_data %d", tsb[4]); + return 1; + } + if (tsb[12]) { + tvherror("pass", "Multi-section PAT not supported"); + return 2; + } + + /* Rewrite continuity counter, in case this is a multi-packet PAT (we discard all but the first packet) */ + tsb[3] = (tsb[3] & 0xf0) | pm->pm_pat_cc; + pm->pm_pat_cc = (pm->pm_pat_cc + 1) & 0xf; + + tsb[6] = 0; + tsb[7] = 13; /* section_length (number of bytes after this field, including CRC) */ + + tsb[13] = (pm->pm_service_id & 0xff00) >> 8; + tsb[14] = pm->pm_service_id & 0x00ff; + tsb[15] = 0xe0 | ((pm->pm_pmt_pid & 0x1f00) >> 8); + tsb[16] = pm->pm_pmt_pid & 0x00ff; + + crc32 = tvh_crc32(tsb+5, 12, 0xffffffff); + tsb[17] = (crc32 & 0xff000000) >> 24; + tsb[18] = (crc32 & 0x00ff0000) >> 16; + tsb[19] = (crc32 & 0x0000ff00) >> 8; + tsb[20] = crc32 & 0x000000ff; + + printf("rewrite PAT\n"); + + memset(tsb + 21, 0xff, 167); /* Wipe rest of packet */ + return 0; } -static int pass_muxer_rewrite_pmt(pass_muxer_t* pm, unsigned char* buf) +static int +pass_muxer_rewrite_pmt(pass_muxer_t* pm, unsigned char* tsb) { - int i; + int i, prog_len; + uint32_t crc32; + int pusi = tsb[1] & 0x40; + int sect_len = ((tsb[6] & 0x0f) << 8) | tsb[7]; + tvhlog_hexdump("pass", tsb, 188); - 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!)",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!)",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 (section_length = %d) (Please report to developers!)",section_length); - return 3; - } - - 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); + /* NULL packet */ + if (!pusi) { + printf("NULL PMT\n"); + tsb[1] = 0x1f; + memset(tsb+2, 0xff, 186); + return 0; } + + /* Ignore Next (TODO: should we wipe it?) */ + if (!(tsb[10] & 0x1)) { + printf("NEXT PMT\n"); + return 0; + } + + /* Some sanity checks */ + if (tsb[4]) { + tvherror("pass", "Unsupported PMT format - pointer_to_data %d", tsb[4]); + return 1; + } + if (tsb[12]) { + tvherror("pass", "Multi-section PMT not supported"); + return 2; + } + if (sect_len > (188 - 7 - 4)) { + tvherror("pass", "Multi-packet PMT not supported - sect_len %d", sect_len); + return 3; + } + + prog_len = ((tsb[15] & 0x0f) << 8) | tsb[16]; + i = 17 + prog_len; + + while (i < (7 + sect_len - 4)) { + //int stream_type = tsb[i]; + int pid = ((tsb[i+1] & 0x1f) << 8) | tsb[i+2]; + int es_len = ((tsb[i+3] & 0x0f) << 8) | tsb[i+4]; + + /* Keep */ + if (check_pid_bit(pm->pm_streams, pid)) { + i += 5 + es_len; + + /* Drop */ + } else { + memmove(tsb + i, tsb + i + 5 + es_len, (188 - i - 5 - es_len)); + sect_len -= (5 + es_len); + } + } + + printf("rewrite PMT, orig len %d new len %d i %d", tsb[7], sect_len, i); + + tsb[7] = sect_len; + + crc32 = tvh_crc32(tsb+5, i-5, 0xffffffff); + tsb[i++] = (crc32 >> 24) & 0xff; + tsb[i++] = (crc32 >> 16) & 0xff; + tsb[i++] = (crc32 >> 8) & 0xff; + tsb[i++] = (crc32 >> 0) & 0xff; return 0; } @@ -233,7 +246,7 @@ pass_muxer_reconfigure(muxer_t* m, const struct streaming_start *ss) pm->pm_service_id = ss->ss_service_id; /* Store the PIDs of all the components */ - if (pm->pm_rewrite_patpmt) { + if (pm->pm_flags & MUX_REWRITE_PMT) { int i; memset(pm->pm_streams,0,1024); for (i=0;iss_num_components;i++) { @@ -325,29 +338,33 @@ static void pass_muxer_write_ts(muxer_t *m, pktbuf_t *pb) { pass_muxer_t *pm = (pass_muxer_t*)m; - unsigned char* p; + unsigned char* tsb; + + // Note: this code currently assumes PAT/PMT will never span 1 packet - 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]; + /* Rewrite PAT/PMT in operation */ + if (pm->pm_flags & (MUX_REWRITE_PAT | MUX_REWRITE_PMT)) { - if (pid == 0) { - /* Remove all PMT references except the one being streamed */ - if (pass_muxer_rewrite_pat(pm, p)) { - tvhlog(LOG_ERR, "pass", "Disabling PAT/PMT rewriting"); - pm->pm_rewrite_patpmt = 0; /* There was an error, so just give user original PAT from now on */ - break; + tsb = pb->pb_data; + while (tsb < pb->pb_data + pb->pb_size) { + int pid = (tsb[1] & 0x1f) << 8 | tsb[2]; + + /* PAT */ + if (pm->pm_flags & MUX_REWRITE_PAT && pid == 0) { + if (pass_muxer_rewrite_pat(pm, tsb)) { + tvherror("pass", "PAT rewrite failed, disabling"); + pm->pm_flags &= ~MUX_REWRITE_PAT; } - } 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", "Disabling PAT/PMT rewriting"); - pm->pm_rewrite_patpmt = 0; /* There was an error, so just give user original PMT from now on */ - break; + + /* PMT */ + } else if (pm->pm_flags & MUX_REWRITE_PMT && pid == pm->pm_pmt_pid) { + if (pass_muxer_rewrite_pmt(pm, tsb)) { + tvherror("pass", "PMT rewrite failed, disabling"); + pm->pm_flags &= ~MUX_REWRITE_PMT; } } - p += 188; + + tsb += 188; } } @@ -449,9 +466,8 @@ pass_muxer_create(muxer_container_type_t mc, muxer_config_t *m_cfg) pm->m_destroy = pass_muxer_destroy; pm->pm_fd = -1; /* Copy any configuration values we are interested in */ - if ((mc == MC_PASS) && (m_cfg)) { - pm->pm_rewrite_patpmt = m_cfg->rewrite_patpmt; - } + if ((mc == MC_PASS) && (m_cfg)) + pm->pm_flags = m_cfg->dvr_flags; return (muxer_t *)pm; } diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 873b0d5f..f0fbb5ca 100755 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -1084,7 +1084,8 @@ 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)); + htsmsg_add_u32(r, "rewritePAT", !!(cfg->dvr_mux_flags & MUX_REWRITE_PAT)); + htsmsg_add_u32(r, "rewritePMT", !!(cfg->dvr_mux_flags & MUX_REWRITE_PMT)); if(cfg->dvr_postproc != NULL) htsmsg_add_str(r, "postproc", cfg->dvr_postproc); htsmsg_add_u32(r, "retention", cfg->dvr_retention_days); @@ -1121,9 +1122,6 @@ 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); @@ -1166,6 +1164,15 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque) dvr_flags_set(cfg,flags); + /* Muxer flags */ + flags = 0; + if(http_arg_get(&hc->hc_req_args, "rewritePAT") != NULL) + flags |= MUX_REWRITE_PAT; + if(http_arg_get(&hc->hc_req_args, "rewritePMT") != NULL) + flags |= MUX_REWRITE_PMT; + + dvr_mux_flags_set(cfg, flags); + out = htsmsg_create_map(); htsmsg_add_u32(out, "success", 1); diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js index 8bdef464..c0131213 100644 --- a/src/webui/static/app/dvr.js +++ b/src/webui/static/app/dvr.js @@ -735,7 +735,7 @@ tvheadend.dvrsettings = function() { }, [ 'storage', 'postproc', 'retention', 'dayDirs', 'channelDirs', 'channelInTitle', 'container', 'dateInTitle', 'timeInTitle', 'preExtraTime', 'postExtraTime', 'whitespaceInTitle', 'titleDirs', - 'episodeInTitle', 'cleanTitle', 'tagFiles', 'commSkip', 'subtitleInTitle', 'episodeBeforeDate']); + 'episodeInTitle', 'cleanTitle', 'tagFiles', 'commSkip', 'subtitleInTitle', 'episodeBeforeDate', 'rewritePAT', 'rewritePMT' ]); var confcombo = new Ext.form.ComboBox({ store : tvheadend.configNames, @@ -782,8 +782,11 @@ tvheadend.dvrsettings = function() { width : 200, hiddenName : 'container' }), new Ext.form.Checkbox({ - fieldLabel : 'Rewrite PAT/PMT in passthrough mode', - name : 'rewritePATPMT' + fieldLabel : 'Rewrite PAT in passthrough mode', + name : 'rewritePAT' + }), new Ext.form.Checkbox({ + fieldLabel : 'Rewrite PMT in passthrough mode', + name : 'rewritePMT' }), new Ext.form.NumberField({ allowNegative : false, allowDecimals : false, diff --git a/src/webui/webui.c b/src/webui/webui.c index e0bac462..aaff1041 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -672,7 +672,7 @@ http_stream_service(http_connection_t *hc, service_t *service, int weight) if(mc == MC_UNKNOWN) { mc = cfg->dvr_mc; } - m_cfg.rewrite_patpmt = !!(cfg->dvr_flags & DVR_REWRITE_PATPMT); + m_cfg.dvr_flags = cfg->dvr_mux_flags; if ((str = http_arg_get(&hc->hc_req_args, "qsize"))) qsize = atoll(str); @@ -784,7 +784,7 @@ http_stream_channel(http_connection_t *hc, channel_t *ch, int weight) if(mc == MC_UNKNOWN) { mc = cfg->dvr_mc; } - m_cfg.rewrite_patpmt = !!(cfg->dvr_flags & DVR_REWRITE_PATPMT); + m_cfg.dvr_flags = cfg->dvr_mux_flags; if ((str = http_arg_get(&hc->hc_req_args, "qsize"))) qsize = atoll(str);