1. 利用fffmpeg将mp3转为pcm并在pcm数据加上wav头就是一个完整的wav文件

2. 代码

#include "utils.h"
#include <libavutil/avutil.h>
#include <libavutil/attributes.h>
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavutil/mathematics.h>
#include <libswresample/swresample.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavformat/avio.h>
#include <libavutil/file.h>
#include <libswresample/swresample.h>#define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000//下面这四个结构体是为了分析wav头的
typedef struct {u_int magic;      /* 'RIFF' */u_int length;     /* filelen */u_int type;       /* 'WAVE' */
} WaveHeader;typedef struct {u_short format;       /* see WAV_FMT_* */u_short channels;u_int sample_fq;      /* frequence of sample */u_int byte_p_sec;u_short byte_p_spl;   /* samplesize; 1 or 2 bytes */u_short bit_p_spl;    /* 8, 12 or 16 bit */
} WaveFmtBody;typedef struct {u_int type;        /* 'data' */u_int length;      /* samplecount */
} WaveChunkHeader;#define COMPOSE_ID(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
#define WAV_RIFF COMPOSE_ID('R','I','F','F')
#define WAV_WAVE COMPOSE_ID('W','A','V','E')
#define WAV_FMT COMPOSE_ID('f','m','t',' ')
#define WAV_DATA COMPOSE_ID('d','a','t','a')
int insert_wave_header(FILE* fp, long data_len)
{int len;WaveHeader* header;WaveChunkHeader* chunk;WaveFmtBody* body;fseek(fp, 0, SEEK_SET);        //写到wav文件的开始处len = sizeof(WaveHeader)+sizeof(WaveFmtBody)+sizeof(WaveChunkHeader)*2;char* buf = (char*)malloc(len);header = (WaveHeader*)buf;header->magic = WAV_RIFF;header->length = data_len + sizeof(WaveFmtBody)+sizeof(WaveChunkHeader)*2 + 4;header->type = WAV_WAVE;chunk = buf+sizeof(WaveHeader);chunk->type = WAV_FMT;chunk->length = 16;body = buf+sizeof(WaveHeader)+sizeof(WaveChunkHeader);body->format = (u_short)0x0001;      //编码方式为pcmbody->channels = (u_short)0x02;      //声道数为2body->sample_fq = 44100;             //采样频率为44.1kbody->byte_p_sec = 176400;           //每秒所需字节数 44100*2*2=采样频率*声道*采样位数body->byte_p_spl = (u_short)0x4;     //对齐无意义body->bit_p_spl = (u_short)16;       //采样位数16bit=2Bytechunk = buf+sizeof(WaveHeader)+sizeof(WaveChunkHeader)+sizeof(WaveFmtBody);chunk->type = WAV_DATA;chunk->length = data_len;fwrite(buf, 1, len, fp);free(buf);return 0;
}typedef struct {int videoindex;int sndindex;AVFormatContext* pFormatCtx;AVCodecContext* sndCodecCtx;AVCodec* sndCodec;SwrContext *swr_ctx;DECLARE_ALIGNED(16,uint8_t,audio_buf) [AVCODEC_MAX_AUDIO_FRAME_SIZE * 4];
}AudioState;int init_ffmpeg(AudioState* is, char* filepath)
{int i=0;int ret;is->sndindex = -1;if(NULL == filepath){dbmsg("input file is NULL");return -1;}avcodec_register_all();avfilter_register_all();av_register_all();is->pFormatCtx = avformat_alloc_context();if(avformat_open_input(&is->pFormatCtx, filepath, NULL, NULL)!=0)return -1;if(avformat_find_stream_info(is->pFormatCtx, NULL)<0)return -1;av_dump_format(is->pFormatCtx,0, 0, 0);is->videoindex = av_find_best_stream(is->pFormatCtx, AVMEDIA_TYPE_VIDEO, is->videoindex, -1, NULL, 0);is->sndindex = av_find_best_stream(is->pFormatCtx, AVMEDIA_TYPE_AUDIO,is->sndindex, is->videoindex, NULL, 0);dbmsg("videoindex=%d, sndindex=%d", is->videoindex, is->sndindex);if(is->sndindex != -1){is->sndCodecCtx = is->pFormatCtx->streams[is->sndindex]->codec;is->sndCodec = avcodec_find_decoder(is->sndCodecCtx->codec_id);if(is->sndCodec == NULL){dbmsg("Codec not found");return -1;}if(avcodec_open2(is->sndCodecCtx, is->sndCodec, NULL) < 0)return -1;}return 0;
}int main(int argc, char **argv)
{int ret;FILE* fp;int file_data_size = 0;                //这儿注意一个问题: 变量用时一定要初始化,否则会出现异常int len1,len2, data_size, got_frame;AVPacket *packet = av_mallocz(sizeof(AVPacket));AVFrame *frame = av_frame_alloc();AudioState* is = (AudioState*) av_mallocz(sizeof(AudioState));uint8_t *out[] = { is->audio_buf };fp = fopen("./test.wav", "wb+");len1 = sizeof(WaveHeader)+sizeof(WaveFmtBody)+sizeof(WaveChunkHeader)*2;fseek(fp,len1, SEEK_SET);      //在写之前先预留出wav的header,即44个字节dbmsg("len1=%d",len1);//第1步初始化ffmpeg,并用ffmpeg解码,最后转为pcm格式if( (ret=init_ffmpeg(is, argv[1])) != 0)            //1.1 初始化ffmpeg{dbmsg("init_ffmpeg error");return -1;}while( (av_read_frame(is->pFormatCtx, packet)>=0) )    //1.2 循环读取mp3文件中的数据帧{if(packet->stream_index != is->sndindex)continue;if((ret=avcodec_decode_audio4(is->sndCodecCtx, frame, &got_frame, packet)) < 0) //1.3 解码数据帧{dbmsg("file eof");break;}if(got_frame <= 0) /* No data yet, get more frames */continue;data_size = av_samples_get_buffer_size(NULL, is->sndCodecCtx->channels, frame->nb_samples, is->sndCodecCtx->sample_fmt, 1);//1.4下面将ffmpeg解码后的数据帧转为我们需要的数据(关于"需要的数据"下面有解释)if(NULL==is->swr_ctx){if(is->swr_ctx != NULL)swr_free(&is->swr_ctx);dbmsg("frame: channnels=%d,format=%d, sample_rate=%d", frame->channels, frame->format, frame->sample_rate);is->swr_ctx = swr_alloc_set_opts(NULL, AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, 44100, av_get_default_channel_layout(frame->channels), frame->format, frame->sample_rate, 0, NULL);if(is->swr_ctx == NULL){dbmsg("swr_ctx == NULL");}swr_init(is->swr_ctx);}len2 = swr_convert(is->swr_ctx, out, 44100,(const uint8_t **)frame->extended_data, frame->nb_samples);file_data_size += len2;//1.5 数据格式转换完成后就写到文件中fwrite((short *)is->audio_buf, sizeof(short), (size_t) len2* 2, fp);}file_data_size *= 4;dbmsg("file_data_size=%d", file_data_size);//第2步添加上wav的头ret = insert_wave_header(fp, file_data_size);av_free_packet(packet);av_free(frame);avcodec_close(is->sndCodecCtx);avformat_close_input(&is->pFormatCtx);fclose(fp);return 0;
}

