Motown: Add examples/media_test, a command line media player app.

R=johngro@google.com

Review URL: https://codereview.chromium.org/1809703003 .
diff --git a/examples/BUILD.gn b/examples/BUILD.gn
index 8a5b07e..e93c3c3 100644
--- a/examples/BUILD.gn
+++ b/examples/BUILD.gn
@@ -22,6 +22,7 @@
     "//examples/hello_mojo",
     "//examples/http_handler",
     "//examples/indirect_service",
+    "//examples/media_test",
     "//examples/native_run_app",
     "//examples/notification_generator",
     "//examples/recursive_content_handler",
diff --git a/examples/media_test/BUILD.gn b/examples/media_test/BUILD.gn
new file mode 100644
index 0000000..55a3078
--- /dev/null
+++ b/examples/media_test/BUILD.gn
@@ -0,0 +1,28 @@
+# 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.
+
+import("//mojo/public/mojo_application.gni")
+
+mojo_native_application("media_test") {
+  sources = [
+    "keystroke.cc",
+    "keystroke.h",
+    "media_test.cc",
+    "media_test.h",
+    "media_test_app.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/environment:chromium",
+    "//mojo/gpu",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/utility",
+    "//mojo/public/interfaces/application",
+    "//mojo/services/media/common/cpp",
+    "//mojo/services/media/common/interfaces",
+    "//mojo/services/media/control/interfaces",
+  ]
+}
diff --git a/examples/media_test/keystroke.cc b/examples/media_test/keystroke.cc
new file mode 100644
index 0000000..9e12063
--- /dev/null
+++ b/examples/media_test/keystroke.cc
@@ -0,0 +1,52 @@
+// 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 <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <iostream>
+
+#include "examples/media_test/keystroke.h"
+
+namespace mojo {
+namespace media {
+namespace examples {
+
+namespace {
+
+bool eat_newline = false;
+bool upped_already = false;
+
+} // namespace
+
+char Keystroke(void) {
+  const char *kUp = "\033[A";
+
+  fcntl(STDIN_FILENO, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
+  char buf[1];
+  if (read(STDIN_FILENO, buf, 1) > 0) {
+    if (!upped_already) {
+      std::cout << kUp << std::flush;
+      upped_already = true;
+    }
+
+    if (buf[0] == '\n') {
+      upped_already = false;
+      if (eat_newline) {
+        eat_newline = false;
+      } else {
+        return buf[0];
+      }
+    } else {
+      eat_newline = true;
+      return buf[0];
+    }
+  }
+  return 0;
+}
+
+}  // namespace examples
+}  // namespace media
+}  // namespace mojo
diff --git a/examples/media_test/keystroke.h b/examples/media_test/keystroke.h
new file mode 100644
index 0000000..7da4b63
--- /dev/null
+++ b/examples/media_test/keystroke.h
@@ -0,0 +1,22 @@
+// 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.
+
+#ifndef EXAMPLES_MEDIA_TEST_KEYSTROKE_H_
+#define EXAMPLES_MEDIA_TEST_KEYSTROKE_H_
+
+namespace mojo {
+namespace media {
+namespace examples {
+
+// Returns keystroke or 0 if no key has been pressed. This is non-blocking,
+// but requires the user to hit enter and doesn't suppress echo. Enter alone
+// produces a newline ('\n'). If non-newline characters are entered, the
+// terminating newline is suppressed.
+char Keystroke();
+
+}  // namespace examples
+}  // namespace media
+}  // namespace mojo
+
+#endif  // EXAMPLES_MEDIA_TEST_KEYSTROKE_H_
diff --git a/examples/media_test/media_test.cc b/examples/media_test/media_test.cc
new file mode 100644
index 0000000..206e959
--- /dev/null
+++ b/examples/media_test/media_test.cc
@@ -0,0 +1,111 @@
+// 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 "examples/media_test/media_test.h"
+#include "mojo/services/media/common/cpp/linear_transform.h"
+#include "mojo/services/media/common/cpp/local_time.h"
+#include "mojo/services/media/control/interfaces/media_factory.mojom.h"
+
+namespace mojo {
+namespace media {
+namespace examples {
+
+// static
+std::unique_ptr<MediaTest> MediaTest::Create(
+    mojo::ApplicationImpl* app,
+    const std::string& input_file_name) {
+  return std::unique_ptr<MediaTest>(new MediaTest(app, input_file_name));
+}
+
+MediaTest::MediaTest(
+    mojo::ApplicationImpl* app,
+    const std::string& input_file_name) :
+    state_(MediaState::UNPREPARED) {
+  MediaFactoryPtr factory;
+  app->ConnectToService("mojo:media_factory", &factory);
+
+  factory->CreatePlayer(input_file_name, GetProxy(&media_player_));
+
+  HandleStatusUpdates();
+}
+
+MediaTest::~MediaTest() {}
+
+void MediaTest::RegisterUpdateCallback(const UpdateCallback& callback) {
+  update_callback_ = callback;
+}
+
+void MediaTest::Play() {
+  media_player_->Play();
+}
+
+void MediaTest::Pause() {
+  media_player_->Pause();
+}
+
+void MediaTest::Seek(int64_t position_ns) {
+  media_player_->Seek(position_ns);
+}
+
+MediaState MediaTest::state() const {
+  return state_;
+}
+
+int64_t MediaTest::position_ns() const {
+  // Apply the transform to the current time.
+  int64_t position;
+  transform_.DoForwardTransform(
+      LocalClock::now().time_since_epoch().count(),
+      &position);
+
+  MOJO_DCHECK(position >= 0);
+
+  if (metadata_ &&
+      static_cast<uint64_t>(position) > metadata_->duration) {
+    position = metadata_->duration;
+  }
+
+  return position;
+}
+
+const MediaMetadataPtr& MediaTest::metadata() const {
+  return metadata_;
+}
+
+void MediaTest::HandleStatusUpdates(
+    uint64_t version,
+    MediaPlayerStatusPtr status) {
+  if (status) {
+    // Process status received from the player.
+    state_ = status->state;
+
+    // Create a linear transform that translates local time to presentation
+    // time. Note that 'reference' here refers to the presentation time, and
+    // 'target' refers to the local time.
+    if (status->timeline_transform) {
+      transform_ = LinearTransform(
+          status->timeline_transform->quad->target_offset,
+          status->timeline_transform->quad->reference_delta,
+          status->timeline_transform->quad->target_delta,
+          status->timeline_transform->quad->reference_offset);
+    }
+
+    metadata_ = status->metadata.Pass();
+
+    if (update_callback_ != nullptr) {
+      update_callback_();
+    }
+  }
+
+  // Request a status update.
+  media_player_->GetStatus(
+      version,
+      [this](uint64_t version, MediaPlayerStatusPtr status) {
+        HandleStatusUpdates(version, status.Pass());
+      });
+}
+
+}  // namespace examples
+}  // namespace media
+}  // namespace mojo
diff --git a/examples/media_test/media_test.h b/examples/media_test/media_test.h
new file mode 100644
index 0000000..2c03118
--- /dev/null
+++ b/examples/media_test/media_test.h
@@ -0,0 +1,73 @@
+// 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.
+
+#ifndef EXAMPLES_MEDIA_TEST_MEDIA_TEST_H_
+#define EXAMPLES_MEDIA_TEST_MEDIA_TEST_H_
+
+#include "base/macros.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/services/media/common/cpp/linear_transform.h"
+#include "mojo/services/media/common/interfaces/rate_control.mojom.h"
+#include "mojo/services/media/control/interfaces/media_factory.mojom.h"
+#include "mojo/services/media/control/interfaces/media_player.mojom.h"
+
+namespace mojo {
+namespace media {
+namespace examples {
+
+// Model for media test application.
+class MediaTest {
+ public:
+  using UpdateCallback = std::function<void()>;
+
+  static std::unique_ptr<MediaTest> Create(
+      mojo::ApplicationImpl* app,
+      const std::string& input_file_name);
+
+  ~MediaTest();
+
+  // Registers a callback signalling that the app should update its view.
+  void RegisterUpdateCallback(const UpdateCallback& callback);
+
+  // Starts playback.
+  void Play();
+
+  // Pauses playback.
+  void Pause();
+
+  // Seeks to the position indicated in nanoseconds from the start of the media.
+  void Seek(int64_t position_ns);
+
+  // Returns the current state of the player.
+  MediaState state() const;
+
+  // Returns the current presentation time in nanoseconds.
+  int64_t position_ns() const;
+
+  // Returns the current media metadata, if there is any.
+  const MediaMetadataPtr& metadata() const;
+
+ private:
+  MediaTest(mojo::ApplicationImpl* app, const std::string& input_file_name);
+
+  // Handles a status update from the player. When called with the default
+  // argument values, initiates status updates.
+  void HandleStatusUpdates(
+      uint64_t version = MediaPlayer::kInitialStatus,
+      MediaPlayerStatusPtr status = nullptr);
+
+  MediaPlayerPtr media_player_;
+  MediaState state_;
+  LinearTransform transform_ = LinearTransform(0, 0, 1, 0);
+  MediaMetadataPtr metadata_;
+  UpdateCallback update_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaTest);
+};
+
+}  // namespace examples
+}  // namespace media
+}  // namespace mojo
+
+#endif  // EXAMPLES_MEDIA_TEST_MEDIA_TEST_H_
diff --git a/examples/media_test/media_test_app.cc b/examples/media_test/media_test_app.cc
new file mode 100644
index 0000000..b916f61
--- /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);
+}
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/media/media_player.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/media/media_player.mojom.dart
index 29293e0..87f4dc8 100644
--- a/mojo/dart/packages/mojo_services/lib/mojo/media/media_player.mojom.dart
+++ b/mojo/dart/packages/mojo_services/lib/mojo/media/media_player.mojom.dart
@@ -487,6 +487,7 @@
   void pause();
   void seek(int position);
   dynamic getStatus(int versionLastSeen,[Function responseFactory = null]);
