summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
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_