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

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

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

流程(2014.9.29更新)

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

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

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.csdn.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

CSDN工程下载地址:

http://download.csdn.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:相关的编译命令已经保存到了工程文件夹中

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

SourceForge上已经更新。

最简单的基于FFMPEG的音频编码器(PCM编码为AAC)的更多相关文章

  1. 最简单的基于FFMPEG的图像编码器(YUV编码为JPEG)

    伴随着毕业论文的完成,这两天终于腾出了空闲,又有时间搞搞FFMPEG的研究了.想着之前一直搞的都是FFMPEG解码方面的工作,很少涉及到FFMPEG编码方面的东西,于是打算研究一下FFMPEG的编码. ...

  2. 最简单的基于FFMPEG的图像编码器(YUV编码为JPEG)(转)

    原文转自 https://blog.csdn.net/leixiaohua1020/article/details/25346147/ 伴随着毕业论文的完成,这两天终于腾出了空闲,又有时间搞搞FFMP ...

  3. 最简单的基于FFMPEG的视频编码器(YUV编码为H.264)

    本文介绍一个最简单的基于FFMPEG的视频编码器.该编码器实现了YUV420P的像素数据编码为H.264的压缩编码数据.编码器代码十分简单,可是每一行代码都非常重要,适合好好研究一下.弄清楚了本代码也 ...

  4. 最简单的基于FFMPEG+SDL的音频播放器 ver2 (采用SDL2.0)

    ===================================================== 最简单的基于FFmpeg的音频播放器系列文章列表: <最简单的基于FFMPEG+SDL ...

  5. 最简单的基于FFMPEG+SDL的音频播放器 ver2 (採用SDL2.0)

    ===================================================== 最简单的基于FFmpeg的音频播放器系列文章列表: <最简单的基于FFMPEG+SDL ...

  6. 最简单的基于FFmpeg的编码器-纯净版(不包含libavformat)

    ===================================================== 最简单的基于FFmpeg的视频编码器文章列表: 最简单的基于FFMPEG的视频编码器(YUV ...

  7. 最简单的基于FFMPEG的转码程序

    本文介绍一个简单的基于FFmpeg的转码器.它可以将一种视频格式(包括封转格式和编码格式)转换为另一种视频格式.转码器在视音频编解码处理的程序中,属于一个比较复杂的东西.因为它结合了视频的解码和编码. ...

  8. 最简单的基于FFmpeg的视频编码器-更新版(YUV编码为HEVC(H.265))

    ===================================================== 最简单的基于FFmpeg的视频编码器文章列表: 最简单的基于FFMPEG的视频编码器(YUV ...

  9. 最简单的基于FFmpeg的内存读写的例子:内存转码器

    ===================================================== 最简单的基于FFmpeg的内存读写的例子系列文章列表: 最简单的基于FFmpeg的内存读写的 ...

随机推荐

  1. poj 3013 Big Christmas Tree Djistra

    Big Christmas Tree 题意:图中每个节点和边都有权值,图中找出一颗树,树根为1使得 Σ(树中的节点到树根的距离)*(以该节点为子树的所有节点的权值之和) 结果最小: 分析:直接求出每个 ...

  2. MVC5 ModelBinder

    MVC5 ModelBinder 什么是ModelBinding ASP.NET MVC中,所有的请求最终都会到达某个Controller中的某个Action并由该Action负责具体的处理和响应.为 ...

  3. Oracle删除所有表

    Oracle 清理用户所有的表 PLSQL中执行语句 select 'drop '||object_type||' '||object_name||';' from user_objects; 结果集 ...

  4. [转载]MongoDB学习 (四):创建、读取、更新、删除(CRUD)快速入门

    本文介绍数据库的4个基本操作:创建.读取.更新和删除(CRUD). 接下来的数据库操作演示,我们使用MongoDB自带简洁但功能强大的JavaScript shell,MongoDB shell是一个 ...

  5. [转载]MongoDB学习(三):MongoDB Shell的使用

    MongoDB shell MongoDB自带简洁但功能强大的JavaScript shell.JavaScript shell键入一个变量会将变量的值转换为字符串打印到控制台上. 下面介绍基本的操作 ...

  6. Android-java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

    章出自:luchg技术交流 http://www.luchg.com 版权所有.本站文章除注明出处外,皆为作者原创文章,可自由引用,但请注明来源,谢谢. Android-java.lang.Runti ...

  7. [Gauss]POJ1222 EXTENDED LIGHTS OUT

    题意:给一个5*6的矩阵 1代表该位置的灯亮着, 0代表该位置的灯没亮 按某个位置的开关,可以同时改变 该位置 以及 该位置上方.下方.左方.右方, 共五个位置的灯的开.关(1->0, 0-&g ...

  8. 如何查看自己运行ubuntu是32位还是64位

    当安装ubuntu在pc上,不推荐在32位pc安装64位操作系统,64位pc安装32位操作系统 方法/步骤 按ctrl+shift+t 快捷键,打开终端,输入sudo uname --m ,按下ent ...

  9. 查看wtmp文件内容

    1./var/log/wtmp文件的作用     /var/log/wtmp也是一个二进制文件,记录每个用户的登录次数和持续时间等信息!   2.查看方法:可以用last命令输出当中内容 1 2 3 ...

  10. UpdatePanel无法直接弹出窗口的解决

    UpdatePanel无法直接弹出窗口的解决 2010-06-20  来自:博客园  字体大小:[大 中 小] 摘要:本文介绍一些UpdatePanel无法直接弹出窗口的解决方法 ///<sum ...