ffmpeg 编程常用 pcm 转 aac aac 转 pcm mp4 h264解码

ffmpeg 是现在开源的全能编解码器,基本上全格式都支持,纯 c 语言作成,相对比其它的 VLC ,GStreamer glib2 写的,开发更简单些,文档很棒,就是 examples 比较少。

常用的功能有:

AVFrame 数据帧
AVCodecContext 编解码器
AVPacket 数据帧
swr_convert 格式转换器

ffmpeg 的使用都差不多,查找解码器,准备数据,解码,拿结果。

基本上会了一种,其它的也就能会,新版的 4.x 的代码较之前的有些变化,现在大部分之前的代码也是兼容的。有一些 定义成了 enum 。

介绍几个非常实用的例子:

1, pcm 编码 aac (aac 和 m4a 是一种类型)

需要 libfdk_aac 库自行安装配置好,使用 ubuntu 16.0.4 x64 g++ 编译

g++ -g main.cpp -lavcodec -lavformat -lswresample -lavutil -std=c++11 -o wav_to_m4a

用法 ./wav_to_m4a ../xxx.wav ,需要说明的是,有些网站下载的 wav 根本不能用,最好是用 ffmpeg 命令转换。

现实的需求中,没有人让你做一个格式转换器。可能是从 ALSA 读取原始PCM 在编码成 AAC 或通过网络发走,或保存文件。

