[FFmpeg-devel] x11 output device for libavdevice

Moguillansky, Jeff Jeff.Moguillansky at am.sony.com
Thu Apr 11 01:10:47 CEST 2013


Hello,
I really like the libavdevice code.  I implemented an X11 output device for libavdevice.  I would like to request if it's possible to add this to the main branch?  It uses XVideo extension for hardware acceleration and it supports window resizing.  

To use (with video and audio output):
ffmpeg -i <input file> -f x11 "Output" -f alsa "default"

Here's my code:

libavdevice/x11.c:

/*
 * Copyright (c) 2011 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 "libavutil/avstring.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
#include "libavutil/pixdesc.h"
#include "avdevice.h"
#include <X11/Xlib.h>
#include <X11/extensions/Xv.h>
#include <X11/extensions/Xvlib.h>
#include <sys/shm.h>

typedef struct {
	GC gc; Window window; Display* dpy; XvImage* yuv_image[1]; XShmSegmentInfo yuv_shminfo[1]; 
	int xv_port, width, height;
} X11Data;

typedef struct {
    AVClass *class;
    char *window_title;
    X11Data *data;
} X11Context;

#define OFFSET(x) offsetof(X11Context,x)

static int x11_write_header(AVFormatContext *s) {
	X11Context *x11 = s->priv_data;
	X11Data *data = (X11Data*)calloc(1,sizeof(X11Data));
	unsigned int p_num_adaptors; 
	int j;
	XvAdaptorInfo *ai;
	AVCodecContext *pCodecCtx = s->streams[0]->codec;
	x11->data = data;
	if (!x11->window_title) x11->window_title = av_strdup("");
	data->width = pCodecCtx->width; data->height = pCodecCtx->height;
	data->dpy = XOpenDisplay(NULL);
	data->window = XCreateSimpleWindow(data->dpy, DefaultRootWindow(data->dpy),0, 0,data->width,data->height,0,0,0);
	XStoreName(data->dpy, data->window, x11->window_title);
	XMapWindow(data->dpy, data->window);
	XvQueryAdaptors(data->dpy, DefaultRootWindow(data->dpy),&p_num_adaptors, &ai);
	data->xv_port = ai[0].base_id;
	data->gc = XCreateGC(data->dpy, data->window, 0, 0);
	for (j = 0; j<1; j++) {
		data->yuv_image[j] = XvShmCreateImage(data->dpy, data->xv_port+j, ('I'|('4'<<8)|('2'<<16)|'0'<<24) , 0, data->width, data->height, &data->yuv_shminfo[j]);
		data->yuv_shminfo[j].shmid = shmget(IPC_PRIVATE, data->yuv_image[j]->data_size, IPC_CREAT | 0777); 
		data->yuv_image[j]->data = (char*)shmat(data->yuv_shminfo[j].shmid, 0, 0); data->yuv_shminfo[j].shmaddr = data->yuv_image[j]->data; 
		data->yuv_shminfo[j].readOnly = False; 
	}
	XShmAttach(data->dpy, &data->yuv_shminfo[0]);
	return 0;
}

static int x11_write_packet(AVFormatContext *s, AVPacket *pkt) {
	X11Context *x11 = s->priv_data;
	X11Data *data = x11->data;
	XvImage	*img = data->yuv_image[0];
	int	y, h = img->height / 2;
	XWindowAttributes attr;
	AVPicture pict;
	AVCodecContext *pCodecCtx = s->streams[0]->codec;
	avpicture_fill(&pict, pkt->data, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->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);
	XvShmPutImage(data->dpy, data->xv_port, data->window, data->gc, data->yuv_image[0],0, 0, data->width, data->height,0, 0, attr.width,attr.height, True);
	XSync(data->dpy,True);
	return 0;
}

static int x11_write_trailer(AVFormatContext *s) {
	X11Context *x11 = s->priv_data;
	X11Data *data = x11->data;
	int j;
	av_freep(&x11->window_title);
	XShmDetach(data->dpy,&data->yuv_shminfo[0]);
	for (j = 0; j<1; j++) {
		shmdt(data->yuv_image[j]->data);
		XFree(data->yuv_image[j]);
	}
	XCloseDisplay(data->dpy);
	
	return 0;
}

static const AVOption options[] = {
    { "window_title", "set X11 window title", OFFSET(window_title), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
	{ NULL },
};

static const AVClass x11_class = {
    .class_name = "x11 outdev",
    .item_name  = av_default_item_name,
    .option     = options,
    .version    = LIBAVUTIL_VERSION_INT,
};

AVOutputFormat ff_x11_muxer = {
    .name           = "x11",
    .long_name      = NULL_IF_CONFIG_SMALL("x11 output device"),
    .priv_data_size = sizeof(X11Context),
    .audio_codec    = AV_CODEC_ID_NONE,
    .video_codec    = AV_CODEC_ID_RAWVIDEO,
    .write_header   = x11_write_header,
    .write_packet   = x11_write_packet,
    .write_trailer  = x11_write_trailer,
    .flags          = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS,
    .priv_class     = &x11_class,
};




....
here's the other modifications:
libavdevice/alldevices.c:
void avdevice_register_all(void) {
  ...
REGISTER_OUTDEV (X11, x11);
  ...
}

libavdevice/Makefile:
...
OBJS-$(CONFIG_X11_OUTDEV)                += x11.o


ffmpeg/config.mak:
EXTRALIBS=... -lX11 -lXv ...

ffmpeg/config.h:
...
#define CONFIG_X11_OUTDEV 1




Thanks,
Jeff



More information about the ffmpeg-devel mailing list