summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-25 00:03:34 +0000
committerhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-25 00:03:34 +0000
commit5ce0b16b97321df662c68124a26d5f7ad6afadc0 (patch)
treebc403a96a3a74467b7ee10d68e17eb082680b2c3 /media
parent96449d2cf2f37ffcab09ad8e15d9d6f3534271cb (diff)
downloadchromium_src-5ce0b16b97321df662c68124a26d5f7ad6afadc0.zip
chromium_src-5ce0b16b97321df662c68124a26d5f7ad6afadc0.tar.gz
chromium_src-5ce0b16b97321df662c68124a26d5f7ad6afadc0.tar.bz2
X11 media player for video playback
A X11 media player application that uses the video stack pipeline. It uses XRender when possible, this will allow the XRender extension to convert RGBA8888 to the target depth and bits per pixel on the X Server. Review URL: http://codereview.chromium.org/399113 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@33007 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/media.gyp24
-rw-r--r--media/player/player_x11.cc154
-rw-r--r--media/player/x11_video_renderer.cc237
-rw-r--r--media/player/x11_video_renderer.h70
4 files changed, 485 insertions, 0 deletions
diff --git a/media/media.gyp b/media/media.gyp
index 3bbc7df..4b5c13f 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -277,6 +277,30 @@
},
],
}],
+ ['OS=="linux"', {
+ 'targets': [
+ {
+ 'target_name': 'media_player',
+ 'type': 'executable',
+ 'dependencies': [
+ 'media',
+ '../base/base.gyp:base',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '-lX11',
+ '-lXrender',
+ '-lXext',
+ ],
+ },
+ 'sources': [
+ 'player/player_x11.cc',
+ 'player/x11_video_renderer.cc',
+ 'player/x11_video_renderer.h',
+ ],
+ },
+ ],
+ }],
],
}
diff --git a/media/player/player_x11.cc b/media/player/player_x11.cc
new file mode 100644
index 0000000..24d7430
--- /dev/null
+++ b/media/player/player_x11.cc
@@ -0,0 +1,154 @@
+// 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 <iostream>
+#include <X11/Xlib.h>
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/scoped_ptr.h"
+#include "base/thread.h"
+#include "media/base/media.h"
+#include "media/base/pipeline_impl.h"
+#include "media/filters/audio_renderer_impl.h"
+#include "media/filters/ffmpeg_audio_decoder.h"
+#include "media/filters/ffmpeg_demuxer.h"
+#include "media/filters/ffmpeg_video_decoder.h"
+#include "media/filters/file_data_source.h"
+#include "media/filters/null_audio_renderer.h"
+#include "media/player/x11_video_renderer.h"
+
+Display* g_display;
+Window g_window;
+
+// Initialize X11. Returns true if successful. This method creates the X11
+// window. Further initialization is done in X11VideoRenderer.
+bool InitX11() {
+ g_display = XOpenDisplay(NULL);
+ if (!g_display) {
+ std::cout << "Error - cannot open display" << std::endl;
+ return false;
+ }
+
+ // Get properties of the screen.
+ int screen = DefaultScreen(g_display);
+ int root_window = RootWindow(g_display, screen);
+
+ // Creates the window.
+ g_window = XCreateSimpleWindow(g_display, root_window, 1, 1, 100, 50, 0,
+ BlackPixel(g_display, screen),
+ BlackPixel(g_display, screen));
+ XStoreName(g_display, g_window, "X11 Media Player");
+
+ XSelectInput(g_display, g_window, ExposureMask | ButtonPressMask);
+ XMapWindow(g_display, g_window);
+ return true;
+}
+
+bool InitPipeline(MessageLoop* message_loop,
+ const char* filename, bool enable_audio,
+ scoped_refptr<media::PipelineImpl>* pipeline) {
+ // Load media libraries.
+ if (!media::InitializeMediaLibrary(FilePath())) {
+ std::cout << "Unable to initialize the media library." << std::endl;
+ return false;
+ }
+
+ // Create our filter factories.
+ scoped_refptr<media::FilterFactoryCollection> factories =
+ new media::FilterFactoryCollection();
+ factories->AddFactory(media::FileDataSource::CreateFactory());
+ factories->AddFactory(media::FFmpegAudioDecoder::CreateFactory());
+ factories->AddFactory(media::FFmpegDemuxer::CreateFilterFactory());
+ factories->AddFactory(media::FFmpegVideoDecoder::CreateFactory());
+ factories->AddFactory(X11VideoRenderer::CreateFactory(g_display, g_window));
+
+ if (enable_audio) {
+ factories->AddFactory(media::AudioRendererImpl::CreateFilterFactory());
+ } else {
+ factories->AddFactory(media::NullAudioRenderer::CreateFilterFactory());
+ }
+
+ // Creates the pipeline and start it.
+ *pipeline = new media::PipelineImpl(message_loop);
+ (*pipeline)->Start(factories, filename, NULL);
+
+ // Wait until the pipeline is fully initialized.
+ while (true) {
+ PlatformThread::Sleep(100);
+ if ((*pipeline)->IsInitialized())
+ break;
+ if ((*pipeline)->GetError() != media::PIPELINE_OK) {
+ (*pipeline)->Stop(NULL);
+ return false;
+ }
+ }
+
+ // And starts the playback.
+ (*pipeline)->SetPlaybackRate(1.0f);
+ return true;
+}
+
+int main(int argc, char** argv) {
+ // Read arguments.
+ if (argc == 1) {
+ std::cout << "Usage: media_player --file=FILE [--audio]"
+ " [--alsa-device=DEVICE]" << std::endl;
+ return 1;
+ }
+
+ // Read command line.
+ CommandLine::Init(argc, argv);
+ std::string filename =
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII("file");
+ bool enable_audio = CommandLine::ForCurrentProcess()->HasSwitch("audio");
+
+ // Initialize X11.
+ if (!InitX11())
+ return 1;
+
+ // Initialize the pipeline thread and the pipeline.
+ base::AtExitManager at_exit;
+ scoped_ptr<base::Thread> thread;
+ scoped_refptr<media::PipelineImpl> pipeline;
+ thread.reset(new base::Thread("PipelineThread"));
+ thread->Start();
+ if (InitPipeline(thread->message_loop(), filename.c_str(),
+ enable_audio, &pipeline)) {
+ // Main loop of the application.
+ while (true) {
+ if (XPending(g_display)) {
+ XEvent e;
+ XNextEvent(g_display, &e);
+ if (e.type == Expose) {
+ // Tell the renderer to paint.
+ DCHECK(X11VideoRenderer::instance());
+ X11VideoRenderer::instance()->Paint();
+ } else if (e.type == ButtonPress) {
+ // Stop the playback.
+ std::cout << "Stopping..." << std::endl;
+ pipeline->Stop(NULL);
+ break;
+ }
+ } else {
+ // If there's no event in the queue, make an expose event.
+ XEvent event;
+ event.type = Expose;
+ XSendEvent(g_display, g_window, true, ExposureMask, &event);
+
+ // TODO(hclam): It is rather arbitrary to sleep for 10ms and wait
+ // for the next event. We should submit an expose event when
+ // a frame is available but not firing an expose event every 10ms.
+ usleep(10000);
+ }
+ }
+ }
+
+ // Cleanup tasks.
+ thread->Stop();
+ XDestroyWindow(g_display, g_window);
+ XCloseDisplay(g_display);
+ return 0;
+}
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);
+}
diff --git a/media/player/x11_video_renderer.h b/media/player/x11_video_renderer.h
new file mode 100644
index 0000000..66f0137
--- /dev/null
+++ b/media/player/x11_video_renderer.h
@@ -0,0 +1,70 @@
+// 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.
+
+#ifndef MEDIA_PLAYER_X11_VIDEO_RENDERER_H_
+#define MEDIA_PLAYER_X11_VIDEO_RENDERER_H_
+
+#include <X11/Xlib.h>
+
+#include "base/lock.h"
+#include "base/scoped_ptr.h"
+#include "media/base/factory.h"
+#include "media/filters/video_renderer_base.h"
+
+class X11VideoRenderer : public media::VideoRendererBase {
+ public:
+ static media::FilterFactory* CreateFactory(Display* display,
+ Window window) {
+ return new media::FilterFactoryImpl2<
+ X11VideoRenderer, Display*, Window>(display, window);
+ }
+
+ X11VideoRenderer(Display* display, Window window);
+
+ // This method is called to paint the current video frame to the assigned
+ // window.
+ void Paint();
+
+ // media::FilterFactoryImpl2 Implementation.
+ static bool IsMediaFormatSupported(const media::MediaFormat& media_format);
+
+ // Returns the instance of this class.
+ static X11VideoRenderer* instance() { return instance_; }
+
+ protected:
+ // VideoRendererBase implementation.
+ virtual bool OnInitialize(media::VideoDecoder* decoder);
+ virtual void OnStop();
+ virtual void OnFrameAvailable();
+
+ private:
+ // Only allow to be deleted by reference counting.
+ friend class scoped_refptr<X11VideoRenderer>;
+ virtual ~X11VideoRenderer();
+
+ int width_;
+ int height_;
+
+ Display* display_;
+ Window window_;
+
+ // Image in heap that contains the RGBA data of the video frame.
+ XImage* image_;
+
+ // Protects |new_frame_|.
+ Lock lock_;
+ bool new_frame_;
+
+ // Picture represents the paint target. This is a picture located
+ // in the server.
+ unsigned long picture_;
+
+ bool use_render_;
+
+ static X11VideoRenderer* instance_;
+
+ DISALLOW_COPY_AND_ASSIGN(X11VideoRenderer);
+};
+
+#endif // MEDIA_PLAYER_X11_VIDEO_RENDERER_H_