Index: examples/media_test/media_test_app.cc |
diff --git a/examples/media_test/media_test_app.cc b/examples/media_test/media_test_app.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b916f6199900558563e92b606492f2179327c75a |
--- /dev/null |
+++ b/examples/media_test/media_test_app.cc |
@@ -0,0 +1,332 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include <deque> |
+#include <iomanip> |
+#include <iostream> |
+ |
+#include "base/bind.h" |
+#include "examples/media_test/keystroke.h" |
+#include "examples/media_test/media_test.h" |
+#include "mojo/application/application_runner_chromium.h" |
+#include "mojo/public/c/system/main.h" |
+#include "mojo/public/cpp/application/application_connection.h" |
+#include "mojo/public/cpp/application/application_delegate.h" |
+#include "mojo/public/cpp/application/application_impl.h" |
+ |
+namespace mojo { |
+namespace media { |
+namespace examples { |
+ |
+class MediaTestApp : public mojo::ApplicationDelegate { |
+ public: |
+ MediaTestApp() {} |
+ |
+ ~MediaTestApp() override {} |
+ |
+ // ApplicationDelegate implementation. |
+ void Initialize(mojo::ApplicationImpl* app) override { |
+ app_ = app; |
+ ProcessArgs(app->args()); |
+ |
+ std::cout << std::endl << "MEDIA TEST" << std::endl << std::endl; |
+ |
+ if (input_file_names_.empty()) { |
+ std::cout << "Please provide the names of the files you want to play;" |
+ << " for example:" << std::endl; |
+ std::cout << "mojo/devtools/common/mojo_run \\" << std::endl; |
+ std::cout << " \"https://core.mojoapps.io/media_test.mojo \\" |
+ << std::endl; |
+ std::cout << " file:///usr/local/google/home/you/superstition.ogg \\" |
+ << std::endl; |
+ std::cout << " file:///usr/local/google/home/you/higherground.ogg\"" |
+ << std::endl << std::endl; |
+ base::MessageLoop::current()->Quit(); |
+ return; |
+ } |
+ |
+ std::cout << " <enter> play/pause" << std::endl; |
+ std::cout << " n<enter> next file" << std::endl; |
+ std::cout << " p<enter> previous file" << std::endl; |
+ std::cout << " <digit><enter> seek (0% - 90%)" << std::endl; |
+ std::cout << " q<enter> quit" << std::endl << std::endl; |
+ |
+ if (paint_) { |
+ std::cout << " duration <none>" << std::endl; |
+ std::cout << " title <none>" << std::endl; |
+ std::cout << " artist <none>" << std::endl; |
+ std::cout << " album <none>" << std::endl; |
+ std::cout << " publisher <none>" << std::endl; |
+ std::cout << " genre <none>" << std::endl; |
+ std::cout << " composer <none>" << std::endl << std::endl; |
+ std::cout << std::endl << std::endl << kUp << std::flush; |
+ } else { |
+ std::cout << std::endl; |
+ } |
+ |
+ CreateNewMediaTest(); |
+ Poll(); |
+ } |
+ |
+ bool ConfigureIncomingConnection( |
+ mojo::ApplicationConnection* connection) override { |
+ return true; |
+ } |
+ |
+ private: |
+ static const char *kHome; |
+ static const char *kClearLine; |
+ static const char *kUp; |
+ static constexpr double ns_per_second = 1000000000.0; |
+ |
+ // Processes arguments. |
+ void ProcessArgs(const std::vector<std::string>& args) { |
+ for (size_t i = 1; i < args.size(); ++i) { |
+ const std::string& arg = args[i]; |
+ if (arg == "--paint") { |
+ paint_ = true; |
+ } else if (arg == "--no-paint") { |
+ paint_ = false; |
+ } else { |
+ input_file_names_.push_back(arg); |
+ } |
+ } |
+ |
+ input_file_names_iter_ = input_file_names_.begin(); |
+ } |
+ |
+ // Creates a new MediaTest object to play the file referenced by |
+ // input_file_names_iter_. |
+ void CreateNewMediaTest() { |
+ MOJO_DCHECK(input_file_names_iter_ != input_file_names_.end()); |
+ media_test_ = MediaTest::Create(app_, *input_file_names_iter_); |
+ |
+ metadata_shown_ = false; |
+ media_test_->RegisterUpdateCallback([this]() { |
+ HandleMediaTestUpdateCallback(); |
+ }); |
+ |
+ media_test_->Play(); |
+ } |
+ |
+ void HandleMediaTestUpdateCallback() { |
+ if (media_test_->state() == MediaState::ENDED) { |
+ // MediaTest doesn't appreciate being deleted in this callback. |
+ // Next time Poll runs, we move on to the next file. |
+ base::MessageLoop::current()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&MediaTestApp::PlayNext, base::Unretained(this))); |
+ } |
+ |
+ const MediaMetadataPtr& metadata = media_test_->metadata(); |
+ |
+ if (metadata) { |
+ duration_ns_ = metadata->duration; |
+ } |
+ |
+ if (paint_) { |
+ // Move the cursor up the terminal so we paint over the old metadata |
+ // (7 lines) a blank line and the state line (total of 9 lines). |
+ std::cout << kHome |
+ << kUp << kUp << kUp << kUp << kUp << kUp << kUp << kUp << kUp; |
+ } |
+ |
+ if (!paint_ && metadata_shown_) { |
+ // Do nothing. |
+ } else if (metadata) { |
+ metadata_shown_ = true; |
+ std::cout << " duration " << std::setprecision(1) << |
+ double(metadata->duration) / ns_per_second << " seconds" |
+ << clear_line() << std::endl; |
+ std::cout << " title " << |
+ (metadata->title ? metadata->title : "<none>") |
+ << clear_line() << std::endl; |
+ std::cout << " artist " << |
+ (metadata->artist ? metadata->artist : "<none>") |
+ << clear_line() << std::endl; |
+ std::cout << " album " << |
+ (metadata->album ? metadata->album : "<none>") |
+ << clear_line() << std::endl; |
+ std::cout << " publisher " << |
+ (metadata->publisher ? metadata->publisher : "<none>") |
+ << clear_line() << std::endl; |
+ std::cout << " genre " << |
+ (metadata->genre ? metadata->genre : "<none>") |
+ << clear_line() << std::endl; |
+ std::cout << " composer " << |
+ (metadata->composer ? metadata->composer : "<none>") |
+ << clear_line() << std::endl << std::endl; |
+ } else { |
+ std::cout << " duration <none>" << kClearLine << std::endl; |
+ std::cout << " title <none>" << kClearLine << std::endl; |
+ std::cout << " artist <none>" << kClearLine << std::endl; |
+ std::cout << " album <none>" << kClearLine << std::endl; |
+ std::cout << " publisher <none>" << kClearLine << std::endl; |
+ std::cout << " genre <none>" << kClearLine << std::endl; |
+ std::cout << " composer <none>" << kClearLine << std::endl; |
+ std::cout << std::endl; |
+ } |
+ std::cout << " " << state_string() << clear_line() << std::endl; |
+ } |
+ |
+ // Returns a string describing the MediaTest object's state. |
+ const char* state_string() const { |
+ switch (media_test_->state()) { |
+ case MediaState::FAULT: |
+ return "FAULT"; |
+ case MediaState::UNPREPARED: |
+ return "unprepared"; |
+ case MediaState::PAUSED: |
+ return "paused"; |
+ case MediaState::PLAYING: |
+ return "playing"; |
+ case MediaState::ENDED: |
+ return "ended"; |
+ } |
+ return "UNSUPPORTED STATE VALUE"; |
+ } |
+ |
+ // Handles a keystroke. |
+ void HandleKeystroke(char keystroke) { |
+ switch (keystroke) { |
+ case '\n': |
+ TogglePlayPause(); |
+ break; |
+ case 'q': |
+ base::MessageLoop::current()->Quit(); |
+ quit_ = true; |
+ if (paint_) { |
+ std::cout << kHome << kUp << " quitting" << kClearLine << std::endl |
+ << kClearLine << std::endl; |
+ } else { |
+ std::cout << " quitting" << std::endl; |
+ } |
+ break; |
+ case 'n': |
+ if (++input_file_names_iter_ == input_file_names_.end()) { |
+ input_file_names_iter_ = input_file_names_.begin(); |
+ } |
+ CreateNewMediaTest(); |
+ break; |
+ case 'p': |
+ if (input_file_names_iter_ == input_file_names_.begin()) { |
+ input_file_names_iter_ = input_file_names_.end(); |
+ } |
+ input_file_names_iter_--; |
+ CreateNewMediaTest(); |
+ break; |
+ case '0': |
+ case '1': |
+ case '2': |
+ case '3': |
+ case '4': |
+ case '5': |
+ case '6': |
+ case '7': |
+ case '8': |
+ case '9': { |
+ int64_t position_ns = ((keystroke - '0') / 10.0) * duration_ns_; |
+ media_test_->Seek(position_ns); |
+ if (!paint_) { |
+ std::cout << " seeking to " << std::fixed << std::setprecision(1) |
+ << double(position_ns) / ns_per_second |
+ << " seconds " << std::endl; |
+ } |
+ break; |
+ } |
+ } |
+ } |
+ |
+ // Toggles between play and pause (prepared) states. |
+ void TogglePlayPause() { |
+ switch (media_test_->state()) { |
+ case MediaState::PAUSED: |
+ media_test_->Play(); |
+ break; |
+ case MediaState::PLAYING: |
+ media_test_->Pause(); |
+ break; |
+ case MediaState::ENDED: |
+ if (input_file_names_.size() == 1) { |
+ // Replaying the only file. Reuse the same MediaTest instance. |
+ media_test_->Seek(0); |
+ media_test_->Play(); |
+ } else { |
+ // Starting a new file. Create a new MediaTest instance. |
+ CreateNewMediaTest(); |
+ } |
+ break; |
+ default: |
+ break; |
+ } |
+ } |
+ |
+ // Does any needed work and schedules itself to run again soon. Quits when |
+ // quit_ is true. |
+ void Poll() { |
+ MOJO_DCHECK(!quit_); |
+ |
+ char keystroke = Keystroke(); |
+ if (keystroke != 0) { |
+ HandleKeystroke(keystroke); |
+ } |
+ |
+ if (quit_) { |
+ // Eat the any additional keystrokes, which would otherwise make it to the |
+ // command shell. |
+ while (Keystroke() != 0) { |
+ // Do nothing. |
+ } |
+ return; |
+ } |
+ |
+ if (paint_) { |
+ std::cout << kHome << " " << std::fixed << std::setprecision(1) |
+ << double(media_test_->position_ns()) / ns_per_second |
+ << " seconds " << kClearLine << std::flush; |
+ } |
+ |
+ base::MessageLoop::current()->PostDelayedTask( |
+ FROM_HERE, |
+ base::Bind(&MediaTestApp::Poll, base::Unretained(this)), |
+ base::TimeDelta::FromMilliseconds(100)); |
+ } |
+ |
+ void PlayNext() { |
+ if (++input_file_names_iter_ == input_file_names_.end()) { |
+ input_file_names_iter_ = input_file_names_.begin(); |
+ } else { |
+ CreateNewMediaTest(); |
+ } |
+ } |
+ |
+ const char* clear_line() const { |
+ return paint_ ? kClearLine : ""; |
+ } |
+ |
+ mojo::ApplicationImpl* app_; |
+ std::unique_ptr<MediaTest> media_test_; |
+ std::deque<std::string> input_file_names_; |
+ decltype(input_file_names_.begin()) input_file_names_iter_; |
+ bool quit_ = false; |
+ bool paint_ = true; |
+ bool metadata_shown_ = false; |
+ uint64_t duration_ns_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(MediaTestApp); |
+}; |
+ |
+const char* MediaTestApp::kHome = "\r"; |
+const char* MediaTestApp::kClearLine = "\033[K"; |
+const char* MediaTestApp::kUp = "\033[A"; |
+ |
+} // namespace examples |
+} // namespace media |
+} // namespace mojo |
+ |
+MojoResult MojoMain(MojoHandle application_request) { |
+ mojo::ApplicationRunnerChromium |
+ runner(new mojo::media::examples::MediaTestApp); |
+ return runner.Run(application_request); |
+} |