Commit b2fd9098 authored by Thomas Guilbert's avatar Thomas Guilbert Committed by Commit Bot

Connect Java and native MediaStatusObservers

This CL adds the necessary plumbing to propagate MediaStatus updates
from Java to native code. All the layers between FlingingRenderer and
RemoteMediaPlayerWrapper are associated in a 1:1 capacity. This means
classes don't have to manage collections of observers and can keep
direct references instead.

Bug: 790766
Change-Id: I0e7b2fe061857a07f10149453b86ddb57dba46e5
Reviewed-on: https://chromium-review.googlesource.com/1147678
Commit-Queue: Thomas Guilbert <tguilbert@chromium.org>
Reviewed-by: default avatarTommy Nyquist <nyquist@chromium.org>
Reviewed-by: default avatarDerek Cheng <imcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583112}
parent eb46484d
...@@ -16,6 +16,8 @@ import org.chromium.base.Log; ...@@ -16,6 +16,8 @@ import org.chromium.base.Log;
import org.chromium.chrome.browser.media.router.CastSessionUtil; import org.chromium.chrome.browser.media.router.CastSessionUtil;
import org.chromium.chrome.browser.media.router.FlingingController; import org.chromium.chrome.browser.media.router.FlingingController;
import org.chromium.chrome.browser.media.router.MediaController; import org.chromium.chrome.browser.media.router.MediaController;
import org.chromium.chrome.browser.media.router.MediaStatusBridge;
import org.chromium.chrome.browser.media.router.MediaStatusObserver;
import org.chromium.chrome.browser.media.ui.MediaNotificationInfo; import org.chromium.chrome.browser.media.ui.MediaNotificationInfo;
import org.chromium.chrome.browser.media.ui.MediaNotificationManager; import org.chromium.chrome.browser.media.ui.MediaNotificationManager;
...@@ -34,6 +36,7 @@ public class RemoteMediaPlayerWrapper implements RemoteMediaPlayer.OnMetadataUpd ...@@ -34,6 +36,7 @@ public class RemoteMediaPlayerWrapper implements RemoteMediaPlayer.OnMetadataUpd
private GoogleApiClient mApiClient; private GoogleApiClient mApiClient;
private RemoteMediaPlayer mMediaPlayer; private RemoteMediaPlayer mMediaPlayer;
private MediaNotificationInfo.Builder mNotificationBuilder; private MediaNotificationInfo.Builder mNotificationBuilder;
private MediaStatusObserver mMediaStatusObserver;
public RemoteMediaPlayerWrapper(GoogleApiClient apiClient, public RemoteMediaPlayerWrapper(GoogleApiClient apiClient,
MediaNotificationInfo.Builder notificationBuilder, CastDevice castDevice) { MediaNotificationInfo.Builder notificationBuilder, CastDevice castDevice) {
...@@ -63,6 +66,10 @@ public class RemoteMediaPlayerWrapper implements RemoteMediaPlayer.OnMetadataUpd ...@@ -63,6 +66,10 @@ public class RemoteMediaPlayerWrapper implements RemoteMediaPlayer.OnMetadataUpd
MediaStatus mediaStatus = mMediaPlayer.getMediaStatus(); MediaStatus mediaStatus = mMediaPlayer.getMediaStatus();
if (mediaStatus == null) return; if (mediaStatus == null) return;
if (mMediaStatusObserver != null) {
mMediaStatusObserver.onMediaStatusUpdate(new MediaStatusBridge(mediaStatus));
}
int playerState = mediaStatus.getPlayerState(); int playerState = mediaStatus.getPlayerState();
if (playerState == MediaStatus.PLAYER_STATE_PAUSED if (playerState == MediaStatus.PLAYER_STATE_PAUSED
|| playerState == MediaStatus.PLAYER_STATE_PLAYING) { || playerState == MediaStatus.PLAYER_STATE_PLAYING) {
...@@ -220,4 +227,16 @@ public class RemoteMediaPlayerWrapper implements RemoteMediaPlayer.OnMetadataUpd ...@@ -220,4 +227,16 @@ public class RemoteMediaPlayerWrapper implements RemoteMediaPlayer.OnMetadataUpd
public long getApproximateCurrentTime() { public long getApproximateCurrentTime() {
return mMediaPlayer.getApproximateStreamPosition(); return mMediaPlayer.getApproximateStreamPosition();
} }
@Override
public void setMediaStatusObserver(MediaStatusObserver observer) {
assert mMediaStatusObserver == null;
mMediaStatusObserver = observer;
}
@Override
public void clearMediaStatusObserver() {
assert mMediaStatusObserver != null;
mMediaStatusObserver = null;
}
} }
...@@ -8,7 +8,6 @@ package org.chromium.chrome.browser.media.router; ...@@ -8,7 +8,6 @@ package org.chromium.chrome.browser.media.router;
* Interface that groups all the necessary hooks to control media being flung to a Cast device, * Interface that groups all the necessary hooks to control media being flung to a Cast device,
* as part of RemotePlayback. * as part of RemotePlayback.
* This interface should be the same as media/base/flinging_controller.h. * This interface should be the same as media/base/flinging_controller.h.
* TODO(tguilbert): add MediaStatusObserver.
*/ */
public interface FlingingController { public interface FlingingController {
/** /**
...@@ -16,6 +15,12 @@ public interface FlingingController { ...@@ -16,6 +15,12 @@ public interface FlingingController {
*/ */
public MediaController getMediaController(); public MediaController getMediaController();
/**
* Subscribe or unsubscribe to changes in the MediaStatus.
*/
public void setMediaStatusObserver(MediaStatusObserver observer);
public void clearMediaStatusObserver();
/** /**
* Gets the current media time. Implementers may sacrifice precision in order to avoid a * Gets the current media time. Implementers may sacrifice precision in order to avoid a
* round-trip query to Cast devices (see gms.cast.RemoteMediaPlayer's * round-trip query to Cast devices (see gms.cast.RemoteMediaPlayer's
......
...@@ -13,8 +13,9 @@ import org.chromium.base.annotations.JNINamespace; ...@@ -13,8 +13,9 @@ import org.chromium.base.annotations.JNINamespace;
* corresponding native code. * corresponding native code.
*/ */
@JNINamespace("media_router") @JNINamespace("media_router")
public class FlingingControllerBridge { public class FlingingControllerBridge implements MediaStatusObserver {
private final FlingingController mFlingingController; private final FlingingController mFlingingController;
private long mNativeFlingingControllerBridge;
public FlingingControllerBridge(FlingingController flingingController) { public FlingingControllerBridge(FlingingController flingingController) {
mFlingingController = flingingController; mFlingingController = flingingController;
...@@ -44,4 +45,27 @@ public class FlingingControllerBridge { ...@@ -44,4 +45,27 @@ public class FlingingControllerBridge {
public void seek(long positionInMs) { public void seek(long positionInMs) {
mFlingingController.getMediaController().seek(positionInMs); mFlingingController.getMediaController().seek(positionInMs);
} }
// MediaStatusObserver implementation.
@Override
public void onMediaStatusUpdate(MediaStatusBridge status) {
if (mNativeFlingingControllerBridge != 0) {
nativeOnMediaStatusUpdated(mNativeFlingingControllerBridge, status);
}
}
@CalledByNative
public void addNativeFlingingController(long nativeFlingingControllerBridge) {
mNativeFlingingControllerBridge = nativeFlingingControllerBridge;
mFlingingController.setMediaStatusObserver(this);
}
@CalledByNative
public void clearNativeFlingingController() {
mFlingingController.clearMediaStatusObserver();
mNativeFlingingControllerBridge = 0;
}
private native void nativeOnMediaStatusUpdated(
long nativeFlingingControllerBridge, MediaStatusBridge status);
} }
...@@ -58,11 +58,33 @@ media::MediaController* FlingingControllerBridge::GetMediaController() { ...@@ -58,11 +58,33 @@ media::MediaController* FlingingControllerBridge::GetMediaController() {
void FlingingControllerBridge::AddMediaStatusObserver( void FlingingControllerBridge::AddMediaStatusObserver(
media::MediaStatusObserver* observer) { media::MediaStatusObserver* observer) {
NOTREACHED(); DCHECK(!observer_);
observer_ = observer;
JNIEnv* env = base::android::AttachCurrentThread();
DCHECK(env);
Java_FlingingControllerBridge_addNativeFlingingController(
env, j_flinging_controller_bridge_, reinterpret_cast<intptr_t>(this));
} }
void FlingingControllerBridge::RemoveMediaStatusObserver( void FlingingControllerBridge::RemoveMediaStatusObserver(
media::MediaStatusObserver* observer) { media::MediaStatusObserver* observer) {
NOTREACHED(); DCHECK_EQ(observer_, observer);
observer_ = nullptr;
JNIEnv* env = base::android::AttachCurrentThread();
DCHECK(env);
Java_FlingingControllerBridge_clearNativeFlingingController(
env, j_flinging_controller_bridge_);
}
void FlingingControllerBridge::OnMediaStatusUpdated(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_bridge,
const base::android::JavaParamRef<jobject>& j_status) {
// TODO(tguilbert): convert j_status to media::MediaStatus.
} }
base::TimeDelta FlingingControllerBridge::GetApproximateCurrentTime() { base::TimeDelta FlingingControllerBridge::GetApproximateCurrentTime() {
......
...@@ -33,10 +33,21 @@ class FlingingControllerBridge : public media::FlingingController, ...@@ -33,10 +33,21 @@ class FlingingControllerBridge : public media::FlingingController,
void SetVolume(float volume) override; void SetVolume(float volume) override;
void Seek(base::TimeDelta time) override; void Seek(base::TimeDelta time) override;
// Called by the Java side on status updates.
void OnMediaStatusUpdated(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_bridge,
const base::android::JavaParamRef<jobject>& j_status);
private: private:
// Java MediaControllerBridge instance. // Java MediaControllerBridge instance.
base::android::ScopedJavaGlobalRef<jobject> j_flinging_controller_bridge_; base::android::ScopedJavaGlobalRef<jobject> j_flinging_controller_bridge_;
// Observer to be notified of media status changes from the Java side.
// NOTE: We don't manage a collection of observers because FlingingRenderer is
// the only observer that subscribes to |this|, with a 1:1 relationship.
media::MediaStatusObserver* observer_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(FlingingControllerBridge); DISALLOW_COPY_AND_ASSIGN(FlingingControllerBridge);
}; };
......
...@@ -16,9 +16,13 @@ namespace content { ...@@ -16,9 +16,13 @@ namespace content {
FlingingRenderer::FlingingRenderer( FlingingRenderer::FlingingRenderer(
std::unique_ptr<media::FlingingController> controller) std::unique_ptr<media::FlingingController> controller)
: controller_(std::move(controller)) {} : controller_(std::move(controller)) {
controller_->AddMediaStatusObserver(this);
}
FlingingRenderer::~FlingingRenderer() = default; FlingingRenderer::~FlingingRenderer() {
controller_->RemoveMediaStatusObserver(this);
};
// static // static
std::unique_ptr<FlingingRenderer> FlingingRenderer::Create( std::unique_ptr<FlingingRenderer> FlingingRenderer::Create(
...@@ -98,4 +102,8 @@ base::TimeDelta FlingingRenderer::GetMediaTime() { ...@@ -98,4 +102,8 @@ base::TimeDelta FlingingRenderer::GetMediaTime() {
return base::TimeDelta(); return base::TimeDelta();
} }
void FlingingRenderer::OnMediaStatusUpdated(const media::MediaStatus& status) {
// TODO(tguilbert): propagate important changes to RendererClient.
}
} // namespace content } // namespace content
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "media/base/flinging_controller.h" #include "media/base/flinging_controller.h"
#include "media/base/media_resource.h" #include "media/base/media_resource.h"
#include "media/base/media_status_observer.h"
#include "media/base/renderer.h" #include "media/base/renderer.h"
#include "media/base/renderer_client.h" #include "media/base/renderer_client.h"
#include "url/gurl.h" #include "url/gurl.h"
...@@ -23,7 +24,8 @@ class RenderFrameHost; ...@@ -23,7 +24,8 @@ class RenderFrameHost;
// playback commands. In this case, the media we are controlling should be an // playback commands. In this case, the media we are controlling should be an
// already existing RemotingCastSession, which should have been initiated by a // already existing RemotingCastSession, which should have been initiated by a
// blink::RemotePlayback object, using the PresentationService. // blink::RemotePlayback object, using the PresentationService.
class CONTENT_EXPORT FlingingRenderer : public media::Renderer { class CONTENT_EXPORT FlingingRenderer : public media::Renderer,
media::MediaStatusObserver {
public: public:
// Helper method to create a FlingingRenderer from an already existing // Helper method to create a FlingingRenderer from an already existing
// presentation ID. // presentation ID.
...@@ -47,6 +49,9 @@ class CONTENT_EXPORT FlingingRenderer : public media::Renderer { ...@@ -47,6 +49,9 @@ class CONTENT_EXPORT FlingingRenderer : public media::Renderer {
void SetVolume(float volume) override; void SetVolume(float volume) override;
base::TimeDelta GetMediaTime() override; base::TimeDelta GetMediaTime() override;
// media::MediaStatusObserver implementation.
void OnMediaStatusUpdated(const media::MediaStatus& status) override;
private: private:
friend class FlingingRendererTest; friend class FlingingRendererTest;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "content/browser/media/flinging_renderer.h" #include "content/browser/media/flinging_renderer.h"
#include "base/memory/ptr_util.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "base/version.h" #include "base/version.h"
#include "media/base/media_controller.h" #include "media/base/media_controller.h"
...@@ -44,14 +45,18 @@ class FlingingRendererTest : public testing::Test { ...@@ -44,14 +45,18 @@ class FlingingRendererTest : public testing::Test {
FlingingRendererTest() FlingingRendererTest()
: media_controller_(new StrictMock<MockMediaController>()), : media_controller_(new StrictMock<MockMediaController>()),
flinging_controller_( flinging_controller_(
new StrictMock<MockFlingingController>(media_controller_.get())), new StrictMock<MockFlingingController>(media_controller_.get())) {
renderer_( EXPECT_CALL(*flinging_controller_, AddMediaStatusObserver(_));
std::unique_ptr<media::FlingingController>(flinging_controller_)) {} EXPECT_CALL(*flinging_controller_, RemoveMediaStatusObserver(_));
renderer_ = base::WrapUnique(new FlingingRenderer(
std::unique_ptr<media::FlingingController>(flinging_controller_)));
}
protected: protected:
std::unique_ptr<MockMediaController> media_controller_; std::unique_ptr<MockMediaController> media_controller_;
StrictMock<MockFlingingController>* flinging_controller_; StrictMock<MockFlingingController>* flinging_controller_;
FlingingRenderer renderer_; std::unique_ptr<FlingingRenderer> renderer_;
}; };
TEST_F(FlingingRendererTest, StartPlayingFromTime) { TEST_F(FlingingRendererTest, StartPlayingFromTime) {
...@@ -59,28 +64,28 @@ TEST_F(FlingingRendererTest, StartPlayingFromTime) { ...@@ -59,28 +64,28 @@ TEST_F(FlingingRendererTest, StartPlayingFromTime) {
EXPECT_CALL(*media_controller_, Play()); EXPECT_CALL(*media_controller_, Play());
EXPECT_CALL(*media_controller_, Seek(seek_time)); EXPECT_CALL(*media_controller_, Seek(seek_time));
renderer_.StartPlayingFrom(seek_time); renderer_->StartPlayingFrom(seek_time);
} }
TEST_F(FlingingRendererTest, StartPlayingFromBeginning) { TEST_F(FlingingRendererTest, StartPlayingFromBeginning) {
EXPECT_CALL(*media_controller_, Play()); EXPECT_CALL(*media_controller_, Play());
EXPECT_CALL(*media_controller_, Seek(base::TimeDelta())); EXPECT_CALL(*media_controller_, Seek(base::TimeDelta()));
renderer_.StartPlayingFrom(base::TimeDelta()); renderer_->StartPlayingFrom(base::TimeDelta());
} }
TEST_F(FlingingRendererTest, SetPlaybackRate) { TEST_F(FlingingRendererTest, SetPlaybackRate) {
double playback_rate = 1.0; double playback_rate = 1.0;
EXPECT_CALL(*media_controller_, Play()); EXPECT_CALL(*media_controller_, Play());
renderer_.SetPlaybackRate(playback_rate); renderer_->SetPlaybackRate(playback_rate);
} }
TEST_F(FlingingRendererTest, SetPlaybackRateToZero) { TEST_F(FlingingRendererTest, SetPlaybackRateToZero) {
double playback_rate = 0.0; double playback_rate = 0.0;
EXPECT_CALL(*media_controller_, Pause()); EXPECT_CALL(*media_controller_, Pause());
renderer_.SetPlaybackRate(playback_rate); renderer_->SetPlaybackRate(playback_rate);
} }
// Setting the volume to a positive value should not change the mute state. // Setting the volume to a positive value should not change the mute state.
...@@ -89,7 +94,7 @@ TEST_F(FlingingRendererTest, SetVolume) { ...@@ -89,7 +94,7 @@ TEST_F(FlingingRendererTest, SetVolume) {
EXPECT_CALL(*media_controller_, SetVolume(volume)); EXPECT_CALL(*media_controller_, SetVolume(volume));
EXPECT_CALL(*media_controller_, SetMute(_)).Times(0); EXPECT_CALL(*media_controller_, SetMute(_)).Times(0);
renderer_.SetVolume(volume); renderer_->SetVolume(volume);
} }
// Setting the volume to 0 should not set the mute state. // Setting the volume to 0 should not set the mute state.
...@@ -98,7 +103,7 @@ TEST_F(FlingingRendererTest, SetVolumeToZero) { ...@@ -98,7 +103,7 @@ TEST_F(FlingingRendererTest, SetVolumeToZero) {
EXPECT_CALL(*media_controller_, SetVolume(volume)); EXPECT_CALL(*media_controller_, SetVolume(volume));
EXPECT_CALL(*media_controller_, SetMute(_)).Times(0); EXPECT_CALL(*media_controller_, SetMute(_)).Times(0);
renderer_.SetVolume(volume); renderer_->SetVolume(volume);
} }
} // namespace content } // namespace content
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define MEDIA_BASE_MEDIA_STATUS_H_ #define MEDIA_BASE_MEDIA_STATUS_H_
#include "base/time/time.h" #include "base/time/time.h"
#include "media/base/media_export.h"
namespace media { namespace media {
...@@ -13,7 +14,7 @@ namespace media { ...@@ -13,7 +14,7 @@ namespace media {
// interface. This is a copy of the media_router.mojom.MediaStatus interface, // interface. This is a copy of the media_router.mojom.MediaStatus interface,
// without the cast specific portions. // without the cast specific portions.
// TODO(https://crbug.com/820277): Deduplicate media_router::MediaStatus. // TODO(https://crbug.com/820277): Deduplicate media_router::MediaStatus.
struct MediaStatus { struct MEDIA_EXPORT MediaStatus {
public: public:
enum class PlayState { PLAYING, PAUSED, BUFFERING }; enum class PlayState { PLAYING, PAUSED, BUFFERING };
......
...@@ -9,9 +9,9 @@ ...@@ -9,9 +9,9 @@
namespace media { namespace media {
// Describes the current state of media being controlled via the MediaController // Describes the current state of media being controlled via the
// interface. This is a copy of the media_router.mojom.MediaStatus interface, // FlingingController interface. This is a copy of
// without the cast specific portions. // media_router.mojom.MediaStatus interface, without the cast specific portions.
// TODO(https://crbug.com/820277): Deduplicate media_router::MediaStatus. // TODO(https://crbug.com/820277): Deduplicate media_router::MediaStatus.
class MediaStatusObserver { class MediaStatusObserver {
public: public:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment