1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "media/tools/player_x11/x11_video_renderer.h"
6
7#include <dlfcn.h>
8#include <X11/Xutil.h>
9#include <X11/extensions/Xrender.h>
10#include <X11/extensions/Xcomposite.h>
11
12#include "base/bind.h"
13#include "base/message_loop/message_loop.h"
14#include "media/base/video_frame.h"
15#include "media/base/yuv_convert.h"
16
17// Creates a 32-bit XImage.
18static XImage* CreateImage(Display* display, int width, int height) {
19  VLOG(0) << "Allocating XImage " << width << "x" << height;
20  return  XCreateImage(display,
21                       DefaultVisual(display, DefaultScreen(display)),
22                       DefaultDepth(display, DefaultScreen(display)),
23                       ZPixmap,
24                       0,
25                       static_cast<char*>(malloc(width * height * 4)),
26                       width,
27                       height,
28                       32,
29                       width * 4);
30}
31
32// Returns the picture format for ARGB.
33// This method is originally from chrome/common/x11_util.cc.
34static XRenderPictFormat* GetRenderARGB32Format(Display* dpy) {
35  static XRenderPictFormat* pictformat = NULL;
36  if (pictformat)
37    return pictformat;
38
39  // First look for a 32-bit format which ignores the alpha value.
40  XRenderPictFormat templ;
41  templ.depth = 32;
42  templ.type = PictTypeDirect;
43  templ.direct.red = 16;
44  templ.direct.green = 8;
45  templ.direct.blue = 0;
46  templ.direct.redMask = 0xff;
47  templ.direct.greenMask = 0xff;
48  templ.direct.blueMask = 0xff;
49  templ.direct.alphaMask = 0;
50
51  static const unsigned long kMask =
52      PictFormatType | PictFormatDepth |
53      PictFormatRed | PictFormatRedMask |
54      PictFormatGreen | PictFormatGreenMask |
55      PictFormatBlue | PictFormatBlueMask |
56      PictFormatAlphaMask;
57
58  pictformat = XRenderFindFormat(dpy, kMask, &templ, 0 /* first result */);
59
60  if (!pictformat) {
61    // Not all X servers support xRGB32 formats. However, the XRender spec
62    // says that they must support an ARGB32 format, so we can always return
63    // that.
64    pictformat = XRenderFindStandardFormat(dpy, PictStandardARGB32);
65    CHECK(pictformat) << "XRender ARGB32 not supported.";
66  }
67
68  return pictformat;
69}
70
71X11VideoRenderer::X11VideoRenderer(Display* display, Window window)
72    : display_(display),
73      window_(window),
74      image_(NULL),
75      picture_(0),
76      use_render_(false) {
77}
78
79X11VideoRenderer::~X11VideoRenderer() {
80  if (image_)
81    XDestroyImage(image_);
82  if (use_render_)
83    XRenderFreePicture(display_, picture_);
84}
85
86void X11VideoRenderer::Paint(
87    const scoped_refptr<media::VideoFrame>& video_frame) {
88  if (!image_)
89    Initialize(video_frame->coded_size(), video_frame->visible_rect());
90
91  const int coded_width = video_frame->coded_size().width();
92  const int coded_height = video_frame->coded_size().height();
93  const int visible_width = video_frame->visible_rect().width();
94  const int visible_height = video_frame->visible_rect().height();
95
96  // Check if we need to reallocate our XImage.
97  if (image_->width != coded_width || image_->height != coded_height) {
98    XDestroyImage(image_);
99    image_ = CreateImage(display_, coded_width, coded_height);
100  }
101
102  // Convert YUV frame to RGB.
103  DCHECK(video_frame->format() == media::VideoFrame::YV12 ||
104         video_frame->format() == media::VideoFrame::I420 ||
105         video_frame->format() == media::VideoFrame::YV16);
106  DCHECK(video_frame->stride(media::VideoFrame::kUPlane) ==
107         video_frame->stride(media::VideoFrame::kVPlane));
108
109  DCHECK(image_->data);
110  media::YUVType yuv_type = (video_frame->format() == media::VideoFrame::YV12 ||
111                             video_frame->format() == media::VideoFrame::I420)
112                                ? media::YV12
113                                : media::YV16;
114  media::ConvertYUVToRGB32(video_frame->data(media::VideoFrame::kYPlane),
115                           video_frame->data(media::VideoFrame::kUPlane),
116                           video_frame->data(media::VideoFrame::kVPlane),
117                           (uint8*)image_->data, coded_width, coded_height,
118                           video_frame->stride(media::VideoFrame::kYPlane),
119                           video_frame->stride(media::VideoFrame::kUPlane),
120                           image_->bytes_per_line,
121                           yuv_type);
122
123  if (use_render_) {
124    // If XRender is used, we'll upload the image to a pixmap. And then
125    // creats a picture from the pixmap and composite the picture over
126    // the picture represending the window.
127
128    // Creates a XImage.
129    XImage image;
130    memset(&image, 0, sizeof(image));
131    image.width = coded_width;
132    image.height = coded_height;
133    image.depth = 32;
134    image.bits_per_pixel = 32;
135    image.format = ZPixmap;
136    image.byte_order = LSBFirst;
137    image.bitmap_unit = 8;
138    image.bitmap_bit_order = LSBFirst;
139    image.bytes_per_line = image_->bytes_per_line;
140    image.red_mask = 0xff;
141    image.green_mask = 0xff00;
142    image.blue_mask = 0xff0000;
143    image.data = image_->data;
144
145    // Creates a pixmap and uploads from the XImage.
146    unsigned long pixmap = XCreatePixmap(display_, window_,
147                                         visible_width, visible_height,
148                                         32);
149    GC gc = XCreateGC(display_, pixmap, 0, NULL);
150    XPutImage(display_, pixmap, gc, &image,
151              video_frame->visible_rect().x(),
152              video_frame->visible_rect().y(),
153              0, 0,
154              visible_width, visible_height);
155    XFreeGC(display_, gc);
156
157    // Creates the picture representing the pixmap.
158    unsigned long picture = XRenderCreatePicture(
159        display_, pixmap, GetRenderARGB32Format(display_), 0, NULL);
160
161    // Composite the picture over the picture representing the window.
162    XRenderComposite(display_, PictOpSrc, picture, 0,
163                     picture_, 0, 0, 0, 0, 0, 0,
164                     visible_width, visible_height);
165
166    XRenderFreePicture(display_, picture);
167    XFreePixmap(display_, pixmap);
168    return;
169  }
170
171  // If XRender is not used, simply put the image to the server.
172  // This will have a tearing effect but this is OK.
173  // TODO(hclam): Upload the image to a pixmap and do XCopyArea()
174  // to the window.
175  GC gc = XCreateGC(display_, window_, 0, NULL);
176  XPutImage(display_, window_, gc, image_,
177            video_frame->visible_rect().x(),
178            video_frame->visible_rect().y(),
179            0, 0, visible_width, visible_height);
180  XFlush(display_);
181  XFreeGC(display_, gc);
182}
183
184void X11VideoRenderer::Initialize(gfx::Size coded_size,
185                                  gfx::Rect visible_rect) {
186  CHECK(!image_);
187  VLOG(0) << "Initializing X11 Renderer...";
188
189  // Resize the window to fit that of the video.
190  XResizeWindow(display_, window_, visible_rect.width(), visible_rect.height());
191  image_ = CreateImage(display_, coded_size.width(), coded_size.height());
192
193  // Testing XRender support. We'll use the very basic of XRender
194  // so if it presents it is already good enough. We don't need
195  // to check its version.
196  int dummy;
197  use_render_ = XRenderQueryExtension(display_, &dummy, &dummy);
198
199  if (use_render_) {
200    VLOG(0) << "Using XRender extension.";
201
202    // If we are using XRender, we'll create a picture representing the
203    // window.
204    XWindowAttributes attr;
205    XGetWindowAttributes(display_, window_, &attr);
206
207    XRenderPictFormat* pictformat = XRenderFindVisualFormat(
208        display_,
209        attr.visual);
210    CHECK(pictformat) << "XRender does not support default visual";
211
212    picture_ = XRenderCreatePicture(display_, window_, pictformat, 0, NULL);
213    CHECK(picture_) << "Backing picture not created";
214  }
215}
216