我目前正在使用MediaCodec
进行快速精确搜索。我目前要逐帧跳过的操作是,首先获取总帧数:
mediaInfo.totalFrames = videoTrack.getSamples().size();
然后我得到了视频文件的长度:
mediaInfo.durationUs = videoTrack.getDuration() * 1000 *1000 / timeScale;
//then calling:
public long getDuration() {
if (mMediaInfo != null) {
return (int) mMediaInfo.durationUs / 1000; // to millisecond
}
return -1;
}
现在,当我想获取下一帧时,请调用以下内容:
mNextFrame.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int frames = Integer.parseInt(String.valueOf(getTotalFrames));
int intervals = Integer.parseInt(String.valueOf(mPlayer.getDuration() / frames));
if (mPlayer.isPlaying()) {
mPlayer.pause();
mPlayer.seekTo(mPlayer.getCurrentPosition() + intervals);
} else {
mPlayer.seekTo(mPlayer.getCurrentPosition() + intervals);
}
}
});
以下是我正在测试的文件的信息:
Frames = 466
Duration = 15523
所以帧之间的间隔是
33,311158798283262
换句话说,每次我按下next按钮时,间隔将四舍五入为33
,当我按下next按钮时,它将调用mPlayer.seekTo(mPlayer.getCurrentPosition() + 33
,这意味着某些帧将会丢失,或者是我的想法每次按下按钮后登录getCurrentPosition
时,我进行测试并得到以下结果:
33 -> 66 -> 99 -> 132 -> 166
从132
到166
的时间是34ms,而不是33ms,因此要补偿可能丢失的帧。
上面的方法非常正常,我可以跳过所有框架而没有任何问题,这是我面临的问题。
采用与上面相同的逻辑,我创建了一个自定义RangeBar
。我创建了一个方法setTickCount
(与seekbar.setMax
基本相同),并按如下方式设置“ TickCount”:
int frames = Integer.parseInt(String.valueOf(getTotalFrames));
mrange_bar.setTickCount(frames);
所以我的RangeBar
的最大值是视频中的帧数。
当“刻度”值更改时,我将调用以下内容:
int frames = Integer.parseInt(String.valueOf(getTotalFrames));
int intervals = Integer.parseInt(String.valueOf(mPlayer.getDuration() / frames));
mPlayer.seekTo(intervals * TickPosition);
因此,上面的代码将像这样工作,如果我的tickCount位置为40:
mPlayer.seekTo(33 * 40); //1320ms
我认为上面的方法可以正常工作,因为我使用了完全相同的逻辑,但是视频“跳转/跳过”回到了(我认为是关键帧)并继续搜索。
为什么会发生以及如何解决此问题?
我在上面提到,它正在跳到上一个关键帧,但是我再次看了一下,它在搜索时(视频中的特殊点)正在调用流的结尾。当到达流的末尾时,我释放先前的缓冲区,以便仍然可以通过调用以下内容来显示一帧以避免黑屏:
mDecoder.releaseOutputBuffer(prevBufferIndex, true);
因此,出于某种原因,将调用流的末尾,然后在其中重新启动mediacodec,从而导致“滞后/跳跃”效应。 如果删除上面的内容,则不会出现“跳转”帧,但是在初始化mediacodec时仍然存在滞后。
深入研究之后,我发现readSampleData
为-1:
ByteBuffer[] inputBuffers = mDecoder.getInputBuffers();
int inIndex = mDecoder.dequeueInputBuffer(TIMEOUT_USEC);
if (inIndex >= 0) {
ByteBuffer buffer = inputBuffers[inIndex];
int sampleSize = mExtractor.readSampleData(buffer, 0);
if (sampleSize < 0) {
mDecoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
mIsExtractorReachedEOS = true;
} else {
mDecoder.queueInputBuffer(inIndex, 0, sampleSize, mExtractor.getSampleTime(), 0);
mExtractor.advance();
}
}
出于某种原因,我的sampleSize
在搜索过程中的特定点为-1。
这个问题肯定与我过去的时间有关,我尝试了两种不同的方法,第一种:
mPlayer.seekTo(progress);
//position is retrieved by setting mSeekBar.setMax(mPlayer.getDuration); ....
第二种方法,我确定帧间隔:
//Total amount of frames in video
long TotalFramesInVideo = videoTrack.getSamples().size();
//Duration of file in milliseconds
int DurationOfVideoInMs = mPlayer.getDuration();
//Determine interval between frames
int frameIntervals = DurationOfVideoInMs / Integer.parseInt(String.valueOf(TotalFramesInVideo));
//Then I seek to the frames like this:
mPlayer.seekTo(position * frameIntervals);
尝试了上述两种方法之后,我意识到问题与传递给mediaCodec的时间有关,因为“滞后/跳跃”发生在不同的地方。
我不确定为什么我打电话时不会发生这种情况
mPlayer.seekTo(mPlayer.getCurrentPosition() + intervals);