解码AAC裸流为PCM写入文件

使用AAC裸流解析器将aac裸流文件解析为pcm数据,然后写入文件

#include "myLog.h"
#include <iostream>extern "C"
{
#include <libavcodec\avcodec.h>
}#define AUDIO_INBUF_SIZE 20480			// 读取 20KB数据
#define AUDIO_REFILL_THRESH 4096/*从src复制size字符到dst(dstBuff 与 srcBuff内存块可重叠)memmove(dst, src, size);
*/static char* av_get_err(int errnum)
{char err_buf[128] = { 0 };av_strerror(errnum, err_buf, 128);return err_buf;
}static void print_sample_format(const AVFrame *frame)
{printf("ar_samplerate: %uHz\n", frame->sample_rate);printf("ac_channel: %u\n", frame->channels);printf("f_format: %u\n", frame->format);	// 格式需要注意,实际存储到本地文件时已经改成交错模式 FLT
}static void decode(AVCodecContext* dec_ctx, AVPacket* pkt, AVFrame* frame, FILE* out_fp)
{int nRet = avcodec_send_packet(dec_ctx, pkt);if (nRet == AVERROR(EAGAIN))		// 需要更多pkt{LOG_WARNING("need more pkt\n");return;}else if (nRet < 0){// 当为mp3文件时,因为前面的一部分字节解码器解不出来, 直接退出程序的话,会导致mp3文件解析失败,应该函数返回解析下一帧LOG_WARNING("Error submitting the packet to the decoder, err:%s, pkt_size:%d\n",av_get_err(nRet), pkt->size);return;}// 一个pkt可能对应多个framewhile (nRet >= 0){nRet = avcodec_receive_frame(dec_ctx, frame);if (nRet == AVERROR(EAGAIN) || nRet == AVERROR_EOF){return;}else if (nRet < 0){LOG_WARNING("Error during decoding\n");return;}// 一个采样点字节数int data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);if (data_size < 0){LOG_WARNING("av_get_bytes_per_sample error\n");return;}// 打印一次信息static bool is_print = 0;if (!is_print){is_print = !is_print;print_sample_format(frame);}// planar to packedfor (int i = 0; i < frame->nb_samples; i++){for (int ch = 0; ch < frame->channels; ch++){fwrite(frame->data[ch] + i * data_size, 1, data_size, out_fp);}}}
}int main_audio_decodec()
{// ffplay -ar 44100 -ac 2 -f f32le -i out_put_48000_2_f32le.pcmconst char* in_file_name = "./out_put.aac";const char* out_file_name = "./out_put_48000_2_f32le.pcm";enum AVCodecID audio_codec_id = AV_CODEC_ID_AAC;	// 指定aac// 1. 查找解码器AVCodec* audio_decoder = avcodec_find_decoder(audio_codec_id);if (nullptr == audio_decoder){LOG_WARNING("avcodec_find_decoder error\n");return -1;}// 2. 获取(aac)裸流解析器AVCodecParserContext* parser = av_parser_init(audio_decoder->id);if (nullptr == parser){LOG_WARNING("av_parser_init error\n");return -2;}// 3. 根据解码器创建解码上下文auto audio_decoder_ctx = avcodec_alloc_context3(audio_decoder);if (audio_decoder_ctx == nullptr){LOG_WARNING("avcodec_alloc_context3 error\n");return -3;}// 4. 打开解码器int nRet = avcodec_open2(audio_decoder_ctx, audio_decoder, NULL);if (nRet < 0){LOG_WARNING("avcodec_open2 error\n");return -4;}// 5. 打开输入输出文件FILE* in_fp = fopen(in_file_name, "rb");if (NULL == in_fp){LOG_WARNING("fopen in_file_name error\n");return -5;}FILE* out_fp = fopen(out_file_name, "wb");if (NULL == out_fp){LOG_WARNING("out_fp out_file_name error\n");return -5;}// 6.从输入文件中读取数据// AV_INPUT_BUFFER_PADDING_SIZE   在用于解码的输入比特流末尾所需的额外分配的字节数uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];uint8_t *data = NULL;size_t   data_size = 0;data = inbuf;data_size = fread(inbuf, 1, AUDIO_INBUF_SIZE, in_fp);AVPacket* pkt = av_packet_alloc();AVFrame* decoded_frame = av_frame_alloc();while (data_size > 0){if (decoded_frame == nullptr){LOG_WARNING("av_frame_alloc error\n");return -6;}// pkt->data 不会分配内存而是直接指向data的内存// av_read_frame():获取媒体的一帧压缩编码数据。其中调用了av_parser_parse2()// 返回值为 该函数返回读取的bytesnRet = av_parser_parse2(parser, audio_decoder_ctx,&pkt->data, &pkt->size,		// 输出 pktdata, data_size,			// 输入 buff裸流数据(h264, aac等)AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);if (nRet < 0){LOG_WARNING("av_parser_parse2 error\n");return -7;}data += nRet;		// 将指针挪动到未解析的buff位置data_size -= nRet;if (pkt->size >= 0){// 解码decode(audio_decoder_ctx, pkt, decoded_frame, out_fp);}// 数据不够了继续读取if (data_size < AUDIO_REFILL_THRESH){memmove(inbuf, data, data_size);	// 将未读取完的拷贝到inbuf开始位置data = inbuf;int len = fread(data + data_size, 1, AUDIO_INBUF_SIZE - data_size, in_fp);if (len > 0){data_size += len;}}}// 空包冲刷解码器pkt->data = NULL;pkt->size = 0;decode(audio_decoder_ctx, pkt, decoded_frame, out_fp);// 释放相关资源fclose(in_fp);fclose(out_fp);av_frame_free(&decoded_frame);av_packet_free(&pkt);avcodec_free_context(&audio_decoder_ctx);av_parser_close(parser);getchar();return 0;
}

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

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

