diff --git a/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json b/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json index 022454cac5..b01dbf0ab6 100644 --- a/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json +++ b/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json @@ -7809,6 +7809,32 @@ } }, "properties": { + "cea608-padding-strategy": { + "blurb": "What transformations to perform on CEA-608 padding data", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "valid", + "mutable": "playing", + "readable": true, + "type": "GstCCBufferCea608PaddingStrategy", + "writable": true + }, + "cea608-padding-valid-timeout": { + "blurb": "How long after receiving valid non-padding CEA-608 data to keep writing valid CEA-608 padding bytes", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "18446744073709551615", + "max": "18446744073709551615", + "min": "0", + "mutable": "playing", + "readable": true, + "type": "guint64", + "writable": true + }, "max-scheduled": { "blurb": "Maximum number of buffers to queue for scheduling", "conditionally-available": false, @@ -8071,6 +8097,21 @@ "filename": "gstclosedcaption", "license": "LGPL", "other-types": { + "GstCCBufferCea608PaddingStrategy": { + "kind": "flags", + "values": [ + { + "desc": "Remove padding from input data", + "name": "input-remove", + "value": "0x00000001" + }, + { + "desc": "Write 608 padding as valid", + "name": "valid", + "value": "0x00000002" + } + ] + }, "GstCCConverterCDPMode": { "kind": "flags", "values": [ diff --git a/subprojects/gst-plugins-bad/ext/closedcaption/ccutils.c b/subprojects/gst-plugins-bad/ext/closedcaption/ccutils.c index 599f543cfa..a4e00c798b 100644 --- a/subprojects/gst-plugins-bad/ext/closedcaption/ccutils.c +++ b/subprojects/gst-plugins-bad/ext/closedcaption/ccutils.c @@ -342,7 +342,8 @@ convert_cea708_cdp_to_cc_data (GstObject * dbg_obj, static gint cc_data_extract_cea608 (const guint8 * cc_data, guint cc_data_len, guint8 * cea608_field1, guint * cea608_field1_len, - guint8 * cea608_field2, guint * cea608_field2_len) + guint8 * cea608_field2, guint * cea608_field2_len, + gboolean remove_cea608_padding) { guint i, field_1_len = 0, field_2_len = 0; @@ -382,8 +383,10 @@ cc_data_extract_cea608 (const guint8 * cc_data, guint cc_data_len, return CC_DATA_EXTRACT_TOO_MANY_FIELD1; } - cea608_field1[(*cea608_field1_len)++] = byte1; - cea608_field1[(*cea608_field1_len)++] = byte2; + if (!remove_cea608_padding || byte1 != 0x80 || byte2 != 0x80) { + cea608_field1[(*cea608_field1_len)++] = byte1; + cea608_field1[(*cea608_field1_len)++] = byte2; + } } } else if (cc_type == 0x01) { if (!cc_valid) @@ -395,8 +398,10 @@ cc_data_extract_cea608 (const guint8 * cc_data, guint cc_data_len, *cea608_field2_len + 2); return CC_DATA_EXTRACT_TOO_MANY_FIELD2; } - cea608_field2[(*cea608_field2_len)++] = byte1; - cea608_field2[(*cea608_field2_len)++] = byte2; + if (!remove_cea608_padding || byte1 != 0x80 || byte2 != 0x80) { + cea608_field2[(*cea608_field2_len)++] = byte1; + cea608_field2[(*cea608_field2_len)++] = byte2; + } } } else { /* all cea608 packets must be at the beginning of a cc_data */ @@ -416,10 +421,35 @@ cc_data_extract_cea608 (const guint8 * cc_data, guint cc_data_len, gint drop_ccp_from_cc_data (guint8 * cc_data, guint cc_data_len) { - return cc_data_extract_cea608 (cc_data, cc_data_len, NULL, NULL, NULL, NULL); + return cc_data_extract_cea608 (cc_data, cc_data_len, NULL, NULL, NULL, NULL, + FALSE); +} + +GType +gst_cc_buffer_cea608_padding_strategy_get_type (void) +{ + static const GFlagsValue values[] = { + {CC_BUFFER_CEA608_PADDING_STRATEGY_INPUT_REMOVE, + "Remove padding from input data", "input-remove"}, + {CC_BUFFER_CEA608_PADDING_STRATEGY_VALID, "Write 608 padding as valid", + "valid"}, + {0, NULL, NULL} + }; + static GType id = 0; + + if (g_once_init_enter ((gsize *) & id)) { + GType _id; + + _id = g_flags_register_static ("GstCCBufferCea608PaddingStrategy", values); + + g_once_init_leave ((gsize *) & id, _id); + } + + return id; } #define DEFAULT_MAX_BUFFER_TIME (100 * GST_MSECOND) +#define DEFAULT_VALID_TIMEOUT GST_CLOCK_TIME_NONE struct _CCBuffer { @@ -429,11 +459,18 @@ struct _CCBuffer GArray *cc_data; /* used for tracking which field to write across output buffer boundaries */ gboolean last_cea608_written_was_field1; + /* tracks the timeout for valid_timeout */ + guint64 field1_padding_written_count; + guint64 field2_padding_written_count; + gboolean cea608_1_any_valid; + gboolean cea608_2_any_valid; /* properties */ GstClockTime max_buffer_time; gboolean output_padding; gboolean output_ccp_padding; + CCBufferCea608PaddingStrategy cea608_padding_strategy; + GstClockTime valid_timeout; }; G_DEFINE_TYPE (CCBuffer, cc_buffer, G_TYPE_OBJECT); @@ -454,6 +491,12 @@ cc_buffer_init (CCBuffer * buf) buf->max_buffer_time = DEFAULT_MAX_BUFFER_TIME; buf->output_padding = TRUE; buf->output_ccp_padding = FALSE; + buf->cea608_padding_strategy = 0; + buf->valid_timeout = DEFAULT_VALID_TIMEOUT; + buf->field1_padding_written_count = 0; + buf->field2_padding_written_count = 0; + buf->cea608_1_any_valid = FALSE; + buf->cea608_2_any_valid = FALSE; } static void @@ -529,7 +572,7 @@ compact_cc_data (guint8 * cc_data, guint cc_data_len) } static guint -calculate_n_cea608_doubles_from_time_ceil (CCBuffer * buf, GstClockTime ns) +calculate_n_cea608_doubles_from_time_ceil (GstClockTime ns) { /* cea608 has a maximum bitrate of 60000/1001 * 2 bytes/s */ guint ret = gst_util_uint64_scale_ceil (ns, 120000, 1001 * GST_SECOND); @@ -538,7 +581,7 @@ calculate_n_cea608_doubles_from_time_ceil (CCBuffer * buf, GstClockTime ns) } static guint -calculate_n_cea708_doubles_from_time_ceil (CCBuffer * buf, GstClockTime ns) +calculate_n_cea708_doubles_from_time_ceil (GstClockTime ns) { /* ccp has a maximum bitrate of 9600000/1001 bits/s */ guint ret = gst_util_uint64_scale_ceil (ns, 9600000 / 8, 1001 * GST_SECOND); @@ -556,7 +599,7 @@ push_internal (CCBuffer * buf, const guint8 * cea608_1, GST_DEBUG_OBJECT (buf, "pushing cea608-1: %u cea608-2: %u ccp: %u", cea608_1_len, cea608_2_len, cc_data_len); max_cea608_bytes = - calculate_n_cea608_doubles_from_time_ceil (buf, buf->max_buffer_time); + calculate_n_cea608_doubles_from_time_ceil (buf->max_buffer_time); if (cea608_1_len > 0) { if (cea608_1_len + buf->cea608_1->len > max_cea608_bytes) { @@ -578,7 +621,7 @@ push_internal (CCBuffer * buf, const guint8 * cea608_1, } if (cc_data_len > 0) { guint max_cea708_bytes = - calculate_n_cea708_doubles_from_time_ceil (buf, buf->max_buffer_time); + calculate_n_cea708_doubles_from_time_ceil (buf->max_buffer_time); if (cc_data_len + buf->cc_data->len > max_cea708_bytes) { GST_WARNING_OBJECT (buf, "ccp data overflow, dropping all " "previous data, max %u, attempted to hold %u", max_cea708_bytes, @@ -597,12 +640,17 @@ cc_buffer_push_separated (CCBuffer * buf, const guint8 * cea608_1, guint8 cea608_1_copy[MAX_CEA608_LEN]; guint8 cea608_2_copy[MAX_CEA608_LEN]; guint8 cc_data_copy[MAX_CDP_PACKET_LEN]; + gboolean remove_cea608_padding = + (buf->cea608_padding_strategy & + CC_BUFFER_CEA608_PADDING_STRATEGY_INPUT_REMOVE) + != 0; guint i; if (cea608_1 && cea608_1_len > 0) { guint out_i = 0; for (i = 0; i < cea608_1_len / 2; i++) { - if (cea608_1[i] != 0x80 || cea608_1[i + 1] != 0x80) { + if (!remove_cea608_padding || cea608_1[i] != 0x80 + || cea608_1[i + 1] != 0x80) { cea608_1_copy[out_i++] = cea608_1[i]; cea608_1_copy[out_i++] = cea608_1[i + 1]; } @@ -615,7 +663,8 @@ cc_buffer_push_separated (CCBuffer * buf, const guint8 * cea608_1, if (cea608_2 && cea608_2_len > 0) { guint out_i = 0; for (i = 0; i < cea608_2_len / 2; i++) { - if (cea608_2[i] != 0x80 || cea608_2[i + 1] != 0x80) { + if (!remove_cea608_padding || cea608_2[i] != 0x80 + || cea608_2[i + 1] != 0x80) { cea608_2_copy[out_i++] = cea608_2[i]; cea608_2_copy[out_i++] = cea608_2[i + 1]; } @@ -654,7 +703,9 @@ cc_buffer_push_cc_data (CCBuffer * buf, const guint8 * cc_data, cc_data_len = compact_cc_data (cc_data_copy, cc_data_len); ccp_offset = cc_data_extract_cea608 (cc_data_copy, cc_data_len, cea608_1, - &cea608_1_len, cea608_2, &cea608_2_len); + &cea608_1_len, cea608_2, &cea608_2_len, + (buf->cea608_padding_strategy & + CC_BUFFER_CEA608_PADDING_STRATEGY_INPUT_REMOVE) != 0); if (ccp_offset < 0) { GST_WARNING_OBJECT (buf, "Failed to extract cea608 from cc_data"); @@ -816,6 +867,11 @@ cc_buffer_take_separated (CCBuffer * buf, } else if (cea608_1) { memcpy (cea608_1, buf->cea608_1->data, write_cea608_1_size); memset (&cea608_1[write_cea608_1_size], 0x80, field1_padding); + if (write_cea608_1_size == 0) { + buf->field1_padding_written_count += write_cea608_1_size / 2; + } else { + buf->field1_padding_written_count = 0; + } *cea608_1_len = write_cea608_1_size + field1_padding; } else { *cea608_1_len = 0; @@ -829,6 +885,11 @@ cc_buffer_take_separated (CCBuffer * buf, } else if (cea608_2) { memcpy (cea608_2, buf->cea608_2->data, write_cea608_2_size); memset (&cea608_2[write_cea608_2_size], 0x80, field2_padding); + if (write_cea608_2_size == 0) { + buf->field2_padding_written_count += write_cea608_2_size / 2; + } else { + buf->field2_padding_written_count = 0; + } *cea608_2_len = write_cea608_2_size + field2_padding; } else { *cea608_2_len = 0; @@ -881,13 +942,15 @@ cc_buffer_take_separated (CCBuffer * buf, void cc_buffer_take_cc_data (CCBuffer * buf, - const struct cdp_fps_entry *fps_entry, gboolean nul_padding, + const struct cdp_fps_entry *fps_entry, guint8 * cc_data, guint * cc_data_len) { guint write_cea608_1_size, write_cea608_2_size, write_ccp_size; guint field1_padding, field2_padding; gboolean wrote_first; - guint8 padding_byte = nul_padding ? 0x00 : 0x80; + gboolean nul_padding = + (buf->cea608_padding_strategy & CC_BUFFER_CEA608_PADDING_STRATEGY_VALID) + == 0; cc_buffer_get_out_sizes (buf, fps_entry, &write_cea608_1_size, &field1_padding, &write_cea608_2_size, &field2_padding, &write_ccp_size); @@ -911,6 +974,8 @@ cc_buffer_take_cc_data (CCBuffer * buf, cc_data[out_i++] = cea608_1[cea608_1_i + 1]; cea608_1_i += 2; buf->last_cea608_written_was_field1 = TRUE; + buf->field1_padding_written_count = 0; + buf->cea608_1_any_valid = TRUE; } else if (cea608_1_i < write_cea608_1_size + field1_padding) { GST_TRACE_OBJECT (buf, "write field2:%u field2_i:%u, cea608-2 buf len:%u", @@ -923,10 +988,16 @@ cc_buffer_take_cc_data (CCBuffer * buf, cc_data[out_i++] = 0xfc; cc_data[out_i++] = 0x80; cc_data[out_i++] = 0x80; + buf->field1_padding_written_count = 0; } else { - cc_data[out_i++] = 0xf8; + gboolean write_invalid = nul_padding || (buf->cea608_1_any_valid && + calculate_n_cea608_doubles_from_time_ceil (buf->valid_timeout) / + 2 < buf->field1_padding_written_count); + guint8 padding_byte = write_invalid ? 0x00 : 0x80; + cc_data[out_i++] = write_invalid ? 0xf8 : 0xfc; cc_data[out_i++] = padding_byte; cc_data[out_i++] = padding_byte; + buf->field1_padding_written_count += 1; } cea608_1_i += 2; buf->last_cea608_written_was_field1 = TRUE; @@ -939,12 +1010,19 @@ cc_buffer_take_cc_data (CCBuffer * buf, cc_data[out_i++] = cea608_2[cea608_2_i + 1]; cea608_2_i += 2; buf->last_cea608_written_was_field1 = FALSE; + buf->field2_padding_written_count = 0; + buf->cea608_2_any_valid = TRUE; } else if (cea608_2_i < write_cea608_2_size + field2_padding) { - cc_data[out_i++] = 0xf9; + gboolean write_invalid = nul_padding || (buf->cea608_2_any_valid && + calculate_n_cea608_doubles_from_time_ceil (buf->valid_timeout) / 2 < + buf->field2_padding_written_count); + guint8 padding_byte = write_invalid ? 0x00 : 0x80; + cc_data[out_i++] = write_invalid ? 0xf9 : 0xfd; cc_data[out_i++] = padding_byte; cc_data[out_i++] = padding_byte; cea608_2_i += 2; buf->last_cea608_written_was_field1 = FALSE; + buf->field2_padding_written_count += 1; } wrote_first = TRUE; @@ -1058,3 +1136,16 @@ cc_buffer_set_output_padding (CCBuffer * buf, gboolean output_padding, buf->output_padding = output_padding; buf->output_ccp_padding = output_ccp_padding; } + +void +cc_buffer_set_cea608_padding_strategy (CCBuffer * buf, + CCBufferCea608PaddingStrategy padding_strategy) +{ + buf->cea608_padding_strategy = padding_strategy; +} + +void +cc_buffer_set_cea608_valid_timeout (CCBuffer * buf, GstClockTime valid_timeout) +{ + buf->valid_timeout = valid_timeout; +} diff --git a/subprojects/gst-plugins-bad/ext/closedcaption/ccutils.h b/subprojects/gst-plugins-bad/ext/closedcaption/ccutils.h index 611969e8de..89ffdf4f97 100644 --- a/subprojects/gst-plugins-bad/ext/closedcaption/ccutils.h +++ b/subprojects/gst-plugins-bad/ext/closedcaption/ccutils.h @@ -75,6 +75,31 @@ gint drop_ccp_from_cc_data (guint8 * cc_data, #define MAX_CDP_PACKET_LEN 256 #define MAX_CEA608_LEN 32 +/** + * GstCCBufferCea608PaddingStrategy: + * @CC_BUFFER_CEA608_PADDING_STRATEGY_INPUT_REMOVE: Keep whatever padding was provided on the input. + * Do not add, remove, or modify, any padding bytes. + * @CC_BUFFER_CEA608_PADDING_STRATEGY_VALID: Always modify any padding data to become valid. + * This may cause a stream to show as a CEA-608 caption stream with no contents. + * + * Since: 1.26 + */ +/** + * @CC_BUFFER_CEA608_PADDING_STRATEGY_INPUT_REMOVE: + * Since: 1.26 + */ +/** + * @CC_BUFFER_CEA608_PADDING_STRATEGY_VALID: + * Since: 1.26 + */ +typedef enum { + CC_BUFFER_CEA608_PADDING_STRATEGY_INPUT_REMOVE = (1 << 0), + CC_BUFFER_CEA608_PADDING_STRATEGY_VALID = (1 << 1), +} CCBufferCea608PaddingStrategy; + +#define GST_TYPE_CC_BUFFER_CEA608_PADDING_STRATEGY (gst_cc_buffer_cea608_padding_strategy_get_type()) +GType gst_cc_buffer_cea608_padding_strategy_get_type (void); + G_DECLARE_FINAL_TYPE (CCBuffer, cc_buffer, GST, CC_BUFFER, GObject); G_GNUC_INTERNAL @@ -99,7 +124,6 @@ gboolean cc_buffer_push_cc_data (CCBuffer * buf, G_GNUC_INTERNAL void cc_buffer_take_cc_data (CCBuffer * buf, const struct cdp_fps_entry * fps_entry, - gboolean nul_padding, guint8 * cc_data, guint * cc_data_len); G_GNUC_INTERNAL @@ -132,6 +156,11 @@ G_GNUC_INTERNAL void cc_buffer_set_output_padding (CCBuffer * buf, gboolean output_padding, gboolean output_ccp_padding); +G_GNUC_INTERNAL +void cc_buffer_set_cea608_padding_strategy(CCBuffer * buf, + CCBufferCea608PaddingStrategy padding_strategy); +G_GNUC_INTERNAL +void cc_buffer_set_cea608_valid_timeout (CCBuffer * buf, GstClockTime valid_timeout); G_END_DECLS diff --git a/subprojects/gst-plugins-bad/ext/closedcaption/gstcccombiner.c b/subprojects/gst-plugins-bad/ext/closedcaption/gstcccombiner.c index 402fa6df38..58e836c555 100644 --- a/subprojects/gst-plugins-bad/ext/closedcaption/gstcccombiner.c +++ b/subprojects/gst-plugins-bad/ext/closedcaption/gstcccombiner.c @@ -63,11 +63,15 @@ enum PROP_SCHEDULE, PROP_OUTPUT_PADDING, PROP_MAX_SCHEDULED, + PROP_CEA608_PADDING_STRATEGY, + PROP_CEA608_VALID_PADDING_TIMEOUT, }; #define DEFAULT_MAX_SCHEDULED 30 #define DEFAULT_SCHEDULE TRUE #define DEFAULT_OUTPUT_PADDING TRUE +#define DEFAULT_CEA608_PADDING_STRATEGY CC_BUFFER_CEA608_PADDING_STRATEGY_VALID +#define DEFAULT_CEA608_VALID_PADDING_TIMEOUT GST_CLOCK_TIME_NONE typedef struct { @@ -156,8 +160,7 @@ write_cc_data_to (GstCCCombiner * self, GstBuffer * buffer) gst_buffer_map (buffer, &map, GST_MAP_WRITE); len = map.size; - cc_buffer_take_cc_data (self->cc_buffer, self->cdp_fps_entry, TRUE, map.data, - &len); + cc_buffer_take_cc_data (self->cc_buffer, self->cdp_fps_entry, map.data, &len); gst_buffer_unmap (buffer, &map); gst_buffer_set_size (buffer, len); } @@ -188,7 +191,7 @@ take_s334_both_fields (GstCCCombiner * self, GstBuffer * buffer) gst_buffer_map (buffer, &out, GST_MAP_READWRITE); cc_data_len = out.size; - cc_buffer_take_cc_data (self->cc_buffer, self->cdp_fps_entry, FALSE, + cc_buffer_take_cc_data (self->cc_buffer, self->cdp_fps_entry, out.data, &cc_data_len); s334_len = drop_ccp_from_cc_data (out.data, cc_data_len); if (s334_len < 0) { @@ -351,7 +354,7 @@ dequeue_caption (GstCCCombiner * self, GstVideoTimeCode * tc, gboolean drain) if (GST_BUFFER_FLAG_IS_SET (self->current_video_buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED)) { if (!GST_VIDEO_BUFFER_IS_BOTTOM_FIELD (self->current_video_buffer)) { - cc_buffer_take_cc_data (self->cc_buffer, self->cdp_fps_entry, TRUE, + cc_buffer_take_cc_data (self->cc_buffer, self->cdp_fps_entry, cc_data, &cc_data_len); caption_data.buffer = make_cdp_buffer (self, cc_data, cc_data_len, self->cdp_fps_entry, @@ -359,7 +362,7 @@ dequeue_caption (GstCCCombiner * self, GstVideoTimeCode * tc, gboolean drain) g_array_append_val (self->current_frame_captions, caption_data); } } else { - cc_buffer_take_cc_data (self->cc_buffer, self->cdp_fps_entry, TRUE, + cc_buffer_take_cc_data (self->cc_buffer, self->cdp_fps_entry, cc_data, &cc_data_len); caption_data.buffer = make_cdp_buffer (self, cc_data, cc_data_len, self->cdp_fps_entry, @@ -1125,6 +1128,16 @@ gst_cc_combiner_set_property (GObject * object, guint prop_id, case PROP_OUTPUT_PADDING: self->prop_output_padding = g_value_get_boolean (value); break; + case PROP_CEA608_PADDING_STRATEGY: + self->prop_cea608_padding_strategy = g_value_get_flags (value); + cc_buffer_set_cea608_padding_strategy (self->cc_buffer, + self->prop_cea608_padding_strategy); + break; + case PROP_CEA608_VALID_PADDING_TIMEOUT: + self->prop_cea608_valid_padding_timeout = g_value_get_uint64 (value); + cc_buffer_set_cea608_valid_timeout (self->cc_buffer, + self->prop_cea608_valid_padding_timeout); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1147,6 +1160,12 @@ gst_cc_combiner_get_property (GObject * object, guint prop_id, GValue * value, case PROP_OUTPUT_PADDING: g_value_set_boolean (value, self->prop_output_padding); break; + case PROP_CEA608_PADDING_STRATEGY: + g_value_set_flags (value, self->prop_cea608_padding_strategy); + break; + case PROP_CEA608_VALID_PADDING_TIMEOUT: + g_value_set_uint64 (value, self->prop_cea608_valid_padding_timeout); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1240,6 +1259,40 @@ gst_cc_combiner_class_init (GstCCCombinerClass * klass) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY)); + /** + * GstCCCombiner:cea608-padding-strategy: + * + * Controls the transformations that may be done on padding CEA-608 data. + * + * Since: 1.26 + */ + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_CEA608_PADDING_STRATEGY, + g_param_spec_flags ("cea608-padding-strategy", + "CEA-608 Padding Strategy", + "What transformations to perform on CEA-608 padding data", + GST_TYPE_CC_BUFFER_CEA608_PADDING_STRATEGY, + DEFAULT_CEA608_PADDING_STRATEGY, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); + + /** + * GstCCCombiner:cea608-padding-valid-timeout: + * + * Timeout to apply when padding strategy contains "valid". After this time + * hase passed, CEA-608 padding will be signalled as invalid until new valid + * CEA-608 non-padding data is received. + * + * Since: 1.26 + */ + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_CEA608_VALID_PADDING_TIMEOUT, + g_param_spec_uint64 ("cea608-padding-valid-timeout", + "CEA-608 Padding Valid Timeout", + "How long after receiving valid non-padding CEA-608 data to keep writing valid CEA-608 padding bytes", + 0, G_MAXUINT64, DEFAULT_CEA608_VALID_PADDING_TIMEOUT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); gst_element_class_add_static_pad_template_with_gtype (gstelement_class, &sinktemplate, GST_TYPE_AGGREGATOR_PAD); @@ -1291,9 +1344,16 @@ gst_cc_combiner_init (GstCCCombiner * self) self->prop_schedule = DEFAULT_SCHEDULE; self->prop_max_scheduled = DEFAULT_MAX_SCHEDULED; self->prop_output_padding = DEFAULT_OUTPUT_PADDING; + self->prop_cea608_padding_strategy = DEFAULT_CEA608_PADDING_STRATEGY; + self->prop_cea608_valid_padding_timeout = + DEFAULT_CEA608_VALID_PADDING_TIMEOUT; self->cdp_hdr_sequence_cntr = 0; self->cdp_fps_entry = &null_fps_entry; self->cc_buffer = cc_buffer_new (); cc_buffer_set_max_buffer_time (self->cc_buffer, GST_CLOCK_TIME_NONE); + cc_buffer_set_cea608_valid_timeout (self->cc_buffer, + self->prop_cea608_valid_padding_timeout); + cc_buffer_set_cea608_padding_strategy (self->cc_buffer, + self->prop_cea608_padding_strategy); } diff --git a/subprojects/gst-plugins-bad/ext/closedcaption/gstcccombiner.h b/subprojects/gst-plugins-bad/ext/closedcaption/gstcccombiner.h index 8d370bcf00..ed3e44f271 100644 --- a/subprojects/gst-plugins-bad/ext/closedcaption/gstcccombiner.h +++ b/subprojects/gst-plugins-bad/ext/closedcaption/gstcccombiner.h @@ -59,6 +59,8 @@ struct _GstCCCombiner gboolean prop_schedule; guint prop_max_scheduled; gboolean prop_output_padding; + CCBufferCea608PaddingStrategy prop_cea608_padding_strategy; + GstClockTime prop_cea608_valid_padding_timeout; gboolean schedule; guint max_scheduled; diff --git a/subprojects/gst-plugins-bad/ext/closedcaption/gstccconverter.c b/subprojects/gst-plugins-bad/ext/closedcaption/gstccconverter.c index 88cfe12c49..d012e995b1 100644 --- a/subprojects/gst-plugins-bad/ext/closedcaption/gstccconverter.c +++ b/subprojects/gst-plugins-bad/ext/closedcaption/gstccconverter.c @@ -498,6 +498,14 @@ gst_cc_converter_set_caps (GstBaseTransform * base, GstCaps * incaps, "Got caps %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT " (passthrough %d)", incaps, outcaps, passthrough); + if (self->output_caption_type == GST_VIDEO_CAPTION_TYPE_CEA708_RAW + || self->output_caption_type == GST_VIDEO_CAPTION_TYPE_CEA708_CDP) { + cc_buffer_set_cea608_padding_strategy (self->cc_buffer, 0); + } else { + cc_buffer_set_cea608_padding_strategy (self->cc_buffer, + CC_BUFFER_CEA608_PADDING_STRATEGY_VALID); + } + return TRUE; invalid_caps: @@ -901,7 +909,7 @@ convert_cea608_raw_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf, tc_meta ? &tc_meta->tc : NULL, &self->current_output_timecode)) goto drop; - cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, TRUE, cc_data, + cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, cc_data, &cc_data_len); gst_buffer_map (outbuf, &out, GST_MAP_WRITE); @@ -1062,7 +1070,7 @@ convert_cea608_s334_1a_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf, tc_meta ? &tc_meta->tc : NULL, &self->current_output_timecode)) goto drop; - cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, TRUE, cc_data, + cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, cc_data, &cc_data_len); gst_buffer_map (outbuf, &out, GST_MAP_WRITE); @@ -1202,7 +1210,7 @@ convert_cea708_cc_data_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf, tc_meta ? &tc_meta->tc : NULL, &self->current_output_timecode)) goto drop; - cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, TRUE, cc_data, + cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, cc_data, &cc_data_len); gst_buffer_map (outbuf, &out, GST_MAP_WRITE); @@ -1296,8 +1304,8 @@ convert_cea708_cdp_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf, gst_buffer_map (outbuf, &out, GST_MAP_READWRITE); cc_data_len = out.size; - cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, FALSE, - out.data, &cc_data_len); + cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, out.data, + &cc_data_len); s334_len = drop_ccp_from_cc_data (out.data, cc_data_len); if (s334_len < 0) goto drop; @@ -1348,8 +1356,7 @@ convert_cea708_cdp_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf, gst_buffer_map (outbuf, &out, GST_MAP_WRITE); out_len = (guint) out.size; - cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, TRUE, out.data, - &out_len); + cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, out.data, &out_len); gst_buffer_unmap (outbuf, &out); self->output_frames++; @@ -1388,7 +1395,7 @@ convert_cea708_cdp_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf, &self->current_output_timecode)) goto out; - cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, TRUE, cc_data, + cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, cc_data, &cc_data_len); gst_buffer_map (outbuf, &out, GST_MAP_WRITE); @@ -1891,6 +1898,7 @@ gst_cc_converter_class_init (GstCCConverterClass * klass) 0, "Closed Caption converter"); gst_type_mark_as_plugin_api (GST_TYPE_CC_CONVERTER_CDP_MODE, 0); + gst_type_mark_as_plugin_api (GST_TYPE_CC_BUFFER_CEA608_PADDING_STRATEGY, 0); } static void @@ -1901,4 +1909,5 @@ gst_cc_converter_init (GstCCConverter * self) self->out_field = 0; self->cc_buffer = cc_buffer_new (); cc_buffer_set_output_padding (self->cc_buffer, TRUE, FALSE); + cc_buffer_set_cea608_padding_strategy (self->cc_buffer, 0); } diff --git a/subprojects/gst-plugins-bad/ext/closedcaption/gstcea608mux.c b/subprojects/gst-plugins-bad/ext/closedcaption/gstcea608mux.c index 1f5a3133a8..073f7583f7 100644 --- a/subprojects/gst-plugins-bad/ext/closedcaption/gstcea608mux.c +++ b/subprojects/gst-plugins-bad/ext/closedcaption/gstcea608mux.c @@ -190,7 +190,7 @@ take_s334_both_fields (GstCea608Mux * self, GstBuffer * buffer) gst_buffer_map (buffer, &out, GST_MAP_READWRITE); cc_data_len = out.size; - cc_buffer_take_cc_data (self->cc_buffer, self->cdp_fps_entry, FALSE, out.data, + cc_buffer_take_cc_data (self->cc_buffer, self->cdp_fps_entry, out.data, &cc_data_len); s334_len = drop_ccp_from_cc_data (out.data, cc_data_len); if (s334_len < 0) { @@ -438,6 +438,9 @@ gst_cea608_mux_init (GstCea608Mux * self) self->cc_buffer = cc_buffer_new (); cc_buffer_set_max_buffer_time (self->cc_buffer, GST_CLOCK_TIME_NONE); cc_buffer_set_output_padding (self->cc_buffer, TRUE, FALSE); + cc_buffer_set_cea608_padding_strategy (self->cc_buffer, + CC_BUFFER_CEA608_PADDING_STRATEGY_VALID | + CC_BUFFER_CEA608_PADDING_STRATEGY_INPUT_REMOVE); self->cdp_fps_entry = &null_fps_entry; self->start_time = GST_CLOCK_TIME_NONE; } diff --git a/subprojects/gst-plugins-bad/tests/check/elements/cccombiner.c b/subprojects/gst-plugins-bad/tests/check/elements/cccombiner.c index fa56cb7fed..236e45b295 100644 --- a/subprojects/gst-plugins-bad/tests/check/elements/cccombiner.c +++ b/subprojects/gst-plugins-bad/tests/check/elements/cccombiner.c @@ -210,6 +210,8 @@ GST_START_TEST (captions_no_output_padding_60fps_608_field1_only) int i = 0; h = gst_harness_new_with_padnames ("cccombiner", "sink", "src"); + gst_util_set_object_arg ((GObject *) h->element, "cea608-padding-strategy", + "0"); h2 = gst_harness_new_with_element (h->element, NULL, NULL); caption_pad = gst_element_request_pad_simple (h->element, "caption"); gst_harness_add_element_sink_pad (h2, caption_pad);