From 088597b4308a4fe1bd00f72fa4a67810d92d7651 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Wed, 1 Jun 2022 17:47:55 +1000 Subject: [PATCH] closedcaption: move CC buffering to helper object Move most of the interesting code from ccconverter to this new helper object. Part-of: --- .../gst-plugins-bad/ext/closedcaption/ccutils.c | 572 ++++++++++++ .../gst-plugins-bad/ext/closedcaption/ccutils.h | 47 + .../ext/closedcaption/gstccconverter.c | 981 +++------------------ .../ext/closedcaption/gstccconverter.h | 15 +- .../tests/check/elements/ccconverter.c | 32 +- 5 files changed, 768 insertions(+), 879 deletions(-) diff --git a/subprojects/gst-plugins-bad/ext/closedcaption/ccutils.c b/subprojects/gst-plugins-bad/ext/closedcaption/ccutils.c index cf1585b..c349a20 100644 --- a/subprojects/gst-plugins-bad/ext/closedcaption/ccutils.c +++ b/subprojects/gst-plugins-bad/ext/closedcaption/ccutils.c @@ -31,6 +31,8 @@ GST_DEBUG_CATEGORY (GST_CAT_DEFAULT); typedef struct cdp_fps_entry cdp_fps_entry; +#define VAL_OR_0(v) ((v) ? (*(v)) : 0) + static const struct cdp_fps_entry cdp_fps_table[] = { {0x1f, 24000, 1001, 25, 22, 3 /* FIXME: alternating max cea608 count! */ }, {0x2f, 24, 1, 25, 22, 2}, @@ -333,3 +335,573 @@ convert_cea708_cdp_to_cc_data (GstObject * dbg_obj, /* skip everything else we don't care about */ return len; } + +#define CC_DATA_EXTRACT_TOO_MANY_FIELD1 -2 +#define CC_DATA_EXTRACT_TOO_MANY_FIELD2 -3 + +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) +{ + guint i, field_1_len = 0, field_2_len = 0; + + if (cea608_field1_len) { + field_1_len = *cea608_field1_len; + *cea608_field1_len = 0; + } + if (cea608_field2_len) { + field_2_len = *cea608_field2_len; + *cea608_field2_len = 0; + } + + if (cc_data_len % 3 != 0) { + GST_WARNING ("Invalid cc_data buffer size %u. Truncating to a multiple " + "of 3", cc_data_len); + cc_data_len = cc_data_len - (cc_data_len % 3); + } + + for (i = 0; i < cc_data_len / 3; i++) { + guint8 byte0 = cc_data[i * 3 + 0]; + guint8 byte1 = cc_data[i * 3 + 1]; + guint8 byte2 = cc_data[i * 3 + 2]; + gboolean cc_valid = (byte0 & 0x04) == 0x04; + guint8 cc_type = byte0 & 0x03; + + GST_TRACE ("0x%02x 0x%02x 0x%02x, valid: %u, type: 0b%u%u", byte0, byte1, + byte2, cc_valid, (cc_type & 0x2) == 0x2, (cc_type & 0x1) == 0x1); + + if (cc_type == 0x00) { + if (!cc_valid) + continue; + + if (cea608_field1 && cea608_field1_len) { + if (*cea608_field1_len + 2 > field_1_len) { + GST_WARNING ("Too many cea608 input bytes %u for field 1", + *cea608_field1_len + 2); + return CC_DATA_EXTRACT_TOO_MANY_FIELD1; + } + + if (byte1 != 0x80 || byte2 != 0x80) { + cea608_field1[(*cea608_field1_len)++] = byte1; + cea608_field1[(*cea608_field1_len)++] = byte2; + } + } + } else if (cc_type == 0x01) { + if (!cc_valid) + continue; + + if (cea608_field2 && cea608_field2_len) { + if (*cea608_field2_len + 2 > field_2_len) { + GST_WARNING ("Too many cea608 input bytes %u for field 2", + *cea608_field2_len + 2); + return CC_DATA_EXTRACT_TOO_MANY_FIELD2; + } + if (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 */ + break; + } + } + + g_assert_cmpint (i * 3, <=, cc_data_len); + + GST_LOG ("Extracted cea608-1 of length %u and cea608-2 of length %u, " + "ccp_offset %i", VAL_OR_0 (cea608_field1_len), + VAL_OR_0 (cea608_field2_len), i * 3); + + return i * 3; +} + +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); +} + +#define DEFAULT_MAX_BUFFER_TIME (100 * GST_MSECOND) + +struct _CCBuffer +{ + GstObject parent; + GArray *cea608_1; + GArray *cea608_2; + GArray *cc_data; + /* used for tracking which field to write across output buffer boundaries */ + gboolean last_cea608_written_was_field1; + + GstClockTime max_buffer_time; +}; + +G_DEFINE_TYPE (CCBuffer, cc_buffer, G_TYPE_OBJECT); + +CCBuffer * +cc_buffer_new (void) +{ + return g_object_new (cc_buffer_get_type (), NULL); +} + +static void +cc_buffer_init (CCBuffer * buf) +{ + buf->cea608_1 = g_array_new (FALSE, FALSE, sizeof (guint8)); + buf->cea608_2 = g_array_new (FALSE, FALSE, sizeof (guint8)); + buf->cc_data = g_array_new (FALSE, FALSE, sizeof (guint8)); + + buf->max_buffer_time = DEFAULT_MAX_BUFFER_TIME; +} + +static void +cc_buffer_finalize (GObject * object) +{ + CCBuffer *buf = GST_CC_BUFFER (object); + + g_array_unref (buf->cea608_1); + buf->cea608_1 = NULL; + g_array_unref (buf->cea608_2); + buf->cea608_2 = NULL; + g_array_unref (buf->cc_data); + buf->cc_data = NULL; + + G_OBJECT_CLASS (cc_buffer_parent_class)->finalize (object); +} + +static void +cc_buffer_class_init (CCBufferClass * buf_class) +{ + GObjectClass *gobject_class = (GObjectClass *) buf_class; + + gobject_class->finalize = cc_buffer_finalize; +} + +/* remove padding bytes from a cc_data packet. Returns the length of the new + * data in @cc_data */ +static guint +compact_cc_data (guint8 * cc_data, guint cc_data_len) +{ + gboolean started_ccp = FALSE; + guint out_len = 0; + guint i; + + if (cc_data_len % 3 != 0) { + GST_WARNING ("Invalid cc_data buffer size"); + cc_data_len = cc_data_len - (cc_data_len % 3); + } + + for (i = 0; i < cc_data_len / 3; i++) { + gboolean cc_valid = (cc_data[i * 3] & 0x04) == 0x04; + guint8 cc_type = cc_data[i * 3] & 0x03; + + if (!started_ccp && (cc_type == 0x00 || cc_type == 0x01)) { + if (cc_valid) { + /* copy over valid 608 data */ + cc_data[out_len++] = cc_data[i * 3]; + cc_data[out_len++] = cc_data[i * 3 + 1]; + cc_data[out_len++] = cc_data[i * 3 + 2]; + } + continue; + } + + if (cc_type & 0x10) + started_ccp = TRUE; + + if (!cc_valid) + continue; + + if (cc_type == 0x00 || cc_type == 0x01) { + GST_WARNING ("Invalid cc_data. cea608 bytes after cea708"); + return 0; + } + + cc_data[out_len++] = cc_data[i * 3]; + cc_data[out_len++] = cc_data[i * 3 + 1]; + cc_data[out_len++] = cc_data[i * 3 + 2]; + } + + GST_LOG ("compacted cc_data from %u to %u", cc_data_len, out_len); + + return out_len; +} + +static guint +calculate_n_cea608_doubles_from_time_ceil (CCBuffer * buf, 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); + + return GST_ROUND_UP_2 (ret); +} + +static guint +calculate_n_cea708_doubles_from_time_ceil (CCBuffer * buf, 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); + + return GST_ROUND_UP_2 (ret); +} + +static void +push_internal (CCBuffer * buf, const guint8 * cea608_1, + guint cea608_1_len, const guint8 * cea608_2, guint cea608_2_len, + const guint8 * cc_data, guint cc_data_len) +{ + guint max_cea608_bytes; + + 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); + + if (cea608_1_len > 0) { + if (cea608_1_len + buf->cea608_1->len > max_cea608_bytes) { + GST_WARNING_OBJECT (buf, "cea608 field 1 overflow, dropping all " + "previous data, max %u, attempted to hold %u", max_cea608_bytes, + cea608_1_len + buf->cea608_1->len); + g_array_set_size (buf->cea608_1, 0); + } + g_array_append_vals (buf->cea608_1, cea608_1, cea608_1_len); + } + if (cea608_2_len > 0) { + if (cea608_2_len + buf->cea608_2->len > max_cea608_bytes) { + GST_WARNING_OBJECT (buf, "cea608 field 2 overflow, dropping all " + "previous data, max %u, attempted to hold %u", max_cea608_bytes, + cea608_2_len + buf->cea608_2->len); + g_array_set_size (buf->cea608_2, 0); + } + g_array_append_vals (buf->cea608_2, cea608_2, cea608_2_len); + } + if (cc_data_len > 0) { + guint max_cea708_bytes = + calculate_n_cea708_doubles_from_time_ceil (buf, 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, + cc_data_len + buf->cc_data->len); + g_array_set_size (buf->cea608_2, 0); + } + g_array_append_vals (buf->cc_data, cc_data, cc_data_len); + } +} + +void +cc_buffer_push_separated (CCBuffer * buf, const guint8 * cea608_1, + guint cea608_1_len, const guint8 * cea608_2, guint cea608_2_len, + const guint8 * cc_data, guint cc_data_len) +{ + guint8 cea608_1_copy[MAX_CEA608_LEN]; + guint8 cea608_2_copy[MAX_CEA608_LEN]; + guint8 cc_data_copy[MAX_CDP_PACKET_LEN]; + 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) { + cea608_1_copy[out_i++] = cea608_1[i]; + cea608_1_copy[out_i++] = cea608_1[i + 1]; + } + } + cea608_1_len = out_i; + } else { + cea608_1_len = 0; + } + + 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) { + cea608_2_copy[out_i++] = cea608_2[i]; + cea608_2_copy[out_i++] = cea608_2[i + 1]; + } + } + cea608_2_len = out_i; + } else { + cea608_2_len = 0; + } + + if (cc_data && cc_data_len > 0) { + memcpy (cc_data_copy, cc_data, cc_data_len); + cc_data_len = compact_cc_data (cc_data_copy, cc_data_len); + } else { + cc_data_len = 0; + } + + push_internal (buf, cea608_1_copy, cea608_1_len, cea608_2_copy, + cea608_2_len, cc_data_copy, cc_data_len); +} + +void +cc_buffer_push_cc_data (CCBuffer * buf, const guint8 * cc_data, + guint cc_data_len) +{ + guint8 cea608_1[MAX_CEA608_LEN]; + guint8 cea608_2[MAX_CEA608_LEN]; + guint8 cc_data_copy[MAX_CDP_PACKET_LEN]; + guint cea608_1_len = MAX_CEA608_LEN; + guint cea608_2_len = MAX_CEA608_LEN; + int ccp_offset; + + memcpy (cc_data_copy, cc_data, cc_data_len); + + 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); + + if (ccp_offset < 0) { + GST_WARNING_OBJECT (buf, "Failed to extract cea608 from cc_data"); + return; + } + + push_internal (buf, cea608_1, cea608_1_len, cea608_2, + cea608_2_len, &cc_data_copy[ccp_offset], cc_data_len - ccp_offset); +} + +void +cc_buffer_get_stored_size (CCBuffer * buf, guint * cea608_1_len, + guint * cea608_2_len, guint * cc_data_len) +{ + if (cea608_1_len) + *cea608_1_len = buf->cea608_1->len; + if (cea608_2_len) + *cea608_2_len = buf->cea608_2->len; + if (cc_data_len) + *cc_data_len = buf->cc_data->len; +} + +void +cc_buffer_discard (CCBuffer * buf) +{ + g_array_set_size (buf->cea608_1, 0); + g_array_set_size (buf->cea608_2, 0); + g_array_set_size (buf->cc_data, 0); +} + +#if 0 +void +cc_buffer_peek (CCBuffer * buf, guint8 ** cea608_1, guint * cea608_1_len, + guint8 ** cea608_2, guint * cea608_2_len, guint8 ** cc_data, + guint * cc_data_len) +{ + if (cea608_1_len) { + if (cea608_1) { + *cea608_1 = (guint8 *) buf->cea608_1->data; + } + *cea608_1_len = buf->cea608_1->len; + } + if (cea608_1_len) { + if (cea608_2) { + *cea608_2 = (guint8 *) buf->cea608_2->data; + } + *cea608_2_len = buf->cea608_2->len; + } + if (cc_data_len) { + if (cc_data) { + *cc_data = (guint8 *) buf->cc_data->data; + } + *cc_data_len = buf->cc_data->len; + } +} +#endif +static void +cc_buffer_get_out_sizes (CCBuffer * buf, const struct cdp_fps_entry *fps_entry, + guint * cea608_1_len, guint * field1_padding, guint * cea608_2_len, + guint * field2_padding, guint * cc_data_len) +{ + gint extra_ccp = 0, extra_cea608_1 = 0, extra_cea608_2 = 0; + gint write_ccp_size = 0, write_cea608_1_size = 0, write_cea608_2_size = 0; + gboolean wrote_first = FALSE; + + if (buf->cc_data->len) { + extra_ccp = buf->cc_data->len - 3 * fps_entry->max_ccp_count; + extra_ccp = MAX (0, extra_ccp); + write_ccp_size = buf->cc_data->len - extra_ccp; + } + + extra_cea608_1 = buf->cea608_1->len; + extra_cea608_2 = buf->cea608_2->len; + *field1_padding = 0; + *field2_padding = 0; + + wrote_first = !buf->last_cea608_written_was_field1; + /* try to push data into the packets. Anything 'extra' will be + * stored for later */ + while (TRUE) { + gint avail_1, avail_2; + + avail_1 = buf->cea608_1->len - extra_cea608_1 + *field1_padding; + avail_2 = buf->cea608_2->len - extra_cea608_2 + *field2_padding; + if (avail_1 + avail_2 >= 2 * fps_entry->max_cea608_count) + break; + + if (wrote_first) { + if (extra_cea608_1 > 0) { + extra_cea608_1 -= 2; + g_assert_cmpint (extra_cea608_1, >=, 0); + write_cea608_1_size += 2; + g_assert_cmpint (write_cea608_1_size, <=, buf->cea608_1->len); + } else { + *field1_padding += 2; + } + } + + avail_1 = buf->cea608_1->len - extra_cea608_1 + *field1_padding; + avail_2 = buf->cea608_2->len - extra_cea608_2 + *field2_padding; + if (avail_1 + avail_2 >= 2 * fps_entry->max_cea608_count) + break; + + if (extra_cea608_2 > 0) { + extra_cea608_2 -= 2; + g_assert_cmpint (extra_cea608_2, >=, 0); + write_cea608_2_size += 2; + g_assert_cmpint (write_cea608_2_size, <=, buf->cea608_2->len); + } else { + /* we need to insert field 2 padding if we don't have data and are + * requested to start with field2 */ + *field2_padding += 2; + } + wrote_first = TRUE; + } + + GST_TRACE_OBJECT (buf, "allocated sizes ccp:%u, cea608-1:%u (pad:%u), " + "cea608-2:%u (pad:%u)", write_ccp_size, write_cea608_1_size, + *field1_padding, write_cea608_2_size, *field2_padding); + + *cea608_1_len = write_cea608_1_size; + *cea608_2_len = write_cea608_2_size; + *cc_data_len = write_ccp_size; +} + +void +cc_buffer_take_separated (CCBuffer * buf, + const struct cdp_fps_entry *fps_entry, guint8 * cea608_1, + guint * cea608_1_len, guint8 * cea608_2, guint * cea608_2_len, + guint8 * cc_data, guint * cc_data_len) +{ + guint write_cea608_1_size, write_cea608_2_size, write_ccp_size; + guint field1_padding, field2_padding; + + cc_buffer_get_out_sizes (buf, fps_entry, &write_cea608_1_size, + &field1_padding, &write_cea608_2_size, &field2_padding, &write_ccp_size); + + if (cea608_1_len) { + if (*cea608_1_len < write_cea608_1_size + field1_padding) { + GST_WARNING_OBJECT (buf, "output cea608 field 1 buffer (%u) is too " + "small to hold output (%u)", *cea608_1_len, + write_cea608_1_size + field1_padding); + *cea608_1_len = 0; + } 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); + *cea608_1_len = write_cea608_1_size + field1_padding; + } else { + *cea608_1_len = 0; + } + } + if (cea608_2_len) { + if (*cea608_2_len < write_cea608_2_size + field2_padding) { + GST_WARNING_OBJECT (buf, "output cea608 field 2 buffer (%u) is too " + "small to hold output (%u)", *cea608_2_len, write_cea608_2_size); + *cea608_2_len = 0; + } 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); + *cea608_2_len = write_cea608_2_size + field2_padding; + } else { + *cea608_2_len = 0; + } + } + if (cc_data_len) { + if (*cc_data_len < write_ccp_size) { + GST_WARNING_OBJECT (buf, "output ccp buffer (%u) is too " + "small to hold output (%u)", *cc_data_len, write_ccp_size); + *cc_data_len = 0; + } else if (cc_data) { + memcpy (cc_data, buf->cc_data->data, write_ccp_size); + *cc_data_len = write_ccp_size; + } else { + *cc_data_len = 0; + } + } + + g_array_remove_range (buf->cea608_1, 0, write_cea608_1_size); + g_array_remove_range (buf->cea608_2, 0, write_cea608_2_size); + g_array_remove_range (buf->cc_data, 0, write_ccp_size); + + GST_LOG_OBJECT (buf, "bytes currently stored, cea608-1:%u, cea608-2:%u " + "ccp:%u", buf->cea608_1->len, buf->cea608_2->len, buf->cc_data->len); +} + +void +cc_buffer_take_cc_data (CCBuffer * buf, + 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; + + cc_buffer_get_out_sizes (buf, fps_entry, &write_cea608_1_size, + &field1_padding, &write_cea608_2_size, &field2_padding, &write_ccp_size); + + { + guint cea608_1_i = 0, cea608_2_i = 0; + guint out_i = 0; + guint8 *cea608_1 = (guint8 *) buf->cea608_1->data; + guint8 *cea608_2 = (guint8 *) buf->cea608_2->data; + guint cea608_output_count = + write_cea608_1_size + write_cea608_2_size + field1_padding + + field2_padding; + + wrote_first = !buf->last_cea608_written_was_field1; + while (cea608_1_i + cea608_2_i < cea608_output_count) { + if (wrote_first) { + if (cea608_1_i < write_cea608_1_size) { + cc_data[out_i++] = 0xfc; + cc_data[out_i++] = cea608_1[cea608_1_i]; + cc_data[out_i++] = cea608_1[cea608_1_i + 1]; + cea608_1_i += 2; + buf->last_cea608_written_was_field1 = TRUE; + } else if (cea608_1_i < write_cea608_1_size + field1_padding) { + cc_data[out_i++] = 0xf8; + cc_data[out_i++] = 0x80; + cc_data[out_i++] = 0x80; + cea608_1_i += 2; + buf->last_cea608_written_was_field1 = TRUE; + } + } + + if (cea608_2_i < write_cea608_2_size) { + cc_data[out_i++] = 0xfd; + cc_data[out_i++] = cea608_2[cea608_2_i]; + cc_data[out_i++] = cea608_2[cea608_2_i + 1]; + cea608_2_i += 2; + buf->last_cea608_written_was_field1 = FALSE; + } else if (cea608_2_i < write_cea608_2_size + field2_padding) { + cc_data[out_i++] = 0xf9; + cc_data[out_i++] = 0x80; + cc_data[out_i++] = 0x80; + cea608_2_i += 2; + buf->last_cea608_written_was_field1 = FALSE; + } + + wrote_first = TRUE; + } + + if (write_ccp_size > 0) + memcpy (&cc_data[out_i], buf->cc_data->data, write_ccp_size); + *cc_data_len = out_i + write_ccp_size; + } + + g_array_remove_range (buf->cea608_1, 0, write_cea608_1_size); + g_array_remove_range (buf->cea608_2, 0, write_cea608_2_size); + g_array_remove_range (buf->cc_data, 0, write_ccp_size); + + GST_LOG_OBJECT (buf, "bytes currently stored, cea608-1:%u, cea608-2:%u " + "ccp:%u", buf->cea608_1->len, buf->cea608_2->len, buf->cc_data->len); +} diff --git a/subprojects/gst-plugins-bad/ext/closedcaption/ccutils.h b/subprojects/gst-plugins-bad/ext/closedcaption/ccutils.h index 391d12f..c9903bc 100644 --- a/subprojects/gst-plugins-bad/ext/closedcaption/ccutils.h +++ b/subprojects/gst-plugins-bad/ext/closedcaption/ccutils.h @@ -50,6 +50,7 @@ typedef enum { GST_CC_CDP_MODE_CC_SVC_INFO = (1<<2) } GstCCCDPMode; +G_GNUC_INTERNAL guint convert_cea708_cc_data_to_cdp (GstObject * dbg_obj, GstCCCDPMode cdp_mode, guint16 cdp_hdr_sequence_cntr, @@ -60,14 +61,60 @@ guint convert_cea708_cc_data_to_cdp (GstObject * dbg_obj, const GstVideoTimeCode * tc, const struct cdp_fps_entry *fps_entry); +G_GNUC_INTERNAL guint convert_cea708_cdp_to_cc_data (GstObject * dbg_obj, const guint8 * cdp, guint cdp_len, guint8 *cc_data, GstVideoTimeCode * tc, const struct cdp_fps_entry **out_fps_entry); +G_GNUC_INTERNAL +gint drop_ccp_from_cc_data (guint8 * cc_data, + guint cc_data_len); #define MAX_CDP_PACKET_LEN 256 +#define MAX_CEA608_LEN 32 + +G_DECLARE_FINAL_TYPE (CCBuffer, cc_buffer, GST, CC_BUFFER, GObject); + +G_GNUC_INTERNAL +CCBuffer * cc_buffer_new (void); +G_GNUC_INTERNAL +void cc_buffer_get_stored_size (CCBuffer * buf, + guint * cea608_1_len, + guint * cea608_2_len, + guint * cc_data_len); +G_GNUC_INTERNAL +void cc_buffer_push_separated (CCBuffer * buf, + const guint8 * cea608_1, + guint cea608_1_len, + const guint8 * cea608_2, + guint cea608_2_len, + const guint8 * cc_data, + guint cc_data_len); +G_GNUC_INTERNAL +void cc_buffer_push_cc_data (CCBuffer * buf, + const guint8 * cc_data, + guint cc_data_len); +G_GNUC_INTERNAL +void cc_buffer_take_cc_data (CCBuffer * buf, + const struct cdp_fps_entry * fps_entry, + guint8 * cc_data, + guint * cc_data_len); +G_GNUC_INTERNAL +void cc_buffer_take_separated (CCBuffer * buf, + const struct cdp_fps_entry * fps_entry, + guint8 * cea608_1, + guint * cea608_1_len, + guint8 * cea608_2, + guint * cea608_2_len, + guint8 * cc_data, + guint * cc_data_len); +G_GNUC_INTERNAL +void cc_buffer_discard (CCBuffer * buf); +G_GNUC_INTERNAL +void cc_buffer_set_max_buffer_time (CCBuffer * buf, + GstClockTime max_time); G_END_DECLS diff --git a/subprojects/gst-plugins-bad/ext/closedcaption/gstccconverter.c b/subprojects/gst-plugins-bad/ext/closedcaption/gstccconverter.c index c87aeab..14ac7c6 100644 --- a/subprojects/gst-plugins-bad/ext/closedcaption/gstccconverter.c +++ b/subprojects/gst-plugins-bad/ext/closedcaption/gstccconverter.c @@ -499,7 +499,6 @@ interpolate_time_code_with_framerate (GstCCConverter * self, guint output_frame; GstVideoTimeCodeFlags flags; - g_return_val_if_fail (tc != NULL, FALSE); g_return_val_if_fail (out != NULL, FALSE); /* out_n/d can only be 0 if scale_n/d are 1/1 */ g_return_val_if_fail ((scale_n == 1 && scale_d == 1) || (out_fps_n != 0 @@ -556,552 +555,59 @@ interpolate_time_code_with_framerate (GstCCConverter * self, return TRUE; } -/* remove padding bytes from a cc_data packet. Returns the length of the new - * data in @cc_data */ -static guint -compact_cc_data (guint8 * cc_data, guint cc_data_len) -{ - gboolean started_ccp = FALSE; - guint out_len = 0; - guint i; - - if (cc_data_len % 3 != 0) { - GST_WARNING ("Invalid cc_data buffer size"); - cc_data_len = cc_data_len - (cc_data_len % 3); - } - - for (i = 0; i < cc_data_len / 3; i++) { - gboolean cc_valid = (cc_data[i * 3] & 0x04) == 0x04; - guint8 cc_type = cc_data[i * 3] & 0x03; - - if (!started_ccp && (cc_type == 0x00 || cc_type == 0x01)) { - if (cc_valid) { - /* copy over valid 608 data */ - cc_data[out_len++] = cc_data[i * 3]; - cc_data[out_len++] = cc_data[i * 3 + 1]; - cc_data[out_len++] = cc_data[i * 3 + 2]; - } - continue; - } - - if (cc_type & 0x10) - started_ccp = TRUE; - - if (!cc_valid) - continue; - - if (cc_type == 0x00 || cc_type == 0x01) { - GST_WARNING ("Invalid cc_data. cea608 bytes after cea708"); - return 0; - } - - cc_data[out_len++] = cc_data[i * 3]; - cc_data[out_len++] = cc_data[i * 3 + 1]; - cc_data[out_len++] = cc_data[i * 3 + 2]; - } - - GST_LOG ("compacted cc_data from %u to %u", cc_data_len, out_len); - - return out_len; -} - -#define CC_DATA_EXTRACT_TOO_MANY_FIELD1 -2 -#define CC_DATA_EXTRACT_TOO_MANY_FIELD2 -3 - -static gint -cc_data_extract_cea608 (guint8 * cc_data, guint cc_data_len, - guint8 * cea608_field1, guint * cea608_field1_len, - guint8 * cea608_field2, guint * cea608_field2_len) -{ - guint i, field_1_len = 0, field_2_len = 0; - - if (cea608_field1_len) { - field_1_len = *cea608_field1_len; - *cea608_field1_len = 0; - } - if (cea608_field2_len) { - field_2_len = *cea608_field2_len; - *cea608_field2_len = 0; - } - - if (cc_data_len % 3 != 0) { - GST_WARNING ("Invalid cc_data buffer size %u. Truncating to a multiple " - "of 3", cc_data_len); - cc_data_len = cc_data_len - (cc_data_len % 3); - } - - for (i = 0; i < cc_data_len / 3; i++) { - guint8 byte0 = cc_data[i * 3 + 0]; - guint8 byte1 = cc_data[i * 3 + 1]; - guint8 byte2 = cc_data[i * 3 + 2]; - gboolean cc_valid = (byte0 & 0x04) == 0x04; - guint8 cc_type = byte0 & 0x03; - - GST_TRACE ("0x%02x 0x%02x 0x%02x, valid: %u, type: 0b%u%u", byte0, byte1, - byte2, cc_valid, (cc_type & 0x2) == 0x2, (cc_type & 0x1) == 0x1); - - if (cc_type == 0x00) { - if (!cc_valid) - continue; - - if (cea608_field1 && cea608_field1_len) { - if (*cea608_field1_len + 2 > field_1_len) { - GST_WARNING ("Too many cea608 input bytes %u for field 1", - *cea608_field1_len + 2); - return CC_DATA_EXTRACT_TOO_MANY_FIELD1; - } - - if (byte1 != 0x80 || byte2 != 0x80) { - cea608_field1[(*cea608_field1_len)++] = byte1; - cea608_field1[(*cea608_field1_len)++] = byte2; - } - } - } else if (cc_type == 0x01) { - if (!cc_valid) - continue; - - if (cea608_field2 && cea608_field2_len) { - if (*cea608_field2_len + 2 > field_2_len) { - GST_WARNING ("Too many cea608 input bytes %u for field 2", - *cea608_field2_len + 2); - return CC_DATA_EXTRACT_TOO_MANY_FIELD2; - } - if (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 */ - break; - } - } - - g_assert_cmpint (i * 3, <=, cc_data_len); - - GST_LOG ("Extracted cea608-1 of length %u and cea608-2 of length %u", - VAL_OR_0 (cea608_field1_len), VAL_OR_0 (cea608_field2_len)); - - return i * 3; -} - -static void -store_cc_data (GstCCConverter * self, const guint8 * ccp_data, - guint ccp_data_len, const guint8 * cea608_1, guint cea608_1_len, - const guint8 * cea608_2, guint cea608_2_len) -{ - GST_TRACE_OBJECT (self, "attempting to hold data of len ccp:%u, cea608 1:%u, " - "cea608 2:%u until next output buffer", ccp_data_len, cea608_1_len, - cea608_2_len); - - if (ccp_data && ccp_data_len > 0) { - if (ccp_data_len > sizeof (self->scratch_ccp)) { - GST_ELEMENT_WARNING (self, STREAM, DECODE, - ("Closed Caption internal buffer overun. Dropping data"), - ("CCP scratch buffer requires space for %u bytes but only %" - G_GSIZE_FORMAT " bytes are available", ccp_data_len, - sizeof (self->scratch_ccp))); - self->scratch_ccp_len = 0; - } else { - memcpy (self->scratch_ccp, ccp_data, ccp_data_len); - self->scratch_ccp_len = ccp_data_len; - } - } else { - self->scratch_ccp_len = 0; - } - g_assert_cmpint (self->scratch_ccp_len, <=, sizeof (self->scratch_ccp)); - - if (cea608_1 && cea608_1_len > 0) { - if (cea608_1_len > sizeof (self->scratch_cea608_1)) { - GST_ELEMENT_WARNING (self, STREAM, DECODE, - ("Closed Caption internal buffer overun. Dropping data"), - ("CEA608 field 1 scratch buffer requires space for %u bytes but " - "only %" G_GSIZE_FORMAT " bytes are available", cea608_1_len, - sizeof (self->scratch_cea608_1_len))); - self->scratch_cea608_1_len = 0; - } else { - memcpy (self->scratch_cea608_1, cea608_1, cea608_1_len); - self->scratch_cea608_1_len = cea608_1_len; - } - } else { - self->scratch_cea608_1_len = 0; - } - g_assert_cmpint (self->scratch_cea608_1_len, <=, - sizeof (self->scratch_cea608_1)); - - if (cea608_2 && cea608_2_len > 0) { - if (cea608_2_len > sizeof (self->scratch_cea608_2)) { - GST_ELEMENT_WARNING (self, STREAM, DECODE, - ("Closed Caption internal buffer overun. Dropping data"), - ("CEA608 field 2 scratch buffer requires space for %u bytes but " - "only %" G_GSIZE_FORMAT " bytes are available", cea608_2_len, - sizeof (self->scratch_cea608_2_len))); - self->scratch_cea608_2_len = 0; - } else { - memcpy (self->scratch_cea608_2, cea608_2, cea608_2_len); - self->scratch_cea608_2_len = cea608_2_len; - } - } else { - self->scratch_cea608_2_len = 0; - } - g_assert_cmpint (self->scratch_cea608_2_len, <=, - sizeof (self->scratch_cea608_2)); - - GST_DEBUG_OBJECT (self, "holding data of len ccp:%u, cea608 1:%u, " - "cea608 2:%u until next output buffer", self->scratch_ccp_len, - self->scratch_cea608_1_len, self->scratch_cea608_2_len); -} - static gboolean -write_cea608 (GstCCConverter * self, gboolean pad_cea608, - const struct cdp_fps_entry *out_fps_entry, const guint8 * cea608_1, - guint cea608_1_len, const guint8 * cea608_2, guint cea608_2_len, - guint8 * out, guint * out_size, gboolean * is_last_cea608_field1) +can_take_buffer (GstCCConverter * self, + const struct cdp_fps_entry *in_fps_entry, + const struct cdp_fps_entry *out_fps_entry, + const GstVideoTimeCode * in_tc, GstVideoTimeCode * out_tc) { - guint i = 0, out_i = 0, max_size = 0, cea608_1_i = 0, cea608_2_i = 0; - guint cea608_output_count; - guint total_cea608_1_count, total_cea608_2_count; - gboolean write_cea608_field2_first = FALSE; - gboolean wrote_field1_last = FALSE; - gboolean wrote_first = FALSE; - - g_assert (out); - g_assert (out_size); - g_assert (!cea608_1 || cea608_1_len % 2 == 0); - g_assert (!cea608_2 || cea608_2_len % 2 == 0); - g_assert (cea608_1 || cea608_2); - - if (is_last_cea608_field1) - write_cea608_field2_first = *is_last_cea608_field1; - - cea608_1_len /= 2; - cea608_2_len /= 2; -#if 0 - /* FIXME: if cea608 field 2 is generated, field 1 needs to be generated, - * However that is not possible for 60fps (where only one cea608 field fits) - * without adding previous output buffer tracking */ - g_assert_cmpint (cea608_1_len >= cea608_2_len); -#endif - g_assert_cmpint (cea608_1_len + cea608_2_len, <=, - out_fps_entry->max_cea608_count); + int input_frame_n, input_frame_d, output_frame_n, output_frame_d; + int output_time_cmp, scale_n, scale_d; -#if 0 - /* FIXME: if cea608 field 2 is generated, field 1 needs to be generated. */ - if (cea608_1_len < cea608_2_len) - total_cea608_1_count += cea608_2_len - cea608_1_len; -#endif + /* TODO: handle input discont */ - if (pad_cea608) { - max_size = out_fps_entry->max_cea608_count * 3; + if (self->in_fps_n == 0) { + input_frame_n = self->input_frames; + input_frame_d = 1; } else { - max_size = (cea608_1_len + cea608_2_len) * 3; - } - if (*out_size < max_size) { - GST_WARNING_OBJECT (self, "Output data too small (%u < %u) for cea608 data", - *out_size, max_size); - return FALSE; - } - - /* FIXME: interlacing, tff, rff, ensuring cea608 field1 is generated if - * field2 exists even across packets */ - - total_cea608_1_count = cea608_1_len; - total_cea608_2_count = cea608_2_len; - cea608_output_count = cea608_1_len + cea608_2_len; - if (pad_cea608) { - guint max_cea608_count = out_fps_entry->max_cea608_count; - - total_cea608_1_count = max_cea608_count / 2; - total_cea608_2_count = max_cea608_count / 2; - - if (total_cea608_1_count + total_cea608_2_count < max_cea608_count) { - if (write_cea608_field2_first) { - total_cea608_2_count++; - } else { - total_cea608_1_count++; - } - } - - cea608_output_count = total_cea608_1_count + total_cea608_2_count; - } - - GST_LOG ("writing %u cea608-1 fields and %u cea608-2 fields", - total_cea608_1_count, total_cea608_2_count); - g_assert_cmpint (total_cea608_1_count + total_cea608_2_count, <=, - out_fps_entry->max_cea608_count); - - wrote_first = !write_cea608_field2_first; - while (cea608_1_i + cea608_2_i < cea608_output_count) { - if (wrote_first) { - if (cea608_1_i < cea608_1_len) { - out[out_i++] = 0xfc; - out[out_i++] = cea608_1[cea608_1_i * 2]; - out[out_i++] = cea608_1[cea608_1_i * 2 + 1]; - cea608_1_i++; - i++; - wrote_field1_last = TRUE; - } else if (cea608_1_i < total_cea608_1_count) { - out[out_i++] = 0xf8; - out[out_i++] = 0x80; - out[out_i++] = 0x80; - cea608_1_i++; - i++; - wrote_field1_last = TRUE; - } - } - - if (cea608_2_i < cea608_2_len) { - out[out_i++] = 0xfd; - out[out_i++] = cea608_2[cea608_2_i * 2]; - out[out_i++] = cea608_2[cea608_2_i * 2 + 1]; - cea608_2_i++; - i++; - wrote_field1_last = FALSE; - } else if (cea608_2_i < total_cea608_2_count) { - out[out_i++] = 0xf9; - out[out_i++] = 0x80; - out[out_i++] = 0x80; - cea608_2_i++; - i++; - wrote_field1_last = FALSE; - } - - wrote_first = TRUE; + /* compute the relative frame count for each */ + if (!gst_util_fraction_multiply (self->in_fps_d, self->in_fps_n, + self->input_frames, 1, &input_frame_n, &input_frame_d)) + /* we should never overflow */ + g_assert_not_reached (); } - g_assert_cmpint (out_i / 3, <=, out_fps_entry->max_cea608_count); - - *out_size = out_i; - if (is_last_cea608_field1) - *is_last_cea608_field1 = wrote_field1_last; - - return TRUE; -} - -static gboolean -combine_cc_data (GstCCConverter * self, gboolean pad_cea608, - const struct cdp_fps_entry *out_fps_entry, const guint8 * ccp_data, - guint ccp_data_len, const guint8 * cea608_1, guint cea608_1_len, - const guint8 * cea608_2, guint cea608_2_len, guint8 * out, guint * out_size, - gboolean * last_cea608_is_field1) -{ - guint out_i = 0, max_size = 0; - guint cea608_size = *out_size; - - g_assert (out); - g_assert (out_size); - g_assert (!ccp_data || ccp_data_len % 3 == 0); - - if (cea608_1 || cea608_2) { - if (!write_cea608 (self, pad_cea608, out_fps_entry, cea608_1, cea608_1_len, - cea608_2, cea608_2_len, out, &cea608_size, last_cea608_is_field1)) - return FALSE; + if (self->in_fps_n == 0) { + output_frame_n = self->output_frames; + output_frame_d = 1; } else { - cea608_size = 0; - } - - max_size = ccp_data_len + cea608_size; - if (*out_size < max_size) { - GST_WARNING_OBJECT (self, "Output data too small (%u < %u)", *out_size, - max_size); - return FALSE; - } - - *out_size = cea608_size; - out_i = cea608_size; - if (ccp_data) { - memcpy (&out[out_i], ccp_data, ccp_data_len); - *out_size += ccp_data_len; - } - - g_assert_cmpint (*out_size, <, MAX_CDP_PACKET_LEN); - - return TRUE; -} - -/* takes cc_data cea608_1, cea608_2 and attempts to fit it into a hypothetical - * output packet. Any leftover data is stored for later addition. Returns - * whether any output can be generated. @ccp_data_len, @cea608_1_len, - * @cea608_2_len are also updated to reflect the size of that data to add to - * the output packet */ -static gboolean -fit_and_scale_cc_data (GstCCConverter * self, - const struct cdp_fps_entry *in_fps_entry, - const struct cdp_fps_entry *out_fps_entry, const guint8 * ccp_data, - guint * ccp_data_len, const guint8 * cea608_1, guint * cea608_1_len, - const guint8 * cea608_2, guint * cea608_2_len, const GstVideoTimeCode * tc, - gboolean use_cea608_field2_first) -{ - if (!in_fps_entry || in_fps_entry->fps_n == 0) { - in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d); - if (!in_fps_entry || in_fps_entry->fps_n == 0) + if (!gst_util_fraction_multiply (self->out_fps_d, self->out_fps_n, + self->output_frames, 1, &output_frame_n, &output_frame_d)) + /* we should never overflow */ g_assert_not_reached (); } - /* This is slightly looser than checking for the exact framerate as the cdp - * spec allow for 0.1% difference between framerates to be considered equal */ - { - int input_frame_n, input_frame_d, output_frame_n, output_frame_d; - int output_time_cmp, scale_n, scale_d; - - /* TODO: handle input discont */ - - if (self->in_fps_n == 0) { - input_frame_n = self->input_frames; - input_frame_d = 1; - } else { - /* compute the relative frame count for each */ - if (!gst_util_fraction_multiply (self->in_fps_d, self->in_fps_n, - self->input_frames, 1, &input_frame_n, &input_frame_d)) - /* we should never overflow */ - g_assert_not_reached (); - } - - if (self->in_fps_n == 0) { - output_frame_n = self->output_frames; - output_frame_d = 1; - } else { - if (!gst_util_fraction_multiply (self->out_fps_d, self->out_fps_n, - self->output_frames, 1, &output_frame_n, &output_frame_d)) - /* we should never overflow */ - g_assert_not_reached (); - } - - output_time_cmp = gst_util_fraction_compare (input_frame_n, input_frame_d, - output_frame_n, output_frame_d); - - /* compute the relative rates of the two framerates */ - get_framerate_output_scale (self, in_fps_entry, &scale_n, &scale_d); - - GST_TRACE_OBJECT (self, "performing conversion at scale %d/%d " - "of cc data of with sizes, ccp:%u, cea608-1:%u, cea608-2:%u", scale_n, - scale_d, VAL_OR_0 (ccp_data_len), VAL_OR_0 (cea608_1_len), - VAL_OR_0 (cea608_2_len)); - - if (output_time_cmp < 0) { - /* we can't generate an output yet */ - guint cd_len = ccp_data_len ? *ccp_data_len : 0; - guint c1_len = cea608_1_len ? *cea608_1_len : 0; - guint c2_len = cea608_2_len ? *cea608_2_len : 0; - - store_cc_data (self, ccp_data, cd_len, cea608_1, c1_len, cea608_2, - c2_len); - if (ccp_data_len) - *ccp_data_len = 0; - if (cea608_1_len) - *cea608_1_len = 0; - if (cea608_2_len) - *cea608_2_len = 0; - return FALSE; - } else { - /* we are changing the framerate and may overflow the max output packet - * size. Split them where necessary. */ - gint extra_ccp = 0, extra_cea608_1 = 0, extra_cea608_2 = 0; - gint ccp_off = 0, cea608_1_off = 0, cea608_2_off = 0; - gboolean wrote_first = FALSE; - gint field2_padding = 0; - - if (output_time_cmp == 0) { - /* we have completed a cycle and can reset our counters to avoid - * overflow. Anything that fits into the output packet will be written */ - GST_LOG_OBJECT (self, "cycle completed, resetting frame counters"); - self->scratch_ccp_len = 0; - self->scratch_cea608_1_len = 0; - self->scratch_cea608_2_len = 0; - self->input_frames = 0; - self->output_frames = 0; - } - - if (ccp_data_len) { - extra_ccp = *ccp_data_len - 3 * out_fps_entry->max_ccp_count; - extra_ccp = MAX (0, extra_ccp); - ccp_off = *ccp_data_len - extra_ccp; - } - - extra_cea608_1 = VAL_OR_0 (cea608_1_len); - extra_cea608_2 = VAL_OR_0 (cea608_2_len); - - wrote_first = !use_cea608_field2_first; - /* try to push data into the packets. Anything 'extra' will be - * stored for later */ - while (extra_cea608_1 > 0 || extra_cea608_2 > 0) { - gint avail_1, avail_2; - - avail_1 = VAL_OR_0 (cea608_1_len) - extra_cea608_1; - avail_2 = VAL_OR_0 (cea608_2_len) - extra_cea608_2; - if (avail_1 + avail_2 + field2_padding >= - 2 * out_fps_entry->max_cea608_count) - break; - - if (wrote_first && extra_cea608_1 > 0) { - extra_cea608_1 -= 2; - g_assert_cmpint (extra_cea608_1, >=, 0); - cea608_1_off += 2; - g_assert_cmpint (cea608_1_off, <=, *cea608_1_len); - } + output_time_cmp = gst_util_fraction_compare (input_frame_n, input_frame_d, + output_frame_n, output_frame_d); - avail_1 = VAL_OR_0 (cea608_1_len) - extra_cea608_1; - avail_2 = VAL_OR_0 (cea608_2_len) - extra_cea608_2; - if (avail_1 + avail_2 + field2_padding >= - 2 * out_fps_entry->max_cea608_count) - break; + in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d); + if (!in_fps_entry || in_fps_entry->fps_n == 0) + g_assert_not_reached (); - if (extra_cea608_2 > 0) { - extra_cea608_2 -= 2; - g_assert_cmpint (extra_cea608_2, >=, 0); - cea608_2_off += 2; - g_assert_cmpint (cea608_2_off, <=, *cea608_2_len); - } else if (!wrote_first) { - /* we need to insert field 2 padding if we don't have data and are - * requested to start with field2 */ - field2_padding += 2; - } - wrote_first = TRUE; - } + /* compute the relative rates of the two framerates */ + get_framerate_output_scale (self, in_fps_entry, &scale_n, &scale_d); - GST_TRACE_OBJECT (self, "allocated sizes ccp:%u, cea608-1:%u, " - "cea608-2:%u", ccp_off, cea608_1_off, cea608_2_off); - - if (extra_ccp > 0 || extra_cea608_1 > 0 || extra_cea608_2 > 0) { - /* packet would overflow, push extra bytes into the next packet */ - GST_DEBUG_OBJECT (self, "buffer would overflow by %u ccp bytes, " - "%u cea608 field 1 bytes, or %u cea608 field 2 bytes", extra_ccp, - extra_cea608_1, extra_cea608_2); - store_cc_data (self, &ccp_data[ccp_off], extra_ccp, - &cea608_1[cea608_1_off], extra_cea608_1, &cea608_2[cea608_2_off], - extra_cea608_2); - if (ccp_data_len) - *ccp_data_len = MIN (*ccp_data_len, ccp_off); - if (cea608_1_len) - *cea608_1_len = MIN (*cea608_1_len, cea608_1_off); - if (cea608_2_len) - *cea608_2_len = MIN (*cea608_2_len, cea608_2_off); - } else { - GST_DEBUG_OBJECT (self, "section sizes of %u ccp bytes, " - "%u cea608 field 1 bytes, and %u cea608 field 2 bytes fit within " - "output packet", VAL_OR_0 (ccp_data_len), VAL_OR_0 (cea608_1_len), - VAL_OR_0 (cea608_2_len)); - self->scratch_ccp_len = 0; - self->scratch_cea608_1_len = 0; - self->scratch_cea608_2_len = 0; - } - } + GST_TRACE_OBJECT (self, "performing conversion at scale %d/%d, " + "time comparison %i", scale_n, scale_d, output_time_cmp); - if (tc && tc->config.fps_n != 0) - interpolate_time_code_with_framerate (self, tc, out_fps_entry->fps_n, - out_fps_entry->fps_d, scale_n, scale_d, - &self->current_output_timecode); + if (output_time_cmp < 0) { + /* we can't generate an output yet */ + return FALSE; + } else { + interpolate_time_code_with_framerate (self, in_tc, out_fps_entry->fps_n, + out_fps_entry->fps_d, scale_n, scale_d, out_tc); + return TRUE; } - - g_assert_cmpint (VAL_OR_0 (ccp_data_len) + (VAL_OR_0 (cea608_1_len) + - VAL_OR_0 (cea608_2_len)) / 2 * 3, <=, - 3 * out_fps_entry->max_cc_count); - - GST_DEBUG_OBJECT (self, "write out packet with lengths ccp:%u, cea608-1:%u, " - "cea608-2:%u", VAL_OR_0 (ccp_data_len), VAL_OR_0 (cea608_1_len), - VAL_OR_0 (cea608_2_len)); - - return TRUE; } static guint @@ -1120,192 +626,8 @@ convert_cea708_cc_data_cea708_cdp_internal (GstCCConverter * self, } static gboolean -copy_from_stored_data (GstCCConverter * self, guint8 * out_ccp, - guint * ccp_size, guint8 * cea608_1, guint * cea608_1_len, - guint8 * cea608_2, guint * cea608_2_len) -{ - guint ccp_in_size = 0, cea608_1_in_size = 0, cea608_2_in_size = 0; - - g_assert ((out_ccp && ccp_size) || (!out_ccp && !ccp_size)); - g_assert ((cea608_1 && cea608_1_len) || (!cea608_1 && !cea608_1_len)); - g_assert ((cea608_2 && cea608_2_len) || (!cea608_2 && !cea608_2_len)); - - if (ccp_size) { - ccp_in_size = *ccp_size; - *ccp_size = 0; - } - if (cea608_1_len) { - cea608_1_in_size = *cea608_1_len; - *cea608_1_len = 0; - } - if (cea608_2_len) { - cea608_2_in_size = *cea608_2_len; - *cea608_2_len = 0; - } - - if (out_ccp && self->scratch_ccp_len > 0) { - GST_DEBUG_OBJECT (self, "copying from previous scratch ccp buffer of " - "%u bytes", self->scratch_ccp_len); - if (ccp_in_size < *ccp_size + self->scratch_ccp_len) { - GST_WARNING_OBJECT (self, "output buffer too small %u < %u", ccp_in_size, - *ccp_size + self->scratch_ccp_len); - goto fail; - } - memcpy (&out_ccp[*ccp_size], self->scratch_ccp, self->scratch_ccp_len); - *ccp_size += self->scratch_ccp_len; - } - - if (cea608_1 && self->scratch_cea608_1_len > 0) { - GST_DEBUG_OBJECT (self, "copying from previous scratch cea608 field 1 " - "buffer of %u bytes", self->scratch_cea608_1_len); - if (cea608_1_in_size < *cea608_1_len + self->scratch_cea608_1_len) { - GST_WARNING_OBJECT (self, "output buffer too small %u < %u", - cea608_1_in_size, *cea608_1_len + self->scratch_cea608_1_len); - goto fail; - } - memcpy (&cea608_1[*cea608_1_len], self->scratch_cea608_1, - self->scratch_cea608_1_len); - *cea608_1_len += self->scratch_cea608_1_len; - } - - if (cea608_2 && self->scratch_cea608_2_len > 0) { - GST_DEBUG_OBJECT (self, "copying from previous scratch cea608 field 2 " - "buffer of %u bytes", self->scratch_cea608_2_len); - if (cea608_2_in_size < *cea608_2_len + self->scratch_cea608_2_len) { - GST_WARNING_OBJECT (self, "output buffer too small %u < %u", - cea608_2_in_size, *cea608_2_len + self->scratch_cea608_2_len); - goto fail; - } - memcpy (&cea608_2[*cea608_2_len], self->scratch_cea608_2, - self->scratch_cea608_2_len); - *cea608_2_len += self->scratch_cea608_2_len; - } - - return TRUE; - -fail: - if (ccp_size) - *ccp_size = 0; - if (cea608_1_len) - *cea608_1_len = 0; - if (cea608_2_len) - *cea608_2_len = 0; - return FALSE; -} - -static gboolean -cc_data_to_cea608_ccp (GstCCConverter * self, guint8 * cc_data, - guint cc_data_len, guint8 * out_ccp, guint * ccp_size, guint8 * cea608_1, - guint * cea608_1_len, guint8 * cea608_2, guint * cea608_2_len, - const struct cdp_fps_entry *in_fps_entry) -{ - guint ccp_in_size = 0, cea608_1_in_size = 0, cea608_2_in_size = 0; - - g_assert (cc_data || cc_data_len == 0); - - if (ccp_size) - ccp_in_size = *ccp_size; - if (cea608_1_len) - cea608_1_in_size = *cea608_1_len; - if (cea608_2_len) - cea608_2_in_size = *cea608_2_len; - - if (!copy_from_stored_data (self, out_ccp, ccp_size, cea608_1, cea608_1_len, - cea608_2, cea608_2_len)) - goto fail; - - if (cc_data) { - gint ccp_offset = 0; - guint new_cea608_1_len = 0, new_cea608_2_len = 0; - guint8 *new_cea608_1 = cea608_1, *new_cea608_2 = cea608_2; - - cc_data_len = compact_cc_data (cc_data, cc_data_len); - - if (cc_data_len / 3 > in_fps_entry->max_cc_count) { - GST_WARNING_OBJECT (self, "Too many cc_data triples in CDP packet %u. " - "Truncating to %u", cc_data_len / 3, in_fps_entry->max_cc_count); - cc_data_len = 3 * in_fps_entry->max_cc_count; - } - - while (TRUE) { - if (cea608_1_len) { - new_cea608_1_len = cea608_1_in_size - *cea608_1_len; - new_cea608_1 = &cea608_1[*cea608_1_len]; - } - if (cea608_2_len) { - new_cea608_2_len = cea608_2_in_size - *cea608_2_len; - new_cea608_2 = &cea608_2[*cea608_2_len]; - } - - ccp_offset = cc_data_extract_cea608 (cc_data, cc_data_len, new_cea608_1, - &new_cea608_1_len, new_cea608_2, &new_cea608_2_len); - - if (ccp_offset == CC_DATA_EXTRACT_TOO_MANY_FIELD1 && cea608_1_len) { - GST_WARNING_OBJECT (self, "cea608 field 1 overflow, dropping all " - "previously stored field 1 data and trying again"); - *cea608_1_len = 0; - } else if (ccp_offset == CC_DATA_EXTRACT_TOO_MANY_FIELD2 && cea608_2_len) { - GST_WARNING_OBJECT (self, "cea608 field 2 overflow, dropping all " - "previously stored field 2 data and trying again"); - *cea608_2_len = 0; - } else if (ccp_offset < 0) { - GST_WARNING_OBJECT (self, "Failed to extract cea608 from cc_data"); - goto fail; - } else { - /* success */ - break; - } - } - - if ((new_cea608_1_len + new_cea608_2_len) / 2 > - in_fps_entry->max_cea608_count) { - GST_WARNING_OBJECT (self, "Too many cea608 triples in CDP packet %u. " - "Truncating to %u", (new_cea608_1_len + new_cea608_2_len) / 2, - in_fps_entry->max_cea608_count); - if ((new_cea608_1_len + new_cea608_2_len) / 2 > - in_fps_entry->max_cea608_count) { - new_cea608_1_len = 2 * in_fps_entry->max_cea608_count; - new_cea608_2_len = 0; - } else { - new_cea608_2_len = - 2 * in_fps_entry->max_cea608_count - new_cea608_1_len; - } - } - - if (cea608_1_len) - *cea608_1_len += new_cea608_1_len; - if (cea608_2_len) - *cea608_2_len += new_cea608_2_len; - - if (out_ccp) { - if (ccp_in_size < *ccp_size + cc_data_len - ccp_offset) { - GST_WARNING_OBJECT (self, "output buffer too small %u < %u", - ccp_in_size, *ccp_size + cc_data_len - ccp_offset); - goto fail; - } - memcpy (&out_ccp[*ccp_size], &cc_data[ccp_offset], - cc_data_len - ccp_offset); - *ccp_size += cc_data_len - ccp_offset; - } - } - - return TRUE; - -fail: - if (ccp_size) - *ccp_size = 0; - if (cea608_1_len) - *cea608_1_len = 0; - if (cea608_2_len) - *cea608_2_len = 0; - return FALSE; -} - -static gboolean -cdp_to_cea608_cc_data (GstCCConverter * self, GstBuffer * inbuf, - guint8 * out_ccp, guint * ccp_size, guint8 * cea608_1, guint * cea608_1_len, - guint8 * cea608_2, guint * cea608_2_len, GstVideoTimeCode * out_tc, - const struct cdp_fps_entry **in_fps_entry) +push_cdp_buffer (GstCCConverter * self, GstBuffer * inbuf, + GstVideoTimeCode * out_tc, const struct cdp_fps_entry **in_fps_entry) { guint8 cc_data[MAX_CDP_PACKET_LEN]; guint cc_data_len = 0; @@ -1318,13 +640,13 @@ cdp_to_cea608_cc_data (GstCCConverter * self, GstBuffer * inbuf, convert_cea708_cdp_to_cc_data (GST_OBJECT (self), in.data, in.size, cc_data, out_tc, in_fps_entry); + cc_buffer_push_cc_data (self->cc_buffer, cc_data, cc_data_len); + gst_buffer_unmap (inbuf, &in); self->input_frames++; } - return cc_data_to_cea608_ccp (self, inbuf ? cc_data : NULL, cc_data_len, - out_ccp, ccp_size, cea608_1, cea608_1_len, cea608_2, cea608_2_len, - inbuf ? *in_fps_entry : NULL); + return TRUE; } static GstFlowReturn @@ -1416,18 +738,14 @@ convert_cea608_raw_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf, GstMapInfo in, out; const struct cdp_fps_entry *in_fps_entry, *out_fps_entry; guint cc_data_len = MAX_CDP_PACKET_LEN; - guint cea608_1_len = MAX_CDP_PACKET_LEN; - guint8 cc_data[MAX_CDP_PACKET_LEN], cea608_1[MAX_CEA608_LEN]; + guint8 cc_data[MAX_CDP_PACKET_LEN]; in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d); if (!in_fps_entry || in_fps_entry->fps_n == 0) g_assert_not_reached (); - if (!copy_from_stored_data (self, NULL, 0, cea608_1, &cea608_1_len, NULL, 0)) - goto drop; - if (inbuf) { - guint n = 0, i; + guint n = 0; n = gst_buffer_get_size (inbuf); if (n & 1) { @@ -1445,14 +763,8 @@ convert_cea608_raw_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf, } gst_buffer_map (inbuf, &in, GST_MAP_READ); - for (i = 0; i < n; i++) { - guint byte1 = in.data[i * 2 + 0]; - guint byte2 = in.data[i * 2 + 1]; - if (byte1 != 0x80 || byte2 != 0x80) { - cea608_1[cea608_1_len++] = byte1; - cea608_1[cea608_1_len++] = byte2; - } - } + cc_buffer_push_separated (self->cc_buffer, in.data, in.size, NULL, 0, NULL, + 0); gst_buffer_unmap (inbuf, &in); self->input_frames++; } @@ -1461,15 +773,12 @@ convert_cea608_raw_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf, if (!out_fps_entry || out_fps_entry->fps_n == 0) g_assert_not_reached (); - if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, NULL, 0, - cea608_1, &cea608_1_len, NULL, 0, tc_meta ? &tc_meta->tc : NULL, - FALSE)) + if (!can_take_buffer (self, in_fps_entry, out_fps_entry, + tc_meta ? &tc_meta->tc : NULL, &self->current_output_timecode)) goto drop; - if (!combine_cc_data (self, TRUE, out_fps_entry, NULL, 0, cea608_1, - cea608_1_len, NULL, 0, cc_data, &cc_data_len, - &self->last_cea608_written_was_field1)) - goto drop; + cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, cc_data, + &cc_data_len); gst_buffer_map (outbuf, &out, GST_MAP_WRITE); cc_data_len = @@ -1572,7 +881,7 @@ convert_cea608_s334_1a_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf, GstMapInfo in, out; const struct cdp_fps_entry *in_fps_entry, *out_fps_entry; guint cc_data_len = MAX_CDP_PACKET_LEN; - guint cea608_1_len = MAX_CDP_PACKET_LEN, cea608_2_len = MAX_CDP_PACKET_LEN; + guint cea608_1_len = 0, cea608_2_len = 0; guint8 cc_data[MAX_CDP_PACKET_LEN]; guint8 cea608_1[MAX_CEA608_LEN], cea608_2[MAX_CEA608_LEN]; guint i, n; @@ -1581,10 +890,6 @@ convert_cea608_s334_1a_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf, if (!in_fps_entry || in_fps_entry->fps_n == 0) g_assert_not_reached (); - if (!copy_from_stored_data (self, NULL, 0, cea608_1, &cea608_1_len, - cea608_2, &cea608_2_len)) - goto drop; - if (inbuf) { n = gst_buffer_get_size (inbuf); if (n % 3 != 0) { @@ -1618,6 +923,9 @@ convert_cea608_s334_1a_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf, } } gst_buffer_unmap (inbuf, &in); + + cc_buffer_push_separated (self->cc_buffer, cea608_1, cea608_1_len, + cea608_2, cea608_2_len, NULL, 0); self->input_frames++; } @@ -1625,18 +933,12 @@ convert_cea608_s334_1a_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf, if (!out_fps_entry || out_fps_entry->fps_n == 0) g_assert_not_reached (); - if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, NULL, 0, - cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, - tc_meta ? &tc_meta->tc : NULL, - self->last_cea608_written_was_field1)) { + if (!can_take_buffer (self, in_fps_entry, out_fps_entry, + tc_meta ? &tc_meta->tc : NULL, &self->current_output_timecode)) goto drop; - } - if (!combine_cc_data (self, TRUE, out_fps_entry, NULL, 0, cea608_1, - cea608_1_len, cea608_2, cea608_2_len, cc_data, &cc_data_len, - &self->last_cea608_written_was_field1)) { - goto drop; - } + cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, cc_data, + &cc_data_len); gst_buffer_map (outbuf, &out, GST_MAP_WRITE); cc_data_len = @@ -1748,10 +1050,8 @@ convert_cea708_cc_data_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf, GstMapInfo in, out; const struct cdp_fps_entry *in_fps_entry, *out_fps_entry; guint in_cc_data_len; - guint cc_data_len = MAX_CDP_PACKET_LEN, ccp_data_len = MAX_CDP_PACKET_LEN; - guint cea608_1_len = MAX_CEA608_LEN, cea608_2_len = MAX_CEA608_LEN; - guint8 cc_data[MAX_CDP_PACKET_LEN], ccp_data[MAX_CDP_PACKET_LEN]; - guint8 cea608_1[MAX_CEA608_LEN], cea608_2[MAX_CEA608_LEN]; + guint cc_data_len = MAX_CDP_PACKET_LEN; + guint8 cc_data[MAX_CDP_PACKET_LEN]; guint8 *in_cc_data; if (inbuf) { @@ -1772,26 +1072,16 @@ convert_cea708_cc_data_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf, if (!out_fps_entry || out_fps_entry->fps_n == 0) g_assert_not_reached (); - if (!cc_data_to_cea608_ccp (self, in_cc_data, in_cc_data_len, ccp_data, - &ccp_data_len, cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, - in_fps_entry)) { - if (inbuf) - gst_buffer_unmap (inbuf, &in); - goto drop; - } - + cc_buffer_push_cc_data (self->cc_buffer, in_cc_data, in_cc_data_len); if (inbuf) gst_buffer_unmap (inbuf, &in); - if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, ccp_data, - &ccp_data_len, cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, - tc_meta ? &tc_meta->tc : NULL, self->last_cea608_written_was_field1)) + if (!can_take_buffer (self, in_fps_entry, out_fps_entry, + tc_meta ? &tc_meta->tc : NULL, &self->current_output_timecode)) goto drop; - if (!combine_cc_data (self, TRUE, out_fps_entry, ccp_data, ccp_data_len, - cea608_1, cea608_1_len, cea608_2, cea608_2_len, cc_data, - &cc_data_len, &self->last_cea608_written_was_field1)) - goto drop; + cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, cc_data, + &cc_data_len); gst_buffer_map (outbuf, &out, GST_MAP_WRITE); cc_data_len = @@ -1816,13 +1106,10 @@ convert_cea708_cdp_cea608_raw (GstCCConverter * self, GstBuffer * inbuf, { GstMapInfo out; GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT; - guint8 cea608_1[MAX_CEA608_LEN]; - guint cea608_1_len = MAX_CEA608_LEN; + guint cea608_1_len; const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry; - gst_buffer_map (outbuf, &out, GST_MAP_WRITE); - if (!cdp_to_cea608_cc_data (self, inbuf, NULL, NULL, cea608_1, &cea608_1_len, - NULL, NULL, &tc, &in_fps_entry)) { + if (!push_cdp_buffer (self, inbuf, &tc, &in_fps_entry)) { gst_buffer_set_size (outbuf, 0); return GST_FLOW_OK; } @@ -1831,29 +1118,16 @@ convert_cea708_cdp_cea608_raw (GstCCConverter * self, GstBuffer * inbuf, if (!out_fps_entry || out_fps_entry->fps_n == 0) out_fps_entry = in_fps_entry; - if (fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, NULL, 0, - cea608_1, &cea608_1_len, NULL, NULL, &tc, FALSE)) { - guint i, out_size = (guint) out.size; - - self->output_frames++; - if (!write_cea608 (self, TRUE, out_fps_entry, cea608_1, cea608_1_len, NULL, - 0, out.data, &out_size, NULL)) { - gst_buffer_unmap (outbuf, &out); - return GST_FLOW_ERROR; - } + if (!can_take_buffer (self, in_fps_entry, out_fps_entry, &tc, + &self->current_output_timecode)) + goto drop; - /* remove the first byte from each cea608 packet */ - for (i = 0; i < out_size / 3; i++) { - out.data[i * 2 + 0] = out.data[i * 3 + 1]; - out.data[i * 2 + 1] = out.data[i * 3 + 2]; - } - cea608_1_len = out_size / 3 * 2; - } else { - cea608_1_len = 0; - } + gst_buffer_map (outbuf, &out, GST_MAP_WRITE); + cea608_1_len = out.size; + cc_buffer_take_separated (self->cc_buffer, out_fps_entry, out.data, + &cea608_1_len, NULL, 0, NULL, 0); gst_buffer_unmap (outbuf, &out); - - gst_buffer_set_size (outbuf, cea608_1_len); + self->output_frames++; if (self->current_output_timecode.config.fps_n != 0 && !tc_meta) { gst_buffer_add_video_time_code_meta (outbuf, @@ -1861,7 +1135,13 @@ convert_cea708_cdp_cea608_raw (GstCCConverter * self, GstBuffer * inbuf, gst_video_time_code_increment_frame (&self->current_output_timecode); } +out: + gst_buffer_set_size (outbuf, cea608_1_len); return GST_FLOW_OK; + +drop: + cea608_1_len = 0; + goto out; } static GstFlowReturn @@ -1871,34 +1151,31 @@ convert_cea708_cdp_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf, GstMapInfo out; GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT; const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry; - guint8 cea608_1[MAX_CEA608_LEN], cea608_2[MAX_CEA608_LEN]; - guint cea608_1_len = MAX_CEA608_LEN, cea608_2_len = MAX_CEA608_LEN; - guint i, cc_data_len; + guint cc_data_len; + int s334_len; + guint i; - if (!cdp_to_cea608_cc_data (self, inbuf, NULL, NULL, cea608_1, &cea608_1_len, - cea608_2, &cea608_2_len, &tc, &in_fps_entry)) + if (!push_cdp_buffer (self, inbuf, &tc, &in_fps_entry)) goto drop; out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d); if (!out_fps_entry || out_fps_entry->fps_n == 0) out_fps_entry = in_fps_entry; - if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, NULL, 0, - cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, &tc, - self->last_cea608_written_was_field1)) + if (!can_take_buffer (self, in_fps_entry, out_fps_entry, &tc, + &self->current_output_timecode)) goto drop; - cc_data_len = gst_buffer_get_sizes (outbuf, NULL, NULL); - gst_buffer_map (outbuf, &out, GST_MAP_READWRITE); - if (!combine_cc_data (self, TRUE, out_fps_entry, NULL, 0, cea608_1, - cea608_1_len, cea608_2, cea608_2_len, out.data, &cc_data_len, - &self->last_cea608_written_was_field1)) { - gst_buffer_unmap (outbuf, &out); + + cc_data_len = out.size; + 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; - } - for (i = 0; i < cc_data_len / 3; i++) { + for (i = 0; i < s334_len / 3; i++) { guint byte = out.data[i * 3]; /* We have to assume a line offset of 0 */ out.data[i * 3] = (byte == 0xfc || byte == 0xf8) ? 0x80 : 0x00; @@ -1907,7 +1184,7 @@ convert_cea708_cdp_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf, gst_buffer_unmap (outbuf, &out); self->output_frames++; - gst_buffer_set_size (outbuf, cc_data_len); + gst_buffer_set_size (outbuf, s334_len); if (self->current_output_timecode.config.fps_n != 0 && !tc_meta) { gst_buffer_add_video_time_code_meta (outbuf, @@ -1929,34 +1206,22 @@ convert_cea708_cdp_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf, GstMapInfo out; GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT; const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry; - guint8 cea608_1[MAX_CEA608_LEN], cea608_2[MAX_CEA608_LEN]; - guint8 ccp_data[MAX_CDP_PACKET_LEN]; - guint cea608_1_len = MAX_CEA608_LEN, cea608_2_len = MAX_CEA608_LEN; - guint ccp_data_len = MAX_CDP_PACKET_LEN; guint out_len = 0; - if (!cdp_to_cea608_cc_data (self, inbuf, ccp_data, &ccp_data_len, - cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, &tc, &in_fps_entry)) + if (!push_cdp_buffer (self, inbuf, &tc, &in_fps_entry)) goto out; out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d); if (!out_fps_entry || out_fps_entry->fps_n == 0) out_fps_entry = in_fps_entry; - if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, ccp_data, - &ccp_data_len, cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, &tc, - self->last_cea608_written_was_field1)) + if (!can_take_buffer (self, in_fps_entry, out_fps_entry, &tc, + &self->current_output_timecode)) goto out; gst_buffer_map (outbuf, &out, GST_MAP_WRITE); out_len = (guint) out.size; - if (!combine_cc_data (self, TRUE, out_fps_entry, ccp_data, ccp_data_len, - cea608_1, cea608_1_len, cea608_2, cea608_2_len, out.data, &out_len, - &self->last_cea608_written_was_field1)) { - gst_buffer_unmap (outbuf, &out); - out_len = 0; - goto out; - } + cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, out.data, &out_len); gst_buffer_unmap (outbuf, &out); self->output_frames++; @@ -1980,30 +1245,23 @@ convert_cea708_cdp_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf, GstMapInfo out; GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT; const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry; - guint8 cea608_1[MAX_CEA608_LEN], cea608_2[MAX_CEA608_LEN]; - guint8 ccp_data[MAX_CDP_PACKET_LEN], cc_data[MAX_CDP_PACKET_LEN]; - guint cea608_1_len = MAX_CEA608_LEN, cea608_2_len = MAX_CEA608_LEN; - guint ccp_data_len = MAX_CDP_PACKET_LEN, cc_data_len = MAX_CDP_PACKET_LEN; + guint8 cc_data[MAX_CDP_PACKET_LEN]; + guint cc_data_len = MAX_CDP_PACKET_LEN; guint out_len = 0; - if (!cdp_to_cea608_cc_data (self, inbuf, ccp_data, &ccp_data_len, - cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, &tc, &in_fps_entry)) + if (!push_cdp_buffer (self, inbuf, &tc, &in_fps_entry)) goto out; out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d); if (!out_fps_entry || out_fps_entry->fps_n == 0) out_fps_entry = in_fps_entry; - if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, ccp_data, - &ccp_data_len, cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, &tc, - self->last_cea608_written_was_field1)) + if (!can_take_buffer (self, in_fps_entry, out_fps_entry, &tc, + &self->current_output_timecode)) goto out; - if (!combine_cc_data (self, TRUE, out_fps_entry, ccp_data, ccp_data_len, - cea608_1, cea608_1_len, cea608_2, cea608_2_len, cc_data, - &cc_data_len, &self->last_cea608_written_was_field1)) { - goto out; - } + cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, cc_data, + &cc_data_len); gst_buffer_map (outbuf, &out, GST_MAP_WRITE); out_len = @@ -2206,14 +1464,11 @@ can_generate_output (GstCCConverter * self) static void reset_counters (GstCCConverter * self) { - self->scratch_ccp_len = 0; - self->scratch_cea608_1_len = 0; - self->scratch_cea608_2_len = 0; self->input_frames = 0; self->output_frames = 1; gst_video_time_code_clear (&self->current_output_timecode); gst_clear_buffer (&self->previous_buffer); - self->last_cea608_written_was_field1 = FALSE; + cc_buffer_discard (self->cc_buffer); } static GstFlowReturn @@ -2222,9 +1477,13 @@ drain_input (GstCCConverter * self) GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (self); GstBaseTransform *trans = GST_BASE_TRANSFORM (self); GstFlowReturn ret = GST_FLOW_OK; + guint cea608_1_len, cea608_2_len, ccp_len; + + cc_buffer_get_stored_size (self->cc_buffer, &cea608_1_len, &cea608_2_len, + &ccp_len); - while (self->scratch_ccp_len > 0 || self->scratch_cea608_1_len > 0 - || self->scratch_cea608_2_len > 0 || can_generate_output (self)) { + while (ccp_len > 0 || cea608_1_len > 0 || cea608_2_len > 0 + || can_generate_output (self)) { GstBuffer *outbuf; if (!self->previous_buffer) { @@ -2244,6 +1503,8 @@ drain_input (GstCCConverter * self) } ret = gst_cc_converter_transform (self, NULL, outbuf); + cc_buffer_get_stored_size (self->cc_buffer, &cea608_1_len, &cea608_2_len, + &ccp_len); if (gst_buffer_get_size (outbuf) <= 0) { /* try to move the output along */ self->input_frames++; @@ -2405,6 +1666,16 @@ gst_cc_converter_get_property (GObject * object, guint prop_id, GValue * value, } static void +gst_cc_converter_finalize (GObject * object) +{ + GstCCConverter *self = GST_CCCONVERTER (object); + + gst_clear_object (&self->cc_buffer); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void gst_cc_converter_class_init (GstCCConverterClass * klass) { GObjectClass *gobject_class; @@ -2417,6 +1688,7 @@ gst_cc_converter_class_init (GstCCConverterClass * klass) gobject_class->set_property = gst_cc_converter_set_property; gobject_class->get_property = gst_cc_converter_get_property; + gobject_class->finalize = gst_cc_converter_finalize; /** * GstCCConverter:cdp-mode @@ -2471,4 +1743,5 @@ static void gst_cc_converter_init (GstCCConverter * self) { self->cdp_mode = DEFAULT_CDP_MODE; + self->cc_buffer = cc_buffer_new (); } diff --git a/subprojects/gst-plugins-bad/ext/closedcaption/gstccconverter.h b/subprojects/gst-plugins-bad/ext/closedcaption/gstccconverter.h index 4202550..6792c76 100644 --- a/subprojects/gst-plugins-bad/ext/closedcaption/gstccconverter.h +++ b/subprojects/gst-plugins-bad/ext/closedcaption/gstccconverter.h @@ -25,6 +25,8 @@ #include #include +#include "ccutils.h" + G_BEGIN_DECLS #define GST_TYPE_CCCONVERTER \ (gst_cc_converter_get_type()) @@ -40,9 +42,6 @@ G_BEGIN_DECLS typedef struct _GstCCConverter GstCCConverter; typedef struct _GstCCConverterClass GstCCConverterClass; -#define MAX_CDP_PACKET_LEN 256 -#define MAX_CEA608_LEN 32 - typedef enum { GST_CC_CONVERTER_CDP_MODE_TIME_CODE = (1<<0), GST_CC_CONVERTER_CDP_MODE_CC_DATA = (1<<1), @@ -67,21 +66,13 @@ struct _GstCCConverter /* for framerate differences, we need to keep previous/next frames in order * to split/merge data across multiple input or output buffers. The data is * stored as cc_data */ - guint8 scratch_cea608_1[MAX_CEA608_LEN]; - guint scratch_cea608_1_len; - guint8 scratch_cea608_2[MAX_CEA608_LEN]; - guint scratch_cea608_2_len; - guint8 scratch_ccp[MAX_CDP_PACKET_LEN]; - guint scratch_ccp_len; + CCBuffer *cc_buffer; guint input_frames; guint output_frames; GstVideoTimeCode current_output_timecode; /* previous buffer for copying metas onto */ GstBuffer *previous_buffer; - - /* used for tracking which field to write across output buffer boundaries */ - gboolean last_cea608_written_was_field1; }; struct _GstCCConverterClass diff --git a/subprojects/gst-plugins-bad/tests/check/elements/ccconverter.c b/subprojects/gst-plugins-bad/tests/check/elements/ccconverter.c index a925087..4e8138e 100644 --- a/subprojects/gst-plugins-bad/tests/check/elements/ccconverter.c +++ b/subprojects/gst-plugins-bad/tests/check/elements/ccconverter.c @@ -476,12 +476,12 @@ GST_START_TEST (convert_cea708_cdp_cea608_raw) { 0x96, 0x69, 0x13, 0x5f, 0x43, 0x00, 0x00, 0x72, 0xe2, 0xfc, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x74, 0x00, 0x00, 0x8a }; - const guint8 out1[] = { 0x80, 0x80, 0x80, 0x80 }; + const guint8 out1[] = { 0x80, 0x80, }; const guint8 in2[] = { 0x96, 0x69, 0x13, 0x5f, 0x43, 0x00, 0x00, 0x72, 0xe2, 0xfc, 0x80, 0x81, 0xfd, 0x82, 0x83, 0x74, 0x00, 0x00, 0x8a }; - const guint8 out2[] = { 0x80, 0x81, 0x80, 0x80 }; + const guint8 out2[] = { 0x80, 0x81, }; check_conversion_tc_passthrough (in1, sizeof (in1), out1, sizeof (out1), "closedcaption/x-cea-708,format=(string)cdp,framerate=30/1", "closedcaption/x-cea-608,format=(string)raw"); @@ -514,7 +514,7 @@ GST_START_TEST (convert_cea708_cdp_cea708_cc_data) }; const guint8 out[] = { 0xf8, 0x80, 0x80, 0xf9, 0x80, 0x80 }; check_conversion_tc_passthrough (in, sizeof (in), out, sizeof (out), - "closedcaption/x-cea-708,format=(string)cdp", + "closedcaption/x-cea-708,format=(string)cdp,framerate=30/1", "closedcaption/x-cea-708,format=(string)cc_data"); } @@ -524,17 +524,23 @@ GST_START_TEST (convert_cea708_cdp_cea708_cc_data_too_big) { /* tests that too large input is truncated */ const guint8 in[] = - { 0x96, 0x69, 0x2e, 0x8f, 0x43, 0x00, 0x00, 0x72, 0xeb, 0xfc, 0x80, 0x80, + { 0x96, 0x69, 0x4c, 0x8f, 0x43, 0x00, 0x00, 0x72, 0xf5, 0xfc, 0x80, 0x80, + 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, + 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, + 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, - 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x74, 0x00, 0x00, 0x8a, + 0x74, 0x00, 0x00, 0x8a, }; - const guint8 out[] = { 0xf8, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, + const guint8 out[] = { 0xf8, 0x80, 0x80, 0xf9, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, - 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80 + 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, + 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, + 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, + 0xfe, 0x80, 0x80 }; check_conversion_tc_passthrough (in, sizeof (in), out, sizeof (out), - "closedcaption/x-cea-708,format=(string)cdp", + "closedcaption/x-cea-708,format=(string)cdp,framerate=30/1", "closedcaption/x-cea-708,format=(string)cc_data"); } @@ -1131,12 +1137,12 @@ GST_START_TEST (convert_cea708_cc_data_cea708_cdp_field1_overflow) gsize in_data_offset; /* take frames sequentially from the input */ gsize in_idx = i / 2; - /* take the first 16 input frames, then skip the next 16 frames and take - * the next 16 frames etc. - * 32 is the byte size of the internal cea608 field buffers that we are + /* take the first 12 input frames, then skip the next 12 frames and take + * the next 12 frames etc. + * 24 is the byte size of the internal cea608 field buffers that we are * overflowing but every second buffer will have cea608 field 1 in it. - * 16 frames is 32 bytes stored and is enough to cause overflow */ - in_idx = (in_idx / 16) * 32 + in_idx % 16; + * 12 frames is 24 bytes stored and is enough to cause overflow */ + in_idx = (in_idx / 6) * 12 + in_idx % 6; in_data_offset = in_idx * 3; out_data[i * 43 + 9] = in_data[in_data_offset + 0]; -- 2.7.4