Skip to content

Commit

Permalink
[Media Router] Custom Controls 1 - Add MediaStatus, MediaRouteControl…
Browse files Browse the repository at this point in the history
…ler, and

mojo interfaces

This CL adds MediaRouteController that will forward media controller commands
from the custom controls WebUI to the Media Router component extension, and
will receive MediaStatus updates from the extension which it then will forward
to its Observer(s) (= to the WebUI).

This patch includes mojo interfaces/structs that were first reviewed in this
patch [1]. The MediaController mojo interface will be implemented in the
component extension (not a part of Chromium), and will receive commands from
MediaRouteController. MediaRouteController implements the MediaStatusObserver
mojo interface and receives updates from the extension. Typemapping between
media_router::mojom::MediaStatus and media_router::MediaStatus is also
included in this CL.

Changes in extensions/renderer/ are for exposing the new mojo interfaces to
the component extension.

The Chromium-side implementation of custom controls redesign will be done in
these patches:
1. Mojo/MediaStatus/MediaRouteController:     this patch
2. MediaRouter::GetRouteController():         http://crrev/2728543009
3. MRUI/MRWebUIMessageHandler:                http://crrev/2731033002
4. Custom controls WebUI:                     http://crrev/2725503002

Custom controls redesign design doc:
https://docs.google.com/document/d/1_8QxdFIiiJX39jR1Wi1Zn9FW-Y66EMvX1GmQZvjN4G0/edit

[1] https://codereview.chromium.org/2674363002/

BUG=684636,684635

