开发环境
操作系统:win10
IDE:VS2019
第三方库:ffmpeg5
代码
/*
* Copyright (c) 2001 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* @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 <libavformat/avformat.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/frame.h>
#include <libavutil/samplefmt.h>
#include <libswresample/swresample.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;
}
/* just pick the highest supported samplerate */
static int select_sample_rate(const AVCodec* codec)
{
const int* p;
int best_samplerate = 0;
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, AVChannelLayout* dst)
{
const AVChannelLayout* p, * best_ch_layout = NULL;
int best_nb_channels = 0;
if (!codec->ch_layouts)
return av_channel_layout_copy(dst, &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO);
p = codec->ch_layouts;
while (p->nb_channels) {
int nb_channels = p->nb_channels;
if (nb_channels > best_nb_channels) {
best_ch_layout = p;
best_nb_channels = nb_channels;
}
p++;
}
return av_channel_layout_copy(dst, best_ch_layout);
}
static void encode(AVCodecContext* ctx, AVFrame* frame, AVPacket* pkt,
AVFormatContext* avFormatContext)
{
int ret;
/* 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) {
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);
}
//fwrite(pkt->data, 1, pkt->size, output);
// 指定是音频流。 1为视频流
pkt->stream_index = 0;
// 解码时间、显示时间置为0
pkt->dts = 0;
pkt->pts = 0;
// 编码后的写入ac文件 1.输出上下文
av_interleaved_write_frame(avFormatContext, pkt);
av_packet_unref(pkt);
}
}
int main(int argc, char** argv)
{
const char* filename;
const AVCodec* codec;
AVCodecContext* c = NULL;
AVFrame* frame;
AVFrame* aacFrame;
AVPacket* pkt;
int i, j, k, ret;
FILE* f;
uint16_t* samples;
float t, tincr;
struct SwrContext* swr_ctx;
//if (argc <= 1) {
// fprintf(stderr, "Usage: %s <output file>\n", argv[0]);
// return 0;
//}
const char* inFileName = "../resources/output.pcm";
filename = "output.aac";
FILE* inFile;
fopen_s(&inFile, inFileName, "rb");
if (!inFile) {
fprintf(stderr, "Could not open %s\n", inFileName);
exit(1);
}
/* find the AAC encoder */
codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
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;
/* check that the encoder supports s16 pcm input */
c->sample_fmt = AV_SAMPLE_FMT_FLTP;
if (!check_sample_fmt(codec, c->sample_fmt)) {
fprintf(stderr, "Encoder does not support sample format %s",
av_get_sample_fmt_name(c->sample_fmt));
exit(1);
}
c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;//音频帧设置头部
c->channels = 2;
/* select other audio parameters supported by the encoder */
c->sample_rate = select_sample_rate(codec);
ret = select_channel_layout(codec, &c->ch_layout);
if (ret < 0)
exit(1);
c->channel_layout = AV_CH_LAYOUT_STEREO;
/* create resampler context */
swr_ctx = swr_alloc();
if (!swr_ctx) {
fprintf(stderr, "Could not allocate resampler context\n");
ret = AVERROR(ENOMEM);
exit(1);
}
///* set options */
av_opt_set_int(swr_ctx, "in_channel_layout", AV_CH_LAYOUT_STEREO, 0);
av_opt_set_int(swr_ctx, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0);
av_opt_set_int(swr_ctx, "in_sample_rate", c->sample_rate, 0);
av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
av_opt_set_int(swr_ctx, "out_sample_rate", c->sample_rate, 0);
av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
/* initialize the resampling context */
if ((ret = swr_init(swr_ctx)) < 0) {
fprintf(stderr, "Failed to initialize the resampling context\n");
exit(1);
}
/* open it */
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
// 创建输出的上下文
AVFormatContext* avFormatContext = NULL;
// 初始化
avformat_alloc_output_context2(&avFormatContext, NULL, NULL, filename);
if (!avFormatContext) {
return -1;
}
// 创建音频流
AVStream* st = avformat_new_stream(avFormatContext, NULL);
// 音频流参数设置
st->codecpar->codec_tag = 0;
// 编码器参数复制到音频流上,省得再设置一遍
avcodec_parameters_from_context(st->codecpar, c);
// 第三个参数,0音频、1视频
av_dump_format(avFormatContext, 0, filename, 1);
// 打开输出文件
ret = avio_open(&avFormatContext->pb, filename, AVIO_FLAG_WRITE);
if (ret < 0) {
return -1;
}
// 写头部信息,创建输出文件
avformat_write_header(avFormatContext, NULL);
/* packet for holding encoded output */
pkt = av_packet_alloc();
if (!pkt) {
fprintf(stderr, "could not allocate the packet\n");
exit(1);
}
/* frame containing input raw audio */
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate audio frame\n");
exit(1);
}
frame->nb_samples = c->frame_size;
frame->format = c->sample_fmt;
ret = av_channel_layout_copy(&frame->ch_layout, &c->ch_layout);
if (ret < 0)
exit(1);
/* allocate the data buffers */
ret = av_frame_get_buffer(frame, 0);
if (ret < 0) {
fprintf(stderr, "Could not allocate audio data buffers\n");
exit(1);
}
/* frame containing input raw audio */
aacFrame = av_frame_alloc();
if (!aacFrame) {
fprintf(stderr, "Could not allocate audio frame\n");
exit(1);
}
aacFrame->nb_samples = c->frame_size;
aacFrame->format = AV_SAMPLE_FMT_FLTP;
ret = av_channel_layout_copy(&aacFrame->ch_layout, &c->ch_layout);
if (ret < 0)
exit(1);
/* allocate the data buffers */
ret = av_frame_get_buffer(aacFrame, 0);
if (ret < 0) {
fprintf(stderr, "Could not allocate audio data buffers\n");
exit(1);
}
// 准备缓存空间存放数据
int readSize = frame->nb_samples * 2 * 2; //双声道,float
char* pcms = (char*)malloc(readSize);
int sum = 0;
int index = 0;
for (;;) {
int len = fread(pcms, 1, readSize, inFile);
if (len <= 0) {
// 告诉解码器没有帧了
avcodec_send_frame(c, NULL);
while (avcodec_receive_packet(c, pkt) != AVERROR_EOF);
break;
}
else {
index++;
sum += len;
// 强制数据类型转换,重采样之前的数据
const uint8_t* data[1];
data[0] = (uint8_t*)pcms;
// 重采样 1.重采样上下文 2.重采样后的数据 3.样本数量不变 4.重采样之前的数据
len = swr_convert(swr_ctx, frame->data, frame->nb_samples, data, frame->nb_samples);
if (len <= 0) {
break;
}
encode(c, frame, pkt, avFormatContext);
}
}
av_packet_free(&pkt);
free(pcms);
pcms = NULL;
// 写入索引
av_write_trailer(avFormatContext);
fclose(inFile);
avio_close(avFormatContext->pb);
avcodec_close(c);
swr_free(&swr_ctx);
av_frame_free(&frame);
av_frame_free(&aacFrame);
av_packet_free(&pkt);
avcodec_free_context(&c);
avformat_free_context(avFormatContext);
return 0;
}
注意
aac的编码器的音频数据格式(sample_fmt)需要设置为AV_SAMPLE_FMT_FLTP,而原始的pcm的sample_fmt是AV_SAMPLE_FMT_S16,因此需要做重采样。重采样使用libswresample库。