title: ffmpeg_sample解读_encode_audio
date: 2020-10-23 10:15:02
tags: [读书笔记]
typora-copy-images-to: ./imgs
typora-root-url: ./imgs
总结
随机生成音频数据.然后编码成帧.送入编码生成packet.在写入文件.自己制定音频的码率bit_rate,采样位数sample_fmt,采样率sample_rate,声道数channel和声道布局channel_layout
流程图
graph TB
findEncoder[avcodec_find_encoder]
-->allocContext[avcodec_alloc_context3]
-->setContextParam[设置编码上下文参数]
-->codecOpen[avcodec_open2]
-->packet[av_packet_alloc]
-->frame[av_frame_alloc]
-->buff[av_frame_get_buffer]
-->writeable[av_frame_make_writable]
-->encode[encode]
-->sendFrame{avcodec_send_frame>0?}
-->|yes|readPacket[avcodec_receive_packet]
-->fwrite[fwrite]
-->unref[av_packet_unref]
-->release[release]
sendFrame-->|no|no
no-->release
代码
/**
* @file
* audio encoding with libavcodec API example.
*
* @example encode_audio.c
*/
#include
#include
#include
#include
#include
#include
#include
#include
/* check that a given sample format is supported by the encoder */
static int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt) {
const enum AVSampleFormat *p = codec->sample_fmts;
while (*p != AV_SAMPLE_FMT_NONE) {
if (*p == sample_fmt)
return 1;
p++;
}
return 0;
}
/**
* 找到最匹配 编码器支持的采样率最接近的值 supported_samplerates
* just pick the highest supported samplerate */
static int select_sample_rate(const AVCodec *codec) {
const int *p;
int best_samplerate = 0;
//找到离44100最佳的采样率
if (!codec->supported_samplerates)
return 44100;
p = codec->supported_samplerates;
while (*p) {
if (!best_samplerate || abs(44100 - *p) < abs(44100 - best_samplerate))
best_samplerate = *p;
p++;
}
return best_samplerate;
}
//找到编码器最高的通道数
/* select layout with the highest channel count */
static int select_channel_layout(const AVCodec *codec) {
const uint64_t *p;
uint64_t best_ch_layout = 0;
int best_nb_channels = 0;
if (!codec->channel_layouts)
return AV_CH_LAYOUT_STEREO;
p = codec->channel_layouts;
while (*p) {
int nb_channels = av_get_channel_layout_nb_channels(*p);
if (nb_channels > best_nb_channels) {
best_ch_layout = *p;
best_nb_channels = nb_channels;
}
p++;
}
return best_ch_layout;
}
static void encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt,
FILE *output) {
int ret;
//原生数据送入编码器,得到packet,如果 frame传null.就会flush 编码器中遗留的数据.所以最后总要才传个null进来
/* send the frame for encoding */
ret = avcodec_send_frame(ctx, frame);
if (ret < 0) {
fprintf(stderr, "Error sending the frame to the encoder\n");
exit(1);
}
/* read all the available output packets (in general there may be any
* number of them */
while (ret >= 0) {
//从编码器中拿到能用的编码后的packet .写到文件中
ret = avcodec_receive_packet(ctx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error encoding audio frame\n");
exit(1);
}
//把pkt.data中的数据.以 1字节为单位,一共写入size个字节.到output文件中,同时更新文件指针到写入的末尾
fwrite(pkt->data, 1, pkt->size, output);
//是否packet以便再次写入
av_packet_unref(pkt);
}
}
/**
* 编码音频 随机生成音频文件.组成frame.送如编码器编码后生成packet.写入文件
* @param argc
* @param argv
* @return
* avcodec_find_encoder:根据指定的AVCodecID查找注册的解码器。
avcodec_alloc_context3:为AVCodecContext分配内存。
avcodec_open2:打开解码器。
avcodec_send_frame:将AVFrame非压缩数据给编码器。详细介绍见FFmpeg音频解码的编解码API介绍部分。
avcodec_receive_packet:获取到编码后的AVPacket数据。
av_frame_get_buffer: 为音频或视频数据分配新的buffer。在调用这个函数之前,必须在AVFame上设置好以下属性:format(视频为像素格式,音频为样本格式)、nb_samples(样本个数,针对音频)、channel_layout(通道类型,针对音频)、width/height(宽高,针对视频)。
av_frame_make_writable:确保AVFrame是可写的,尽可能避免数据的复制。
如果AVFrame不是是可写的,将分配新的buffer和复制数据。
链接:https://www.jianshu.com/p/c6154e106b8c
*/
int encode_audio_main(int argc, char **argv) {
const char *filename;
const AVCodec *codec;
AVCodecContext *c = NULL;
AVFrame *frame;
AVPacket *pkt;
int i, j, k, ret;
FILE *f;
uint16_t *samples;
float t, tincr;
if (argc <= 1) {
fprintf(stderr, "Usage: %s