summaryrefslogtreecommitdiff
path: root/ext/closedcaption
diff options
context:
space:
mode:
authorMatthew Waters <matthew@centricular.com>2020-03-19 17:42:13 +1100
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>2020-05-11 12:30:31 +0000
commit7ed0bc539f473b66e3dad4f137f89e2cb0cdf808 (patch)
tree79767e1fe2b1ebaefe203ddb57634d76fbeb950c /ext/closedcaption
parent3417a1709c764a43fcd3966b2fb0e2f3c19efcb4 (diff)
downloadgstreamer-plugins-bad-7ed0bc539f473b66e3dad4f137f89e2cb0cdf808.tar.gz
ccconverter: split temporary storage into 3
Instead of storing the raw cc_data, store the 2 cea608 fields individually as well as the ccp data. Simply copying the input cc_data to the output cc_data violates a number of requirements in the cea708 specification. The most prominent being, that cea608 triples must be placed at the beginning of each cdp. We also need to comply with the framerate-dpendent limits for both the cea608 and the ccp data which may involve splitting or merging some cea608 data but not ccp data or vice versa. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1116>
Diffstat (limited to 'ext/closedcaption')
-rw-r--r--ext/closedcaption/gstccconverter.c1033
-rw-r--r--ext/closedcaption/gstccconverter.h9
2 files changed, 789 insertions, 253 deletions
diff --git a/ext/closedcaption/gstccconverter.c b/ext/closedcaption/gstccconverter.c
index 6c7a4a2d6..d62d717a5 100644
--- a/ext/closedcaption/gstccconverter.c
+++ b/ext/closedcaption/gstccconverter.c
@@ -40,6 +40,8 @@ GST_DEBUG_CATEGORY_STATIC (gst_cc_converter_debug);
"closedcaption/x-cea-608,format=(string) s334-1a; " \
"closedcaption/x-cea-608,format=(string) raw"
+#define VAL_OR_0(v) ((v) ? (*(v)) : 0)
+
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
@@ -384,17 +386,19 @@ struct cdp_fps_entry
guint8 fps_idx;
guint fps_n, fps_d;
guint max_cc_count;
+ guint max_ccp_count;
+ guint max_cea608_count;
};
static const struct cdp_fps_entry cdp_fps_table[] = {
- {0x1f, 24000, 1001, 25},
- {0x2f, 24, 1, 25},
- {0x3f, 25, 1, 24},
- {0x4f, 30000, 1001, 20},
- {0x5f, 30, 1, 20},
- {0x6f, 50, 1, 12},
- {0x7f, 60000, 1001, 10},
- {0x8f, 60, 1, 10},
+ {0x1f, 24000, 1001, 25, 22, 3 /* FIXME: alternating max cea608 count! */ },
+ {0x2f, 24, 1, 25, 22, 2},
+ {0x3f, 25, 1, 24, 22, 2},
+ {0x4f, 30000, 1001, 20, 18, 2},
+ {0x5f, 30, 1, 20, 18, 2},
+ {0x6f, 50, 1, 12, 11, 1},
+ {0x7f, 60000, 1001, 10, 9, 1},
+ {0x8f, 60, 1, 10, 9, 1},
};
static const struct cdp_fps_entry null_fps_entry = { 0, 0, 0, 0 };
@@ -520,11 +524,13 @@ compact_cc_data (guint8 * cc_data, guint cc_data_len)
gboolean cc_valid = (cc_data[i * 3] & 0x04) == 0x04;
guint8 cc_type = cc_data[i * 3] & 0x03;
- if (!started_ccp && cc_valid && (cc_type == 0x00 || cc_type == 0x01)) {
- /* skip over 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];
+ 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;
}
@@ -534,22 +540,241 @@ compact_cc_data (guint8 * cc_data, guint cc_data_len)
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;
}
-/* takes cc_data and cc_data_len and attempts to fit it into a hypothetical
- * output packet. Any leftover data is stored for later addition. Returns
- * the number of bytes of @cc_data to place in a new output packet */
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++) {
+ gboolean cc_valid = (cc_data[i * 3] & 0x04) == 0x04;
+ guint8 cc_type = cc_data[i * 3] & 0x03;
+
+ 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 -1;
+ }
+ cea608_field1[(*cea608_field1_len)++] = cc_data[i * 3 + 1];
+ cea608_field1[(*cea608_field1_len)++] = cc_data[i * 3 + 2];
+ }
+ } 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 -1;
+ }
+ cea608_field2[(*cea608_field2_len)++] = cc_data[i * 3 + 1];
+ cea608_field2[(*cea608_field2_len)++] = cc_data[i * 3 + 2];
+ }
+ } 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_DEBUG_OBJECT (self, "holding data of len ccp:%u, cea608 1:%u, "
+ "cea608 2:%u until next input buffer", ccp_data_len, cea608_1_len,
+ cea608_2_len);
+
+ if (ccp_data && ccp_data_len > 0) {
+ 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) {
+ 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) {
+ 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));
+}
+
+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)
+{
+ 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;
+
+ g_assert (out);
+ g_assert (out_size);
+ g_assert (!ccp_data || ccp_data_len % 3 == 0);
+ g_assert (!cea608_1 || cea608_1_len % 2 == 0);
+ g_assert (!cea608_2 || cea608_2_len % 2 == 0);
+ 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);
+
+ total_cea608_1_count = cea608_1_len;
+ total_cea608_2_count = cea608_2_len;
+
+#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
+
+ max_size = ccp_data_len + (total_cea608_1_count + total_cea608_2_count) * 3;
+ if (*out_size < max_size) {
+ GST_WARNING_OBJECT (self, "Output data too small (%u < %u)", *out_size,
+ max_size);
+ return FALSE;
+ }
+
+ /* FIXME: interlacing, tff, rff, ensuring cea608 field1 is generated if
+ * field2 exists even across packets */
+
+ cea608_output_count = cea608_1_len + cea608_2_len;
+ if (pad_cea608) {
+ for (i = total_cea608_1_count + total_cea608_2_count;
+ i < out_fps_entry->max_cea608_count; i++) {
+ /* try to pad evenly */
+ if (i > cea608_1_len / 2)
+ total_cea608_1_count++;
+ else
+ total_cea608_2_count++;
+ cea608_output_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);
+
+ while (cea608_1_i + cea608_2_i < cea608_output_count) {
+ 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++;
+ } else if (cea608_1_i < total_cea608_1_count) {
+ out[out_i++] = 0xfc;
+ out[out_i++] = 0x80;
+ out[out_i++] = 0x80;
+ cea608_1_i++;
+ i++;
+ }
+
+ 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++;
+ } else if (cea608_2_i < total_cea608_2_count) {
+ out[out_i++] = 0xfd;
+ out[out_i++] = 0x80;
+ out[out_i++] = 0x80;
+ cea608_2_i++;
+ i++;
+ }
+ }
+
+ g_assert_cmpint (out_i / 3, <=, out_fps_entry->max_cea608_count);
+
+ *out_size = out_i;
+
+ 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 * cc_data,
- guint cc_data_len, const GstVideoTimeCode * tc)
+ 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)
{
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);
@@ -576,7 +801,7 @@ fit_and_scale_cc_data (GstCCConverter * self,
g_assert_not_reached ();
if (!gst_util_fraction_multiply (self->out_fps_d, self->out_fps_n,
- self->output_frames + 1, 1, &output_frame_n, &output_frame_d))
+ self->output_frames, 1, &output_frame_n, &output_frame_d))
/* we should never overflow */
g_assert_not_reached ();
@@ -589,46 +814,101 @@ fit_and_scale_cc_data (GstCCConverter * self,
rate_cmp = gst_util_fraction_compare (scale_n, scale_d, 1, 1);
GST_TRACE_OBJECT (self, "performing framerate conversion at scale %d/%d "
- "of cc data", scale_n, scale_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 (rate_cmp == 0) {
/* we are not scaling. Should never happen with current conditions
* above */
g_assert_not_reached ();
- } else 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_len = 0;
- self->input_frames = self->output_frames = 0;
- if (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);
- }
} else if (output_time_cmp < 0) {
/* we can't generate an output yet */
- self->scratch_len = cc_data_len;
- GST_DEBUG_OBJECT (self, "holding cc_data of len %u until next input "
- "buffer", self->scratch_len);
- memcpy (self->scratch, cc_data, self->scratch_len);
- return -1;
+ 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 if (rate_cmp != 0) {
/* 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;
+
+ 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 (cc_data_len / 3 > out_fps_entry->max_cc_count) {
+ 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;
+ }
+
+ if (cea608_1_len) {
+ extra_cea608_1 = *cea608_1_len - 2 * out_fps_entry->max_cea608_count;
+ extra_cea608_1 = MAX (0, extra_cea608_1);
+ cea608_1_off = *cea608_1_len - extra_cea608_1;
+ }
+
+ if (cea608_2_len) {
+ /* this prefers using field1 data first. This may not be quite correct */
+ if (extra_cea608_1 > 0) {
+ /* all the cea608 space is for field 1 */
+ extra_cea608_2 = *cea608_2_len;
+ cea608_2_off = 0;
+ } else if (cea608_1_len) {
+ /* cea608 space is shared between field 1 and field 2 */
+ extra_cea608_2 =
+ *cea608_1_len + *cea608_2_len -
+ 2 * out_fps_entry->max_cea608_count;
+ extra_cea608_2 = MAX (0, extra_cea608_2);
+ cea608_2_off = *cea608_2_len - extra_cea608_2;
+ } else {
+ /* all of the cea608 space is for field 2 */
+ extra_cea608_2 = *cea608_2_len - 2 * out_fps_entry->max_cea608_count;
+ extra_cea608_2 = MAX (0, extra_cea608_2);
+ cea608_2_off = *cea608_2_len - extra_cea608_2;
+ }
+ }
+
+ if (extra_ccp > 0 || extra_cea608_1 > 0 || extra_cea608_2 > 0) {
/* packet would overflow, push extra bytes into the next packet */
- self->scratch_len = cc_data_len - 3 * out_fps_entry->max_cc_count;
- GST_DEBUG_OBJECT (self, "buffer would overflow by %u bytes (max "
- "length %u)", self->scratch_len, 3 * out_fps_entry->max_cc_count);
- memcpy (self->scratch, &cc_data[3 * out_fps_entry->max_cc_count],
- self->scratch_len);
- cc_data_len = 3 * out_fps_entry->max_cc_count;
+ 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, "packet length of %u fits within max output "
- "packet size %u", cc_data_len, 3 * out_fps_entry->max_cc_count);
- self->scratch_len = 0;
+ 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;
}
} else {
g_assert_not_reached ();
@@ -640,7 +920,15 @@ fit_and_scale_cc_data (GstCCConverter * self,
&self->current_output_timecode);
}
- return cc_data_len;
+ 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;
}
/* Converts raw CEA708 cc_data and an optional timecode into CDP */
@@ -846,42 +1134,197 @@ convert_cea708_cdp_cea708_cc_data_internal (GstCCConverter * self,
return len;
}
-static guint
-cdp_to_cc_data (GstCCConverter * self, GstBuffer * inbuf, guint8 * out,
- guint out_size, GstVideoTimeCode * out_tc,
- const struct cdp_fps_entry **out_fps_entry)
+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)
{
- GstMapInfo in;
- guint len = 0;
+ guint ccp_in_size = 0, cea608_1_in_size = 0, cea608_2_in_size = 0;
+
+ g_assert (out_ccp || !ccp_size);
+ g_assert (cea608_1 || !cea608_1_len);
+ g_assert (cea608_2 || !cea608_2_len);
- if (self->scratch_len > 0) {
- GST_DEBUG_OBJECT (self, "copying from previous scratch buffer of %u bytes",
- self->scratch_len);
- memcpy (&out[len], self->scratch, self->scratch_len);
- len += self->scratch_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 (inbuf) {
- guint cc_data_len;
+ 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;
+ }
- gst_buffer_map (inbuf, &in, GST_MAP_READ);
+ 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;
+ }
- cc_data_len =
- convert_cea708_cdp_cea708_cc_data_internal (self, in.data, in.size,
- &out[len], out_tc, out_fps_entry);
- if (cc_data_len / 3 > (*out_fps_entry)->max_cc_count) {
+ 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;
+
+ 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];
+ }
+
+ 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",
cc_data_len / 3);
- cc_data_len = 3 * (*out_fps_entry)->max_cc_count;
+ cc_data_len = 3 * in_fps_entry->max_cc_count;
}
- cc_data_len = compact_cc_data (&out[len], cc_data_len);
- len += cc_data_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 < 0) {
+ GST_WARNING_OBJECT (self, "Failed to extract cea608 from cc_data");
+ goto fail;
+ }
+
+ 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",
+ (new_cea608_1_len + new_cea608_2_len) / 2);
+ 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)
+{
+ guint8 cc_data[MAX_CDP_PACKET_LEN];
+ guint cc_data_len = 0;
+ GstMapInfo in;
+
+ if (inbuf) {
+ gst_buffer_map (inbuf, &in, GST_MAP_READ);
+
+ cc_data_len =
+ convert_cea708_cdp_cea708_cc_data_internal (self, in.data, in.size,
+ cc_data, out_tc, in_fps_entry);
gst_buffer_unmap (inbuf, &in);
self->input_frames++;
}
- return len;
+ 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);
}
static GstFlowReturn
@@ -969,57 +1412,76 @@ convert_cea608_raw_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
GstBuffer * outbuf)
{
GstMapInfo in, out;
- guint i, n;
- gint len;
- guint8 cc_data[MAX_CDP_PACKET_LEN];
const GstVideoTimeCodeMeta *tc_meta;
- const struct cdp_fps_entry *fps_entry;
+ 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];
- n = gst_buffer_get_size (inbuf);
- if (n & 1) {
- GST_WARNING_OBJECT (self, "Invalid raw CEA608 buffer size");
- gst_buffer_set_size (outbuf, 0);
- return GST_FLOW_OK;
- }
+ 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 ();
- n /= 2;
+ if (!copy_from_stored_data (self, NULL, 0, cea608_1, &cea608_1_len, NULL, 0))
+ goto drop;
- if (n > 3) {
- GST_WARNING_OBJECT (self, "Too many CEA608 pairs %u", n);
- n = 3;
- }
+ if (inbuf) {
+ guint n = 0;
- gst_buffer_map (inbuf, &in, GST_MAP_READ);
- gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
+ n = gst_buffer_get_size (inbuf);
+ if (n & 1) {
+ GST_WARNING_OBJECT (self, "Invalid raw CEA608 buffer size");
+ gst_buffer_set_size (outbuf, 0);
+ return GST_FLOW_OK;
+ }
- for (i = 0; i < n; i++) {
- cc_data[i * 3] = 0xfc;
- cc_data[i * 3 + 1] = in.data[i * 2];
- cc_data[i * 3 + 2] = in.data[i * 2 + 1];
- }
+ n /= 2;
- fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
- if (!fps_entry || fps_entry->fps_n == 0)
- g_assert_not_reached ();
+ if (n > in_fps_entry->max_cea608_count) {
+ GST_WARNING_OBJECT (self, "Too many CEA608 pairs %u", n);
+ n = 3;
+ }
- tc_meta = gst_buffer_get_video_time_code_meta (inbuf);
+ gst_buffer_map (inbuf, &in, GST_MAP_READ);
+ memcpy (&cea608_1[cea608_1_len], in.data, n * 2);
+ gst_buffer_unmap (inbuf, &in);
+ cea608_1_len += n * 2;
+ self->input_frames++;
- len = fit_and_scale_cc_data (self, NULL, fps_entry, cc_data,
- n * 3, tc_meta ? &tc_meta->tc : NULL);
- if (len >= 0) {
- len =
- convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, len,
- out.data, out.size, &self->current_output_timecode, fps_entry);
+ tc_meta = gst_buffer_get_video_time_code_meta (inbuf);
} else {
- len = 0;
+ tc_meta = NULL;
}
- gst_buffer_unmap (inbuf, &in);
+ 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)
+ g_assert_not_reached ();
+
+ gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
+
+ 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))
+ 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))
+ goto drop;
+
+ cc_data_len =
+ convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, cc_data_len,
+ out.data, out.size, &self->current_output_timecode, out_fps_entry);
+ self->output_frames++;
+
+out:
gst_buffer_unmap (outbuf, &out);
- gst_buffer_set_size (outbuf, len);
+ gst_buffer_set_size (outbuf, cc_data_len);
return GST_FLOW_OK;
+
+drop:
+ cc_data_len = 0;
+ goto out;
}
static GstFlowReturn
@@ -1104,56 +1566,84 @@ convert_cea608_s334_1a_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
GstBuffer * outbuf)
{
GstMapInfo in, out;
- guint i, n;
- gint len;
- guint8 cc_data[MAX_CDP_PACKET_LEN];
const GstVideoTimeCodeMeta *tc_meta;
- const struct cdp_fps_entry *fps_entry;
+ 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;
+ guint8 cc_data[MAX_CDP_PACKET_LEN];
+ guint8 cea608_1[MAX_CEA608_LEN], cea608_2[MAX_CEA608_LEN];
+ guint i, n;
- n = gst_buffer_get_size (inbuf);
- if (n % 3 != 0) {
- GST_WARNING_OBJECT (self, "Invalid S334-1A CEA608 buffer size");
- n = n - (n % 3);
- }
+ 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 ();
- n /= 3;
+ if (!copy_from_stored_data (self, NULL, 0, cea608_1, &cea608_1_len,
+ cea608_2, &cea608_2_len))
+ goto drop;
- if (n > 3) {
- GST_WARNING_OBJECT (self, "Too many S334-1A CEA608 triplets %u", n);
- n = 3;
- }
+ if (inbuf) {
+ n = gst_buffer_get_size (inbuf);
+ if (n % 3 != 0) {
+ GST_WARNING_OBJECT (self, "Invalid S334-1A CEA608 buffer size");
+ n = n - (n % 3);
+ }
- gst_buffer_map (inbuf, &in, GST_MAP_READ);
- gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
+ n /= 3;
- for (i = 0; i < n; i++) {
- cc_data[i * 3] = (in.data[i * 3] & 0x80) ? 0xfc : 0xfd;
- cc_data[i * 3 + 1] = in.data[i * 3 + 1];
- cc_data[i * 3 + 2] = in.data[i * 3 + 2];
+ if (n > in_fps_entry->max_cea608_count) {
+ GST_WARNING_OBJECT (self, "Too many S334-1A CEA608 triplets %u", n);
+ n = 3;
+ }
+
+ gst_buffer_map (inbuf, &in, GST_MAP_READ);
+
+ for (i = 0; i < n; i++) {
+ if (in.data[i * 3] & 0x80) {
+ cea608_1[cea608_1_len++] = in.data[i * 3 + 1];
+ cea608_1[cea608_1_len++] = in.data[i * 3 + 2];
+ } else {
+ cea608_2[cea608_2_len++] = in.data[i * 3 + 1];
+ cea608_2[cea608_2_len++] = in.data[i * 3 + 2];
+ }
+ }
+ gst_buffer_unmap (inbuf, &in);
+ self->input_frames++;
+ tc_meta = gst_buffer_get_video_time_code_meta (inbuf);
+ } else {
+ tc_meta = NULL;
}
- fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
- if (!fps_entry || fps_entry->fps_n == 0)
+ 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)
g_assert_not_reached ();
- tc_meta = gst_buffer_get_video_time_code_meta (inbuf);
+ gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
- len = fit_and_scale_cc_data (self, NULL, fps_entry, cc_data,
- n * 3, tc_meta ? &tc_meta->tc : NULL);
- if (len >= 0) {
- len =
- convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, len,
- out.data, out.size, &self->current_output_timecode, fps_entry);
- } else {
- len = 0;
- }
+ 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))
+ goto drop;
- gst_buffer_unmap (inbuf, &in);
+ 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))
+ goto drop;
+
+ cc_data_len =
+ convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, cc_data_len,
+ out.data, out.size, &self->current_output_timecode, out_fps_entry);
+ self->output_frames++;
+
+out:
gst_buffer_unmap (outbuf, &out);
- gst_buffer_set_size (outbuf, len);
+ gst_buffer_set_size (outbuf, cc_data_len);
return GST_FLOW_OK;
+
+drop:
+ cc_data_len = 0;
+ goto out;
}
static GstFlowReturn
@@ -1247,49 +1737,74 @@ convert_cea708_cc_data_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
GstBuffer * outbuf)
{
GstMapInfo in, out;
- guint n;
- gint len;
const GstVideoTimeCodeMeta *tc_meta;
- const struct cdp_fps_entry *fps_entry;
+ 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];
+ guint8 *in_cc_data;
- n = gst_buffer_get_size (inbuf);
- if (n % 3 != 0) {
- GST_WARNING_OBJECT (self, "Invalid raw CEA708 buffer size");
- n = n - (n % 3);
+ if (inbuf) {
+ gst_buffer_map (inbuf, &in, GST_MAP_READ);
+ in_cc_data = in.data;
+ in_cc_data_len = in.size;
+ tc_meta = gst_buffer_get_video_time_code_meta (inbuf);
+ self->input_frames++;
+ } else {
+ in_cc_data = NULL;
+ in_cc_data_len = 0;
+ tc_meta = NULL;
}
- n /= 3;
+ 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 (n > 25) {
- GST_WARNING_OBJECT (self, "Too many CEA708 triplets %u", n);
- n = 25;
+ 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)
+ 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);
+ gst_buffer_set_size (outbuf, cc_data_len);
+ return GST_FLOW_OK;
}
- gst_buffer_map (inbuf, &in, GST_MAP_READ);
+ if (inbuf)
+ gst_buffer_unmap (inbuf, &in);
+
gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
- fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
- if (!fps_entry || fps_entry->fps_n == 0)
- g_assert_not_reached ();
+ 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))
+ goto drop;
- tc_meta = gst_buffer_get_video_time_code_meta (inbuf);
+ 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))
+ goto drop;
- len = fit_and_scale_cc_data (self, NULL, fps_entry, in.data,
- in.size, tc_meta ? &tc_meta->tc : NULL);
- if (len >= 0) {
- len =
- convert_cea708_cc_data_cea708_cdp_internal (self, in.data, len,
- out.data, out.size, &self->current_output_timecode, fps_entry);
- } else {
- len = 0;
- }
+ cc_data_len =
+ convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, cc_data_len,
+ out.data, out.size, &self->current_output_timecode, out_fps_entry);
+ self->output_frames++;
- gst_buffer_unmap (inbuf, &in);
+out:
gst_buffer_unmap (outbuf, &out);
- gst_buffer_set_size (outbuf, len);
+ gst_buffer_set_size (outbuf, cc_data_len);
return GST_FLOW_OK;
+
+drop:
+ cc_data_len = 0;
+ goto out;
}
static GstFlowReturn
@@ -1298,42 +1813,30 @@ convert_cea708_cdp_cea608_raw (GstCCConverter * self, GstBuffer * inbuf,
{
GstMapInfo out;
GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT;
- guint i, cea608 = 0;
- gint len = 0;
+ guint cea608_1_len;
const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry;
- guint8 cc_data[MAX_CDP_PACKET_LEN] = { 0, };
- len =
- cdp_to_cc_data (self, inbuf, cc_data, sizeof (cc_data), &tc,
- &in_fps_entry);
+ gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
+ cea608_1_len = (guint) out.size;
+ if (!cdp_to_cea608_cc_data (self, inbuf, NULL, NULL, out.data, &cea608_1_len,
+ NULL, NULL, &tc, &in_fps_entry)) {
+ gst_buffer_set_size (outbuf, 0);
+ return GST_FLOW_OK;
+ }
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;
- len = fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, cc_data, len,
- &tc);
- if (len > 0) {
- len /= 3;
-
- gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
-
- for (i = 0; i < len; i++) {
- /* We can only really copy the first field here as there can't be any
- * signalling in raw CEA608 and we must not mix the streams of different
- * fields
- */
- if (cc_data[i * 3] == 0xfc) {
- out.data[cea608 * 2] = cc_data[i * 3 + 1];
- out.data[cea608 * 2 + 1] = cc_data[i * 3 + 2];
- cea608++;
- }
- }
-
- gst_buffer_unmap (outbuf, &out);
+ if (fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, NULL, 0,
+ out.data, &cea608_1_len, NULL, NULL, &tc)) {
+ self->output_frames++;
+ } else {
+ cea608_1_len = 0;
}
+ gst_buffer_unmap (outbuf, &out);
- gst_buffer_set_size (outbuf, 2 * cea608);
+ gst_buffer_set_size (outbuf, cea608_1_len);
if (self->current_output_timecode.config.fps_n != 0
&& !gst_buffer_get_video_time_code_meta (inbuf)) {
@@ -1351,39 +1854,40 @@ convert_cea708_cdp_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
{
GstMapInfo out;
GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT;
- guint i, cea608 = 0;
- gint len = 0;
const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry;
- guint8 cc_data[MAX_CDP_PACKET_LEN] = { 0, };
+ 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;
- len =
- cdp_to_cc_data (self, inbuf, cc_data, sizeof (cc_data), &tc,
- &in_fps_entry);
+ if (!cdp_to_cea608_cc_data (self, inbuf, NULL, NULL, cea608_1, &cea608_1_len,
+ cea608_2, &cea608_2_len, &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;
- len = fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, cc_data, len,
- &tc);
- if (len > 0) {
- len /= 3;
-
- gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
- for (i = 0; i < len; i++) {
- if (cc_data[i * 3] == 0xfc || cc_data[i * 3] == 0xfd) {
- /* We have to assume a line offset of 0 */
- out.data[cea608 * 3] = cc_data[i * 3] == 0xfc ? 0x80 : 0x00;
- out.data[cea608 * 3 + 1] = cc_data[i * 3 + 1];
- out.data[cea608 * 3 + 2] = cc_data[i * 3 + 2];
- cea608++;
- }
- }
+ 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))
+ 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, FALSE, out_fps_entry, NULL, 0, cea608_1,
+ cea608_1_len, cea608_2, cea608_2_len, out.data, &cc_data_len)) {
gst_buffer_unmap (outbuf, &out);
- self->output_frames++;
+ goto drop;
}
- gst_buffer_set_size (outbuf, 3 * cea608);
+ for (i = 0; i < cc_data_len / 3; i++)
+ /* We have to assume a line offset of 0 */
+ out.data[i * 3] = out.data[i * 3] == 0xfc ? 0x80 : 0x00;
+
+ gst_buffer_unmap (outbuf, &out);
+ self->output_frames++;
+
+ gst_buffer_set_size (outbuf, cc_data_len);
if (self->current_output_timecode.config.fps_n != 0
&& !gst_buffer_get_video_time_code_meta (inbuf)) {
@@ -1393,6 +1897,10 @@ convert_cea708_cdp_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
}
return GST_FLOW_OK;
+
+drop:
+ gst_buffer_set_size (outbuf, 0);
+ return GST_FLOW_OK;
}
static GstFlowReturn
@@ -1401,29 +1909,37 @@ convert_cea708_cdp_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
{
GstMapInfo out;
GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT;
- gint len = 0;
const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry;
- guint8 cc_data[MAX_CDP_PACKET_LEN] = { 0, };
+ 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;
- len =
- cdp_to_cc_data (self, inbuf, cc_data, sizeof (cc_data), &tc,
- &in_fps_entry);
+ 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))
+ 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;
- len = fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, cc_data, len,
- &tc);
- if (len >= 0) {
- gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
- memcpy (out.data, cc_data, len);
+ 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))
+ goto out;
+
+ gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
+ out_len = (guint) out.size;
+ if (!combine_cc_data (self, FALSE, out_fps_entry, ccp_data, ccp_data_len,
+ cea608_1, cea608_1_len, cea608_2, cea608_2_len, out.data, &out_len)) {
gst_buffer_unmap (outbuf, &out);
- self->output_frames++;
- } else {
- len = 0;
+ out_len = 0;
+ goto out;
}
+ gst_buffer_unmap (outbuf, &out);
+ self->output_frames++;
+
if (self->current_output_timecode.config.fps_n != 0
&& !gst_buffer_get_video_time_code_meta (inbuf)) {
gst_buffer_add_video_time_code_meta (outbuf,
@@ -1431,7 +1947,8 @@ convert_cea708_cdp_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
gst_video_time_code_increment_frame (&self->current_output_timecode);
}
- gst_buffer_set_size (outbuf, len);
+out:
+ gst_buffer_set_size (outbuf, out_len);
return GST_FLOW_OK;
}
@@ -1442,33 +1959,42 @@ convert_cea708_cdp_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
{
GstMapInfo out;
GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT;
- gint len = 0;
const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry;
- guint8 cc_data[MAX_CDP_PACKET_LEN] = { 0, };
+ 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;
+ guint out_len = 0;
- len =
- cdp_to_cc_data (self, inbuf, cc_data, sizeof (cc_data), &tc,
- &in_fps_entry);
+ 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))
+ 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;
- len = fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, cc_data, len,
- &tc);
- if (len >= 0) {
- gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
- len =
- convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, len,
- out.data, out.size, &self->current_output_timecode, out_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))
+ 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)) {
gst_buffer_unmap (outbuf, &out);
- self->output_frames++;
- } else {
- len = 0;
+ goto out;
}
- gst_buffer_set_size (outbuf, len);
+ gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
+ out_len =
+ convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, cc_data_len,
+ out.data, out.size, &self->current_output_timecode, out_fps_entry);
+
+ gst_buffer_unmap (outbuf, &out);
+ self->output_frames++;
+
+out:
+ gst_buffer_set_size (outbuf, out_len);
return GST_FLOW_OK;
}
@@ -1641,7 +2167,7 @@ can_generate_output (GstCCConverter * self)
g_assert_not_reached ();
if (!gst_util_fraction_multiply (self->out_fps_d, self->out_fps_n,
- self->output_frames + 1, 1, &output_frame_n, &output_frame_d))
+ self->output_frames, 1, &output_frame_n, &output_frame_d))
/* we should never overflow */
g_assert_not_reached ();
@@ -1715,7 +2241,8 @@ gst_cc_converter_sink_event (GstBaseTransform * trans, GstEvent * event)
case GST_EVENT_EOS:
GST_DEBUG_OBJECT (self, "received EOS");
- while (self->scratch_len > 0 || can_generate_output (self)) {
+ while (self->scratch_ccp_len > 0 || self->scratch_cea608_1_len > 0
+ || self->scratch_cea608_2_len > 0 || can_generate_output (self)) {
GstBuffer *outbuf;
GstFlowReturn ret;
@@ -1736,9 +2263,11 @@ gst_cc_converter_sink_event (GstBaseTransform * trans, GstEvent * event)
}
/* fallthrough */
case GST_EVENT_FLUSH_START:
- self->scratch_len = 0;
+ 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;
+ self->output_frames = 1;
gst_video_time_code_clear (&self->current_output_timecode);
gst_clear_buffer (&self->previous_buffer);
break;
@@ -1758,8 +2287,10 @@ gst_cc_converter_start (GstBaseTransform * base)
self->cdp_hdr_sequence_cntr = 0;
self->current_output_timecode = (GstVideoTimeCode) GST_VIDEO_TIME_CODE_INIT;
self->input_frames = 0;
- self->output_frames = 0;
- self->scratch_len = 0;
+ self->output_frames = 1;
+ self->scratch_ccp_len = 0;
+ self->scratch_cea608_1_len = 0;
+ self->scratch_cea608_2_len = 0;
return TRUE;
}
diff --git a/ext/closedcaption/gstccconverter.h b/ext/closedcaption/gstccconverter.h
index b51302ae3..7ed6e4285 100644
--- a/ext/closedcaption/gstccconverter.h
+++ b/ext/closedcaption/gstccconverter.h
@@ -41,6 +41,7 @@ typedef struct _GstCCConverter GstCCConverter;
typedef struct _GstCCConverterClass GstCCConverterClass;
#define MAX_CDP_PACKET_LEN 256
+#define MAX_CEA608_LEN 32
struct _GstCCConverter
{
@@ -58,8 +59,12 @@ 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[MAX_CDP_PACKET_LEN];
- guint scratch_len;
+ 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;
guint input_frames;
guint output_frames;