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