alsa学习--5.用ffmpeg将mp3转为wav

本文介绍使用FFmpeg将MP3文件转换成WAV格式的方法,并详细解释了如何添加WAV头来创建完整文件。通过代码示例展示了从读取MP3到解码音频直至转换为PCM的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. 
1. 利用fffmpeg将mp3转为pcm并在pcm数据加上wav头就是一个完整的wav文件
2. 代码
  1. #include "utils.h"
  2. #include <libavutil/avutil.h>
  3. #include <libavutil/attributes.h>
  4. #include <libavutil/opt.h>
  5. #include <libavutil/mathematics.h>
  6. #include <libavutil/imgutils.h>
  7. #include <libavutil/samplefmt.h>
  8. #include <libavutil/timestamp.h>
  9. #include <libavformat/avformat.h>
  10. #include <libavcodec/avcodec.h>
  11. #include <libswscale/swscale.h>
  12. #include <libavutil/mathematics.h>
  13. #include <libswresample/swresample.h>
  14. #include <libavutil/channel_layout.h>
  15. #include <libavutil/common.h>
  16. #include <libavformat/avio.h>
  17. #include <libavutil/file.h>
  18. #include <libswresample/swresample.h>

  19. #define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000
  20.  //下面这四个结构体是为了分析wav头的
  21. typedef struct {
  22.     u_int magic;      /* 'RIFF' */
  23.     u_int length;     /* filelen */
  24.     u_int type;       /* 'WAVE' */
  25. } WaveHeader;

  26. typedef struct {
  27.     u_short format;       /* see WAV_FMT_* */
  28.     u_short channels;
  29.     u_int sample_fq;      /* frequence of sample */
  30.     u_int byte_p_sec;
  31.     u_short byte_p_spl;   /* samplesize; 1 or 2 bytes */
  32.     u_short bit_p_spl;    /* 8, 12 or 16 bit */
  33. } WaveFmtBody;

  34. typedef struct {
  35.     u_int type;        /* 'data' */
  36.     u_int length;      /* samplecount */
  37. } WaveChunkHeader;

  38. #define COMPOSE_ID(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
  39. #define WAV_RIFF COMPOSE_ID('R','I','F','F')
  40. #define WAV_WAVE COMPOSE_ID('W','A','V','E')
  41. #define WAV_FMT COMPOSE_ID('f','m','t',' ')
  42. #define WAV_DATA COMPOSE_ID('d','a','t','a')
  43. int insert_wave_header(FILE* fp, long data_len)
  44. {
  45.     int len; 
  46.     WaveHeader* header;
  47.     WaveChunkHeader* chunk;
  48.     WaveFmtBody* body;
  49.     
  50.     fseek(fp, 0, SEEK_SET);        //写到wav文件的开始处
  51.     
  52.     len = sizeof(WaveHeader)+sizeof(WaveFmtBody)+sizeof(WaveChunkHeader)*2;
  53.     char* buf = (char*)malloc(len);
  54.     header = (WaveHeader*)buf;
  55.     header->magic = WAV_RIFF;
  56.     header->length = data_len + sizeof(WaveFmtBody)+sizeof(WaveChunkHeader)*+ 4;
  57.     header->type = WAV_WAVE;
  58.    
  59.     chunk = buf+sizeof(WaveHeader);
  60.     chunk->type = WAV_FMT;
  61.     chunk->length = 16;

  62.     body = buf+sizeof(WaveHeader)+sizeof(WaveChunkHeader);
  63.     body->format = (u_short)0x0001;      //编码方式为pcm
  64.     body->channels = (u_short)0x02;      //声道数为2
  65.     body->sample_fq = 44100;             //采样频率为44.1k 
  66.     body->byte_p_sec = 176400;           //每秒所需字节数 44100*2*2=采样频率*声道*采样位数 
  67.     body->byte_p_spl = (u_short)0x4;     //对齐无意义
  68.     body->bit_p_spl = (u_short)16;       //采样位数16bit=2Byte


  69.     chunk = buf+sizeof(WaveHeader)+sizeof(WaveChunkHeader)+sizeof(WaveFmtBody);
  70.     chunk->type = WAV_DATA;
  71.     chunk->length = data_len;
  72.     
  73.     fwrite(buf, 1, len, fp);
  74.     free(buf);
  75.     return 0;
  76. }

  77. typedef struct {
  78.     int videoindex;
  79.     int sndindex;
  80.     AVFormatContext* pFormatCtx;
  81.     AVCodecContext* sndCodecCtx;
  82.     AVCodec* sndCodec;
  83.     SwrContext *swr_ctx;
  84.     DECLARE_ALIGNED(16,uint8_t,audio_buf) [AVCODEC_MAX_AUDIO_FRAME_SIZE * 4];
  85. }AudioState;

  86. int init_ffmpeg(AudioState* is, char* filepath)
  87. {
  88.     int i=0;
  89.     int ret;
  90.     is->sndindex = -1;
  91.     if(NULL == filepath)
  92.     {
  93.         dbmsg("input file is NULL");
  94.         return -1;
  95.     }
  96.     avcodec_register_all();
  97.     avfilter_register_all();
  98.     av_register_all();

  99.     is->pFormatCtx = avformat_alloc_context();

  100.     if(avformat_open_input(&is->pFormatCtx, filepath, NULL, NULL)!=0)
  101.         return -1;

  102.     if(avformat_find_stream_info(is->pFormatCtx, NULL)<0)
  103.         return -1;
  104.     av_dump_format(is->pFormatCtx,0, 0, 0);
  105.     is->videoindex = av_find_best_stream(is->pFormatCtx, AVMEDIA_TYPE_VIDEO, is->videoindex, -1, NULL, 0); 
  106.     is->sndindex = av_find_best_stream(is->pFormatCtx, AVMEDIA_TYPE_AUDIO,is->sndindex, is->videoindex, NULL, 0);
  107.     dbmsg("videoindex=%d, sndindex=%d", is->videoindex, is->sndindex);
  108.     if(is->sndindex != -1)
  109.     {
  110.         is->sndCodecCtx = is->pFormatCtx->streams[is->sndindex]->codec;
  111.         is->sndCodec = avcodec_find_decoder(is->sndCodecCtx->codec_id);
  112.         if(is->sndCodec == NULL)
  113.         {
  114.             dbmsg("Codec not found");
  115.             return -1;
  116.         }
  117.         if(avcodec_open2(is->sndCodecCtx, is->sndCodec, NULL) < 0)
  118.             return -1;
  119.     }
  120.     return 0;
  121. }

  122. int main(int argc, char **argv)
  123. {
  124.     int ret;
  125.     FILE* fp; 
  126.     int file_data_size = 0;                //这儿注意一个问题: 变量用时一定要初始化,否则会出现异常
  127.     int len1,len2, data_size, got_frame;
  128.     AVPacket *packet = av_mallocz(sizeof(AVPacket));
  129.     AVFrame *frame = av_frame_alloc();
  130.     AudioState* is = (AudioState*) av_mallocz(sizeof(AudioState));
  131.     uint8_t *out[] = { is->audio_buf };
  132.     fp = fopen("./test.wav", "wb+");
  133.     len1 = sizeof(WaveHeader)+sizeof(WaveFmtBody)+sizeof(WaveChunkHeader)*2;
  134.     fseek(fp,len1, SEEK_SET);      //在写之前先预留出wav的header,即44个字节
  135.     dbmsg("len1=%d",len1);
  136.      
  137.     //第1步初始化ffmpeg,并用ffmpeg解码,最后转为pcm格式
  138.     if( (ret=init_ffmpeg(is, argv[1])) != 0)            //1.1 初始化ffmpeg
  139.     {
  140.         dbmsg("init_ffmpeg error");
  141.         return -1;
  142.     }
  143.     while( (av_read_frame(is->pFormatCtx, packet)>=0) )    //1.2 循环读取mp3文件中的数据帧
  144.     { 
  145.         if(packet->stream_index != is->sndindex)
  146.             continue;
  147.         if((ret=avcodec_decode_audio4(is->sndCodecCtx, frame, &got_frame, packet)) < 0) //1.3 解码数据帧
  148.         {
  149.             dbmsg("file eof");
  150.             break;
  151.         }

  152.         if(got_frame <= 0) /* No data yet, get more frames */
  153.             continue;
  154.         data_size = av_samples_get_buffer_size(NULL, is->sndCodecCtx->channels, frame->nb_samples, is->sndCodecCtx->sample_fmt, 1);
  155.         //1.4下面将ffmpeg解码后的数据帧转为我们需要的数据(关于"需要的数据"下面有解释)
  156.         if(NULL==is->swr_ctx)
  157.         {
  158.             if(is->swr_ctx != NULL)
  159.                 swr_free(&is->swr_ctx);
  160.             dbmsg("frame: channnels=%d,format=%d, sample_rate=%d", frame->channels, frame->format, frame->sample_rate);
  161.             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);
  162.             if(is->swr_ctx == NULL)
  163.             {
  164.                 dbmsg("swr_ctx == NULL");
  165.             }
  166.             swr_init(is->swr_ctx);
  167.         }
  168.         len2 = swr_convert(is->swr_ctx, out, 44100,(const uint8_t **)frame->extended_data, frame->nb_samples);
  169.         file_data_size += len2;
  170.         //1.5 数据格式转换完成后就写到文件中 
  171.         fwrite((short *)is->audio_buf, sizeof(short), (size_t) len2* 2, fp);
  172.     }
  173.     file_data_size *= 4;
  174.     dbmsg("file_data_size=%d", file_data_size);
  175.     //第2步添加上wav的头
  176.     ret = insert_wave_header(fp, file_data_size);
  177.     av_free_packet(packet);
  178.     av_free(frame);
  179.     avcodec_close(is->sndCodecCtx);
  180.     avformat_close_input(&is->pFormatCtx);
  181.     fclose(fp);
  182.     return 0;
  183. }
