title: ffmpeg_sample解讀_encode_audio
date: 2020-10-23 10:15:02
tags: [讀書(shū)筆記](méi)
typora-copy-images-to: ./imgs
typora-root-url: ./imgs
總結(jié)
隨機(jī)生成音頻數(shù)據(jù).然后編碼成幀.送入編碼生成packet.在寫(xiě)入文件.自己制定音頻的碼率bit_rate,采樣位數(shù)sample_fmt,采樣率sample_rate,聲道數(shù)channel和聲道布局channel_layout
流程圖
graph TB
findEncoder[avcodec_find_encoder]
-->allocContext[avcodec_alloc_context3]
-->setContextParam[設(shè)置編碼上下文參數(shù)]
-->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
image-20201023153653504
代碼
/**
* @file
* audio encoding with libavcodec API example.
*
* @example encode_audio.c
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/frame.h>
#include <libavutil/samplefmt.h>
/* 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;
}
//找到編碼器最高的通道數(shù)
/* 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;
//原生數(shù)據(jù)送入編碼器,得到packet,如果 frame傳null.就會(huì)flush 編碼器中遺留的數(shù)據(jù).所以最后總要才傳個(gè)null進(jìn)來(lái)
/* 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 .寫(xiě)到文件中
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中的數(shù)據(jù).以 1字節(jié)為單位,一共寫(xiě)入size個(gè)字節(jié).到output文件中,同時(shí)更新文件指針到寫(xiě)入的末尾
fwrite(pkt->data, 1, pkt->size, output);
//是否packet以便再次寫(xiě)入
av_packet_unref(pkt);
}
}
/**
* 編碼音頻 隨機(jī)生成音頻文件.組成frame.送如編碼器編碼后生成packet.寫(xiě)入文件
* @param argc
* @param argv
* @return
* avcodec_find_encoder:根據(jù)指定的AVCodecID查找注冊(cè)的解碼器。
avcodec_alloc_context3:為AVCodecContext分配內(nèi)存。
avcodec_open2:打開(kāi)解碼器。
avcodec_send_frame:將AVFrame非壓縮數(shù)據(jù)給編碼器。詳細(xì)介紹見(jiàn)FFmpeg音頻解碼的編解碼API介紹部分。
avcodec_receive_packet:獲取到編碼后的AVPacket數(shù)據(jù)。
av_frame_get_buffer: 為音頻或視頻數(shù)據(jù)分配新的buffer。在調(diào)用這個(gè)函數(shù)之前,必須在AVFame上設(shè)置好以下屬性:format(視頻為像素格式,音頻為樣本格式)、nb_samples(樣本個(gè)數(shù),針對(duì)音頻)、channel_layout(通道類(lèi)型,針對(duì)音頻)、width/height(寬高,針對(duì)視頻)。
av_frame_make_writable:確保AVFrame是可寫(xiě)的,盡可能避免數(shù)據(jù)的復(fù)制。
如果AVFrame不是是可寫(xiě)的,將分配新的buffer和復(fù)制數(shù)據(jù)。
鏈接:http://www.lxweimin.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 <output file>\n", argv[0]);
return 0;
}
filename = argv[1];
/* find the MP2 encoder */ //查找音頻編碼器
codec = avcodec_find_encoder(AV_CODEC_ID_MP2);
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
//分配 音頻編碼器上下文
c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "Could not allocate audio codec context\n");
exit(1);
}
/* put sample parameters */
c->bit_rate = 64000; //設(shè)置碼率64kb
/* check that the encoder supports s16 pcm input */
c->sample_fmt = AV_SAMPLE_FMT_S16; //采樣位數(shù) 16位
if (!check_sample_fmt(codec, c->sample_fmt)) {//看編碼器是否支持這個(gè)采樣位數(shù)
fprintf(stderr, "Encoder does not support sample format %s",
av_get_sample_fmt_name(c->sample_fmt));
exit(1);
}
/* select other audio parameters supported by the encoder */
c->sample_rate = select_sample_rate(codec);//找到最佳采樣率.向上靠攏
c->channel_layout = select_channel_layout(codec); //找到最高的channel_layout
//編碼器用戶(hù)設(shè)置channel_layout, 解碼器由文件里得到
c->channels = av_get_channel_layout_nb_channels(c->channel_layout); //找到最多的通道,根據(jù)channel_layout
/* open it */ //初始化編碼器上下文
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
//打開(kāi)文件
f = fopen(filename, "wb");
if (!f) {
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
}
//初始化packet
/* packet for holding encoded output */
pkt = av_packet_alloc();
if (!pkt) {
fprintf(stderr, "could not allocate the packet\n");
exit(1);
}
//初始化frame
/* frame containing input raw audio */
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate audio frame\n");
exit(1);
}
//用編碼器上下文 來(lái)更新frame. 因?yàn)榫幋a是把原始數(shù)據(jù)轉(zhuǎn)為壓縮數(shù)據(jù).所以這些參數(shù)都是有用戶(hù)指定的.
frame->nb_samples = c->frame_size;//幀大小
frame->format = c->sample_fmt;
frame->channel_layout = c->channel_layout;
/* allocate the data buffers */ //給frame里的buf 和bufsize 分配控件
ret = av_frame_get_buffer(frame, 0);
if (ret < 0) {
fprintf(stderr, "Could not allocate audio data buffers\n");
exit(1);
}
/* encode a single tone sound */
t = 0;
tincr = 2 * M_PI * 440.0 / c->sample_rate;//這個(gè)看不懂
for (i = 0; i < 200; i++) {
/* make sure the frame is writable -- makes a copy if the encoder
* kept a reference internally */
//確保frame 里的緩存是可寫(xiě)入的.如果不可寫(xiě)入.就分配新的緩存給frame.這是防止編碼器群對(duì)frame處理還沒(méi)問(wèn)問(wèn)你的,新數(shù)據(jù)又無(wú)法寫(xiě)入
ret = av_frame_make_writable(frame);
if (ret < 0)
exit(1);
//這一段看不懂,難道是隨機(jī)產(chǎn)生的音樂(lè)數(shù)據(jù)?
samples = (uint16_t *) frame->data[0];
for (j = 0; j < c->frame_size; j++) {
samples[2 * j] = (int) (sin(t) * 10000);
for (k = 1; k < c->channels; k++)
samples[2 * j + k] = samples[2 * j];
t += tincr;
}
//編碼數(shù)據(jù), 參數(shù)是編碼上下文.幀.packet.文件 .應(yīng)該是把幀數(shù)據(jù)編碼后得到packet.在寫(xiě)入問(wèn)就
encode(c, frame, pkt, f);
}
//刷新編碼器里最后的數(shù)據(jù),老操作
/* flush the encoder */
encode(c, NULL, pkt, f);
fclose(f);
av_frame_free(&frame);
av_packet_free(&pkt);
avcodec_free_context(&c);
return 0;
}