随便看看
一、问题
- 测试反馈说视频应用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,那么问题来了,为啥联芯没改这里?是否是故意留的坑呢?