十二、h.264解码

前言


测试环境:

  • ffmpeg的4.3.2自行编译版本
  • windows环境
  • qt5.12

完整代码:

H264DncodeThread.h

#ifndef H264DNCODETHREAD_H
#define H264DNCODETHREAD_H#include <QObject>
#include <QThread>extern "C" {
#include <libavutil/avutil.h>
}typedef struct {const char *filename;int width;int height;AVPixelFormat pixFmt;int fps;
} VideoDecodeSpec;class H264DncodeThread : public QThread
{Q_OBJECT
public:explicit H264DncodeThread(QObject *parent = nullptr);~H264DncodeThread();static void h264Decode(const char *inFilename,VideoDecodeSpec &out);signals:// QThread interface
protected:virtual void run() override;
};#endif // H264DNCODETHREAD_H

H264DncodeThread.cpp

#include "h264dncodethread.h"#include <QDebug>
#include <QFile>extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
}#define ERROR_BUF(ret) \char errbuf[1024]; \av_strerror(ret, errbuf, sizeof (errbuf));// 输入缓冲区的大小
#define IN_DATA_SIZE 4096static int frameIdx = 0;static int decode(AVCodecContext *ctx,AVPacket *pkt,AVFrame *frame,QFile &outFile) {// 发送压缩数据到解码器int ret = avcodec_send_packet(ctx, pkt);if (ret < 0) {ERROR_BUF(ret);qDebug() << "avcodec_send_packet error" << errbuf;return ret;}while (true) {// 获取解码后的数据ret = avcodec_receive_frame(ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {return 0;} else if (ret < 0) {ERROR_BUF(ret);qDebug() << "avcodec_receive_frame error" << errbuf;return ret;}qDebug() << "解码出第" << ++frameIdx << "帧";// 将解码后的数据写入文件// 写入Y平面outFile.write((char *) frame->data[0],frame->linesize[0] * ctx->height);// 写入U平面outFile.write((char *) frame->data[1],frame->linesize[1] * ctx->height >> 1);// 写入V平面outFile.write((char *) frame->data[2],frame->linesize[2] * ctx->height >> 1);//        qDebug() << frame->data[0] << frame->data[1] << frame->data[2];/** frame->data[0] 0xd08c400 0x8c400* frame->data[1] 0xd0d79c0 0xd79c0* frame->data[2] 0xd0ea780 0xea780** frame->data[1] - frame->data[0] = 308672 = y平面的大小* frame->data[2] - frame->data[1] = 77248 = u平面的大小** y平面的大小 640x480*1 = 307200* u平面的大小 640x480*0.25 = 76800* v平面的大小 640x480*0.25*///        // 将解码后的数据写入文件(460800)
//        int imgSize = av_image_get_buffer_size(ctx->pix_fmt, ctx->width, ctx->height, 1);
//        // outFile.write((char *) frame->data[0], frame->linesize[0]);
//        outFile.write((char *) frame->data[0], imgSize);}
}H264DncodeThread::H264DncodeThread(QObject *parent) : QThread(parent)
{// 当监听到线程结束时(finished),就调用deleteLater回收内存connect(this,&H264DncodeThread::finished,this,[=](){this->deleteLater();qDebug()<<"H264DncodeThread线程结束,线程指针被dlete";});
}H264DncodeThread::~H264DncodeThread()
{// 断开所有的连接disconnect();//强制关闭窗口时,线程也能安全关闭requestInterruption();wait();qDebug()<<"H264DncodeThread析构函数";
}void H264DncodeThread::h264Decode(const char *inFilename, VideoDecodeSpec &out)
{// 返回结果int ret = 0;// 用来存放读取的输入文件数据(h264)// 加上AV_INPUT_BUFFER_PADDING_SIZE是为了防止某些优化过的reader一次性读取过多导致越界char inDataArray[IN_DATA_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];char *inData = inDataArray;// 每次从输入文件中读取的长度(h264)// 输入缓冲区中,剩下的等待进行解码的有效数据长度int inLen;// 是否已经读取到了输入文件的尾部int inEnd = 0;// 文件QFile inFile(inFilename);QFile outFile(out.filename);// 解码器AVCodec *codec = nullptr;// 上下文AVCodecContext *ctx = nullptr;// 解析器上下文AVCodecParserContext *parserCtx = nullptr;// 存放解码前的数据(h264)AVPacket *pkt = nullptr;// 存放解码后的数据(yuv)AVFrame *frame = nullptr;// 获取解码器//    codec = avcodec_find_decoder_by_name("h264");codec = avcodec_find_decoder(AV_CODEC_ID_H264);if (!codec) {qDebug() << "decoder not found";return;}// 初始化解析器上下文parserCtx = av_parser_init(codec->id);if (!parserCtx) {qDebug() << "av_parser_init error";return;}// 创建上下文ctx = avcodec_alloc_context3(codec);if (!ctx) {qDebug() << "avcodec_alloc_context3 error";goto end;}// 创建AVPacketpkt = av_packet_alloc();if (!pkt) {qDebug() << "av_packet_alloc error";goto end;}// 创建AVFrameframe = av_frame_alloc();if (!frame) {qDebug() << "av_frame_alloc error";goto end;}// 打开解码器ret = avcodec_open2(ctx, codec, nullptr);if (ret < 0) {ERROR_BUF(ret);qDebug() << "avcodec_open2 error" << errbuf;goto end;}// 打开文件if (!inFile.open(QFile::ReadOnly)) {qDebug() << "file open error:" << inFilename;goto end;}if (!outFile.open(QFile::WriteOnly)) {qDebug() << "file open error:" << out.filename;goto end;}// 读取文件数据do {inLen = inFile.read(inDataArray, IN_DATA_SIZE);// 设置是否到了文件尾部inEnd = !inLen;// 让inData指向数组的首元素inData = inDataArray;// 只要输入缓冲区中还有等待进行解码的数据while (inLen > 0 || inEnd) {// 到了文件尾部(虽然没有读取任何数据,但也要调用av_parser_parse2,修复bug)// 经过解析器解析ret = av_parser_parse2(parserCtx, ctx,&pkt->data, &pkt->size,(uint8_t *) inData, inLen,AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);if (ret < 0) {ERROR_BUF(ret);qDebug() << "av_parser_parse2 error" << errbuf;goto end;}// 跳过已经解析过的数据inData += ret;// 减去已经解析过的数据大小inLen -= ret;qDebug() << inEnd << pkt->size << ret;// 解码if (pkt->size > 0 && decode(ctx, pkt, frame, outFile) < 0) {goto end;}// 如果到了文件尾部if (inEnd) break;}} while (!inEnd);// 刷新缓冲区//    pkt->data = nullptr;//    pkt->size = 0;//    decode(ctx, pkt, frame, outFile);decode(ctx, nullptr, frame, outFile);// 赋值输出参数out.width = ctx->width;out.height = ctx->height;out.pixFmt = ctx->pix_fmt;// 用framerate.num获取帧率,并不是time_base.denout.fps = ctx->framerate.num;end:inFile.close();outFile.close();av_packet_free(&pkt);av_frame_free(&frame);av_parser_close(parserCtx);avcodec_free_context(&ctx);// bug fix
// https://patchwork.ffmpeg.org/project/ffmpeg/patch/tencent_609A2E9F73AB634ED670392DD89A63400008@qq.com///
//    while ((inLen = inFile.read(inDataArray, IN_DATA_SIZE)) > 0)
//        while (inLen > 0) {
//            // 经过解析器解析
//            ret = av_parser_parse2(parserCtx, ctx,
//                                   &pkt->data, &pkt->size,
//                                   (uint8_t *) inData, inLen,
//                                   AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);//            if (ret < 0) {
//                ERROR_BUF(ret);
//                qDebug() << "av_parser_parse2 error" << errbuf;
//                goto end;
//            }//            // 跳过已经解析过的数据
//            inData += ret;
//            // 减去已经解析过的数据大小
//            inLen -= ret;//            // 解码
//            if (pkt->size > 0 && decode(ctx, pkt, frame, outFile) < 0) {
//                goto end;
//            }
//        }
//    }
}void H264DncodeThread::run()
{VideoDecodeSpec out;out.filename = "E:/media/out-yuv420p-decode.yuv";h264Decode("E:/media/out-yuv420p.h264", out);qDebug() << out.width << out.height<< out.fps << av_get_pix_fmt_name(out.pixFmt);
}

