联芯平台MPEG视频seek出现ANR问题

随便看看

石器时代

一、问题

  • 测试反馈说视频应用seek-next流程很容易出现ANR
  • 自己拿视频测试,刚好是.mpg视频,1.12GB,约1:55长度
  • 发现必出现seek>=35min的ANR

二、排查视频源码

  • SeekBar操作
public class VideoControllerOverlay extends FrameLayout ... {
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
	   ...
	    // 获取视频时长ms
	    long duration = mPlayerListener.getDuration();
	    // 根据SeekBar取千分比
	    long newposition = (duration * progress) / 1000L;
	    // Seek
	    mPlayerListener.onSeekTo((int) newposition);
	    ...
    }
}
public class VideoPlayer ... {
	@Override
	public void onSeekTo(int time) {
		// 调用到SurfaceView中
		mVideoView.seekTo(time);
	}
}
public class CMCCVideoView extends SurfaceView ... {
	@Override
	public void seekTo(int msec) {
		if (isInPlaybackState()) {
			// 系统API的MediapLayer接口
			mMediaPlayer.seekTo(msec);
			mSeekWhenPrepared = 0;
		} else {
			mSeekWhenPrepared = msec;
		}
	}
}

我们可以看到,应用层的源码中seek部分没有多余的操作,看不出导致ANR的地方。

  • 那么会不会是SeekBar手势完成后的操作导致的呢
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
	// 取消静音
	AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
	audioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
	mDragging = false;
	// 同步progress到视频实际位置
	setProgress();
	// 更新View
	show(sDefaultTimeout);
	// 继续同步
	mHandler.sendEmptyMessage(SHOW_PROGRESS);
}
public int setProgress() {
	...
	// 获取位置和视频时长,计算progress位置
	int position = mPlayerListener.getCurrentPosition();
	int duration = mPlayerListener.getDuration();
		if (mSeekBar != null) {
			if (duration > 0) {
				long pos = 1000L * position / duration;
				mSeekBar.setProgress((int) pos);
			} else {
				mSeekBar.setProgress(0);
			}
			int percent = mPlayerListener.getBufferPercentage();
			mSeekBar.setSecondaryProgress(percent * 10);
		}
	...
	return position;
}

手势完成后也只是取消静音和更新了View状态,LOG打印没有卡在这里,没有发现会导致ANR的。

三、seekTo

  • 问题应该还是出现seekTo这里,查framework的LOG和源码吧
  • LOG中很异常的部分
      MPEG2PSExtractor: [AUDIO] start_pos=802101469, pos=802101248, ts=4608343266

这个LOG在seek后飞速刷新不停止,而其他视频没有刷这么多,所以问题就在这里

  • frameworks/av/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp
    这个是MPEG算法的源码,经过仔细分析,定位出了问题
int64_t MPEG2PSExtractor::Track::utilSearch(int64_t target_ts, int64_t pos_min, int64_t pos_max, int64_t pos_limit, int64_t ts_min, int64_t ts_max, int flags, int64_t *ts_ret) {
   // ts为64位
   int64_t pos, ts;
   // 为毛这里是32位?
   int preTs = 0;
   ...
   while (pos_min < pos_limit) {
       ...
       // LOG出处
       MPEG2PS_LOGI("start_pos=%lld, pos=%lld, ts=%lld", start_pos, pos, ts);
       ...
       // 32位与64位比较
       if (preTs == ts && pos == prePos && no_change == 0){
           break;
       }
       // 一个64位值赋给32位,单位是us(微秒),会有精度丢失
       preTs = ts;
       ...
   }
   ...
}
  • 最终改法
//int preTs = 0;
int64_t preTs = 0;

四、揣测

  • 善意揣测
    数值转换精度丢失问题往往容易忽略,面向对象开发中更是如此(嵌入式开发很注重资源的分配,一般较少出这个问题),尤其是long型的计算更要注意了。估计手头的联芯版本较老,所以会有这个问题。
  • 恶意揣测
    这里很明显联芯也加了LOG,很容易发现MPEG大视频不能正常seek,那么问题来了,为啥联芯没改这里?是否是故意留的坑呢?

你可能感兴趣的:(android)