[FFmpeg-devel] x11 output device for libavdevice

Paul B Mahol onemda at gmail.com
Sat Apr 13 12:41:50 CEST 2013


On 4/12/13, Moguillansky, Jeff <Jeff.Moguillansky at am.sony.com> wrote:
> Thanks,
> how about this one?
> Jeff


> diff --git a/libavdevice/Makefile b/libavdevice/Makefile
> index efffa8b..83728cc 100644
> --- a/libavdevice/Makefile
> +++ b/libavdevice/Makefile
> @@ -36,6 +36,7 @@ OBJS-$(CONFIG_V4L2_INDEV)                += v4l2.o timefilter.o
>  OBJS-$(CONFIG_V4L_INDEV)                 += v4l.o
>  OBJS-$(CONFIG_VFWCAP_INDEV)              += vfwcap.o
>  OBJS-$(CONFIG_X11GRAB_INDEV)             += x11grab.o
> +OBJS-$(CONFIG_XVIDEO_OUTDEV)             += xvideo.o
>
>  # external libraries
>  OBJS-$(CONFIG_LIBCDIO_INDEV)             += libcdio.o
> diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c
> index daa6638..5680796 100644
> --- a/libavdevice/alldevices.c
> +++ b/libavdevice/alldevices.c
> @@ -64,6 +64,7 @@ void avdevice_register_all(void)
>  //    REGISTER_INDEV   (V4L,              v4l
>      REGISTER_INDEV   (VFWCAP,           vfwcap);
>      REGISTER_INDEV   (X11GRAB,          x11grab);
> +    REGISTER_OUTDEV  (XVIDEO,           xvideo);
>
>      /* external libraries */
>      REGISTER_INDEV   (LIBCDIO,          libcdio);
> diff --git a/libavdevice/xvideo.c b/libavdevice/xvideo.c
> new file mode 100644
> index 0000000..d7a1131
> --- /dev/null
> +++ b/libavdevice/xvideo.c
> @@ -0,0 +1,155 @@
> +/*
> + * Copyright (c) 2013 Jeff Moguillansky
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg 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.
> + *
> + * FFmpeg 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 FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include <X11/Xlib.h>
> +#include <X11/extensions/Xv.h>
> +#include <X11/extensions/Xvlib.h>
> +#include <sys/shm.h>
> +#include "libavutil/avstring.h"
> +#include "libavutil/mem.h"
> +#include "libavutil/opt.h"
> +#include "libavutil/parseutils.h"
> +#include "libavutil/pixdesc.h"
> +#include "avdevice.h"
> +#include "libavfilter/libmpcodecs/img_format.h"

remove that^

> +
> +typedef struct {
> +    GC gc;
> +    Window window;
> +    Display* dpy;
> +    XvImage* yuv_image;
> +    XShmSegmentInfo yuv_shminfo;
> +    int xv_port, width, height;
> +} XVideoData;

Another struct is not really needed.

> +
> +typedef struct {
> +    AVClass *class;
> +    char *window_title;
> +    XVideoData *data;
> +} XVideoContext;
> +
> +#define OFFSET(x) offsetof(XVideoContext,x)
> +
> +#define VERIFY(s) if (!(s)) { av_log(NULL, AV_LOG_ERROR, "[%s][%d]: ERROR: %s failed\n", __FUNCTION__,__LINE__,#s); return AVERROR_UNKNOWN; }
> +
> +static int xvideo_write_header(AVFormatContext *s) {
> +    XVideoContext *xvideo = s->priv_data;
> +    XVideoData *data = av_mallocz(sizeof(XVideoData));

Again this is pointless, use same struct. and then this call is not needed.

If size of X11/Xv*/GC/Window struct can change (we should safely
assume they do not), malloc them instead.

> +    unsigned int p_num_adaptors;
> +    XvAdaptorInfo *ai = NULL;
> +    AVCodecContext *pCodecCtx = s->streams[0]->codec;

