linux+QT+FFmpeg 6.0,把多个QImage组合成一个视频

直接上代码吧:

RecordingThread.h#ifndef RECORDINGTHREAD_H
#define RECORDINGTHREAD_H
#include "QTimer"
#include <QObject>
#include <QImage>
#include <QQueue>extern "C"{//因为FFmpeg是c语言,QT里面调用的话需要extern "C"#include "libavcodec/avcodec.h"#include "libavformat/avformat.h"#include "libswscale/swscale.h"#include "libavdevice/avdevice.h"#include "libavformat/avio.h"#include "libavutil/imgutils.h"
}class RecordingThread : public QObject
{Q_OBJECTpublic:void FFmpegInit();void saveMp4(QImage image);void stopMp4();void setImage(QImage image);public slots:void recordInit();signals:void send(QString);private:AVFormatContext* formatContext;AVCodecParameters* codecParameters;const AVCodec* codec;AVCodecContext* codecContext;AVStream* stream;const AVPixelFormat* pixFmt;int num = 0;QQueue<QImage> gQdata;int isRecord = -1;};#endif // RECORDINGTHREAD_H
#include "recordingthread.h"
#include <QPainter>
#include <cmath>
#include <QPainterPath>
#include <QDebug>
#include <QTimer>
#include <QDateTime>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>void RecordingThread::FFmpegInit(){isRecord = -1;int ret;// 初始化 FFmpegqDebug()<<"avdevice_register_all()";avdevice_register_all(); //初始化所有设备qDebug()<<"formatContext = avformat_alloc_context()";formatContext = avformat_alloc_context();//分配format上下文qint64 timeT = QDateTime::currentMSecsSinceEpoch();//毫秒级时间戳QString outputFileName = QString("/sdcard/").append("ffmpeg").append(QString::number(timeT)).append(".mp4");//第三个参数可以直接使用nullptr 根据outputFileName的后缀自动识别qDebug()<<"avformat_alloc_output_context2(&formatContext, nullptr, \"mp4\", outputFileName.toUtf8().constData())";ret = avformat_alloc_output_context2(&formatContext, nullptr, nullptr, outputFileName.toUtf8().constData());qDebug()<<"ret===="<<ret;qDebug()<<"formatContext===="<<formatContext;qDebug()<<"formatContext->oformat = av_guess_format(nullptr, outputFileName.toUtf8().constData(), nullptr);";formatContext->oformat = av_guess_format(nullptr, outputFileName.toUtf8().constData(), nullptr);qDebug() << "avio_open(&formatContext->pb, outputFileName.toUtf8().constData(), AVIO_FLAG_WRITE) < 0";// 打开输出文件if (avio_open(&formatContext->pb, outputFileName.toUtf8().constData(), AVIO_FLAG_WRITE) < 0) {qDebug() << "Failed to open output file";return;}qDebug() << "AVStream* stream = avformat_new_stream(formatContext, nullptr);";// 创建一个AVStream对象stream = avformat_new_stream(formatContext, nullptr);if (!stream) {qDebug() << "Failed to create output stream";return;}qDebug() << "AVCodecParameters* codecParameters = stream->codecpar;";// 配置AVCodecContextcodecParameters = stream->codecpar;codecParameters->codec_type = AVMEDIA_TYPE_VIDEO;codecParameters->codec_id = AV_CODEC_ID_H264; // 使用H.264编码器codecParameters->width = 400;codecParameters->height = 400;qDebug() << " const AVCodec* codec = avcodec_find_encoder(codecParameters->codec_id);";// 打开编解码器codec = avcodec_find_encoder(codecParameters->codec_id);codecContext = avcodec_alloc_context3(codec);codecContext->width = 400;codecContext->height = 400;codecContext->pix_fmt = AV_PIX_FMT_YUV420P;codecContext->time_base = {1, 25}; // 设置编码器的时间基为 1秒/30帧codecContext->framerate = {25, 1}; // 设置编码器的帧率为 30fps//codecContext->thread_count = 4;qDebug() << "AV_PIX_FMT_YUV420P====="<<AV_PIX_FMT_YUV420P;qDebug() << "codecContext->pix_fmt====="<<codecContext->pix_fmt;qDebug() << "avcodec_open2(codecContext, codec, nullptr);";//设置完成编码格式以后要立刻打开,要不然调用avcodec_parameters_to_context的时候会重置编码ret = avcodec_open2(codecContext, codec, nullptr);if(ret < 0){qDebug() << "Failed to avcodec_open2";return;}qDebug() << "avcodec_parameters_to_context(codecContext, codecParameters);";// 将编码器参数复制到输出流avcodec_parameters_to_context(codecContext, codecParameters);// 检查编解码器支持的像素格式pixFmt = codec->pix_fmts;qDebug() << "while";while (*pixFmt != AV_PIX_FMT_NONE) {qDebug() << av_get_pix_fmt_name(*pixFmt);++pixFmt;}qDebug() << " avformat_write_header(formatContext, nullptr);";// 写入头部信息avformat_write_header(formatContext, nullptr);
}
void RecordingThread::saveMp4(QImage image){int imagewidth = image.width();int imageheight = image.height();int ret;//qDebug() << "  AVFrame* frame = av_frame_alloc();";// 逐个写入图像帧AVFrame* frame = av_frame_alloc();if (!frame) {qDebug() << "Failed to allocate frame.";return;}//qDebug() << "frame->format = AV_PIX_FMT_YUV420P";frame->format = AV_PIX_FMT_YUV420P;frame->width = imagewidth;frame->height = imageheight;// 在循环中设置每一帧的时间戳 如果没有这个可能就是0秒视频frame->pts = (((AV_TIME_BASE / 25))/10 * num);// qDebug() << "frame->pts===========" << frame->pts;if (av_frame_get_buffer(frame, 0) < 0) {qDebug() << "Failed to allocate frame buffer.";av_frame_free(&frame);return;}// 图像格式转换SwsContext* swsContext = sws_getContext(imagewidth, imageheight, AV_PIX_FMT_RGB32,frame->width, frame->height, AV_PIX_FMT_YUV420P,SWS_BICUBIC, nullptr, nullptr, nullptr);if (!swsContext) {qDebug() << "Failed to create SwsContext.";av_frame_free(&frame);return;}uint8_t* destData[4] = {frame->data[0], frame->data[1], frame->data[2], nullptr};int destLinesize[4] = {frame->linesize[0], frame->linesize[1], frame->linesize[2], 0};//av_image_fill_arrays(frame->data, frame->linesize, destData[0], codecContext->pix_fmt, codecContext->width, codecContext->height, 1);image = image.convertToFormat(QImage::Format_RGB32);const uchar* bits = image.constBits();int bytesPerLine = image.bytesPerLine();// 函数返回的值是转换后的图像的输出行数。输出的图像高度为图像像素。ret = sws_scale(swsContext, &bits, &bytesPerLine, 0, image.height(), destData, destLinesize);//qDebug() << "sws_scale ret==="<<ret;//函数用于释放由 sws_getContext 函数创建的图像格式转换上下文sws_freeContext(swsContext);//qDebug() << "AVPacket packet;";// 编码并写入视频帧AVPacket packet;av_init_packet(&packet);packet.data = nullptr;packet.size = 0;int code = -1;// 接收输出包while (code < 0) {ret = avcodec_send_frame(codecContext, frame);// qDebug() << "avcodec_send_frame ret===="<<ret;code = avcodec_receive_packet(codecContext, &packet);//qDebug() << "while avcodec_receive_packet====" << code;if(code == 0){// 处理输出包ret = av_interleaved_write_frame(formatContext, &packet);//  qDebug() << "av_interleaved_write_frame==================" << ret;av_packet_unref(&packet);  // 释放输出包} else if (code == AVERROR(EAGAIN)) {// 当输出队列为空时,需要重新发送帧进行编码continue;} else {qDebug() << "Error encoding frame: " << code;break;}}//qDebug() << "av_frame_free(&frame);";av_frame_free(&frame);//qDebug()<<"num==============================================="<<num;++num;
}
void RecordingThread::stopMp4(){isRecord = 0;
}void RecordingThread::setImage(QImage image){isRecord = 1;gQdata.push_back(image);
}void RecordingThread::recordInit(){while (1) {if(!gQdata.isEmpty() && gQdata.size()>0){QImage qimage = gQdata.dequeue();saveMp4(qimage);}else{if(isRecord == 0){isRecord = -1;num = 0;//写入尾部信息qDebug() << "av_write_trailer(formatContext)";	int ret = av_write_trailer(formatContext);qDebug() << "av_write_trailer(formatContext) ret==="<<ret;	emit send("stopRecode");}		}usleep(5000);}
}

我这里是专门搞了个类封装,我把这个类当成线程使用了,在启动程序的时候直接当线程启动recordInit():比如这样


 然后我在需要合成视频的时候先调用初始化:

mRecordingThread->FFmpegInit();

再传入QImage:

mRecordingThread->setImage(rotatedImage);
停止的时候再调用:
mRecordingThread->stopMp4();

这样就不会造成卡死主线程的情况

我在使用FFmpeg的时候主要出现两个比较明显的情况:

1.pix_fmt为-1的情况,原因是

设置完成编码格式以后要立刻打开,要不然调用avcodec_parameters_to_context的时候会重置编码

2.合成的视频只有一帧的情况

//主要是因为这个参数导致的,你们可以根据自己的需求微调
// 在循环中设置每一帧的时间戳 如果没有这个可能就是0秒视频 
frame->pts = ((baseTimestamp + (num * (AV_TIME_BASE / 30)))/10);

嵌入式编译FFmpeg6.0版本并且组合x264_想取一个与众不同的名字好难的博客-CSDN博客

嵌入式编译x264源码_想取一个与众不同的名字好难的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/110191.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

7、监测数据采集物联网应用开发步骤(5.3)

监测数据采集物联网应用开发步骤(5.2) 静态配置库数据库调用&#xff0c;新建全局变量初始化类com.zxy.main.Init_Page.py #! python3 # -*- coding: utf-8 -Created on 2017年05月10日 author: zxyong 13738196011 from com.zxy.z_debug import z_debug from com.zxy.common…

新SDK平台下载开源全志V853的SDK

获取SDK SDK 使用 Repo 工具管理&#xff0c;拉取 SDK 需要配置安装 Repo 工具。 Repo is a tool built on top of Git. Repo helps manage many Git repositories, does the uploads to revision control systems, and automates parts of the development workflow. Repo is…

开发新能源的好处

风能无论是总装机容量还是新增装机容量&#xff0c;全球都保持着较快的发展速度&#xff0c;风能将迎来发展高峰。风电上网电价高于火电&#xff0c;期待价格理顺促进发展。生物质能有望在农业资源丰富的热带和亚热带普及&#xff0c;主要问题是降低制造成本&#xff0c;生物乙…

设计模式入门笔记

1 设计模式简介 在IT这个行业&#xff0c;技术日新月异&#xff0c;可能你今年刚弄懂一个编程框架&#xff0c;明年它就不流行了。 然而即使在易变的IT世界也有很多几乎不变的知识&#xff0c;他们晦涩而重要&#xff0c;默默的将程序员划分为卓越与平庸两类。比如说&#xff…

战略文化派,战略形成是集体信念和愿景形成的过程

战略文化派&#xff1a;战略形成是集体信念和愿景形成的过程 趣讲大白话&#xff1a;在乎集体认同 【趣讲信息科技271期】 **************************** 关于企业文化的故事很多 比如&#xff1a;中国海尔砸冰箱后蜕变的文化 比如&#xff1a;日本的稻盛和夫倡导的东方利他文化…

基于空洞卷积DCNN与长短期时间记忆模型LSTM的dcnn-lstm的回归预测模型

周末的时候有时间鼓捣的一个小实践&#xff0c;主要就是做的多因子回归预测的任务&#xff0c;关于时序数据建模和回归预测建模我的专栏和系列博文里面已经有了非常详细的介绍了&#xff0c;这里就不再多加赘述了&#xff0c;这里主要是一个模型融合的实践&#xff0c;这里的数…

网络编程套接字(3): 简单的TCP网络程序

文章目录 网络编程套接字(3)4. 简单的TCP网络程序4.1 服务端创建(1) 创建套接字(2) 绑定端口(3) 监听(4) 获取新连接(5) 处理读取与写入 4.2 客户端创建(1)连接服务器 4.3 代码编写(1) v1__简单发送消息(2) v2_多进程版本(3) v3_多线程版本(4) v4_线程池版本 网络编程套接字(3)…

如何使用CSS实现一个自适应等高布局?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 使用 Flexbox 布局⭐ 使用 Grid 布局⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发…

爬虫逆向实战(二十三)--某准网数据

一、数据接口分析 主页地址&#xff1a;某准网 1、抓包 通过抓包可以发现数据接口是api_to/search/company_v2.json 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以发现b参数和kiv参数是加密参数 请求头是否加密&#xff1f; 无响应是否加…

在云原生时代,构建高效的大数据存储与分析平台

文章目录 1. **选择适当的数据存储技术&#xff1a;**2. **采用分布式架构&#xff1a;**3. **数据分区和索引&#xff1a;**4. **采用列式存储&#xff1a;**5. **数据压缩和编码&#xff1a;**6. **使用缓存技术&#xff1a;**7. **数据分片和复制&#xff1a;**8. **自动化运…

GNS3 在 Linux 上的安装指南

文章目录 GNS3 在 Linux 上的安装指南1. 基于 Ubuntu 的发行版安装 GNS32. 基于 Debian 的安装3. 基于 ArchLinux 的安装4. 从 Pypi 安装 GNS35. 启动 GNS3 服务端GNS3 在 Linux 上的安装指南 大家好,今天我们来聊聊如何在 Linux 上安装 GNS3。GNS3 是一个非常受欢迎的网络模…

软件工程(二十) 系统运行与软件维护

1、系统转换计划 1.1、遗留系统的演化策略 时至今日,你想去开发一个系统,想完全不涉及到已有的系统,基本是不可能的事情。但是对于已有系统我们有一个策略。 比如我们是淘汰掉已有系统,还是继承已有系统,或者集成已有系统,或者改造遗留的系统呢,都是不同的策略。 技术…

macOS上开源免费的新闻阅读器SABnzbd

SABnzbd Mac版是一款运行在Mac平台上的开源新闻阅读器&#xff0c;这款阅读器界面简约、功效简单强大&#xff0c;使用SABnzbd时可以帮助使用Python语言编写&#xff0c;让用户使用usenet新闻组更便利&#xff0c;是你阅读新闻的好帮手&#xff01; SABnzbd具有以下主要特点&a…

Sentinel流量控制与熔断降级

&#x1f4dd; 学技术、更要掌握学习的方法&#xff0c;一起学习&#xff0c;让进步发生 &#x1f469;&#x1f3fb; 作者&#xff1a;一只IT攻城狮 &#xff0c;关注我&#xff0c;不迷路 。 &#x1f490;学习建议&#xff1a;1、养成习惯&#xff0c;学习java的任何一个技术…

git 把项目托管到 码云出现的错误集合

分享一下我git项目时碰见的错误 1、error: could not lock config file D:/orcad/Cadence/SPB_Data/.gitconfig: No suchfile or directory 在下载git后设置用户名、邮箱时会出现的错误 需要去修改环境变量&#xff0c;这个之前写好了&#xff0c;可以跳转看看 Git配置error:…

三维模型OBJ格式轻量化压缩并行计算处理方法浅析

三维模型OBJ格式轻量化压缩并行计算处理方法浅析 三维模型的轻量化是指通过一系列技术和算法来减小三维模型的文件大小&#xff0c;以提高模型在计算机中的加载、渲染和传输效率。并行计算是利用多个计算单元同时执行任务&#xff0c;以加速计算过程的一种技术。在三维模型的O…

900ES1-0100 honeywell 可减少视觉引导应用的整体开发时间

900ES1-0100 honeywell 可减少视觉引导应用的整体开发时间 CV2视觉系统配有高柔性电缆(以太网或USB)。通过将高柔性电缆作为所有CV2视觉系统的标准配置&#xff0c;Epson CV2摄像机可以安装在机器人臂(移动)或固定装置(固定)上。基于向导的校准使机器人到视觉系统的校准变得轻…

关于单例模式

单例模式的目的&#xff1a; 单例模式的目的和其他的设计模式的目的都是一样的&#xff0c;都是为了降低对象之间的耦合性&#xff0c;增加代码的可复用性&#xff0c;可维护性和可扩展性。 单例模式&#xff1a; 单例模式是一种常用的设计模式&#xff0c;用简单的言语说&am…

CSS学习笔记01

CSS笔记01 什么是CSS CSS&#xff08;Cascading Style Sheets &#xff09;&#xff1a;层叠样式表&#xff0c;也可以叫做级联样式表&#xff0c;是一种用来表现 HTML 或 XML 等文件样式的计算机语言。字体&#xff0c;颜色&#xff0c;边距&#xff0c;高度&#xff0c;宽度…

代码随想录算法训练营之JAVA|第四十二天|70. 爬楼梯

今天是第 天刷leetcode&#xff0c;立个flag&#xff0c;打卡60天&#xff0c;如果做不到&#xff0c;完成一件评论区点赞最高的挑战。 算法挑战链接 70. 爬楼梯https://leetcode.cn/problems/climbing-stairs/ 第一想法 这是一个动态规划的入门题目&#xff0c;在看完完全背…