Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4349 Serge 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
 * - add support to window id specification
28
 */
29
 
30
#include 
31
#include 
32
#include 
33
#include 
34
#include 
35
 
36
#include "libavutil/opt.h"
37
#include "libavutil/pixdesc.h"
38
#include "avdevice.h"
39
 
40
typedef struct {
41
    AVClass *class;
42
    GC gc;
43
 
44
    Window window;
45
    char *window_title;
46
    int window_width, window_height;
47
    int window_x, window_y;
48
 
49
    Display* display;
50
    char *display_name;
51
 
52
    XvImage* yuv_image;
53
    int image_width, image_height;
54
    XShmSegmentInfo yuv_shminfo;
55
    int xv_port;
56
} XVContext;
57
 
58
static int xv_write_header(AVFormatContext *s)
59
{
60
    XVContext *xv = s->priv_data;
61
    unsigned int num_adaptors;
62
    XvAdaptorInfo *ai;
63
    XvImageFormatValues *fv;
64
    int num_formats = 0, j;
65
    AVCodecContext *encctx = s->streams[0]->codec;
66
 
67
    if (   s->nb_streams > 1
68
        || encctx->codec_type != AVMEDIA_TYPE_VIDEO
69
        || encctx->codec_id   != AV_CODEC_ID_RAWVIDEO) {
70
        av_log(s, AV_LOG_ERROR, "Only supports one rawvideo stream\n");
71
        return AVERROR(EINVAL);
72
    }
73
 
74
    xv->display = XOpenDisplay(xv->display_name);
75
    if (!xv->display) {
76
        av_log(s, AV_LOG_ERROR, "Could not open the X11 display '%s'\n", xv->display_name);
77
        return AVERROR(EINVAL);
78
    }
79
 
80
    xv->image_width  = encctx->width;
81
    xv->image_height = encctx->height;
82
    if (!xv->window_width && !xv->window_height) {
83
        xv->window_width  = encctx->width;
84
        xv->window_height = encctx->height;
85
    }
86
    xv->window = XCreateSimpleWindow(xv->display, DefaultRootWindow(xv->display),
87
                                     xv->window_x, xv->window_y,
88
                                     xv->window_width, xv->window_height,
89
                                     0, 0, 0);
90
    if (!xv->window_title) {
91
        if (!(xv->window_title = av_strdup(s->filename)))
92
            return AVERROR(ENOMEM);
93
    }
94
    XStoreName(xv->display, xv->window, xv->window_title);
95
    XMapWindow(xv->display, xv->window);
96
 
97
    if (XvQueryAdaptors(xv->display, DefaultRootWindow(xv->display), &num_adaptors, &ai) != Success)
98
        return AVERROR_EXTERNAL;
99
    xv->xv_port = ai[0].base_id;
100
 
101
    if (encctx->pix_fmt != AV_PIX_FMT_YUV420P) {
102
        av_log(s, AV_LOG_ERROR,
103
               "Unsupported pixel format '%s', only yuv420p is currently supported\n",
104
               av_get_pix_fmt_name(encctx->pix_fmt));
105
        return AVERROR_PATCHWELCOME;
106
    }
107
 
108
    fv = XvListImageFormats(xv->display, xv->xv_port, &num_formats);
109
    if (!fv)
110
        return AVERROR_EXTERNAL;
111
    for (j = 0; j < num_formats; j++) {
112
        if (fv[j].id == MKTAG('I','4','2','0')) {
113
            break;
114
        }
115
    }
116
    XFree(fv);
117
 
118
    if (j >= num_formats) {
119
        av_log(s, AV_LOG_ERROR,
120
               "Device does not support pixel format yuv420p, aborting\n");
121
        return AVERROR(EINVAL);
122
    }
123
 
124
    xv->gc = XCreateGC(xv->display, xv->window, 0, 0);
125
    xv->image_width  = encctx->width;
126
    xv->image_height = encctx->height;
127
    xv->yuv_image = XvShmCreateImage(xv->display, xv->xv_port,
128
                                     MKTAG('I','4','2','0'), 0,
129
                                     xv->image_width, xv->image_height, &xv->yuv_shminfo);
130
    xv->yuv_shminfo.shmid = shmget(IPC_PRIVATE, xv->yuv_image->data_size,
131
                                   IPC_CREAT | 0777);
132
    xv->yuv_shminfo.shmaddr = (char *)shmat(xv->yuv_shminfo.shmid, 0, 0);
133
    xv->yuv_image->data = xv->yuv_shminfo.shmaddr;
134
    xv->yuv_shminfo.readOnly = False;
135
 
136
    XShmAttach(xv->display, &xv->yuv_shminfo);
137
    XSync(xv->display, False);
138
    shmctl(xv->yuv_shminfo.shmid, IPC_RMID, 0);
139
 
140
    return 0;
141
}
142
 
