您现在的位置是:首页 >其他 >Android MediaPlayer多次Seek产生杂音优化网站首页其他

Android MediaPlayer多次Seek产生杂音优化

Rex叶然 2024-10-13 12:01:04
简介Android MediaPlayer多次Seek产生杂音优化

前言

MediaPlayer 作为Android自带的Player目前还是存在很多不好使用问题,但实际开发中,还是有不少使用场景,本文针对多次seek产生杂音的问题进行分析讨论,自己遇到了进行记录,目前底层也不好解决和轻易改动原生代码,只能通过应用层兼容

1.为什么会产生杂音

多次seek音视频
从图中可以看出,是因为产生的两次start,那我们就得研究这两次start如何产生的

2.单次Seek场景

pause - seek - resume(恢复pause前记录的状态)
resume即代表了你之前播放就是继续播放,之前暂停则会继续暂停

3.多次seek的场景

setOnCompletionListener

在这里插入图片描述
这是我实现视频播放器多次seek的自定义日志
日志TAG解释

**pauseByEvent 主动暂停**
startTouch 开始按住(手势滑动 或者seekbar)
seek   拖动进度,或者手按住的时候抖动
stopTouch 松开 (手势滑动 或者seekbar)
**resumeByEvetn 主动**
seekComplete 监听seek结束
  • 这里得解释下为什么会有pauseByEvent 主动暂停和resumeByEvetn 主动恢复,根据【2.单次Seek场景】,单次seek是自动会记录,暂停和恢复的,但是实际场景中,你在拖动过程中是不能有声音的,它单次seek完就出声了,显然是不满足需求的

问题最后一次就出现了回调了两个seekComplete,根据【2.单次Seek场景】,自然就出现了多次带间隔的pause和start杂音就产生了

4.溯本求源

/aosp13/frameworks/av/media/libmedia/mediaplayer.cpp 

  // cache duration
577          mCurrentPosition = msec;
578          mCurrentSeekMode = mode;
579          if (mSeekPosition < 0) {
580              mSeekPosition = msec;
581              mSeekMode = mode;
582          return mPlayer->seekTo(msec, mode);
---------------------------------------------------------
930      case MEDIA_SEEK_COMPLETE:
931          ALOGV("Received seek complete");
932          if (mSeekPosition != mCurrentPosition || (mSeekMode != mCurrentSeekMode)) {
933              ALOGV("Executing queued seekTo(%d, %d)", mCurrentPosition, mCurrentSeekMode);
934              mSeekPosition = -1;
935              mSeekMode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC;
936              seekTo_l(mCurrentPosition, mCurrentSeekMode);
937          }
938          else {
939              ALOGV("All seeks complete - return to regularly scheduled program");
940              mCurrentPosition = mSeekPosition = -1;
941              mCurrentSeekMode = mSeekMode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC;
942          }
943          break;

查看源码,我们可以看到无论多少次seek,cache最多有两个seek,mSeekPosition 小于0则不会执行seek,complete不相等则执行新的mSeekPosition 则不会为-1

5.记录seek缓存及其执行次数

根据log实际分析再结合源码,我们得到以下简单的工具类

/**
 * @author rex
 * @date 2023/6/9 17:13
 * 解决多次seek杂音的问题
 * seek 在mediaplayer中最多缓存两次seek(MAX_CACHE_SEEK),再松开seek后计数为0则适合resume-play
 */
object HandlerSeekNum {

    const val TAG = "HandlerSeekNum"
    const val MAX_CACHE_SEEK = 2
    const val FINISH = 0
    var isOnStartTrackingTouch = false

    var seekNum = FINISH

    fun reset() {
        seekNum = FINISH
        SLog.d(TAG, "reset 0")
    }

    fun add() {
        seekNum++
        if (seekNum > MAX_CACHE_SEEK) {
            seekNum = MAX_CACHE_SEEK
        }
        SLog.d(TAG, "add $seekNum")

    }

    fun reduce() {
        seekNum--
        if (seekNum < FINISH) {
            seekNum = FINISH
        }
        SLog.d(TAG, "reduce $seekNum")

    }

    fun isFinish(): Boolean {
        return seekNum == FINISH
    }

}

6.将工具类用于seek事件

[seekBar.setOnSeekBarChangeListener]


           override fun onStartTrackingTouch(seekBar: SeekBar?) {
                SLog.e(TAG, "onStartTrackingTouch")
                isOnStartTrackingTouch = true
                HandlerSeekNum.reset()
                mOnOverlayCallback?.pauseByEvent()


            }

            override fun onStopTrackingTouch(seekBar: SeekBar?) {

                isOnStartTrackingTouch = false    
                SLog.e(TAG, "onStopTrackingTouch")
				mOnOverlayCallback?.seek(progress)
				// 此处为seek完成的较快抬手回调马上结束了 此时也可以直接播放
                if (HandlerSeekNum.isFinish()) {
                    mOnOverlayCallback?.resumeByEvent()
            }
	}
			override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {

   				HandlerSeekNum.add()
                SLog.i(TAG, "seek position $position")
                PlayerManager.getInstance().seekTo(position)
                
            }

7.监听seekComplete

[mediaPlayer.setOnSeekCompleteListener ]
1.判断seek彻底结束
2.手已经松开
3.恢复播放

  setOnSeekCompleteListener {
                    SLog.d(TAG, "seekComplete:${player?.currentPosition}")
                    HandlerSeekNum.reduce()
                    if (HandlerSeekNum.isFinish() && !HandlerSeekNum.isOnStartTrackingTouch) {
                        PlayerManager.getInstance().resumeByEvent()
                    }
                }

8.结果LOG验证,此时再验证波形已没有seek产生的杂音

# 7.监听seekComplete

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。