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 47static Display* g_display = NULL; 48static Window g_window = 0; 49static bool g_running = false; 50 51media::AudioManager* g_audio_manager = NULL; 52 53scoped_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. 63bool 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 86static void DoNothing() {} 87 88static void OnStatus(media::PipelineStatus status) {} 89 90static void OnMetadata(media::PipelineMetadata metadata) {} 91 92static void OnBufferingStateChanged(media::BufferingState buffering_state) {} 93 94static void OnAddTextTrack(const media::TextTrackConfig& config, 95 const media::AddTextTrackDoneCB& done_cb) { 96} 97 98static void NeedKey(const std::string& type, 99 const std::vector<uint8>& init_data) { 100 std::cout << "File is encrypted." << std::endl; 101} 102 103static 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) 111void 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 170void TerminateHandler(int signal) { 171 g_running = false; 172} 173 174void 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 236int 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