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 <signal.h>
      6 
      7 #include <iostream>  // NOLINT
      8 
      9 #include "base/at_exit.h"
     10 #include "base/bind.h"
     11 #include "base/command_line.h"
     12 #include "base/files/file_path.h"
     13 #include "base/memory/scoped_ptr.h"
     14 #include "base/memory/scoped_vector.h"
     15 #include "base/threading/platform_thread.h"
     16 #include "base/threading/thread.h"
     17 #include "media/audio/audio_manager.h"
     18 #include "media/audio/null_audio_sink.h"
     19 #include "media/base/audio_hardware_config.h"
     20 #include "media/base/bind_to_current_loop.h"
     21 #include "media/base/decryptor.h"
     22 #include "media/base/media.h"
     23 #include "media/base/media_log.h"
     24 #include "media/base/media_switches.h"
     25 #include "media/base/pipeline.h"
     26 #include "media/base/text_track.h"
     27 #include "media/base/text_track_config.h"
     28 #include "media/base/video_frame.h"
     29 #include "media/filters/audio_renderer_impl.h"
     30 #include "media/filters/ffmpeg_audio_decoder.h"
     31 #include "media/filters/ffmpeg_demuxer.h"
     32 #include "media/filters/ffmpeg_video_decoder.h"
     33 #include "media/filters/file_data_source.h"
     34 #include "media/filters/renderer_impl.h"
     35 #include "media/filters/video_renderer_impl.h"
     36 #include "media/tools/player_x11/data_source_logger.h"
     37 
     38 // Include X11 headers here because X11/Xlib.h #define's Status
     39 // which causes compiler errors with Status enum declarations
     40 // in media::DemuxerStream & media::AudioDecoder.
     41 #include <X11/XKBlib.h>
     42 #include <X11/Xlib.h>
     43 
     44 #include "media/tools/player_x11/gl_video_renderer.h"
     45 #include "media/tools/player_x11/x11_video_renderer.h"
     46 
     47 static Display* g_display = NULL;
     48 static Window g_window = 0;
     49 static bool g_running = false;
     50 
     51 media::AudioManager* g_audio_manager = NULL;
     52 
     53 scoped_ptr<media::DataSource> CreateDataSource(const std::string& file_path) {
     54   media::FileDataSource* file_data_source = new media::FileDataSource();
     55   CHECK(file_data_source->Initialize(base::FilePath(file_path)));
     56 
     57   scoped_ptr<media::DataSource> data_source(file_data_source);
     58   return data_source.Pass();
     59 }
     60 
     61 // Initialize X11. Returns true if successful. This method creates the X11
     62 // window. Further initialization is done in X11VideoRenderer.
     63 bool InitX11() {
     64   g_display = XOpenDisplay(NULL);
     65   if (!g_display) {
     66     std::cout << "Error - cannot open display" << std::endl;
     67     return false;
     68   }
     69 
     70   // Get properties of the screen.
     71   int screen = DefaultScreen(g_display);
     72   int root_window = RootWindow(g_display, screen);
     73 
     74   // Creates the window.
     75   g_window = XCreateSimpleWindow(g_display, root_window, 1, 1, 100, 50, 0,
     76                                  BlackPixel(g_display, screen),
     77                                  BlackPixel(g_display, screen));
     78   XStoreName(g_display, g_window, "X11 Media Player");
     79 
     80   XSelectInput(g_display, g_window,
     81                ExposureMask | ButtonPressMask | KeyPressMask);
     82   XMapWindow(g_display, g_window);
     83   return true;
     84 }
     85 
     86 static void DoNothing() {}
     87 
     88 static void OnStatus(media::PipelineStatus status) {}
     89 
     90 static void OnMetadata(media::PipelineMetadata metadata) {}
     91 
     92 static void OnBufferingStateChanged(media::BufferingState buffering_state) {}
     93 
     94 static void OnAddTextTrack(const media::TextTrackConfig& config,
     95                            const media::AddTextTrackDoneCB& done_cb) {
     96 }
     97 
     98 static void NeedKey(const std::string& type,
     99                     const std::vector<uint8>& init_data) {
    100   std::cout << "File is encrypted." << std::endl;
    101 }
    102 
    103 static void SaveStatusAndSignal(base::WaitableEvent* event,
    104                                 media::PipelineStatus* status_out,
    105                                 media::PipelineStatus status) {
    106   *status_out = status;
    107   event->Signal();
    108 }
    109 
    110 // TODO(vrk): Re-enabled audio. (crbug.com/112159)
    111 void InitPipeline(
    112     media::Pipeline* pipeline,
    113     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
    114     media::Demuxer* demuxer,
    115     const media::VideoRendererImpl::PaintCB& paint_cb,
    116     bool /* enable_audio */) {
    117   ScopedVector<media::VideoDecoder> video_decoders;
    118   video_decoders.push_back(new media::FFmpegVideoDecoder(task_runner));
    119   scoped_ptr<media::VideoRenderer> video_renderer(
    120       new media::VideoRendererImpl(task_runner,
    121                                    video_decoders.Pass(),
    122                                    media::SetDecryptorReadyCB(),
    123                                    paint_cb,
    124                                    true,
    125                                    new media::MediaLog()));
    126 
    127   ScopedVector<media::AudioDecoder> audio_decoders;
    128   audio_decoders.push_back(new media::FFmpegAudioDecoder(task_runner,
    129                                                          media::LogCB()));
    130   media::AudioParameters out_params(
    131       media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
    132       media::CHANNEL_LAYOUT_STEREO,
    133       44100,
    134       16,
    135       512);
    136   media::AudioHardwareConfig hardware_config(out_params, out_params);
    137 
    138   scoped_ptr<media::AudioRenderer> audio_renderer(
    139       new media::AudioRendererImpl(task_runner,
    140                                    new media::NullAudioSink(task_runner),
    141                                    audio_decoders.Pass(),
    142                                    media::SetDecryptorReadyCB(),
    143                                    hardware_config,
    144                                    new media::MediaLog()));
    145 
    146   scoped_ptr<media::Renderer> renderer(new media::RendererImpl(
    147       task_runner, demuxer, audio_renderer.Pass(), video_renderer.Pass()));
    148 
    149   base::WaitableEvent event(true, false);
    150   media::PipelineStatus status;
    151 
    152   pipeline->Start(demuxer,
    153                   renderer.Pass(),
    154                   base::Bind(&DoNothing),
    155                   base::Bind(&OnStatus),
    156                   base::Bind(&SaveStatusAndSignal, &event, &status),
    157                   base::Bind(&OnMetadata),
    158                   base::Bind(&OnBufferingStateChanged),
    159                   base::Bind(&DoNothing),
    160                   base::Bind(&OnAddTextTrack));
    161 
    162   // Wait until the pipeline is fully initialized.
    163   event.Wait();
    164   CHECK_EQ(status, media::PIPELINE_OK) << "Pipeline initialization failed";
    165 
    166   // And start the playback.
    167   pipeline->SetPlaybackRate(1.0f);
    168 }
    169 
    170 void TerminateHandler(int signal) {
    171   g_running = false;
    172 }
    173 
    174 void PeriodicalUpdate(
    175     media::Pipeline* pipeline,
    176     base::MessageLoop* message_loop) {
    177   if (!g_running) {
    178     // interrupt signal was received during last time period.
    179     // Quit message_loop only when pipeline is fully stopped.
    180     pipeline->Stop(base::MessageLoop::QuitClosure());
    181     return;
    182   }
    183 
    184   // Consume all the X events
    185   while (XPending(g_display)) {
    186     XEvent e;
    187     XNextEvent(g_display, &e);
    188     switch (e.type) {
    189       case ButtonPress:
    190         {
    191           Window window;
    192           int x, y;
    193           unsigned int width, height, border_width, depth;
    194           XGetGeometry(g_display,
    195                        g_window,
    196                        &window,
    197                        &x,
    198                        &y,
    199                        &width,
    200                        &height,
    201                        &border_width,
    202                        &depth);
    203           base::TimeDelta time = pipeline->GetMediaDuration();
    204           pipeline->Seek(time*e.xbutton.x/width, base::Bind(&OnStatus));
    205         }
    206         break;
    207       case KeyPress:
    208         {
    209           KeySym key = XkbKeycodeToKeysym(g_display, e.xkey.keycode, 0, 0);
    210           if (key == XK_Escape) {
    211             g_running = false;
    212             // Quit message_loop only when pipeline is fully stopped.
    213             pipeline->Stop(base::MessageLoop::QuitClosure());
    214             return;
    215           } else if (key == XK_space) {
    216             if (pipeline->GetPlaybackRate() < 0.01f)  // paused
    217               pipeline->SetPlaybackRate(1.0f);
    218             else
    219               pipeline->SetPlaybackRate(0.0f);
    220           }
    221         }
    222         break;
    223       default:
    224         break;
    225     }
    226   }
    227 
    228   message_loop->PostDelayedTask(
    229       FROM_HERE,
    230       base::Bind(&PeriodicalUpdate,
    231                  base::Unretained(pipeline),
    232                  message_loop),
    233       base::TimeDelta::FromMilliseconds(10));
    234 }
    235 
    236 int main(int argc, char** argv) {
    237   base::AtExitManager at_exit;
    238   media::InitializeMediaLibraryForTesting();
    239 
    240   CommandLine::Init(argc, argv);
    241   CommandLine* command_line = CommandLine::ForCurrentProcess();
    242   std::string filename = command_line->GetSwitchValueASCII("file");
    243 
    244   if (filename.empty()) {
    245     std::cout << "Usage: " << argv[0] << " --file=FILE" << std::endl
    246               << std::endl
    247               << "Optional arguments:" << std::endl
    248               << "  [--audio]"
    249               << "  [--alsa-device=DEVICE]"
    250               << "  [--use-gl]"
    251               << "  [--streaming]" << std::endl
    252               << " Press [ESC] to stop" << std::endl
    253               << " Press [SPACE] to toggle pause/play" << std::endl
    254               << " Press mouse left button to seek" << std::endl;
    255     return 1;
    256   }
    257 
    258   scoped_ptr<media::AudioManager> audio_manager(
    259       media::AudioManager::CreateForTesting());
    260   g_audio_manager = audio_manager.get();
    261 
    262   logging::LoggingSettings settings;
    263   settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
    264   logging::InitLogging(settings);
    265 
    266   // Install the signal handler.
    267   signal(SIGTERM, &TerminateHandler);
    268   signal(SIGINT, &TerminateHandler);
    269 
    270   // Initialize X11.
    271   if (!InitX11())
    272     return 1;
    273 
    274   // Initialize the pipeline thread and the pipeline.
    275   base::MessageLoop message_loop;
    276   base::Thread media_thread("MediaThread");
    277   media_thread.Start();
    278 
    279   media::VideoRendererImpl::PaintCB paint_cb;
    280   if (command_line->HasSwitch("use-gl")) {
    281     paint_cb = media::BindToCurrentLoop(base::Bind(
    282         &GlVideoRenderer::Paint, new GlVideoRenderer(g_display, g_window)));
    283   } else {
    284     paint_cb = media::BindToCurrentLoop(base::Bind(
    285         &X11VideoRenderer::Paint, new X11VideoRenderer(g_display, g_window)));
    286   }
    287 
    288   scoped_ptr<media::DataSource> data_source(new DataSourceLogger(
    289       CreateDataSource(filename), command_line->HasSwitch("streaming")));
    290   scoped_ptr<media::Demuxer> demuxer(new media::FFmpegDemuxer(
    291       media_thread.message_loop_proxy(), data_source.get(),
    292       base::Bind(&NeedKey), new media::MediaLog()));
    293 
    294   media::Pipeline pipeline(media_thread.message_loop_proxy(),
    295                            new media::MediaLog());
    296   InitPipeline(&pipeline, media_thread.message_loop_proxy(), demuxer.get(),
    297                paint_cb, command_line->HasSwitch("audio"));
    298 
    299   // Main loop of the application.
    300   g_running = true;
    301 
    302   message_loop.PostTask(FROM_HERE, base::Bind(
    303       &PeriodicalUpdate, base::Unretained(&pipeline), &message_loop));
    304   message_loop.Run();
    305 
    306   // Cleanup tasks.
    307   media_thread.Stop();
    308 
    309   // Release callback which releases video renderer. Do this before cleaning up
    310   // X below since the video renderer has some X cleanup duties as well.
    311   paint_cb.Reset();
    312 
    313   XDestroyWindow(g_display, g_window);
    314   XCloseDisplay(g_display);
    315   g_audio_manager = NULL;
    316 
    317   return 0;
    318 }
    319