1 /**************************************************************************
2  *
3  * Copyright 2009 Younes Manton.
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  **************************************************************************/
27 
28 #include <assert.h>
29 
30 #include <X11/Xlibint.h>
31 #include <X11/extensions/XvMClib.h>
32 
33 #include "pipe/p_screen.h"
34 #include "pipe/p_video_codec.h"
35 #include "pipe/p_video_state.h"
36 #include "pipe/p_state.h"
37 
38 #include "util/u_memory.h"
39 
40 #include "vl/vl_csc.h"
41 #include "vl/vl_winsys.h"
42 
43 #include "xvmc_private.h"
44 
Validate(Display * dpy,XvPortID port,int surface_type_id,unsigned int width,unsigned int height,int flags,bool * found_port,int * screen,int * chroma_format,int * mc_type,int * surface_flags,unsigned short * subpic_max_w,unsigned short * subpic_max_h)45 static Status Validate(Display *dpy, XvPortID port, int surface_type_id,
46                        unsigned int width, unsigned int height, int flags,
47                        bool *found_port, int *screen, int *chroma_format,
48                        int *mc_type, int *surface_flags,
49                        unsigned short *subpic_max_w,
50                        unsigned short *subpic_max_h)
51 {
52    bool found_surface = false;
53    XvAdaptorInfo *adaptor_info;
54    unsigned int num_adaptors;
55    int num_types;
56    unsigned int max_width = 0, max_height = 0;
57    Status ret;
58 
59    assert(dpy);
60    assert(found_port);
61    assert(screen);
62    assert(chroma_format);
63    assert(mc_type);
64    assert(surface_flags);
65    assert(subpic_max_w);
66    assert(subpic_max_h);
67 
68    *found_port = false;
69 
70    for (int i = 0; i < XScreenCount(dpy); ++i) {
71       ret = XvQueryAdaptors(dpy, XRootWindow(dpy, i), &num_adaptors, &adaptor_info);
72       if (ret != Success)
73          return ret;
74 
75       for (unsigned int j = 0; j < num_adaptors && !*found_port; ++j) {
76          for (unsigned int k = 0; k < adaptor_info[j].num_ports && !*found_port; ++k) {
77             XvMCSurfaceInfo *surface_info;
78 
79             if (adaptor_info[j].base_id + k != port)
80                continue;
81 
82             *found_port = true;
83 
84             surface_info = XvMCListSurfaceTypes(dpy, adaptor_info[j].base_id, &num_types);
85             if (!surface_info) {
86                XvFreeAdaptorInfo(adaptor_info);
87                return BadAlloc;
88             }
89 
90             for (int l = 0; l < num_types && !found_surface; ++l) {
91                if (surface_info[l].surface_type_id != surface_type_id)
92                   continue;
93 
94                found_surface = true;
95                max_width = surface_info[l].max_width;
96                max_height = surface_info[l].max_height;
97                *chroma_format = surface_info[l].chroma_format;
98                *mc_type = surface_info[l].mc_type;
99                *surface_flags = surface_info[l].flags;
100                *subpic_max_w = surface_info[l].subpicture_max_width;
101                *subpic_max_h = surface_info[l].subpicture_max_height;
102                *screen = i;
103 
104                XVMC_MSG(XVMC_TRACE, "[XvMC] Found requested context surface format.\n" \
105                                     "[XvMC]   screen=%u, port=%u\n" \
106                                     "[XvMC]   id=0x%08X\n" \
107                                     "[XvMC]   max width=%u, max height=%u\n" \
108                                     "[XvMC]   chroma format=0x%08X\n" \
109                                     "[XvMC]   acceleration level=0x%08X\n" \
110                                     "[XvMC]   flags=0x%08X\n" \
111                                     "[XvMC]   subpicture max width=%u, max height=%u\n",
112                                     i, port, surface_type_id, max_width, max_height, *chroma_format,
113                                     *mc_type, *surface_flags, *subpic_max_w, *subpic_max_h);
114             }
115 
116             free(surface_info);
117          }
118       }
119 
120       XvFreeAdaptorInfo(adaptor_info);
121    }
122 
123    if (!*found_port) {
124       XVMC_MSG(XVMC_ERR, "[XvMC] Could not find a suitable port.\n");
125       return XvBadPort;
126    }
127    if (!found_surface) {
128       XVMC_MSG(XVMC_ERR, "[XvMC] Could not find a suitable surface.\n");
129       return BadMatch;
130    }
131    if (width > max_width || height > max_height) {
132       XVMC_MSG(XVMC_ERR, "[XvMC] Requested context dimensions (w=%u,h=%u) too large (max w=%u,h=%u).\n",
133                width, height, max_width, max_height);
134       return BadValue;
135    }
136    if (flags != XVMC_DIRECT && flags != 0) {
137       XVMC_MSG(XVMC_ERR, "[XvMC] Invalid context flags 0x%08X.\n", flags);
138       return BadValue;
139    }
140 
141    return Success;
142 }
143 
ProfileToPipe(int xvmc_profile)144 static enum pipe_video_profile ProfileToPipe(int xvmc_profile)
145 {
146    if (xvmc_profile & XVMC_MPEG_1)
147       assert(0);
148    if (xvmc_profile & XVMC_MPEG_2)
149       return PIPE_VIDEO_PROFILE_MPEG2_MAIN;
150    if (xvmc_profile & XVMC_H263)
151       assert(0);
152    if (xvmc_profile & XVMC_MPEG_4)
153       assert(0);
154 
155    assert(0);
156 
157    XVMC_MSG(XVMC_ERR, "[XvMC] Unrecognized profile 0x%08X.\n", xvmc_profile);
158 
159    return -1;
160 }
161 
FormatToPipe(int xvmc_format)162 static enum pipe_video_chroma_format FormatToPipe(int xvmc_format)
163 {
164    switch (xvmc_format) {
165       case XVMC_CHROMA_FORMAT_420:
166          return PIPE_VIDEO_CHROMA_FORMAT_420;
167       case XVMC_CHROMA_FORMAT_422:
168          return PIPE_VIDEO_CHROMA_FORMAT_422;
169       case XVMC_CHROMA_FORMAT_444:
170          return PIPE_VIDEO_CHROMA_FORMAT_444;
171       default:
172          assert(0);
173    }
174 
175    XVMC_MSG(XVMC_ERR, "[XvMC] Unrecognized format 0x%08X.\n", xvmc_format);
176 
177    return -1;
178 }
179 
180 PUBLIC
XvMCCreateContext(Display * dpy,XvPortID port,int surface_type_id,int width,int height,int flags,XvMCContext * context)181 Status XvMCCreateContext(Display *dpy, XvPortID port, int surface_type_id,
182                          int width, int height, int flags, XvMCContext *context)
183 {
184    bool found_port;
185    int scrn = 0;
186    int chroma_format = 0;
187    int mc_type = 0;
188    int surface_flags = 0;
189    unsigned short subpic_max_w = 0;
190    unsigned short subpic_max_h = 0;
191    Status ret;
192    struct vl_screen *vscreen;
193    struct pipe_context *pipe;
194    struct pipe_video_codec templat = {0};
195    XvMCContextPrivate *context_priv;
196    vl_csc_matrix csc;
197 
198    XVMC_MSG(XVMC_TRACE, "[XvMC] Creating context %p.\n", context);
199 
200    assert(dpy);
201 
202    if (!context)
203       return XvMCBadContext;
204 
205    ret = Validate(dpy, port, surface_type_id, width, height, flags,
206                   &found_port, &scrn, &chroma_format, &mc_type, &surface_flags,
207                   &subpic_max_w, &subpic_max_h);
208 
209    /* Success and XvBadPort have the same value */
210    if (ret != Success || !found_port)
211       return ret;
212 
213    /* XXX: Current limits */
214    if (chroma_format != XVMC_CHROMA_FORMAT_420) {
215       XVMC_MSG(XVMC_ERR, "[XvMC] Cannot decode requested surface type. Unsupported chroma format.\n");
216       return BadImplementation;
217    }
218    if ((mc_type & ~XVMC_IDCT) != (XVMC_MOCOMP | XVMC_MPEG_2)) {
219       XVMC_MSG(XVMC_ERR, "[XvMC] Cannot decode requested surface type. Non-MPEG2/Mocomp/iDCT acceleration unsupported.\n");
220       return BadImplementation;
221    }
222    if (surface_flags & XVMC_INTRA_UNSIGNED) {
223       XVMC_MSG(XVMC_ERR, "[XvMC] Cannot decode requested surface type. Unsigned intra unsupported.\n");
224       return BadImplementation;
225    }
226 
227    context_priv = CALLOC(1, sizeof(XvMCContextPrivate));
228    if (!context_priv)
229       return BadAlloc;
230 
231    /* TODO: Reuse screen if process creates another context */
232    vscreen = vl_dri2_screen_create(dpy, scrn);
233 
234    if (!vscreen) {
235       XVMC_MSG(XVMC_ERR, "[XvMC] Could not create VL screen.\n");
236       FREE(context_priv);
237       return BadAlloc;
238    }
239 
240    pipe = vscreen->pscreen->context_create(vscreen->pscreen, vscreen, 0);
241    if (!pipe) {
242       XVMC_MSG(XVMC_ERR, "[XvMC] Could not create VL context.\n");
243       vscreen->destroy(vscreen);
244       FREE(context_priv);
245       return BadAlloc;
246    }
247 
248    templat.profile = ProfileToPipe(mc_type);
249    templat.entrypoint = (mc_type & XVMC_IDCT) ? PIPE_VIDEO_ENTRYPOINT_IDCT : PIPE_VIDEO_ENTRYPOINT_MC;
250    templat.chroma_format = FormatToPipe(chroma_format);
251    templat.width = width;
252    templat.height = height;
253    templat.max_references = 2;
254    templat.expect_chunked_decode = true;
255 
256    context_priv->decoder = pipe->create_video_codec(pipe, &templat);
257 
258    if (!context_priv->decoder) {
259       XVMC_MSG(XVMC_ERR, "[XvMC] Could not create VL decoder.\n");
260       pipe->destroy(pipe);
261       vscreen->destroy(vscreen);
262       FREE(context_priv);
263       return BadAlloc;
264    }
265 
266    if (!vl_compositor_init(&context_priv->compositor, pipe)) {
267       XVMC_MSG(XVMC_ERR, "[XvMC] Could not create VL compositor.\n");
268       context_priv->decoder->destroy(context_priv->decoder);
269       pipe->destroy(pipe);
270       vscreen->destroy(vscreen);
271       FREE(context_priv);
272       return BadAlloc;
273    }
274 
275    if (!vl_compositor_init_state(&context_priv->cstate, pipe)) {
276       XVMC_MSG(XVMC_ERR, "[XvMC] Could not create VL compositor state.\n");
277       vl_compositor_cleanup(&context_priv->compositor);
278       context_priv->decoder->destroy(context_priv->decoder);
279       pipe->destroy(pipe);
280       vscreen->destroy(vscreen);
281       FREE(context_priv);
282       return BadAlloc;
283    }
284 
285 
286    context_priv->color_standard =
287       debug_get_bool_option("G3DVL_NO_CSC", FALSE) ?
288       VL_CSC_COLOR_STANDARD_IDENTITY : VL_CSC_COLOR_STANDARD_BT_601;
289    context_priv->procamp = vl_default_procamp;
290 
291    vl_csc_get_matrix
292    (
293       context_priv->color_standard,
294       &context_priv->procamp, true, &csc
295    );
296    vl_compositor_set_csc_matrix(&context_priv->cstate, (const vl_csc_matrix *)&csc, 1.0f, 0.0f);
297 
298    context_priv->vscreen = vscreen;
299    context_priv->pipe = pipe;
300    context_priv->subpicture_max_width = subpic_max_w;
301    context_priv->subpicture_max_height = subpic_max_h;
302 
303    context->context_id = XAllocID(dpy);
304    context->surface_type_id = surface_type_id;
305    context->width = width;
306    context->height = height;
307    context->flags = flags;
308    context->port = port;
309    context->privData = context_priv;
310 
311    SyncHandle();
312 
313    XVMC_MSG(XVMC_TRACE, "[XvMC] Context %p created.\n", context);
314 
315    return Success;
316 }
317 
318 PUBLIC
XvMCDestroyContext(Display * dpy,XvMCContext * context)319 Status XvMCDestroyContext(Display *dpy, XvMCContext *context)
320 {
321    XvMCContextPrivate *context_priv;
322 
323    XVMC_MSG(XVMC_TRACE, "[XvMC] Destroying context %p.\n", context);
324 
325    assert(dpy);
326 
327    if (!context || !context->privData)
328       return XvMCBadContext;
329 
330    context_priv = context->privData;
331    context_priv->decoder->destroy(context_priv->decoder);
332    vl_compositor_cleanup_state(&context_priv->cstate);
333    vl_compositor_cleanup(&context_priv->compositor);
334    context_priv->pipe->destroy(context_priv->pipe);
335    context_priv->vscreen->destroy(context_priv->vscreen);
336    FREE(context_priv);
337    context->privData = NULL;
338 
339    XVMC_MSG(XVMC_TRACE, "[XvMC] Context %p destroyed.\n", context);
340 
341    return Success;
342 }
343