diff options
Diffstat (limited to 'media/player/x11_video_renderer.cc')
-rw-r--r-- | media/player/x11_video_renderer.cc | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/media/player/x11_video_renderer.cc b/media/player/x11_video_renderer.cc new file mode 100644 index 0000000..4780a45 --- /dev/null +++ b/media/player/x11_video_renderer.cc @@ -0,0 +1,237 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/player/x11_video_renderer.h" + +#include <X11/Xutil.h> +#include <X11/extensions/Xrender.h> +#include <X11/extensions/Xcomposite.h> + +#include "media/base/buffers.h" +#include "media/base/yuv_convert.h" + +X11VideoRenderer* X11VideoRenderer::instance_ = NULL; + +// Returns the picture format for ARGB. +// This method is originally from chrome/common/x11_util.cc. +static XRenderPictFormat* GetRenderARGB32Format(Display* dpy) { + static XRenderPictFormat* pictformat = NULL; + if (pictformat) + return pictformat; + + // First look for a 32-bit format which ignores the alpha value. + XRenderPictFormat templ; + templ.depth = 32; + templ.type = PictTypeDirect; + templ.direct.red = 16; + templ.direct.green = 8; + templ.direct.blue = 0; + templ.direct.redMask = 0xff; + templ.direct.greenMask = 0xff; + templ.direct.blueMask = 0xff; + templ.direct.alphaMask = 0; + + static const unsigned long kMask = + PictFormatType | PictFormatDepth | + PictFormatRed | PictFormatRedMask | + PictFormatGreen | PictFormatGreenMask | + PictFormatBlue | PictFormatBlueMask | + PictFormatAlphaMask; + + pictformat = XRenderFindFormat(dpy, kMask, &templ, 0 /* first result */); + + if (!pictformat) { + // Not all X servers support xRGB32 formats. However, the XRENDER spec + // says that they must support an ARGB32 format, so we can always return + // that. + pictformat = XRenderFindStandardFormat(dpy, PictStandardARGB32); + CHECK(pictformat) << "XRENDER ARGB32 not supported."; + } + + return pictformat; +} + +X11VideoRenderer::X11VideoRenderer(Display* display, Window window) + : display_(display), + window_(window), + image_(NULL), + new_frame_(false), + picture_(0), + use_render_(true) { + // Save the instance of the video renderer. + CHECK(!instance_); + instance_ = this; +} + +X11VideoRenderer::~X11VideoRenderer() { + CHECK(instance_); + instance_ = NULL; +} + +// static +bool X11VideoRenderer::IsMediaFormatSupported( + const media::MediaFormat& media_format) { + int width = 0; + int height = 0; + return ParseMediaFormat(media_format, &width, &height); +} + +void X11VideoRenderer::OnStop() { + XDestroyImage(image_); + if (use_render_) { + XRenderFreePicture(display_, picture_); + } +} + +bool X11VideoRenderer::OnInitialize(media::VideoDecoder* decoder) { + if (!ParseMediaFormat(decoder->media_format(), &width_, &height_)) + return false; + + // Resize the window to fit that of the video. + XResizeWindow(display_, window_, width_, height_); + + // Testing XRender support. We'll use the very basic of XRender + // so if it presents it is already good enough. We don't need + // to check its version. + int dummy; + use_render_ = XRenderQueryExtension(display_, &dummy, &dummy); + + if (use_render_) { + // If we are using XRender, we'll create a picture representing the + // window. + XWindowAttributes attr; + XGetWindowAttributes(display_, window_, &attr); + + XRenderPictFormat* pictformat = XRenderFindVisualFormat( + display_, + attr.visual); + CHECK(pictformat) << "XRENDER does not support default visual"; + + picture_ = XRenderCreatePicture(display_, window_, pictformat, 0, NULL); + CHECK(picture_) << "Backing picture not created"; + } + + // Initialize the XImage to store the output of YUV -> RGB conversion. + image_ = XCreateImage(display_, + DefaultVisual(display_, DefaultScreen(display_)), + DefaultDepth(display_, DefaultScreen(display_)), + ZPixmap, + 0, + static_cast<char*>(malloc(width_ * height_ * 4)), + width_, + height_, + 32, + width_ * 4); + DCHECK(image_); + return true; +} + +void X11VideoRenderer::OnFrameAvailable() { + AutoLock auto_lock(lock_); + new_frame_ = true; +} + +void X11VideoRenderer::Paint() { + // Use |new_frame_| to prevent overdraw since Paint() is called more + // often than needed. It is OK to lock only this flag and we don't + // want to lock the whole function because this method takes a long + // time to complete. + { + AutoLock auto_lock(lock_); + if (!new_frame_) + return; + new_frame_ = false; + } + + scoped_refptr<media::VideoFrame> video_frame; + GetCurrentFrame(&video_frame); + + if (!image_ || !video_frame) + return; + + // Convert YUV frame to RGB. + media::VideoSurface frame_in; + if (video_frame->Lock(&frame_in)) { + DCHECK(frame_in.format == media::VideoSurface::YV12 || + frame_in.format == media::VideoSurface::YV16); + DCHECK(frame_in.strides[media::VideoSurface::kUPlane] == + frame_in.strides[media::VideoSurface::kVPlane]); + DCHECK(frame_in.planes == media::VideoSurface::kNumYUVPlanes); + DCHECK(image_->data); + + media::YUVType yuv_type = (frame_in.format == media::VideoSurface::YV12) ? + media::YV12 : media::YV16; + media::ConvertYUVToRGB32(frame_in.data[media::VideoSurface::kYPlane], + frame_in.data[media::VideoSurface::kUPlane], + frame_in.data[media::VideoSurface::kVPlane], + (uint8*)image_->data, + frame_in.width, + frame_in.height, + frame_in.strides[media::VideoSurface::kYPlane], + frame_in.strides[media::VideoSurface::kUPlane], + image_->bytes_per_line, + yuv_type); + video_frame->Unlock(); + } else { + NOTREACHED(); + } + + if (use_render_) { + // If XRender is used, we'll upload the image to a pixmap. And then + // creats a picture from the pixmap and composite the picture over + // the picture represending the window. + + // Creates a XImage. + XImage image; + memset(&image, 0, sizeof(image)); + image.width = width_; + image.height = height_; + image.depth = 32; + image.bits_per_pixel = 32; + image.format = ZPixmap; + image.byte_order = LSBFirst; + image.bitmap_unit = 8; + image.bitmap_bit_order = LSBFirst; + image.bytes_per_line = image_->bytes_per_line; + image.red_mask = 0xff; + image.green_mask = 0xff00; + image.blue_mask = 0xff0000; + image.data = image_->data; + + // Creates a pixmap and uploads from the XImage. + unsigned long pixmap = XCreatePixmap(display_, + window_, + width_, + height_, + 32); + GC gc = XCreateGC(display_, pixmap, 0, NULL); + XPutImage(display_, pixmap, gc, &image, + 0, 0, 0, 0, + width_, height_); + XFreeGC(display_, gc); + + // Creates the picture representing the pixmap. + unsigned long picture = XRenderCreatePicture( + display_, pixmap, GetRenderARGB32Format(display_), 0, NULL); + + // Composite the picture over the picture representing the window. + XRenderComposite(display_, PictOpSrc, picture, 0, + picture_, 0, 0, 0, 0, 0, 0, + width_, height_); + + XRenderFreePicture(display_, picture); + XFreePixmap(display_, pixmap); + return; + } + + // If XRender is not used, simply put the image to the server. + // This will have a tearing effect but this is OK. + // TODO(hclam): Upload the image to a pixmap and do XCopyArea() + // to the window. + GC gc = XCreateGC(display_, window_, 0, NULL); + XPutImage(display_, window_, gc, image_, + 0, 0, 0, 0, width_, height_); + XFlush(display_); + XFreeGC(display_, gc); +} |