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. 18 static 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. 34 static 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 71 X11VideoRenderer::X11VideoRenderer(Display* display, Window window) 72 : display_(display), 73 window_(window), 74 image_(NULL), 75 picture_(0), 76 use_render_(false) { 77 } 78 79 X11VideoRenderer::~X11VideoRenderer() { 80 if (image_) 81 XDestroyImage(image_); 82 if (use_render_) 83 XRenderFreePicture(display_, picture_); 84 } 85 86 void 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 184 void 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