3.运行结果

cong@msi:/work/ffmpeg/test/alsa/testalsa/5mp3towav$ make run
export LD_LIBRARY_PATH=/work/ffmpeg/out/lib/ \&& ./mp3towav /work/ffmpeg/test/resource//test.mp3
mp3towav.c:main[150]: len1=44
[mp3 @ 0x14d3620] Skipping 0 bytes of junk at 197687.
libavutil/crc.c:av_crc_init[313]:
[mp3 @ 0x14d3620] Estimating duration from bitrate, this may be inaccurate
Input #0, mp3, from '(null)':Metadata:artist : 佚名title : 法国国歌 马赛曲TYER : 2013-10-26Duration: 00:03:28.20, start: 0.000000, bitrate: 199 kb/sStream #0:0: Audio: mp3, 44100 Hz, stereo, s16p, 192 kb/sStream #0:1: Video: mjpeg, yuvj420p(pc, bt470bg/unknown/unknown), 600x600 [SAR 1:1 DAR 1:1], 90k tbr, 90k tbn, 90k tbcMetadata:title : ecomment : Cover (front)
mp3towav.c:init_ffmpeg[120]: videoindex=-1381258232, sndindex=0
mp3towav.c:main[173]: frame: channnels=2,format=6, sample_rate=44100
mp3towav.c:main[186]: file_data_size=36725760

