diff mbox series

[FFmpeg-devel] mpegts: identify and demux DSMCC-B/MHEG streams

Message ID 20220917170843.308894-1-scott.the.elm@gmail.com
State New
Headers show
Series [FFmpeg-devel] mpegts: identify and demux DSMCC-B/MHEG streams | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Scott Theisen Sept. 17, 2022, 5:08 p.m. UTC
These changes are from MythTV.
---

The `AV_CODEC_ID`s are probably in the wrong place since these are
data codecs, but that is where they are In MythTV.

There was also a related change to libavformat/demux.c's
avformat_find_stream_info() trying to optimize it for MHEG streams,
but it is unnecessary and was causing FATE to fail.

 libavcodec/codec_desc.c |  12 +++
 libavcodec/codec_id.h   |   6 ++
 libavformat/avformat.h  |   5 +
 libavformat/mpegts.c    | 199 +++++++++++++++++++++++++++++++++++++++-
 libavformat/mpegts.h    |  17 ++++
 5 files changed, 237 insertions(+), 2 deletions(-)

Comments

Scott Theisen Nov. 13, 2022, 11:19 p.m. UTC | #1
Ping for review.

On 9/17/22 13:08, Scott Theisen wrote:
> These changes are from MythTV.
> ---
>
> The `AV_CODEC_ID`s are probably in the wrong place since these are
> data codecs, but that is where they are In MythTV.
>
> There was also a related change to libavformat/demux.c's
> avformat_find_stream_info() trying to optimize it for MHEG streams,
> but it is unnecessary and was causing FATE to fail.
>
>   libavcodec/codec_desc.c |  12 +++
>   libavcodec/codec_id.h   |   6 ++
>   libavformat/avformat.h  |   5 +
>   libavformat/mpegts.c    | 199 +++++++++++++++++++++++++++++++++++++++-
>   libavformat/mpegts.h    |  17 ++++
>   5 files changed, 237 insertions(+), 2 deletions(-)
>
> diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> index 648c518b3c..0e54087863 100644
> --- a/libavcodec/codec_desc.c
> +++ b/libavcodec/codec_desc.c
> @@ -3488,6 +3488,18 @@ static const AVCodecDescriptor codec_descriptors[] = {
>           .props     = AV_CODEC_PROP_TEXT_SUB,
>           .profiles  = NULL_IF_CONFIG_SMALL(ff_arib_caption_profiles),
>       },
> +    {
> +        .id        = AV_CODEC_ID_DVB_VBI,
> +        .type      = AVMEDIA_TYPE_DATA,
> +        .name      = "dvb_vbi",
> +        .long_name = NULL_IF_CONFIG_SMALL("dvb teletext"),
> +    },
> +    {
> +        .id        = AV_CODEC_ID_DSMCC_B,
> +        .type      = AVMEDIA_TYPE_DATA,
> +        .name      = "dsmcc_b",
> +        .long_name = NULL_IF_CONFIG_SMALL("DSMCC B"),
> +    },
>   
>       /* other kind of codecs and pseudo-codecs */
>       {
> diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
> index bc8226ff07..7e2198a800 100644
> --- a/libavcodec/codec_id.h
> +++ b/libavcodec/codec_id.h
> @@ -559,6 +559,12 @@ enum AVCodecID {
>       AV_CODEC_ID_TTML,
>       AV_CODEC_ID_ARIB_CAPTION,
>   
> +    /* teletext codecs */
> +    AV_CODEC_ID_DVB_VBI,
> +
> +    /* DSMCC codec */
> +    AV_CODEC_ID_DSMCC_B,
> +
>       /* other specific kind of codecs (generally used for attachments) */
>       AV_CODEC_ID_FIRST_UNKNOWN = 0x18000,           ///< A dummy ID pointing at the start of various fake codecs.
>       AV_CODEC_ID_TTF = 0x18000,
> diff --git a/libavformat/avformat.h b/libavformat/avformat.h
> index 9d46875cce..664a1afa61 100644
> --- a/libavformat/avformat.h
> +++ b/libavformat/avformat.h
> @@ -1117,6 +1117,11 @@ typedef struct AVStream {
>        *
>        */
>       int pts_wrap_bits;
> +
> +    /* MHEG support */
> +    int component_tag; ///< Component tag given in PMT
> +    int carousel_id;
> +    int data_id;
>   } AVStream;
>   
>   struct AVCodecParserContext *av_stream_get_parser(const AVStream *s);
> diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
> index 8a3436f2be..be8edd9e62 100644
> --- a/libavformat/mpegts.c
> +++ b/libavformat/mpegts.c
> @@ -46,6 +46,15 @@
>   #include <iconv.h>
>   #endif
>   
> +typedef struct SectionContext {
> +    int pid;
> +    int stream_type;
> +    int new_packet;
> +    MpegTSContext *ts;
> +    AVFormatContext *stream;
> +    AVStream *st;
> +} SectionContext;
> +
>   /* maximum size in which we look for synchronization if
>    * synchronization is lost */
>   #define MAX_RESYNC_SIZE 65536
> @@ -412,6 +421,8 @@ static int discard_pid(MpegTSContext *ts, unsigned int pid)
>       return !used && discarded;
>   }
>   
> +static void mpegts_push_section(MpegTSFilter *filter, const uint8_t *section, int section_len);
> +
>   /**
>    *  Assemble PES packets out of TS packets, and then call the "section_cb"
>    *  function when they are complete.
> @@ -438,6 +449,11 @@ static void write_section_data(MpegTSContext *ts, MpegTSFilter *tss1,
>           tss->section_index += len;
>       }
>   
> +    if (tss->section_cb == mpegts_push_section) {
> +        SectionContext *sect = tss->opaque;
> +        sect->new_packet = 1;
> +    }
> +
>       offset = 0;
>       cur_section_buf = tss->section_buf;
>       while (cur_section_buf - tss->section_buf < MAX_SECTION_SIZE && cur_section_buf[0] != 0xff) {
> @@ -798,6 +814,7 @@ static const StreamType ISO_types[] = {
>       { 0x02, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO },
>       { 0x03, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3        },
>       { 0x04, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3        },
> +    { 0x0b, AVMEDIA_TYPE_DATA,  AV_CODEC_ID_DSMCC_B    }, /* DVB_CAROUSEL_ID */
>       { 0x0f, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC        },
>       { 0x10, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4      },
>       /* Makito encoder sets stream type 0x11 for AAC,
> @@ -882,11 +899,20 @@ static const StreamType DESC_types[] = {
>       { 0x6a, AVMEDIA_TYPE_AUDIO,    AV_CODEC_ID_AC3          }, /* AC-3 descriptor */
>       { 0x7a, AVMEDIA_TYPE_AUDIO,    AV_CODEC_ID_EAC3         }, /* E-AC-3 descriptor */
>       { 0x7b, AVMEDIA_TYPE_AUDIO,    AV_CODEC_ID_DTS          },
> +    { 0x13, AVMEDIA_TYPE_DATA,     AV_CODEC_ID_DSMCC_B      }, /* DVB_CAROUSEL_ID */
> +    { 0x45, AVMEDIA_TYPE_DATA,     AV_CODEC_ID_DVB_VBI      }, /* DVB_VBI_DATA_ID */
> +    { 0x46, AVMEDIA_TYPE_DATA,     AV_CODEC_ID_DVB_VBI      }, /* DVB_VBI_TELETEXT_ID */ //FixMe type subtilte
>       { 0x56, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_DVB_TELETEXT },
>       { 0x59, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_DVB_SUBTITLE }, /* subtitling descriptor */
>       { 0 },
>   };
>   
> +/* component tags */
> +static const StreamType COMPONENT_TAG_types[] = {
> +    { 0x0a, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3        },
> +    { 0x52, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO },
> +};
> +
>   static void mpegts_find_stream_type(AVStream *st,
>                                       uint32_t stream_type,
>                                       const StreamType *types)
> @@ -1979,7 +2005,13 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
>                       memcpy(extradata, *pp, 4); /* composition_page_id and ancillary_page_id */
>                       extradata += 5;
>   
> -                    *pp += 4;
> +                    {
> +                        int comp_page   = get16(pp, desc_end);
> +                        int anc_page    = get16(pp, desc_end);
> +                        int sub_id      = (anc_page << 16) | comp_page;
> +                        if (sub_id && (st->codecpar->codec_id == AV_CODEC_ID_DVB_SUBTITLE))
> +                            st->carousel_id = sub_id;
> +                    }
>                   }
>   
>                   language[i * 4 - 1] = 0;
> @@ -2023,8 +2055,45 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
>                   sti->request_probe = 50;
>           }
>           break;
> +    case DVB_BROADCAST_ID:
> +        st->data_id = get16(pp, desc_end);
> +        break;
> +    case DVB_CAROUSEL_ID:
> +        {
> +            int carId = 0;
> +            carId = get8(pp, desc_end);
> +            carId = (carId << 8) | get8(pp, desc_end);
> +            carId = (carId << 8) | get8(pp, desc_end);
> +            carId = (carId << 8) | get8(pp, desc_end);
> +            st->carousel_id = carId;
> +        }
> +        break;
>       case 0x52: /* stream identifier descriptor */
>           sti->stream_identifier = 1 + get8(pp, desc_end);
> +        st->component_tag     = sti->stream_identifier - 1;
> +    // DVB_DATA_STREAM:
> +        /* Audio and video are sometimes encoded in private streams labelled with
> +         * a component tag. */
> +#if 0
> +         if (st->codecpar->codec_id == AV_CODEC_ID_NONE &&
> +             desc_count  == 1 &&
> +             stream_type == STREAM_TYPE_PRIVATE_DATA)
> +             mpegts_find_stream_type(st, st->component_tag,
> +                                         COMPONENT_TAG_types);
> +#endif
> +        break;
> +    case DVB_VBI_TELETEXT_ID:
> +        language[0] = get8(pp, desc_end);
> +        language[1] = get8(pp, desc_end);
> +        language[2] = get8(pp, desc_end);
> +        language[3] = 0;
> +
> +        /* dvbci->txt_type = */ i = (get8(pp, desc_end)) >> 3; // not exported, defeat compiler -Wunused-value
> +        if (language[0])
> +            av_dict_set(&st->metadata, "language", language, 0);
> +        break;
> +    case DVB_VBI_DATA_ID:
> +        // dvbci->vbi_data = 1; //not parsing the data service descriptors
>           break;
>       case METADATA_DESCRIPTOR:
>           if (get16(pp, desc_end) == 0xFFFF)
> @@ -2307,6 +2376,34 @@ static int is_pes_stream(int stream_type, uint32_t prog_reg_desc)
>                (stream_type == 0x86 && prog_reg_desc == AV_RL32("CUEI")) );
>   }
>   
> +static SectionContext *add_section_stream(MpegTSContext *ts, int pid, int stream_type)
> +{
> +    MpegTSFilter *tss = ts->pids[pid];
> +    SectionContext *sect = 0;
> +    if (tss) { /* filter already exists */
> +        /* kill it, and start a new stream */
> +        mpegts_close_filter(ts, tss);
> +    }
> +
> +    /* create a SECTION context */
> +    if (!(sect=av_mallocz(sizeof(SectionContext)))) {
> +        av_log(ts, AV_LOG_ERROR, "Error: av_mallocz() failed in add_section_stream");
> +        return 0;
> +    }
> +    sect->ts = ts;
> +    sect->stream = ts->stream;
> +    sect->pid = pid;
> +    sect->stream_type = stream_type;
> +    tss = mpegts_open_section_filter(ts, pid, mpegts_push_section, sect, 1);
> +    if (!tss) {
> +        av_free(sect);
> +        av_log(ts, AV_LOG_ERROR, "Error: unable to open mpegts Section filter in add_section_stream");
> +        return 0;
> +    }
> +
> +    return sect;
> +}
> +
>   static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
>   {
>       MpegTSContext *ts = filter->u.section_filter.opaque;
> @@ -2425,7 +2522,56 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
>           stream_identifier = parse_stream_identifier_desc(p, p_end) + 1;
>   
>           /* now create stream */
> -        if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) {
> +        if (stream_type == STREAM_TYPE_DSMCC_B)
> +        {
> +            SectionContext *sect = NULL;
> +            int idx = -1;
> +
> +            if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_SECTION &&
> +                ts->pids[pid]->u.section_filter.section_cb == mpegts_push_section) {
> +                // u.section_filter.opaque may be the MpegTSContext, so test the section_cb
> +                sect = (SectionContext*) ts->pids[pid]->u.section_filter.opaque;
> +            }
> +            if (!sect) {
> +                sect = add_section_stream(ts, pid, stream_type);
> +            }
> +            if (!sect)
> +            {
> +                av_log(ts, AV_LOG_ERROR, "mpegts_add_stream: "
> +                       "error creating Section context for pid 0x%x with type %i\n",
> +                       pid, stream_type);
> +                goto out;
> +            }
> +
> +            idx = ff_find_stream_index(ts->stream, pid);
> +            if (idx >= 0) {
> +                st = ts->stream->streams[idx];
> +                av_log(ts, AV_LOG_DEBUG, "mpegts_add_stream: "
> +                   "reusing stream #%d, has id 0x%x and codec %s, type %s at 0x%p\n",
> +                   st->index, st->id, avcodec_get_name(st->codecpar->codec_id),
> +                   av_get_media_type_string(st->codecpar->codec_type), st);
> +            }
> +            if (!st) {
> +                st = avformat_new_stream(sect->stream, NULL);
> +            }
> +            if (!st) {
> +                goto out;
> +            }
> +            sect->st = st;
> +            sect->st->id = sect->pid;
> +
> +            avpriv_set_pts_info(sect->st, 33, 1, 90000);
> +
> +            sect->st->codecpar->codec_type = AVMEDIA_TYPE_DATA;
> +            sect->st->codecpar->codec_id   = AV_CODEC_ID_DSMCC_B;
> +            sect->st->priv_data = sect;
> +            ffstream(sect->st)->need_parsing = AVSTREAM_PARSE_NONE;
> +
> +            av_log(ts, AV_LOG_DEBUG, "mpegts_add_stream: "
> +                   "stream #%d, has id 0x%x and codec %s, type %s at 0x%p\n",
> +                   st->index, st->id, avcodec_get_name(st->codecpar->codec_id),
> +                   av_get_media_type_string(st->codecpar->codec_type), st);
> +        } else if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) {
>               pes = ts->pids[pid]->u.pes_filter.opaque;
>               if (ts->merge_pmt_versions && !pes->st) {
>                   st = find_matching_stream(ts, pid, h->id, stream_identifier, i, &old_program);
> @@ -2527,6 +2673,55 @@ out:
>           av_free(mp4_descr[i].dec_config_descr);
>   }
>   
> +/* mpegts_push_section: return one or more tables.  The tables may not completely fill
> +   the packet and there may be stuffing bytes at the end.
> +   This is complicated because a single TS packet may result in several tables being
> +   produced.  We may have a "start" bit indicating, in effect, the end of a table but
> +   the rest of the TS packet after the start may be filled with one or more small tables.
> +*/
> +static void mpegts_push_section(MpegTSFilter *filter, const uint8_t *section, int section_len)
> +{
> +    SectionContext *sect = filter->u.section_filter.opaque;
> +    MpegTSContext *ts = sect->ts;
> +    SectionHeader header;
> +    AVPacket *pkt = ts->pkt;
> +    const uint8_t *p = section, *p_end = section + section_len - 4;
> +
> +    if (parse_section_header(&header, &p, p_end) < 0)
> +    {
> +        av_log(ts, AV_LOG_DEBUG, "Unable to parse header\n");
> +        return;
> +    }
> +
> +    if (sect->new_packet && pkt && sect->st && pkt->size == -1) {
> +        int pktLen = section_len + 184; /* Add enough for a complete TS payload. */
> +        sect->new_packet = 0;
> +        av_packet_unref(pkt);
> +        if (av_new_packet(pkt, pktLen) == 0) {
> +            memcpy(pkt->data, section, section_len);
> +            memset(pkt->data+section_len, 0xff, pktLen-section_len);
> +            pkt->stream_index = sect->st->index;
> +            ts->stop_parse = 1;
> +        }
> +    } else if (pkt->data) { /* We've already added at least one table. */
> +        uint8_t *data = pkt->data;
> +        int space = pkt->size;
> +        int table_size = 0;
> +        while (space > 3 + table_size) {
> +            table_size = (((data[1] & 0xf) << 8) | data[2]) + 3;
> +            if (table_size < space) {
> +                space -= table_size;
> +                data += table_size;
> +            } /* Otherwise we've got filler. */
> +        }
> +        if (space < section_len) {
> +            av_log(ts, AV_LOG_DEBUG, "Insufficient space for additional packet\n");
> +            return;
> +        }
> +        memcpy(data, section, section_len);
> +   }
> +}
> +
>   static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
>   {
>       MpegTSContext *ts = filter->u.section_filter.opaque;
> diff --git a/libavformat/mpegts.h b/libavformat/mpegts.h
> index a48f14e768..6f57af7786 100644
> --- a/libavformat/mpegts.h
> +++ b/libavformat/mpegts.h
> @@ -122,6 +122,7 @@
>   #define STREAM_TYPE_AUDIO_MPEG2     0x04
>   #define STREAM_TYPE_PRIVATE_SECTION 0x05
>   #define STREAM_TYPE_PRIVATE_DATA    0x06
> +#define STREAM_TYPE_DSMCC_B         0x0b
>   #define STREAM_TYPE_AUDIO_AAC       0x0f
>   #define STREAM_TYPE_AUDIO_AAC_LATM  0x11
>   #define STREAM_TYPE_VIDEO_MPEG4     0x10
> @@ -139,6 +140,22 @@
>   #define STREAM_TYPE_AUDIO_TRUEHD    0x83
>   #define STREAM_TYPE_AUDIO_EAC3      0x87
>   
> +#define STREAM_TYPE_AUDIO_MISC_DTS           0x8a
> +#define STREAM_TYPE_AUDIO_HDMV_AC3_PLUS      0x84
> +#define STREAM_TYPE_AUDIO_HDMV_DTS_HD        0x85
> +#define STREAM_TYPE_AUDIO_HDMV_DTS_HD_MASTER 0x86
> +
> +#define STREAM_TYPE_SUBTITLE_DVB    0x100
> +#define STREAM_TYPE_VBI_DVB         0x101
> +
> +#define DVB_CAROUSEL_ID             0x13
> +#define DVB_VBI_DATA_ID             0x45
> +#define DVB_VBI_TELETEXT_ID         0x46
> +#define DVB_TELETEXT_ID             0x56
> +#define DVB_SUBT_DESCID             0x59
> +#define DVB_BROADCAST_ID            0x66
> +#define DVB_DATA_STREAM             0x52
> +
>   /* ISO/IEC 13818-1 Table 2-22 */
>   #define STREAM_ID_PROGRAM_STREAM_MAP        0xbc
>   #define STREAM_ID_PRIVATE_STREAM_1          0xbd
Scott Theisen Nov. 28, 2022, 8:08 p.m. UTC | #2
Ping for review.

Thanks in advance,

Scott Theisen

On 11/13/22 18:19, Scott Theisen wrote:
> Ping for review.
>
> On 9/17/22 13:08, Scott Theisen wrote:
>> These changes are from MythTV.
>> ---
>>
>> The `AV_CODEC_ID`s are probably in the wrong place since these are
>> data codecs, but that is where they are In MythTV.
>>
>> There was also a related change to libavformat/demux.c's
>> avformat_find_stream_info() trying to optimize it for MHEG streams,
>> but it is unnecessary and was causing FATE to fail.
>>
>>   libavcodec/codec_desc.c |  12 +++
>>   libavcodec/codec_id.h   |   6 ++
>>   libavformat/avformat.h  |   5 +
>>   libavformat/mpegts.c    | 199 +++++++++++++++++++++++++++++++++++++++-
>>   libavformat/mpegts.h    |  17 ++++
>>   5 files changed, 237 insertions(+), 2 deletions(-)
>>
>> diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
>> index 648c518b3c..0e54087863 100644
>> --- a/libavcodec/codec_desc.c
>> +++ b/libavcodec/codec_desc.c
>> @@ -3488,6 +3488,18 @@ static const AVCodecDescriptor 
>> codec_descriptors[] = {
>>           .props     = AV_CODEC_PROP_TEXT_SUB,
>>           .profiles  = NULL_IF_CONFIG_SMALL(ff_arib_caption_profiles),
>>       },
>> +    {
>> +        .id        = AV_CODEC_ID_DVB_VBI,
>> +        .type      = AVMEDIA_TYPE_DATA,
>> +        .name      = "dvb_vbi",
>> +        .long_name = NULL_IF_CONFIG_SMALL("dvb teletext"),
>> +    },
>> +    {
>> +        .id        = AV_CODEC_ID_DSMCC_B,
>> +        .type      = AVMEDIA_TYPE_DATA,
>> +        .name      = "dsmcc_b",
>> +        .long_name = NULL_IF_CONFIG_SMALL("DSMCC B"),
>> +    },
>>         /* other kind of codecs and pseudo-codecs */
>>       {
>> diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
>> index bc8226ff07..7e2198a800 100644
>> --- a/libavcodec/codec_id.h
>> +++ b/libavcodec/codec_id.h
>> @@ -559,6 +559,12 @@ enum AVCodecID {
>>       AV_CODEC_ID_TTML,
>>       AV_CODEC_ID_ARIB_CAPTION,
>>   +    /* teletext codecs */
>> +    AV_CODEC_ID_DVB_VBI,
>> +
>> +    /* DSMCC codec */
>> +    AV_CODEC_ID_DSMCC_B,
>> +
>>       /* other specific kind of codecs (generally used for 
>> attachments) */
>>       AV_CODEC_ID_FIRST_UNKNOWN = 0x18000,           ///< A dummy ID 
>> pointing at the start of various fake codecs.
>>       AV_CODEC_ID_TTF = 0x18000,
>> diff --git a/libavformat/avformat.h b/libavformat/avformat.h
>> index 9d46875cce..664a1afa61 100644
>> --- a/libavformat/avformat.h
>> +++ b/libavformat/avformat.h
>> @@ -1117,6 +1117,11 @@ typedef struct AVStream {
>>        *
>>        */
>>       int pts_wrap_bits;
>> +
>> +    /* MHEG support */
>> +    int component_tag; ///< Component tag given in PMT
>> +    int carousel_id;
>> +    int data_id;
>>   } AVStream;
>>     struct AVCodecParserContext *av_stream_get_parser(const AVStream 
>> *s);
>> diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
>> index 8a3436f2be..be8edd9e62 100644
>> --- a/libavformat/mpegts.c
>> +++ b/libavformat/mpegts.c
>> @@ -46,6 +46,15 @@
>>   #include <iconv.h>
>>   #endif
>>   +typedef struct SectionContext {
>> +    int pid;
>> +    int stream_type;
>> +    int new_packet;
>> +    MpegTSContext *ts;
>> +    AVFormatContext *stream;
>> +    AVStream *st;
>> +} SectionContext;
>> +
>>   /* maximum size in which we look for synchronization if
>>    * synchronization is lost */
>>   #define MAX_RESYNC_SIZE 65536
>> @@ -412,6 +421,8 @@ static int discard_pid(MpegTSContext *ts, 
>> unsigned int pid)
>>       return !used && discarded;
>>   }
>>   +static void mpegts_push_section(MpegTSFilter *filter, const 
>> uint8_t *section, int section_len);
>> +
>>   /**
>>    *  Assemble PES packets out of TS packets, and then call the 
>> "section_cb"
>>    *  function when they are complete.
>> @@ -438,6 +449,11 @@ static void write_section_data(MpegTSContext 
>> *ts, MpegTSFilter *tss1,
>>           tss->section_index += len;
>>       }
>>   +    if (tss->section_cb == mpegts_push_section) {
>> +        SectionContext *sect = tss->opaque;
>> +        sect->new_packet = 1;
>> +    }
>> +
>>       offset = 0;
>>       cur_section_buf = tss->section_buf;
>>       while (cur_section_buf - tss->section_buf < MAX_SECTION_SIZE && 
>> cur_section_buf[0] != 0xff) {
>> @@ -798,6 +814,7 @@ static const StreamType ISO_types[] = {
>>       { 0x02, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO },
>>       { 0x03, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3        },
>>       { 0x04, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3        },
>> +    { 0x0b, AVMEDIA_TYPE_DATA,  AV_CODEC_ID_DSMCC_B    }, /* 
>> DVB_CAROUSEL_ID */
>>       { 0x0f, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC        },
>>       { 0x10, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4      },
>>       /* Makito encoder sets stream type 0x11 for AAC,
>> @@ -882,11 +899,20 @@ static const StreamType DESC_types[] = {
>>       { 0x6a, AVMEDIA_TYPE_AUDIO,    AV_CODEC_ID_AC3          }, /* 
>> AC-3 descriptor */
>>       { 0x7a, AVMEDIA_TYPE_AUDIO,    AV_CODEC_ID_EAC3         }, /* 
>> E-AC-3 descriptor */
>>       { 0x7b, AVMEDIA_TYPE_AUDIO,    AV_CODEC_ID_DTS          },
>> +    { 0x13, AVMEDIA_TYPE_DATA,     AV_CODEC_ID_DSMCC_B      }, /* 
>> DVB_CAROUSEL_ID */
>> +    { 0x45, AVMEDIA_TYPE_DATA,     AV_CODEC_ID_DVB_VBI      }, /* 
>> DVB_VBI_DATA_ID */
>> +    { 0x46, AVMEDIA_TYPE_DATA,     AV_CODEC_ID_DVB_VBI      }, /* 
>> DVB_VBI_TELETEXT_ID */ //FixMe type subtilte
>>       { 0x56, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_DVB_TELETEXT },
>>       { 0x59, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_DVB_SUBTITLE }, /* 
>> subtitling descriptor */
>>       { 0 },
>>   };
>>   +/* component tags */
>> +static const StreamType COMPONENT_TAG_types[] = {
>> +    { 0x0a, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3        },
>> +    { 0x52, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO },
>> +};
>> +
>>   static void mpegts_find_stream_type(AVStream *st,
>>                                       uint32_t stream_type,
>>                                       const StreamType *types)
>> @@ -1979,7 +2005,13 @@ int ff_parse_mpeg2_descriptor(AVFormatContext 
>> *fc, AVStream *st, int stream_type
>>                       memcpy(extradata, *pp, 4); /* 
>> composition_page_id and ancillary_page_id */
>>                       extradata += 5;
>>   -                    *pp += 4;
>> +                    {
>> +                        int comp_page   = get16(pp, desc_end);
>> +                        int anc_page    = get16(pp, desc_end);
>> +                        int sub_id      = (anc_page << 16) | comp_page;
>> +                        if (sub_id && (st->codecpar->codec_id == 
>> AV_CODEC_ID_DVB_SUBTITLE))
>> +                            st->carousel_id = sub_id;
>> +                    }
>>                   }
>>                     language[i * 4 - 1] = 0;
>> @@ -2023,8 +2055,45 @@ int ff_parse_mpeg2_descriptor(AVFormatContext 
>> *fc, AVStream *st, int stream_type
>>                   sti->request_probe = 50;
>>           }
>>           break;
>> +    case DVB_BROADCAST_ID:
>> +        st->data_id = get16(pp, desc_end);
>> +        break;
>> +    case DVB_CAROUSEL_ID:
>> +        {
>> +            int carId = 0;
>> +            carId = get8(pp, desc_end);
>> +            carId = (carId << 8) | get8(pp, desc_end);
>> +            carId = (carId << 8) | get8(pp, desc_end);
>> +            carId = (carId << 8) | get8(pp, desc_end);
>> +            st->carousel_id = carId;
>> +        }
>> +        break;
>>       case 0x52: /* stream identifier descriptor */
>>           sti->stream_identifier = 1 + get8(pp, desc_end);
>> +        st->component_tag     = sti->stream_identifier - 1;
>> +    // DVB_DATA_STREAM:
>> +        /* Audio and video are sometimes encoded in private streams 
>> labelled with
>> +         * a component tag. */
>> +#if 0
>> +         if (st->codecpar->codec_id == AV_CODEC_ID_NONE &&
>> +             desc_count  == 1 &&
>> +             stream_type == STREAM_TYPE_PRIVATE_DATA)
>> +             mpegts_find_stream_type(st, st->component_tag,
>> +                                         COMPONENT_TAG_types);
>> +#endif
>> +        break;
>> +    case DVB_VBI_TELETEXT_ID:
>> +        language[0] = get8(pp, desc_end);
>> +        language[1] = get8(pp, desc_end);
>> +        language[2] = get8(pp, desc_end);
>> +        language[3] = 0;
>> +
>> +        /* dvbci->txt_type = */ i = (get8(pp, desc_end)) >> 3; // 
>> not exported, defeat compiler -Wunused-value
>> +        if (language[0])
>> +            av_dict_set(&st->metadata, "language", language, 0);
>> +        break;
>> +    case DVB_VBI_DATA_ID:
>> +        // dvbci->vbi_data = 1; //not parsing the data service 
>> descriptors
>>           break;
>>       case METADATA_DESCRIPTOR:
>>           if (get16(pp, desc_end) == 0xFFFF)
>> @@ -2307,6 +2376,34 @@ static int is_pes_stream(int stream_type, 
>> uint32_t prog_reg_desc)
>>                (stream_type == 0x86 && prog_reg_desc == 
>> AV_RL32("CUEI")) );
>>   }
>>   +static SectionContext *add_section_stream(MpegTSContext *ts, int 
>> pid, int stream_type)
>> +{
>> +    MpegTSFilter *tss = ts->pids[pid];
>> +    SectionContext *sect = 0;
>> +    if (tss) { /* filter already exists */
>> +        /* kill it, and start a new stream */
>> +        mpegts_close_filter(ts, tss);
>> +    }
>> +
>> +    /* create a SECTION context */
>> +    if (!(sect=av_mallocz(sizeof(SectionContext)))) {
>> +        av_log(ts, AV_LOG_ERROR, "Error: av_mallocz() failed in 
>> add_section_stream");
>> +        return 0;
>> +    }
>> +    sect->ts = ts;
>> +    sect->stream = ts->stream;
>> +    sect->pid = pid;
>> +    sect->stream_type = stream_type;
>> +    tss = mpegts_open_section_filter(ts, pid, mpegts_push_section, 
>> sect, 1);
>> +    if (!tss) {
>> +        av_free(sect);
>> +        av_log(ts, AV_LOG_ERROR, "Error: unable to open mpegts 
>> Section filter in add_section_stream");
>> +        return 0;
>> +    }
>> +
>> +    return sect;
>> +}
>> +
>>   static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, 
>> int section_len)
>>   {
>>       MpegTSContext *ts = filter->u.section_filter.opaque;
>> @@ -2425,7 +2522,56 @@ static void pmt_cb(MpegTSFilter *filter, const 
>> uint8_t *section, int section_len
>>           stream_identifier = parse_stream_identifier_desc(p, p_end) 
>> + 1;
>>             /* now create stream */
>> -        if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) {
>> +        if (stream_type == STREAM_TYPE_DSMCC_B)
>> +        {
>> +            SectionContext *sect = NULL;
>> +            int idx = -1;
>> +
>> +            if (ts->pids[pid] && ts->pids[pid]->type == 
>> MPEGTS_SECTION &&
>> + ts->pids[pid]->u.section_filter.section_cb == mpegts_push_section) {
>> +                // u.section_filter.opaque may be the MpegTSContext, 
>> so test the section_cb
>> +                sect = (SectionContext*) 
>> ts->pids[pid]->u.section_filter.opaque;
>> +            }
>> +            if (!sect) {
>> +                sect = add_section_stream(ts, pid, stream_type);
>> +            }
>> +            if (!sect)
>> +            {
>> +                av_log(ts, AV_LOG_ERROR, "mpegts_add_stream: "
>> +                       "error creating Section context for pid 0x%x 
>> with type %i\n",
>> +                       pid, stream_type);
>> +                goto out;
>> +            }
>> +
>> +            idx = ff_find_stream_index(ts->stream, pid);
>> +            if (idx >= 0) {
>> +                st = ts->stream->streams[idx];
>> +                av_log(ts, AV_LOG_DEBUG, "mpegts_add_stream: "
>> +                   "reusing stream #%d, has id 0x%x and codec %s, 
>> type %s at 0x%p\n",
>> +                   st->index, st->id, 
>> avcodec_get_name(st->codecpar->codec_id),
>> + av_get_media_type_string(st->codecpar->codec_type), st);
>> +            }
>> +            if (!st) {
>> +                st = avformat_new_stream(sect->stream, NULL);
>> +            }
>> +            if (!st) {
>> +                goto out;
>> +            }
>> +            sect->st = st;
>> +            sect->st->id = sect->pid;
>> +
>> +            avpriv_set_pts_info(sect->st, 33, 1, 90000);
>> +
>> +            sect->st->codecpar->codec_type = AVMEDIA_TYPE_DATA;
>> +            sect->st->codecpar->codec_id   = AV_CODEC_ID_DSMCC_B;
>> +            sect->st->priv_data = sect;
>> +            ffstream(sect->st)->need_parsing = AVSTREAM_PARSE_NONE;
>> +
>> +            av_log(ts, AV_LOG_DEBUG, "mpegts_add_stream: "
>> +                   "stream #%d, has id 0x%x and codec %s, type %s at 
>> 0x%p\n",
>> +                   st->index, st->id, 
>> avcodec_get_name(st->codecpar->codec_id),
>> + av_get_media_type_string(st->codecpar->codec_type), st);
>> +        } else if (ts->pids[pid] && ts->pids[pid]->type == 
>> MPEGTS_PES) {
>>               pes = ts->pids[pid]->u.pes_filter.opaque;
>>               if (ts->merge_pmt_versions && !pes->st) {
>>                   st = find_matching_stream(ts, pid, h->id, 
>> stream_identifier, i, &old_program);
>> @@ -2527,6 +2673,55 @@ out:
>>           av_free(mp4_descr[i].dec_config_descr);
>>   }
>>   +/* mpegts_push_section: return one or more tables.  The tables may 
>> not completely fill
>> +   the packet and there may be stuffing bytes at the end.
>> +   This is complicated because a single TS packet may result in 
>> several tables being
>> +   produced.  We may have a "start" bit indicating, in effect, the 
>> end of a table but
>> +   the rest of the TS packet after the start may be filled with one 
>> or more small tables.
>> +*/
>> +static void mpegts_push_section(MpegTSFilter *filter, const uint8_t 
>> *section, int section_len)
>> +{
>> +    SectionContext *sect = filter->u.section_filter.opaque;
>> +    MpegTSContext *ts = sect->ts;
>> +    SectionHeader header;
>> +    AVPacket *pkt = ts->pkt;
>> +    const uint8_t *p = section, *p_end = section + section_len - 4;
>> +
>> +    if (parse_section_header(&header, &p, p_end) < 0)
>> +    {
>> +        av_log(ts, AV_LOG_DEBUG, "Unable to parse header\n");
>> +        return;
>> +    }
>> +
>> +    if (sect->new_packet && pkt && sect->st && pkt->size == -1) {
>> +        int pktLen = section_len + 184; /* Add enough for a complete 
>> TS payload. */
>> +        sect->new_packet = 0;
>> +        av_packet_unref(pkt);
>> +        if (av_new_packet(pkt, pktLen) == 0) {
>> +            memcpy(pkt->data, section, section_len);
>> +            memset(pkt->data+section_len, 0xff, pktLen-section_len);
>> +            pkt->stream_index = sect->st->index;
>> +            ts->stop_parse = 1;
>> +        }
>> +    } else if (pkt->data) { /* We've already added at least one 
>> table. */
>> +        uint8_t *data = pkt->data;
>> +        int space = pkt->size;
>> +        int table_size = 0;
>> +        while (space > 3 + table_size) {
>> +            table_size = (((data[1] & 0xf) << 8) | data[2]) + 3;
>> +            if (table_size < space) {
>> +                space -= table_size;
>> +                data += table_size;
>> +            } /* Otherwise we've got filler. */
>> +        }
>> +        if (space < section_len) {
>> +            av_log(ts, AV_LOG_DEBUG, "Insufficient space for 
>> additional packet\n");
>> +            return;
>> +        }
>> +        memcpy(data, section, section_len);
>> +   }
>> +}
>> +
>>   static void pat_cb(MpegTSFilter *filter, const uint8_t *section, 
>> int section_len)
>>   {
>>       MpegTSContext *ts = filter->u.section_filter.opaque;
>> diff --git a/libavformat/mpegts.h b/libavformat/mpegts.h
>> index a48f14e768..6f57af7786 100644
>> --- a/libavformat/mpegts.h
>> +++ b/libavformat/mpegts.h
>> @@ -122,6 +122,7 @@
>>   #define STREAM_TYPE_AUDIO_MPEG2     0x04
>>   #define STREAM_TYPE_PRIVATE_SECTION 0x05
>>   #define STREAM_TYPE_PRIVATE_DATA    0x06
>> +#define STREAM_TYPE_DSMCC_B         0x0b
>>   #define STREAM_TYPE_AUDIO_AAC       0x0f
>>   #define STREAM_TYPE_AUDIO_AAC_LATM  0x11
>>   #define STREAM_TYPE_VIDEO_MPEG4     0x10
>> @@ -139,6 +140,22 @@
>>   #define STREAM_TYPE_AUDIO_TRUEHD    0x83
>>   #define STREAM_TYPE_AUDIO_EAC3      0x87
>>   +#define STREAM_TYPE_AUDIO_MISC_DTS           0x8a
>> +#define STREAM_TYPE_AUDIO_HDMV_AC3_PLUS      0x84
>> +#define STREAM_TYPE_AUDIO_HDMV_DTS_HD        0x85
>> +#define STREAM_TYPE_AUDIO_HDMV_DTS_HD_MASTER 0x86
>> +
>> +#define STREAM_TYPE_SUBTITLE_DVB    0x100
>> +#define STREAM_TYPE_VBI_DVB         0x101
>> +
>> +#define DVB_CAROUSEL_ID             0x13
>> +#define DVB_VBI_DATA_ID             0x45
>> +#define DVB_VBI_TELETEXT_ID         0x46
>> +#define DVB_TELETEXT_ID             0x56
>> +#define DVB_SUBT_DESCID             0x59
>> +#define DVB_BROADCAST_ID            0x66
>> +#define DVB_DATA_STREAM             0x52
>> +
>>   /* ISO/IEC 13818-1 Table 2-22 */
>>   #define STREAM_ID_PROGRAM_STREAM_MAP        0xbc
>>   #define STREAM_ID_PRIVATE_STREAM_1          0xbd
>
diff mbox series

Patch

diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 648c518b3c..0e54087863 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -3488,6 +3488,18 @@  static const AVCodecDescriptor codec_descriptors[] = {
         .props     = AV_CODEC_PROP_TEXT_SUB,
         .profiles  = NULL_IF_CONFIG_SMALL(ff_arib_caption_profiles),
     },
+    {
+        .id        = AV_CODEC_ID_DVB_VBI,
+        .type      = AVMEDIA_TYPE_DATA,
+        .name      = "dvb_vbi",
+        .long_name = NULL_IF_CONFIG_SMALL("dvb teletext"),
+    },
+    {
+        .id        = AV_CODEC_ID_DSMCC_B,
+        .type      = AVMEDIA_TYPE_DATA,
+        .name      = "dsmcc_b",
+        .long_name = NULL_IF_CONFIG_SMALL("DSMCC B"),
+    },
 
     /* other kind of codecs and pseudo-codecs */
     {
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index bc8226ff07..7e2198a800 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -559,6 +559,12 @@  enum AVCodecID {
     AV_CODEC_ID_TTML,
     AV_CODEC_ID_ARIB_CAPTION,
 
+    /* teletext codecs */
+    AV_CODEC_ID_DVB_VBI,
+
+    /* DSMCC codec */
+    AV_CODEC_ID_DSMCC_B,
+
     /* other specific kind of codecs (generally used for attachments) */
     AV_CODEC_ID_FIRST_UNKNOWN = 0x18000,           ///< A dummy ID pointing at the start of various fake codecs.
     AV_CODEC_ID_TTF = 0x18000,
diff --git a/libavformat/avformat.h b/libavformat/avformat.h
index 9d46875cce..664a1afa61 100644
--- a/libavformat/avformat.h
+++ b/libavformat/avformat.h
@@ -1117,6 +1117,11 @@  typedef struct AVStream {
      *
      */
     int pts_wrap_bits;
+
+    /* MHEG support */
+    int component_tag; ///< Component tag given in PMT
+    int carousel_id;
+    int data_id;
 } AVStream;
 
 struct AVCodecParserContext *av_stream_get_parser(const AVStream *s);
diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
index 8a3436f2be..be8edd9e62 100644
--- a/libavformat/mpegts.c
+++ b/libavformat/mpegts.c
@@ -46,6 +46,15 @@ 
 #include <iconv.h>
 #endif
 
+typedef struct SectionContext {
+    int pid;
+    int stream_type;
+    int new_packet;
+    MpegTSContext *ts;
+    AVFormatContext *stream;
+    AVStream *st;
+} SectionContext;
+
 /* maximum size in which we look for synchronization if
  * synchronization is lost */
 #define MAX_RESYNC_SIZE 65536
@@ -412,6 +421,8 @@  static int discard_pid(MpegTSContext *ts, unsigned int pid)
     return !used && discarded;
 }
 
+static void mpegts_push_section(MpegTSFilter *filter, const uint8_t *section, int section_len);
+
 /**
  *  Assemble PES packets out of TS packets, and then call the "section_cb"
  *  function when they are complete.
@@ -438,6 +449,11 @@  static void write_section_data(MpegTSContext *ts, MpegTSFilter *tss1,
         tss->section_index += len;
     }
 
+    if (tss->section_cb == mpegts_push_section) {
+        SectionContext *sect = tss->opaque;
+        sect->new_packet = 1;
+    }
+
     offset = 0;
     cur_section_buf = tss->section_buf;
     while (cur_section_buf - tss->section_buf < MAX_SECTION_SIZE && cur_section_buf[0] != 0xff) {
@@ -798,6 +814,7 @@  static const StreamType ISO_types[] = {
     { 0x02, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO },
     { 0x03, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3        },
     { 0x04, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3        },
+    { 0x0b, AVMEDIA_TYPE_DATA,  AV_CODEC_ID_DSMCC_B    }, /* DVB_CAROUSEL_ID */
     { 0x0f, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC        },
     { 0x10, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4      },
     /* Makito encoder sets stream type 0x11 for AAC,
@@ -882,11 +899,20 @@  static const StreamType DESC_types[] = {
     { 0x6a, AVMEDIA_TYPE_AUDIO,    AV_CODEC_ID_AC3          }, /* AC-3 descriptor */
     { 0x7a, AVMEDIA_TYPE_AUDIO,    AV_CODEC_ID_EAC3         }, /* E-AC-3 descriptor */
     { 0x7b, AVMEDIA_TYPE_AUDIO,    AV_CODEC_ID_DTS          },
+    { 0x13, AVMEDIA_TYPE_DATA,     AV_CODEC_ID_DSMCC_B      }, /* DVB_CAROUSEL_ID */
+    { 0x45, AVMEDIA_TYPE_DATA,     AV_CODEC_ID_DVB_VBI      }, /* DVB_VBI_DATA_ID */
+    { 0x46, AVMEDIA_TYPE_DATA,     AV_CODEC_ID_DVB_VBI      }, /* DVB_VBI_TELETEXT_ID */ //FixMe type subtilte
     { 0x56, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_DVB_TELETEXT },
     { 0x59, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_DVB_SUBTITLE }, /* subtitling descriptor */
     { 0 },
 };
 
+/* component tags */
+static const StreamType COMPONENT_TAG_types[] = {
+    { 0x0a, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3        },
+    { 0x52, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO },
+};
+
 static void mpegts_find_stream_type(AVStream *st,
                                     uint32_t stream_type,
                                     const StreamType *types)
@@ -1979,7 +2005,13 @@  int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
                     memcpy(extradata, *pp, 4); /* composition_page_id and ancillary_page_id */
                     extradata += 5;
 
-                    *pp += 4;
+                    {
+                        int comp_page   = get16(pp, desc_end);
+                        int anc_page    = get16(pp, desc_end);
+                        int sub_id      = (anc_page << 16) | comp_page;
+                        if (sub_id && (st->codecpar->codec_id == AV_CODEC_ID_DVB_SUBTITLE))
+                            st->carousel_id = sub_id;
+                    }
                 }
 
                 language[i * 4 - 1] = 0;
@@ -2023,8 +2055,45 @@  int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
                 sti->request_probe = 50;
         }
         break;
+    case DVB_BROADCAST_ID:
+        st->data_id = get16(pp, desc_end);
+        break;
+    case DVB_CAROUSEL_ID:
+        {
+            int carId = 0;
+            carId = get8(pp, desc_end);
+            carId = (carId << 8) | get8(pp, desc_end);
+            carId = (carId << 8) | get8(pp, desc_end);
+            carId = (carId << 8) | get8(pp, desc_end);
+            st->carousel_id = carId;
+        }
+        break;
     case 0x52: /* stream identifier descriptor */
         sti->stream_identifier = 1 + get8(pp, desc_end);
+        st->component_tag     = sti->stream_identifier - 1;
+    // DVB_DATA_STREAM:
+        /* Audio and video are sometimes encoded in private streams labelled with
+         * a component tag. */
+#if 0
+         if (st->codecpar->codec_id == AV_CODEC_ID_NONE &&
+             desc_count  == 1 &&
+             stream_type == STREAM_TYPE_PRIVATE_DATA)
+             mpegts_find_stream_type(st, st->component_tag,
+                                         COMPONENT_TAG_types);
+#endif
+        break;
+    case DVB_VBI_TELETEXT_ID:
+        language[0] = get8(pp, desc_end);
+        language[1] = get8(pp, desc_end);
+        language[2] = get8(pp, desc_end);
+        language[3] = 0;
+
+        /* dvbci->txt_type = */ i = (get8(pp, desc_end)) >> 3; // not exported, defeat compiler -Wunused-value
+        if (language[0])
+            av_dict_set(&st->metadata, "language", language, 0);
+        break;
+    case DVB_VBI_DATA_ID:
+        // dvbci->vbi_data = 1; //not parsing the data service descriptors
         break;
     case METADATA_DESCRIPTOR:
         if (get16(pp, desc_end) == 0xFFFF)
@@ -2307,6 +2376,34 @@  static int is_pes_stream(int stream_type, uint32_t prog_reg_desc)
              (stream_type == 0x86 && prog_reg_desc == AV_RL32("CUEI")) );
 }
 
+static SectionContext *add_section_stream(MpegTSContext *ts, int pid, int stream_type)
+{
+    MpegTSFilter *tss = ts->pids[pid];
+    SectionContext *sect = 0;
+    if (tss) { /* filter already exists */
+        /* kill it, and start a new stream */
+        mpegts_close_filter(ts, tss);
+    }
+
+    /* create a SECTION context */
+    if (!(sect=av_mallocz(sizeof(SectionContext)))) {
+        av_log(ts, AV_LOG_ERROR, "Error: av_mallocz() failed in add_section_stream");
+        return 0;
+    }
+    sect->ts = ts;
+    sect->stream = ts->stream;
+    sect->pid = pid;
+    sect->stream_type = stream_type;
+    tss = mpegts_open_section_filter(ts, pid, mpegts_push_section, sect, 1);
+    if (!tss) {
+        av_free(sect);
+        av_log(ts, AV_LOG_ERROR, "Error: unable to open mpegts Section filter in add_section_stream");
+        return 0;
+    }
+
+    return sect;
+}
+
 static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
 {
     MpegTSContext *ts = filter->u.section_filter.opaque;
@@ -2425,7 +2522,56 @@  static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
         stream_identifier = parse_stream_identifier_desc(p, p_end) + 1;
 
         /* now create stream */
-        if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) {
+        if (stream_type == STREAM_TYPE_DSMCC_B)
+        {
+            SectionContext *sect = NULL;
+            int idx = -1;
+
+            if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_SECTION &&
+                ts->pids[pid]->u.section_filter.section_cb == mpegts_push_section) {
+                // u.section_filter.opaque may be the MpegTSContext, so test the section_cb
+                sect = (SectionContext*) ts->pids[pid]->u.section_filter.opaque;
+            }
+            if (!sect) {
+                sect = add_section_stream(ts, pid, stream_type);
+            }
+            if (!sect)
+            {
+                av_log(ts, AV_LOG_ERROR, "mpegts_add_stream: "
+                       "error creating Section context for pid 0x%x with type %i\n",
+                       pid, stream_type);
+                goto out;
+            }
+
+            idx = ff_find_stream_index(ts->stream, pid);
+            if (idx >= 0) {
+                st = ts->stream->streams[idx];
+                av_log(ts, AV_LOG_DEBUG, "mpegts_add_stream: "
+                   "reusing stream #%d, has id 0x%x and codec %s, type %s at 0x%p\n",
+                   st->index, st->id, avcodec_get_name(st->codecpar->codec_id),
+                   av_get_media_type_string(st->codecpar->codec_type), st);
+            }
+            if (!st) {
+                st = avformat_new_stream(sect->stream, NULL);
+            }
+            if (!st) {
+                goto out;
+            }
+            sect->st = st;
+            sect->st->id = sect->pid;
+
+            avpriv_set_pts_info(sect->st, 33, 1, 90000);
+
+            sect->st->codecpar->codec_type = AVMEDIA_TYPE_DATA;
+            sect->st->codecpar->codec_id   = AV_CODEC_ID_DSMCC_B;
+            sect->st->priv_data = sect;
+            ffstream(sect->st)->need_parsing = AVSTREAM_PARSE_NONE;
+
+            av_log(ts, AV_LOG_DEBUG, "mpegts_add_stream: "
+                   "stream #%d, has id 0x%x and codec %s, type %s at 0x%p\n",
+                   st->index, st->id, avcodec_get_name(st->codecpar->codec_id),
+                   av_get_media_type_string(st->codecpar->codec_type), st);
+        } else if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) {
             pes = ts->pids[pid]->u.pes_filter.opaque;
             if (ts->merge_pmt_versions && !pes->st) {
                 st = find_matching_stream(ts, pid, h->id, stream_identifier, i, &old_program);
@@ -2527,6 +2673,55 @@  out:
         av_free(mp4_descr[i].dec_config_descr);
 }
 
+/* mpegts_push_section: return one or more tables.  The tables may not completely fill
+   the packet and there may be stuffing bytes at the end.
+   This is complicated because a single TS packet may result in several tables being
+   produced.  We may have a "start" bit indicating, in effect, the end of a table but
+   the rest of the TS packet after the start may be filled with one or more small tables.
+*/
+static void mpegts_push_section(MpegTSFilter *filter, const uint8_t *section, int section_len)
+{
+    SectionContext *sect = filter->u.section_filter.opaque;
+    MpegTSContext *ts = sect->ts;
+    SectionHeader header;
+    AVPacket *pkt = ts->pkt;
+    const uint8_t *p = section, *p_end = section + section_len - 4;
+
+    if (parse_section_header(&header, &p, p_end) < 0)
+    {
+        av_log(ts, AV_LOG_DEBUG, "Unable to parse header\n");
+        return;
+    }
+
+    if (sect->new_packet && pkt && sect->st && pkt->size == -1) {
+        int pktLen = section_len + 184; /* Add enough for a complete TS payload. */
+        sect->new_packet = 0;
+        av_packet_unref(pkt);
+        if (av_new_packet(pkt, pktLen) == 0) {
+            memcpy(pkt->data, section, section_len);
+            memset(pkt->data+section_len, 0xff, pktLen-section_len);
+            pkt->stream_index = sect->st->index;
+            ts->stop_parse = 1;
+        }
+    } else if (pkt->data) { /* We've already added at least one table. */
+        uint8_t *data = pkt->data;
+        int space = pkt->size;
+        int table_size = 0;
+        while (space > 3 + table_size) {
+            table_size = (((data[1] & 0xf) << 8) | data[2]) + 3;
+            if (table_size < space) {
+                space -= table_size;
+                data += table_size;
+            } /* Otherwise we've got filler. */
+        }
+        if (space < section_len) {
+            av_log(ts, AV_LOG_DEBUG, "Insufficient space for additional packet\n");
+            return;
+        }
+        memcpy(data, section, section_len);
+   }
+}
+
 static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
 {
     MpegTSContext *ts = filter->u.section_filter.opaque;
diff --git a/libavformat/mpegts.h b/libavformat/mpegts.h
index a48f14e768..6f57af7786 100644
--- a/libavformat/mpegts.h
+++ b/libavformat/mpegts.h
@@ -122,6 +122,7 @@ 
 #define STREAM_TYPE_AUDIO_MPEG2     0x04
 #define STREAM_TYPE_PRIVATE_SECTION 0x05
 #define STREAM_TYPE_PRIVATE_DATA    0x06
+#define STREAM_TYPE_DSMCC_B         0x0b
 #define STREAM_TYPE_AUDIO_AAC       0x0f
 #define STREAM_TYPE_AUDIO_AAC_LATM  0x11
 #define STREAM_TYPE_VIDEO_MPEG4     0x10
@@ -139,6 +140,22 @@ 
 #define STREAM_TYPE_AUDIO_TRUEHD    0x83
 #define STREAM_TYPE_AUDIO_EAC3      0x87
 
+#define STREAM_TYPE_AUDIO_MISC_DTS           0x8a
+#define STREAM_TYPE_AUDIO_HDMV_AC3_PLUS      0x84
+#define STREAM_TYPE_AUDIO_HDMV_DTS_HD        0x85
+#define STREAM_TYPE_AUDIO_HDMV_DTS_HD_MASTER 0x86
+
+#define STREAM_TYPE_SUBTITLE_DVB    0x100
+#define STREAM_TYPE_VBI_DVB         0x101
+
+#define DVB_CAROUSEL_ID             0x13
+#define DVB_VBI_DATA_ID             0x45
+#define DVB_VBI_TELETEXT_ID         0x46
+#define DVB_TELETEXT_ID             0x56
+#define DVB_SUBT_DESCID             0x59
+#define DVB_BROADCAST_ID            0x66
+#define DVB_DATA_STREAM             0x52
+
 /* ISO/IEC 13818-1 Table 2-22 */
 #define STREAM_ID_PROGRAM_STREAM_MAP        0xbc
 #define STREAM_ID_PRIVATE_STREAM_1          0xbd