Home | History | Annotate | Download | only in player_x11
      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