diff --git a/debian/changelog b/debian/changelog index 4589804e..5611667f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -46,6 +46,13 @@ hts-tvheadend (2.3) hts; urgency=low * Remove configuration and settings (/home/hts/.hts/tvheadend) on a deb package purge operation. Ticket #73 + * If the Program Stream Information changes during a subscription, + react and send a subscriptionStop + subscriptionStart. + This happens on SVT (in sweden) when the transmission switches + from local to nationwide broadcast (AC3 audio is only present + in nationwide broadcast) + Ticket #78 + hts-tvheadend (2.2) hts; urgency=low * Set $HOME so forked processes (XMLTV) will have correct environment diff --git a/src/dvb/dvb_transport.c b/src/dvb/dvb_transport.c index a5b72fb3..96b34679 100644 --- a/src/dvb/dvb_transport.c +++ b/src/dvb/dvb_transport.c @@ -45,8 +45,53 @@ #include "dvb_support.h" #include "notify.h" +/** + * + */ +static void +dvb_transport_open_demuxers(th_dvb_adapter_t *tda, th_transport_t *t) +{ + struct dmx_pes_filter_params dmx_param; + int fd; + th_stream_t *st; -/* + LIST_FOREACH(st, &t->tht_components, st_link) { + if(st->st_demuxer_fd != -1) + continue; + + fd = open(tda->tda_demux_path, O_RDWR); + st->st_cc_valid = 0; + + if(fd == -1) { + st->st_demuxer_fd = -1; + tvhlog(LOG_ERR, "dvb", + "\"%s\" unable to open demuxer \"%s\" for pid %d -- %s", + t->tht_name, tda->tda_demux_path, st->st_pid, strerror(errno)); + continue; + } + + memset(&dmx_param, 0, sizeof(dmx_param)); + dmx_param.pid = st->st_pid; + dmx_param.input = DMX_IN_FRONTEND; + dmx_param.output = DMX_OUT_TS_TAP; + dmx_param.pes_type = DMX_PES_OTHER; + dmx_param.flags = DMX_IMMEDIATE_START; + + if(ioctl(fd, DMX_SET_PES_FILTER, &dmx_param)) { + tvhlog(LOG_ERR, "dvb", + "\"%s\" unable to configure demuxer \"%s\" for pid %d -- %s", + t->tht_name, tda->tda_demux_path, st->st_pid, strerror(errno)); + close(fd); + fd = -1; + } + + st->st_demuxer_fd = fd; + } +} + + + +/** * Switch the adapter (which is implicitly tied to our transport) * to receive the given transport. * @@ -57,9 +102,7 @@ static int dvb_transport_start(th_transport_t *t, unsigned int weight, int status, int force_start) { - struct dmx_pes_filter_params dmx_param; - th_stream_t *st; - int w, fd, pid; + int w; th_dvb_adapter_t *tda = t->tht_dvb_mux_instance->tdmi_adapter; th_dvb_mux_instance_t *tdmi = tda->tda_mux_current; @@ -81,50 +124,16 @@ dvb_transport_start(th_transport_t *t, unsigned int weight, int status, dvb_adapter_clean(tda); } - tdmi = t->tht_dvb_mux_instance; - LIST_FOREACH(st, &t->tht_components, st_link) { - fd = open(tda->tda_demux_path, O_RDWR); - - pid = st->st_pid; - st->st_cc_valid = 0; - - if(fd == -1) { - st->st_demuxer_fd = -1; - tvhlog(LOG_ERR, "dvb", - "\"%s\" unable to open demuxer \"%s\" for pid %d -- %s", - t->tht_name, tda->tda_demux_path, pid, strerror(errno)); - continue; - } - - memset(&dmx_param, 0, sizeof(dmx_param)); - dmx_param.pid = pid; - dmx_param.input = DMX_IN_FRONTEND; - dmx_param.output = DMX_OUT_TS_TAP; - dmx_param.pes_type = DMX_PES_OTHER; - dmx_param.flags = DMX_IMMEDIATE_START; - - if(ioctl(fd, DMX_SET_PES_FILTER, &dmx_param)) { - tvhlog(LOG_ERR, "dvb", - "\"%s\" unable to configure demuxer \"%s\" for pid %d -- %s", - t->tht_name, tda->tda_demux_path, pid, strerror(errno)); - close(fd); - fd = -1; - } - - st->st_demuxer_fd = fd; - } + dvb_transport_open_demuxers(tda, t); pthread_mutex_lock(&tda->tda_delivery_mutex); LIST_INSERT_HEAD(&tda->tda_transports, t, tht_active_link); t->tht_status = status; - - - dvb_fe_tune(tdmi, "Transport start"); + dvb_fe_tune(t->tht_dvb_mux_instance, "Transport start"); pthread_mutex_unlock(&tda->tda_delivery_mutex); - return 0; } @@ -145,13 +154,28 @@ dvb_transport_stop(th_transport_t *t) pthread_mutex_unlock(&tda->tda_delivery_mutex); LIST_FOREACH(st, &t->tht_components, st_link) { - close(st->st_demuxer_fd); - st->st_demuxer_fd = -1; + if(st->st_demuxer_fd != -1) { + close(st->st_demuxer_fd); + st->st_demuxer_fd = -1; + } } t->tht_status = TRANSPORT_IDLE; } +/** + * + */ +static void +dvb_transport_refresh(th_transport_t *t) +{ + th_dvb_adapter_t *tda = t->tht_dvb_mux_instance->tdmi_adapter; + + lock_assert(&global_lock); + dvb_transport_open_demuxers(tda, t); +} + + /** * Load config for the given mux @@ -330,6 +354,7 @@ dvb_transport_find(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid, t->tht_pmt_pid = pmt_pid; t->tht_start_feed = dvb_transport_start; + t->tht_refresh_feed = dvb_transport_refresh; t->tht_stop_feed = dvb_transport_stop; t->tht_config_change = dvb_transport_save; t->tht_sourcename = dvb_transport_sourcename; diff --git a/src/psi.c b/src/psi.c index 7093bc43..9fdf3115 100644 --- a/src/psi.c +++ b/src/psi.c @@ -181,6 +181,8 @@ psi_desc_ca(th_transport_t *t, uint8_t *ptr) r = 1; } + st->st_delete_me = 0; + if(st->st_caid != caid) { st->st_caid = caid; r = 1; @@ -200,10 +202,10 @@ psi_parse_pmt(th_transport_t *t, uint8_t *ptr, int len, int chksvcid) uint8_t dtag, dlen; uint16_t sid; streaming_component_type_t hts_stream_type; - th_stream_t *st; + th_stream_t *st, *next; char lang[4]; int frameduration; - int need_save = 0; + int update = 0; if(len < 9) return -1; @@ -225,12 +227,16 @@ psi_parse_pmt(th_transport_t *t, uint8_t *ptr, int len, int chksvcid) if(t->tht_pcr_pid != pcr_pid) { t->tht_pcr_pid = pcr_pid; - need_save = 1; + update = 1; } ptr += 9; len -= 9; + /* Mark all streams for deletion */ + LIST_FOREACH(st, &t->tht_components, st_link) + st->st_delete_me = 1; + while(dllen > 1) { dtag = ptr[0]; dlen = ptr[1]; @@ -241,7 +247,7 @@ psi_parse_pmt(th_transport_t *t, uint8_t *ptr, int len, int chksvcid) switch(dtag) { case DVB_DESC_CA: - need_save |= psi_desc_ca(t, ptr); + update |= psi_desc_ca(t, ptr); break; default: @@ -294,7 +300,7 @@ psi_parse_pmt(th_transport_t *t, uint8_t *ptr, int len, int chksvcid) switch(dtag) { case DVB_DESC_CA: - need_save |= psi_desc_ca(t, ptr); + update |= psi_desc_ca(t, ptr); break; case DVB_DESC_VIDEO_STREAM: @@ -329,27 +335,40 @@ psi_parse_pmt(th_transport_t *t, uint8_t *ptr, int len, int chksvcid) if(hts_stream_type != 0) { if((st = transport_stream_find(t, pid)) == NULL) { - need_save = 1; + update = 1; st = transport_stream_create(t, pid, hts_stream_type); } + st->st_delete_me = 0; + st->st_tb = (AVRational){1, 90000}; if(memcmp(st->st_lang, lang, 4)) { - need_save = 1; + update = 1; memcpy(st->st_lang, lang, 4); } if(st->st_frame_duration == 0 && frameduration != 0) { st->st_frame_duration = frameduration; - need_save = 1; + update = 1; } } } - if(need_save) - t->tht_config_change(t); + /* Scan again to see if any streams should be deleted */ + for(st = LIST_FIRST(&t->tht_components); st != NULL; st = next) { + next = LIST_NEXT(st, st_link); + if(st->st_delete_me) { + transport_stream_destroy(t, st); + update = 1; + } + } + if(update) { + t->tht_config_change(t); + if(t->tht_status == TRANSPORT_RUNNING) + transport_restart(t); + } return 0; } diff --git a/src/transports.c b/src/transports.c index 8fb0d9f5..cee5b375 100644 --- a/src/transports.c +++ b/src/transports.c @@ -53,6 +53,121 @@ static struct th_transport_list transporthash[TRANSPORT_HASH_WIDTH]; static void transport_data_timeout(void *aux); +/** + * + */ +static void +stream_init(th_stream_t *st) +{ + AVCodec *c; + enum CodecID id; + + st->st_startcond = 0xffffffff; + st->st_curdts = AV_NOPTS_VALUE; + st->st_curpts = AV_NOPTS_VALUE; + st->st_prevdts = AV_NOPTS_VALUE; + + st->st_last_dts = AV_NOPTS_VALUE; + st->st_dts_epoch = 0; + + st->st_pcr_real_last = AV_NOPTS_VALUE; + st->st_pcr_last = AV_NOPTS_VALUE; + st->st_pcr_drift = 0; + st->st_pcr_recovery_fails = 0; + /* Open ffmpeg context and parser */ + + switch(st->st_type) { + case SCT_MPEG2VIDEO: id = CODEC_ID_MPEG2VIDEO; break; + case SCT_MPEG2AUDIO: id = CODEC_ID_MP3; break; + case SCT_H264: id = CODEC_ID_H264; break; + case SCT_AC3: id = CODEC_ID_AC3; break; + default: id = CODEC_ID_NONE; break; + } + + assert(st->st_ctx == NULL); + assert(st->st_parser == NULL); + + + if(id == CODEC_ID_NONE) + return; + c = avcodec_find_decoder(id); + if(c != NULL) { + st->st_ctx = avcodec_alloc_context(); + pthread_mutex_lock(&ffmpeg_lock); + avcodec_open(st->st_ctx, c); + pthread_mutex_unlock(&ffmpeg_lock); + st->st_parser = av_parser_init(id); + } else { + abort(); + } +} + + +/** + * + */ +static void +stream_clean(th_stream_t *st) +{ + if(st->st_demuxer_fd != -1) { + // XXX: Should be in DVB-code perhaps + close(st->st_demuxer_fd); + st->st_demuxer_fd = -1; + } + + if(st->st_parser != NULL) + av_parser_close(st->st_parser); + + if(st->st_ctx != NULL) { + pthread_mutex_lock(&ffmpeg_lock); + avcodec_close(st->st_ctx); + pthread_mutex_unlock(&ffmpeg_lock); + } + + av_free(st->st_ctx); + + st->st_parser = NULL; + st->st_ctx = NULL; + + free(st->st_priv); + st->st_priv = NULL; + + /* Clear reassembly buffer */ + + free(st->st_buffer); + st->st_buffer = NULL; + st->st_buffer_size = 0; + st->st_buffer_ptr = 0; + st->st_startcode = 0; + + if(st->st_curpkt != NULL) { + pkt_ref_dec(st->st_curpkt); + st->st_curpkt = NULL; + } + + /* Clear PTS queue */ + + pktref_clear_queue(&st->st_ptsq); + st->st_ptsq_len = 0; + + /* Clear durationq */ + + pktref_clear_queue(&st->st_durationq); +} + + +/** + * + */ +void +transport_stream_destroy(th_transport_t *t, th_stream_t *st) +{ + if(t->tht_status == TRANSPORT_RUNNING) + stream_clean(st); + LIST_REMOVE(st, st_link); + free(st); +} + /** * Transport lock must be held */ @@ -79,47 +194,8 @@ transport_stop(th_transport_t *t) /** * Clean up each stream */ - LIST_FOREACH(st, &t->tht_components, st_link) { - - if(st->st_parser != NULL) - av_parser_close(st->st_parser); - - if(st->st_ctx != NULL) { - pthread_mutex_lock(&ffmpeg_lock); - avcodec_close(st->st_ctx); - pthread_mutex_unlock(&ffmpeg_lock); - } - - av_free(st->st_ctx); - - st->st_parser = NULL; - st->st_ctx = NULL; - - free(st->st_priv); - st->st_priv = NULL; - - /* Clear reassembly buffer */ - - free(st->st_buffer); - st->st_buffer = NULL; - st->st_buffer_size = 0; - st->st_buffer_ptr = 0; - st->st_startcode = 0; - - if(st->st_curpkt != NULL) { - pkt_ref_dec(st->st_curpkt); - st->st_curpkt = NULL; - } - - /* Clear PTS queue */ - - pktref_clear_queue(&st->st_ptsq); - st->st_ptsq_len = 0; - - /* Clear durationq */ - - pktref_clear_queue(&st->st_durationq); - } + LIST_FOREACH(st, &t->tht_components, st_link) + stream_clean(st); pthread_mutex_unlock(&t->tht_stream_mutex); } @@ -157,8 +233,6 @@ int transport_start(th_transport_t *t, unsigned int weight, int force_start) { th_stream_t *st; - AVCodec *c; - enum CodecID id; lock_assert(&global_lock); @@ -173,46 +247,8 @@ transport_start(th_transport_t *t, unsigned int weight, int force_start) /** * Initialize stream */ - LIST_FOREACH(st, &t->tht_components, st_link) { - st->st_startcond = 0xffffffff; - st->st_curdts = AV_NOPTS_VALUE; - st->st_curpts = AV_NOPTS_VALUE; - st->st_prevdts = AV_NOPTS_VALUE; - - st->st_last_dts = AV_NOPTS_VALUE; - st->st_dts_epoch = 0; - - st->st_pcr_real_last = AV_NOPTS_VALUE; - st->st_pcr_last = AV_NOPTS_VALUE; - st->st_pcr_drift = 0; - st->st_pcr_recovery_fails = 0; - /* Open ffmpeg context and parser */ - - switch(st->st_type) { - case SCT_MPEG2VIDEO: id = CODEC_ID_MPEG2VIDEO; break; - case SCT_MPEG2AUDIO: id = CODEC_ID_MP3; break; - case SCT_H264: id = CODEC_ID_H264; break; - case SCT_AC3: id = CODEC_ID_AC3; break; - default: id = CODEC_ID_NONE; break; - } - - assert(st->st_ctx == NULL); - assert(st->st_parser == NULL); - - - if(id != CODEC_ID_NONE) { - c = avcodec_find_decoder(id); - if(c != NULL) { - st->st_ctx = avcodec_alloc_context(); - pthread_mutex_lock(&ffmpeg_lock); - avcodec_open(st->st_ctx, c); - pthread_mutex_unlock(&ffmpeg_lock); - st->st_parser = av_parser_init(id); - } else { - abort(); - } - } - } + LIST_FOREACH(st, &t->tht_components, st_link) + stream_init(st); cwc_transport_start(t); @@ -478,10 +514,12 @@ transport_stream_create(th_transport_t *t, int pid, { th_stream_t *st; int i = 0; - + int idx = 0; lock_assert(&t->tht_stream_mutex); LIST_FOREACH(st, &t->tht_components, st_link) { + if(st->st_index > idx) + idx = st->st_index; i++; if(pid != -1 && st->st_pid == pid) return st; @@ -492,7 +530,7 @@ transport_stream_create(th_transport_t *t, int pid, t->tht_identifier, streaming_component_type2txt(type), pid); st = calloc(1, sizeof(th_stream_t)); - st->st_index = i; + st->st_index = idx + 1; st->st_type = type; LIST_INSERT_HEAD(&t->tht_components, st, st_link); @@ -505,6 +543,9 @@ transport_stream_create(th_transport_t *t, int pid, avgstat_init(&st->st_rate, 10); avgstat_init(&st->st_cc_errors, 10); + if(t->tht_status == TRANSPORT_RUNNING) + stream_init(st); + return st; } @@ -627,6 +668,29 @@ transport_set_feed_status(th_transport_t *t, transport_feed_status_t newstatus) newstatus)); } + +/** + * Restart output on a transport. + * Happens if the stream composition changes. + * (i.e. an AC3 stream disappears, etc) + */ +void +transport_restart(th_transport_t *t) +{ + streaming_message_t *sm; + lock_assert(&t->tht_stream_mutex); + + sm = streaming_msg_create_msg(SMT_STOP, htsmsg_create_map()); + streaming_pad_deliver(&t->tht_streaming_pad, sm); + + t->tht_refresh_feed(t); + + sm = streaming_msg_create_msg(SMT_START, + transport_build_stream_start_msg(t)); + streaming_pad_deliver(&t->tht_streaming_pad, sm); +} + + /** * Generate a message containing info about all components * diff --git a/src/transports.h b/src/transports.h index 397f702a..e9d07312 100644 --- a/src/transports.h +++ b/src/transports.h @@ -78,4 +78,8 @@ htsmsg_t *transport_build_stream_start_msg(th_transport_t *t); void transport_set_enable(th_transport_t *t, int enabled); +void transport_restart(th_transport_t *t); + +void transport_stream_destroy(th_transport_t *t, th_stream_t *st); + #endif /* TRANSPORTS_H */ diff --git a/src/tvhead.h b/src/tvhead.h index aac00edb..86fc4331 100644 --- a/src/tvhead.h +++ b/src/tvhead.h @@ -312,6 +312,10 @@ typedef struct th_stream { int st_vbv_size; /* Video buffer size (in bytes) */ int st_vbv_delay; /* -1 if CBR */ + /* */ + + int st_delete_me; /* Temporary flag for deleting streams */ + } th_stream_t; @@ -438,6 +442,8 @@ typedef struct th_transport { int (*tht_start_feed)(struct th_transport *t, unsigned int weight, int status, int force_start); + void (*tht_refresh_feed)(struct th_transport *t); + void (*tht_stop_feed)(struct th_transport *t); void (*tht_config_change)(struct th_transport *t);