+  static const int kInitialStatus = 0;
 }
 
 
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/media/media_sink.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/media/media_sink.mojom.dart
index f4a441a..ae1aa27 100644
--- a/mojo/dart/packages/mojo_services/lib/mojo/media/media_sink.mojom.dart
+++ b/mojo/dart/packages/mojo_services/lib/mojo/media/media_sink.mojom.dart
@@ -756,6 +756,7 @@
   dynamic getStatus(int versionLastSeen,[Function responseFactory = null]);
   void play();
   void pause();
+  static const int kInitialStatus = 0;
 }
 
 
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/media/media_source.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/media/media_source.mojom.dart
index 284f295..bc7d94d 100644
--- a/mojo/dart/packages/mojo_services/lib/mojo/media/media_source.mojom.dart
+++ b/mojo/dart/packages/mojo_services/lib/mojo/media/media_source.mojom.dart
@@ -1471,6 +1471,7 @@
   dynamic prime([Function responseFactory = null]);
   dynamic flush([Function responseFactory = null]);
   dynamic seek(int position,[Function responseFactory = null]);
+  static const int kInitialStatus = 0;
 }
 
 
diff --git a/mojo/services/media/control/interfaces/media_player.mojom b/mojo/services/media/control/interfaces/media_player.mojom
index 12cad67..f9907bb 100644
--- a/mojo/services/media/control/interfaces/media_player.mojom
+++ b/mojo/services/media/control/interfaces/media_player.mojom
@@ -11,6 +11,10 @@
 
 // Plays media.
 interface MediaPlayer {
+  // Special value for GetStatus version_last_seen parameter to get the current
+  // status immediately.
+  const uint64 kInitialStatus = 0;
+
   // Starts playback.
   Play();
 
@@ -22,8 +26,9 @@
   // TODO(dalesat): Consider adding parameters regarding desired precision.
   Seek(int64 position);
 
-  // Gets the status. To get the status immediately, call GetStatus(0). To
-  // get updates thereafter, pass the version sent in the previous callback.
+  // Gets the status. To get the status immediately, call
+  // GetStatus(kInitialStatus). To get updates thereafter, pass the version
+  // sent in the previous callback.
   GetStatus(uint64 version_last_seen) =>
       (uint64 version, MediaPlayerStatus status);
 };
