* 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
This commit is contained in:
Andreas Öman 2009-07-04 09:31:59 +00:00
parent f2538a4c46
commit b37177433c
6 changed files with 262 additions and 137 deletions

7
debian/changelog vendored
View file

@ -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

View file

@ -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;

View file

@ -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;
}

View file

@ -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
*

View file

@ -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 */

View file

@ -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);