处理掉帧的MediaCodec视频

时间:2018-08-27 09:50:10

标签: android mediacodec android-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

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

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

132166的时间是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

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

为什么会发生以及如何解决此问题?


编辑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();
        }
    }

出于某种原因,我的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);

0 个答案:

没有答案