blob: 0c42f22387e9ee2647b465774233c030d333351b [file] [log] [blame]
// 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_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
<< "mojo/devtools/common/mojo_run \\" << std::endl
<< " \"https://core.mojoapps.io/media_test.mojo \\"
<< std::endl
<< " http:/localhost/superstition.ogg \\" << std::endl
<< " http:/localhost/higherground.ogg\"" << std::endl
<< std::endl;
base::MessageLoop::current()->Quit();
return;
}
std::cout << " <enter> play/pause" << std::endl
<< " n<enter> next file" << std::endl
<< " p<enter> previous file" << std::endl
<< " <digit><enter> seek (0% - 90%)" << std::endl
<< " q<enter> quit" << std::endl
<< std::endl;
if (paint_) {
std::cout << " duration <none>" << std::endl
<< " title <none>" << std::endl
<< " artist <none>" << std::endl
<< " album <none>" << std::endl
<< " publisher <none>" << std::endl
<< " genre <none>" << std::endl
<< " composer <none>" << std::endl
<< std::endl
<< std::endl
<< std::endl
<< kUp << std::flush;
}
CreateNewMediaTest();
Poll();
}
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 &&
media_test_->previous_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::fixed << std::setprecision(1)
<< double(metadata->duration) / ns_per_second << " seconds"
<< clear_line() << std::endl
<< " title "
<< (metadata->title ? metadata->title : "<none>")
<< clear_line() << std::endl
<< " artist "
<< (metadata->artist ? metadata->artist : "<none>")
<< clear_line() << std::endl
<< " album "
<< (metadata->album ? metadata->album : "<none>")
<< clear_line() << std::endl
<< " publisher "
<< (metadata->publisher ? metadata->publisher : "<none>")
<< clear_line() << std::endl
<< " genre "
<< (metadata->genre ? metadata->genre : "<none>")
<< clear_line() << std::endl
<< " composer "
<< (metadata->composer ? metadata->composer : "<none>")
<< clear_line() << std::endl
<< std::endl;
} else if (paint_) {
std::cout << " duration <none>" << kClearLine << std::endl
<< " title <none>" << kClearLine << std::endl
<< " artist <none>" << kClearLine << std::endl
<< " album <none>" << kClearLine << std::endl
<< " publisher <none>" << kClearLine << std::endl
<< " genre <none>" << kClearLine << std::endl
<< " composer <none>" << kClearLine << std::endl
<< 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);
}