143
static int xv_write_packet(AVFormatContext *s, AVPacket *pkt)
144
{
145
    XVContext *xv = s->priv_data;
146
    XvImage *img = xv->yuv_image;
147
    XWindowAttributes window_attrs;
148
    AVPicture pict;
149
    AVCodecContext *ctx = s->streams[0]->codec;
150
    int y, h;
151
 
152
    h = img->height / 2;
153
 
154
    avpicture_fill(&pict, pkt->data, ctx->pix_fmt, ctx->width, ctx->height);
155
    for (y = 0; y < img->height; y++) {
156
        memcpy(&img->data[img->offsets[0] + (y * img->pitches[0])],
157
               &pict.data[0][y * pict.linesize[0]], img->pitches[0]);
158
    }
159
 
160
    for (y = 0; y < h; ++y) {
161
        memcpy(&img->data[img->offsets[1] + (y * img->pitches[1])],
162
               &pict.data[1][y * pict.linesize[1]], img->pitches[1]);
163
        memcpy(&img->data[img->offsets[2] + (y * img->pitches[2])],
164
               &pict.data[2][y * pict.linesize[2]], img->pitches[2]);
165
    }
166
 
167
    XGetWindowAttributes(xv->display, xv->window, &window_attrs);
168
    if (XvShmPutImage(xv->display, xv->xv_port, xv->window, xv->gc,
169
                      xv->yuv_image, 0, 0, xv->image_width, xv->image_height, 0, 0,
170
                      window_attrs.width, window_attrs.height, True) != Success) {
171
        av_log(s, AV_LOG_ERROR, "Could not copy image to XV shared memory buffer\n");
172
        return AVERROR_EXTERNAL;
173
    }
174
    return 0;
175
}
176
 
177
static int xv_write_trailer(AVFormatContext *s)
178
{
179
    XVContext *xv = s->priv_data;
180
 
181
    XShmDetach(xv->display, &xv->yuv_shminfo);
182
    shmdt(xv->yuv_image->data);
183
    XFree(xv->yuv_image);
184
    XCloseDisplay(xv->display);
185
    return 0;
186
}
187
 
188
#define OFFSET(x) offsetof(XVContext, x)
189
static const AVOption options[] = {
190
    { "display_name", "set display name",       OFFSET(display_name), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
191
    { "window_size",  "set window forced size", OFFSET(window_width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
192
    { "window_title", "set window title",       OFFSET(window_title), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
193
    { "window_x",     "set window x offset",    OFFSET(window_x),     AV_OPT_TYPE_INT,    {.i64 = 0 }, -INT_MAX, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
194
    { "window_y",     "set window y offset",    OFFSET(window_y),     AV_OPT_TYPE_INT,    {.i64 = 0 }, -INT_MAX, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
195
    { NULL }
196
 
197
};
198
 
199
static const AVClass xv_class = {
200
    .class_name = "xvideo outdev",
201
    .item_name  = av_default_item_name,
202
    .option     = options,
203
    .version    = LIBAVUTIL_VERSION_INT,
204
};
205
 
206
AVOutputFormat ff_xv_muxer = {
207
    .name           = "xv",
208
    .long_name      = NULL_IF_CONFIG_SMALL("XV (XVideo) output device"),
209
    .priv_data_size = sizeof(XVContext),
210
    .audio_codec    = AV_CODEC_ID_NONE,
211
    .video_codec    = AV_CODEC_ID_RAWVIDEO,
212
    .write_header   = xv_write_header,
213
    .write_packet   = xv_write_packet,
214
    .write_trailer  = xv_write_trailer,
215
    .flags          = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS,
216
    .priv_class     = &xv_class,
217
};