最简单的基于FFMPEG的音频编码器(PCM编码为AAC)

时间:2024-04-22 13:07:17

http://blog.****.net/leixiaohua1020/article/details/25430449

本文介绍一个最简单的基于FFMPEG的音频编码器。该编码器实现了PCM音频采样数据编码为AAC的压缩编码数据。编码器代码十分简单,但是每一行代码都很重要。通过看本编码器的源代码,可以了解FFMPEG音频编码的流程。

本程序使用最新版的类库(编译时间为2014.5.6),开发平台为VC2010。所有的配置都已经做好,只需要运行就可以了。

流程(2014.9.29更新)

下面附一张使用FFmpeg编码音频的流程图。使用该流程,不仅可以编码AAC的音频,而且可以编码MP3,MP2等等各种FFmpeg支持的音频。图中蓝色背景的函数是实际输出数据的函数。浅绿色的函数是音频编码的函数。

最简单的基于FFMPEG的音频编码器(PCM编码为AAC)

简单介绍一下流程中各个函数的意义:

av_register_all():注册FFmpeg所有编解码器。

avformat_alloc_output_context2():初始化输出码流的AVFormatContext。

avio_open():打开输出文件。

av_new_stream():创建输出码流的AVStream。

avcodec_find_encoder():查找编码器。

avcodec_open2():打开编码器。

avformat_write_header():写文件头(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)。

avcodec_encode_audio2():编码音频。即将AVFrame(存储PCM采样数据)编码为AVPacket(存储AAC,MP3等格式的码流数据)。

av_write_frame():将编码后的视频码流写入文件。

av_write_trailer():写文件尾(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)。

代码

  1. /**
  2. *最简单的基于FFmpeg的音频编码器
  3. *Simplest FFmpeg Audio Encoder
  4. *
  5. *雷霄骅 Lei Xiaohua
  6. *leixiaohua1020@126.com
  7. *中国传媒大学/数字电视技术
  8. *Communication University of China / Digital TV Technology
  9. *http://blog.****.net/leixiaohua1020
  10. *
  11. *本程序实现了音频PCM采样数据编码为压缩码流(MP3,WMA,AAC等)。
  12. *是最简单的FFmpeg音频编码方面的教程。
  13. *通过学习本例子可以了解FFmpeg的编码流程。
  14. *This software encode PCM data to AAC bitstream.
  15. *It's the simplest audio encoding software based on FFmpeg.
  16. *Suitable for beginner of FFmpeg
  17. */
  18. #include <stdio.h>
  19. #define __STDC_CONSTANT_MACROS
  20. #ifdef _WIN32
  21. //Windows
  22. extern "C"
  23. {
  24. #include "libavcodec/avcodec.h"
  25. #include "libavformat/avformat.h"
  26. };
  27. #else
  28. //Linux...
  29. #ifdef __cplusplus
  30. extern "C"
  31. {
  32. #endif
  33. #include <libavcodec/avcodec.h>
  34. #include <libavformat/avformat.h>
  35. #ifdef __cplusplus
  36. };
  37. #endif
  38. #endif
  39. int flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index){
  40. int ret;
  41. int got_frame;
  42. AVPacket enc_pkt;
  43. if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &
  44. CODEC_CAP_DELAY))
  45. return 0;
  46. while (1) {
  47. enc_pkt.data = NULL;
  48. enc_pkt.size = 0;
  49. av_init_packet(&enc_pkt);
  50. ret = avcodec_encode_audio2 (fmt_ctx->streams[stream_index]->codec, &enc_pkt,
  51. NULL, &got_frame);
  52. av_frame_free(NULL);
  53. if (ret < 0)
  54. break;
  55. if (!got_frame){
  56. ret=0;
  57. break;
  58. }
  59. printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n",enc_pkt.size);
  60. /* mux encoded frame */
  61. ret = av_write_frame(fmt_ctx, &enc_pkt);
  62. if (ret < 0)
  63. break;
  64. }
  65. return ret;
  66. }
  67. int main(int argc, char* argv[])
  68. {
  69. AVFormatContext* pFormatCtx;
  70. AVOutputFormat* fmt;
  71. AVStream* audio_st;
  72. AVCodecContext* pCodecCtx;
  73. AVCodec* pCodec;
  74. uint8_t* frame_buf;
  75. AVFrame* pFrame;
  76. AVPacket pkt;
  77. int got_frame=0;
  78. int ret=0;
  79. int size=0;
  80. FILE *in_file=NULL;                         //Raw PCM data
  81. int framenum=1000;                          //Audio frame number
  82. const char* out_file = "tdjm.aac";          //Output URL
  83. int i;
  84. in_file= fopen("tdjm.pcm", "rb");
  85. av_register_all();
  86. //Method 1.
  87. pFormatCtx = avformat_alloc_context();
  88. fmt = av_guess_format(NULL, out_file, NULL);
  89. pFormatCtx->oformat = fmt;
  90. //Method 2.
  91. //avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, out_file);
  92. //fmt = pFormatCtx->oformat;
  93. //Open output URL
  94. if (avio_open(&pFormatCtx->pb,out_file, AVIO_FLAG_READ_WRITE) < 0){
  95. printf("Failed to open output file!\n");
  96. return -1;
  97. }
  98. audio_st = avformat_new_stream(pFormatCtx, 0);
  99. if (audio_st==NULL){
  100. return -1;
  101. }
  102. pCodecCtx = audio_st->codec;
  103. pCodecCtx->codec_id = fmt->audio_codec;
  104. pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
  105. pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;
  106. pCodecCtx->sample_rate= 44100;
  107. pCodecCtx->channel_layout=AV_CH_LAYOUT_STEREO;
  108. pCodecCtx->channels = av_get_channel_layout_nb_channels(pCodecCtx->channel_layout);
  109. pCodecCtx->bit_rate = 64000;
  110. //Show some information
  111. av_dump_format(pFormatCtx, 0, out_file, 1);
  112. pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
  113. if (!pCodec){
  114. printf("Can not find encoder!\n");
  115. return -1;
  116. }
  117. if (avcodec_open2(pCodecCtx, pCodec,NULL) < 0){
  118. printf("Failed to open encoder!\n");
  119. return -1;
  120. }
  121. pFrame = av_frame_alloc();
  122. pFrame->nb_samples= pCodecCtx->frame_size;
  123. pFrame->format= pCodecCtx->sample_fmt;
  124. size = av_samples_get_buffer_size(NULL, pCodecCtx->channels,pCodecCtx->frame_size,pCodecCtx->sample_fmt, 1);
  125. frame_buf = (uint8_t *)av_malloc(size);
  126. avcodec_fill_audio_frame(pFrame, pCodecCtx->channels, pCodecCtx->sample_fmt,(const uint8_t*)frame_buf, size, 1);
  127. //Write Header
  128. avformat_write_header(pFormatCtx,NULL);
  129. av_new_packet(&pkt,size);
  130. for (i=0; i<framenum; i++){
  131. //Read PCM
  132. if (fread(frame_buf, 1, size, in_file) <= 0){
  133. printf("Failed to read raw data! \n");
  134. return -1;
  135. }else if(feof(in_file)){
  136. break;
  137. }
  138. pFrame->data[0] = frame_buf;  //PCM Data
  139. pFrame->pts=i*100;
  140. got_frame=0;
  141. //Encode
  142. ret = avcodec_encode_audio2(pCodecCtx, &pkt,pFrame, &got_frame);
  143. if(ret < 0){
  144. printf("Failed to encode!\n");
  145. return -1;
  146. }
  147. if (got_frame==1){
  148. printf("Succeed to encode 1 frame! \tsize:%5d\n",pkt.size);
  149. pkt.stream_index = audio_st->index;
  150. ret = av_write_frame(pFormatCtx, &pkt);
  151. av_free_packet(&pkt);
  152. }
  153. }
  154. //Flush Encoder
  155. ret = flush_encoder(pFormatCtx,0);
  156. if (ret < 0) {
  157. printf("Flushing encoder failed\n");
  158. return -1;
  159. }
  160. //Write Trailer
  161. av_write_trailer(pFormatCtx);
  162. //Clean
  163. if (audio_st){
  164. avcodec_close(audio_st->codec);
  165. av_free(pFrame);
  166. av_free(frame_buf);
  167. }
  168. avio_close(pFormatCtx->pb);
  169. avformat_free_context(pFormatCtx);
  170. fclose(in_file);
  171. return 0;
  172. }