线程调用:

void MainWindow::on_pushButton_h264_decode_clicked()
{m_pH264DncodeThread=new H264DncodeThread(this);m_pH264DncodeThread->start();
}

注意:.h文件中提前声明了以下全局变量

	H264DncodeThread *m_pH264DncodeThread=nullptr;

注意:本文为个人记录,新手照搬可能会出现各种问题,请谨慎使用


码字不易,如果这篇博客对你有帮助,麻烦点赞收藏,非常感谢!有不对的地方

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

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

相关文章

【论文阅读笔记】Emu Edit: Precise Image Editing via Recognition and Generation Tasks

【论文阅读笔记】Emu Edit: Precise Image Editing via Recognition and Generation Tasks 论文阅读笔记论文信息摘要背景方法结果额外 关键发现作者动机相关工作1. 使用输入和编辑图像的对齐和详细描述来执行特定的编辑2. 另一类图像编辑模型采用输入掩码作为附加输入 。3. 为…

鸿蒙4.0开发笔记之ArkTs语言基础与基本组件结构(四)

文章声明&#xff1a;本文关于HarmonyOS系统的部分内容和描述借鉴于华为官网的“HarmonyOS开发者学堂”&#xff0c;有需要的也可以进入官网查看。<HarmonyOS第一课>ArkTS开发语言介绍 一、ArkTs语言介绍 ArkTS是鸿蒙系统&#xff08;HarmonyOS&#xff09;优选的主力应…