Replace pCodecCtx with cctx, or codec_ctx, if you insist on long names.

> +    VERIFY(pCodecCtx->codec_tag == IMGFMT_I420);

Replace IMGFMT_I420 with MKTAG('I','4','2','0');

Also it is not goal to write code with smallest possible number of
lines, so just remove this VERIFY macro.

> +    VERIFY(s->nb_streams == 1);
> +    xvideo->data = data;
> +    if (!xvideo->window_title) xvideo->window_title = av_strdup("");
> +    data->width = pCodecCtx->width;
> +    data->height = pCodecCtx->height;
> +    data->dpy = XOpenDisplay(NULL);
> +    VERIFY(data->dpy != NULL);
> +    data->window = XCreateSimpleWindow(data->dpy, DefaultRootWindow(data->dpy),0, 0,data->width,data->height,0,0,0);
> +    XStoreName(data->dpy, data->window, xvideo->window_title);
> +    XMapWindow(data->dpy, data->window);
> +    VERIFY(XvQueryAdaptors(data->dpy, DefaultRootWindow(data->dpy),&p_num_adaptors, &ai) == Success);
> +    data->xv_port = ai[0].base_id;
> +    data->gc = XCreateGC(data->dpy, data->window, 0, 0);
> +    VERIFY(data->gc != NULL);
> +    data->yuv_image = XvShmCreateImage(data->dpy, data->xv_port, IMGFMT_I420 , 0, data->width, data->height, &data->yuv_shminfo);

Replace IMGFMT_I420 with MKTAG('I','4','2','0');

What about quering what formats driver actually supports instead of
this hardcoded case.

See sdl.c for some hints how to do it.

