millerdev
millerdev

Reputation: 10349

MediaSession Play/Pause button does not work in Chrome

I'm building a media controller application that controls a media player running in a remote process, and trying to use the MediaSession API to facilitate media key control. An audio element that is nearly silent is used to establish the media session, and after a few seconds it is paused indefinitely.

This works well in Firefox, but in Chrome-based browsers (desktop and mobile) the Play/Pause button does not change state and ultimately stops working after a few seconds. The Next/Previous track buttons work as expected.

What do I need to do to make the Play/Pause media session controls work in Chrome-based browsers?

React app to reproduce the issue:

import "./styles.css";
import React from "react";

export default function App() {
  return (
    <div className="App">
      <h1>MediaSession demo</h1>
      <Player />
    </div>
  );
}

function Player() {
  const playctl = usePlayer();
  if (playctl.state === "stopped") {
    return <button onClick={playctl.playPause}>Begin</button>;
  }
  return (
    <>
      <p>Use media session notification to control player state.</p>
      <MediaSession playctl={playctl} />
      <p>Player state: {playctl.state}</p>
      <p>Track: {playctl.track}</p>
    </>
  );
}

function usePlayer() {
  const [state, setState] = React.useState("stopped");
  const [track, setTrack] = React.useState(1);
  let playing = state === "playing";
  return {
    playPause: () => {
      playing = !playing;
      setState(playing ? "playing" : "paused");
    },
    nextTrack: () => {
      setTrack(track < 5 ? track + 1 : 1);
    },
    prevTrack: () => {
      setTrack(track > 1 ? track - 1 : 5);
    },
    state,
    nextState: playing ? "Pause" : "Play",
    playing,
    track
  };
}

const MediaSession = ({ playctl }) => {
  const controls = useMediaControls();
  React.useEffect(() => controls.update(playctl), [controls, playctl]);
  return controls.audio;
};

function useMediaControls() {
  const audiofile = require("./near-silence.mp3");
  const hasSession = window.navigator && "mediaSession" in window.navigator;
  const ref = React.useRef();
  let shouldShow = true;

  function showControls(audio) {
    shouldShow = false;
    audio.volume = 0.00001; // very low volume level
    audio.play();
    audio.currentTime = 0;
    // pause before track ends so controls remain visible
    setTimeout(() => audio.pause(), 5000);
  }

  function updateSession(playctl) {
    const session = window.navigator.mediaSession;
    session.playbackState = playctl.playing ? "playing" : "paused";
    session.setActionHandler("pause", playctl.playPause);
    session.setActionHandler("play", playctl.playPause);
    session.setActionHandler("nexttrack", playctl.nextTrack);
    session.setActionHandler("previoustrack", playctl.prevTrack);
  }

  function createApi() {
    return {
      audio: hasSession && <audio ref={ref} src={audiofile} />,
      update: (playctl) => {
        if (hasSession) {
          const audio = ref.current;
          shouldShow && audio && showControls(audio);
          updateSession(playctl);
        }
      }
    };
  }

  return React.useState(createApi)[0];
}

Code sandbox: https://codesandbox.io/s/mediasession-demo-r773i

Upvotes: 1

Views: 634

Answers (0)

Related Questions