FFmpeg播放Hls录像控制解码速度
目录
FFmpeg播放Hls录像控制解码速度
进行hls录像文件播放,播放速度很快,并未按照正常1秒25帧的帧率进行播放;
播放速度过快的原因是因为代码中没有根据视频的帧率(Frame Rate)来控制帧的显示时间。HLS 视频通常有一个固定的帧率(例如 25 FPS),而代码在解码后立即显示每一帧,没有考虑帧之间的时间间隔,导致播放速度过快。
要解决这个问题,需要在显示每一帧时,根据帧率或帧的时间戳(PTS,Presentation Time Stamp)来控制帧的显示时间。
解决方案
计算帧间隔时间 :
- 根据视频的帧率(FPS)计算每帧的显示时间。
- 例如,25 FPS 的视频,每帧的显示时间应为
1 / 25 = 0.04 秒
(40 毫秒)。
使用帧的时间戳(PTS) :
- 视频帧通常带有时间戳(PTS),表示帧应该在什么时间显示。
- 通过比较当前时间和帧的 PTS,可以精确控制帧的显示时间。
代码:
#include <chrono>
#include <thread>
// 在显示帧的地方增加帧率控制
auto start_time = std::chrono::steady_clock::now(); // 记录开始时间
while (av_read_frame(format_ctx, &packet) >= 0) {
if (packet.stream_index == video_stream_index) {
if (avcodec_send_packet(codec_ctx, &packet) < 0) {
fprintf(stderr, "Error sending packet for decoding\n");
return -1;
}
while (avcodec_receive_frame(codec_ctx, frame) >= 0) {
// 转换图像格式为 YUV420P
sws_scale(sws_ctx, (const uint8_t* const*)frame->data, frame->linesize, 0, codec_ctx->height, frame_yuv->data, frame_yuv->linesize);
// 计算当前帧的显示时间
double frame_delay = av_q2d(format_ctx->streams[video_stream_index]->time_base); // 时间基
double pts = frame->pts * frame_delay; // 当前帧的显示时间(秒)
// 计算已经过去的时间
auto now = std::chrono::steady_clock::now();
double elapsed_time = std::chrono::duration<double>(now - start_time).count();
// 如果当前帧应该在未来显示,则等待
if (pts > elapsed_time) {
double sleep_time = pts - elapsed_time;
std::this_thread::sleep_for(std::chrono::duration<double>(sleep_time));
}
// 使用 SDL 显示帧
SDL_UpdateYUVTexture(
texture,
NULL,
frame_yuv->data[0],
frame_yuv->linesize[0],
frame_yuv->data[1],
frame_yuv->linesize[1],
frame_yuv->data[2],
frame_yuv->linesize[2]
);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
}
}
av_packet_unref(&packet);
}
关键点:
时间基(Time Base) :
av_q2d(format_ctx->streams[video_stream_index]->time_base)
将时间基转换为秒。- 帧的 PTS 乘以时间基,得到帧的显示时间(秒)。
帧显示时间控制 :
- 使用
std::chrono::steady_clock
记录开始时间。 - 计算当前帧的显示时间(
pts
)和已经过去的时间(elapsed_time
)。 - 如果当前帧应该在未来显示,则使用
std::this_thread::sleep_for
等待。
- 使用
帧率控制 :
- 通过帧的 PTS 和当前时间的比较,确保帧按照正确的时间间隔显示。