ls查看

cong@msi:/work/ffmpeg/test/alsa/testalsa/5mp3towav$ ls -l
total 36064
-rw-rw-r-- 1 cong cong 885 Sep 11 11:25 Makefile
-rwxrwxr-x 1 cong cong 64126 Sep 11 11:44 mp3towav
-rw-rw-r-- 1 cong cong 6183 Sep 11 11:24 mp3towav.c
-rw-rw-r-- 1 cong cong 115344 Sep 11 11:44 mp3towav.o
-rw-rw-r-- 1 cong cong 36725804 Sep 11 11:44 test.wav
-rw-rw-r-- 1 cong cong 333 Sep 9 11:31 utils.h

4. 说明

mp3towav.c:main[173]: AV_CH_LAYOUT_STEREO=3, AV_SAMPLE_FMT_S16=1, freq=44100
mp3towav.c:main[174]: frame: channnels=2, default_layout=3, format=6, sample_rate=44100

ffmpeg中:include/libavutil/samplefmt.h
enum AVSampleFormat {AV_SAMPLE_FMT_NONE = -1,AV_SAMPLE_FMT_U8, ///< unsigned 8 bitsAV_SAMPLE_FMT_S16, ///< signed 16 bits    --> 1 这个是pcm的数据格式AV_SAMPLE_FMT_S32, ///< signed 32 bitsAV_SAMPLE_FMT_FLT, ///< floatAV_SAMPLE_FMT_DBL, ///< doubleAV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planarAV_SAMPLE_FMT_S16P, ///< signed 16 bits, planar  -->6 这个是ffmepg解码之后的数据格式AV_SAMPLE_FMT_S32P, ///< signed 32 bits, planarAV_SAMPLE_FMT_FLTP, ///< float, planarAV_SAMPLE_FMT_DBLP, ///< double, planarAV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically
};

interleaved -->理解为交叉存取 --> AV_SAMPLE_FMT_S16是两个声道的声音是交叉存储的
plannar–> 理解为平面存取 --> AV_SAMPLE_FMT_S16P是先存1个声道的数据再存另一个声道的数据

AV_SAMPLE_FMT_S16P is planar signed 16 bit audio, i.e. 2 bytes for each sample which is same for AV_SAMPLE_FMT_S16.

The only difference is in AV_SAMPLE_FMT_S16 samples of each channel are interleaved i.e. if you have two channel audio then the samples buffer will look like

c1 c1 c2 c2 c1 c1 c2 c2… -->AV_SAMPLE_FMT_S16的数据组织方式

where c1 is a sample for channel1 and c2 is sample for channel2.

while for one frame of planar audio you will have something like

c1 c1 c1 c1 … c2 c2 c2 c2 … -->AV_SAMPLE_FMT_S16P的数据组织方式

now how is it stored in AVFrame:

for planar audio:

data[i] will contain the data of channel i (assuming channel 0 is first channel).

however if you have more channels then 8 then data for rest of the channels can be found in extended_data attribute of AVFrame.

for non-planar audio

data[0] will contain the data for all channels in an interleaved manner.

参考文章:
What is the difference between AV_SAMPLE_FMT_S16P and AV_SAMPLE_FMT_S16?

