我从麦克风读取到的数据大小是88200(老师的视频里面是4096),我用av_dump_format函数查看格式如下,
Input #0, dshow, from ‘audio=@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{0D541114-F0DB-4B7E-BADF-7F045E1881F0}’:
Duration: N/A, bitrate: N/A
Stream #0:0: Audio: pcm_s16le, 44100 Hz, 2 channels, s16, 1411 kb
那我的样本数量=88200/2/2=22050? 当我用22050时,avcodec_send_frame函数会失败,返回-22(编码器已经打开),有如下输出信息nb_samples(22050)>frame_size(1024)。如果我使用1024而不用22050,那么会在memcpy时引发异常,原因是src_data太小放不大这88200字节的数据,然后修改src_data大小,让其分配多一些的内存,使其放得下88200字节,在swr_convert函数重采样转换时也会引发异常。
以下是我的源代码(用的是ffmpeg5.1版本的库)
#include <stdio.h>
#include <string.h>
#ifdef __cplusplus
extern “C”
{
#endif
#include “libavformat/avformat.h”
#include “libavutil/avutil.h”
#include “libavdevice/avdevice.h”
#include “libswresample/swresample.h”
#include “libavcodec/avcodec.h”
#include “libavutil/samplefmt.h”
#include “libavutil/channel_layout.h”
#include “libavutil/time.h”
#ifdef __cplusplus
}
#endif
int main()
{
// av_log_set_level(AV_LOG_DEBUG);
avdevice_register_all();
FILE *outFile = fopen("output.aac", "wb+");
if (outFile == NULL)
{
av_log(NULL, AV_LOG_ERROR, "Could not open output file.\n");
return -1;
}
const AVInputFormat *infmt = av_find_input_format("dshow");
char devName[1024] = "audio=";
AVDeviceInfoList *devs = NULL;
avdevice_list_input_sources(infmt, NULL, NULL, &devs);
if (devs == NULL)
{
av_log(NULL, AV_LOG_ERROR, "Could not find audio capture device.\n");
return -1;
}
for (int i = 0; i < devs->nb_devices; i++)
{
AVDeviceInfo *devInfo = devs->devices[i];
if (*(devInfo->media_types) == AVMEDIA_TYPE_AUDIO)
strcat(devName, devInfo->device_name);
}
AVFormatContext *fmtCtx = NULL;
int ret = avformat_open_input(&fmtCtx, devName, infmt, NULL);
if (ret != 0)
{
char err[1024];
av_strerror(ret, err, sizeof(err));
av_log(NULL, AV_LOG_ERROR, "Could not open input file: %s\n", err);
return -1;
}
const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
AVCodecContext *codecCtx = avcodec_alloc_context3(codec);
if (codecCtx == NULL)
{
av_log(NULL, AV_LOG_ERROR, "Could not allocate audio codec context.\n");
return -1;
}
codecCtx->bit_rate = 0;
codecCtx->profile = FF_PROFILE_AAC_HE_V2;
codecCtx->sample_fmt = AV_SAMPLE_FMT_FLTP;//avcodec_open2调用后会自动设置成FLTP格式,
codecCtx->sample_rate = 44100;
AVChannelLayout cl = AV_CHANNEL_LAYOUT_STEREO;
av_channel_layout_copy(&codecCtx->ch_layout, &cl);
if (avcodec_open2(codecCtx, codec, NULL) < 0)
{
char err[1024];
av_strerror(ret, err, sizeof(err));
av_log(NULL, AV_LOG_ERROR, "Could not open audio codec: %s\n", err);
return -1;
}
SwrContext *swrCtx = NULL;
swr_alloc_set_opts2(&swrCtx, &cl, AV_SAMPLE_FMT_S16, 44100,
&codecCtx->ch_layout, codecCtx->sample_fmt, codecCtx->sample_rate, 0, NULL);
if (swrCtx == NULL)
{
av_log(NULL, AV_LOG_ERROR, "Could not allocate SwrContext.\n");
return -1;
}
//该函数被标记为过时,不建议使用
// swrCtx = swr_alloc_set_opts(NULL, AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, 44100,
// codecCtx->ch_layout.u.mask, codecCtx->sample_fmt, codecCtx->sample_rate, 0, NULL);
// if (swrCtx == NULL)
// {
// av_log(NULL, AV_LOG_ERROR, "Could not allocate SwrContext.\n");
// return -1;
// }
if (swr_init(swrCtx) < 0)
{
av_log(NULL, AV_LOG_ERROR, "Could not initialize SwrContext.\n");
return -1;
}
AVFrame *frame = av_frame_alloc();
if (frame == NULL)
{
av_log(NULL, AV_LOG_ERROR, "Could not allocate audio frame.\n");
return -1;
}
frame->nb_samples = 22050;
frame->format = AV_SAMPLE_FMT_S16;
av_channel_layout_copy(&frame->ch_layout, &cl);
ret = av_frame_get_buffer(frame, 0);
if (ret != 0)
{
av_log(NULL, AV_LOG_ERROR, "Could not allocate audio buffer.\n");
return -1;
}
AVPacket *pkt = av_packet_alloc();
if (pkt == NULL)
{
av_log(NULL, AV_LOG_ERROR, "Could not allocate packet.\n");
return -1;
}
uint8_t **srcData = nullptr, **dstData = nullptr;
int srcLineSize = 0, dstLineSize = 0;
av_samples_alloc_array_and_samples(&srcData, &srcLineSize, 2, 22050, AV_SAMPLE_FMT_FLTP, 0);
av_samples_alloc_array_and_samples(&dstData, &dstLineSize, 2, av_rescale_rnd(22050, 44100, 44100, AV_ROUND_UP),
AV_SAMPLE_FMT_S16, 0);
int ct = 0;
while (ct++ < 10)
{
int ret = av_read_frame(fmtCtx, pkt);
if (ret < 0)
{
if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
{
av_usleep(10000);
continue;
}
break;
}
memcpy((void *)srcData[0], (const void *)pkt->data, pkt->size);
swr_convert(swrCtx, dstData, 22050, (const uint8_t **)srcData, 22050);
memcpy((void *)frame->data[0], (const void *)dstData[0], dstLineSize);
ret = avcodec_send_frame(codecCtx, frame);
while (ret >= 0)
{
ret = avcodec_receive_packet(codecCtx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
else if (ret < 0)
{
av_log(NULL, AV_LOG_ERROR,
"Error while sending a packet to the decoder.\n");
return -1;
}
fwrite(pkt->data, 1, pkt->size, outFile);
}
av_packet_unref(pkt);
}
// 清空编码器数据
ret = avcodec_send_frame(codecCtx, NULL);
while (ret >= 0)
{
ret = avcodec_receive_packet(codecCtx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
else if (ret < 0)
{
av_log(NULL, AV_LOG_ERROR,
"Error while sending a packet to the decoder.\n");
return -1;
}
fwrite(pkt->data, 1, pkt->size, outFile);
}
av_packet_unref(pkt);
fclose(outFile);
return 0;
}