Add DVB subtitle support. Ticket #39

This commit is contained in:
Andreas Öman 2009-11-30 22:33:19 +00:00
parent b0399bdeea
commit 3ee73a5b81
8 changed files with 147 additions and 14 deletions

4
debian/changelog vendored
View file

@ -1,3 +1,7 @@
hts-tvheadend (2.7) hts; urgency=low
* Added support for DVB subtitles. Currently only forwarded over HTSP
hts-tvheadend (2.6) hts; urgency=low
* Tvheadend's DVB service probe will now make the channel join tags based

View file

@ -1418,6 +1418,12 @@ htsp_subscription_start(htsp_subscription_t *hs, const streaming_start_t *ss)
htsmsg_add_str(c, "type", streaming_component_type2txt(ssc->ssc_type));
if(ssc->ssc_lang[0])
htsmsg_add_str(c, "language", ssc->ssc_lang);
if(ssc->ssc_type == SCT_SUBTITLES) {
htsmsg_add_u32(c, "composition_id", ssc->ssc_composition_id);
htsmsg_add_u32(c, "ancillary_id", ssc->ssc_ancillary_id);
}
htsmsg_add_msg(streams, NULL, c);
}

View file

@ -92,6 +92,9 @@ static void parse_audio(th_transport_t *t, th_stream_t *st, uint8_t *data,
static void parse_aac(th_transport_t *t, th_stream_t *st, uint8_t *data,
int len, int start);
static void parse_subtitles(th_transport_t *t, th_stream_t *st, uint8_t *data,
int len, int start);
static void parse_mpegaudio(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt);
static void parse_ac3(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt);
@ -99,7 +102,8 @@ static void parse_ac3(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt);
static void parse_compute_pts(th_transport_t *t, th_stream_t *st,
th_pkt_t *pkt);
static void parser_deliver(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt);
static void parser_deliver(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt,
int checkts);
static int parse_pes_header(th_transport_t *t, th_stream_t *st, uint8_t *buf,
size_t len);
@ -130,6 +134,10 @@ parse_mpeg_ts(th_transport_t *t, th_stream_t *st, uint8_t *data,
case SCT_AC3:
parse_audio(t, st, data, len, start, parse_ac3);
break;
case SCT_SUBTITLES:
parse_subtitles(t, st, data, len, start);
break;
case SCT_AAC:
if(0) // not yet
@ -441,7 +449,7 @@ parse_mpegaudio(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt)
pkt->pkt_duration = duration;
st->st_nextdts = pkt->pkt_dts + duration;
parser_deliver(t, st, pkt);
parser_deliver(t, st, pkt, 1);
}
@ -517,7 +525,7 @@ parse_ac3(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt)
pkt->pkt_duration = duration;
st->st_nextdts = pkt->pkt_dts + duration;
parser_deliver(t, st, pkt);
parser_deliver(t, st, pkt, 1);
}
@ -833,7 +841,7 @@ parse_h264(th_transport_t *t, th_stream_t *st, size_t len,
st->st_curpkt->pkt_payload = st->st_buffer;
st->st_curpkt->pkt_payloadlen = st->st_buffer_ptr;
parser_deliver(t, st, st->st_curpkt);
parser_deliver(t, st, st->st_curpkt, 1);
st->st_curpkt = NULL;
st->st_buffer = malloc(st->st_buffer_size);
@ -842,6 +850,68 @@ parse_h264(th_transport_t *t, th_stream_t *st, size_t len,
return 0;
}
/**
* http://broadcasting.ru/pdf-standard-specifications/subtitling/dvb-sub/en300743.v1.2.1.pdf
*/
static void
parse_subtitles(th_transport_t *t, th_stream_t *st, uint8_t *data,
int len, int start)
{
th_pkt_t *pkt;
int psize, hlen;
uint8_t *buf;
if(start) {
/* Payload unit start */
st->st_parser_state = 1;
st->st_buffer_ptr = 0;
}
if(st->st_parser_state == 0)
return;
if(st->st_buffer == NULL) {
st->st_buffer_size = 4000;
st->st_buffer = malloc(st->st_buffer_size);
}
if(st->st_buffer_ptr + len >= st->st_buffer_size) {
st->st_buffer_size += len * 4;
st->st_buffer = realloc(st->st_buffer, st->st_buffer_size);
}
memcpy(st->st_buffer + st->st_buffer_ptr, data, len);
st->st_buffer_ptr += len;
if(st->st_buffer_ptr < 6)
return;
psize = st->st_buffer[4] << 8 | st->st_buffer[5];
if(st->st_buffer_ptr != psize + 6)
return;
st->st_parser_state = 0;
hlen = parse_pes_header(t, st, st->st_buffer + 6, st->st_buffer_ptr - 6);
if(hlen < 0)
return;
psize -= 6 + hlen;
buf = st->st_buffer + 6 + hlen;
if(psize < 2 || buf[0] != 0x20 || buf[1] != 0x00)
return;
psize -= 2;
buf += 2;
if(psize >= 6) {
pkt = pkt_alloc(buf, psize, st->st_curpts, st->st_curdts);
pkt->pkt_commercial = t->tht_tt_commercial_advice;
parser_deliver(t, st, pkt, 0);
}
}
/**
@ -860,7 +930,7 @@ parse_compute_pts(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt)
/* PTS known and no other packets in queue, deliver at once */
if(validpts && pkt->pkt_duration)
return parser_deliver(t, st, pkt);
return parser_deliver(t, st, pkt, 1);
/* Reference count is transfered to queue */
@ -910,7 +980,7 @@ parse_compute_pts(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt)
if(pkt->pkt_duration == 0) {
parser_compute_duration(t, st, pr);
} else {
parser_deliver(t, st, pkt);
parser_deliver(t, st, pkt, 1);
free(pr);
}
}
@ -938,7 +1008,7 @@ parser_compute_duration(th_transport_t *t, th_stream_t *st, th_pktref_t *pr)
pkt_ref_dec(pr->pr_pkt);
} else {
pr->pr_pkt->pkt_duration = d;
parser_deliver(t, st, pr->pr_pkt);
parser_deliver(t, st, pr->pr_pkt, 1);
}
free(pr);
}
@ -949,13 +1019,13 @@ parser_compute_duration(th_transport_t *t, th_stream_t *st, th_pktref_t *pr)
* De-wrap and normalize PTS/DTS to 1MHz clock domain
*/
static void
parser_deliver(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt)
parser_deliver(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt,
int checkts)
{
int64_t dts, pts, ptsoff, d;
assert(pkt->pkt_dts != AV_NOPTS_VALUE);
assert(pkt->pkt_pts != AV_NOPTS_VALUE);
assert(pkt->pkt_duration > 10);
if(t->tht_dts_start == AV_NOPTS_VALUE) {
pkt_ref_dec(pkt);
@ -977,7 +1047,7 @@ parser_deliver(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt)
pkt_ref_dec(pkt);
return;
}
} else {
} else if(checkts) {
d = dts + st->st_dts_epoch - st->st_last_dts;
if(d < 0 || d > 90000) {

View file

@ -277,6 +277,8 @@ psi_parse_pmt(th_transport_t *t, const uint8_t *ptr, int len, int chksvcid,
int frameduration;
int update = 0;
int had_components;
int composition_id;
int ancillary_id;
if(len < 9)
return -1;
@ -339,8 +341,12 @@ psi_parse_pmt(th_transport_t *t, const uint8_t *ptr, int len, int chksvcid,
ptr += 5;
len -= 5;
frameduration = 0;
hts_stream_type = 0;
memset(lang, 0, 4);
composition_id = -1;
ancillary_id = -1;
switch(estype) {
case 0x01:
@ -362,8 +368,6 @@ psi_parse_pmt(th_transport_t *t, const uint8_t *ptr, int len, int chksvcid,
break;
}
memset(lang, 0, 4);
while(dllen > 1) {
dtag = ptr[0];
dlen = ptr[1];
@ -406,6 +410,16 @@ psi_parse_pmt(th_transport_t *t, const uint8_t *ptr, int len, int chksvcid,
hts_stream_type = SCT_AAC;
break;
case DVB_DESC_SUBTITLE:
if(dlen < 8)
break;
memcpy(lang, ptr, 3);
composition_id = ptr[4] << 8 | ptr[5];
ancillary_id = ptr[6] << 8 | ptr[7];
hts_stream_type = SCT_SUBTITLES;
break;
default:
break;
}
@ -432,6 +446,16 @@ psi_parse_pmt(th_transport_t *t, const uint8_t *ptr, int len, int chksvcid,
st->st_frame_duration = frameduration;
update = 1;
}
if(composition_id != -1 && st->st_composition_id != composition_id) {
st->st_composition_id = composition_id;
update = 1;
}
if(ancillary_id != -1 && st->st_ancillary_id != ancillary_id) {
st->st_ancillary_id = ancillary_id;
update = 1;
}
}
}
@ -445,6 +469,9 @@ psi_parse_pmt(th_transport_t *t, const uint8_t *ptr, int len, int chksvcid,
}
if(update) {
tvhlog(LOG_DEBUG, "PSI", "Transport %s (%s) updated due to PMT change",
t->tht_svcname, t->tht_identifier);
transport_request_save(t);
if(t->tht_status == TRANSPORT_RUNNING)
transport_restart(t, had_components);
@ -721,6 +748,11 @@ psi_save_transport_settings(htsmsg_t *m, th_transport_t *t)
htsmsg_add_u32(sub, "caproviderid", st->st_providerid);
}
if(st->st_type == SCT_SUBTITLES) {
htsmsg_add_u32(sub, "compositionid", st->st_composition_id);
htsmsg_add_u32(sub, "ancillartyid", st->st_ancillary_id);
}
if(st->st_frame_duration)
htsmsg_add_u32(sub, "frameduration", st->st_frame_duration);
@ -783,5 +815,13 @@ psi_load_transport_settings(htsmsg_t *m, th_transport_t *t)
}
htsmsg_get_u32(c, "caproviderid", &st->st_providerid);
if(type == SCT_SUBTITLES) {
if(!htsmsg_get_u32(c, "compositionid", &u32))
st->st_composition_id = u32;
if(!htsmsg_get_u32(c, "ancillartyid", &u32))
st->st_ancillary_id = u32;
}
}
}

View file

@ -28,7 +28,8 @@ typedef struct streaming_start_component {
int ssc_index;
int ssc_type;
char ssc_lang[4];
uint16_t ssc_composition_id;
uint16_t ssc_ancillary_id;
} streaming_start_component_t;

View file

@ -731,6 +731,8 @@ transport_build_stream_start(th_transport_t *t)
ssc->ssc_index = st->st_index;
ssc->ssc_type = st->st_type;
memcpy(ssc->ssc_lang, st->st_lang, 4);
ssc->ssc_composition_id = st->st_composition_id;
ssc->ssc_ancillary_id = st->st_ancillary_id;
}
t->tht_setsourceinfo(t, &ss->ss_si);

