OLD | NEW |
(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 mojo { |
| 19 namespace media { |
| 20 namespace examples { |
| 21 |
| 22 class MediaTestApp : public mojo::ApplicationDelegate { |
| 23 public: |
| 24 MediaTestApp() {} |
| 25 |
| 26 ~MediaTestApp() override {} |
| 27 |
| 28 // ApplicationDelegate implementation. |
| 29 void Initialize(mojo::ApplicationImpl* app) override { |
| 30 app_ = app; |
| 31 ProcessArgs(app->args()); |
| 32 |
| 33 std::cout << std::endl << "MEDIA TEST" << std::endl << std::endl; |
| 34 |
| 35 if (input_file_names_.empty()) { |
| 36 std::cout << "Please provide the names of the files you want to play;" |
| 37 << " for example:" << std::endl; |
| 38 std::cout << "mojo/devtools/common/mojo_run \\" << std::endl; |
| 39 std::cout << " \"https://core.mojoapps.io/media_test.mojo \\" |
| 40 << std::endl; |
| 41 std::cout << " file:///usr/local/google/home/you/superstition.ogg \\" |
| 42 << std::endl; |
| 43 std::cout << " file:///usr/local/google/home/you/higherground.ogg\"" |
| 44 << std::endl << std::endl; |
| 45 base::MessageLoop::current()->Quit(); |
| 46 return; |
| 47 } |
| 48 |
| 49 std::cout << " <enter> play/pause" << std::endl; |
| 50 std::cout << " n<enter> next file" << std::endl; |
| 51 std::cout << " p<enter> previous file" << std::endl; |
| 52 std::cout << " <digit><enter> seek (0% - 90%)" << std::endl; |
| 53 std::cout << " q<enter> quit" << std::endl << std::endl; |
| 54 |
| 55 if (paint_) { |
| 56 std::cout << " duration <none>" << std::endl; |
| 57 std::cout << " title <none>" << std::endl; |
| 58 std::cout << " artist <none>" << std::endl; |
| 59 std::cout << " album <none>" << std::endl; |
| 60 std::cout << " publisher <none>" << std::endl; |
| 61 std::cout << " genre <none>" << std::endl; |
| 62 std::cout << " composer <none>" << std::endl << std::endl; |
| 63 std::cout << std::endl << std::endl << kUp << std::flush; |
| 64 } else { |
| 65 std::cout << std::endl; |
| 66 } |
| 67 |
| 68 CreateNewMediaTest(); |
| 69 Poll(); |
| 70 } |
| 71 |
| 72 bool ConfigureIncomingConnection( |
| 73 mojo::ApplicationConnection* connection) override { |
| 74 return true; |
| 75 } |
| 76 |
| 77 private: |
| 78 static const char *kHome; |
| 79 static const char *kClearLine; |
| 80 static const char *kUp; |
| 81 static constexpr double ns_per_second = 1000000000.0; |
| 82 |
| 83 // Processes arguments. |
| 84 void ProcessArgs(const std::vector<std::string>& args) { |
| 85 for (size_t i = 1; i < args.size(); ++i) { |
| 86 const std::string& arg = args[i]; |
| 87 if (arg == "--paint") { |
| 88 paint_ = true; |
| 89 } else if (arg == "--no-paint") { |
| 90 paint_ = false; |
| 91 } else { |
| 92 input_file_names_.push_back(arg); |
| 93 } |
| 94 } |
| 95 |
| 96 input_file_names_iter_ = input_file_names_.begin(); |
| 97 } |
| 98 |
| 99 // Creates a new MediaTest object to play the file referenced by |
| 100 // input_file_names_iter_. |
| 101 void CreateNewMediaTest() { |
| 102 MOJO_DCHECK(input_file_names_iter_ != input_file_names_.end()); |
| 103 media_test_ = MediaTest::Create(app_, *input_file_names_iter_); |
| 104 |
| 105 metadata_shown_ = false; |
| 106 media_test_->RegisterUpdateCallback([this]() { |
| 107 HandleMediaTestUpdateCallback(); |
| 108 }); |
| 109 |
| 110 media_test_->Play(); |
| 111 } |
| 112 |
| 113 void HandleMediaTestUpdateCallback() { |
| 114 if (media_test_->state() == MediaState::ENDED) { |
| 115 // MediaTest doesn't appreciate being deleted in this callback. |
| 116 // Next time Poll runs, we move on to the next file. |
| 117 base::MessageLoop::current()->PostTask( |
| 118 FROM_HERE, |
| 119 base::Bind(&MediaTestApp::PlayNext, base::Unretained(this))); |
| 120 } |
| 121 |
| 122 const MediaMetadataPtr& metadata = media_test_->metadata(); |
| 123 |
| 124 if (metadata) { |
| 125 duration_ns_ = metadata->duration; |
| 126 } |
| 127 |
| 128 if (paint_) { |
| 129 // Move the cursor up the terminal so we paint over the old metadata |
| 130 // (7 lines) a blank line and the state line (total of 9 lines). |
| 131 std::cout << kHome |
| 132 << kUp << kUp << kUp << kUp << kUp << kUp << kUp << kUp << kUp; |
| 133 } |
| 134 |
| 135 if (!paint_ && metadata_shown_) { |
| 136 // Do nothing. |
| 137 } else if (metadata) { |
| 138 metadata_shown_ = true; |
| 139 std::cout << " duration " << std::setprecision(1) << |
| 140 double(metadata->duration) / ns_per_second << " seconds" |
| 141 << clear_line() << std::endl; |
| 142 std::cout << " title " << |
| 143 (metadata->title ? metadata->title : "<none>") |
| 144 << clear_line() << std::endl; |
| 145 std::cout << " artist " << |
| 146 (metadata->artist ? metadata->artist : "<none>") |
| 147 << clear_line() << std::endl; |
| 148 std::cout << " album " << |
| 149 (metadata->album ? metadata->album : "<none>") |
| 150 << clear_line() << std::endl; |
| 151 std::cout << " publisher " << |
| 152 (metadata->publisher ? metadata->publisher : "<none>") |
| 153 << clear_line() << std::endl; |
| 154 std::cout << " genre " << |
| 155 (metadata->genre ? metadata->genre : "<none>") |
| 156 << clear_line() << std::endl; |
| 157 std::cout << " composer " << |
| 158 (metadata->composer ? metadata->composer : "<none>") |
| 159 << clear_line() << std::endl << std::endl; |
| 160 } else { |
| 161 std::cout << " duration <none>" << kClearLine << std::endl; |
| 162 std::cout << " title <none>" << kClearLine << std::endl; |
| 163 std::cout << " artist <none>" << kClearLine << std::endl; |
| 164 std::cout << " album <none>" << kClearLine << std::endl; |
| 165 std::cout << " publisher <none>" << kClearLine << std::endl; |
| 166 std::cout << " genre <none>" << kClearLine << std::endl; |
| 167 std::cout << " composer <none>" << kClearLine << std::endl; |
| 168 std::cout << std::endl; |
| 169 } |
| 170 std::cout << " " << state_string() << clear_line() << std::endl; |
| 171 } |
| 172 |
| 173 // Returns a string describing the MediaTest object's state. |
| 174 const char* state_string() const { |
| 175 switch (media_test_->state()) { |
| 176 case MediaState::FAULT: |
| 177 return "FAULT"; |
| 178 case MediaState::UNPREPARED: |
| 179 return "unprepared"; |
| 180 case MediaState::PAUSED: |
| 181 return "paused"; |
| 182 case MediaState::PLAYING: |
| 183 return "playing"; |
| 184 case MediaState::ENDED: |
| 185 return "ended"; |
| 186 } |
| 187 return "UNSUPPORTED STATE VALUE"; |
| 188 } |
| 189 |
| 190 // Handles a keystroke. |
| 191 void HandleKeystroke(char keystroke) { |
| 192 switch (keystroke) { |
| 193 case '\n': |
| 194 TogglePlayPause(); |
| 195 break; |
| 196 case 'q': |
| 197 base::MessageLoop::current()->Quit(); |
| 198 quit_ = true; |
| 199 if (paint_) { |
| 200 std::cout << kHome << kUp << " quitting" << kClearLine << std::endl |
| 201 << kClearLine << std::endl; |
| 202 } else { |
| 203 std::cout << " quitting" << std::endl; |
| 204 } |
| 205 break; |
| 206 case 'n': |
| 207 if (++input_file_names_iter_ == input_file_names_.end()) { |
| 208 input_file_names_iter_ = input_file_names_.begin(); |
| 209 } |
| 210 CreateNewMediaTest(); |
| 211 break; |
| 212 case 'p': |
| 213 if (input_file_names_iter_ == input_file_names_.begin()) { |
| 214 input_file_names_iter_ = input_file_names_.end(); |
| 215 } |
| 216 input_file_names_iter_--; |
| 217 CreateNewMediaTest(); |
| 218 break; |
| 219 case '0': |
| 220 case '1': |
| 221 case '2': |
| 222 case '3': |
| 223 case '4': |
| 224 case '5': |
| 225 case '6': |
| 226 case '7': |
| 227 case '8': |
| 228 case '9': { |
| 229 int64_t position_ns = ((keystroke - '0') / 10.0) * duration_ns_; |
| 230 media_test_->Seek(position_ns); |
| 231 if (!paint_) { |
| 232 std::cout << " seeking to " << std::fixed << std::setprecision(1) |
| 233 << double(position_ns) / ns_per_second |
| 234 << " seconds " << std::endl; |
| 235 } |
| 236 break; |
| 237 } |
| 238 } |
| 239 } |
| 240 |
| 241 // Toggles between play and pause (prepared) states. |
| 242 void TogglePlayPause() { |
| 243 switch (media_test_->state()) { |
| 244 case MediaState::PAUSED: |
| 245 media_test_->Play(); |
| 246 break; |
| 247 case MediaState::PLAYING: |
| 248 media_test_->Pause(); |
| 249 break; |
| 250 case MediaState::ENDED: |
| 251 if (input_file_names_.size() == 1) { |
| 252 // Replaying the only file. Reuse the same MediaTest instance. |
| 253 media_test_->Seek(0); |
| 254 media_test_->Play(); |
| 255 } else { |
| 256 // Starting a new file. Create a new MediaTest instance. |
| 257 CreateNewMediaTest(); |
| 258 } |
| 259 break; |
| 260 default: |
| 261 break; |
| 262 } |
| 263 } |
| 264 |
| 265 // Does any needed work and schedules itself to run again soon. Quits when |
| 266 // quit_ is true. |
| 267 void Poll() { |
| 268 MOJO_DCHECK(!quit_); |
| 269 |
| 270 char keystroke = Keystroke(); |
| 271 if (keystroke != 0) { |
| 272 HandleKeystroke(keystroke); |
| 273 } |
| 274 |
| 275 if (quit_) { |
| 276 // Eat the any additional keystrokes, which would otherwise make it to the |
| 277 // command shell. |
| 278 while (Keystroke() != 0) { |
| 279 // Do nothing. |
| 280 } |
| 281 return; |
| 282 } |
| 283 |
| 284 if (paint_) { |
| 285 std::cout << kHome << " " << std::fixed << std::setprecision(1) |
| 286 << double(media_test_->position_ns()) / ns_per_second |
| 287 << " seconds " << kClearLine << std::flush; |
| 288 } |
| 289 |
| 290 base::MessageLoop::current()->PostDelayedTask( |
| 291 FROM_HERE, |
| 292 base::Bind(&MediaTestApp::Poll, base::Unretained(this)), |
| 293 base::TimeDelta::FromMilliseconds(100)); |
| 294 } |
| 295 |
| 296 void PlayNext() { |
| 297 if (++input_file_names_iter_ == input_file_names_.end()) { |
| 298 input_file_names_iter_ = input_file_names_.begin(); |
| 299 } else { |
| 300 CreateNewMediaTest(); |
| 301 } |
| 302 } |
| 303 |
| 304 const char* clear_line() const { |
| 305 return paint_ ? kClearLine : ""; |
| 306 } |
| 307 |
| 308 mojo::ApplicationImpl* app_; |
| 309 std::unique_ptr<MediaTest> media_test_; |
| 310 std::deque<std::string> input_file_names_; |
| 311 decltype(input_file_names_.begin()) input_file_names_iter_; |
| 312 bool quit_ = false; |
| 313 bool paint_ = true; |
| 314 bool metadata_shown_ = false; |
| 315 uint64_t duration_ns_; |
| 316 |
| 317 DISALLOW_COPY_AND_ASSIGN(MediaTestApp); |
| 318 }; |
| 319 |
| 320 const char* MediaTestApp::kHome = "\r"; |
| 321 const char* MediaTestApp::kClearLine = "\033[K"; |
| 322 const char* MediaTestApp::kUp = "\033[A"; |
| 323 |
| 324 } // namespace examples |
| 325 } // namespace media |
| 326 } // namespace mojo |
| 327 |
| 328 MojoResult MojoMain(MojoHandle application_request) { |
| 329 mojo::ApplicationRunnerChromium |
| 330 runner(new mojo::media::examples::MediaTestApp); |
| 331 return runner.Run(application_request); |
| 332 } |
OLD | NEW |