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
换句话说,每次我按下下一个按钮时,间隔都会四舍五入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);
所以我的最大值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);
解决方案
推荐阅读
- azure-active-directory - 如何拦截 winlogon 进程并获取 Azure AD 访问令牌?
- c# - 发出 Http GET 请求时出现 OutOfMemoryException
- reactjs - Google 字体不适用于在 Github Pages 上部署的 React 应用程序
- html - CSS样式不呈现
- azure - AKS 使用 Terraform - 安装 hashcorp/azurerm v2.70.0 时出错:意外 EOF
- python - 从python中的另一个函数打印在构造函数中实例化的列表中的元素
- python - 用户的个人资料未创建
- webhooks - 如何在 Cloudflare bot-fight-mode 中允许 Razorpay Webhook?
- javascript - JS:如何在单击事件处理程序中将用户输入添加到对象实例的静态数组
- node.js - 将遥测数据从设备发送到 IoT 中心的合适 NodeJS 代码是什么?