音视频学习(十八)——使用ffmepg实现视音频解码
视频解码
初始化
- 视频常用的编解码器id定义(以h264和h265为例)
// 定义在ffmpeg\include\libavcodec\avcodec.h
AV_CODEC_ID_H264
AV_CODEC_ID_H265
- 查找解码器:根据编解码id查看解码器
AVCodec* pCodecVideo = avcodec_find_decoder(codecID);
if (!pCodecVideo)
{printf("avcodec_find_decoder failed\n");return -1;
}
- 申请编码器上下文结构体内存,保存了视频编解码相关信息
AVCodecContext* pCodecCtxVideo = avcodec_alloc_context3(pCodecVideo);
if (!pCodecCtxVideo)
{printf("avcodec_alloc_context3 error\n");return -1;
}
- 打开解码器
if (avcodec_open2(pCodecCtxVideo, pCodecVideo, NULL) < 0)
{printf("avcodec_open2 failed\n");return -1;
}
- 申请帧内存:存储一帧解码后像素(采样)数据
AVFrame* pFrameVideo = av_frame_alloc();
if (!pFrameVideo)
{printf("av_frame_alloc failed\n");return -1;
}
视频解码
- 解码一帧压缩数据
// data和len为压缩数据的指针和大小AVPacket packet;
av_init_packet(&packet);
packet.data = (uint8_t*)data;
packet.size = len;int got_picture = 0;
if (avcodec_decode_video2(pCodecCtxVideo, pFrameVideo, &got_picture, &packet) < 0)
{printf("avcodec_decode_video2 failed\n");return -1;
}
- 获取帧大小
// 以YUV420为例
int frameSize = avpicture_get_size(AV_PIX_FMT_YUV420P, pFrameVideo->linesize[0], pFrameVideo->height);
- 获取上下文,获取用于转码的参数**(初始化一次)**
// pFrameVideo->width:输入帧数据宽
// pFrameVideo->height:输入帧数据高
// pCodecCtxVideo->pix_fmt:帧数据格式
// pFrameVideo->width:输出帧数据宽
// pFrameVideo->height:输出帧数据高
// AV_PIX_FMT_YUV420P:输出帧数据格式,例如YUV420、RGB32等
// SWS_BICUBIC:视频像素数据格式转换算法类型
SwsContext* imgConvertCtx = sws_getContext(pFrameVideo->width, pFrameVideo->height,pCodecCtxVideo->pix_fmt,pFrameVideo->width, pFrameVideo->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
- 缓冲区分配缓存**(初始化一次)**
int frameSize = avpicture_get_size(AV_PIX_FMT_YUV420P, pFrameVideo->width, pFrameVideo->height);
AVFrame* picture = av_frame_alloc();
uint8_t* pictureBuf = new uint8_t[frameSize];
- 初始化缓冲区**(初始化一次)**
avpicture_fill((AVPicture *)m_picture, m_pictureBuf, AV_PIX_FMT_YUV420P, pFrameVideo->width, pFrameVideo->height);
- 图片转换**(针对实时流或读取的文件流,循环调用)**
sws_scale(imgConvertCtx, (const uint8_t* const*)pFrameVideo->data, pFrameVideo->linesize, 0, pFrameVideo->height, picture->data, picture->linesize);
解码关闭
if (nullptr != pCodecCtxVideo)
{avcodec_close(pCodecCtxVideo);av_free(pCodecCtxVideo);pCodecCtxVideo = nullptr;
}if (nullptr != pFrameVideo)
{av_frame_free(&pFrameVideo);pFrameVideo = nullptr;
}if (nullptr != picture)
{av_frame_free(&picture);picture = nullptr;
}if (nullptr != pictureBuf)
{delete[] pictureBuf;pictureBuf = nullptr;
}if (nullptr != imgConvertCtx)
{sws_freeContext(imgConvertCtx);imgConvertCtx = nullptr;
}
音频解码
初始化
- 音频常用的编解码器id定义
AV_CODEC_ID_PCM_ALAW
AV_CODEC_ID_PCM_MULAW
AV_CODEC_ID_FIRST_AUDIO
AV_CODEC_ID_AAC
- 查找解码器:根据编解码id查看解码器
AVCodec* pCodecAudio = avcodec_find_decoder(codecID);
if (!pCodecAudio)
{printf("audio avcodec_find_decoder failed\n");return -1;
}
- 申请编码器上下文结构体内存,保存了音频编解码相关信息
AVCodecContext* pCodecCtxAudio = avcodec_alloc_context3(pCodecAudio);
if (!pCodecCtxAudio)
{printf("audio avcodec_alloc_context3 failed\n");return -1;
}
- 打开解码器
int audioCodecType = (int)codec;
switch (audioCodecType)
{case CODEC_AUDIO_AAC:break;case CODEC_AUDIO_MP3:break;case CODEC_AUDIO_G711:case CODEC_AUDIO_G711U:pCodecCtxAudio->codec_type = AVMEDIA_TYPE_AUDIO;pCodecCtxAudio->sample_fmt = AV_SAMPLE_FMT_S16;pCodecCtxAudio->sample_rate = 8000;pCodecCtxAudio->channel_layout = AV_CH_LAYOUT_MONO;pCodecCtxAudio->channels = 1;break;case CODEC_AUDIO_G7231:break;case CODEC_AUDIO_G7221:break;default:break;
}pCodecCtxAudio->codec_id = codecID;
int ret = avcodec_open2(pCodecCtxAudio, pCodecAudio, NULL);
if (ret < 0)
{printf("audio avcodec_open2 failed\n");return -1;
}
- 申请内存和初始化参数
AVFrame* frameAudio = av_frame_alloc();
if (!frameAudio)
{printf("audio av_frame_alloc failed\n");return -1;
}AVPacket* audioPacket = av_packet_alloc();
if (!audioPacket)
{printf("av_packet_alloc failed\n");return -1;
}
av_init_packet(audioPacket);
音频解码
- 解码一帧音频数据
audioPacket->data = (uint8_t*)data;
audioPacket->size = datalen;int ret = avcodec_send_packet(m_pCodecCtxAudio, m_audioPacket);
if (ret < 0)
{av_packet_unref(audioPacket);printf("audio avcodec_send_packet failed\n");return -1;
}
- 接收一帧数据
ret = avcodec_receive_frame(m_pCodecCtxAudio, m_frameAudio);
if (ret < 0)
{return -1;
}
- 设置输入和输出音频信息**(执行一次)**
// 分配SwrContext
SwrContext* audioSwrCtx = swr_alloc();
int channelLayout = av_get_default_channel_layout(frameAudio->channels);// audioSwrCtx:重采样申请的内存。如果传NULL,内部会申请一块内存,非NULL可以复用之前的内存
// AV_CH_LAYOUT_MONO:目标声道
// AV_SAMPLE_FMT_S16:目标采样格式
// frameAudio->sample_rate:目标采样率
// channelLayout:原始声道布局
// pCodecCtxAudio->sample_fmt:原始采样格式
// frameAudio->sample_rate:原始采样率
// 设置输入和输出的音频信息
swr_alloc_set_opts(audioSwrCtx, AV_CH_LAYOUT_MONO, AV_SAMPLE_FMT_S16,frameAudio->sample_rate,channelLayout, pCodecCtxAudio->sample_fmt, frameAudio->sample_rate, 0, NULL);// 设置用户参数后初始化上下文
swr_init(audioSwrCtx);
- 重采样转换(循环执行)
// audioSwrCtx:音频重采样的上下文
// audioBuffer:输出的指针。传递的输出的数组
// 1024*256:输出的样本数量,不是字节数。单通道的样本数量。
// (const uint8_t**)frameAudio->data:输入的数组,AVFrame解码出来的DATA
// frameAudio->nb_samples:输入的单通道的样本数量。
// 以单声道为例
int len = swr_convert(audioSwrCtx, &audioBuffer, 1024*256,(const uint8_t**)frameAudio->data,frameAudio->nb_samples);// 获取音频大小
av_get_channel_layout_nb_channels(AV_CH_LAYOUT_MONO);
int bufSize = av_samples_get_buffer_size(NULL, av_get_channel_layout_nb_channels(AV_CH_LAYOUT_MONO),frameAudio->nb_samples,AV_SAMPLE_FMT_S16, 0);
解码关闭
if (nullptr != pCodecCtxAudio)
{avcodec_close(pCodecCtxAudio);av_free(pCodecCtxAudio);pCodecCtxAudio = nullptr;
}if (nullptr != frameAudio)
{av_frame_free(&frameAudio);frameAudio = nullptr;
}if (nullptr != audioPacket)
{av_packet_unref(audioPacket);av_packet_free(&audioPacket);audioPacket = nullptr;
}if (nullptr != audioSwrCtx)
{swr_free(&audioSwrCtx);audioSwrCtx = nullptr;
}// 其他资源释放
相关文章:
音视频学习(十八)——使用ffmepg实现视音频解码
视频解码 初始化 视频常用的编解码器id定义(以h264和h265为例) // 定义在ffmpeg\include\libavcodec\avcodec.h AV_CODEC_ID_H264 AV_CODEC_ID_H265查找解码器:根据编解码id查看解码器 AVCodec* pCodecVideo avcodec_find_decoder(codec…...
nginx的GeoIP模块
使用场景 过滤指定地区/国家的IP,一般是国外IP禁止请求。 使用geoip模块实现不同国家的请求被转发到不同国家的nginx服务器,也就是根据国家负载均衡。 前置知识 GeoIP是什么? 官网地址 https://www.maxmind.com/en/home包含IP地址的地理位…...
mac控制台命令小技巧
shigen日更文章的博客写手,擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长,分享认知,留住感动。 hello伙伴们,作为忠实的mac骨灰级别的粉丝,它真的给我带来了很多效率上的提升。那作为接…...
Postman:API测试之Postman使用完全指南
Postman是一个可扩展的API开发和测试协同平台工具,可以快速集成到CI/CD管道中。旨在简化测试和开发中的API工作流。 Postman工具有Chrome扩展和独立客户端,推荐安装独立客户端。 Postman有个workspace的概念,workspace分personal和team类型…...
Flume学习笔记(3)—— Flume 自定义组件
前置知识: Flume学习笔记(1)—— Flume入门-CSDN博客 Flume学习笔记(2)—— Flume进阶-CSDN博客 Flume 自定义组件 自定义 Interceptor 需求分析:使用 Flume 采集服务器本地日志,需要按照日志…...
go的字符切片和字符串互转
Go 1.21 // 返回一个Slice,它的底层数组自ptr开始,长度和容量都是len func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType // 返回一个指针,指向底层的数组 func SliceData(slice []ArbitraryType) *ArbitraryType // 生成一…...
所见即所得的动画效果:Animate.css
我们可以在集成Animate.css来改善界面的用户体验,省掉大量手写css动画的时间。 官网:Animate.css 使用 1、安装依赖 npm install animate.css --save2、引入依赖 import animate.css;3、在项目中使用 在class类名上animate__animated是必须的&#x…...
ERR:Navicat连接Sql Server报错
错误信息:报错:未发现数据源名称并且未指定默认驱动程序。 原因:Navicat没有安装Sqlserver驱动。 解决方案:在Navicat安装目录下找到sqlncli_x64.msi安装即可。 一键安装即可。 Navicat链接SQL Server配置 - MarchXD - 博客园 …...
python算法例10 整数转换为罗马数字
1. 问题描述 给定一个整数,将其转换为罗马数字,要求返回结果的取值范围为1~3999。 2. 问题示例 4→Ⅳ,12→Ⅻ,21→XⅪ,99→XCIX。 3. 代码实现 def int_to_roman(num):val [1000, 900, 500, 400,100, 90, 50, 40…...
springboot引入第三方jar包放到项目目录中,添加web.xml
参考博客:https://www.cnblogs.com/mask-xiexie/p/16086612.html https://zhuanlan.zhihu.com/p/587605618 1、在resources目录下新建lib文件夹,将jar包放到lib文件夹中 2、修改pom.xml文件 <dependency><groupId>com.lanren312</grou…...
大数据研发工程师课前环境搭建
大数据研发工程师课前环境搭建 第一章 VMware Workstation 安装 在Windows的合适的目录来进行安装,如下图 1.1 双击打开 1.2 下一步,接受协议 1.3 选择安装位置 1.4 用户体验设置 1.5 快捷方式 已经准备好安装,点击安装 1.6 安装中 1.7 安装…...
Qt图形视图框架:QGraphicsItem详解
Qt图形视图框架:QGraphicsItem详解 Chapter1 Qt图形视图框架:QGraphicsItem详解Chapter2 自定义QGraphicsItem实现平移、改变尺寸和旋转1. 平移2. 改变尺寸3. 旋转完整代码如下:头文件源文件 Chapter1 Qt图形视图框架:QGraphicsIt…...
defer和async
如果两个属性浏览器都不兼容,推荐把<script>标签放到底部 一般情况下,浏览器在解析html源文件时,如果遇到外部的<script>标签,解析过程就会先暂停,这时会对script进行加载,执行两个过程&…...
数电实验-----实现74LS139芯片扩展为3-8译码器以及应用(Quartus II )
目录 一、74LS139芯片介绍 芯片管脚 芯片功能表 二、2-4译码器扩展为3-8译码器 1.扩展原理 2.电路图连接 3.仿真结果 三、3-8译码器的应用(基于74ls139芯片) 1.三变量表决器 2.奇偶校验电路 一、74LS139芯片介绍 74LS139芯片是属于2-4译码器…...
洋葱架构、三层架构及两者区别
前言 洋葱架构它的名称来源于洋葱的层次结构,即软件代码的各层次之间的关系。在这种架构中,应用程序的各个组件通过一系列层次结构被逐层包裹在一起,形成一个类似于洋葱的结构。 一、经典三层架构 三层架构是一种软件设计模式,…...
JavaEE进阶学习:Spring 的创建和使用
Spring 就是⼀个包含了众多工具方法的 IoC 容器。既然是容器那么它就具备两个最基本的功能: 将对象存储到容器(Spring)中从容器中将对象取出来 接下来使用 Maven 方式来创建一个 Spring 项目,创建 Spring 项目和 Servlet 类似&a…...
音视频项目—基于FFmpeg和SDL的音视频播放器解析(十四)
介绍 在本系列,我打算花大篇幅讲解我的 gitee 项目音视频播放器,在这个项目,您可以学到音视频解封装,解码,SDL渲染相关的知识。您对源代码感兴趣的话,请查看基于FFmpeg和SDL的音视频播放器 如果您不理解本…...
Tomcat无法映射到activiti-app导致activiti无法启动页面
原因之一:JDK版本与Tomcat版本不匹配,jdk8 yyds 我使用的是JDK11,Tomcat是9.0的,都是最新的,但还是不行,最后JDK改为8,tomcat的cmd后台没有报错,activiti-pp也可以正常访问了,很神奇…...
c语言常见的面试问题
在C语言编程中,面试官可能会询问你以下一些常见问题: 什么是C语言? C语言是一种通用的、过程式的计算机编程语言,由Dennis Ritchie在1972年创建。它是Unix操作系统的核心语言,也是许多其他编程语言(如Go、…...
image图片之间的间隙消除
多个图片排列展示,水平和垂直方向的间隔如何消除 垂直方向 vertical-align 原因: vertical-align属性主要用于改变行内元素的对齐方式,行内元素默认垂直对齐方式是基线对齐(baseline) 这是因为图片属于行内元素&…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
