MediaPositionState 显示不正确的 currentTime

问题描述

所以,问题来了。我在我的反应项目中使用音频 html5 元素。 问题的典型流程是: 这首歌正在播放。 用户按下暂停键(假设时间是 1.20)。 用户锁定电话。 几分钟后,用户解锁手机,按下“播放”按钮,会发生什么: mediaPositionState 将当前时间计数为先前(1.20)加上音频的当前时间,而不是仅计算当前时间。 即使在更改歌曲时,这个额外的 1.20 仍然存在。 我已经尝试在下面的 useEffect 中控制它

useEffect(() => {
    const audioEl = audioRef.current;
    if (audioEl) {
      audioEl.addEventListener('timeupdate',updateTime);
      audioEl.addEventListener('loadeddata',updatePositionState);
    }
    return () => {
      if (audioEl) {
        audioEl.removeEventListener('timeupdate',updateTime);
        audioEl.removeEventListener('loadeddata',updateTime);
        updatePositionState();
      }
    };
  },[]);

但只有当用户专注于音频时它才能正常工作。

我还有以下代码

  function updatePositionState() {
    if (navigator.mediaSession?.setPositionState) {
      navigator.mediaSession.setPositionState({
        duration: audioRef.current?.duration ?? 0.0,position: audioRef.current?.currentTime ?? 0.0,});
    }
  }
  const createMediaSession = (state: AudioStateType) => {
    if (navigator.mediaSession) {
      navigator.mediaSession.Metadata = new MediaMetadata({
        title: state.currentSongName,artist: state.currentArtistName,album: state.currentAlbumName,artwork: [
          {
            sizes: '300x300',src: `http://storage.musicstream.app/cover/${state.currentAlbumCoverId}`,},],});
      navigator.mediaSession.setActionHandler('play',function () {
        dispatch({ type: 'resume' });
        updatePositionState();
      });
      navigator.mediaSession.setActionHandler('pause',function () {
        dispatch({ type: 'pause' });
        updatePositionState();
      });
      navigator.mediaSession.setActionHandler('seekto',function (details) {
        dispatch({ type: 'manual_update_time',time: details.seekTime });
        updatePositionState();
      });
      navigator.mediaSession.setActionHandler('prevIoUstrack',() => {
        return 0;
      });
      navigator.mediaSession.setActionHandler('nexttrack',() => {
        return 0;
      });
    }
  };

我不知道如何正常描述问题,我们假设当用户刷出 MediaSession 通知时 mediaposition 混乱。 如果你问,我会提供更多的代码。 我还提供了屏幕截图(尽管我试图强制出现类似的问题:它显示时间为轨道的结尾)。

current song time

当前歌曲时间

current song time is okay when paused

暂停时当前歌曲时间正常

current song time is messed when playing

播放时当前歌曲时间混乱

根据请求添加updateTime函数 它只是为了更新 react.Context 中的状态

const updateTime = () => {
    if (audioRef.current) {
      dispatch({ type: 'update_time',time: audioRef.current.currentTime });
    }
  };

此外,完整的减速器看起来像这样(我认为这不会有帮助):

function audioReducer(state: AudioStateType,action: Action): AudioStateType {
    switch (action.type) {
      case 'fetch_and_play': {
        play(action.songData?.currentSongId).then(() => {
          dispatch({
            type: 'play',songData: {
              ...action.songData,length: audioRef.current?.duration,});
        });
        return state;
      }
      case 'play': {
        createMediaSession({ ...state,...action.songData });
        return { ...state,...action.songData };
      }
      case 'pause': {
        pause();
        return { ...state,songIsPaused: true };
      }
      case 'resume': {
        resume();
        return { ...state,songIsPaused: false };
      }
      case 'update_time': {
        return { ...state,currentTime: action.time };
      }
      case 'manual_update_time': {
        if (audioRef.current) {
          audioRef.current.currentTime = action.time;
          return { ...state,currentTime: action.time };
        } else {
          return state;
        }
      }
      default: {
        return state;
      }
    }
  }

我做了一个代码沙盒,你可以在那里看到我的问题。 https://codesandbox.io/s/wizardly-wiles-87qi1?file=/src/App.js 为了真正理解请使用安卓手机

解决方法

Reducer 应该是Pure function,在reducer 之前为dispatch 和其他人员使用action:

Action.ts

const fetchAndPlay = (currentSongId) => {
   play(currentSongId).then(() => {
        createMediaSession({ ...state,...action.songData });
        dispatch({
          type: 'play',//just pass data u need to show or you want rerender them
          length: audioRef.current?.duration,});
    });
}

Reducer.ts

function audioReducer(state: AudioStateType,action: Action): AudioStateType {
switch (action.type) {
  case 'play': 
       return { ...state,length:action.length,playing: true};
  ...
}

在你的组件中调用这个动作:

fetchAndPlay(songId)