FFMPEG4.1 - 音頻編碼
PCM格式爲採樣率44100,單聲道,s16le音頻格式的pcm文件
編碼後,pcm轉換爲mp3格式文件
#include <memory>
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
}
// 申請智能指針變量
#define NEW_PTR(T, P, V, Fn) T *P = V; std::shared_ptr<T> P##P(P, [&P](T *){if(P != nullptr){Fn;}})
// 打印異常信息,並退出main函數
#define FATAL(M, ...) printf(M, ##__VA_ARGS__); return -1
// 自定義變量
const char *src_media = "D:/2.pcm";
const char *dst_media = "D:/2.mp3";
// PCM的原始參數
#define PCM_IN_FORMAT AV_SAMPLE_FMT_S16
#define PCM_IN_CHANNELS 1
#define PCM_IN_SAMPLE_RATE 44100
int main(int argc, char **argv) {
// 申請一個輸出的上下文
NEW_PTR(AVFormatContext, fmt_ctx, nullptr, avio_closep(&fmt_ctx->pb); avformat_close_input(&fmt_ctx));
avformat_alloc_output_context2(&fmt_ctx, nullptr, nullptr, dst_media);
if (fmt_ctx == nullptr) {
FATAL("alloc output format context failed.");
}
// 查詢編碼器
AVCodec *audio_enc;
if ((audio_enc = avcodec_find_encoder(fmt_ctx->oformat->audio_codec)) == nullptr) {
FATAL("find audio encoder failed.");
}
AVStream *audio_stream = avformat_new_stream(fmt_ctx, audio_enc);
audio_stream->id = fmt_ctx->nb_streams - 1;
// 爲編碼器申請上下文
NEW_PTR(AVCodecContext, audio_enc_ctx, nullptr, avcodec_free_context(&audio_enc_ctx));
if ((audio_enc_ctx = avcodec_alloc_context3(audio_enc)) == nullptr) {
FATAL("allocate audio enc context failed.");
}
// 爲編碼器配置編碼參數
audio_enc_ctx->sample_fmt = audio_enc->sample_fmts ? audio_enc->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
audio_enc_ctx->bit_rate = 64000;
audio_enc_ctx->sample_rate = 44100;
audio_enc_ctx->channel_layout = AV_CH_LAYOUT_STEREO;
audio_enc_ctx->channels = av_get_channel_layout_nb_channels(audio_enc_ctx->channel_layout);
audio_enc_ctx->time_base = AVRational{1, audio_enc_ctx->sample_rate};
audio_stream->time_base = audio_enc_ctx->time_base;
if (fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
audio_enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
// 打開編碼器
if (avcodec_open2(audio_enc_ctx, audio_enc, nullptr) < 0) {
FATAL("codec open failed.");
}
if (avcodec_parameters_from_context(audio_stream->codecpar, audio_enc_ctx) < 0) {
FATAL("copy params failed.");
}
if (avio_open(&fmt_ctx->pb, dst_media, AVIO_FLAG_WRITE) < 0) {
FATAL("open dst file failed.");
}
// 寫入頭信息
if (avformat_write_header(fmt_ctx, nullptr) < 0) {
FATAL("write header failed.");
}
// 申請一個音頻frame
NEW_PTR(AVFrame, audio_frame, nullptr, av_frame_free(&audio_frame));
if ((audio_frame = av_frame_alloc()) == nullptr) {
FATAL("allocate frame failed.");
}
audio_frame->format = audio_enc_ctx->sample_fmt;
audio_frame->channel_layout = audio_enc_ctx->channel_layout;
audio_frame->nb_samples = audio_enc_ctx->frame_size;
audio_frame->sample_rate = audio_enc_ctx->sample_rate;
if (av_frame_get_buffer(audio_frame, 0) < 0) {
FATAL("audio frame get buffer failed.");
}
// 創建一個frame,用來存儲從pcm讀取的數據
NEW_PTR(AVFrame, buf_frame, nullptr, av_frame_free(&buf_frame));
if ((buf_frame = av_frame_alloc()) == nullptr) {
FATAL("allocate buf frame failed.");
}
buf_frame->format = PCM_IN_FORMAT;
buf_frame->nb_samples = audio_frame->nb_samples;
buf_frame->channel_layout = (uint64_t) av_get_default_channel_layout(PCM_IN_CHANNELS);
buf_frame->sample_rate = PCM_IN_SAMPLE_RATE;
if (av_frame_get_buffer(buf_frame, 0) < 0) {
FATAL("create buf frame buffer failed.");
}
// 從pcm文件中讀取適應音頻幀的尺寸數據
auto readSize = av_samples_get_buffer_size(nullptr, buf_frame->channels, buf_frame->nb_samples,
(AVSampleFormat) buf_frame->format, 1);
NEW_PTR(uint8_t , buf, (uint8_t*)av_malloc((size_t)readSize), av_freep(&buf));
// 創建swr的上下文
NEW_PTR(SwrContext, swr_ctx, swr_alloc(), swr_free(&swr_ctx));
swr_alloc_set_opts(swr_ctx,
audio_frame->channel_layout,
(AVSampleFormat)audio_frame->format,
audio_frame->sample_rate,
av_get_default_channel_layout(PCM_IN_CHANNELS),
PCM_IN_FORMAT,
PCM_IN_SAMPLE_RATE,
0, nullptr);
swr_init(swr_ctx);
// 申請一個packet,並初始化
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = nullptr;
pkt.size = 0;
NEW_PTR(FILE, input, nullptr, fclose(input));
if ((input = fopen(src_media, "rb")) == nullptr) {
FATAL("no readable file.");
}
// 循環讀取frame數據
int samples_count = 0;
while (true) {
// 用來編碼的幀
AVFrame * encode_frame = nullptr;
if (fread(buf, 1, (size_t) readSize, input) < 0) {
FATAL("read input file failed.");
} else if (!feof(input)) {
// 文件沒有到結尾,則獲取編碼幀
avcodec_fill_audio_frame(buf_frame, buf_frame->channels, (AVSampleFormat)buf_frame->format, buf, readSize, 1);
swr_convert(swr_ctx, audio_frame->data, audio_frame->nb_samples, (const uint8_t**)buf_frame->data, buf_frame->nb_samples);
audio_frame->pts = av_rescale_q(samples_count, audio_stream->time_base, audio_enc_ctx->time_base);
samples_count += audio_frame->nb_samples;
encode_frame = audio_frame;
} else {
// 文件結束了,則發送一個空指針的frame,用來清空緩衝區
encode_frame = nullptr;
}
// 發送一個frame
if (avcodec_send_frame(audio_enc_ctx, encode_frame) < 0) {
FATAL("send frame exception.");
}
// 接受編碼完的內容
while (true) {
auto packet_ret = avcodec_receive_packet(audio_enc_ctx, &pkt);
// 判斷是否完全接受了packet
if (packet_ret == AVERROR(EAGAIN) || packet_ret == AVERROR_EOF) {
break;
}
// 檢查是否接受異常
if (packet_ret < 0) {
FATAL("receive packet exception.");
}
av_packet_rescale_ts(&pkt, audio_enc_ctx->time_base, audio_stream->time_base);
pkt.stream_index = audio_stream->index;
av_interleaved_write_frame(fmt_ctx, &pkt);
av_packet_unref(&pkt);
}
// 編碼幀爲空,則表示已經處理完所有的編碼,退出該循環
if (encode_frame == nullptr) break;
}
av_write_trailer(fmt_ctx);
return 0;
}