Bug 1382151 - Pause Exoplayer when mediaelement is paused.
MozReview-Commit-ID: 5MDBBP5vfpa
--- a/dom/media/hls/HLSDecoder.cpp
+++ b/dom/media/hls/HLSDecoder.cpp
@@ -87,9 +87,31 @@ HLSDecoder::Load(nsIChannel* aChannel,
nsresult
HLSDecoder::Load(MediaResource*)
{
MOZ_CRASH("Clone is not supported");
return NS_ERROR_FAILURE;
}
+nsresult
+HLSDecoder::Play()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ HLS_DEBUG("HLSDecoder", "MediaElement called Play");
+ auto resourceWrapper =
+ static_cast<HLSResource*>(GetResource())->GetResourceWrapper();
+ resourceWrapper->Play();
+ return MediaDecoder::Play();
+}
+
+void
+HLSDecoder::Pause()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ HLS_DEBUG("HLSDecoder", "MediaElement called Pause");
+ auto resourceWrapper =
+ static_cast<HLSResource*>(GetResource())->GetResourceWrapper();
+ resourceWrapper->Pause();
+ return MediaDecoder::Pause();
+}
+
} // namespace mozilla
--- a/dom/media/hls/HLSDecoder.h
+++ b/dom/media/hls/HLSDecoder.h
@@ -32,13 +32,17 @@ public:
// with the a platform decoder backend.
// If provided, codecs are checked for support.
static bool IsSupportedType(const MediaContainerType& aContainerType);
nsresult Load(nsIChannel* aChannel,
bool aIsPrivateBrowsing,
nsIStreamListener**) override;
nsresult Load(MediaResource*) override;
+
+ nsresult Play() override;
+
+ void Pause() override;
};
} // namespace mozilla
#endif /* HLSDecoder_h_ */
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/BaseHlsPlayer.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/BaseHlsPlayer.java
@@ -83,10 +83,14 @@ public interface BaseHlsPlayer {
public boolean seek(long positionUs);
public long getNextKeyFrameTime();
public void suspend();
public void resume();
+ public void play();
+
+ public void pause();
+
public void release();
}
\ No newline at end of file
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHLSResourceWrapper.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHLSResourceWrapper.java
@@ -74,16 +74,32 @@ public class GeckoHLSResourceWrapper {
@WrapForJNI(calledFrom = "gecko")
public void resume() {
if (DEBUG) Log.d(LOGTAG, "GeckoHLSResourceWrapper resume");
if (mPlayer != null) {
mPlayer.resume();
}
}
+ @WrapForJNI(calledFrom = "gecko")
+ public void play() {
+ if (DEBUG) Log.d(LOGTAG, "GeckoHLSResourceWrapper mediaelement played");
+ if (mPlayer != null) {
+ mPlayer.play();
+ }
+ }
+
+ @WrapForJNI(calledFrom = "gecko")
+ public void pause() {
+ if (DEBUG) Log.d(LOGTAG, "GeckoHLSResourceWrapper mediaelement paused");
+ if (mPlayer != null) {
+ mPlayer.pause();
+ }
+ }
+
private static void assertTrue(boolean condition) {
if (DEBUG && !condition) {
throw new AssertionError("Expected condition to be true");
}
}
@WrapForJNI // Called when native object is mDestroy.
private void destroy() {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHlsPlayer.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHlsPlayer.java
@@ -55,17 +55,18 @@ public class GeckoHlsPlayer implements B
* Because we treat GeckoHlsPlayer as a source data provider.
* It will be created and initialized with a URL by HLSResource in
* Gecko media pipleine (in cpp). Once HLSDemuxer is created later, we
* need to bridge this HLSResource to the created demuxer. And they share
* the same GeckoHlsPlayer.
* mPlayerId is a token used for Gecko media pipeline to obtain corresponding player.
*/
private final int mPlayerId;
- private volatile boolean mSuspended = false;
+ private boolean mExoplayerSuspended = false;
+ private boolean mMediaElementSuspended = false;
private DataSource.Factory mMediaDataSourceFactory;
private Handler mMainHandler;
private HandlerThread mThread;
private ExoPlayer mPlayer;
private GeckoHlsRendererBase[] mRenderers;
private DefaultTrackSelector mTrackSelector;
private MediaSource mMediaSource;
@@ -307,27 +308,30 @@ public class GeckoHlsPlayer implements B
mDemuxerCallbacks = callback;
}
// Called on GeckoHlsPlayerThread from ExoPlayer
@Override
public synchronized void onLoadingChanged(boolean isLoading) {
if (DEBUG) { Log.d(LOGTAG, "loading [" + isLoading + "]"); }
if (!isLoading) {
+ if (mMediaElementSuspended) {
+ suspendExoplayer();
+ }
// To update buffered position.
mComponentEventDispatcher.onDataArrived(C.TRACK_TYPE_DEFAULT);
}
}
// Called on GeckoHlsPlayerThread from ExoPlayer
@Override
public synchronized void onPlayerStateChanged(boolean playWhenReady, int state) {
if (DEBUG) { Log.d(LOGTAG, "state [" + playWhenReady + ", " + getStateString(state) + "]"); }
- if (state == ExoPlayer.STATE_READY && !mSuspended) {
- mPlayer.setPlayWhenReady(true);
+ if (state == ExoPlayer.STATE_READY && !mExoplayerSuspended && !mMediaElementSuspended) {
+ resumeExoplayer();
}
}
// Called on GeckoHlsPlayerThread from ExoPlayer
@Override
public void onPositionDiscontinuity() {
if (DEBUG) { Log.d(LOGTAG, "positionDiscontinuity"); }
}
@@ -674,16 +678,22 @@ public class GeckoHlsPlayer implements B
16, 0, getDuration(),
fmt.sampleMimeType, csd);
return aInfo;
}
// Called on HLSDemuxer's TaskQueue
@Override
public synchronized boolean seek(long positionUs) {
+ // Need to temporarily resume Exoplayer to download the chunks for getting the demuxed
+ // keyframe sample when HTMLMediaElement is paused. Suspend Exoplayer when collecting enough
+ // samples in onLoadingChanged.
+ if (mExoplayerSuspended) {
+ resumeExoplayer();
+ }
// positionUs : microseconds.
// NOTE : 1) It's not possible to seek media by tracktype via ExoPlayer Interface.
// 2) positionUs is samples PTS from MFR, we need to re-adjust it
// for ExoPlayer by subtracting sample start time.
// 3) Time unit for ExoPlayer.seek() is milliseconds.
try {
// TODO : Gather Timeline Period / Window information to develop
// complete timeline, and seekTime should be inside the duration.
@@ -707,49 +717,88 @@ public class GeckoHlsPlayer implements B
}
return false;
}
return true;
}
// Called on HLSDemuxer's TaskQueue
@Override
- public long getNextKeyFrameTime() {
+ public synchronized long getNextKeyFrameTime() {
long nextKeyFrameTime = mVRenderer != null
? mVRenderer.getNextKeyFrameTime()
: Long.MAX_VALUE;
return nextKeyFrameTime;
}
// Called on Gecko's main thread.
@Override
public synchronized void suspend() {
- if (mSuspended) {
+ if (mExoplayerSuspended) {
return;
}
- if (DEBUG) { Log.d(LOGTAG, "suspend player id : " + mPlayerId); }
- mSuspended = true;
- if (mPlayer != null) {
- mPlayer.setPlayWhenReady(false);
+ if (mMediaElementSuspended) {
+ if (DEBUG) {
+ Log.d(LOGTAG, "suspend player id : " + mPlayerId);
+ }
+ suspendExoplayer();
}
}
// Called on Gecko's main thread.
@Override
public synchronized void resume() {
- if (!mSuspended) {
+ if (!mExoplayerSuspended) {
return;
}
- if (DEBUG) { Log.d(LOGTAG, "resume player id : " + mPlayerId); }
- mSuspended = false;
+ if (!mMediaElementSuspended) {
+ if (DEBUG) {
+ Log.d(LOGTAG, "resume player id : " + mPlayerId);
+ }
+ resumeExoplayer();
+ }
+ }
+
+ // Called on Gecko's main thread.
+ @Override
+ public synchronized void play() {
+ if (!mMediaElementSuspended) {
+ return;
+ }
+ if (DEBUG) { Log.d(LOGTAG, "mediaElement played."); }
+ mMediaElementSuspended = false;
+ resumeExoplayer();
+ }
+
+ // Called on Gecko's main thread.
+ @Override
+ public synchronized void pause() {
+ if (mMediaElementSuspended) {
+ return;
+ }
+ if (DEBUG) { Log.d(LOGTAG, "mediaElement paused."); }
+ mMediaElementSuspended = true;
+ suspendExoplayer();
+ }
+
+ private synchronized void suspendExoplayer() {
if (mPlayer != null) {
+ mExoplayerSuspended = true;
+ if (DEBUG) { Log.d(LOGTAG, "suspend Exoplayer"); }
+ mPlayer.setPlayWhenReady(false);
+ }
+ }
+
+ private synchronized void resumeExoplayer() {
+ if (mPlayer != null) {
+ mExoplayerSuspended = false;
+ if (DEBUG) { Log.d(LOGTAG, "resume Exoplayer"); }
mPlayer.setPlayWhenReady(true);
}
}
-
// Called on Gecko's main thread, when HLSDemuxer or HLSResource destructs.
@Override
public synchronized void release() {
if (DEBUG) { Log.d(LOGTAG, "releasing ... id : " + mPlayerId); }
if (mPlayer != null) {
mPlayer.removeListener(this);
mPlayer.stop();
mPlayer.release();
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -2045,16 +2045,32 @@ auto GeckoHLSResourceWrapper::Destroy()
constexpr char GeckoHLSResourceWrapper::GetPlayerId_t::name[];
constexpr char GeckoHLSResourceWrapper::GetPlayerId_t::signature[];
auto GeckoHLSResourceWrapper::GetPlayerId() const -> int32_t
{
return mozilla::jni::Method<GetPlayerId_t>::Call(GeckoHLSResourceWrapper::mCtx, nullptr);
}
+constexpr char GeckoHLSResourceWrapper::Pause_t::name[];
+constexpr char GeckoHLSResourceWrapper::Pause_t::signature[];
+
+auto GeckoHLSResourceWrapper::Pause() const -> void
+{
+ return mozilla::jni::Method<Pause_t>::Call(GeckoHLSResourceWrapper::mCtx, nullptr);
+}
+
+constexpr char GeckoHLSResourceWrapper::Play_t::name[];
+constexpr char GeckoHLSResourceWrapper::Play_t::signature[];
+
+auto GeckoHLSResourceWrapper::Play() const -> void
+{
+ return mozilla::jni::Method<Play_t>::Call(GeckoHLSResourceWrapper::mCtx, nullptr);
+}
+
constexpr char GeckoHLSResourceWrapper::Resume_t::name[];
constexpr char GeckoHLSResourceWrapper::Resume_t::signature[];
auto GeckoHLSResourceWrapper::Resume() const -> void
{
return mozilla::jni::Method<Resume_t>::Call(GeckoHLSResourceWrapper::mCtx, nullptr);
}
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -5874,16 +5874,54 @@ public:
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::GECKO;
static const mozilla::jni::DispatchTarget dispatchTarget =
mozilla::jni::DispatchTarget::CURRENT;
};
auto GetPlayerId() const -> int32_t;
+ struct Pause_t {
+ typedef GeckoHLSResourceWrapper Owner;
+ typedef void ReturnType;
+ typedef void SetterType;
+ typedef mozilla::jni::Args<> Args;
+ static constexpr char name[] = "pause";
+ static constexpr char signature[] =
+ "()V";
+ static const bool isStatic = false;
+ static const mozilla::jni::ExceptionMode exceptionMode =
+ mozilla::jni::ExceptionMode::ABORT;
+ static const mozilla::jni::CallingThread callingThread =
+ mozilla::jni::CallingThread::GECKO;
+ static const mozilla::jni::DispatchTarget dispatchTarget =
+ mozilla::jni::DispatchTarget::CURRENT;
+ };
+
+ auto Pause() const -> void;
+
+ struct Play_t {
+ typedef GeckoHLSResourceWrapper Owner;
+ typedef void ReturnType;
+ typedef void SetterType;
+ typedef mozilla::jni::Args<> Args;
+ static constexpr char name[] = "play";
+ static constexpr char signature[] =
+ "()V";
+ static const bool isStatic = false;
+ static const mozilla::jni::ExceptionMode exceptionMode =
+ mozilla::jni::ExceptionMode::ABORT;
+ static const mozilla::jni::CallingThread callingThread =
+ mozilla::jni::CallingThread::GECKO;
+ static const mozilla::jni::DispatchTarget dispatchTarget =
+ mozilla::jni::DispatchTarget::CURRENT;
+ };
+
+ auto Play() const -> void;
+
struct Resume_t {
typedef GeckoHLSResourceWrapper Owner;
typedef void ReturnType;
typedef void SetterType;
typedef mozilla::jni::Args<> Args;
static constexpr char name[] = "resume";
static constexpr char signature[] =
"()V";