From 3ee73a5b81d4b6c48fbbba65d6ea126105665eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 30 Nov 2009 22:33:19 +0000 Subject: [PATCH] Add DVB subtitle support. Ticket #39 --- debian/changelog | 4 +++ src/htsp.c | 6 ++++ src/parsers.c | 90 +++++++++++++++++++++++++++++++++++++++++------ src/psi.c | 44 +++++++++++++++++++++-- src/streaming.h | 3 +- src/transports.c | 2 ++ src/tvhead.h | 4 +++ src/webui/extjs.c | 8 ++++- 8 files changed, 147 insertions(+), 14 deletions(-) diff --git a/debian/changelog b/debian/changelog index 2bd3e21f..78fa06b6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -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 diff --git a/src/htsp.c b/src/htsp.c index 9d67e348..163cb71b 100644 --- a/src/htsp.c +++ b/src/htsp.c @@ -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); } diff --git a/src/parsers.c b/src/parsers.c index 3b0ff017..37f3f78c 100644 --- a/src/parsers.c +++ b/src/parsers.c @@ -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) { diff --git a/src/psi.c b/src/psi.c index 46b99824..553900ea 100644 --- a/src/psi.c +++ b/src/psi.c @@ -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; + } } } diff --git a/src/streaming.h b/src/streaming.h index 7a6010ca..d75fdbd4 100644 --- a/src/streaming.h +++ b/src/streaming.h @@ -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; diff --git a/src/transports.c b/src/transports.c index 59b96a30..c7fe8c12 100644 --- a/src/transports.c +++ b/src/transports.c @@ -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); diff --git a/src/tvhead.h b/src/tvhead.h index ca769da5..39f899a3 100644 --- a/src/tvhead.h +++ b/src/tvhead.h @@ -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; diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 0ebf368e..8625fbee 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -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;