diff options
Diffstat (limited to 'media')
-rw-r--r-- | media/media.gyp | 24 | ||||
-rw-r--r-- | media/player/player_x11.cc | 154 | ||||
-rw-r--r-- | media/player/x11_video_renderer.cc | 237 | ||||
-rw-r--r-- | media/player/x11_video_renderer.h | 70 |
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_ |