当前位置: 首页 > news >正文

上海沙龙网站建设谷歌网站推广策略方案

上海沙龙网站建设,谷歌网站推广策略方案,wordpress如何实现精确查询,河北搜索引擎优化Qt-FFmpeg开发-保存视频流裸流#x1f4c0; 文章目录Qt-FFmpeg开发-保存视频流裸流#x1f4c0;1、概述#x1f4f8;2、实现效果#x1f4bd;3、FFmpeg保存裸流代码流程#x1f4a1;4、主要代码#x1f50d;5、完整源代码#x1f4d1;更多精彩内容#x1f449;个人内容…Qt-FFmpeg开发-保存视频流裸流 文章目录Qt-FFmpeg开发-保存视频流裸流1、概述2、实现效果3、FFmpeg保存裸流代码流程4、主要代码5、完整源代码更多精彩内容个人内容分类汇总 音视频开发 1、概述 最近研究了一下FFmpeg开发功能实在是太强大了网上ffmpeg3、4的文章还是很多的但是学习嘛最新的还是不能放过就选了一个最新的ffmpeg n5.1.2版本和3、4版本api变化还是挺大的在这个Demo里主要使用Qt FFmpeg开发一个简单的【视频播放器】支持【保存视频流裸流】功能这里主要使用的是【软解码】需要使用硬解码的可以看之前的文章同时为了尽可能的简单这里没有进行音频解码和播放只是单独的进行视频解码播放再日常开发中经常有将播放的网络视频流图像保存到本地视频文件中的需求但是如果将图像重新编码保存则会非常消耗CPU资源裸流数据一般是H264格式的数据这里其实可以直接将网络视频流未解码的AVPacket直接保存到视频文件中不需要编码可大大降低资源占用并且直接保存裸流的代码流程不重新编码/转码保存的流程简单许多。 开发环境说明 系统Windows10、Ubuntu20.04Qt版本V5.12.5编译器MSVC2017-64、GCC/G64FFmpeg版本n5.1.2 注意如果使用了较低版本的库程序中部分功能可能会存在问题不会兼容。官方下载我使用的库 2、实现效果 ffmpeg音视频库【软解码】实现的视频播放器支持打开本地视频文件如mp4、mov、avi等、网络视频流rtsp、rtmp、http等支持视频匀速播放采用QPainter进行显示支持自适应窗口缩放视频播放支持实时开始/关闭、暂停/继续播放视频解码、线程控制、显示各部分功能分离低耦合度。采用最新的5.1.2版本ffmpeg库进行开发超详细注释信息将所有踩过的坑、解决办法、注意事项都得很写清楚。在使用ffmpeg打开网络视频流时如果是【h264裸流可以直接保存为本地文件】不需要进行编码操作。 3、FFmpeg保存裸流代码流程 白色部分 主要为打开读取网络视频流、解码流程绿色部分 主要是打开输出文件将裸流保存到文件的流程。 4、主要代码 啥也不说了直接上代码一切有注释 videodecode.h文件 /******************************************************************************* 文件名 videodecode.h* 功能 视频解码类在这个类中调用ffmpeg打开视频进行解码并且打开输出文件将h264裸流保存** 开发者 mhf* 邮箱 1603291350qq.com* 时间 2022/09/15* 备注*****************************************************************************/ #ifndef VIDEODECODE_H #define VIDEODECODE_H#include QString #include QSizestruct AVFormatContext; struct AVCodecContext; struct AVRational; struct AVPacket; struct AVFrame; struct SwsContext; struct AVBufferRef; struct AVStream; class QImage;class VideoDecode { public:VideoDecode();~VideoDecode();bool open(const QString url QString()); // 打开媒体文件或者流媒体rtmp、strp、httpQImage read(); // 读取视频图像void close(); // 关闭bool isEnd(); // 是否读取完成const qint64 pts(); // 获取当前帧显示时间private:void initFFmpeg(); // 初始化ffmpeg库整个程序中只需加载一次void showError(int err); // 显示ffmpeg执行错误时的错误信息qreal rationalToDouble(AVRational* rational); // 将AVRational转换为doublevoid clear(); // 清空读取缓冲void free(); // 释放bool openSave(); // 打开输出文件并初始化private:AVFormatContext* m_formatContext nullptr; // 解封装上下文AVCodecContext* m_codecContext nullptr; // 解码器上下文SwsContext* m_swsContext nullptr; // 图像转换上下文AVPacket* m_packet nullptr; // 数据包AVFrame* m_frame nullptr; // 解码后的视频帧int m_videoIndex 0; // 视频流索引qint64 m_totalTime 0; // 视频总时长qint64 m_totalFrames 0; // 视频总帧数qint64 m_obtainFrames 0; // 视频当前获取到的帧数qint64 m_pts 0; // 图像帧的显示时间qreal m_frameRate 0; // 视频帧率QSize m_size; // 视频分辨率大小char* m_error nullptr; // 保存异常信息bool m_end false; // 视频读取完成uchar* m_buffer nullptr; // YUV图像需要转换位RGBA图像这里保存转换后的图形数据/******** 保存裸流使用 ******************/AVFormatContext* m_formatContextSave nullptr; // 封装上下文QString m_strCodecName; // 编解码器名称AVStream* m_videoStream nullptr; // 输出视频流bool m_writeHeader false; // 是否写入文件头 };#endif // VIDEODECODE_H videodecode.cpp文件 #include videodecode.h #include QDebug #include QDir #include QImage #include QMutex #include qdatetime.hextern C { // 用C规则编译指定的代码 #include libavcodec/avcodec.h #include libavformat/avformat.h #include libavutil/avutil.h #include libswscale/swscale.h #include libavutil/imgutils.h}#define ERROR_LEN 1024 // 异常信息数组长度 #define PRINT_LOG 1VideoDecode::VideoDecode() { // initFFmpeg(); // 5.1.2版本不需要调用了m_error new char[ERROR_LEN]; }VideoDecode::~VideoDecode() {close(); }/*** brief 初始化ffmpeg库整个程序中只需加载一次* 旧版本的ffmpeg需要注册各种文件格式、解复用器、对网络库进行全局初始化。* 在新版本的ffmpeg中纷纷弃用了不需要注册了*/ void VideoDecode::initFFmpeg() {static bool isFirst true;static QMutex mutex;QMutexLocker locker(mutex);if(isFirst){// av_register_all(); // 已经从源码中删除/*** 初始化网络库,用于打开网络流媒体此函数仅用于解决旧GnuTLS或OpenSSL库的线程安全问题。* 一旦删除对旧GnuTLS和OpenSSL库的支持此函数将被弃用并且此函数不再有任何用途。*/avformat_network_init();isFirst false;} }/*** brief 打开媒体文件或者流媒体例如rtmp、strp、http* param url 视频地址* return true成功 false失败*/ bool VideoDecode::open(const QString url) {if(url.isNull()) return false;AVDictionary* dict nullptr;av_dict_set(dict, rtsp_transport, tcp, 0); // 设置rtsp流使用tcp打开如果打开失败错误信息为【Error number -135 occurred】可以切换UDP、tcp、udp_multicast、http比如vlc推流就需要使用udp打开 // av_dict_set(dict, max_delay, 3, 0); // 设置最大复用或解复用延迟以微秒为单位。当通过【UDP】 接收数据时解复用器尝试重新排序接收到的数据包因为它们可能无序到达或者数据包可能完全丢失。这可以通过将最大解复用延迟设置为零通过max_delayAVFormatContext 字段来禁用。 // av_dict_set(dict, timeout, 1000000, 0); // 以微秒为单位设置套接字 TCP I/O 超时如果等待时间过短也可能会还没连接就返回了。// 打开输入流并返回解封装上下文int ret avformat_open_input(m_formatContext, // 返回解封装上下文url.toStdString().data(), // 打开视频地址nullptr, // 如果非null此参数强制使用特定的输入格式。自动选择解封装器文件格式dict); // 参数设置// 释放参数字典if(dict){av_dict_free(dict);}// 打开视频失败if(ret 0){showError(ret);free();return false;}// 读取媒体文件的数据包以获取流信息。ret avformat_find_stream_info(m_formatContext, nullptr);if(ret 0){showError(ret);free();return false;}m_totalTime m_formatContext-duration / (AV_TIME_BASE / 1000); // 计算视频总时长毫秒 #if PRINT_LOGqDebug() QString(视频总时长%1 ms[%2]).arg(m_totalTime).arg(QTime::fromMSecsSinceStartOfDay(int(m_totalTime)).toString(HH:mm:ss zzz)); #endif// 通过AVMediaType枚举查询视频流ID也可以通过遍历查找最后一个参数无用m_videoIndex av_find_best_stream(m_formatContext, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);if(m_videoIndex 0){showError(m_videoIndex);free();return false;}AVStream* videoStream m_formatContext-streams[m_videoIndex]; // 通过查询到的索引获取视频流// 获取视频图像分辨率AVStream中的AVCodecContext在新版本中弃用改为使用AVCodecParametersm_size.setWidth(videoStream-codecpar-width);m_size.setHeight(videoStream-codecpar-height);m_frameRate rationalToDouble(videoStream-avg_frame_rate); // 视频帧率// 通过解码器ID获取视频解码器新版本返回值必须使用constconst AVCodec* codec avcodec_find_decoder(videoStream-codecpar-codec_id);m_totalFrames videoStream-nb_frames;m_strCodecName codec-name;#if PRINT_LOGqDebug() QString(分辨率[w:%1,h:%2] 帧率%3 总帧数%4 解码器%5).arg(m_size.width()).arg(m_size.height()).arg(m_frameRate).arg(m_totalFrames).arg(codec-name); #endif// 分配AVCodecContext并将其字段设置为默认值。m_codecContext avcodec_alloc_context3(codec);if(!m_codecContext){ #if PRINT_LOGqWarning() 创建视频解码器上下文失败; #endiffree();return false;}// 使用视频流的codecpar为解码器上下文赋值ret avcodec_parameters_to_context(m_codecContext, videoStream-codecpar);if(ret 0){showError(ret);free();return false;}m_codecContext-flags2 | AV_CODEC_FLAG2_FAST; // 允许不符合规范的加速技巧。m_codecContext-thread_count 8; // 使用8线程解码// 初始化解码器上下文如果之前avcodec_alloc_context3传入了解码器这里设置NULL就可以ret avcodec_open2(m_codecContext, nullptr, nullptr);if(ret 0){showError(ret);free();return false;}// 分配AVPacket并将其字段设置为默认值。m_packet av_packet_alloc();if(!m_packet){ #if PRINT_LOGqWarning() av_packet_alloc() Error; #endiffree();return false;}// 分配AVFrame并将其字段设置为默认值。m_frame av_frame_alloc();if(!m_frame){ #if PRINT_LOGqWarning() av_frame_alloc() Error; #endiffree();return false;}// 分配图像空间int size av_image_get_buffer_size(AV_PIX_FMT_RGBA, m_size.width(), m_size.height(), 4);/*** 【注意】这里可以多分配一些否则如果只是安装size分配大部分视频图像数据拷贝没有问题* 但是少部分视频图像在使用sws_scale()拷贝时会超出数组长度在使用使用msvc debug模式时delete[] m_buffer会报错HEAP CORRUPTION DETECTED: after Normal block(#32215) at 0x000001AC442830370.CRT delected that the application wrote to memory after end of heap buffer* 特别是这个视频流http://vfx.mtime.cn/Video/2019/02/04/mp4/190204084208765161.mp4*/m_buffer new uchar[size 1000]; // 这里多分配1000个字节就基本不会出现拷贝超出的情况了反正不缺这点内存 // m_image new QImage(m_buffer, m_size.width(), m_size.height(), QImage::Format_RGBA8888); // 这种方式分配内存大部分情况下也可以但是因为存在拷贝超出数组的情况delete时也会报错m_end false;return openSave(); }/*** brief* return*/ QImage VideoDecode::read() {// 如果没有打开则返回if(!m_formatContext){return QImage();}// 读取下一帧数据int readRet av_read_frame(m_formatContext, m_packet);if(readRet 0){avcodec_send_packet(m_codecContext, m_packet); // 读取完成后向解码器中传如空AVPacket否则无法读取出最后几帧}else{if(m_packet-stream_index m_videoIndex) // 如果是图像数据则进行解码{if(m_formatContextSave){// 由于保存的m_formatContextSave只创建了一个视频流而读取到的图像的流索引不一定为0可能会出现错误【Invalid packet stream index: 1】// 所以这里需要将stream_index指定为和m_formatContextSave中视频流索引相同因为就一个流所以直接设置为0m_packet-stream_index 0;av_write_frame(m_formatContextSave, m_packet); // 将数据包写入输出媒体文件}// 计算当前帧时间毫秒 #if 1 // 方法一适用于所有场景但是存在一定误差m_packet-pts qRound64(m_packet-pts * (1000 * rationalToDouble(m_formatContext-streams[m_videoIndex]-time_base)));m_packet-dts qRound64(m_packet-dts * (1000 * rationalToDouble(m_formatContext-streams[m_videoIndex]-time_base))); #else // 方法二适用于播放本地视频文件计算每一帧时间较准但是由于网络视频流无法获取总帧数所以无法适用m_obtainFrames;m_packet-pts qRound64(m_obtainFrames * (qreal(m_totalTime) / m_totalFrames)); #endif// 将读取到的原始数据包传入解码器int ret avcodec_send_packet(m_codecContext, m_packet);if(ret 0){showError(ret);}}}av_packet_unref(m_packet); // 释放数据包引用计数-1为0时释放空间int ret avcodec_receive_frame(m_codecContext, m_frame);if(ret 0){av_frame_unref(m_frame);if(readRet 0){m_end true; // 当无法读取到AVPacket并且解码器中也没有数据时表示读取完成}return QImage();}m_pts m_frame-pts;// 为什么图像转换上下文要放在这里初始化呢是因为m_frame-format如果使用硬件解码解码出来的图像格式和m_codecContext-pix_fmt的图像格式不一样就会导致无法转换为QImageif(!m_swsContext){// 获取缓存的图像转换上下文。首先校验参数是否一致如果校验不通过就释放资源然后判断上下文是否存在如果存在直接复用如不存在进行分配、初始化操作m_swsContext sws_getCachedContext(m_swsContext,m_frame-width, // 输入图像的宽度m_frame-height, // 输入图像的高度(AVPixelFormat)m_frame-format, // 输入图像的像素格式m_size.width(), // 输出图像的宽度m_size.height(), // 输出图像的高度AV_PIX_FMT_RGBA, // 输出图像的像素格式SWS_BILINEAR, // 选择缩放算法(只有当输入输出图像大小不同时有效),一般选择SWS_FAST_BILINEARnullptr, // 输入图像的滤波器信息, 若不需要传NULLnullptr, // 输出图像的滤波器信息, 若不需要传NULLnullptr); // 特定缩放算法需要的参数(?)默认为NULLif(!m_swsContext){ #if PRINT_LOGqWarning() sws_getCachedContext() Error; #endiffree();return QImage();}}// AVFrame转QImageuchar* data[] {m_buffer};int lines[4];av_image_fill_linesizes(lines, AV_PIX_FMT_RGBA, m_frame-width); // 使用像素格式pix_fmt和宽度填充图像的平面线条大小。ret sws_scale(m_swsContext, // 缩放上下文m_frame-data, // 原图像数组m_frame-linesize, // 包含源图像每个平面步幅的数组0, // 开始位置m_frame-height, // 行数data, // 目标图像数组lines); // 包含目标图像每个平面的步幅的数组QImage image(m_buffer, m_frame-width, m_frame-height, QImage::Format_RGBA8888);av_frame_unref(m_frame);return image; }/*** brief 关闭视频播放并释放内存*/ void VideoDecode::close() {clear();free();m_totalTime 0;m_videoIndex 0;m_totalFrames 0;m_obtainFrames 0;m_pts 0;m_frameRate 0;m_size QSize(0, 0); }/*** brief 视频是否读取完成* return*/ bool VideoDecode::isEnd() {return m_end; }/*** brief 返回当前帧图像播放时间* return*/ const qint64 VideoDecode::pts() {return m_pts; }/*** brief 显示ffmpeg函数调用异常信息* param err*/ void VideoDecode::showError(int err) { #if PRINT_LOGmemset(m_error, 0, ERROR_LEN); // 将数组置零av_strerror(err, m_error, ERROR_LEN);qWarning() DecodeVideo Error m_error; #elseQ_UNUSED(err) #endif }/*** brief 将AVRational转换为double用于计算帧率* param rational* return*/ qreal VideoDecode::rationalToDouble(AVRational* rational) {qreal frameRate (rational-den 0) ? 0 : (qreal(rational-num) / rational-den);return frameRate; }/*** brief 清空读取缓冲*/ void VideoDecode::clear() {if(m_formatContextSave m_writeHeader){av_write_trailer(m_formatContextSave); // 写入文件尾m_writeHeader false;avformat_free_context(m_formatContextSave);m_formatContext nullptr;m_videoStream nullptr;}// 因为avformat_flush不会刷新AVIOContext (s-pb)。如果有必要在调用此函数之前调用avio_flush(s-pb)。if(m_formatContext m_formatContext-pb){avio_flush(m_formatContext-pb);}if(m_formatContext){avformat_flush(m_formatContext); // 清理读取缓冲} }void VideoDecode::free() {// 释放上下文swsContext。if(m_swsContext){sws_freeContext(m_swsContext);m_swsContext nullptr; // sws_freeContext不会把上下文置NULL}// 释放编解码器上下文和与之相关的所有内容并将NULL写入提供的指针if(m_codecContext){avcodec_free_context(m_codecContext);}// 关闭并失败m_formatContext并将指针置为nullif(m_formatContext){avformat_close_input(m_formatContext);}if(m_packet){av_packet_free(m_packet);}if(m_frame){av_frame_free(m_frame);}if(m_buffer){delete [] m_buffer;m_buffer nullptr;} }/*** brief 打开输出文件* return*/ bool VideoDecode::openSave() {QDir dir;if(!dir.exists(./Videos)){dir.mkdir(./Videos);}QString strName QString(./Videos/%1.h264).arg(QDateTime::currentDateTime().toString(yyyy-MM-dd HH-mm-ss));int ret avformat_alloc_output_context2(m_formatContextSave, nullptr, m_strCodecName.toStdString().data(), strName.toStdString().data()); // 这里使用和解码一样的编码器防止保存的图像颜色出问题if(ret 0){free();showError(ret);return false;}// 创建并初始化AVIOContext以访问url所指示的资源。ret avio_open(m_formatContextSave-pb, strName.toStdString().data(), AVIO_FLAG_WRITE);if(ret 0){free();showError(ret);return false;}// 向媒体文件添加新流m_videoStream avformat_new_stream(m_formatContextSave, nullptr);if(!m_videoStream){free();showError(AVERROR(ENOMEM));return false;}//拷贝一些参数给codecpar赋值(这里使用编码器上下文进行赋值)ret avcodec_parameters_from_context(m_videoStream-codecpar, m_codecContext);if(ret 0){free();showError(ret);return false;}// 写入文件头ret avformat_write_header(m_formatContextSave, nullptr);if(ret 0){free();showError(ret);return false;}m_writeHeader true;qDebug() 开始录制视频;return true; } 5、完整源代码 githubgitee ☁️           ☁️                ☁️    ⁣            |/               ☁️   ☁️         ☁️ _
http://www.w-s-a.com/news/326114/