设计模式-创建型模式-工厂方法模式

一、什么是工厂方法模式 工厂模式又称工厂方法模式&#xff0c;是一种创建型设计模式&#xff0c;其在父类中提供一个创建对象的方法&#xff0c; 允许子类决定实例化对象的类型。工厂方法模式是目标是定义一个创建产品对象的工厂接口&#xff0c;将实际创建工作推迟到子类中。…

解读可解释性机器学习:理解解释性基准模型(EBM)

解读可解释性机器学习&#xff1a;理解解释性基准模型&#xff08;EBM&#xff09; 近年来&#xff0c;随着机器学习模型的复杂性不断增加&#xff0c;研究人员和从业者对模型的可解释性提出了更高的要求。可解释性机器学习&#xff08;Explainable Machine Learning, XAI&…

SHAP - 机器学习模型可解释性工具

github地址&#xff1a;shap/docs/index.rst at master shap/shap (github.com) SHAP使用文档&#xff1a;欢迎使用 SHAP 文档 — SHAP 最新文档 SHAP介绍 SHAP&#xff08;SHapley Additive exPlanations&#xff09;是一种用于解释预测结果的方法&#xff0c;它基于Shapley…

实现el-input-number数字框带单位

实现的效果展示&#xff0c;可以是前缀单位&#xff0c;也可以是后缀单位。实现的思路就是动态修改伪元素 ::before 和 ::after 的 content值 实现二次封装数字框的代码如下&#xff1a; <template><el-input-numberref"inputNumber"v-model"inputVal…

opencv-直方图

直方图是一种对图像亮度分布的统计表示&#xff0c;它显示了图像中每个灰度级别的像素数量。在OpenCV中&#xff0c;你可以使用cv2.calcHist() 函数计算直方图。 以下是一个简单的示例&#xff0c;演示如何计算和绘制图像的直方图&#xff1a; import cv2 import numpy as np …

【C++容器】优先级队列 仿函数 反向迭代器

优先级队列&#xff0c;仿函数&#xff0c;反向迭代器 优先级队列认识优先级队列模拟实现优先级队列 浅谈仿函数仿函数的大致了解仿函数的实现 反向迭代器什么是反向迭代器&#xff1f;反向迭代器的实现 结语 优先级队列 认识优先级队列 优先级队列&#xff08;priority_queue…

Doris分区与分桶(八)

接上篇----------Doris 建表示例 Doris 支持两层的数据划分。第一层是 Partition&#xff0c;支持 Range 和 List 的划分方式。第二层是 Bucket&#xff08;Tablet&#xff09;&#xff0c;仅支持 Hash 的划分方式。 也可以仅使用一层分区。使用一层分区时&#xff0c;只支持…