linux下lamealsa进行音频流操作(八)用ffmpeg将mp3转为wav相关推荐

  1. linux下lamealsa进行音频流操作(三)alsa音频编程教程

    1. ALSA音频编程介绍   ALSA 代表高级 Linux 声音架构.它由一组内核驱动程序.一个应用程序编程接口 (API) 库和用于在 Linux 下支持声音的实用程序组成.在本文中,我简要概述 ...

  2. Linux下对文件的操作及添加新用户

    Linux下对文件的操作及添加新用户 一.对文件的操作 1.打包压缩文件 2.解压缩文件 3.对文件操作的其他命令 二.创建新用户 一.对文件的操作 1.打包压缩文件 2.解压缩文件 3.对文件操作的 ...

  3. Linux下如何避免误操作执行 rm

    转载自 Linux下如何避免误操作执行 rm 最近IT圈子流行着一个段子: 某个蠢萌的程序员,不小心在公司的服务器上输入了 rm -rf/ 指令,结果......现在还没出狱呢. 当然,绝大部分程序员 ...

  4. linux 下卸载nginx的操作步骤

    linux 下卸载nginx的操作步骤 1.执行命令,看nginx是否在运行 ps -ef|grep nginx 2.如上图,发现nginx是在运行状态,所以停止nginx服务 注意,要进入到ngin ...

  5. linux下git的相关操作指令

    linux下git的相关操作指令 git版本管理工具 1.克隆仓库git clone "url" 2.上传2.1标记:告诉git工具需要管理那些文件了git add [filena ...

  6. linux下C语言编程操作数据库(sqlite3)

    前言:C语言中通过调用 sqlite 的函数接口来实现对数据库的管理(创建数据库.创建表格.插入数据.查询.数据.删除数据等),掌握sqlite数据库的语法,以及sqlite提供的函数接口,那么在li ...

  7. linux mysql c语言编程,在Linux下通过C语言操作MySQL数据库

    2010年1月27日 晚 22:10 作者:longyun(http://www.linuxdiyf.com/mailto:mtd527@gmail.com) 续:小弟最近想学习数据库,并想开发一个简 ...

  8. linux下监控用户的操作记录

    想知道用户登陆系统后都操作了什么,怎么办? 别急,linux下有一个script工具,专门记录终端会话中所有输入输出结果,并存放到指定文件中. 先看看怎么录制吧! 1.创建日志存放目录 1 2 # m ...

  9. 解决Linux下vi或vim操作Found a swap file by the name

    在linux下用vi或vim打开 文件时 E325: ATTENTION Found a swap file by the name ".1.py.swp"           o ...

  10. Linux下记录所有用户操作的脚本

    这个脚本是在网上找到的,稍微做了一些修改,可以实现在Linux下所有用户,不管是远程还是本地登陆,在本机的所有操作都会记录下来,并生成包含"用户/IP/时间"的文件存放在指定位置, ...

最新文章

  1. java 加载dll后打包_让Jacob从当前路径读取dll文件及相关打包方法
  2. 不愧是你!Python 之父退休太无聊,进微软搞开源!
  3. 阿里云容器化GPU共享服务已开放!性能无损失,对你的环境无侵入,真正实现AI降本增效...
  4. 蓝牙4.0技术分析1-广播者角色
  5. QT Creator应用程序开发——QT程序设计基本知识
  6. 一名“企业定制化人才”的自诉:“我不愿意,但却无可奈何”
  7. 使用纯索引子查询优化MySQL的分页查询速度
  8. 导入项目报错报错Error:java: Cannot run program “D:/jdk/jdk1.7.0_67/bin/java“
  9. Oracle 10gR2 Psu 相关
  10. vue 中引入使用jquery
  11. RHCE 学习笔记(4)- 重定向,管道和VIM编辑器
  12. Android View onVisibilityChanged onAttachedToWindow onDetachedFromWindow
  13. 光纤尾纤的型号和作用有哪些?
  14. 家谱宗族网站源码_云码宗谱网络家谱软件
  15. 目标检测网络 -- FasterRCNN
  16. android投影电脑屏幕,如何在电脑上投影手机屏幕
  17. 1116: 删除元素(数组)
  18. Deformable Convolutional Networks论文翻译——中文版
  19. 通过PyQt5+PyQtWebEngine+pyecharts建立自己的收入支出记账软件
  20. android ios 实时视频,Twitter推出适用于Android和iOS设备的实时视频

热门文章

  1. centos7 备份系统
  2. centos官网下载地址
  3. 计算机word虚线在哪里,电脑虚线怎么打出来
  4. 计算机常用英语词汇 短语,四级英语常用词汇短语
  5. 如何注册PayPal账户
  6. FTP文件实现上传下载
  7. 9年技术面试官讲解:计算机专业应届生怎样写简历
  8. JAVA项目经理面试题
  9. 使用注册表reg文件修复git bash git gui 右键快捷方式
  10. 各种软件系统架构图解析