avdevice/qtkit: fix include
[ffmpeg.git] / libavdevice / xv.c
1 /*
2  * Copyright (c) 2013 Jeff Moguillansky
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20
21 /**
22  * @file
23  * XVideo output device
24  *
25  * TODO:
26  * - add support to more formats
27  */
28
29 #include <X11/Xlib.h>
30 #include <X11/extensions/Xv.h>
31 #include <X11/extensions/XShm.h>
32 #include <X11/extensions/Xvlib.h>
33 #include <sys/shm.h>
34
35 #include "libavutil/opt.h"
36 #include "libavutil/pixdesc.h"
37 #include "libavutil/imgutils.h"
38 #include "libavformat/internal.h"
39 #include "avdevice.h"
40
41 typedef struct {
42     AVClass *class;
43     GC gc;
44
45     Window window;
46     int64_t window_id;
47     char *window_title;
48     int window_width, window_height;
49     int window_x, window_y;
50     int dest_x, dest_y;          /**< display area position */
51     unsigned int dest_w, dest_h; /**< display area dimensions */
52
53     Display* display;
54     char *display_name;
55
56     XvImage* yuv_image;
57     enum AVPixelFormat image_format;
58     int image_width, image_height;
59     XShmSegmentInfo yuv_shminfo;
60     int xv_port;
61 } XVContext;
62
63 typedef struct XVTagFormatMap
64 {
65     int tag;
66     enum AVPixelFormat format;
67 } XVTagFormatMap;
68
69 static XVTagFormatMap tag_codec_map[] = {
70     { MKTAG('I','4','2','0'), AV_PIX_FMT_YUV420P },
71     { MKTAG('U','Y','V','Y'), AV_PIX_FMT_UYVY422 },
72     { MKTAG('Y','U','Y','2'), AV_PIX_FMT_YUYV422 },
73     { 0,                      AV_PIX_FMT_NONE }
74 };
75
76 static int xv_get_tag_from_format(enum AVPixelFormat format)
77 {
78     XVTagFormatMap *m = tag_codec_map;
79     int i;
80     for (i = 0; m->tag; m = &tag_codec_map[++i]) {
81         if (m->format == format)
82             return m->tag;
83     }
84     return 0;
85 }
86
87 static int xv_write_trailer(AVFormatContext *s)
88 {
89     XVContext *xv = s->priv_data;
90     if (xv->display) {
91         XShmDetach(xv->display, &xv->yuv_shminfo);
92         if (xv->yuv_image)
93             shmdt(xv->yuv_image->data);
94         XFree(xv->yuv_image);
95         if (xv->gc)
96             XFreeGC(xv->display, xv->gc);
97         XCloseDisplay(xv->display);
98     }
99     return 0;
100 }
101
102 static int xv_write_header(AVFormatContext *s)
103 {
104     XVContext *xv = s->priv_data;
105     unsigned int num_adaptors;
106     XvAdaptorInfo *ai;
107     XvImageFormatValues *fv;
108     XColor fgcolor;
109     XWindowAttributes window_attrs;
110     int num_formats = 0, j, tag, ret;
111     AVCodecContext *encctx = s->streams[0]->codec;
112
113     if (   s->nb_streams > 1
114         || encctx->codec_type != AVMEDIA_TYPE_VIDEO
115         || encctx->codec_id   != AV_CODEC_ID_RAWVIDEO) {
116         av_log(s, AV_LOG_ERROR, "Only supports one rawvideo stream\n");
117         return AVERROR(EINVAL);
118     }
119
120     if (!(tag = xv_get_tag_from_format(encctx->pix_fmt))) {
121         av_log(s, AV_LOG_ERROR,
122                "Unsupported pixel format '%s', only yuv420p, uyvy422, yuyv422 are currently supported\n",
123                av_get_pix_fmt_name(encctx->pix_fmt));
124         return AVERROR_PATCHWELCOME;
125     }
126     xv->image_format = encctx->pix_fmt;
127
128     xv->display = XOpenDisplay(xv->display_name);
129     if (!xv->display) {
130         av_log(s, AV_LOG_ERROR, "Could not open the X11 display '%s'\n", xv->display_name);
131         return AVERROR(EINVAL);
132     }
133
134     xv->image_width  = encctx->width;
135     xv->image_height = encctx->height;
136     if (!xv->window_width && !xv->window_height) {
137         AVRational sar = encctx->sample_aspect_ratio;
138         xv->window_width  = encctx->width;
139         xv->window_height = encctx->height;
140         if (sar.num) {
141             if (sar.num > sar.den)
142                 xv->window_width = av_rescale(xv->window_width, sar.num, sar.den);
143             if (sar.num < sar.den)
144                 xv->window_height = av_rescale(xv->window_height, sar.den, sar.num);
145         }
146     }
147     if (!xv->window_id) {
148     //TODO: reident
149     xv->window = XCreateSimpleWindow(xv->display, DefaultRootWindow(xv->display),
150                                      xv->window_x, xv->window_y,
151                                      xv->window_width, xv->window_height,
152                                      0, 0, 0);
153     if (!xv->window_title) {
154         if (!(xv->window_title = av_strdup(s->filename))) {
155             ret = AVERROR(ENOMEM);
156             goto fail;
157         }
158     }
159     XStoreName(xv->display, xv->window, xv->window_title);
160     XMapWindow(xv->display, xv->window);
161     } else
162         xv->window = xv->window_id;
163
164     if (XvQueryAdaptors(xv->display, DefaultRootWindow(xv->display), &num_adaptors, &ai) != Success) {
165         ret = AVERROR_EXTERNAL;
166         goto fail;
167     }
168     if (!num_adaptors) {
169         av_log(s, AV_LOG_ERROR, "No X-Video adaptors present\n");
170         return AVERROR(ENODEV);
171     }
172     xv->xv_port = ai[0].base_id;
173     XvFreeAdaptorInfo(ai);
174
175     fv = XvListImageFormats(xv->display, xv->xv_port, &num_formats);
176     if (!fv) {
177         ret = AVERROR_EXTERNAL;
178         goto fail;
179     }
180     for (j = 0; j < num_formats; j++) {
181         if (fv[j].id == tag) {
182             break;
183         }
184     }
185     XFree(fv);
186
187     if (j >= num_formats) {
188         av_log(s, AV_LOG_ERROR,
189                "Device does not support pixel format %s, aborting\n",
190                av_get_pix_fmt_name(encctx->pix_fmt));
191         ret = AVERROR(EINVAL);
192         goto fail;
193     }
194
195     xv->gc = XCreateGC(xv->display, xv->window, 0, 0);
196     xv->image_width  = encctx->width;
197     xv->image_height = encctx->height;
198     xv->yuv_image = XvShmCreateImage(xv->display, xv->xv_port, tag, 0,
199                                      xv->image_width, xv->image_height, &xv->yuv_shminfo);
200     xv->yuv_shminfo.shmid = shmget(IPC_PRIVATE, xv->yuv_image->data_size,
201                                    IPC_CREAT | 0777);
202     xv->yuv_shminfo.shmaddr = (char *)shmat(xv->yuv_shminfo.shmid, 0, 0);
203     xv->yuv_image->data = xv->yuv_shminfo.shmaddr;
204     xv->yuv_shminfo.readOnly = False;
205
206     XShmAttach(xv->display, &xv->yuv_shminfo);
207     XSync(xv->display, False);
208     shmctl(xv->yuv_shminfo.shmid, IPC_RMID, 0);
209
210     XGetWindowAttributes(xv->display, xv->window, &window_attrs);
211     fgcolor.red = fgcolor.green = fgcolor.blue = 0;
212     fgcolor.flags = DoRed | DoGreen | DoBlue;
213     XAllocColor(xv->display, window_attrs.colormap, &fgcolor);
214     XSetForeground(xv->display, xv->gc, fgcolor.pixel);
215     //force display area recalculation at first frame
216     xv->window_width = xv->window_height = 0;
217
218     return 0;
219   fail:
220     xv_write_trailer(s);
221     return ret;
222 }
223
224 static void compute_display_area(AVFormatContext *s)
225 {
226     XVContext *xv = s->priv_data;
227     AVRational sar, dar; /* sample and display aspect ratios */
228     AVStream *st = s->streams[0];
229     AVCodecContext *encctx = st->codec;
230
231     /* compute overlay width and height from the codec context information */
232     sar = st->sample_aspect_ratio.num ? st->sample_aspect_ratio : (AVRational){ 1, 1 };
233     dar = av_mul_q(sar, (AVRational){ encctx->width, encctx->height });
234
235     /* we suppose the screen has a 1/1 sample aspect ratio */
236     /* fit in the window */
237     if (av_cmp_q(dar, (AVRational){ xv->dest_w, xv->dest_h }) > 0) {
238         /* fit in width */
239         xv->dest_y = xv->dest_h;
240         xv->dest_x = 0;
241         xv->dest_h = av_rescale(xv->dest_w, dar.den, dar.num);
242         xv->dest_y -= xv->dest_h;
243         xv->dest_y /= 2;
244     } else {
245         /* fit in height */
246         xv->dest_x = xv->dest_w;
247         xv->dest_y = 0;
248         xv->dest_w = av_rescale(xv->dest_h, dar.num, dar.den);
249         xv->dest_x -= xv->dest_w;
250         xv->dest_x /= 2;
251     }
252 }
253
254 static int xv_repaint(AVFormatContext *s)
255 {
256     XVContext *xv = s->priv_data;
257     XWindowAttributes window_attrs;
258
259     XGetWindowAttributes(xv->display, xv->window, &window_attrs);
260     if (window_attrs.width != xv->window_width || window_attrs.height != xv->window_height) {
261         XRectangle rect[2];
262         xv->dest_w = window_attrs.width;
263         xv->dest_h = window_attrs.height;
264         compute_display_area(s);
265         if (xv->dest_x) {
266             rect[0].width  = rect[1].width  = xv->dest_x;
267             rect[0].height = rect[1].height = window_attrs.height;
268             rect[0].y      = rect[1].y      = 0;
269             rect[0].x = 0;
270             rect[1].x = xv->dest_w + xv->dest_x;
271             XFillRectangles(xv->display, xv->window, xv->gc, rect, 2);
272         }
273         if (xv->dest_y) {
274             rect[0].width  = rect[1].width  = window_attrs.width;
275             rect[0].height = rect[1].height = xv->dest_y;
276             rect[0].x      = rect[1].x      = 0;
277             rect[0].y = 0;
278             rect[1].y = xv->dest_h + xv->dest_y;
279             XFillRectangles(xv->display, xv->window, xv->gc, rect, 2);
280         }
281     }
282
283     if (XvShmPutImage(xv->display, xv->xv_port, xv->window, xv->gc,
284                       xv->yuv_image, 0, 0, xv->image_width, xv->image_height,
285                       xv->dest_x, xv->dest_y, xv->dest_w, xv->dest_h, True) != Success) {
286         av_log(s, AV_LOG_ERROR, "Could not copy image to XV shared memory buffer\n");
287         return AVERROR_EXTERNAL;
288     }
289     return 0;
290 }
291
292 static int write_picture(AVFormatContext *s, AVPicture *pict)
293 {
294     XVContext *xv = s->priv_data;
295     XvImage *img = xv->yuv_image;
296     uint8_t *data[3] = {
297         img->data + img->offsets[0],
298         img->data + img->offsets[1],
299         img->data + img->offsets[2]
300     };
301     av_image_copy(data, img->pitches, (const uint8_t **)pict->data, pict->linesize,
302                   xv->image_format, img->width, img->height);
303     return xv_repaint(s);
304 }
305
306 static int xv_write_packet(AVFormatContext *s, AVPacket *pkt)
307 {
308     AVPicture pict;
309     AVCodecContext *ctx = s->streams[0]->codec;
310
311     avpicture_fill(&pict, pkt->data, ctx->pix_fmt, ctx->width, ctx->height);
312     return write_picture(s, &pict);
313 }
314
315 static int xv_write_frame(AVFormatContext *s, int stream_index, AVFrame **frame,
316                           unsigned flags)
317 {
318     /* xv_write_header() should have accepted only supported formats */
319     if ((flags & AV_WRITE_UNCODED_FRAME_QUERY))
320         return 0;
321     return write_picture(s, (AVPicture *)*frame);
322 }
323
324 static int xv_control_message(AVFormatContext *s, int type, void *data, size_t data_size)
325 {
326     switch(type) {
327     case AV_APP_TO_DEV_WINDOW_REPAINT:
328         return xv_repaint(s);
329     default:
330         break;
331     }
332     return AVERROR(ENOSYS);
333 }
334
335 #define OFFSET(x) offsetof(XVContext, x)
336 static const AVOption options[] = {
337     { "display_name", "set display name",       OFFSET(display_name), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
338     { "window_id",    "set existing window id", OFFSET(window_id),    AV_OPT_TYPE_INT64,  {.i64 = 0 }, 0, INT64_MAX, AV_OPT_FLAG_ENCODING_PARAM },
339     { "window_size",  "set window forced size", OFFSET(window_width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
340     { "window_title", "set window title",       OFFSET(window_title), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
341     { "window_x",     "set window x offset",    OFFSET(window_x),     AV_OPT_TYPE_INT,    {.i64 = 0 }, -INT_MAX, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
342     { "window_y",     "set window y offset",    OFFSET(window_y),     AV_OPT_TYPE_INT,    {.i64 = 0 }, -INT_MAX, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
343     { NULL }
344
345 };
346
347 static const AVClass xv_class = {
348     .class_name = "xvideo outdev",
349     .item_name  = av_default_item_name,
350     .option     = options,
351     .version    = LIBAVUTIL_VERSION_INT,
352     .category   = AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT,
353 };
354
355 AVOutputFormat ff_xv_muxer = {
356     .name           = "xv",
357     .long_name      = NULL_IF_CONFIG_SMALL("XV (XVideo) output device"),
358     .priv_data_size = sizeof(XVContext),
359     .audio_codec    = AV_CODEC_ID_NONE,
360     .video_codec    = AV_CODEC_ID_RAWVIDEO,
361     .write_header   = xv_write_header,
362     .write_packet   = xv_write_packet,
363     .write_uncoded_frame = xv_write_frame,
364     .write_trailer  = xv_write_trailer,
365     .control_message = xv_control_message,
366     .flags          = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS,
367     .priv_class     = &xv_class,
368 };