相关文章

# C++之functional库用法整理

C之functional库用法整理 注&#xff1a;整理一些突然学到的C知识&#xff0c;随时mark一下 例如&#xff1a;忘记的关键字用法&#xff0c;新关键字&#xff0c;新数据结构 C 的function库用法整理 C之functional库用法整理一、functional库的内建仿函数1. 存储和调用函数2. 存…

rust-tokio发布考古

源头&#xff1a; Carl Lerche Aug 4, 2016 ​ I’m very excited to announce a project that has been a long time in the making. 我很兴奋地宣布一个酝酿已久的项目。 Tokio is a network application framework for rapid development and highly scalable deployments…

7-36 输入年份和月份

输入一个年份和月份&#xff0c;输出这个月的天数。 输入格式: 输入年份year和月份month&#xff0c;年份和月份中间用一个空格隔开。 输出格式: 输入year年的month月对应的天数。 输入样例: 2000 2输出样例: 29输入样例: 1900 2输出样例: 28输入样例: 1900 6输出样例…

基于单片机数码管20V电压表仿真设计

**单片机设计介绍&#xff0c;基于单片机数码管20V电压表仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机数码管20V电压表仿真设计的主要目的是通过单片机和数码管显示电路实现一个能够测量0到20V直流电压的电…

南京博物院自动化预约

代码 import timeimport requests from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC# 创建Edge浏览器实例 driver …

如果用大模型考公,kimi、通义千问谁能考高分?

都说大模型要超越人类了&#xff0c;今天就试试让kimi和通义千问做公务员考试题目&#xff0c;谁能考高分&#xff1f; 测评结果再次让人震惊&#xff01; 问题提干&#xff1a;大小两种规格的盒装鸡蛋&#xff0c;大盒装23个&#xff0c;小盒装16个&#xff0c;采购员小王买了…

【鸿蒙开发】系统组件Row

Row组件 Row沿水平方向布局容器 接口&#xff1a; Row(value?:{space?: number | string }) 参数&#xff1a; 参数名 参数类型 必填 参数描述 space string | number 否 横向布局元素间距。 从API version 9开始&#xff0c;space为负数或者justifyContent设置为…

用vue.js写案例——ToDoList待办事项 (步骤和全码解析)

目录 一.准备工作 二.编写各个组件的页面结构 三.实现初始任务列表的渲染 四.新增任务 五.删除任务 六.展示未完成条数 七.切换状态-筛选数据 八.待办事项&#xff08;全&#xff09;代码 一.准备工作 在开发“ToDoList”案例之前&#xff0c;需要先完成一些准备工作&a…

电力变压器数据集介绍和预处理

