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 播放视频
更新中。。