From 1b0e150d8818190609373ef48e569b1ff4ebfa43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Wed, 28 Nov 2018 17:05:33 +0000 Subject: [PATCH] mpegvideoparse: extract CEA-708 closed captions --- gst/videoparsers/gstmpegvideoparse.c | 80 ++++++++++++++++++++++++++++ gst/videoparsers/gstmpegvideoparse.h | 5 ++ 2 files changed, 85 insertions(+) diff --git a/gst/videoparsers/gstmpegvideoparse.c b/gst/videoparsers/gstmpegvideoparse.c index 6896339ee4..7b6390acc8 100644 --- a/gst/videoparsers/gstmpegvideoparse.c +++ b/gst/videoparsers/gstmpegvideoparse.c @@ -461,6 +461,56 @@ parse_packet_extension (GstMpegvParse * mpvparse, GstMapInfo * info, guint off) } } +/* A53-4 Table 6.7 */ +#define A53_USER_DATA_ID_GA94 0x47413934 +#define A53_USER_DATA_ID_DTG1 0x44544731 + +/* A53-4 Table 6.9 */ +#define A53_USER_DATA_TYPE_CODE_MPEG_CC_DATA 0x03 +#define A53_USER_DATA_TYPE_CODE_BAR_DATA 0x06 + +/* CEA-708 Table 2 */ +#define CEA_708_PROCESS_CC_DATA_FLAG 0x40 + +static void +parse_user_data_packet (GstMpegvParse * mpvparse, const guint8 * data, + guint size) +{ + if (size < 2) { + GST_DEBUG_OBJECT (mpvparse, "user data packet too short, ignoring"); + return; + } + + /* A53 part 4 closed captions */ + if (size > 6 && GST_READ_UINT32_BE (data) == A53_USER_DATA_ID_GA94 + && data[4] == A53_USER_DATA_TYPE_CODE_MPEG_CC_DATA + && (data[5] & CEA_708_PROCESS_CC_DATA_FLAG) != 0 && data[6] == 0xff) { + guint8 cc_count = data[5] & 0x1f; + guint cc_size = cc_count * 3; + + data += 7; + size -= 7; + + if (cc_size == 0 || cc_size > size) { + GST_DEBUG_OBJECT (mpvparse, "ignoring closed captions, not enough data"); + return; + } + + /* Shouldn't really happen so let's not go out of our way to handle it */ + if (mpvparse->closedcaptions_size > 0) + GST_WARNING_OBJECT (mpvparse, "unused pending closed captions!"); + + g_assert (cc_size <= sizeof (mpvparse->closedcaptions)); + memcpy (mpvparse->closedcaptions, data, cc_size); + mpvparse->closedcaptions_size = cc_size; + mpvparse->closedcaptions_type = GST_VIDEO_CAPTION_TYPE_CEA708_RAW; + GST_DEBUG_OBJECT (mpvparse, "CEA-708 closed captions, %u bytes", cc_size); + return; + } + + GST_MEMDUMP_OBJECT (mpvparse, "unhandled user data packet", data, size); +} + /* caller guarantees at least start code in @buf at @off ( - 4)*/ /* for off == 4 initial code; returns TRUE if code starts a frame * otherwise returns TRUE if code terminates preceding frame */ @@ -515,6 +565,18 @@ gst_mpegv_parse_process_sc (GstMpegvParse * mpvparse, } checkconfig = FALSE; break; + case GST_MPEG_VIDEO_PACKET_USER_DATA: + GST_LOG_OBJECT (mpvparse, "USER_DATA packet of %d bytes", packet->size); + + if (packet->size < 0) { + GST_LOG_OBJECT (mpvparse, "no size yet, need more data"); + *need_more = TRUE; + return FALSE; + } + + parse_user_data_packet (mpvparse, info->data + off, packet->size); + checkconfig = FALSE; + break; default: if (GST_MPEG_VIDEO_PACKET_IS_SLICE (packet->type)) { mpvparse->slice_count++; @@ -1002,6 +1064,24 @@ gst_mpegv_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) meta->num_slices = mpvparse->slice_count; meta->slice_offset = mpvparse->slice_offset; } + + if (mpvparse->closedcaptions_size > 0) { + GstBuffer *buf; + + if (frame->out_buffer) { + buf = frame->out_buffer = gst_buffer_make_writable (frame->out_buffer); + } else { + buf = frame->buffer = gst_buffer_make_writable (frame->buffer); + } + + gst_buffer_add_video_caption_meta (buf, + mpvparse->closedcaptions_type, mpvparse->closedcaptions, + mpvparse->closedcaptions_size); + + mpvparse->closedcaptions_type = GST_VIDEO_CAPTION_TYPE_UNKNOWN; + mpvparse->closedcaptions_size = 0; + } + return GST_FLOW_OK; } diff --git a/gst/videoparsers/gstmpegvideoparse.h b/gst/videoparsers/gstmpegvideoparse.h index 1a56076ce7..ed9dcce3c4 100644 --- a/gst/videoparsers/gstmpegvideoparse.h +++ b/gst/videoparsers/gstmpegvideoparse.h @@ -87,6 +87,11 @@ struct _GstMpegvParse { gboolean picext_updated; gboolean quantmatrext_updated; + /* pending closed captions */ + guint8 closedcaptions[96]; + guint closedcaptions_size; + GstVideoCaptionType closedcaptions_type; + /* properties */ gboolean drop; gboolean gop_split;