diff --git a/mojo/services/media/control/interfaces/media_sink.mojom b/mojo/services/media/control/interfaces/media_sink.mojom
index efaa630..14c2f40 100644
--- a/mojo/services/media/control/interfaces/media_sink.mojom
+++ b/mojo/services/media/control/interfaces/media_sink.mojom
@@ -18,6 +18,10 @@
 interface MediaSink {
   // TODO(dalesat): Support fanout to many destinations.
 
+  // Special value for GetStatus version_last_seen parameter to get the current
+  // status immediately.
+  const uint64 kInitialStatus = 0;
+
   // Gets the clock disposition from the source.
   GetClockDisposition() => (ClockDisposition clock_disposition);
 
@@ -30,8 +34,9 @@
   // Gets the consumer for the stream to be delivered.
   GetConsumer(MediaConsumer& consumer);
 
-  // Gets the status. To get the status immediately, call GetStatus(0). To
-  // get updates thereafter, pass the version sent in the previous callback.
+  // Gets the status. To get the status immediately, call
+  // GetStatus(kInitialStatus). To get updates thereafter, pass the version
+  // sent in the previous callback.
   GetStatus(uint64 version_last_seen) =>
       (uint64 version, MediaSinkStatus status);
 
diff --git a/mojo/services/media/control/interfaces/media_source.mojom b/mojo/services/media/control/interfaces/media_source.mojom
index a4cebd3..59f40c4 100644
--- a/mojo/services/media/control/interfaces/media_source.mojom
+++ b/mojo/services/media/control/interfaces/media_source.mojom
@@ -14,6 +14,10 @@
 
 // Produces media streams delivered from a specified origin.
 interface MediaSource {
+  // Special value for GetStatus version_last_seen parameter to get the current
+  // status immediately.
+  const uint64 kInitialStatus = 0;
+
   // Gets the streams produced by this source.
   GetStreams() => (array<MediaSourceStreamDescriptor> streams);
 
@@ -32,8 +36,9 @@
   // Gets the pull mode producer for the specified stream.
   GetPullModeProducer(uint32 stream_index, MediaPullModeProducer& producer);
 
-  // Gets the status. To get the status immediately, call GetStatus(0). To
-  // get updates thereafter, pass the version sent in the previous callback.
+  // Gets the status. To get the status immediately, call
+  // GetStatus(kInitialStatus). To get updates thereafter, pass the version
+  // sent in the previous callback.
   GetStatus(uint64 version_last_seen) =>
       (uint64 version, MediaSourceStatus status);
 
diff --git a/services/media/factory_service/media_player_impl.h b/services/media/factory_service/media_player_impl.h
index d871eba..b3b3c9e 100644
--- a/services/media/factory_service/media_player_impl.h
+++ b/services/media/factory_service/media_player_impl.h
@@ -103,14 +103,14 @@
   // Handles a status update from the source. When called with the default
   // argument values, initiates source status updates.
   void HandleSourceStatusUpdates(
-      uint64_t version = 0,
+      uint64_t version = MediaSource::kInitialStatus,
       MediaSourceStatusPtr status = nullptr);
 
   // Handles a status update from a sink. When called with the default
   // argument values, initiates sink status updates.
   void HandleSinkStatusUpdates(
       const std::unique_ptr<Stream>& stream,
-      uint64_t version = 0,
+      uint64_t version = MediaSink::kInitialStatus,
       MediaSinkStatusPtr status = nullptr);
 
   Event event_;
diff --git a/services/media/framework_ffmpeg/ffmpeg_demux.cc b/services/media/framework_ffmpeg/ffmpeg_demux.cc
index e71ebfa..a00a2a7 100644
--- a/services/media/framework_ffmpeg/ffmpeg_demux.cc
+++ b/services/media/framework_ffmpeg/ffmpeg_demux.cc
@@ -76,7 +76,7 @@
             static_cast<size_t>(av_packet->size),
             av_packet->data),
         av_packet_(std::move(av_packet)) {
-      DCHECK(av_packet->size >= 0);
+      DCHECK(av_packet_->size >= 0);
     }
 
     ffmpeg::AvPacketPtr av_packet_;