2.运行结果
  1. cong@msi:/work/ffmpeg/test/alsa/testalsa/5mp3towav$ make run
  2. export LD_LIBRARY_PATH=/work/ffmpeg/out/lib/ \
  3.     && ./mp3towav /work/ffmpeg/test/resource//test.mp3
  4. mp3towav.c:main[150]: len1=44
  5. [mp3 @ 0x14d3620] Skipping 0 bytes of junk at 197687.
  6. libavutil/crc.c:av_crc_init[313]: 
  7. [mp3 @ 0x14d3620] Estimating duration from bitrate, this may be inaccurate
  8. Input #0, mp3, from '(null)':
  9.   Metadata:
  10.     artist : 佚名
  11.     title : 法国国歌 马赛曲
  12.     TYER : 2013-10-26
  13.   Duration: 00:03:28.20, start: 0.000000, bitrate: 199 kb/s
  14.     Stream #0:0: Audio: mp3, 44100 Hz, stereo, s16p, 192 kb/s
  15.     Stream #0:1: Video: mjpeg, yuvj420p(pc, bt470bg/unknown/unknown), 600x600 [SAR 1:1 DAR 1:1], 90k tbr, 90k tbn, 90k tbc
  16.     Metadata:
  17.       title : e
  18.       comment : Cover (front)
  19. mp3towav.c:init_ffmpeg[120]: videoindex=-1381258232, sndindex=0
  20. mp3towav.c:main[173]: frame: channnels=2,format=6, sample_rate=44100
  21. mp3towav.c:main[186]: file_data_size=36725760