1 电力变压器数据集介绍 1.1 数据背景 在这个Github仓库中&#xff0c;作者提供了几个可以用于长序列时间序列问题的数据集。所有数据集都经过了预处理&#xff0c;并存储为.csv文件。数据集的范围从2016/07到2018/07。 ETT-small: 含有2个电力变压器&#xff08;来自2个站点…

React - 你使用过高阶组件吗

难度级别:初级及以上 提问概率:55% 高阶组件并不能单纯的说它是一个函数,或是一个组件,在React中,函数也可以做为一种组件。而高阶组件就是将一个组件做为入参,被传入一个函数或者组件中,经过一定的加工处理,最终再返回一个组件的组合…

使用卷积神经网络(CNN)识别验证码

验证码&#xff08;CAPTCHA&#xff09;是一种常见的用于区分人类和机器的技术&#xff0c;它要求用户完成一些简单的任务或者输入一些字符以验证其身份。本文将介绍如何使用卷积神经网络&#xff08;CNN&#xff09;来识别常见的字符验证码&#xff0c;并提供详细的代码示例。…

公网环境下如何端口映射?

公网端口映射是一种网络技术&#xff0c;它允许将本地网络中的设备暴露在公共互联网上&#xff0c;以便能够从任何地方访问这些设备。通过公网端口映射&#xff0c;用户可以通过互联网直接访问和控制局域网中的设备&#xff0c;而无需在本地网络中进行复杂的配置。 公网端口映射…

动态规划基础思想

本页面主要介绍了动态规划的基本思想,以及动态规划中状态及状态转移方程的设计思路,帮助各位初学者对动态规划有一个初步的了解。 本部分的其他页面,将介绍各种类型问题中动态规划模型的建立方法,以及一些动态规划的优化技巧。 引入 [IOI1994] 数字三角形](https://www.…

AUTOSAR配置工具开发教程 - 开篇

简介 本系列的教程&#xff0c;主要讲述如何自己开发一套简单的AUTOSAR ECU配置工具。适用于有C# WPF基础的人员。 简易介绍见&#xff1a;如何打造AUTOSAR工具_autosar_mod_ecuconfigurationparameters-CSDN博客 实现版本 AUTOSAR 4.0.3AUTOSAR 4.2.2AUTOSAR 4.4.0 效果 …

GD32零基础教程第一节(开发环境搭建及工程模板介绍)

文章目录 前言一、MDK keil5安装二、设备支持包安装三、CH340串口驱动安装四、STLINIK驱动安装五、工程风格介绍总结 前言 本篇文章正式带大家开始学习GD32F407VET6国产单片机的学习&#xff0c;国产单片机性能强&#xff0c;而且价格也便宜&#xff0c;下面就开始带大家来介绍…

Unity 主线程和其他线程之间的数据访问

在Unity中&#xff0c;主线程和其他线程之间的数据访问需要小心处理&#xff0c;因为在多线程环境下&#xff0c;不当的数据访问可能导致竞争条件和数据不一致性。 在Unity中&#xff0c;主线程通常用于处理用户输入、更新游戏逻辑和渲染。其他线程通常用于执行耗时的计算、加…

如何保障人工智能系统开发的安全?

原文地址&#xff1a;https://venturebeat.com/ai/how-to-secure-ai-system-development/ 数据科学家、人工智能工程师和网络安全专家如果在开发过程中未能确保人工智能系统的安全性&#xff0c;可能会使组织面临巨大的经济损失和声誉风险。那么&#xff0c;他们应采取哪些措施…

LeetCode-移除元素

题目 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考虑数组中超出新长…

【SQL】数据操作语言(DML):学习插入、更新和删除数据

数据查询语言&#xff08;DQL&#xff09;用于从数据库中检索数据&#xff0c;主要通过SELECT语句来实现。SELECT语句允许用户指定要检索的数据列、表以及任何筛选条件。以下是对DQL的详细介绍以及多个示例&#xff1a; SELECT语句基础结构&#xff1a; sql SELECT column1,…

如何使用群晖Synology Drive结合cpolar内网穿透实现同步Obsidian笔记文件

文章目录 一、简介软件特色演示&#xff1a; 二、使用免费群晖虚拟机搭建群晖Synology Drive服务&#xff0c;实现局域网同步1 安装并设置Synology Drive套件2 局域网内同步文件测试 三、内网穿透群晖Synology Drive&#xff0c;实现异地多端同步Windows 安装 Cpolar步骤&#…