* [FFmpeg-devel] [PATCH] mpegts: identify and demux DSMCC-B/MHEG streams
@ 2022-09-17 17:08 Scott Theisen
2022-11-13 23:19 ` Scott Theisen
0 siblings, 1 reply; 3+ messages in thread
From: Scott Theisen @ 2022-09-17 17:08 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Scott Theisen
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
--
2.34.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [FFmpeg-devel] [PATCH] mpegts: identify and demux DSMCC-B/MHEG streams
2022-09-17 17:08 [FFmpeg-devel] [PATCH] mpegts: identify and demux DSMCC-B/MHEG streams Scott Theisen
@ 2022-11-13 23:19 ` Scott Theisen
2022-11-28 20:08 ` Scott Theisen
0 siblings, 1 reply; 3+ messages in thread
From: Scott Theisen @ 2022-11-13 23:19 UTC (permalink / raw)
To: ffmpeg-devel
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
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [FFmpeg-devel] [PATCH] mpegts: identify and demux DSMCC-B/MHEG streams
2022-11-13 23:19 ` Scott Theisen
@ 2022-11-28 20:08 ` Scott Theisen
0 siblings, 0 replies; 3+ messages in thread
From: Scott Theisen @ 2022-11-28 20:08 UTC (permalink / raw)
To: ffmpeg-devel
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
>
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2022-11-28 20:08 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-17 17:08 [FFmpeg-devel] [PATCH] mpegts: identify and demux DSMCC-B/MHEG streams Scott Theisen
2022-11-13 23:19 ` Scott Theisen
2022-11-28 20:08 ` Scott Theisen
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git