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 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 10 #ifdef __cplusplus 11 extern "C" 12 { 13 #endif 14 #include 15 #include 16 #include 17 #include 18 #include 19 #include 20 #include 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 any178 * 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 播放视频
更新中。。