View file

@ -256,6 +256,9 @@ typedef struct th_stream {
int st_index;
char st_lang[4]; /* ISO 639 3-letter language code */
uint16_t st_composition_id;
uint16_t st_ancillary_id;
uint16_t st_pid;
uint8_t st_cc; /* Last CC */
@ -285,6 +288,7 @@ typedef struct th_stream {
int st_buffer_ptr;
int st_buffer_size;
int st_buffer_errors; /* Errors accumulated for this packet */
uint32_t st_startcond;
uint32_t st_startcode;
uint32_t st_startcode_offset;

View file

@ -1379,7 +1379,7 @@ extjs_servicedetails(http_connection_t *hc,
htsmsg_t *out, *streams, *c;
th_transport_t *t;
th_stream_t *st;
char buf[20];
char buf[40];
pthread_mutex_lock(&global_lock);
@ -1412,6 +1412,12 @@ extjs_servicedetails(http_connection_t *hc,
htsmsg_add_str(c, "details", st->st_lang);
break;
case SCT_SUBTITLES:
snprintf(buf, sizeof(buf), "%s (%04x %04x)",
st->st_lang, st->st_composition_id, st->st_ancillary_id);
htsmsg_add_str(c, "details", buf);
break;
case SCT_MPEG2VIDEO:
case SCT_H264:
buf[0] = 0;