diff options
author | Mathieu Duponchelle <mathieu@centricular.com> | 2019-02-23 00:23:01 +0100 |
---|---|---|
committer | Mathieu Duponchelle <mathieu@centricular.com> | 2019-03-06 11:29:20 +0100 |
commit | 156865541fb18e93b445f759f940989bc66ccd7d (patch) | |
tree | ec014063d35c64a0d3a12e8f586ac2f8d09567f7 /ext | |
parent | fa8134ed1109e8dbc42a4fc74defdc668927fdaf (diff) | |
download | gstreamer-plugins-bad-156865541fb18e93b445f759f940989bc66ccd7d.tar.gz |
closedcaption: add line21 encoder
This element acts as a counterpart of line21encoder.
Also adds a simple test validating each element using the
other.
Diffstat (limited to 'ext')
-rw-r--r-- | ext/closedcaption/Makefile.am | 10 | ||||
-rw-r--r-- | ext/closedcaption/gstclosedcaption.c | 4 | ||||
-rw-r--r-- | ext/closedcaption/gstline21enc.c | 224 | ||||
-rw-r--r-- | ext/closedcaption/gstline21enc.h | 61 | ||||
-rw-r--r-- | ext/closedcaption/meson.build | 5 |
5 files changed, 300 insertions, 4 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, ) |