下面的例子,仅是编码成了 AAC 但是未添加 AAC 头信息。有空在更新。但是可以用 播放器放的。

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <iostream>
  4 #include <fstream>
  5 #include <sys/types.h>
  6 #include <sys/stat.h>
  7 #include <fcntl.h>
  8 #include <sys/mman.h>
  9 
 10 #ifdef __cplusplus
 11 extern "C" 
 12 {
 13 #endif
 14 #include <libavformat/avformat.h>
 15 #include <libavcodec/avcodec.h>
 16 #include <libavutil/channel_layout.h>
 17 #include <libavutil/common.h>
 18 #include <libavutil/frame.h>
 19 #include <libavutil/samplefmt.h>
 20 #include <libavutil/mem.h>
 21 #ifdef __cplusplus
 22 }
 23 #endif
 24 
 25 #ifndef WORD
 26 #define WORD unsigned short
 27 #endif
 28 
 29 #ifndef DWORD
 30 #define DWORD unsigned int
 31 #endif
 32     
 33 struct RIFF_HEADER
 34 {
 35     char szRiffID[4];  // 'R','I','F','F'
 36     DWORD dwRiffSize;
 37     char szRiffFormat[4]; // 'W','A','V','E'
 38 };
 39 
 40 struct WAVE_FORMAT
 41 {
 42     WORD wFormatTag;
 43     WORD wChannels;
 44     DWORD dwSamplesPerSec;
 45     DWORD dwAvgBytesPerSec;
 46     WORD wBlockAlign;
 47     WORD wBitsPerSample;
 48 };
 49 
 50 struct FMT_BLOCK
 51 {
 52     char  szFmtID[4]; // 'f','m','t',' '
 53     DWORD  dwFmtSize;
 54     struct WAVE_FORMAT wavFormat;
 55 };
 56 
 57 struct DATA_BLOCK
 58 {
 59     char szDataID[4]; // 'd','a','t','a'
 60     DWORD dwDataSize;
 61 };
 62 
 63 using namespace std;
 64 
 65 void read_wav(uint8_t *wav_buf, int *fs, int *channels, int *bits_per_sample, int *wav_size, int *file_size)
 66 {
 67     struct RIFF_HEADER *headblk;
 68     struct FMT_BLOCK   *fmtblk;
 69     struct DATA_BLOCK  *datblk;
 70 
 71     headblk = (struct RIFF_HEADER *) wav_buf;
 72     fmtblk  = (struct FMT_BLOCK *) &headblk[1];
 73     datblk  = (struct DATA_BLOCK *) &fmtblk[1];
 74     
 75     *file_size = headblk->dwRiffSize;
 76 
 77     //采样频率
 78     *fs        = fmtblk->wavFormat.dwSamplesPerSec;
 79     //通道数
 80     *channels  = fmtblk->wavFormat.wChannels;
 81     *wav_size  = datblk->dwDataSize;
 82     //采样bit数 16 24
 83     *bits_per_sample = fmtblk->wavFormat.wBitsPerSample;
 84 }
 85 
 86 int main(int argc, char **argv)
 87 {
 88     int fd;
 89     int ret;
 90     struct stat stat;
 91     int fs, channels, bits_per_sample, wav_size, file_size;
 92     uint8_t *wav_buf;
 93     uint8_t *audio_buf;    
 94     const char           *out_file = "out.m4a";
 95     const AVCodec        *codec;
 96     AVFrame              *frame;
 97     AVPacket             *encodePacket;
 98     AVCodecContext       *codecContext;
 99     
100     //打开文件进行 mmap 
101     fd = open(argv[1], O_RDONLY);
102     fstat(fd, &stat);
103     wav_buf = (uint8_t*)mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd, 0);
104     read_wav(wav_buf, &fs, &channels, &bits_per_sample, &wav_size, &file_size);
105     printf("wav format: fs = %d, channels = %d, bits_per_sample = %d, wav_size = %d file_size = %d\n\r", fs, channels, bits_per_sample, wav_size, file_size);
106     
107     //真实wav 跳过头部
108     audio_buf = wav_buf + sizeof(struct RIFF_HEADER) + sizeof(struct FMT_BLOCK) + sizeof(struct DATA_BLOCK);
109 
110     avcodec_register_all(); //ffmpeg 4.x 已经不需要此函数
111     
112     codec =  avcodec_find_encoder_by_name("libfdk_aac");
113 
114     if(! codec)
115     {
116         printf("avcodec_find_encoder error \n");
117         return -1;
118     }
119 
120     codecContext = avcodec_alloc_context3(codec);
121     if(! codecContext)
122     {
123         printf("Could not allocate audio codec context \n");
124         return -1;
125     }
126 
127     codecContext->bit_rate       = 64000;
128     codecContext->sample_fmt     = AV_SAMPLE_FMT_S16; 
129     codecContext->sample_rate    = 44100;
130     codecContext->channel_layout = AV_CH_LAYOUT_STEREO;
131     codecContext->channels       = av_get_channel_layout_nb_channels(codecContext->channel_layout);
132 
133     printf("sample_fmt:%d sample_rate:%d channel_layout:%d channels:%d \n", codecContext->sample_fmt, codecContext->sample_rate, codecContext->channel_layout, codecContext->channels);
134 
135     /* open avcodec */
136     if(0 > avcodec_open2(codecContext, codec, NULL))
137     {
138         printf("Could not open codec \n");
139         return -1;
140     }
141 
142     /* frame containing input raw audio */
143     frame = av_frame_alloc();
144     if(! frame)
145     {
146         printf("Could not allocate audio frame \n");
147         return -1;
148     }
149 
150     frame->nb_samples     = codecContext->frame_size;
151     frame->format         = codecContext->sample_fmt;
152     frame->channel_layout = codecContext->channel_layout;
153 
154     printf("nb_samples:%d format:%d channel_layout:%d \n", frame->nb_samples, frame->format, frame->channel_layout);
155 
156     encodePacket = av_packet_alloc();
157     
158     int size = av_samples_get_buffer_size(NULL, codecContext->channels,codecContext->frame_size,codecContext->sample_fmt, 1);
159     uint8_t *frame_buf = (uint8_t *)av_malloc(size);
160     avcodec_fill_audio_frame(frame, codecContext->channels, codecContext->sample_fmt,(const uint8_t*)frame_buf, size, 1);
161     
162     ofstream acc_file(out_file, ios::binary | ios::out | ios::trunc);
163 
164     int pos = 0;
165     while(pos <= file_size)
166     {
167         frame->data[0] = audio_buf + pos;
168 
169         /* send the frame for encoding */
170         ret = avcodec_send_frame(codecContext, frame);
171         if(0 > ret)
172         {
173             printf("Error sending the frame to the encoder \n");
174             return -1;
175         }
176 
177         /* read all the available output packets (in general there may be any
178          * number of them */
179         while(0 <= ret)
180         {
181             ret = avcodec_receive_packet(codecContext, encodePacket);
182             if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
183                 break;
184             else if(0 > ret)
185             {
186                 printf("Error encoding audio frame \n");
187                 return -1;
188             }
189 
190             //c++ 写文件流
191             acc_file.write((const char*)encodePacket->data, encodePacket->size);
192             av_packet_unref(encodePacket);
193         }
194 
195         pos += size;
196     }
197 
198     cout << "encode done" << endl;
199     return 0;
200 }
201 
202 // main.cpp

 

2, acc 解码 pcm

3, mp4 解码 h264 acc 并调用 SDL 播放视频

 

更新中。。

 

posted @ 2018-11-10 21:17  宁次  阅读(5872)  评论(0编辑  收藏  举报