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

Unified 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 side-by-side diff with in-line comments
Download patch
« examples/media_test/media_test.cc ('K') | « examples/media_test/media_test.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..04b7059cdf689caa6603bb99c7a8eae484e4d08f
--- /dev/null
+++ b/examples/media_test/media_test_app.cc
@@ -0,0 +1,319 @@
+// 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 examples {
+
+class MediaTestApp : public mojo::ApplicationDelegate {
+ public:
+ MediaTestApp() {}
+
+ ~MediaTestApp() override {}
+
+ // ApplicationDelegate implementation.
+ void Initialize(mojo::ApplicationImpl* app) override {
+ app_ = app;
+ ProcessArgs(app->args());
+
+ std::cerr << std::endl << "MEDIA TEST" << std::endl << std::endl;
+
+ if (input_file_names_.empty()) {
+ std::cerr << "Please provide the names of the files you want to play;"
+ << " for example:" << std::endl;
+ std::cerr << "mojo/devtools/common/mojo_run \\" << std::endl;
+ std::cerr << " \"https://core.mojoapps.io/media_test.mojo \\"
+ << std::endl;
+ std::cerr << " file:///usr/local/google/home/you/superstition.ogg \\"
+ << std::endl;
+ std::cerr << " file:///usr/local/google/home/you/higherground.ogg\""
+ << std::endl << std::endl;
+ base::MessageLoop::current()->Quit();
+ return;
+ }
+
+ std::cerr << " <enter> play/pause" << std::endl;
+ std::cerr << " n<enter> next file" << std::endl;
+ std::cerr << " p<enter> previous file" << std::endl;
+ std::cerr << " <digit><enter> seek (0% - 90%)" << std::endl;
+ std::cerr << " q<enter> quit" << std::endl << std::endl;
+
+ if (paint_) {
+ std::cerr << " duration <none>" << std::endl;
+ std::cerr << " title <none>" << std::endl;
+ std::cerr << " artist <none>" << std::endl;
+ std::cerr << " album <none>" << std::endl;
+ std::cerr << " publisher <none>" << std::endl;
+ std::cerr << " genre <none>" << std::endl;
+ std::cerr << " composer <none>" << std::endl << std::endl;
+ std::cerr << std::endl << std::endl << kUp << std::flush;
+ } else {
+ std::cerr << std::endl;
+ }
+
+ CreateNewMediaTest();
+ Poll();
+ }
+
+ bool ConfigureIncomingConnection(
+ mojo::ApplicationConnection* connection) override {
+ return true;
+ }
+
+ private:
+ const char *kHome = "\r";
+ const char *kClearLine = "\033[K";
+ const char *kUp = "\033[A";
+ 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.
+
+ // Processes arguments.
+ void ProcessArgs(const std::vector<std::string>& args) {
+ if (args.size() <= 1) {
+ return;
+ }
+
+ 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.
+ if (arg == "--paint") {
+ paint_ = true;
+ } else if (arg == "--no-paint") {
+ paint_ = false;
+ } else {
+ input_file_names_.push_back(arg);
+ }
+ }
+
+ 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.
+
+ 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.
+ }
+
+ // Creates a new MediaTest object to play the file referenced by
+ // input_file_names_iter_.
+ 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.
+ media_test_ = MediaTest::Create(app_, *input_file_names_iter_);
+
+ metadata_shown_ = false;
+ 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.
+ if (media_test_->state() == mojo::media::MediaState::ENDED) {
+ // MediaTest doesn't appreciate being deleted in this callback.
+ // Next time Poll runs, we move on to the next file.
+ 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.
+ }
+
+ const mojo::media::MediaMetadataPtr& metadata = media_test_->metadata();
+
+ if (metadata) {
+ duration_ns_ = metadata->duration;
+ }
+
+ if (paint_) {
+ std::cerr << kHome
+ << 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.
+ }
+
+ if (!paint_ && metadata_shown_) {
+ // Do nothing.
+ } else if (metadata) {
+ metadata_shown_ = true;
+ std::cerr << " duration " << std::setprecision(1) <<
+ double(metadata->duration) / ns_per_second << " seconds"
+ << clear_line() << std::endl;
+ std::cerr << " title " <<
+ (metadata->title ? metadata->title : "<none>")
+ << clear_line() << std::endl;
+ std::cerr << " artist " <<
+ (metadata->artist ? metadata->artist : "<none>")
+ << clear_line() << std::endl;
+ std::cerr << " album " <<
+ (metadata->album ? metadata->album : "<none>")
+ << clear_line() << std::endl;
+ std::cerr << " publisher " <<
+ (metadata->publisher ? metadata->publisher : "<none>")
+ << clear_line() << std::endl;
+ std::cerr << " genre " <<
+ (metadata->genre ? metadata->genre : "<none>")
+ << clear_line() << std::endl;
+ std::cerr << " composer " <<
+ (metadata->composer ? metadata->composer : "<none>")
+ << clear_line() << std::endl << std::endl;
+ } else {
+ std::cerr << " duration <none>" << kClearLine << std::endl;
+ std::cerr << " title <none>" << kClearLine << std::endl;
+ std::cerr << " artist <none>" << kClearLine << std::endl;
+ std::cerr << " album <none>" << kClearLine << std::endl;
+ std::cerr << " publisher <none>" << kClearLine << std::endl;
+ std::cerr << " genre <none>" << kClearLine << std::endl;
+ std::cerr << " composer <none>" << kClearLine << std::endl;
+ std::cerr << std::endl;
+ }
+ std::cerr << " " << state_string() << clear_line() << std::endl;
+ });
+
+ media_test_->Play();
+ }
+
+ // Returns a string describing the MediaTest object's state.
+ const char* state_string() const {
+ switch (media_test_->state()) {
+ case mojo::media::MediaState::FAULT:
+ 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.
+ case mojo::media::MediaState::UNPREPARED:
+ return "unprepared";
+ case mojo::media::MediaState::PAUSED:
+ return "paused";
+ case mojo::media::MediaState::PLAYING:
+ return "playing";
+ case mojo::media::MediaState::ENDED:
+ return "ended";
+ }
+ return nullptr;
+ }
+
+ // Handles a keystroke.
+ void HandleKeystroke(char keystroke) {
+ switch (keystroke) {
+ case '\n':
+ TogglePlayPause();
+ break;
+ case 'q':
+ base::MessageLoop::current()->Quit();
+ quit_ = true;
+ if (paint_) {
+ std::cerr << kHome << kUp << " quitting" << kClearLine << std::endl
+ << kClearLine << std::endl;
+ } else {
+ std::cerr << " 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::cerr << " 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 mojo::media::MediaState::PAUSED:
+ media_test_->Play();
+ break;
+ case mojo::media::MediaState::PLAYING:
+ media_test_->Pause();
+ break;
+ case mojo::media::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() {
+ char keystroke = Keystroke();
+ if (keystroke != 0) {
+ HandleKeystroke(keystroke);
+ }
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
+
+ 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
+ // Eat the any additional keystrokes, which would otherwise make it to the
+ // command shell.
+ while (Keystroke() != 0) {
+ // Do nothing.
+ }
+ return;
+ }
+
+ if (play_next_) {
+ play_next_ = false;
+ if (++input_file_names_iter_ == input_file_names_.end()) {
+ input_file_names_iter_ = input_file_names_.begin();
+ } else {
+ CreateNewMediaTest();
+ }
+ }
+
+ if (paint_) {
+ std::cerr << 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));
+ }
+
+ 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 play_next_ = false;
+ bool quit_ = false;
+ bool paint_ = true;
+ bool metadata_shown_ = false;
+ uint64_t duration_ns_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaTestApp);
+};
+
+} // namespace examples
+
+MojoResult MojoMain(MojoHandle application_request) {
+ mojo::ApplicationRunnerChromium runner(new examples::MediaTestApp);
+ return runner.Run(application_request);
+}
« 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