> +    VERIFY(data->yuv_image != NULL);
> +    data->yuv_shminfo.shmid = shmget(IPC_PRIVATE, data->yuv_image->data_size, IPC_CREAT | 0777);
> +    VERIFY(data->yuv_shminfo.shmid != -1);
> +    data->yuv_image->data = (char*)shmat(data->yuv_shminfo.shmid, 0, 0);
> +    VERIFY(data->yuv_image->data != NULL);
> +    data->yuv_shminfo.shmaddr = data->yuv_image->data;
> +    data->yuv_shminfo.readOnly = False;
> +    VERIFY(XShmAttach(data->dpy, &data->yuv_shminfo) != 0);
> +    return 0;
> +}
> +
> +static int xvideo_write_packet(AVFormatContext *s, AVPacket *pkt) {
> +    XVideoContext *xvideo = NULL;
> +    XVideoData *data = NULL;
> +    XvImage *img = NULL;
> +    int y, h;
> +    XWindowAttributes attr;
> +    AVPicture pict;
> +    AVCodecContext *pCodecCtx = s->streams[0]->codec;
> +    xvideo = s->priv_data;
> +    VERIFY(xvideo != NULL);
> +    data = xvideo->data;
> +    VERIFY(data != NULL);
> +    img = data->yuv_image;
> +    VERIFY(img != NULL);
> +    h = img->height / 2;
> +    avpicture_fill(&pict, pkt->data, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
> +    VERIFY(pict.linesize[0] == img->pitches[0] && pict.linesize[1] == img->pitches[1] && pict.linesize[2] == img->pitches[2]);
> +    for(y = 0; y < img->height; y++) {
> +        memcpy(&img->data[img->offsets[0] + (y * img->pitches[0])], &pict.data[0][y * pict.linesize[0]], img->pitches[0]);
> +    }
> +    for(y = 0; y < h; ++y) {
> +        memcpy(&img->data[img->offsets[1] + (y * img->pitches[1])], &pict.data[1][y * pict.linesize[1]], img->pitches[1]);
> +        memcpy(&img->data[img->offsets[2] + (y * img->pitches[2])],&pict.data[2][y * pict.linesize[2]], img->pitches[2]);
> +    }
> +    XGetWindowAttributes(data->dpy, data->window,&attr);
> +    VERIFY(XvShmPutImage(data->dpy, data->xv_port, data->window, data->gc, data->yuv_image,0, 0, data->width, data->height,0, 0, attr.width,attr.height, True) == Success);
> +    XSync(data->dpy,True);
> +    return 0;
> +}
> +
> +static int xvideo_write_trailer(AVFormatContext *s) {
> +    XVideoContext *xvideo = NULL;
> +    XVideoData *data = NULL;
> +    xvideo = s->priv_data;
> +    VERIFY(xvideo != NULL);
> +    data = xvideo->data;
> +    VERIFY(data != NULL);
> +    av_freep(&xvideo->window_title);
> +    XShmDetach(data->dpy,&data->yuv_shminfo);
> +    shmdt(data->yuv_image->data);
> +    XFree(data->yuv_image);
> +    XCloseDisplay(data->dpy);
> +    if (xvideo->data) { free(xvideo->data); xvideo->data = NULL; }
> +
> +    return 0;
> +}
> +
> +static const AVOption options[] = {
> +    { "window_title", "set window title", OFFSET(window_title), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
> +    { NULL },
> +};
> +
> +static const AVClass xvideo_class = {
> +    .class_name = "xvideo outdev",
> +    .item_name  = av_default_item_name,
> +    .option     = options,
> +    .version    = LIBAVUTIL_VERSION_INT,
> +};
> +
> +AVOutputFormat ff_xvideo_muxer = {
> +    .name           = "xv",
> +    .long_name      = NULL_IF_CONFIG_SMALL("XVideo output device"),
> +    .priv_data_size = sizeof(XVideoContext),
> +    .audio_codec    = AV_CODEC_ID_NONE,
> +    .video_codec    = AV_CODEC_ID_RAWVIDEO,
> +    .write_header   = xvideo_write_header,
> +    .write_packet   = xvideo_write_packet,
> +    .write_trailer  = xvideo_write_trailer,
> +    .flags          = AVFMT_NOFILE,
> +    .priv_class     = &xvideo_class,
> +};

Here is WIP what I did using your patch:

---
 configure                |   4 ++
 libavdevice/Makefile     |   1 +
 libavdevice/alldevices.c |   1 +
 libavdevice/xvideo.c     | 165 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 171 insertions(+)
 create mode 100644 libavdevice/xvideo.c

diff --git a/configure b/configure
index d6b8dd3..caf1f82 100755
--- a/configure
+++ b/configure
@@ -239,6 +239,7 @@ External library support:
   --enable-opencl          enable OpenCL code
   --enable-openssl         enable openssl [no]
   --enable-x11grab         enable X11 grabbing [no]
+  --enable-xv              enable Xv video display
   --enable-zlib            enable zlib [autodetect]

 Advanced options (experts only):
@@ -1192,6 +1193,7 @@ EXTERNAL_LIBRARY_LIST="
     opencl
     openssl
     x11grab
+    xv
     zlib
 "

@@ -2063,6 +2065,7 @@ openal_indev_deps="openal"
 oss_indev_deps_any="soundcard_h sys_soundcard_h"
 oss_outdev_deps_any="soundcard_h sys_soundcard_h"
 pulse_indev_deps="libpulse"
+xvideo_outdev_deps="xv"
 sdl_outdev_deps="sdl"
 sndio_indev_deps="sndio_h"
 sndio_outdev_deps="sndio_h"
@@ -4004,6 +4007,7 @@ enabled libquvi    && require_pkg_config libquvi
quvi/quvi.h quvi_init
 enabled librtmp    && require_pkg_config librtmp librtmp/rtmp.h RTMP_Socket
 enabled libschroedinger && require_pkg_config schroedinger-1.0
schroedinger/schro.h schro_init
 enabled libshine   && require_pkg_config shine shine/layer3.h
shine_encode_frame
+enabled xv         && require2  xv "X11/Xlib.h
X11/extensions/Xvlib.h" XvGetPortAttribute -lXv
 enabled libsoxr    && require  libsoxr soxr.h soxr_create -lsoxr
 enabled libspeex   && require  libspeex speex/speex.h
speex_decoder_init -lspeex
 enabled libstagefright_h264  && require_cpp libstagefright_h264
"binder/ProcessState.h media/stagefright/MetaData.h
diff --git a/libavdevice/Makefile b/libavdevice/Makefile
index efffa8b..83728cc 100644
--- a/libavdevice/Makefile
+++ b/libavdevice/Makefile
@@ -36,6 +36,7 @@ OBJS-$(CONFIG_V4L2_INDEV)                += v4l2.o
timefilter.o
 OBJS-$(CONFIG_V4L_INDEV)                 += v4l.o
 OBJS-$(CONFIG_VFWCAP_INDEV)              += vfwcap.o
 OBJS-$(CONFIG_X11GRAB_INDEV)             += x11grab.o
+OBJS-$(CONFIG_XVIDEO_OUTDEV)             += xvideo.o

 # external libraries
 OBJS-$(CONFIG_LIBCDIO_INDEV)             += libcdio.o
diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c
index daa6638..bd4e341 100644
--- a/libavdevice/alldevices.c
+++ b/libavdevice/alldevices.c
@@ -64,6 +64,7 @@ void avdevice_register_all(void)
 //    REGISTER_INDEV   (V4L,              v4l
     REGISTER_INDEV   (VFWCAP,           vfwcap);
     REGISTER_INDEV   (X11GRAB,          x11grab);
+    REGISTER_OUTDEV  (XVIDEO,           xvideo);

     /* external libraries */
     REGISTER_INDEV   (LIBCDIO,          libcdio);
diff --git a/libavdevice/xvideo.c b/libavdevice/xvideo.c
new file mode 100644
index 0000000..626d9ec
--- /dev/null
+++ b/libavdevice/xvideo.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2013 Jeff Moguillansky
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xv.h>
+#include <X11/extensions/Xvlib.h>
+#include <X11/extensions/XShm.h>
+#include <sys/shm.h>
+
+#include "libavutil/avstring.h"
+#include "libavutil/common.h"
+#include "libavutil/opt.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/pixdesc.h"
+#include "avdevice.h"
+
+typedef struct {
+    GC gc;
+    Window window;
+    Display* dpy;
+    XvImage* yuv_image;
+    XShmSegmentInfo yuv_shminfo;
+    int xv_port, width, height;
+} XVideoData;
+
+typedef struct {
+    AVClass *class;
+    char *window_title;
+    XVideoData *data;
+} XVideoContext;
+
+#define OFFSET(x) offsetof(XVideoContext,x)
+
+static int xvideo_write_header(AVFormatContext *s)
+{
+    XVideoContext *xvideo = s->priv_data;
+    XVideoData *data = (XVideoData*)calloc(1,sizeof(XVideoData));
+    unsigned int p_num_adaptors;
+    XvAdaptorInfo *ai = NULL;
+    AVCodecContext *ctx = s->streams[0]->codec;
+    xvideo->data = data;
+
+    data->width = ctx->width;
+    data->height = ctx->height;
+    data->dpy = XOpenDisplay(NULL);
+
+    data->window = XCreateSimpleWindow(data->dpy,
DefaultRootWindow(data->dpy), 0, 0, data->width, data->height, 0, 0,
0);
+
+    if (!xvideo->window_title)
+        xvideo->window_title = av_strdup(s->filename);
+
+    XStoreName(data->dpy, data->window, xvideo->window_title);
+    XMapWindow(data->dpy, data->window);
+
+    if (XvQueryAdaptors(data->dpy, DefaultRootWindow(data->dpy),
&p_num_adaptors, &ai) != Success)
+        return AVERROR_UNKNOWN;
+
+    data->xv_port = ai[0].base_id;
+    data->gc = XCreateGC(data->dpy, data->window, 0, 0);
+    data->yuv_image = XvShmCreateImage(data->dpy, data->xv_port,
MKTAG('I','4','2','0'), 0, data->width, data->height,
&data->yuv_shminfo);
+    data->yuv_shminfo.shmid = shmget(IPC_PRIVATE,
data->yuv_image->data_size, IPC_CREAT | 0777);
+    data->yuv_shminfo.shmaddr = (char *) shmat(data->yuv_shminfo.shmid, 0,0);
+    data->yuv_image->data = data->yuv_shminfo.shmaddr;
+    data->yuv_shminfo.readOnly = False;
+
+    XShmAttach(data->dpy, &data->yuv_shminfo);
+    XSync(data->dpy, False);
+    shmctl(data->yuv_shminfo.shmid, IPC_RMID, 0);
+
+    return 0;
+}
+
+static int xvideo_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    XVideoContext *xvideo = s->priv_data;
+    XVideoData *data = xvideo->data;
+    XvImage *img = NULL;
+    XWindowAttributes attr;
+    AVPicture pict;
+    AVCodecContext *ctx = s->streams[0]->codec;
+    int y, h;
+
+    img = data->yuv_image;
+    h = img->height / 2;
+    avpicture_fill(&pict, pkt->data, ctx->pix_fmt, ctx->width, ctx->height);
+
+    for (y = 0; y < img->height; y++) {
+        memcpy(&img->data[img->offsets[0] + (y * img->pitches[0])],
&pict.data[0][y * pict.linesize[0]], img->pitches[0]);
+    }
+
+    for (y = 0; y < h; ++y) {
+        memcpy(&img->data[img->offsets[1] + (y * img->pitches[1])],
&pict.data[1][y * pict.linesize[1]], img->pitches[1]);
+        memcpy(&img->data[img->offsets[2] + (y * img->pitches[2])],
&pict.data[2][y * pict.linesize[2]], img->pitches[2]);
+    }
+
+    XGetWindowAttributes(data->dpy, data->window, &attr);
+
+    if (XvShmPutImage(data->dpy, data->xv_port, data->window,
data->gc, data->yuv_image, 0, 0, data->width, data->height, 0, 0,
attr.width,attr.height, True) != Success)
+        return AVERROR_UNKNOWN;
+
+    XSync(data->dpy, True);
+
+    return 0;
+}
+
+static int xvideo_write_trailer(AVFormatContext *s)
+{
+    XVideoContext *xvideo = s->priv_data;
+    XVideoData *data = xvideo->data;
+
+    av_freep(&xvideo->window_title);
+    XShmDetach(data->dpy, &data->yuv_shminfo);
+    shmdt(data->yuv_image->data);
+    XFree(data->yuv_image);
+    XCloseDisplay(data->dpy);
+
+    if (xvideo->data) {
+        free(xvideo->data);
+        xvideo->data = NULL;
+    }
+
+    return 0;
+}
+
+static const AVOption options[] = {
+    { "window_title", "set window title", OFFSET(window_title),
AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM
},
+    { NULL },
+};
+
+static const AVClass xvideo_class = {
+    .class_name = "xvideo outdev",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+AVOutputFormat ff_xvideo_muxer = {
+    .name           = "xv",
+    .long_name      = NULL_IF_CONFIG_SMALL("XVideo output device"),
+    .priv_data_size = sizeof(XVideoContext),
+    .audio_codec    = AV_CODEC_ID_NONE,
+    .video_codec    = AV_CODEC_ID_RAWVIDEO,
+    .write_header   = xvideo_write_header,
+    .write_packet   = xvideo_write_packet,
+    .write_trailer  = xvideo_write_trailer,
+    .flags          = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS,
+    .priv_class     = &xvideo_class,
+};
-- 
1.7.11.2


More information about the ffmpeg-devel mailing list