MediaCodec - 处理丢帧的视频

Cla*_*ssA 17 android mediacodec android-mediacodec

这是一个很长的问题,但我必须解释我正在经历的问题才能正确地解决问题.希望我能找到像@fadden@mstorsjo这样的人来看看这个问题.

有关此问题的最新更新,请参阅编辑3.


我目前正在快速精确地寻求使用MediaCodec.我目前要逐帧跳过的是,我首先得到总帧数:

mediaInfo.totalFrames = videoTrack.getSamples().size();
Run Code Online (Sandbox Code Playgroud)

然后我得到视频文件的长度:

mediaInfo.durationUs = videoTrack.getDuration() * 1000 *1000 / timeScale;

//then calling:
public long getDuration() {
    if (mMediaInfo != null) {
        return (int) mMediaInfo.durationUs / 1000; // to millisecond
    }
    return -1;
}
Run Code Online (Sandbox Code Playgroud)

现在,当我想要获得下一帧时,我会调用以下内容:

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

        }
    });
Run Code Online (Sandbox Code Playgroud)

以下是我正在测试的文件的信息:

Frames = 466 Duration = 15523

所以帧之间的间隔是

33,311158798283262

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

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

从那里132开始166是34毫秒而不是33毫秒,所以有补偿来弥补可能丢失的帧.


以上工作完全没问题,我可以跳过框架没有任何问题,这是我面临的问题.

采用我上面使用的相同逻辑,我创建了一个自定义RangeBar.我创建了一个方法setTickCount(它基本上是相同的seekbar.setMax),我设置了"TickCount",如下所示:

int frames = Integer.parseInt(String.valueOf(getTotalFrames));
mrange_bar.setTickCount(frames);
Run Code Online (Sandbox Code Playgroud)

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

当"Tick"值改变时,我调用以下内容:

int frames = Integer.parseInt(String.valueOf(getTotalFrames));
int intervals = Integer.parseInt(String.valueOf(mPlayer.getDuration() / frames));
mPlayer.seekTo(intervals * TickPosition);
Run Code Online (Sandbox Code Playgroud)

所以上面的内容会这样,如果我的tickCount位置是,那就说40:

mPlayer.seekTo(33 * 40); //1320ms

我认为上面的工作正常,因为我使用完全相同的逻辑,而是视频"跳/跳"回到(我假设是关键帧)并继续寻找.

有人可以向我解释为什么会发生这种情况以及如何解决这个问题?


编辑1:

我上面提到过,它跳到了前一个关键帧,但我又看了一眼,它在搜索时调用了流的结尾(在视频期间的特定点).当我到达流的末尾时,我释放了我之前的缓冲区,以便通过调用以下方式仍然可以显示一帧以避免黑屏:

mDecoder.releaseOutputBuffer(prevBufferIndex, true);

所以,由于某种原因,调用了流的结束,然后我重新启动mediacodec,导致"滞后/跳跃"效果.如果我删除上面的内容,我不会让帧"跳",但是在初始化mediacodec时仍然存在延迟.


编辑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();
        }
    }
Run Code Online (Sandbox Code Playgroud)

出于某种原因,我sampleSize在寻找期间的特定点是-1.


编辑3

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

mPlayer.seekTo(progress);

//position is retrieved by setting mSeekBar.setMax(mPlayer.getDuration); ....
Run Code Online (Sandbox Code Playgroud)

和第二种方法,我确定帧间隔:

//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);
Run Code Online (Sandbox Code Playgroud)

在尝试了上述两种方法后,我意识到问题与传递给mediaCodec的时间有关,因为"滞后/跳跃"发生在不同的地方.

我不确定为什么在我打电话时不会发生这种情况:

mPlayer.seekTo(mPlayer.getCurrentPosition() + intervals);
Run Code Online (Sandbox Code Playgroud)