低成本打造便携式无线网络攻防学习环境

1.摘要 一直以来, 无线网络安全问题与大众的个人隐私息息相关, 例如: 为了节省流量, 连接到一个看似安全的免费WiFi, 在使用过程中泄露自己的各类密码信息甚至银行卡账号密码信息。随着家用智能电器的普及, 家中的各类智能设备连入家里的无线网络, 却突然失灵, 甚至无法正常连…

模拟Spring源码思想,手写源码,理解注解

1、BeanDefinition package com.csdn.myspring; import lombok.AllArgsConstructor; import lombok.Data; Data AllArgsConstructor public class BeanDefinition {private String beanName;private Class beanClass; }2、扫描包的工具类MyTools package com.csdn.myspring; im…

@Scheduled注解 定时任务讲解

用于在Java Spring框架中定时执行特定任务的注解 Scheduled&#xff0c;它能够指定方法在特定时间间隔或特定时间点执行。默认参数是cron&#xff0c;cron参数被用来定义一个Cron表达式&#xff0c;它代表了任务执行的时间规则 参数如下 Cron 这是是一种时间表达式&#xff…

【应用程序启动过程-三种加载控制器的方式-上午内容复习 Objective-C语言】

一、我们先来回忆一下,上午所有内容 1.首先呢,我们先说的是这个“应用程序启动过程”, 应用程序启动过程里面,有三方面内容 1)UIApplication对象介绍 2)AppDelegate对象介绍 3)应用程序启动过程 现在不知道大家对这个应用程序启动过程有印象吗, 2.首先,这个UIAp…

MySQL数据库时间计算的用法

今天给大家分享如何通过MySQL内置函数实现时间的转换和计算&#xff0c;在工作当中&#xff0c;测试人员经常需要查询数据库表的日期时间&#xff0c;但发现开发人员存入数据库表的形式都是时间戳形式&#xff0c;不利于测试人员查看&#xff0c;测试人员只能利用工具对时间戳进…

【 顺序表经典算法—移除元素和合并两个有序数组】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 经典算法OJ题1&#xff1a; 移除元素 解法一、逐个判断 解法二、双指针覆盖 经典算法OJ题2&#xff1a; 合并两个有序数组 OJ题分为两个类型&#xff1a; 总结 前言…

MAX/MSP SDK学习07:list传递

实现自定义Obejct&#xff0c;要求将传入的一组数据100后传出。 #include "ext.h" #include "ext_obex.h" typedef struct _listTrans {t_object ob;void* outLet;t_atom* fArr;long listNum;} t_listTrans;void* listTrans_new(t_symbol* s, long arg…

14.Python 模块

目录 1. 使用模块2. 使用包3. 常用模块3.1 日期和时间3.2 伪随机数3.3 摘要算法3.4 JSON 处理3.5 图像处理 模块是Python用来组织代码的一种方法&#xff0c;包是Python用来组织模块的一种方法。 1. 使用模块 Python 把能够相互包含&#xff0c;且有组织的代码段称为模块&…

.NET面试题1

1.什么是C#&#xff1f; C#&#xff08;读作"C sharp"&#xff09;是一种通用的、面向对象的编程语言&#xff0c;由Microsoft开发。它是一种静态类型语言&#xff0c;支持强类型检查和面向对象编程&#xff08;OOP&#xff09;的概念。C#主要用于开发Windows应用程序…

Bug等级划分

Bug是指在程序或系统中存在的错误、缺陷或异常&#xff0c;是由于编码错误、设计问题、逻辑错误或其他因素导致的。 常见的Bug分类方法 功能性Bug与软件的功能有关&#xff0c;软件无法正常工作、功能与需求不符或功能执行不正确。 用户界面Bug与软件的用户界面有关&#xff…

Unity中Shader双向反射分布函数BRDF

文章目录 前言一、渲染方程二、什么是BxDF1、BSSRDF2、BRDF3、BTDF4、BSDF 三、迪士尼原则的BRDF四、迪士尼原则的BRDF的参数五、在Unity中看一下默认Shader的这些参数六、在这里记录一下使用 Blender 和 SubstancePainter 的流程1、在Blender中导出模型为 .obj 格式2、在Subst…