Review-Url: https://codereview.chromium.org/2727123002
Cr-Commit-Position: refs/heads/master@{#458922}
  • Loading branch information
takumif authored and Commit bot committed Mar 22, 2017
1 parent 5a7f45d commit 67a8f64
Show file tree
Hide file tree
Showing 15 changed files with 647 additions and 2 deletions.
17 changes: 17 additions & 0 deletions chrome/browser/media/router/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import("//testing/test.gni")

static_library("router") {
deps = [
":mojo_bindings_common",
"//base",
"//chrome/common:constants",
"//components/keyed_service/content",
Expand Down Expand Up @@ -54,6 +55,10 @@ static_library("router") {
"media_source.h",
"media_source_helper.cc",
"media_source_helper.h",
"media_status.cc",
"media_status.h",
"mojo/media_route_controller.cc",
"mojo/media_route_controller.h",
"offscreen_presentation_manager.cc",
"offscreen_presentation_manager.h",
"offscreen_presentation_manager_factory.cc",
Expand Down Expand Up @@ -100,12 +105,24 @@ static_library("router") {
}
}

mojom("mojo_bindings_common") {
sources = [
"mojo/media_controller.mojom",
"mojo/media_status.mojom",
]

public_deps = [
"//mojo/common:common_custom_types",
]
}

mojom("mojo_bindings") {
sources = [
"mojo/media_router.mojom",
]

public_deps = [
":mojo_bindings_common",
"//mojo/common:common_custom_types",
"//net/interfaces:interfaces",
"//url/mojo:url_mojom_gurl",
Expand Down
26 changes: 26 additions & 0 deletions chrome/browser/media/router/media_status.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2017 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 "chrome/browser/media/router/media_status.h"

namespace media_router {

MediaStatus::MediaStatus() = default;

MediaStatus::MediaStatus(const MediaStatus& other) = default;

MediaStatus::~MediaStatus() = default;

MediaStatus& MediaStatus::operator=(const MediaStatus& other) = default;

bool MediaStatus::operator==(const MediaStatus& other) const {
return title == other.title && description == other.description &&
can_play_pause == other.can_play_pause && can_mute == other.can_mute &&
can_set_volume == other.can_set_volume && can_seek == other.can_seek &&
is_paused == other.is_paused && is_muted == other.is_muted &&
volume == other.volume && duration == other.duration &&
current_time == other.current_time;
}

} // namespace media_router
63 changes: 63 additions & 0 deletions chrome/browser/media/router/media_status.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2017 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 CHROME_BROWSER_MEDIA_ROUTER_MEDIA_STATUS_H_
#define CHROME_BROWSER_MEDIA_ROUTER_MEDIA_STATUS_H_

#include <string>

#include "base/time/time.h"

namespace media_router {

// Represents the current state of a media content.
struct MediaStatus {
public:
MediaStatus();
MediaStatus(const MediaStatus& other);
~MediaStatus();

MediaStatus& operator=(const MediaStatus& other);
bool operator==(const MediaStatus& other) const;

// The main title of the media. For example, in a MediaStatus representing
// a YouTube Cast session, this could be the title of the video.
std::string title;

// Text describing the media, or a secondary title. For example, in a
// MediaStatus representing a YouTube Cast session, this could be "YouTube".
std::string description;

// If this is true, the media can be played and paused.
bool can_play_pause = false;

// If this is true, the media can be muted and unmuted.
bool can_mute = false;

// If this is true, the media's volume can be changed.
bool can_set_volume = false;

// If this is true, the media's current playback position can be changed.
bool can_seek = false;

bool is_paused = false;

bool is_muted = false;

// Current volume of the media, with 1 being the highest and 0 being the
// lowest/no sound. When |is_muted| is true, there should be no sound
// regardless of |volume|.
float volume = 0;

// The length of the media. A value of zero indicates that this is a media
// with no set duration (e.g. a live stream).
base::TimeDelta duration;

// Current playback position. Must be less than or equal to |duration|.
base::TimeDelta current_time;
};

} // namespace media_router

#endif // CHROME_BROWSER_MEDIA_ROUTER_MEDIA_STATUS_H_
35 changes: 35 additions & 0 deletions chrome/browser/media/router/mojo/media_controller.mojom
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2017 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.

module media_router.mojom;

import "chrome/browser/media/router/mojo/media_status.mojom";
import "mojo/common/time.mojom";

// Interface for a controller to change the current state of a media content.
// This interface should be kept free of details specific to Media Router, so
// that it can be moved to the media namespace and be reused for other features
// in the future.
interface MediaController {
// Starts playing the media if it is paused. Is a no-op if not supported by
// the media or the media is already playing.
Play();

// Pauses the media if it is playing. Is a no-op if not supported by the media
// or the media is already paused.
Pause();

// Mutes the media if |mute| is true, and unmutes it if false. Is a no-op if
// not supported by the media.
SetMute(bool mute);

// Changes the current volume of the media, with 1 being the highest and 0
// being the lowest/no sound. Does not change the (un)muted state of the
// media. Is a no-op if not supported by the media.
SetVolume(float volume);

// Sets the current playback position. |time| must be less than or equal to
// the duration of the media. Is a no-op if the media doesn't support seeking.
Seek(mojo.common.mojom.TimeDelta time);
};
80 changes: 80 additions & 0 deletions chrome/browser/media/router/mojo/media_route_controller.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2017 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 "chrome/browser/media/router/mojo/media_route_controller.h"

#include <utility>

namespace media_router {

MediaRouteController::Observer::Observer(
scoped_refptr<MediaRouteController> controller)
: controller_(std::move(controller)) {
controller_->AddObserver(this);
}

MediaRouteController::Observer::~Observer() {
if (controller_)
controller_->RemoveObserver(this);
}

void MediaRouteController::Observer::InvalidateController() {
controller_->RemoveObserver(this);
controller_ = nullptr;
OnControllerInvalidated();
}

void MediaRouteController::Observer::OnControllerInvalidated() {}

MediaRouteController::MediaRouteController(
const MediaRoute::Id& route_id,
mojom::MediaControllerPtr media_controller)
: route_id_(route_id), media_controller_(std::move(media_controller)) {
DCHECK(media_controller_.is_bound());
media_controller_.set_connection_error_handler(
base::Bind(&MediaRouteController::Invalidate, base::Unretained(this)));
}

void MediaRouteController::Play() {
media_controller_->Play();
}

void MediaRouteController::Pause() {
media_controller_->Pause();
}

void MediaRouteController::Seek(base::TimeDelta time) {
media_controller_->Seek(time);
}

void MediaRouteController::SetMute(bool mute) {
media_controller_->SetMute(mute);
}

void MediaRouteController::SetVolume(float volume) {
media_controller_->SetVolume(volume);
}

void MediaRouteController::OnMediaStatusUpdated(const MediaStatus& status) {
for (Observer& observer : observers_)
observer.OnMediaStatusUpdated(status);
}

void MediaRouteController::Invalidate() {
for (Observer& observer : observers_)
observer.InvalidateController();
// |this| is deleted here!
}

MediaRouteController::~MediaRouteController() {}

void MediaRouteController::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}

void MediaRouteController::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}

} // namespace media_router
115 changes: 115 additions & 0 deletions chrome/browser/media/router/mojo/media_route_controller.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright 2017 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 CHROME_BROWSER_MEDIA_ROUTER_MOJO_MEDIA_ROUTE_CONTROLLER_H_
#define CHROME_BROWSER_MEDIA_ROUTER_MOJO_MEDIA_ROUTE_CONTROLLER_H_

#include <memory>

#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
#include "chrome/browser/media/router/media_route.h"
#include "chrome/browser/media/router/mojo/media_controller.mojom.h"
#include "mojo/public/cpp/bindings/binding.h"

namespace media_router {

// A controller for a MediaRoute. Forwards commands for controlling the route to
// an out-of-process controller. Notifies its observers whenever there is a
// change in the route's MediaStatus.
//
// It is owned by its observers, each of which holds a scoped_refptr to it. All
// the classes that hold a scoped_refptr must inherit from the Observer class.
// An observer should be instantiated with a scoped_refptr obtained through
// MediaRouter::GetRouteController().
//
// A MediaRouteController instance is destroyed when all its observers dispose
// their references to it. When the Mojo connection with the out-of-process
// controller is terminated or has an error, OnControllerInvalidated() will be
// called by the MediaRouter or a Mojo error handler to make observers dispose
// their refptrs.
class MediaRouteController : public mojom::MediaStatusObserver,
public base::RefCounted<MediaRouteController> {
public:
// Observes MediaRouteController for MediaStatus updates. The ownership of a
// MediaRouteController is shared by its observers.
class Observer {
public:
// Adds itself as an observer to |controller|.
explicit Observer(scoped_refptr<MediaRouteController> controller);

// Removes itself as an observer if |controller_| is still valid.
virtual ~Observer();

virtual void OnMediaStatusUpdated(const MediaStatus& status) = 0;

// Returns a reference to the observed MediaRouteController. The reference
// should not be stored by any object that does not subclass ::Observer.
scoped_refptr<MediaRouteController> controller() const {
return controller_;
}

private:
friend class MediaRouteController;

// Disposes the reference to the controller.
void InvalidateController();

// Called by InvalidateController() after the reference to the controller is
// disposed. Overridden by subclasses to do custom cleanup.
virtual void OnControllerInvalidated();

scoped_refptr<MediaRouteController> controller_;

DISALLOW_COPY_AND_ASSIGN(Observer);
};

// Constructs a MediaRouteController that forwards media commands to
// |media_controller|. |media_controller| must be bound to a message pipe.
MediaRouteController(const MediaRoute::Id& route_id,
mojom::MediaControllerPtr media_controller);

// Media controller methods for forwarding commands to a
// mojom::MediaControllerPtr held in |media_controller_|.
void Play();
void Pause();
void Seek(base::TimeDelta time);
void SetMute(bool mute);
void SetVolume(float volume);

// mojom::MediaStatusObserver:
// Notifies |observers_| of a status update.
void OnMediaStatusUpdated(const MediaStatus& status) override;

// Called when the connection between |this| and |media_controller_| is no
// longer valid. Notifies |observers_| to dispose their references to |this|.
// |this| gets destroyed when all the references are disposed.
void Invalidate();

MediaRoute::Id route_id() const { return route_id_; }

private:
friend class base::RefCounted<MediaRouteController>;

~MediaRouteController() override;

void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);

// The ID of the Media Route that |this| controls.
const MediaRoute::Id route_id_;

// Handle to the mojom::MediaController that receives media commands.
mojom::MediaControllerPtr media_controller_;

// Observers that |this| notifies of status updates. The observers share the
// ownership of |this| through scoped_refptr.
base::ObserverList<Observer> observers_;

DISALLOW_COPY_AND_ASSIGN(MediaRouteController);
};

} // namespace media_router

#endif // CHROME_BROWSER_MEDIA_ROUTER_MOJO_MEDIA_ROUTE_CONTROLLER_H_
Loading

0 comments on commit 67a8f64

Please sign in to comment.