[vlc-devel] [PATCH 1/3] Initial support for EIA-608 closed captions via V4L2 VBI devices
Devin Heitmueller
dheitmueller at kernellabs.com
Sun Oct 21 22:01:00 CEST 2012
This patch provides initial support for sending EIA-608 closed captions
provided via the /dev/vbi interface to the VLC CC decoder. It makes use of
the ZVBI library, which is what does the VBI slicing.
Validated with the Hauppauge HVR-950q.
---
configure.ac | 4 +-
modules/access/Modules.am | 3 +-
modules/access/v4l2/demux.c | 127 +++++++++++++++++++++++++++++++++++++++----
modules/access/v4l2/v4l2.c | 46 ++++++++++++++++
modules/access/v4l2/v4l2.h | 14 +++++
modules/access/v4l2/vbi.c | 86 +++++++++++++++++++++++++++++
6 files changed, 267 insertions(+), 13 deletions(-)
create mode 100644 modules/access/v4l2/vbi.c
diff --git a/configure.ac b/configure.ac
index a748266..6ef4b11 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2800,11 +2800,11 @@ AC_ARG_ENABLE(telx,
AS_IF( [test "${enable_zvbi}" != "no"],[
PKG_CHECK_MODULES(ZVBI, [zvbi-0.2 >= 0.2.28],
[
- VLC_ADD_LIBS([zvbi],[$ZVBI_LIBS])
+ VLC_ADD_LIBS([zvbi v4l2],[$ZVBI_LIBS])
if test "${SYS}" = "mingw32"; then
VLC_ADD_LIBS([zvbi],[ -lpthreadGC2])
fi
- VLC_ADD_CFLAGS([zvbi],[$ZVBI_CFLAGS])
+ VLC_ADD_CFLAGS([zvbi v4l2],[$ZVBI_CFLAGS])
VLC_ADD_PLUGIN([zvbi])
AC_DEFINE(ZVBI_COMPILED, 1, [Define if the zvbi module is built])
AS_IF( [test "${enable_telx}" = "yes"],[
diff --git a/modules/access/Modules.am b/modules/access/Modules.am
index 5665477..764472d 100644
--- a/modules/access/Modules.am
+++ b/modules/access/Modules.am
@@ -132,6 +132,7 @@ libv4l2_plugin_la_SOURCES = \
v4l2/videodev2.h \
v4l2/v4l2.c \
v4l2/video.c \
+ v4l2/vbi.c \
v4l2/demux.c \
v4l2/access.c \
v4l2/radio.c \
@@ -139,7 +140,7 @@ libv4l2_plugin_la_SOURCES = \
v4l2/lib.c \
v4l2/v4l2.h
libv4l2_plugin_la_CFLAGS = $(AM_CFLAGS)
-libv4l2_plugin_la_LIBADD = $(AM_LIBADD) $(LIBDL) $(LIBM)
+libv4l2_plugin_la_LIBADD = $(AM_LIBADD) $(LIBDL) $(LIBM) $(LIBS_v4l2)
if HAVE_V4L2
libvlc_LTLIBRARIES += libv4l2_plugin.la
endif
diff --git a/modules/access/v4l2/demux.c b/modules/access/v4l2/demux.c
index 15b540f..21e1709 100644
--- a/modules/access/v4l2/demux.c
+++ b/modules/access/v4l2/demux.c
@@ -58,6 +58,14 @@ struct demux_sys_t
es_out_id_t *es;
vlc_v4l2_ctrl_t *controls;
mtime_t start;
+
+#ifdef ZVBI_COMPILED
+ vbi_capture *vbi_cap;
+ es_out_id_t *p_es_subt[4];
+ uint8_t* vbi_raw;
+ vbi_sliced* vbi_sliced;
+ unsigned int vbi_sliced_size;
+#endif
};
static void *UserPtrThread (void *);
@@ -65,6 +73,9 @@ static void *MmapThread (void *);
static void *ReadThread (void *);
static int DemuxControl( demux_t *, int, va_list );
static int InitVideo (demux_t *, int fd, uint32_t caps);
+#ifdef ZVBI_COMPILED
+static int InitVBI (demux_t *);
+#endif
int DemuxOpen( vlc_object_t *obj )
{
@@ -88,6 +99,17 @@ int DemuxOpen( vlc_object_t *obj )
goto error;
sys->fd = fd;
+#ifdef ZVBI_COMPILED
+ char *vbi_path = var_InheritString (obj, CFG_PREFIX"vbidev");
+ if (vbi_path != NULL)
+ {
+ sys->vbi_cap = OpenVBIDev (obj, vbi_path);
+ if (sys->vbi_cap)
+ InitVBI(demux);
+ free(vbi_path);
+ }
+#endif
+
if (InitVideo (demux, fd, caps))
{
v4l2_close (fd);
@@ -448,6 +470,42 @@ static int InitVideo (demux_t *demux, int fd, uint32_t caps)
return 0;
}
+#ifdef ZVBI_COMPILED
+static int InitVBI (demux_t *demux)
+{
+ int i;
+ demux_sys_t *sys = demux->p_sys;
+ vbi_raw_decoder *par;
+
+ par = vbi_capture_parameters (sys->vbi_cap);
+ if (!par)
+ {
+ msg_Err( demux, "Cannot get vbi capture parameters");
+ } else {
+ unsigned int src_w = par->bytes_per_line / 1;
+ unsigned int src_h = par->count[0] + par->count[1];
+ sys->vbi_raw = calloc (1, src_w * src_h);
+ sys->vbi_sliced_size = sizeof (vbi_sliced) * src_h;
+ sys->vbi_sliced = malloc (sys->vbi_sliced_size);
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ es_format_t fmt;
+ char buf[5];
+
+ es_format_Init( &fmt, SPU_ES, VLC_FOURCC('c','c',0x31 + i,' ') );
+ snprintf(buf, sizeof(buf), "CC%d", i + 1);
+ fmt.psz_description = strdup(buf);
+ msg_Dbg( demux, "new spu es %4.4s", (char*)&fmt.i_codec );
+
+ sys->p_es_subt[i] = es_out_Add( demux->out, &fmt );
+ }
+
+ return 0;
+}
+#endif
+
void DemuxClose( vlc_object_t *obj )
{
demux_t *demux = (demux_t *)obj;
@@ -459,6 +517,16 @@ void DemuxClose( vlc_object_t *obj )
StopMmap (sys->fd, sys->bufv, sys->bufc);
ControlsDeinit( obj, sys->controls );
v4l2_close (sys->fd);
+
+#ifdef ZVBI_COMPILED
+ if( sys->vbi_cap )
+ {
+ vbi_capture_delete( sys->vbi_cap );
+ }
+ free( sys->vbi_raw );
+ free( sys->vbi_sliced );
+#endif
+
free( sys );
}
@@ -551,31 +619,70 @@ static void *MmapThread (void *data)
demux_t *demux = data;
demux_sys_t *sys = demux->p_sys;
int fd = sys->fd;
- struct pollfd ufd[1];
+ struct pollfd ufd[2];
+ nfds_t numfds = 1;
ufd[0].fd = fd;
ufd[0].events = POLLIN;
+#ifdef ZVBI_COMPILED
+ if ( sys->vbi_cap )
+ {
+ ufd[1].fd = vbi_capture_fd(sys->vbi_cap);
+ ufd[1].events = POLLIN;
+ ufd[1].revents = 0;
+ numfds++;
+
+ /* Do a single read and throw away the results so that ZVBI calls
+ the STREAMON ioctl() */
+ GrabVBI( VLC_OBJECT(demux), sys->vbi_cap, sys->vbi_sliced,
+ sys->vbi_sliced_size, sys->vbi_raw );
+ }
+#endif
+
for (;;)
{
/* Wait for data */
- if (poll (ufd, 1, -1) == -1)
+ if (poll (ufd, numfds, -1) == -1)
{
if (errno != EINTR)
msg_Err (demux, "poll error: %m");
continue;
}
- int canc = vlc_savecancel ();
- block_t *block = GrabVideo (VLC_OBJECT(demux), fd, sys->bufv);
- if (block != NULL)
+ if( ufd[0].revents & POLLIN )
{
- block->i_pts = block->i_dts = mdate ();
- block->i_flags |= sys->block_flags;
- es_out_Control (demux->out, ES_OUT_SET_PCR, block->i_pts);
- es_out_Send (demux->out, sys->es, block);
+ int canc = vlc_savecancel ();
+ block_t *block = GrabVideo (VLC_OBJECT(demux), fd, sys->bufv);
+ if (block != NULL)
+ {
+ block->i_pts = block->i_dts = mdate ();
+ block->i_flags |= sys->block_flags;
+ es_out_Control (demux->out, ES_OUT_SET_PCR, block->i_pts);
+ es_out_Send (demux->out, sys->es, block);
+ }
+ vlc_restorecancel (canc);
}
- vlc_restorecancel (canc);
+#ifdef ZVBI_COMPILED
+ if( sys->vbi_cap && (ufd[1].revents & POLLIN) )
+ {
+ block_t *p_block = GrabVBI( VLC_OBJECT(demux), sys->vbi_cap,
+ sys->vbi_sliced, sys->vbi_sliced_size,
+ sys->vbi_raw );
+ if( p_block )
+ {
+ int i;
+ for (i = 0; i < 4; i++)
+ {
+ if (i == 3)
+ es_out_Send( demux->out, sys->p_es_subt[i], p_block );
+ else
+ es_out_Send( demux->out, sys->p_es_subt[i],
+ block_Duplicate(p_block) );
+ }
+ }
+ }
+#endif
}
assert (0);
diff --git a/modules/access/v4l2/v4l2.c b/modules/access/v4l2/v4l2.c
index 878fb81..989a3c3 100644
--- a/modules/access/v4l2/v4l2.c
+++ b/modules/access/v4l2/v4l2.c
@@ -43,6 +43,10 @@
#define VIDEO_DEVICE_TEXT N_( "Video capture device" )
#define VIDEO_DEVICE_LONGTEXT N_("Video capture device node." )
+#define VBI_DEVICE_TEXT N_("VBI capture device")
+#define VBI_DEVICE_LONGTEXT N_( \
+ "The device node where VBI data can be read " \
+ " (for closed captions) " )
#define STANDARD_TEXT N_( "Standard" )
#define STANDARD_LONGTEXT N_( \
"Video standard (Default, SECAM, PAL, or NTSC)." )
@@ -277,6 +281,10 @@ vlc_module_begin ()
add_loadfile( CFG_PREFIX "dev", "/dev/video0",
VIDEO_DEVICE_TEXT, VIDEO_DEVICE_LONGTEXT, false )
change_safe()
+#ifdef ZVBI_COMPILED
+ add_loadfile( CFG_PREFIX "vbidev", NULL,
+ VBI_DEVICE_TEXT, VBI_DEVICE_LONGTEXT, false )
+#endif
add_string( CFG_PREFIX "standard", "",
STANDARD_TEXT, STANDARD_LONGTEXT, false )
change_string_list( standards_vlc, standards_user )
@@ -502,6 +510,44 @@ int OpenDevice (vlc_object_t *obj, const char *path, uint32_t *restrict caps)
return fd;
}
+ /*****************************************************************************
+ * OpenVBIDev:
+ *****************************************************************************/
+#ifdef ZVBI_COMPILED
+vbi_capture* OpenVBIDev( vlc_object_t *p_obj, const char *psz_device)
+{
+ vbi_capture *cap;
+ char* errstr = NULL;
+
+ //Can put more in here. See osd.c in zvbi package.
+ unsigned int services = VBI_SLICED_CAPTION_525;
+
+ cap = vbi_capture_v4l2_new (psz_device,
+ /* buffers */ 5,
+ &services,
+ /* strict */ 1,
+ &errstr,
+ /* verbose */ 1);
+ if (cap == NULL)
+ {
+ msg_Err( p_obj, "Cannot capture vbi data with v4l2 interface (%s)", errstr );
+ }
+#if defined( HAVE_FCNTL )
+ else
+ {
+ /* We need to ensure that the /dev/vbiX filehandle doesn't get
+ inherited by child processes that might get spawned. In particular,
+ the screensaver keepalive will inherit the handle, which will
+ prevent it from being closed properly */
+ int fd = vbi_capture_fd(cap);
+ fcntl( fd, F_SETFD, fcntl( fd, F_GETFD ) | FD_CLOEXEC );
+ }
+#endif
+
+ return cap;
+}
+#endif
+
v4l2_std_id var_InheritStandard (vlc_object_t *obj, const char *varname)
{
char *name = var_InheritString (obj, varname);
diff --git a/modules/access/v4l2/v4l2.h b/modules/access/v4l2/v4l2.h
index fa0e4e7..97f0764 100644
--- a/modules/access/v4l2/v4l2.h
+++ b/modules/access/v4l2/v4l2.h
@@ -20,6 +20,10 @@
#include "videodev2.h"
+#ifdef ZVBI_COMPILED
+# include <libzvbi.h>
+#endif
+
/* libv4l2 functions */
extern int v4l2_fd_open (int, int);
extern int (*v4l2_close) (int);
@@ -41,6 +45,9 @@ struct buffer_t
/* v4l2.c */
void ParseMRL(vlc_object_t *, const char *);
int OpenDevice (vlc_object_t *, const char *, uint32_t *);
+#ifdef ZVBI_COMPILED
+vbi_capture* OpenVBIDev( vlc_object_t *, const char *);
+#endif
v4l2_std_id var_InheritStandard (vlc_object_t *, const char *);
/* video.c */
@@ -57,6 +64,13 @@ void StopMmap (int, struct buffer_t *, uint32_t);
block_t* GrabVideo (vlc_object_t *, int, const struct buffer_t *);
+#ifdef ZVBI_COMPILED
+/* vbi.c */
+block_t* GrabVBI (vlc_object_t *p_demux, vbi_capture *vbi_cap,
+ vbi_sliced *sliced, unsigned int vbi_sliced_size,
+ uint8_t *vbi_raw);
+#endif
+
/* demux.c */
int DemuxOpen(vlc_object_t *);
void DemuxClose(vlc_object_t *);
diff --git a/modules/access/v4l2/vbi.c b/modules/access/v4l2/vbi.c
new file mode 100644
index 0000000..18c1482
--- /dev/null
+++ b/modules/access/v4l2/vbi.c
@@ -0,0 +1,86 @@
+/*****************************************************************************
+ * vbi.c : Video4Linux2 VBI input module for vlc
+ *****************************************************************************
+ * Copyright (C) 2002-2009 the VideoLAN team
+ *
+ * Author: Devin Heitmueller <dheitmueller at kernellabs dot com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <errno.h>
+#include <vlc_common.h>
+#include <vlc_block.h>
+
+#include "v4l2.h"
+
+#ifdef ZVBI_COMPILED
+block_t *GrabVBI( vlc_object_t *p_demux, vbi_capture *vbi_cap,
+ vbi_sliced *sliced, unsigned int vbi_sliced_size,
+ uint8_t *vbi_raw)
+{
+ block_t *p_block=NULL;
+
+ struct timeval timeout={0,0}; /* poll */
+ double timestamp;
+ int n_lines;
+
+ memset(sliced, 0, vbi_sliced_size);
+
+ int r = vbi_capture_read (vbi_cap,
+ vbi_raw,
+ sliced,
+ &n_lines,
+ ×tamp,
+ &timeout);
+ switch (r) {
+ case -1:
+ msg_Err( p_demux, "Error reading VBI (%s)", strerror(errno) );
+ break;
+ case 0: /* nothing avail */
+ break;
+ case 1: /* got data */
+ if (n_lines)
+ {
+ int sliced_size = 2; /* Number of sliced lines decoded from raw */
+ int size = (sliced_size + 1) * n_lines;
+ if( !( p_block = block_New( p_demux, size ) ) )
+ {
+ msg_Err( p_demux, "cannot get block" );
+ }
+ else
+ {
+ int field;
+ uint8_t* data = p_block->p_buffer;
+ for (field=0; field<n_lines; field++)
+ {
+ *data = field;
+ data++;
+ memcpy(data, (sliced+field)->data, sliced_size);
+ data += sliced_size;
+ }
+ data = p_block->p_buffer;
+ p_block->i_buffer = size;
+ p_block->i_pts = p_block->i_pts = mdate();
+ }
+ }
+ }
+ return p_block;
+}
+#endif
--
1.7.9.5
More information about the vlc-devel
mailing list