OLD | NEW |
| (Empty) |
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/ffmpeg_audio_decoder.h" | |
30 #include "media/filters/ffmpeg_demuxer.h" | |
31 #include "media/filters/ffmpeg_video_decoder.h" | |
32 #include "media/filters/file_data_source.h" | |
33 #include "media/renderers/audio_renderer_impl.h" | |
34 #include "media/renderers/renderer_impl.h" | |
35 #include "media/renderers/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 OnEncryptedMediaInitData(media::EmeInitDataType init_data_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(new media::VideoRendererImpl( | |
120 task_runner, video_decoders.Pass(), true, new media::MediaLog())); | |
121 | |
122 ScopedVector<media::AudioDecoder> audio_decoders; | |
123 audio_decoders.push_back(new media::FFmpegAudioDecoder(task_runner, | |
124 media::LogCB())); | |
125 media::AudioParameters out_params( | |
126 media::AudioParameters::AUDIO_PCM_LOW_LATENCY, | |
127 media::CHANNEL_LAYOUT_STEREO, | |
128 44100, | |
129 16, | |
130 512); | |
131 media::AudioHardwareConfig hardware_config(out_params, out_params); | |
132 | |
133 scoped_ptr<media::AudioRenderer> audio_renderer(new media::AudioRendererImpl( | |
134 task_runner, new media::NullAudioSink(task_runner), audio_decoders.Pass(), | |
135 hardware_config, new media::MediaLog())); | |
136 | |
137 scoped_ptr<media::Renderer> renderer(new media::RendererImpl( | |
138 task_runner, audio_renderer.Pass(), video_renderer.Pass())); | |
139 | |
140 base::WaitableEvent event(true, false); | |
141 media::PipelineStatus status; | |
142 | |
143 pipeline->Start(demuxer, | |
144 renderer.Pass(), | |
145 base::Bind(&DoNothing), | |
146 base::Bind(&OnStatus), | |
147 base::Bind(&SaveStatusAndSignal, &event, &status), | |
148 base::Bind(&OnMetadata), | |
149 base::Bind(&OnBufferingStateChanged), | |
150 paint_cb, | |
151 base::Bind(&DoNothing), | |
152 base::Bind(&OnAddTextTrack), | |
153 base::Bind(&DoNothing)); | |
154 | |
155 // Wait until the pipeline is fully initialized. | |
156 event.Wait(); | |
157 CHECK_EQ(status, media::PIPELINE_OK) << "Pipeline initialization failed"; | |
158 | |
159 // And start the playback. | |
160 pipeline->SetPlaybackRate(1.0f); | |
161 } | |
162 | |
163 void TerminateHandler(int signal) { | |
164 g_running = false; | |
165 } | |
166 | |
167 void PeriodicalUpdate( | |
168 media::Pipeline* pipeline, | |
169 base::MessageLoop* message_loop) { | |
170 if (!g_running) { | |
171 // interrupt signal was received during last time period. | |
172 // Quit message_loop only when pipeline is fully stopped. | |
173 pipeline->Stop(base::MessageLoop::QuitClosure()); | |
174 return; | |
175 } | |
176 | |
177 // Consume all the X events | |
178 while (XPending(g_display)) { | |
179 XEvent e; | |
180 XNextEvent(g_display, &e); | |
181 switch (e.type) { | |
182 case ButtonPress: | |
183 { | |
184 Window window; | |
185 int x, y; | |
186 unsigned int width, height, border_width, depth; | |
187 XGetGeometry(g_display, | |
188 g_window, | |
189 &window, | |
190 &x, | |
191 &y, | |
192 &width, | |
193 &height, | |
194 &border_width, | |
195 &depth); | |
196 base::TimeDelta time = pipeline->GetMediaDuration(); | |
197 pipeline->Seek(time*e.xbutton.x/width, base::Bind(&OnStatus)); | |
198 } | |
199 break; | |
200 case KeyPress: | |
201 { | |
202 KeySym key = XkbKeycodeToKeysym(g_display, e.xkey.keycode, 0, 0); | |
203 if (key == XK_Escape) { | |
204 g_running = false; | |
205 // Quit message_loop only when pipeline is fully stopped. | |
206 pipeline->Stop(base::MessageLoop::QuitClosure()); | |
207 return; | |
208 } else if (key == XK_space) { | |
209 if (pipeline->GetPlaybackRate() < 0.01f) // paused | |
210 pipeline->SetPlaybackRate(1.0f); | |
211 else | |
212 pipeline->SetPlaybackRate(0.0f); | |
213 } | |
214 } | |
215 break; | |
216 default: | |
217 break; | |
218 } | |
219 } | |
220 | |
221 message_loop->PostDelayedTask( | |
222 FROM_HERE, | |
223 base::Bind(&PeriodicalUpdate, | |
224 base::Unretained(pipeline), | |
225 message_loop), | |
226 base::TimeDelta::FromMilliseconds(10)); | |
227 } | |
228 | |
229 int main(int argc, char** argv) { | |
230 base::AtExitManager at_exit; | |
231 media::InitializeMediaLibraryForTesting(); | |
232 | |
233 base::CommandLine::Init(argc, argv); | |
234 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); | |
235 std::string filename = command_line->GetSwitchValueASCII("file"); | |
236 | |
237 if (filename.empty()) { | |
238 std::cout << "Usage: " << argv[0] << " --file=FILE" << std::endl | |
239 << std::endl | |
240 << "Optional arguments:" << std::endl | |
241 << " [--audio]" | |
242 << " [--alsa-device=DEVICE]" | |
243 << " [--use-gl]" | |
244 << " [--streaming]" << std::endl | |
245 << " Press [ESC] to stop" << std::endl | |
246 << " Press [SPACE] to toggle pause/play" << std::endl | |
247 << " Press mouse left button to seek" << std::endl; | |
248 return 1; | |
249 } | |
250 | |
251 scoped_ptr<media::AudioManager> audio_manager( | |
252 media::AudioManager::CreateForTesting()); | |
253 g_audio_manager = audio_manager.get(); | |
254 | |
255 logging::LoggingSettings settings; | |
256 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; | |
257 logging::InitLogging(settings); | |
258 | |
259 // Install the signal handler. | |
260 signal(SIGTERM, &TerminateHandler); | |
261 signal(SIGINT, &TerminateHandler); | |
262 | |
263 // Initialize X11. | |
264 if (!InitX11()) | |
265 return 1; | |
266 | |
267 // Initialize the pipeline thread and the pipeline. | |
268 base::MessageLoop message_loop; | |
269 base::Thread media_thread("MediaThread"); | |
270 media_thread.Start(); | |
271 | |
272 media::VideoRendererImpl::PaintCB paint_cb; | |
273 if (command_line->HasSwitch("use-gl")) { | |
274 paint_cb = media::BindToCurrentLoop(base::Bind( | |
275 &GlVideoRenderer::Paint, new GlVideoRenderer(g_display, g_window))); | |
276 } else { | |
277 paint_cb = media::BindToCurrentLoop(base::Bind( | |
278 &X11VideoRenderer::Paint, new X11VideoRenderer(g_display, g_window))); | |
279 } | |
280 | |
281 scoped_ptr<media::DataSource> data_source(new DataSourceLogger( | |
282 CreateDataSource(filename), command_line->HasSwitch("streaming"))); | |
283 scoped_ptr<media::Demuxer> demuxer(new media::FFmpegDemuxer( | |
284 media_thread.message_loop_proxy(), data_source.get(), | |
285 base::Bind(&OnEncryptedMediaInitData), new media::MediaLog())); | |
286 | |
287 media::Pipeline pipeline(media_thread.message_loop_proxy(), | |
288 new media::MediaLog()); | |
289 InitPipeline(&pipeline, media_thread.message_loop_proxy(), demuxer.get(), | |
290 paint_cb, command_line->HasSwitch("audio")); | |
291 | |
292 // Main loop of the application. | |
293 g_running = true; | |
294 | |
295 message_loop.PostTask(FROM_HERE, base::Bind( | |
296 &PeriodicalUpdate, base::Unretained(&pipeline), &message_loop)); | |
297 message_loop.Run(); | |
298 | |
299 // Cleanup tasks. | |
300 media_thread.Stop(); | |
301 | |
302 // Release callback which releases video renderer. Do this before cleaning up | |
303 // X below since the video renderer has some X cleanup duties as well. | |
304 paint_cb.Reset(); | |
305 | |
306 XDestroyWindow(g_display, g_window); | |
307 XCloseDisplay(g_display); | |
308 g_audio_manager = NULL; | |
309 | |
310 return 0; | |
311 } | |
OLD | NEW |