首页 > 解决方案 > 处理丢帧的 MediaCodec 视频

问题描述

我目前正在使用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

换句话说,每次我按下下一个按钮时,间隔都会四舍五入33,当我按下下一个按钮时,它会调用mPlayer.seekTo(mPlayer.getCurrentPosition() + 33意味着一些帧将丢失,或者这就是我的想法。getCurrentPosition每次按下按钮后记录时,我都进行了测试并得到了以下结果,结果如下:

33 -> 66 -> 99 -> 132 -> 166

132166是 34 毫秒而不是 33 毫秒,因此有一个补偿来弥补可能丢失的帧。


以上工作非常好,我可以毫无问题地跳过帧,这是我面临的问题。

采用我上面使用的相同逻辑,我创建了一个自定义RangeBar. 我创建了一个方法setTickCount(它与 基本相同seekbar.setMax),并像这样设置“TickCount”:

int frames = Integer.parseInt(String.valueOf(getTotalFrames));
mrange_bar.setTickCount(frames);

所以我的最大值RangeBar是视频中的帧数。

当“Tick”值发生变化时,我调用以下命令:

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

我认为上述方法可以正常工作,因为我使用了完全相同的逻辑,而是视频“跳转/跳过”回到(我假设是关键帧)并继续搜索。

为什么会发生,我该如何解决这个问题?


编辑1:

我在上面提到它正在跳转到上一个关键帧,但我又看了一遍,它在搜索时调用了流结束(在视频中的特定点)。当我到达流的末尾时,我释放我以前的缓冲区,以便仍然可以显示一帧以避免黑屏,方法是调用:

mDecoder.releaseOutputBuffer(prevBufferIndex, true);

因此,由于某种原因,调用了流结束,然后我重新启动媒体编解码器,导致“滞后/跳转”效果。如果我删除上述内容,我不会得到帧“跳转”,但在初始化媒体编解码器时仍然存在延迟。


编辑2:

深入挖掘后,我发现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。


编辑 3

这个问题绝对是关于我过去的时间,我尝试了两种不同的方法,第一种:

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);

标签: androidandroid-mediacodec

解决方案


推荐阅读