相关文章:

  • 快递网站策划怎么做ppt长春建设信息网站
  • 做服装搭配图的网站有哪些经营一个网站要怎么做
  • 呼市品牌网站建设那家好增城住房和建设局网站
  • 网站首页布局设计代码太仓网站开发建设服务
  • 学校网站建设与管理porto wordpress模板
  • 余姚做网站公司网站建设有哪些基本流程
  • 门户网站建设的报价百度医生在线问诊
  • 北京公司注册在哪个网站浏览器打开网址404
  • 廊坊做网站公司绣花图案设计网站
  • 网站空间租用哪个好购物网站建设模板图片
  • 建设银行包头分行网站泰安网签成交量最新
  • 手机微网站与微官网现在去成都需要隔离吗
  • 学校的二级网站怎么建设深圳企业网站制作设计
  • 自己做qq头像静态的网站网站建设是属于软件开发费吗
  • 举报网站建设做网站之前的工作
  • 用QQ群做网站排名个人网站制作协议
  • 做茶叶网站的素材天津网站营销
  • 网站设计建设流程图微信端的网站开发python
  • 湖州网站seo优化网站改域名备案
  • dedecms怎么制作网站合肥电商网站开发
  • 网站开发通用流程图做flash的网站
  • 营销型网站有哪些平台网站建设藤设计
  • 网站需求分析网站建设美食网站建设多少钱
  • 有专门做网站的吗建德网站
  • 做网站要买服务器吗单页设计思路
  • 一 电子商务网站建设规划网站开发前端框架和后端框架
  • 自助网站建设系统软件自己免费建设网站
  • 百度微建站access如何与网站连接数据库
  • ppt素材免费网站网站正能量晚上免费软件
  • 个人淘宝客网站如何备案搭建一个平台要多少钱