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. 关于javac编译时出现“非法字符:\65279”的解决方法

    一般用UE或记事本编辑过的UTF-8的文件头会加入BOM标识,该标识由3个char组成.在UTF-8的标准里该BOM标识是可有可无的,Sun 的javac 在编译带有BOM的UTF-8的格式的文件时会 ...

  2. uCGUI字符串显示过程分析和uCGUI字库的组建

    为什么要分析字符串的显示过程? 学习uCGUI主要是学习如何使用的,为何要深究到源码的层次呢? 就分析字符串显示过程的原因来说,是因为移植汉字字库的需要.uCGUI并么有合适的汉字字库,而且完整的汉字 ...

  3. jquery组件团购倒计时功能(转)

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  4. sum(iterable[, start]) 对集合求和

    >>> LL [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21] >>> L [3, 4, 5, 6, 7, 8, 9] >> ...

  5. eclipse 安装git插件

    Eclipse上安装GIT插件EGit及使用 博客分类: GIT   一.Eclipse上安装GIT插件EGit Eclipse的版本eclipse-java-helios-SR2-win32.zip ...

  6. 中文输入法在vs2010中失效解决方案

    这样你就可以用切换输入法的方式,输入中文咯.     后来用了2次发现还是有问题,后来我就直接把输入法的切换改成ctrl+1,后来使用就一直没有问题.总之,解决方案视具体情况解决.

  7. MySql 命令积累

    一. 修改表的自增起点 ; 二.获取自增键值 1. select max(id) from tablename 2.SELECT LAST_INSERT_ID() 函数 LAST_INSERT_ID ...

  8. MinGW 编译libwebsockets

    libwebsockets是一个轻量的纯C库,在这里尝试使用MinGW进行构建. 官网地址:http://libwebsockets.org/trac/libwebsockets下载地址:http:/ ...

  9. 【原创】FPGA开发手记(一) UART接口

    以下内容均以Xilinx的Nexys3作为开发板 1. UART简介 UART(即Universal Asynchronous Receiver Transmitter 通用异步收发器)是广泛使用的串 ...

  10. hdu4714Tree2cycle

    链接 树上的一些操作还是不是太好想 直接dfs下去 不是最优的 一个节点最多保留两个度 如果它有两个以上的子节点 那么就与父节点断开 与k-2个子节点断开 再重新连 #pragma comment(l ...