summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathieu Duponchelle <mathieu@centricular.com>2019-02-23 00:23:01 +0100
committerMathieu Duponchelle <mathieu@centricular.com>2019-03-06 11:29:20 +0100
commit156865541fb18e93b445f759f940989bc66ccd7d (patch)
treeec014063d35c64a0d3a12e8f586ac2f8d09567f7
parentfa8134ed1109e8dbc42a4fc74defdc668927fdaf (diff)
closedcaption: add line21 encoder
This element acts as a counterpart of line21encoder. Also adds a simple test validating each element using the other.
-rw-r--r--ext/closedcaption/Makefile.am10
-rw-r--r--ext/closedcaption/gstclosedcaption.c4
-rw-r--r--ext/closedcaption/gstline21enc.c224
-rw-r--r--ext/closedcaption/gstline21enc.h61
-rw-r--r--ext/closedcaption/meson.build5
-rw-r--r--tests/check/Makefile.am2
-rw-r--r--tests/check/elements/line21.c103
-rw-r--r--tests/check/meson.build1
8 files changed, 405 insertions, 5 deletions
diff --git a/ext/closedcaption/Makefile.am b/ext/closedcaption/Makefile.am
index dd6914215..7a2a839a8 100644
--- a/ext/closedcaption/Makefile.am
+++ b/ext/closedcaption/Makefile.am
@@ -3,13 +3,16 @@ plugin_LTLIBRARIES = libgstclosedcaption.la
zvbi_sources = \
bit_slicer.c \
decoder.c \
- raw_decoder.c \
+ io-sim.c \
+ raw_decoder.c \
sampling_par.c
zvbi_headers = \
bcd.h \
bit_slicer.h \
decoder.h \
+ hamm.h \
+ io-sim.h \
macros.h \
misc.h \
raw_decoder.h \
@@ -25,6 +28,7 @@ libgstclosedcaption_la_SOURCES = \
gstcea708decoder.c \
gstceaccoverlay.c \
gstline21dec.c \
+ gstline21enc.c \
gstclosedcaption.c
libgstclosedcaption_la_CFLAGS = \
@@ -37,7 +41,8 @@ libgstclosedcaption_la_LIBADD = \
$(GST_PLUGINS_BASE_LIBS) -lgstvideo-@GST_API_VERSION@ \
$(GST_BASE_LIBS) \
$(GST_LIBS) \
- $(PANGO_LIBS)
+ $(PANGO_LIBS) \
+ $(LIBM)
libgstclosedcaption_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
@@ -48,4 +53,5 @@ noinst_HEADERS = \
gstcea708decoder.h \
gstceaccoverlay.h \
gstline21dec.h \
+ gstline21enc.h \
$(zvbi_headers)
diff --git a/ext/closedcaption/gstclosedcaption.c b/ext/closedcaption/gstclosedcaption.c
index dde31c8f8..daab0af3b 100644
--- a/ext/closedcaption/gstclosedcaption.c
+++ b/ext/closedcaption/gstclosedcaption.c
@@ -30,6 +30,7 @@
#include "gstccextractor.h"
#include "gstline21dec.h"
#include "gstceaccoverlay.h"
+#include "gstline21enc.h"
static gboolean
closedcaption_init (GstPlugin * plugin)
@@ -51,6 +52,9 @@ closedcaption_init (GstPlugin * plugin)
ret &= gst_element_register (plugin, "cc708overlay", GST_RANK_PRIMARY,
GST_TYPE_CEA_CC_OVERLAY);
+ ret &= gst_element_register (plugin, "line21encoder", GST_RANK_NONE,
+ GST_TYPE_LINE21ENCODER);
+
return ret;
}
diff --git a/ext/closedcaption/gstline21enc.c b/ext/closedcaption/gstline21enc.c
new file mode 100644
index 000000000..1a63fe542
--- /dev/null
+++ b/ext/closedcaption/gstline21enc.c
@@ -0,0 +1,224 @@
+/*
+ * GStreamer
+ * Copyright (C) 2019 Mathieu Duponchelle <mathieu@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:element-line21encoder
+ * @title: line21encoder
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <string.h>
+
+#include "gstline21enc.h"
+#include "io-sim.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_line_21_encoder_debug);
+#define GST_CAT_DEFAULT gst_line_21_encoder_debug
+
+#define CAPS "video/x-raw, format={ I420, YUY2, YVYU, UYVY, VYUY }, width=(int)720, height=(int)[ 23, MAX ], interlace-mode=interleaved"
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (CAPS));
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (CAPS));
+
+G_DEFINE_TYPE (GstLine21Encoder, gst_line_21_encoder, GST_TYPE_VIDEO_FILTER);
+#define parent_class gst_line_21_encoder_parent_class
+
+static gboolean gst_line_21_encoder_set_info (GstVideoFilter * filter,
+ GstCaps * incaps, GstVideoInfo * in_info,
+ GstCaps * outcaps, GstVideoInfo * out_info);
+static GstFlowReturn gst_line_21_encoder_transform_ip (GstVideoFilter * filter,
+ GstVideoFrame * frame);
+
+static void
+gst_line_21_encoder_class_init (GstLine21EncoderClass * klass)
+{
+ GstElementClass *gstelement_class;
+ GstVideoFilterClass *filter_class;
+
+ gstelement_class = (GstElementClass *) klass;
+ filter_class = (GstVideoFilterClass *) klass;
+
+ gst_element_class_set_static_metadata (gstelement_class,
+ "Line 21 CC Encoder",
+ "Filter/Video/ClosedCaption",
+ "Inject line21 CC in SD video streams",
+ "Mathieu Duponchelle <mathieu@centricular.com>");
+
+ gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
+ gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
+
+ filter_class->set_info = gst_line_21_encoder_set_info;
+ filter_class->transform_frame_ip = gst_line_21_encoder_transform_ip;
+
+ GST_DEBUG_CATEGORY_INIT (gst_line_21_encoder_debug, "line21encoder",
+ 0, "Line 21 CC Encoder");
+ vbi_initialize_gst_debug ();
+}
+
+static void
+gst_line_21_encoder_init (GstLine21Encoder * filter)
+{
+}
+
+static vbi_pixfmt
+vbi_pixfmt_from_gst_video_format (GstVideoFormat format)
+{
+ switch (format) {
+ case GST_VIDEO_FORMAT_I420:
+ return VBI_PIXFMT_YUV420;
+ case GST_VIDEO_FORMAT_YUY2:
+ return VBI_PIXFMT_YUYV;
+ case GST_VIDEO_FORMAT_YVYU:
+ return VBI_PIXFMT_YVYU;
+ case GST_VIDEO_FORMAT_UYVY:
+ return VBI_PIXFMT_UYVY;
+ case GST_VIDEO_FORMAT_VYUY:
+ return VBI_PIXFMT_VYUY;
+ default:
+ g_assert_not_reached ();
+ return (vbi_pixfmt) 0;
+ }
+#undef NATIVE_VBI_FMT
+}
+
+static gboolean
+gst_line_21_encoder_set_info (GstVideoFilter * filter,
+ GstCaps * incaps, GstVideoInfo * in_info,
+ GstCaps * outcaps, GstVideoInfo * out_info)
+{
+ GstLine21Encoder *self = GST_LINE21ENCODER (filter);
+
+ self->info = *in_info;
+
+ /*
+ * Set up blank / black / white levels fit for NTSC, no actual relation
+ * with the height of the video
+ */
+ self->sp.scanning = 525;
+ /* The pixel format */
+ self->sp.sampling_format =
+ vbi_pixfmt_from_gst_video_format (GST_VIDEO_INFO_FORMAT (&self->info));
+ /* Sampling rate. For BT.601 it's 13.5MHz */
+ self->sp.sampling_rate = 13.5e6;
+ /* Stride */
+ self->sp.bytes_per_line = GST_VIDEO_INFO_COMP_STRIDE (&self->info, 0);
+ /* Horizontal offset of the VBI image */
+ self->sp.offset = 122;
+
+ /* FIXME: magic numbers */
+ self->sp.start[0] = 21;
+ self->sp.count[0] = 1;
+ self->sp.start[1] = 284;
+ self->sp.count[1] = 1;
+
+ self->sp.interlaced = FALSE;
+ self->sp.synchronous = TRUE;
+
+ return TRUE;
+}
+
+static GstFlowReturn
+gst_line_21_encoder_transform_ip (GstVideoFilter * filter,
+ GstVideoFrame * frame)
+{
+ GstLine21Encoder *self = GST_LINE21ENCODER (filter);
+ GstVideoCaptionMeta *cc_meta;
+ guint8 *buf;
+ vbi_sliced sliced[2];
+ gpointer iter = NULL;
+ GstFlowReturn ret = GST_FLOW_ERROR;
+
+ sliced[0].id = VBI_SLICED_CAPTION_525_F1;
+ sliced[0].line = self->sp.start[0];
+ sliced[1].id = VBI_SLICED_CAPTION_525_F2;
+ sliced[1].line = self->sp.start[1];
+
+ sliced[0].data[0] = 0x80;
+ sliced[0].data[1] = 0x80;
+ sliced[1].data[0] = 0x80;
+ sliced[1].data[1] = 0x80;
+
+ /* We loop over caption metas until we find the first CEA608 meta */
+ while ((cc_meta = (GstVideoCaptionMeta *)
+ gst_buffer_iterate_meta_filtered (frame->buffer, &iter,
+ GST_VIDEO_CAPTION_META_API_TYPE))) {
+ guint n = cc_meta->size;
+ guint i;
+
+ if (cc_meta->caption_type != GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A)
+ continue;
+
+ if (n % 3 != 0) {
+ GST_ERROR_OBJECT (filter, "Invalid S334-1A CEA608 buffer size");
+ goto done;
+ }
+
+ n /= 3;
+
+ if (n >= 3) {
+ GST_ERROR_OBJECT (filter, "Too many S334-1A CEA608 triplets %u", n);
+ goto done;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (cc_meta->data[i * 3] & 0x80) {
+ sliced[0].data[0] = cc_meta->data[i * 3 + 1];
+ sliced[0].data[1] = cc_meta->data[i * 3 + 2];
+ } else {
+ sliced[1].data[0] = cc_meta->data[i * 3 + 1];
+ sliced[1].data[1] = cc_meta->data[i * 3 + 2];
+ }
+ }
+
+ break;
+ }
+
+ /* We've encoded this meta, it can now be removed */
+ if (cc_meta)
+ gst_buffer_remove_meta (frame->buffer, (GstMeta *) cc_meta);
+
+ buf =
+ (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (frame,
+ 0) + 21 * GST_VIDEO_INFO_COMP_STRIDE (&self->info, 0);
+
+ if (!vbi_raw_video_image (buf, GST_VIDEO_INFO_COMP_STRIDE (&self->info,
+ 0) * 2, &self->sp, 0, 0, 0, 0x000000FF, 0, sliced, 2)) {
+ GST_ERROR_OBJECT (filter, "Failed to encode CC data");
+ goto done;
+ }
+
+ ret = GST_FLOW_OK;
+
+done:
+ return ret;
+}
diff --git a/ext/closedcaption/gstline21enc.h b/ext/closedcaption/gstline21enc.h
new file mode 100644
index 000000000..ac89f2cd5
--- /dev/null
+++ b/ext/closedcaption/gstline21enc.h
@@ -0,0 +1,61 @@
+/*
+ * GStreamer
+ * Copyright (C) 2019 Mathieu Duponchelle <mathieu@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_LINE21ENCODER_H__
+#define __GST_LINE21ENCODER_H__
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/video/video-anc.h>
+#include "io-sim.h"
+
+G_BEGIN_DECLS
+#define GST_TYPE_LINE21ENCODER \
+ (gst_line_21_encoder_get_type())
+#define GST_LINE21ENCODER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_LINE21ENCODER,GstLine21Encoder))
+#define GST_LINE21ENCODER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_LINE21ENCODER,GstLine21EncoderClass))
+#define GST_IS_LINE21ENCODER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_LINE21ENCODER))
+#define GST_IS_LINE21ENCODER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_LINE21ENCODER))
+
+typedef struct _GstLine21Encoder GstLine21Encoder;
+typedef struct _GstLine21EncoderClass GstLine21EncoderClass;
+
+struct _GstLine21Encoder
+{
+ GstVideoFilter parent;
+
+ vbi_sampling_par sp;
+
+ GstVideoInfo info;
+};
+
+struct _GstLine21EncoderClass
+{
+ GstVideoFilterClass parent_class;
+};
+
+GType gst_line_21_encoder_get_type (void);
+
+G_END_DECLS
+#endif /* __GST_LINE21ENCODER_H__ */
diff --git a/ext/closedcaption/meson.build b/ext/closedcaption/meson.build
index c5c971b2e..50329df25 100644
--- a/ext/closedcaption/meson.build
+++ b/ext/closedcaption/meson.build
@@ -12,11 +12,12 @@ zvbi_sources = [
if pangocairo_dep.found()
gstclosedcaption = library('gstclosedcaption',
'gstcccombiner.c', 'gstccextractor.c', 'gstccconverter.c', 'gstclosedcaption.c',
- 'gstline21dec.c', 'gstcea708decoder.c', 'gstceaccoverlay.c', zvbi_sources,
+ 'gstline21dec.c', 'gstcea708decoder.c', 'gstceaccoverlay.c', 'gstline21enc.c',
+ zvbi_sources,
c_args : gst_plugins_bad_args,
link_args : noseh_link_args,
include_directories : [configinc],
- dependencies : [gstvideo_dep, gstbase_dep, gst_dep, pangocairo_dep],
+ dependencies : [gstvideo_dep, gstbase_dep, gst_dep, pangocairo_dep, libm],
install : true,
install_dir : plugins_install_dir,
)
diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am
index 36518d449..a07ce8277 100644
--- a/tests/check/Makefile.am
+++ b/tests/check/Makefile.am
@@ -41,7 +41,7 @@ check_assrender =
endif
if USE_PANGO
-check_closedcaption = elements/ccconverter elements/cccombiner elements/ccextractor
+check_closedcaption = elements/ccconverter elements/cccombiner elements/ccextractor elements/line21
else
check_closedcaption =
endif
diff --git a/tests/check/elements/line21.c b/tests/check/elements/line21.c
new file mode 100644
index 000000000..029ff238f
--- /dev/null
+++ b/tests/check/elements/line21.c
@@ -0,0 +1,103 @@
+/* GStreamer
+ *
+ * Copyright (C) 2019 Mathieu Duponchelle <mathieu@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <gst/gst.h>
+#include <gst/check/gstcheck.h>
+#include <gst/check/gstharness.h>
+#include <gst/video/video.h>
+
+GST_START_TEST (basic)
+{
+ GstHarness *h;
+ GstBuffer *buf, *outbuf;
+ GstVideoInfo info;
+ GstVideoCaptionMeta *in_cc_meta, *out_cc_meta;
+ guint i;
+ guint8 empty_data[] = { 0x90, 0x80, 0x80, 0x0, 0x80, 0x80 };
+ guint8 full_data[] = { 0x90, 0x42, 0x43, 0x0, 0x44, 0x45 };
+ GstCaps *caps = gst_caps_new_simple ("video/x-raw",
+ "format", G_TYPE_STRING, "I420",
+ "width", G_TYPE_INT, 720,
+ "height", G_TYPE_INT, 625,
+ "interlace-mode", G_TYPE_STRING, "interleaved",
+ NULL);
+
+ h = gst_harness_new_parse ("line21encoder ! line21decoder");
+ gst_harness_set_caps (h, gst_caps_ref (caps), gst_caps_ref (caps));
+
+ gst_video_info_from_caps (&info, caps);
+
+ gst_caps_unref (caps);
+
+ buf = gst_buffer_new_and_alloc (info.size);
+ outbuf = gst_harness_push_and_pull (h, buf);
+
+ fail_unless (outbuf != NULL);
+ fail_unless_equals_int (gst_buffer_get_n_meta (outbuf,
+ GST_VIDEO_CAPTION_META_API_TYPE), 1);
+
+ out_cc_meta = gst_buffer_get_video_caption_meta (outbuf);
+
+ fail_unless (out_cc_meta != NULL);
+ fail_unless (out_cc_meta->size == 6);
+
+ for (i = 0; i < out_cc_meta->size; i++)
+ fail_unless (out_cc_meta->data[i] == empty_data[i]);
+
+ gst_buffer_unref (outbuf);
+
+ buf = gst_buffer_new_and_alloc (info.size);
+ gst_buffer_add_video_caption_meta (buf, GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A,
+ full_data, 6);
+ in_cc_meta = gst_buffer_get_video_caption_meta (buf);
+ outbuf = gst_harness_push_and_pull (h, buf);
+
+ fail_unless (outbuf != NULL);
+ fail_unless_equals_int (gst_buffer_get_n_meta (outbuf,
+ GST_VIDEO_CAPTION_META_API_TYPE), 1);
+
+ out_cc_meta = gst_buffer_get_video_caption_meta (outbuf);
+ fail_unless (in_cc_meta != out_cc_meta);
+
+ for (i = 0; i < out_cc_meta->size; i++)
+ fail_unless (out_cc_meta->data[i] == full_data[i]);
+
+ gst_buffer_unref (outbuf);
+}
+
+GST_END_TEST;
+
+static Suite *
+line21_suite (void)
+{
+ Suite *s = suite_create ("line21");
+ TCase *tc = tcase_create ("general");
+
+ suite_add_tcase (s, tc);
+
+ tcase_add_test (tc, basic);
+
+ return s;
+}
+
+GST_CHECK_MAIN (line21);
diff --git a/tests/check/meson.build b/tests/check/meson.build
index 1007b7554..90a334745 100644
--- a/tests/check/meson.build
+++ b/tests/check/meson.build
@@ -58,6 +58,7 @@ if host_machine.system() != 'windows'
[['elements/ccconverter.c']],
[['elements/cccombiner.c']],
[['elements/ccextractor.c']],
+ [['elements/line21.c']],
[['elements/curlhttpsink.c'], not curl_dep.found(), [curl_dep]],
[['elements/curlhttpsrc.c'], not curl_dep.found(), [curl_dep, gio_dep]],
[['elements/curlfilesink.c'],