环境:Win10 + Qt5.15.2 + MSVC 64bit + FFmpeg4.4
解码流程:
图片来源: FFmpeg编程-解码流程_贝勒里恩的博客-CSDN博客_ffmpeg解码流程
其他参考:
博客:Qt+FFMPEG学习(一)视频帧转换为QImage_Italink的博客-CSDN博客_qstring转qimage
博客:使用Qt线程 + FFmpeg获取视频流并显示图像到窗口_友善啊,朋友的博客-CSDN博客_qt接收视频流
博客:FFMPEG开发快速入坑——音视频编码处理 - 知乎
遇到的小问题:
1.用 avcodec_receive_frame 获取解码后的帧数据,返回 AVERROR(EAGAIN) (值为-11),文档描述为:输出在此状态下不可用-用户必须尝试发送新输入,所以加了个 continue 循环执行 avcodec_send_packet,直到可用。
2.sws_scale (视频像素格式和分辨率转换)的输出可以直接用字节缓冲区接收,不一定非要把缓冲区 attach 到 AVFrame 上。
3.QImage、QByteArray这些容器如果直接用字节数据构造,是没有深拷贝一份的,需要自己分离。
代码链接:https://github.com/gongjianbo/LearnFFmpeg.git
主要代码:
#include "MainWindow.h"
#include "ui_MainWindow.h"
extern "C" {
#include
#include
#include
#include
#include
#include
#include
#include
}
#include
#include
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
fprintf(stderr, "ffmpeg version:%s\n", av_version_info());
connect(ui->pushButton, &QPushButton::clicked,
this, [this]{
const QString &filepath = QFileDialog::getOpenFileName(this);
qDebug()height,
guard.codecCtx->pix_fmt,
guard.codecParam->width,
guard.codecParam->height,
AV_PIX_FMT_RGB24,
SWS_BICUBIC, NULL, NULL, NULL);
if (!guard.swsCtx)
return image;
while (true) {
//读取码流中的一帧视频,或者若干帧音频
//注:av_read_frame 每次循环后必须执行av_packet_unref(packet)进行释放
//frame同理
if(av_read_frame(guard.formatCtx, guard.packet) != 0)
return image;
if (guard.packet->stream_index == guard.streamIndex) {
//发送编码数据包,将一个packet放入到队列中等待解码
//注:ffmpeg内部会缓冲几帧,要想取出来就需要传递空的AVPacket进去
result = avcodec_send_packet(guard.codecCtx, guard.packet);
//当前状态下不接受输入-用户必须使用avcodec_receive_frame()读取输出
if (result == AVERROR(EAGAIN)) {
avcodec_receive_frame(guard.codecCtx, guard.frameIn);
av_packet_unref(guard.packet);
av_frame_unref(guard.frameIn);
continue;
} else if (result != 0) {
return image;
}
//接收解码后数据,将解码后的数据拷贝给avframe
//avcodec_send_packet和avcodec_receive_frame调用关系并不一定是一一对应
//如一些音频数据调用一次avcodec_send_packet之后,
//可能需要调用多次avcodec_receive_frame才能获取全部的解码音频数据
result = avcodec_receive_frame(guard.codecCtx, guard.frameIn);
//输出在此状态下不可用-用户必须尝试发送新输入
if (result == AVERROR(EAGAIN)) {
av_packet_unref(guard.packet);
av_frame_unref(guard.frameIn);
continue;
} else if (result != 0) {
return image;
}
//视频像素格式和分辨率的转换
//函数功能:1.图像色彩空间转换;2.分辨率缩放;3.前后图像滤波处理。
//效率相对较低,不如libyuv或shader
sws_scale(guard.swsCtx,
static_cast(guard.frameIn->data),
guard.frameIn->linesize,
0,
guard.codecParam->height,
guard.frameOut->data,
guard.frameOut->linesize);
//guard.frameOut->data就是指向的guard.outBuf,可替换
image = QImage(static_cast(guard.frameOut->data[0]),
guard.codecParam->width,
guard.codecParam->height,
QImage::Format_RGB888);
//构造用的buf的内存,这里拷贝一份
image = image.copy();
qDebug()
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?