Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(146)

Side by Side Diff: examples/media_test/media_test_app.cc

Issue 1809703003: Motown: Add examples/media_test, a command line media player app. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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 <deque>
6 #include <iomanip>
7 #include <iostream>
8
9 #include "base/bind.h"
10 #include "examples/media_test/keystroke.h"
11 #include "examples/media_test/media_test.h"
12 #include "mojo/application/application_runner_chromium.h"
13 #include "mojo/public/c/system/main.h"
14 #include "mojo/public/cpp/application/application_connection.h"
15 #include "mojo/public/cpp/application/application_delegate.h"
16 #include "mojo/public/cpp/application/application_impl.h"
17
18 namespace examples {
19
20 class MediaTestApp : public mojo::ApplicationDelegate {
21 public:
22 MediaTestApp() {}
23
24 ~MediaTestApp() override {}
25
26 // ApplicationDelegate implementation.
27 void Initialize(mojo::ApplicationImpl* app) override {
28 app_ = app;
29 ProcessArgs(app->args());
30
31 std::cerr << std::endl << "MEDIA TEST" << std::endl << std::endl;
32
33 if (input_file_names_.empty()) {
34 std::cerr << "Please provide the names of the files you want to play;"
35 << " for example:" << std::endl;
36 std::cerr << "mojo/devtools/common/mojo_run \\" << std::endl;
37 std::cerr << " \"https://core.mojoapps.io/media_test.mojo \\"
38 << std::endl;
39 std::cerr << " file:///usr/local/google/home/you/superstition.ogg \\"
40 << std::endl;
41 std::cerr << " file:///usr/local/google/home/you/higherground.ogg\""
42 << std::endl << std::endl;
43 base::MessageLoop::current()->Quit();
44 return;
45 }
46
47 std::cerr << " <enter> play/pause" << std::endl;
48 std::cerr << " n<enter> next file" << std::endl;
49 std::cerr << " p<enter> previous file" << std::endl;
50 std::cerr << " <digit><enter> seek (0% - 90%)" << std::endl;
51 std::cerr << " q<enter> quit" << std::endl << std::endl;
52
53 if (paint_) {
54 std::cerr << " duration <none>" << std::endl;
55 std::cerr << " title <none>" << std::endl;
56 std::cerr << " artist <none>" << std::endl;
57 std::cerr << " album <none>" << std::endl;
58 std::cerr << " publisher <none>" << std::endl;
59 std::cerr << " genre <none>" << std::endl;
60 std::cerr << " composer <none>" << std::endl << std::endl;
61 std::cerr << std::endl << std::endl << kUp << std::flush;
62 } else {
63 std::cerr << std::endl;
64 }
65
66 CreateNewMediaTest();
67 Poll();
68 }
69
70 bool ConfigureIncomingConnection(
71 mojo::ApplicationConnection* connection) override {
72 return true;
73 }
74
75 private:
76 const char *kHome = "\r";
77 const char *kClearLine = "\033[K";
78 const char *kUp = "\033[A";
79 const double ns_per_second = 1000000000.0;
johngro 2016/03/21 22:10:47 static constexpr for these. Don't forget to decla
dalesat 2016/03/21 23:58:35 Done.
80
81 // Processes arguments.
82 void ProcessArgs(const std::vector<std::string>& args) {
83 if (args.size() <= 1) {
84 return;
85 }
86
87 for (std::string arg : args) {
johngro 2016/03/21 22:10:47 probably should be const std::string& or const aut
dalesat 2016/03/21 23:58:35 Done.
88 if (arg == "--paint") {
89 paint_ = true;
90 } else if (arg == "--no-paint") {
91 paint_ = false;
92 } else {
93 input_file_names_.push_back(arg);
94 }
95 }
96
97 input_file_names_.pop_front();
johngro 2016/03/21 22:10:47 instead of doing this, why not just for (size_t i
dalesat 2016/03/21 23:58:35 Done.
98
99 input_file_names_iter_ = input_file_names_.begin();
johngro 2016/03/21 22:10:47 see above; this variable will not be initialized b
dalesat 2016/03/21 23:58:35 Acknowledged.
100 }
101
102 // Creates a new MediaTest object to play the file referenced by
103 // input_file_names_iter_.
104 void CreateNewMediaTest() {
johngro 2016/03/21 22:10:47 DCHECK(input_file_names_iter_ != input_file_names.
dalesat 2016/03/21 23:58:35 Done.
105 media_test_ = MediaTest::Create(app_, *input_file_names_iter_);
106
107 metadata_shown_ = false;
108 media_test_->RegisterUpdateCallback([this]() {
johngro 2016/03/21 22:10:47 I really think that this lambda belongs in its own
dalesat 2016/03/21 23:58:35 Done.
109 if (media_test_->state() == mojo::media::MediaState::ENDED) {
110 // MediaTest doesn't appreciate being deleted in this callback.
111 // Next time Poll runs, we move on to the next file.
112 play_next_ = true;
johngro 2016/03/21 22:10:47 consider posting a message to yourself in order to
dalesat 2016/03/21 23:58:35 Done.
113 }
114
115 const mojo::media::MediaMetadataPtr& metadata = media_test_->metadata();
116
117 if (metadata) {
118 duration_ns_ = metadata->duration;
119 }
120
121 if (paint_) {
122 std::cerr << kHome
123 << kUp << kUp << kUp<< kUp << kUp << kUp << kUp << kUp << kUp;
johngro 2016/03/21 22:10:47 This is a bit nasty. While I understand the lack
dalesat 2016/03/21 23:58:35 Done.
124 }
125
126 if (!paint_ && metadata_shown_) {
127 // Do nothing.
128 } else if (metadata) {
129 metadata_shown_ = true;
130 std::cerr << " duration " << std::setprecision(1) <<
131 double(metadata->duration) / ns_per_second << " seconds"
132 << clear_line() << std::endl;
133 std::cerr << " title " <<
134 (metadata->title ? metadata->title : "<none>")
135 << clear_line() << std::endl;
136 std::cerr << " artist " <<
137 (metadata->artist ? metadata->artist : "<none>")
138 << clear_line() << std::endl;
139 std::cerr << " album " <<
140 (metadata->album ? metadata->album : "<none>")
141 << clear_line() << std::endl;
142 std::cerr << " publisher " <<
143 (metadata->publisher ? metadata->publisher : "<none>")
144 << clear_line() << std::endl;
145 std::cerr << " genre " <<
146 (metadata->genre ? metadata->genre : "<none>")
147 << clear_line() << std::endl;
148 std::cerr << " composer " <<
149 (metadata->composer ? metadata->composer : "<none>")
150 << clear_line() << std::endl << std::endl;
151 } else {
152 std::cerr << " duration <none>" << kClearLine << std::endl;
153 std::cerr << " title <none>" << kClearLine << std::endl;
154 std::cerr << " artist <none>" << kClearLine << std::endl;
155 std::cerr << " album <none>" << kClearLine << std::endl;
156 std::cerr << " publisher <none>" << kClearLine << std::endl;
157 std::cerr << " genre <none>" << kClearLine << std::endl;
158 std::cerr << " composer <none>" << kClearLine << std::endl;
159 std::cerr << std::endl;
160 }
161 std::cerr << " " << state_string() << clear_line() << std::endl;
162 });
163
164 media_test_->Play();
165 }
166
167 // Returns a string describing the MediaTest object's state.
168 const char* state_string() const {
169 switch (media_test_->state()) {
170 case mojo::media::MediaState::FAULT:
171 return "FAULT";
johngro 2016/03/21 22:10:47 perhaps put this case at the bottom, grouped with
dalesat 2016/03/21 23:58:35 Never returning nullptr is a good idea, thanks. I
johngro 2016/03/22 16:56:35 Acknowledged.
172 case mojo::media::MediaState::UNPREPARED:
173 return "unprepared";
174 case mojo::media::MediaState::PAUSED:
175 return "paused";
176 case mojo::media::MediaState::PLAYING:
177 return "playing";
178 case mojo::media::MediaState::ENDED:
179 return "ended";
180 }
181 return nullptr;
182 }
183
184 // Handles a keystroke.
185 void HandleKeystroke(char keystroke) {
186 switch (keystroke) {
187 case '\n':
188 TogglePlayPause();
189 break;
190 case 'q':
191 base::MessageLoop::current()->Quit();
192 quit_ = true;
193 if (paint_) {
194 std::cerr << kHome << kUp << " quitting" << kClearLine << std::endl
195 << kClearLine << std::endl;
196 } else {
197 std::cerr << " quitting" << std::endl;
198 }
199 break;
200 case 'n':
201 if (++input_file_names_iter_ == input_file_names_.end()) {
202 input_file_names_iter_ = input_file_names_.begin();
203 }
204 CreateNewMediaTest();
205 break;
206 case 'p':
207 if (input_file_names_iter_ == input_file_names_.begin()) {
208 input_file_names_iter_ = input_file_names_.end();
209 }
210 input_file_names_iter_--;
211 CreateNewMediaTest();
212 break;
213 case '0':
214 case '1':
215 case '2':
216 case '3':
217 case '4':
218 case '5':
219 case '6':
220 case '7':
221 case '8':
222 case '9': {
223 int64_t position_ns = ((keystroke - '0') / 10.0) * duration_ns_;
224 media_test_->Seek(position_ns);
225 if (!paint_) {
226 std::cerr << " seeking to " << std::fixed << std::setprecision(1)
227 << double(position_ns) / ns_per_second
228 << " seconds " << std::endl;
229 }
230 break;
231 }
232 }
233 }
234
235 // Toggles between play and pause (prepared) states.
236 void TogglePlayPause() {
237 switch (media_test_->state()) {
238 case mojo::media::MediaState::PAUSED:
239 media_test_->Play();
240 break;
241 case mojo::media::MediaState::PLAYING:
242 media_test_->Pause();
243 break;
244 case mojo::media::MediaState::ENDED:
245 if (input_file_names_.size() == 1) {
246 // Replaying the only file. Reuse the same MediaTest instance.
247 media_test_->Seek(0);
248 media_test_->Play();
249 } else {
250 // Starting a new file. Create a new MediaTest instance.
251 CreateNewMediaTest();
252 }
253 break;
254 default:
255 break;
256 }
257 }
258
259 // Does any needed work and schedules itself to run again soon. Quits when
260 // quit_ is true.
261 void Poll() {
262 char keystroke = Keystroke();
263 if (keystroke != 0) {
264 HandleKeystroke(keystroke);
265 }
johngro 2016/03/21 22:10:47 right now, Poll seems to be a necessary evil in or
dalesat 2016/03/21 23:58:35 Agreed. I switched to posting a task for play_next
266
267 if (quit_) {
johngro 2016/03/21 22:10:47 this quit_ check should probably be first. We sho
dalesat 2016/03/21 23:58:35 HandleKeystroke sets quit_, so this is the best we
268 // Eat the any additional keystrokes, which would otherwise make it to the
269 // command shell.
270 while (Keystroke() != 0) {
271 // Do nothing.
272 }
273 return;
274 }
275
276 if (play_next_) {
277 play_next_ = false;
278 if (++input_file_names_iter_ == input_file_names_.end()) {
279 input_file_names_iter_ = input_file_names_.begin();
280 } else {
281 CreateNewMediaTest();
282 }
283 }
284
285 if (paint_) {
286 std::cerr << kHome << " " << std::fixed << std::setprecision(1)
287 << double(media_test_->position_ns()) / ns_per_second
288 << " seconds " << kClearLine << std::flush;
289 }
290
291 base::MessageLoop::current()->PostDelayedTask(
292 FROM_HERE,
293 base::Bind(&MediaTestApp::Poll, base::Unretained(this)),
294 base::TimeDelta::FromMilliseconds(100));
295 }
296
297 const char* clear_line() const {
298 return paint_ ? kClearLine : "";
299 }
300
301 mojo::ApplicationImpl* app_;
302 std::unique_ptr<MediaTest> media_test_;
303 std::deque<std::string> input_file_names_;
304 decltype(input_file_names_.begin()) input_file_names_iter_;
305 bool play_next_ = false;
306 bool quit_ = false;
307 bool paint_ = true;
308 bool metadata_shown_ = false;
309 uint64_t duration_ns_;
310
311 DISALLOW_COPY_AND_ASSIGN(MediaTestApp);
312 };
313
314 } // namespace examples
315
316 MojoResult MojoMain(MojoHandle application_request) {
317 mojo::ApplicationRunnerChromium runner(new examples::MediaTestApp);
318 return runner.Run(application_request);
319 }
OLDNEW
« examples/media_test/media_test.cc ('K') | « examples/media_test/media_test.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698