热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

(转载)音频编解码基础(wav/aac/pcma/pcmu)

原文:https:blog.csdn.nethiwubihearticledetails81258879[音频编解码系列文章]音频编解码基础FFMPEG实现音频重采样

原文:https://blog.csdn.net/hiwubihe/article/details/81258879

[音频编解码系列文章]

音频编解码基础
FFMPEG实现音频重采样
FFMPEG实现PCM编码(采用封装格式实现)
FFMPEG实现PCM编码(不采用封装格式实现)
FAAC库实现PCM编码
FAAD库实现RAW格式AAC解码
FAAD库实现RAW格式AAC封装成ADTS格式
FAAD库实现ADTS格式解码
FFMPEG实现对AAC解码(采用封装格式实现)
FFMPEG实现对AAC解码(不采用封装格式实现)

本文介绍音频处理基础知识,介绍常见的音频问题处理。主要包含以下内容

  • WAV头解析并保持PCM
  • PCM文件加WAV头
  • ADTS格式AAC帧获取
  • PCM转G711A/G711U

WAV格式介绍

WAV是微软的RIFF文件的一个特例,通常由一个文件头和若干个CHUNK组成,通常是由RIFF文件头类型“WAVE”子chunk为“fmt”,“data”,和可选数量的chunk组成。format如下:

一段WAV格式实例

52 49 46 46 24 08 00 00 57 41 56 45 66 6d 74 20 10 00 00 00 01 00 02 00
22 56 00 00 88 58 01 00 04 00 10 00 64 61 74 61 00 08 00 00 00 00 00 00
24 17 1e f3 3c 13 3c 14 16 f9 18 f9 34 e7 23 a6 3c f2 24 f2 11 ce 1a 0d
结构分析如下图

注意事项

       1.二进制数据都是"小端"存储方式。

       2.样本存储8位范围为(0-255),样本存储格式16位范围(-32768-32767)

WAV头解析并保持PCM

WAV文件是一种WINDOS riff文件,只是对PCM加了一个头,没做压缩处理,加完头后一般播放就能播放了。DEMO代码如下:

/*******************************************************************************
Copyright (c) wubihe Tech. Co., Ltd. All rights reserved.
--------------------------------------------------------------------------------
Date Created: 2014-10-25
Author: wubihe QQ:1269122125 Email:1269122125@qq.com
Description: 本例子解析WAV头,并打印音频相关信息
--------------------------------------------------------------------------------
Modification History
DATE AUTHOR DESCRIPTION
--------------------------------------------------------------------------------
********************************************************************************/#include
#include
#include
#include
#include #ifndef HAVE_INT32_T
typedef signed int int32_t;
#endif
#ifndef HAVE_INT16_T
typedef signed short int16_t;
#endif
#ifndef HAVE_U_INT32_T
typedef unsigned int u_int32_t;
#endif
#ifndef HAVE_U_INT16_T
typedef unsigned short u_int16_t;
#endif#ifdef WORDS_BIGENDIAN
# define UINT32(x) SWAP32(x)
# define UINT16(x) SWAP16(x)
#else
# define UINT32(x) (x)
# define UINT16(x) (x)
#endiftypedef struct
{FILE *f;int channels;int samplebytes;int samplerate;int samples;int bigendian;int isfloat;
} pcmfile_t;typedef struct
{u_int32_t label; /* 'RIFF' */u_int32_t length; /* Length of rest of file */u_int32_t chunk_type; /* 'WAVE' */
}
riff_t;typedef struct
{u_int32_t label;u_int32_t len;
}
riffsub_t;#ifdef _MSC_VER
#pragma pack(push, 1)
#endif#define WAVE_FORMAT_PCM 1
#define WAVE_FORMAT_FLOAT 3
#define WAVE_FORMAT_EXTENSIBLE 0xfffe
#define INPUT_FILE ("huangdun.wav")
#define OUTPUT_FILE ("huangdun_r48000_FMT_S16_c2.pcm")
struct WAVEFORMATEX
{u_int16_t wFormatTag;u_int16_t nChannels;u_int32_t nSamplesPerSec;u_int32_t nAvgBytesPerSec;u_int16_t nBlockAlign;u_int16_t wBitsPerSample;u_int16_t cbSize;
}
#ifdef __GNUC
__attribute__((packed))
#endif
;struct WAVEFORMATEXTENSIBLE
{struct WAVEFORMATEX Format;union {u_int16_t wValidBitsPerSample; // bits of precisionu_int16_t wSamplesPerBlock; // valid if wBitsPerSample==0u_int16_t wReserved; // If neither applies, set to zero.} Samples;u_int32_t dwChannelMask; // which channels are present in streamunsigned char SubFormat[16]; // guid
}
#ifdef __GNUC
__attribute__((packed))
#endif
;#ifdef _MSC_VER
#pragma pack(pop)
#endifstatic unsigned char waveformat_pcm_guid[16] =
{WAVE_FORMAT_PCM,0,0,0,0x00, 0x00,0x10, 0x00,0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
};static void unsuperr(const char *name)
{fprintf(stderr, "%s: file format not supported\n", name);
}pcmfile_t *wav_open_read(const char *name, int rawinput)
{int i;int skip;FILE *wave_f;riff_t riff;riffsub_t riffsub;struct WAVEFORMATEXTENSIBLE wave;char *riffl = "RIFF";char *wavel = "WAVE";char *bextl = "BEXT";char *fmtl = "fmt ";char *datal = "data";int fmtsize;pcmfile_t *sndf;int dostdin = 0;if (!strcmp(name, "-")){
#ifdef _WIN32_setmode(_fileno(stdin), O_BINARY);
#endifwave_f &#61; stdin;dostdin &#61; 1;}else if (!(wave_f &#61; fopen(name, "rb"))){perror(name);return NULL;}if (!rawinput) // header input{if (fread(&riff, 1, sizeof(riff), wave_f) !&#61; sizeof(riff))return NULL;if (memcmp(&(riff.label), riffl, 4))return NULL;if (memcmp(&(riff.chunk_type), wavel, 4))return NULL;// handle broadcast extensions. added by pro-tools,otherwise it must be fmt chunk.if (fread(&riffsub, 1, sizeof(riffsub), wave_f) !&#61; sizeof(riffsub))return NULL;riffsub.len &#61; UINT32(riffsub.len);if (!memcmp(&(riffsub.label), bextl, 4)){fseek(wave_f, riffsub.len, SEEK_CUR);if (fread(&riffsub, 1, sizeof(riffsub), wave_f) !&#61; sizeof(riffsub))return NULL;riffsub.len &#61; UINT32(riffsub.len);}if (memcmp(&(riffsub.label), fmtl, 4))return NULL;memset(&wave, 0, sizeof(wave));fmtsize &#61; (riffsub.len 0; skip--)fgetc(wave_f);for (i &#61; 0;; i&#43;&#43;){if (fread(&riffsub, 1, sizeof(riffsub), wave_f) !&#61; sizeof(riffsub))return NULL;riffsub.len &#61; UINT32(riffsub.len);if (!memcmp(&(riffsub.label), datal, 4))break;if (i > 10)return NULL;for (skip &#61; riffsub.len; skip > 0; skip--)fgetc(wave_f);}if (UINT16(wave.Format.wFormatTag) !&#61; WAVE_FORMAT_PCM && UINT16(wave.Format.wFormatTag) !&#61; WAVE_FORMAT_FLOAT){if (UINT16(wave.Format.wFormatTag) &#61;&#61; WAVE_FORMAT_EXTENSIBLE){if (UINT16(wave.Format.cbSize) <22) // struct too smallreturn NULL;if (memcmp(wave.SubFormat, waveformat_pcm_guid, 16)){waveformat_pcm_guid[0] &#61; WAVE_FORMAT_FLOAT;if (memcmp(wave.SubFormat, waveformat_pcm_guid, 16)){ unsuperr(name);return NULL;}}}else{unsuperr(name);return NULL;}}}sndf &#61; (pcmfile_t *)malloc(sizeof(*sndf));memset(sndf, 0, sizeof(*sndf));sndf->f &#61; wave_f;if (UINT16(wave.Format.wFormatTag) &#61;&#61; WAVE_FORMAT_FLOAT) {sndf->isfloat &#61; 1;} else {sndf->isfloat &#61; (wave.SubFormat[0] &#61;&#61; WAVE_FORMAT_FLOAT);}if (rawinput){sndf->bigendian &#61; 1;if (dostdin)sndf->samples &#61; 0;else{fseek(sndf->f, 0 , SEEK_END);sndf->samples &#61; ftell(sndf->f);rewind(sndf->f);}}else{sndf->bigendian &#61; 0;sndf->channels &#61; UINT16(wave.Format.nChannels);sndf->samplebytes &#61; UINT16(wave.Format.wBitsPerSample) / 8;sndf->samplerate &#61; UINT32(wave.Format.nSamplesPerSec);sndf->samples &#61; riffsub.len / (sndf->samplebytes * sndf->channels);}return sndf;
}int wav_close(pcmfile_t *sndf)
{int i &#61; fclose(sndf->f);free(sndf);return i;
}int main()
{FILE *fpout;fpout&#61;fopen(OUTPUT_FILE,"wb&#43;");if(fpout &#61;&#61; NULL) {printf("Create pcm file error\n");return -1;}pcmfile_t * pPcmFile &#61; wav_open_read(INPUT_FILE, 0);printf("channels:%1d\t samplebytes:%1d\t samplerate:%06d\t samples:%07d\t bigendian:%1d\n",pPcmFile->channels,pPcmFile->samplebytes,pPcmFile->samplerate,pPcmFile->samples,pPcmFile->bigendian);unsigned short usSample;/*while(!feof(pPcmFile->f)){fread(&usSample,sizeof(unsigned short),1,pPcmFile->f);fwrite(&usSample,sizeof(unsigned short),1,fpout);}*/

    fread(&usSample,sizeof(unsigned short),1,pPcmFile->f);
    while(!feof(pPcmFile->f))//feof结尾EOF判断会多一次循环&#xff0c;导致PCM文件末尾多写两个字节&#xff0c;修改为先读后判断。
    {
      //fread(&usSample,sizeof(unsigned short),1,pPcmFile->f);
      fwrite(&usSample,sizeof(unsigned short),1,fpout);
      fread(&usSample,sizeof(unsigned short),1,pPcmFile->f);

    }

wav_close(pPcmFile);fclose(fpout);printf("Parser WAV Success!!");getchar();return 0;
}

  运行结果保存PCM文件

PCM文件加WAV头

ADTS格式AAC帧获取

ADTS结构

ADTSHeader结构

/** ADTS Header: * MPEG-2 version 56 bits (byte aligned) * MPEG-4 version 56 bits (byte aligned) - note - changed for 0.99 version** syncword 12 bits* id 1 bit* layer 2 bits* protection_absent 1 bit* profile 2 bits* sampling_frequency_index 4 bits* private 1 bit* channel_configuraton 3 bits* original 1 bit* home 1 bit* copyright_id 1 bit* copyright_id_start 1 bit* aac_frame_length 13 bits* adts_buffer_fullness 11 bits* num_raw_data_blocks 2 bits** if (protection_absent &#61;&#61; 0)* crc_check 16 bits*/

  程序运行结果

PCM转G711A/G711U

音频编码分为波形编码和参数编码&#xff0c;常见得编码方式如AAC等是两者之间的编码方式。波形编码就是对声波波形的采样数据进行编码&#xff0c;完全不考虑这个波内部的信息&#xff0c;如时域或者频域上的冗余。参数编码如一个正弦波我们不需要知道在不同时间采样数值&#xff0c;只有知道振幅&#xff0c;频率&#xff0c;相位等信息&#xff0c;编码只保存该信息&#xff0c;在接收方按照这些参数重新建立波形即可播放。G711A/G711U就是波形编码&#xff0c;编码比较简单&#xff0c;只是把样本值从PCM的存储方式16Bit压缩成8Bit,在安防和电话中有应用。DEMO实现把PCM编码成G711A,代码如下&#xff1a;

#include
#include "g711.h"#define INPUT_FILE_NAME ("huangdun_r48000_FMT_S16_c2.pcm")
#define OUTPUT_FILE_NAME ("huangdun_r48000_FMT_S16_c2.g711a")
int main()
{FILE*pInputFile &#61; fopen(INPUT_FILE_NAME, "rb");if (pInputFile &#61;&#61; NULL){/* unable to open file */fprintf(stderr, "Error opening file: %s\n", INPUT_FILE_NAME);return 1;}FILE*pOutputFile&#61;fopen(OUTPUT_FILE_NAME,"wb&#43;");if(pOutputFile &#61;&#61; NULL) {printf("Create g711a file error\n");return -1;}signed short usSample ;unsigned char ucG711Sample;int iReadCnt&#61;0;int iWriteCnt&#61;0;while(!feof(pInputFile)){fread(&usSample,sizeof(unsigned short),1,pInputFile);iReadCnt&#43;&#61;2;ucG711Sample &#61; ALaw_Encode(usSample);fwrite(&ucG711Sample,1,1,pOutputFile);iWriteCnt&#43;&#61;1;}fclose(pInputFile);fclose(pOutputFile);printf("ReadCnt:%d WriteCnt:%d PCM TO G711A Success!!!",iReadCnt,iWriteCnt);getchar();}

  

 

转:https://www.cnblogs.com/cyblogs/p/10696875.html



推荐阅读
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 本文由编程笔记#小编整理,主要介绍了关于数论相关的知识,包括数论的算法和百度百科的链接。文章还介绍了欧几里得算法、辗转相除法、gcd、lcm和扩展欧几里得算法的使用方法。此外,文章还提到了数论在求解不定方程、模线性方程和乘法逆元方面的应用。摘要长度:184字。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • 用Vue实现的Demo商品管理效果图及实现代码
    本文介绍了一个使用Vue实现的Demo商品管理的效果图及实现代码。 ... [详细]
author-avatar
志薇俊元4565
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有