结果

程序运行完成后,会将一个PCM采样数据文件(*.pcm)编码为AAC码流文件(*.aac)。

下载

simplest ffmpeg audio encoder

项目主页

SourceForge:https://sourceforge.net/projects/simplestffmpegaudioencoder/

Github:https://github.com/leixiaohua1020/simplest_ffmpeg_audio_encoder

开源中国:http://git.oschina.net/leixiaohua1020/simplest_ffmpeg_audio_encoder

****工程下载地址:

http://download.****.net/detail/leixiaohua1020/7324091

PUDN工程下载地址:

http://www.pudn.com/downloads644/sourcecode/multimedia/detail2605236.html

更新-1.1 (2015.2.13)=========================================

这次考虑到了跨平台的要求,调整了源代码。经过这次调整之后,源代码可以在以下平台编译通过:

VC++:打开sln文件即可编译,无需配置。

cl.exe:打开compile_cl.bat即可命令行下使用cl.exe进行编译,注意可能需要按照VC的安装路径调整脚本里面的参数。编译命令如下。

  1. ::VS2010 Environment
  2. call "D:\Program Files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"
  3. ::include
  4. @set INCLUDE=include;%INCLUDE%
  5. ::lib
  6. @set LIB=lib;%LIB%
  7. ::compile and link
  8. cl simplest_ffmpeg_audio_encoder.cpp /link avcodec.lib avformat.lib avutil.lib ^
  9. avdevice.lib avfilter.lib postproc.lib swresample.lib swscale.lib /OPT:NOREF

MinGW:MinGW命令行下运行compile_mingw.sh即可使用MinGW的g++进行编译。编译命令如下。

  1. g++ simplest_ffmpeg_audio_encoder.cpp -g -o simplest_ffmpeg_audio_encoder.exe \
  2. -I /usr/local/include -L /usr/local/lib -lavformat -lavcodec -lavutil

GCC:Linux或者MacOS命令行下运行compile_gcc.sh即可使用GCC进行编译。编译命令如下。

  1. gcc simplest_ffmpeg_audio_encoder.cpp -g -o simplest_ffmpeg_audio_encoder.out \
  2. -I /usr/local/include -L /usr/local/lib -lavformat -lavcodec -lavutil

PS:相关的编译命令已经保存到了工程文件夹中

****下载地址:http://download.****.net/detail/leixiaohua1020/8445209

SourceForge上已经更新。