ls查看
  1. cong@msi:/work/ffmpeg/test/alsa/testalsa/5mp3towav$ ls -l
  2. total 36064
  3. -rw-rw-r-- 1 cong cong 885 Sep 11 11:25 Makefile
  4. -rwxrwxr-x 1 cong cong 64126 Sep 11 11:44 mp3towav
  5. -rw-rw-r-- 1 cong cong 6183 Sep 11 11:24 mp3towav.c
  6. -rw-rw-r-- 1 cong cong 115344 Sep 11 11:44 mp3towav.o
  7. -rw-rw-r-- 1 cong cong 36725804 Sep 11 11:44 test.wav
  8. -rw-rw-r-- 1 cong cong 333 Sep 9 11:31 utils.h
3. 说明
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
  1. ffmpeg中:include/libavutil/samplefmt.h
  2. enum AVSampleFormat {
  3.     AV_SAMPLE_FMT_NONE = -1, 
  4.     AV_SAMPLE_FMT_U8, ///< unsigned 8 bits
  5.     AV_SAMPLE_FMT_S16, ///< signed 16 bits    --> 1 这个是pcm的数据格式
  6.     AV_SAMPLE_FMT_S32, ///< signed 32 bits
  7.     AV_SAMPLE_FMT_FLT, ///< float
  8.     AV_SAMPLE_FMT_DBL, ///< double

  9.     AV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planar
  10.     AV_SAMPLE_FMT_S16P, ///< signed 16 bits, planar  -->6 这个是ffmepg解码之后的数据格式
  11.     AV_SAMPLE_FMT_S32P, ///< signed 32 bits, planar
  12.     AV_SAMPLE_FMT_FLTP, ///< float, planar
  13.     AV_SAMPLE_FMT_DBLP, ///< double, planar

  14.     AV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically
  15. };
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?
http://stackoverflow.com/questions/18888986/what-is-the-difference-between-av-sample-fmt-s16p-and-av-sample-fmt-s16
4. 代码打包 
5mp3towav.rar (下载后改名为5mp3towav.tar.gz)
### 回答1: alsa-plugins-1.1.5.tar.bz2是一个压缩文件,用于安装和更新ALSA(Advanced Linux Sound Architecture)插件。ALSA是一种在Linux操作系统上提供音频支持的软件架构。 这个压缩文件包含了ALSA插件的源代码,可以在Linux系统上进行编译和安装。通过安装这些插件,用户可以获得更多的音频功能和选项。这些插件包括不同类型的音频效果、数据转换和输入/输出设备支持。 为了安装alsa-plugins-1.1.5.tar.bz2,首先需要解压缩该文件。在终端中使用以下命令可以完成解压缩操作: tar -xvf alsa-plugins-1.1.5.tar.bz2 然后,在解压后的文件夹中执行以下命令来编译和安装插件: ./configure make sudo make install 这将会在系统中安装ALSA插件,并使其可用于音频应用程序。安装完成后,用户可以根据自己的需要配置和使用这些插件。 总而言之,alsa-plugins-1.1.5.tar.bz2是一个用于安装和更新ALSA插件的压缩文件,用户可以通过编译和安装这些插件来增强Linux系统的音频功能。安装过程中需要解压缩文件并执行编译和安装命令。 ### 回答2: alsa-plugins-1.1.5.tar.bz2是一种软件压缩包,其中包含了alsa-plugins软件的版本1.1.5的安装文件。alsa-plugins是一个用于Advanced Linux Sound Architecture (ALSA)的插件集合,它提供了许多音频处理的功能和特性。 该软件包的.tar.bz2后缀表示它使用了tar和bzip2两种压缩算法。tar是一种文件归档工具,可以将多个文件和目录打包成一个文件。而bzip2是一种压缩工具,可以将文件压缩成较小的尺寸以节省存储空间。因此,alsa-plugins-1.1.5.tar.bz2可以解压缩成一个目录,其中包含了alsa-plugins软件的所有源代码和相关文件。 要安装alsa-plugins-1.1.5.tar.bz2,首先需要解压缩该文件。可以使用以下命令在终端中解压缩该文件: tar -xjf alsa-plugins-1.1.5.tar.bz2 解压缩后将得到一个名为alsa-plugins-1.1.5的目录。在该目录中,可能会包含一些安装说明或配置文件,可以阅读这些文档以了解如何编译和安装alsa-plugins。 一般来说,编译和安装软件需要在终端中使用一系列命令。通常的步骤包括配置软件、编译软件和安装软件。具体步骤可能因软件的不同而有所不同,所以建议参考安装说明或官方文档以获得详细指导。 安装成功后,alsa-plugins将被安装在系统中,并且可以在相关的应用程序或设置中使用。alsa-plugins提供了丰富的音频处理插件,可以用于音频播放、录制和处理等多种场景。通过这些插件,用户可以增强音频性能、改善音质、添加音效等。 总之,alsa-plugins-1.1.5.tar.bz2是alsa-plugins软件版本1.1.5的压缩包,解压缩后即可获取该软件的源代码和文件。安装该软件可以提供丰富的音频处理插件,以改善Linux操作系统中的音频功能和体验。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值