mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-29 05:01:23 +00:00
ccconverter: implement field conversion of CEA 608 control codes
As specified in EIA/CEA-608-B section 8.4: When closed captioning is used on line 21, field 2, it shall conform to all of the applicable specifications and recommended practices as defined for field 1 services with the following differences: a) The non-printing character of the miscellaneous control-character pairs that fall in the range of 14h, 20h to 14h, 2Fh in field 1, shall be replaced with 15h, 20h to 15h, 2Fh when used in field 2. b) The non-printing character of the miscellaneous control-character pairs that fall in the range of 1Ch, 20h to 1Ch, 2Fh in field 1, shall be replaced with 1Dh, 20h to 1Dh, 2Fh when used in field 2. This means simply switching the "field" field in the caps isn't enough for converting raw 608 from one field to another, some control codes also need to be amended. + Adds simple test Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4126>
This commit is contained in:
parent
b4cadf35bd
commit
2eab37f231
2 changed files with 95 additions and 5 deletions
|
@ -483,13 +483,9 @@ gst_cc_converter_set_caps (GstBaseTransform * base, GstCaps * incaps,
|
||||||
|
|
||||||
gst_video_time_code_clear (&self->current_output_timecode);
|
gst_video_time_code_clear (&self->current_output_timecode);
|
||||||
|
|
||||||
/* For raw 608 caps in and out, we can ignore the field to determine
|
|
||||||
* passthrough. We can also ignore framerate at this point, as our transform_caps
|
|
||||||
* implementation guarantees that it is either absent or present on both sides.
|
|
||||||
*/
|
|
||||||
if (gst_caps_is_subset (incaps, gst_static_caps_get (&raw_608_caps)) &&
|
if (gst_caps_is_subset (incaps, gst_static_caps_get (&raw_608_caps)) &&
|
||||||
gst_caps_is_subset (outcaps, gst_static_caps_get (&raw_608_caps))) {
|
gst_caps_is_subset (outcaps, gst_static_caps_get (&raw_608_caps))) {
|
||||||
passthrough = TRUE;
|
passthrough = self->in_field == self->out_field;
|
||||||
} else {
|
} else {
|
||||||
/* Caps can be different but we can passthrough as long as they can
|
/* Caps can be different but we can passthrough as long as they can
|
||||||
* intersect, i.e. have same caps name and format */
|
* intersect, i.e. have same caps name and format */
|
||||||
|
@ -730,6 +726,85 @@ convert_cea608_raw_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline guint8
|
||||||
|
eia608_parity_strip (guint8 cc_data)
|
||||||
|
{
|
||||||
|
return cc_data & 0x7F;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
convert_cea608_raw_cea608_raw (GstCCConverter * self, GstBuffer * inbuf,
|
||||||
|
GstBuffer * outbuf)
|
||||||
|
{
|
||||||
|
GstMapInfo in, out;
|
||||||
|
guint i, n;
|
||||||
|
|
||||||
|
g_assert (self->in_field != self->out_field);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
n /= 2;
|
||||||
|
|
||||||
|
if (n > 3) {
|
||||||
|
GST_WARNING_OBJECT (self, "Too many CEA608 pairs %u. Truncating to %u", n,
|
||||||
|
3);
|
||||||
|
n = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_buffer_set_size (outbuf, 2 * n);
|
||||||
|
|
||||||
|
gst_buffer_map (inbuf, &in, GST_MAP_READ);
|
||||||
|
gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
|
||||||
|
|
||||||
|
/* EIA/CEA-608-B 8.4 Closed Caption Mode:
|
||||||
|
*
|
||||||
|
* When closed captioning is used on line 21, field 2,
|
||||||
|
* it shall conform to all of the applicable specifications and
|
||||||
|
* recommended practices as defined for field 1 services with the
|
||||||
|
* following differences:
|
||||||
|
*
|
||||||
|
* a) The non-printing character of the miscellaneous control-character pairs
|
||||||
|
* that fall in the range of 14h, 20h to 14h, 2Fh in field 1, shall be
|
||||||
|
* replaced with 15h, 20h to 15h, 2Fh when used in field 2.
|
||||||
|
*
|
||||||
|
* b) The non-printing character of the miscellaneous control-character pairs
|
||||||
|
* that fall in the range of 1Ch, 20h to 1Ch, 2Fh in field 1, shall be replaced
|
||||||
|
* with 1Dh, 20h to 1Dh, 2Fh when used in field 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
guint8 cc_data_1 = eia608_parity_strip (in.data[i * 2]);
|
||||||
|
guint8 cc_data_2 = eia608_parity_strip (in.data[i * 2 + 1]);
|
||||||
|
|
||||||
|
out.data[i * 2] = in.data[i * 2];
|
||||||
|
out.data[i * 2 + 1] = in.data[i * 2 + 1];
|
||||||
|
|
||||||
|
if (self->in_field == 0 && self->out_field == 1) {
|
||||||
|
if (cc_data_1 == 0x14 && cc_data_2 >= 0x20 && cc_data_2 <= 0x2f) {
|
||||||
|
out.data[i * 2] = 0x15;
|
||||||
|
} else if (cc_data_1 == 0x1c && cc_data_2 >= 0x20 && cc_data_2 <= 0x2f) {
|
||||||
|
out.data[i * 2] = 0x9d;
|
||||||
|
}
|
||||||
|
} else if (self->in_field == 1 && self->out_field == 0) {
|
||||||
|
if (cc_data_1 == 0x15 && cc_data_2 >= 0x20 && cc_data_2 <= 0x2f) {
|
||||||
|
out.data[i * 2] = 0x94;
|
||||||
|
} else if (cc_data_1 == 0x1d && cc_data_2 >= 0x20 && cc_data_2 <= 0x2f) {
|
||||||
|
out.data[i * 2] = 0x1c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_buffer_unmap (inbuf, &in);
|
||||||
|
gst_buffer_unmap (outbuf, &out);
|
||||||
|
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
convert_cea608_raw_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
|
convert_cea608_raw_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
|
||||||
GstBuffer * outbuf)
|
GstBuffer * outbuf)
|
||||||
|
@ -1370,6 +1445,8 @@ gst_cc_converter_transform (GstCCConverter * self, GstBuffer * inbuf,
|
||||||
ret = convert_cea608_raw_cea708_cdp (self, inbuf, outbuf, tc_meta);
|
ret = convert_cea608_raw_cea708_cdp (self, inbuf, outbuf, tc_meta);
|
||||||
break;
|
break;
|
||||||
case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
|
case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
|
||||||
|
ret = convert_cea608_raw_cea608_raw (self, inbuf, outbuf);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
g_assert_not_reached ();
|
g_assert_not_reached ();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1067,6 +1067,18 @@ GST_START_TEST (convert_cea708_cdp_cea608_raw_field_one)
|
||||||
|
|
||||||
GST_END_TEST;
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (convert_cea608_raw_cea608_raw_field_one)
|
||||||
|
{
|
||||||
|
/* Control codes must be translated when converting from field 0 to field 1 */
|
||||||
|
const guint8 in[] = { 0x94, 0x20 };
|
||||||
|
const guint8 out[] = { 0x15, 0x20 };
|
||||||
|
check_conversion_tc_passthrough (in, sizeof (in), out, sizeof (out),
|
||||||
|
"closedcaption/x-cea-608,format=(string)raw,field=(int)0",
|
||||||
|
"closedcaption/x-cea-608,format=(string)raw,field=(int)1");
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
GST_START_TEST (convert_cea708_cdp_cea708_cc_data_double_input_data)
|
GST_START_TEST (convert_cea708_cdp_cea708_cc_data_double_input_data)
|
||||||
{
|
{
|
||||||
/* caps say 60fps, data has 30fps. Ensure data is taken alternatatively from
|
/* caps say 60fps, data has 30fps. Ensure data is taken alternatatively from
|
||||||
|
@ -1290,6 +1302,7 @@ ccextractor_suite (void)
|
||||||
tcase_add_test (tc, convert_cea608_s334_1a_cea608_raw_field_one);
|
tcase_add_test (tc, convert_cea608_s334_1a_cea608_raw_field_one);
|
||||||
tcase_add_test (tc, convert_cea708_cc_data_cea608_raw_field_one);
|
tcase_add_test (tc, convert_cea708_cc_data_cea608_raw_field_one);
|
||||||
tcase_add_test (tc, convert_cea708_cdp_cea608_raw_field_one);
|
tcase_add_test (tc, convert_cea708_cdp_cea608_raw_field_one);
|
||||||
|
tcase_add_test (tc, convert_cea608_raw_cea608_raw_field_one);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue