恒定帧率30帧以上录制掉帧问题分析

一、背景

录制30帧以上游戏画面,输出恒定帧率60帧视频文件出现掉帧

二、分析

经测试,ffmpeg动态帧率转恒定帧率不会有问题,因此,可能可以把ffmpeg的插帧逻辑移植过来。
1、 ffmpeg补帧逻辑
获取解码后的pkt -----> 拿到pkt_duration和编码时间差计算出nb_frames -------->循环nb_frames次对当前帧重复编码
2、录制补帧逻辑
在这里插入图片描述

3、录制采集逻辑
start—>获取绝对时间—>gdi截图—>设置pts—>frame入队—>sleep()—>start
gid截图过程平均耗时为30ms一张图,第一张图片的截取时间比较长,需要200多ms。因此采集的速度根本达不到60帧
在这里插入图片描述

三、解决过程

1、移植ffmpeg补帧逻辑
录制的采集过程不同于ffmpeg解码,拿不到

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用时间戳调整的 AMediaCodec_queueInputBuffer demo,针对不足30的情况: ```java // 创建 MediaCodec MediaFormat format = MediaFormat.createVideoFormat("video/avc", width, height); format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate); format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); MediaCodec encoder = MediaCodec.createEncoderByType("video/avc"); encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); Surface inputSurface = encoder.createInputSurface(); encoder.start(); // 创建 MediaMuxer MediaMuxer muxer = new MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); // 初始化时间戳 long lastPresentationTimeUs = 0; long frameIntervalUs = 1000000 / frameRate; // 循环编码每一 while (hasFrame()) { // 获取输入缓冲区 int inputBufferIndex = encoder.dequeueInputBuffer(-1); if (inputBufferIndex >= 0) { // 获取输入缓冲区 ByteBuffer inputBuffer = encoder.getInputBuffer(inputBufferIndex); inputBuffer.clear(); // 获取数据 Frame frame = getFrame(); // 将数据放入输入缓冲区 inputBuffer.put(frame.getData()); // 调整时间戳 long presentationTimeUs = frame.getPresentationTimeUs(); if (lastPresentationTimeUs > 0 && presentationTimeUs < lastPresentationTimeUs) { presentationTimeUs = lastPresentationTimeUs + frameIntervalUs; } lastPresentationTimeUs = presentationTimeUs; // 将缓冲区提交给 MediaCodec encoder.queueInputBuffer(inputBufferIndex, 0, frame.getData().length, presentationTimeUs, 0); } // 获取输出缓冲区 MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); int outputBufferIndex = encoder.dequeueOutputBuffer(bufferInfo, 0); while (outputBufferIndex >= 0) { // 获取输出缓冲区 ByteBuffer outputBuffer = encoder.getOutputBuffer(outputBufferIndex); // 写入 MediaMuxer muxer.writeSampleData(trackIndex, outputBuffer, bufferInfo); // 释放输出缓冲区 encoder.releaseOutputBuffer(outputBufferIndex, false); // 获取下一个输出缓冲区 outputBufferIndex = encoder.dequeueOutputBuffer(bufferInfo, 0); } } // 释放资源 encoder.stop(); encoder.release(); muxer.stop(); muxer.release(); ``` 这个 demo 中,每次调用 `MediaCodec.queueInputBuffer()` 时,同样会传入一个时间戳参数 `presentationTimeUs`,表示当前的显示时间戳。如果当前的时间戳比上一的时间戳小,就将当前的时间戳设置为上一的时间戳加上一个固定的时间间隔,这个时间间隔可以根据帧率计算得出。这样可以保证每一之间的时间间隔是恒定的,从而避免由于时间戳错误导致的视频播放卡顿或者花屏等问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值