1/*
2 * Xephyr - A kdrive X server that runs in a host X window.
3 *          Authored by Matthew Allum <mallum@openedhand.com>
4 *
5 * Copyright © 2007 OpenedHand Ltd
6 *
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation, and that the name of OpenedHand Ltd not be used in
12 * advertising or publicity pertaining to distribution of the software without
13 * specific, written prior permission. OpenedHand Ltd makes no
14 * representations about the suitability of this software for any purpose.  It
15 * is provided "as is" without express or implied warranty.
16 *
17 * OpenedHand Ltd DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
19 * EVENT SHALL OpenedHand Ltd BE LIABLE FOR ANY SPECIAL, INDIRECT OR
20 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
22 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23 * PERFORMANCE OF THIS SOFTWARE.
24 *
25 * Authors:
26 *    Dodji Seketeli <dodji@openedhand.com>
27 */
28
29#ifdef HAVE_DIX_CONFIG_H
30#include <dix-config.h>
31#endif
32#include <string.h>
33#include <X11/extensions/Xv.h>
34#include <xcb/xcb.h>
35#include <xcb/xcb_aux.h>
36#include <xcb/xv.h>
37#include "ephyrlog.h"
38#include "kdrive.h"
39#include "kxv.h"
40#include "ephyr.h"
41#include "hostx.h"
42
43struct _EphyrXVPriv {
44    xcb_xv_query_adaptors_reply_t *host_adaptors;
45    KdVideoAdaptorPtr adaptors;
46    int num_adaptors;
47};
48typedef struct _EphyrXVPriv EphyrXVPriv;
49
50struct _EphyrPortPriv {
51    int port_number;
52    KdVideoAdaptorPtr current_adaptor;
53    EphyrXVPriv *xv_priv;
54    unsigned char *image_buf;
55    int image_buf_size;
56    int image_id;
57    int drw_x, drw_y, drw_w, drw_h;
58    int src_x, src_y, src_w, src_h;
59    int image_width, image_height;
60};
61typedef struct _EphyrPortPriv EphyrPortPriv;
62
63static Bool ephyrLocalAtomToHost(int a_local_atom, int *a_host_atom);
64
65static EphyrXVPriv *ephyrXVPrivNew(void);
66static void ephyrXVPrivDelete(EphyrXVPriv * a_this);
67static Bool ephyrXVPrivQueryHostAdaptors(EphyrXVPriv * a_this);
68static Bool ephyrXVPrivSetAdaptorsHooks(EphyrXVPriv * a_this);
69static Bool ephyrXVPrivRegisterAdaptors(EphyrXVPriv * a_this,
70                                        ScreenPtr a_screen);
71
72static Bool ephyrXVPrivIsAttrValueValid(XvAttributePtr a_attrs,
73                                        int a_attrs_len,
74                                        const char *a_attr_name,
75                                        int a_attr_value, Bool *a_is_valid);
76
77static Bool ephyrXVPrivGetImageBufSize(int a_port_id,
78                                       int a_image_id,
79                                       unsigned short a_width,
80                                       unsigned short a_height, int *a_size);
81
82static Bool ephyrXVPrivSaveImageToPortPriv(EphyrPortPriv * a_port_priv,
83                                           const unsigned char *a_image,
84                                           int a_image_len);
85
86static void ephyrStopVideo(KdScreenInfo * a_info,
87                           void *a_xv_priv, Bool a_exit);
88
89static int ephyrSetPortAttribute(KdScreenInfo * a_info,
90                                 Atom a_attr_name,
91                                 int a_attr_value, void *a_port_priv);
92
93static int ephyrGetPortAttribute(KdScreenInfo * a_screen_info,
94                                 Atom a_attr_name,
95                                 int *a_attr_value, void *a_port_priv);
96
97static void ephyrQueryBestSize(KdScreenInfo * a_info,
98                               Bool a_motion,
99                               short a_src_w,
100                               short a_src_h,
101                               short a_drw_w,
102                               short a_drw_h,
103                               unsigned int *a_prefered_w,
104                               unsigned int *a_prefered_h, void *a_port_priv);
105
106static int ephyrPutImage(KdScreenInfo * a_info,
107                         DrawablePtr a_drawable,
108                         short a_src_x,
109                         short a_src_y,
110                         short a_drw_x,
111                         short a_drw_y,
112                         short a_src_w,
113                         short a_src_h,
114                         short a_drw_w,
115                         short a_drw_h,
116                         int a_id,
117                         unsigned char *a_buf,
118                         short a_width,
119                         short a_height,
120                         Bool a_sync,
121                         RegionPtr a_clipping_region, void *a_port_priv);
122
123static int ephyrReputImage(KdScreenInfo * a_info,
124                           DrawablePtr a_drawable,
125                           short a_drw_x,
126                           short a_drw_y,
127                           RegionPtr a_clipping_region, void *a_port_priv);
128
129static int ephyrPutVideo(KdScreenInfo * a_info,
130                         DrawablePtr a_drawable,
131                         short a_vid_x, short a_vid_y,
132                         short a_drw_x, short a_drw_y,
133                         short a_vid_w, short a_vid_h,
134                         short a_drw_w, short a_drw_h,
135                         RegionPtr a_clip_region, void *a_port_priv);
136
137static int ephyrGetVideo(KdScreenInfo * a_info,
138                         DrawablePtr a_drawable,
139                         short a_vid_x, short a_vid_y,
140                         short a_drw_x, short a_drw_y,
141                         short a_vid_w, short a_vid_h,
142                         short a_drw_w, short a_drw_h,
143                         RegionPtr a_clip_region, void *a_port_priv);
144
145static int ephyrPutStill(KdScreenInfo * a_info,
146                         DrawablePtr a_drawable,
147                         short a_vid_x, short a_vid_y,
148                         short a_drw_x, short a_drw_y,
149                         short a_vid_w, short a_vid_h,
150                         short a_drw_w, short a_drw_h,
151                         RegionPtr a_clip_region, void *a_port_priv);
152
153static int ephyrGetStill(KdScreenInfo * a_info,
154                         DrawablePtr a_drawable,
155                         short a_vid_x, short a_vid_y,
156                         short a_drw_x, short a_drw_y,
157                         short a_vid_w, short a_vid_h,
158                         short a_drw_w, short a_drw_h,
159                         RegionPtr a_clip_region, void *a_port_priv);
160
161static int ephyrQueryImageAttributes(KdScreenInfo * a_info,
162                                     int a_id,
163                                     unsigned short *a_w,
164                                     unsigned short *a_h,
165                                     int *a_pitches, int *a_offsets);
166static int s_base_port_id;
167
168/**************
169 * <helpers>
170 * ************/
171
172static Bool
173adaptor_has_flags(const xcb_xv_adaptor_info_t *adaptor, uint32_t flags)
174{
175    return (adaptor->type & flags) == flags;
176}
177
178static Bool
179ephyrLocalAtomToHost(int a_local_atom, int *a_host_atom)
180{
181    xcb_connection_t *conn = hostx_get_xcbconn();
182    xcb_intern_atom_cookie_t cookie;
183    xcb_intern_atom_reply_t *reply;
184    const char *atom_name = NULL;
185
186    EPHYR_RETURN_VAL_IF_FAIL(a_host_atom, FALSE);
187
188    if (!ValidAtom(a_local_atom))
189        return FALSE;
190
191    atom_name = NameForAtom(a_local_atom);
192
193    if (!atom_name)
194        return FALSE;
195
196    cookie = xcb_intern_atom(conn, FALSE, strlen(atom_name), atom_name);
197    reply = xcb_intern_atom_reply(conn, cookie, NULL);
198    if (!reply || reply->atom == None) {
199        EPHYR_LOG_ERROR("no atom for string %s defined in host X\n", atom_name);
200        return FALSE;
201    }
202
203    *a_host_atom = reply->atom;
204    free(reply);
205
206    return TRUE;
207}
208
209/**************
210 *</helpers>
211 * ************/
212
213Bool
214ephyrInitVideo(ScreenPtr pScreen)
215{
216    Bool is_ok = FALSE;
217
218    KdScreenPriv(pScreen);
219    KdScreenInfo *screen = pScreenPriv->screen;
220    static EphyrXVPriv *xv_priv;
221
222    EPHYR_LOG("enter\n");
223
224    if (screen->fb.bitsPerPixel == 8) {
225        EPHYR_LOG_ERROR("8 bits depth not supported\n");
226        return FALSE;
227    }
228
229    if (!hostx_has_extension(&xcb_xv_id)) {
230        EPHYR_LOG_ERROR("Host has no XVideo extension\n");
231        return FALSE;
232    }
233
234    if (!xv_priv) {
235        xv_priv = ephyrXVPrivNew();
236    }
237    if (!xv_priv) {
238        EPHYR_LOG_ERROR("failed to create xv_priv\n");
239        goto out;
240    }
241
242    if (!ephyrXVPrivRegisterAdaptors(xv_priv, pScreen)) {
243        EPHYR_LOG_ERROR("failed to register adaptors\n");
244        goto out;
245    }
246    is_ok = TRUE;
247
248 out:
249    return is_ok;
250}
251
252static EphyrXVPriv *
253ephyrXVPrivNew(void)
254{
255    EphyrXVPriv *xv_priv = NULL;
256
257    EPHYR_LOG("enter\n");
258
259    xv_priv = calloc(1, sizeof(EphyrXVPriv));
260    if (!xv_priv) {
261        EPHYR_LOG_ERROR("failed to create EphyrXVPriv\n");
262        goto error;
263    }
264
265    if (!ephyrXVPrivQueryHostAdaptors(xv_priv)) {
266        EPHYR_LOG_ERROR("failed to query the host x for xv properties\n");
267        goto error;
268    }
269    if (!ephyrXVPrivSetAdaptorsHooks(xv_priv)) {
270        EPHYR_LOG_ERROR("failed to set xv_priv hooks\n");
271        goto error;
272    }
273
274    EPHYR_LOG("leave\n");
275    return xv_priv;
276
277 error:
278    if (xv_priv) {
279        ephyrXVPrivDelete(xv_priv);
280        xv_priv = NULL;
281    }
282    return NULL;
283}
284
285static void
286ephyrXVPrivDelete(EphyrXVPriv * a_this)
287{
288    EPHYR_LOG("enter\n");
289
290    if (!a_this)
291        return;
292    if (a_this->host_adaptors) {
293        free(a_this->host_adaptors);
294        a_this->host_adaptors = NULL;
295    }
296    free(a_this->adaptors);
297    a_this->adaptors = NULL;
298    free(a_this);
299    EPHYR_LOG("leave\n");
300}
301
302static Bool
303translate_video_encodings(KdVideoAdaptorPtr adaptor,
304                          xcb_xv_adaptor_info_t *host_adaptor)
305{
306    xcb_connection_t *conn = hostx_get_xcbconn();
307    int i;
308    xcb_xv_query_encodings_cookie_t cookie;
309    xcb_xv_query_encodings_reply_t *reply;
310    xcb_xv_encoding_info_iterator_t encoding_it;
311
312    cookie = xcb_xv_query_encodings(conn, host_adaptor->base_id);
313    reply = xcb_xv_query_encodings_reply(conn, cookie, NULL);
314    if (!reply)
315        return FALSE;
316
317    adaptor->nEncodings = reply->num_encodings;
318    adaptor->pEncodings = calloc(adaptor->nEncodings,
319                                  sizeof(*adaptor->pEncodings));
320    if (!adaptor->pEncodings) {
321        free(reply);
322        return FALSE;
323    }
324
325    encoding_it = xcb_xv_query_encodings_info_iterator(reply);
326    for (i = 0; i < adaptor->nEncodings; i++) {
327        xcb_xv_encoding_info_t *encoding_info = encoding_it.data;
328        KdVideoEncodingPtr encoding = &adaptor->pEncodings[i];
329
330        encoding->id = encoding_info->encoding;
331        encoding->name = strndup(xcb_xv_encoding_info_name(encoding_info),
332                                 encoding_info->name_size);
333        encoding->width = encoding_info->width;
334        encoding->height = encoding_info->height;
335        encoding->rate.numerator = encoding_info->rate.numerator;
336        encoding->rate.denominator = encoding_info->rate.denominator;
337
338        xcb_xv_encoding_info_next(&encoding_it);
339    }
340
341    free(reply);
342    return TRUE;
343}
344
345static Bool
346translate_xv_attributes(KdVideoAdaptorPtr adaptor,
347                        xcb_xv_adaptor_info_t *host_adaptor)
348{
349    xcb_connection_t *conn = hostx_get_xcbconn();
350    int i = 0;
351    xcb_xv_attribute_info_iterator_t it;
352    xcb_xv_query_port_attributes_cookie_t cookie =
353        xcb_xv_query_port_attributes(conn, host_adaptor->base_id);
354    xcb_xv_query_port_attributes_reply_t *reply =
355        xcb_xv_query_port_attributes_reply(conn, cookie, NULL);
356
357    if (!reply)
358        return FALSE;
359
360    adaptor->nAttributes = reply->num_attributes;
361    adaptor->pAttributes = calloc(reply->num_attributes,
362                                  sizeof(*adaptor->pAttributes));
363    if (!adaptor->pAttributes) {
364        EPHYR_LOG_ERROR("failed to allocate attributes\n");
365        free(reply);
366        return FALSE;
367    }
368
369    it = xcb_xv_query_port_attributes_attributes_iterator(reply);
370    for (i = 0; i < reply->num_attributes; i++) {
371        XvAttributePtr attribute = &adaptor->pAttributes[i];
372
373        attribute->flags = it.data->flags;
374        attribute->min_value = it.data->min;
375        attribute->max_value = it.data->max;
376        attribute->name = strndup(xcb_xv_attribute_info_name(it.data),
377                                  it.data->size);
378
379        /* make sure atoms of attrs names are created in xephyr */
380        MakeAtom(xcb_xv_attribute_info_name(it.data), it.data->size, TRUE);
381
382        xcb_xv_attribute_info_next(&it);
383    }
384
385    free(reply);
386    return TRUE;
387}
388
389static Bool
390translate_xv_image_formats(KdVideoAdaptorPtr adaptor,
391                           xcb_xv_adaptor_info_t *host_adaptor)
392{
393    xcb_connection_t *conn = hostx_get_xcbconn();
394    int i = 0;
395    xcb_xv_list_image_formats_cookie_t cookie =
396        xcb_xv_list_image_formats(conn, host_adaptor->base_id);
397    xcb_xv_list_image_formats_reply_t *reply =
398        xcb_xv_list_image_formats_reply(conn, cookie, NULL);
399    xcb_xv_image_format_info_t *formats;
400
401    if (!reply)
402        return FALSE;
403
404    adaptor->nImages = reply->num_formats;
405    adaptor->pImages = calloc(reply->num_formats, sizeof(XvImageRec));
406    if (!adaptor->pImages) {
407        free(reply);
408        return FALSE;
409    }
410
411    formats = xcb_xv_list_image_formats_format(reply);
412    for (i = 0; i < reply->num_formats; i++) {
413        XvImagePtr image = &adaptor->pImages[i];
414
415        image->id = formats[i].id;
416        image->type = formats[i].type;
417        image->byte_order = formats[i].byte_order;
418        memcpy(image->guid, formats[i].guid, 16);
419        image->bits_per_pixel = formats[i].bpp;
420        image->format = formats[i].format;
421        image->num_planes = formats[i].num_planes;
422        image->depth = formats[i].depth;
423        image->red_mask = formats[i].red_mask;
424        image->green_mask = formats[i].green_mask;
425        image->blue_mask = formats[i].blue_mask;
426        image->y_sample_bits = formats[i].y_sample_bits;
427        image->u_sample_bits = formats[i].u_sample_bits;
428        image->v_sample_bits = formats[i].v_sample_bits;
429        image->horz_y_period = formats[i].vhorz_y_period;
430        image->horz_u_period = formats[i].vhorz_u_period;
431        image->horz_v_period = formats[i].vhorz_v_period;
432        image->vert_y_period = formats[i].vvert_y_period;
433        image->vert_u_period = formats[i].vvert_u_period;
434        image->vert_v_period = formats[i].vvert_v_period;
435        memcpy(image->component_order, formats[i].vcomp_order, 32);
436        image->scanline_order = formats[i].vscanline_order;
437    }
438
439    free(reply);
440    return TRUE;
441}
442
443static Bool
444ephyrXVPrivQueryHostAdaptors(EphyrXVPriv * a_this)
445{
446    xcb_connection_t *conn = hostx_get_xcbconn();
447    xcb_screen_t *xscreen = xcb_aux_get_screen(conn, hostx_get_screen());
448    int base_port_id = 0, i = 0, port_priv_offset = 0;
449    Bool is_ok = FALSE;
450    xcb_generic_error_t *e = NULL;
451    xcb_xv_adaptor_info_iterator_t it;
452
453    EPHYR_RETURN_VAL_IF_FAIL(a_this, FALSE);
454
455    EPHYR_LOG("enter\n");
456
457    {
458        xcb_xv_query_adaptors_cookie_t cookie =
459            xcb_xv_query_adaptors(conn, xscreen->root);
460        a_this->host_adaptors = xcb_xv_query_adaptors_reply(conn, cookie, &e);
461        if (e) {
462            free(e);
463            EPHYR_LOG_ERROR("failed to query host adaptors\n");
464            goto out;
465        }
466    }
467
468    if (a_this->host_adaptors)
469        a_this->num_adaptors = a_this->host_adaptors->num_adaptors;
470    if (a_this->num_adaptors <= 0) {
471        EPHYR_LOG_ERROR("failed to get number of host adaptors\n");
472        goto out;
473    }
474    EPHYR_LOG("host has %d adaptors\n", a_this->num_adaptors);
475    /*
476     * copy what we can from adaptors into a_this->adaptors
477     */
478    if (a_this->num_adaptors) {
479        a_this->adaptors = calloc(a_this->num_adaptors,
480                                  sizeof(KdVideoAdaptorRec));
481        if (!a_this->adaptors) {
482            EPHYR_LOG_ERROR("failed to create internal adaptors\n");
483            goto out;
484        }
485    }
486
487    it = xcb_xv_query_adaptors_info_iterator(a_this->host_adaptors);
488    for (i = 0; i < a_this->num_adaptors; i++) {
489        xcb_xv_adaptor_info_t *cur_host_adaptor = it.data;
490        xcb_xv_format_t *format = xcb_xv_adaptor_info_formats(cur_host_adaptor);
491        int j = 0;
492
493        a_this->adaptors[i].nPorts = cur_host_adaptor->num_ports;
494        if (a_this->adaptors[i].nPorts <= 0) {
495            EPHYR_LOG_ERROR("Could not find any port of adaptor %d\n", i);
496            continue;
497        }
498        a_this->adaptors[i].type = cur_host_adaptor->type;
499        a_this->adaptors[i].type |= XvWindowMask;
500        a_this->adaptors[i].flags =
501            VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT;
502        a_this->adaptors[i].name =
503            strndup(xcb_xv_adaptor_info_name(cur_host_adaptor),
504                    cur_host_adaptor->name_size);
505        if (!a_this->adaptors[i].name)
506            a_this->adaptors[i].name = strdup("Xephyr Video Overlay");
507        base_port_id = cur_host_adaptor->base_id;
508        if (base_port_id < 0) {
509            EPHYR_LOG_ERROR("failed to get port id for adaptor %d\n", i);
510            continue;
511        }
512        if (!s_base_port_id)
513            s_base_port_id = base_port_id;
514
515        if (!translate_video_encodings(&a_this->adaptors[i],
516                                       cur_host_adaptor)) {
517            EPHYR_LOG_ERROR("failed to get encodings for port port id %d,"
518                            " adaptors %d\n", base_port_id, i);
519            continue;
520        }
521
522        a_this->adaptors[i].nFormats = cur_host_adaptor->num_formats;
523        a_this->adaptors[i].pFormats =
524            calloc(cur_host_adaptor->num_formats,
525                   sizeof(*a_this->adaptors[i].pFormats));
526        for (j = 0; j < cur_host_adaptor->num_formats; j++) {
527            xcb_visualtype_t *visual =
528                xcb_aux_find_visual_by_id(xscreen, format[j].visual);
529            a_this->adaptors[i].pFormats[j].depth = format[j].depth;
530            a_this->adaptors[i].pFormats[j].class = visual->_class;
531        }
532
533        a_this->adaptors[i].pPortPrivates =
534            calloc(a_this->adaptors[i].nPorts,
535                   sizeof(DevUnion) + sizeof(EphyrPortPriv));
536        port_priv_offset = a_this->adaptors[i].nPorts;
537        for (j = 0; j < a_this->adaptors[i].nPorts; j++) {
538            EphyrPortPriv *port_privs_base =
539                (EphyrPortPriv *) &a_this->adaptors[i].
540                pPortPrivates[port_priv_offset];
541            EphyrPortPriv *port_priv = &port_privs_base[j];
542
543            port_priv->port_number = base_port_id + j;
544            port_priv->current_adaptor = &a_this->adaptors[i];
545            port_priv->xv_priv = a_this;
546            a_this->adaptors[i].pPortPrivates[j].ptr = port_priv;
547        }
548
549        if (!translate_xv_attributes(&a_this->adaptors[i], cur_host_adaptor)) {
550        {
551            EPHYR_LOG_ERROR("failed to get port attribute "
552                            "for adaptor %d\n", i);
553            continue;
554        }
555        }
556
557        if (!translate_xv_image_formats(&a_this->adaptors[i], cur_host_adaptor)) {
558            EPHYR_LOG_ERROR("failed to get image formats "
559                            "for adaptor %d\n", i);
560            continue;
561        }
562
563        xcb_xv_adaptor_info_next(&it);
564    }
565    is_ok = TRUE;
566
567 out:
568    EPHYR_LOG("leave\n");
569    return is_ok;
570}
571
572static Bool
573ephyrXVPrivSetAdaptorsHooks(EphyrXVPriv * a_this)
574{
575    int i = 0;
576    xcb_xv_adaptor_info_iterator_t it;
577
578    EPHYR_RETURN_VAL_IF_FAIL(a_this, FALSE);
579
580    EPHYR_LOG("enter\n");
581
582    it = xcb_xv_query_adaptors_info_iterator(a_this->host_adaptors);
583    for (i = 0; i < a_this->num_adaptors; i++) {
584        xcb_xv_adaptor_info_t *cur_host_adaptor = it.data;
585
586        a_this->adaptors[i].ReputImage = ephyrReputImage;
587        a_this->adaptors[i].StopVideo = ephyrStopVideo;
588        a_this->adaptors[i].SetPortAttribute = ephyrSetPortAttribute;
589        a_this->adaptors[i].GetPortAttribute = ephyrGetPortAttribute;
590        a_this->adaptors[i].QueryBestSize = ephyrQueryBestSize;
591        a_this->adaptors[i].QueryImageAttributes = ephyrQueryImageAttributes;
592
593        if (adaptor_has_flags(cur_host_adaptor,
594                              XCB_XV_TYPE_IMAGE_MASK | XCB_XV_TYPE_INPUT_MASK))
595            a_this->adaptors[i].PutImage = ephyrPutImage;
596
597        if (adaptor_has_flags(cur_host_adaptor,
598                              XCB_XV_TYPE_VIDEO_MASK | XCB_XV_TYPE_INPUT_MASK))
599            a_this->adaptors[i].PutVideo = ephyrPutVideo;
600
601        if (adaptor_has_flags(cur_host_adaptor,
602                              XCB_XV_TYPE_VIDEO_MASK | XCB_XV_TYPE_OUTPUT_MASK))
603            a_this->adaptors[i].GetVideo = ephyrGetVideo;
604
605        if (adaptor_has_flags(cur_host_adaptor,
606                              XCB_XV_TYPE_STILL_MASK | XCB_XV_TYPE_INPUT_MASK))
607            a_this->adaptors[i].PutStill = ephyrPutStill;
608
609        if (adaptor_has_flags(cur_host_adaptor,
610                              XCB_XV_TYPE_STILL_MASK | XCB_XV_TYPE_OUTPUT_MASK))
611            a_this->adaptors[i].GetStill = ephyrGetStill;
612    }
613    EPHYR_LOG("leave\n");
614    return TRUE;
615}
616
617static Bool
618ephyrXVPrivRegisterAdaptors(EphyrXVPriv * a_this, ScreenPtr a_screen)
619{
620    Bool is_ok = FALSE;
621
622    EPHYR_RETURN_VAL_IF_FAIL(a_this && a_screen, FALSE);
623
624    EPHYR_LOG("enter\n");
625
626    if (!a_this->num_adaptors)
627        goto out;
628
629    if (!KdXVScreenInit(a_screen, a_this->adaptors, a_this->num_adaptors)) {
630        EPHYR_LOG_ERROR("failed to register adaptors\n");
631        goto out;
632    }
633    EPHYR_LOG("there are  %d registered adaptors\n", a_this->num_adaptors);
634    is_ok = TRUE;
635
636 out:
637
638    EPHYR_LOG("leave\n");
639    return is_ok;
640}
641
642static Bool
643ephyrXVPrivIsAttrValueValid(XvAttributePtr a_attrs,
644                            int a_attrs_len,
645                            const char *a_attr_name,
646                            int a_attr_value, Bool *a_is_valid)
647{
648    int i = 0;
649
650    EPHYR_RETURN_VAL_IF_FAIL(a_attrs && a_attr_name && a_is_valid, FALSE);
651
652    for (i = 0; i < a_attrs_len; i++) {
653        if (a_attrs[i].name && strcmp(a_attrs[i].name, a_attr_name))
654            continue;
655        if (a_attrs[i].min_value > a_attr_value ||
656            a_attrs[i].max_value < a_attr_value) {
657            *a_is_valid = FALSE;
658            EPHYR_LOG_ERROR("attribute was not valid\n"
659                            "value:%d. min:%d. max:%d\n",
660                            a_attr_value,
661                            a_attrs[i].min_value, a_attrs[i].max_value);
662        }
663        else {
664            *a_is_valid = TRUE;
665        }
666        return TRUE;
667    }
668    return FALSE;
669}
670
671static Bool
672ephyrXVPrivGetImageBufSize(int a_port_id,
673                           int a_image_id,
674                           unsigned short a_width,
675                           unsigned short a_height, int *a_size)
676{
677    xcb_connection_t *conn = hostx_get_xcbconn();
678    xcb_xv_query_image_attributes_cookie_t cookie;
679    xcb_xv_query_image_attributes_reply_t *reply;
680    Bool is_ok = FALSE;
681
682    EPHYR_RETURN_VAL_IF_FAIL(a_size, FALSE);
683
684    EPHYR_LOG("enter\n");
685
686    cookie = xcb_xv_query_image_attributes(conn,
687                                           a_port_id, a_image_id,
688                                           a_width, a_height);
689    reply = xcb_xv_query_image_attributes_reply(conn, cookie, NULL);
690    if (!reply)
691        goto out;
692
693    *a_size = reply->data_size;
694    is_ok = TRUE;
695
696    free(reply);
697
698 out:
699    EPHYR_LOG("leave\n");
700    return is_ok;
701}
702
703static Bool
704ephyrXVPrivSaveImageToPortPriv(EphyrPortPriv * a_port_priv,
705                               const unsigned char *a_image_buf,
706                               int a_image_len)
707{
708    Bool is_ok = FALSE;
709
710    EPHYR_LOG("enter\n");
711
712    if (a_port_priv->image_buf_size < a_image_len) {
713        unsigned char *buf = NULL;
714
715        buf = realloc(a_port_priv->image_buf, a_image_len);
716        if (!buf) {
717            EPHYR_LOG_ERROR("failed to realloc image buffer\n");
718            goto out;
719        }
720        a_port_priv->image_buf = buf;
721        a_port_priv->image_buf_size = a_image_len;
722    }
723    memmove(a_port_priv->image_buf, a_image_buf, a_image_len);
724    is_ok = TRUE;
725
726 out:
727    return is_ok;
728    EPHYR_LOG("leave\n");
729}
730
731static void
732ephyrStopVideo(KdScreenInfo * a_info, void *a_port_priv, Bool a_exit)
733{
734    xcb_connection_t *conn = hostx_get_xcbconn();
735    EphyrPortPriv *port_priv = a_port_priv;
736    EphyrScrPriv *scrpriv = a_info->driver;
737
738    EPHYR_RETURN_IF_FAIL(port_priv);
739
740    EPHYR_LOG("enter\n");
741    xcb_xv_stop_video(conn, port_priv->port_number, scrpriv->win);
742    EPHYR_LOG("leave\n");
743}
744
745static int
746ephyrSetPortAttribute(KdScreenInfo * a_info,
747                      Atom a_attr_name, int a_attr_value, void *a_port_priv)
748{
749    xcb_connection_t *conn = hostx_get_xcbconn();
750    int res = Success, host_atom = 0;
751    EphyrPortPriv *port_priv = a_port_priv;
752    Bool is_attr_valid = FALSE;
753
754    EPHYR_RETURN_VAL_IF_FAIL(port_priv, BadMatch);
755    EPHYR_RETURN_VAL_IF_FAIL(port_priv->current_adaptor, BadMatch);
756    EPHYR_RETURN_VAL_IF_FAIL(port_priv->current_adaptor->pAttributes, BadMatch);
757    EPHYR_RETURN_VAL_IF_FAIL(port_priv->current_adaptor->nAttributes, BadMatch);
758    EPHYR_RETURN_VAL_IF_FAIL(ValidAtom(a_attr_name), BadMatch);
759
760    EPHYR_LOG("enter, portnum:%d, atomid:%d, attr_name:%s, attr_val:%d\n",
761              port_priv->port_number,
762              (int) a_attr_name, NameForAtom(a_attr_name), a_attr_value);
763
764    if (!ephyrLocalAtomToHost(a_attr_name, &host_atom)) {
765        EPHYR_LOG_ERROR("failed to convert local atom to host atom\n");
766        res = BadMatch;
767        goto out;
768    }
769
770    if (!ephyrXVPrivIsAttrValueValid(port_priv->current_adaptor->pAttributes,
771                                     port_priv->current_adaptor->nAttributes,
772                                     NameForAtom(a_attr_name),
773                                     a_attr_value, &is_attr_valid)) {
774        EPHYR_LOG_ERROR("failed to validate attribute %s\n",
775                        NameForAtom(a_attr_name));
776        /*
777           res = BadMatch ;
778           goto out ;
779         */
780    }
781    if (!is_attr_valid) {
782        EPHYR_LOG_ERROR("attribute %s is not valid\n",
783                        NameForAtom(a_attr_name));
784        /*
785           res = BadMatch ;
786           goto out ;
787         */
788    }
789
790    xcb_xv_set_port_attribute(conn, port_priv->port_number,
791                              host_atom, a_attr_value);
792    xcb_flush(conn);
793
794    res = Success;
795 out:
796    EPHYR_LOG("leave\n");
797    return res;
798}
799
800static int
801ephyrGetPortAttribute(KdScreenInfo * a_screen_info,
802                      Atom a_attr_name, int *a_attr_value, void *a_port_priv)
803{
804    xcb_connection_t *conn = hostx_get_xcbconn();
805    int res = Success, host_atom = 0;
806    EphyrPortPriv *port_priv = a_port_priv;
807    xcb_generic_error_t *e;
808    xcb_xv_get_port_attribute_cookie_t cookie;
809    xcb_xv_get_port_attribute_reply_t *reply;
810
811    EPHYR_RETURN_VAL_IF_FAIL(port_priv, BadMatch);
812    EPHYR_RETURN_VAL_IF_FAIL(ValidAtom(a_attr_name), BadMatch);
813
814    EPHYR_LOG("enter, portnum:%d, atomid:%d, attr_name:%s\n",
815              port_priv->port_number,
816              (int) a_attr_name, NameForAtom(a_attr_name));
817
818    if (!ephyrLocalAtomToHost(a_attr_name, &host_atom)) {
819        EPHYR_LOG_ERROR("failed to convert local atom to host atom\n");
820        res = BadMatch;
821        goto out;
822    }
823
824    cookie = xcb_xv_get_port_attribute(conn, port_priv->port_number, host_atom);
825    reply = xcb_xv_get_port_attribute_reply(conn, cookie, &e);
826    if (e) {
827        EPHYR_LOG_ERROR ("XvGetPortAttribute() failed: %d \n", e->error_code);
828        free(e);
829        res = BadMatch;
830        goto out;
831    }
832    *a_attr_value = reply->value;
833
834    free(reply);
835
836    res = Success;
837 out:
838    EPHYR_LOG("leave\n");
839    return res;
840}
841
842static void
843ephyrQueryBestSize(KdScreenInfo * a_info,
844                   Bool a_motion,
845                   short a_src_w,
846                   short a_src_h,
847                   short a_drw_w,
848                   short a_drw_h,
849                   unsigned int *a_prefered_w,
850                   unsigned int *a_prefered_h, void *a_port_priv)
851{
852    xcb_connection_t *conn = hostx_get_xcbconn();
853    EphyrPortPriv *port_priv = a_port_priv;
854    xcb_xv_query_best_size_cookie_t cookie =
855        xcb_xv_query_best_size(conn,
856                               port_priv->port_number,
857                               a_src_w, a_src_h,
858                               a_drw_w, a_drw_h,
859                               a_motion);
860    xcb_xv_query_best_size_reply_t *reply =
861        xcb_xv_query_best_size_reply(conn, cookie, NULL);
862
863    EPHYR_LOG("enter: frame (%dx%d), drw (%dx%d)\n",
864              a_src_w, a_src_h, a_drw_w, a_drw_h);
865
866    if (!reply) {
867        EPHYR_LOG_ERROR ("XvQueryBestSize() failed\n");
868        return;
869    }
870    *a_prefered_w = reply->actual_width;
871    *a_prefered_h = reply->actual_height;
872    EPHYR_LOG("actual (%dx%d)\n", *a_prefered_w, *a_prefered_h);
873    free(reply);
874
875    EPHYR_LOG("leave\n");
876}
877
878
879static Bool
880ephyrHostXVPutImage(KdScreenInfo * a_info,
881                    EphyrPortPriv *port_priv,
882                    int a_image_id,
883                    int a_drw_x,
884                    int a_drw_y,
885                    int a_drw_w,
886                    int a_drw_h,
887                    int a_src_x,
888                    int a_src_y,
889                    int a_src_w,
890                    int a_src_h,
891                    int a_image_width,
892                    int a_image_height,
893                    unsigned char *a_buf,
894                    BoxPtr a_clip_rects, int a_clip_rect_nums)
895{
896    EphyrScrPriv *scrpriv = a_info->driver;
897    xcb_connection_t *conn = hostx_get_xcbconn();
898    xcb_gcontext_t gc;
899    Bool is_ok = TRUE;
900    xcb_rectangle_t *rects = NULL;
901    int data_len, width, height;
902    xcb_xv_query_image_attributes_cookie_t image_attr_cookie;
903    xcb_xv_query_image_attributes_reply_t *image_attr_reply;
904
905    EPHYR_RETURN_VAL_IF_FAIL(a_buf, FALSE);
906
907    EPHYR_LOG("enter, num_clip_rects: %d\n", a_clip_rect_nums);
908
909    image_attr_cookie = xcb_xv_query_image_attributes(conn,
910                                                      port_priv->port_number,
911                                                      a_image_id,
912                                                      a_image_width,
913                                                      a_image_height);
914    image_attr_reply = xcb_xv_query_image_attributes_reply(conn,
915                                                           image_attr_cookie,
916                                                           NULL);
917    if (!image_attr_reply)
918        goto out;
919    data_len = image_attr_reply->data_size;
920    width = image_attr_reply->width;
921    height = image_attr_reply->height;
922    free(image_attr_reply);
923
924    gc = xcb_generate_id(conn);
925    xcb_create_gc(conn, gc, scrpriv->win, 0, NULL);
926
927    if (a_clip_rect_nums) {
928        int i = 0;
929        rects = calloc(a_clip_rect_nums, sizeof(xcb_rectangle_t));
930        for (i=0; i < a_clip_rect_nums; i++) {
931            rects[i].x = a_clip_rects[i].x1;
932            rects[i].y = a_clip_rects[i].y1;
933            rects[i].width = a_clip_rects[i].x2 - a_clip_rects[i].x1;
934            rects[i].height = a_clip_rects[i].y2 - a_clip_rects[i].y1;
935            EPHYR_LOG("(x,y,w,h): (%d,%d,%d,%d)\n",
936                      rects[i].x, rects[i].y, rects[i].width, rects[i].height);
937        }
938        xcb_set_clip_rectangles(conn,
939                                XCB_CLIP_ORDERING_YX_BANDED,
940                                gc,
941                                0,
942                                0,
943                                a_clip_rect_nums,
944                                rects);
945	free(rects);
946    }
947    xcb_xv_put_image(conn,
948                     port_priv->port_number,
949                     scrpriv->win,
950                     gc,
951                     a_image_id,
952                     a_src_x, a_src_y, a_src_w, a_src_h,
953                     a_drw_x, a_drw_y, a_drw_w, a_drw_h,
954                     width, height,
955                     data_len, a_buf);
956    xcb_free_gc(conn, gc);
957
958    is_ok = TRUE;
959
960out:
961    EPHYR_LOG("leave\n");
962    return is_ok;
963}
964
965static int
966ephyrPutImage(KdScreenInfo * a_info,
967              DrawablePtr a_drawable,
968              short a_src_x,
969              short a_src_y,
970              short a_drw_x,
971              short a_drw_y,
972              short a_src_w,
973              short a_src_h,
974              short a_drw_w,
975              short a_drw_h,
976              int a_id,
977              unsigned char *a_buf,
978              short a_width,
979              short a_height,
980              Bool a_sync, RegionPtr a_clipping_region, void *a_port_priv)
981{
982    EphyrPortPriv *port_priv = a_port_priv;
983    Bool is_ok = FALSE;
984    int result = BadImplementation, image_size = 0;
985
986    EPHYR_RETURN_VAL_IF_FAIL(a_info && a_info->pScreen, BadValue);
987    EPHYR_RETURN_VAL_IF_FAIL(a_drawable, BadValue);
988
989    EPHYR_LOG("enter\n");
990
991    if (!ephyrHostXVPutImage(a_info, port_priv,
992                             a_id,
993                             a_drw_x, a_drw_y, a_drw_w, a_drw_h,
994                             a_src_x, a_src_y, a_src_w, a_src_h,
995                             a_width, a_height, a_buf,
996                             RegionRects(a_clipping_region),
997                             RegionNumRects(a_clipping_region))) {
998        EPHYR_LOG_ERROR("EphyrHostXVPutImage() failed\n");
999        goto out;
1000    }
1001
1002    /*
1003     * Now save the image so that we can resend it to host it
1004     * later, in ReputImage.
1005     */
1006    if (!ephyrXVPrivGetImageBufSize(port_priv->port_number,
1007                                    a_id, a_width, a_height, &image_size)) {
1008        EPHYR_LOG_ERROR("failed to get image size\n");
1009        /*this is a minor error so we won't get bail out abruptly */
1010        is_ok = FALSE;
1011    }
1012    else {
1013        is_ok = TRUE;
1014    }
1015    if (is_ok) {
1016        if (!ephyrXVPrivSaveImageToPortPriv(port_priv, a_buf, image_size)) {
1017            is_ok = FALSE;
1018        }
1019        else {
1020            port_priv->image_id = a_id;
1021            port_priv->drw_x = a_drw_x;
1022            port_priv->drw_y = a_drw_y;
1023            port_priv->drw_w = a_drw_w;
1024            port_priv->drw_h = a_drw_h;
1025            port_priv->src_x = a_src_x;
1026            port_priv->src_y = a_src_y;
1027            port_priv->src_w = a_src_w;
1028            port_priv->src_h = a_src_h;
1029            port_priv->image_width = a_width;
1030            port_priv->image_height = a_height;
1031        }
1032    }
1033    if (!is_ok) {
1034        if (port_priv->image_buf) {
1035            free(port_priv->image_buf);
1036            port_priv->image_buf = NULL;
1037            port_priv->image_buf_size = 0;
1038        }
1039    }
1040
1041    result = Success;
1042
1043 out:
1044    EPHYR_LOG("leave\n");
1045    return result;
1046}
1047
1048static int
1049ephyrReputImage(KdScreenInfo * a_info,
1050                DrawablePtr a_drawable,
1051                short a_drw_x,
1052                short a_drw_y, RegionPtr a_clipping_region, void *a_port_priv)
1053{
1054    EphyrPortPriv *port_priv = a_port_priv;
1055    int result = BadImplementation;
1056
1057    EPHYR_RETURN_VAL_IF_FAIL(a_info->pScreen, FALSE);
1058    EPHYR_RETURN_VAL_IF_FAIL(a_drawable && port_priv, BadValue);
1059
1060    EPHYR_LOG("enter\n");
1061
1062    if (!port_priv->image_buf_size || !port_priv->image_buf) {
1063        EPHYR_LOG_ERROR("has null image buf in cache\n");
1064        goto out;
1065    }
1066    if (!ephyrHostXVPutImage(a_info,
1067                             port_priv,
1068                             port_priv->image_id,
1069                             a_drw_x, a_drw_y,
1070                             port_priv->drw_w, port_priv->drw_h,
1071                             port_priv->src_x, port_priv->src_y,
1072                             port_priv->src_w, port_priv->src_h,
1073                             port_priv->image_width, port_priv->image_height,
1074                             port_priv->image_buf,
1075                             RegionRects(a_clipping_region),
1076                             RegionNumRects(a_clipping_region))) {
1077        EPHYR_LOG_ERROR("ephyrHostXVPutImage() failed\n");
1078        goto out;
1079    }
1080
1081    result = Success;
1082
1083 out:
1084    EPHYR_LOG("leave\n");
1085    return result;
1086}
1087
1088static int
1089ephyrPutVideo(KdScreenInfo * a_info,
1090              DrawablePtr a_drawable,
1091              short a_vid_x, short a_vid_y,
1092              short a_drw_x, short a_drw_y,
1093              short a_vid_w, short a_vid_h,
1094              short a_drw_w, short a_drw_h,
1095              RegionPtr a_clipping_region, void *a_port_priv)
1096{
1097    EphyrScrPriv *scrpriv = a_info->driver;
1098    xcb_connection_t *conn = hostx_get_xcbconn();
1099    xcb_gcontext_t gc;
1100    EphyrPortPriv *port_priv = a_port_priv;
1101
1102    EPHYR_RETURN_VAL_IF_FAIL(a_info->pScreen, BadValue);
1103    EPHYR_RETURN_VAL_IF_FAIL(a_drawable && port_priv, BadValue);
1104
1105    EPHYR_LOG("enter\n");
1106
1107    gc = xcb_generate_id(conn);
1108    xcb_create_gc(conn, gc, scrpriv->win, 0, NULL);
1109    xcb_xv_put_video(conn, port_priv->port_number,
1110                     scrpriv->win, gc,
1111                     a_vid_x, a_vid_y, a_vid_w, a_vid_h,
1112                     a_drw_x, a_drw_y, a_drw_w, a_drw_h);
1113    xcb_free_gc(conn, gc);
1114
1115    EPHYR_LOG("leave\n");
1116    return Success;
1117}
1118
1119static int
1120ephyrGetVideo(KdScreenInfo * a_info,
1121              DrawablePtr a_drawable,
1122              short a_vid_x, short a_vid_y,
1123              short a_drw_x, short a_drw_y,
1124              short a_vid_w, short a_vid_h,
1125              short a_drw_w, short a_drw_h,
1126              RegionPtr a_clipping_region, void *a_port_priv)
1127{
1128    EphyrScrPriv *scrpriv = a_info->driver;
1129    xcb_connection_t *conn = hostx_get_xcbconn();
1130    xcb_gcontext_t gc;
1131    EphyrPortPriv *port_priv = a_port_priv;
1132
1133    EPHYR_RETURN_VAL_IF_FAIL(a_info && a_info->pScreen, BadValue);
1134    EPHYR_RETURN_VAL_IF_FAIL(a_drawable && port_priv, BadValue);
1135
1136    EPHYR_LOG("enter\n");
1137
1138    gc = xcb_generate_id(conn);
1139    xcb_create_gc(conn, gc, scrpriv->win, 0, NULL);
1140    xcb_xv_get_video(conn, port_priv->port_number,
1141                     scrpriv->win, gc,
1142                     a_vid_x, a_vid_y, a_vid_w, a_vid_h,
1143                     a_drw_x, a_drw_y, a_drw_w, a_drw_h);
1144
1145    xcb_free_gc(conn, gc);
1146
1147    EPHYR_LOG("leave\n");
1148    return Success;
1149}
1150
1151static int
1152ephyrPutStill(KdScreenInfo * a_info,
1153              DrawablePtr a_drawable,
1154              short a_vid_x, short a_vid_y,
1155              short a_drw_x, short a_drw_y,
1156              short a_vid_w, short a_vid_h,
1157              short a_drw_w, short a_drw_h,
1158              RegionPtr a_clipping_region, void *a_port_priv)
1159{
1160    EphyrScrPriv *scrpriv = a_info->driver;
1161    xcb_connection_t *conn = hostx_get_xcbconn();
1162    xcb_gcontext_t gc;
1163    EphyrPortPriv *port_priv = a_port_priv;
1164
1165    EPHYR_RETURN_VAL_IF_FAIL(a_info && a_info->pScreen, BadValue);
1166    EPHYR_RETURN_VAL_IF_FAIL(a_drawable && port_priv, BadValue);
1167
1168    EPHYR_LOG("enter\n");
1169
1170    gc = xcb_generate_id(conn);
1171    xcb_create_gc(conn, gc, scrpriv->win, 0, NULL);
1172    xcb_xv_put_still(conn, port_priv->port_number,
1173                     scrpriv->win, gc,
1174                     a_vid_x, a_vid_y, a_vid_w, a_vid_h,
1175                     a_drw_x, a_drw_y, a_drw_w, a_drw_h);
1176    xcb_free_gc(conn, gc);
1177
1178    EPHYR_LOG("leave\n");
1179    return Success;
1180}
1181
1182static int
1183ephyrGetStill(KdScreenInfo * a_info,
1184              DrawablePtr a_drawable,
1185              short a_vid_x, short a_vid_y,
1186              short a_drw_x, short a_drw_y,
1187              short a_vid_w, short a_vid_h,
1188              short a_drw_w, short a_drw_h,
1189              RegionPtr a_clipping_region, void *a_port_priv)
1190{
1191    EphyrScrPriv *scrpriv = a_info->driver;
1192    xcb_connection_t *conn = hostx_get_xcbconn();
1193    xcb_gcontext_t gc;
1194    EphyrPortPriv *port_priv = a_port_priv;
1195
1196    EPHYR_RETURN_VAL_IF_FAIL(a_info && a_info->pScreen, BadValue);
1197    EPHYR_RETURN_VAL_IF_FAIL(a_drawable && port_priv, BadValue);
1198
1199    EPHYR_LOG("enter\n");
1200
1201    gc = xcb_generate_id(conn);
1202    xcb_create_gc(conn, gc, scrpriv->win, 0, NULL);
1203    xcb_xv_get_still(conn, port_priv->port_number,
1204                     scrpriv->win, gc,
1205                     a_vid_x, a_vid_y, a_vid_w, a_vid_h,
1206                     a_drw_x, a_drw_y, a_drw_w, a_drw_h);
1207    xcb_free_gc(conn, gc);
1208
1209    EPHYR_LOG("leave\n");
1210    return Success;
1211}
1212
1213static int
1214ephyrQueryImageAttributes(KdScreenInfo * a_info,
1215                          int a_id,
1216                          unsigned short *a_w,
1217                          unsigned short *a_h, int *a_pitches, int *a_offsets)
1218{
1219    xcb_connection_t *conn = hostx_get_xcbconn();
1220    xcb_xv_query_image_attributes_cookie_t cookie;
1221    xcb_xv_query_image_attributes_reply_t *reply;
1222    int image_size = 0;
1223
1224    EPHYR_RETURN_VAL_IF_FAIL(a_w && a_h, FALSE);
1225
1226    EPHYR_LOG("enter: dim (%dx%d), pitches: %p, offsets: %p\n",
1227              *a_w, *a_h, a_pitches, a_offsets);
1228
1229    cookie = xcb_xv_query_image_attributes(conn,
1230                                           s_base_port_id, a_id,
1231                                           *a_w, *a_h);
1232    reply = xcb_xv_query_image_attributes_reply(conn, cookie, NULL);
1233    if (!reply)
1234        goto out;
1235
1236    *a_w = reply->width;
1237    *a_h = reply->height;
1238    if (a_pitches && a_offsets) {
1239        memcpy(a_pitches, xcb_xv_query_image_attributes_pitches(reply),
1240               reply->num_planes << 2);
1241        memcpy(a_offsets, xcb_xv_query_image_attributes_offsets(reply),
1242               reply->num_planes << 2);
1243    }
1244    image_size = reply->data_size;
1245
1246    free(reply);
1247
1248    EPHYR_LOG("image size: %d, dim (%dx%d)\n", image_size, *a_w, *a_h);
1249
1250 out:
1251    EPHYR_LOG("leave\n");
1252    return image_size;
1253}
1254