一 代码
ffmpeg版本5.1.2,dll是:ffmpeg-5.1.2-full_build-shared。x64的。
代码是windows端,用VS编译。
怎么使用这个代码?新建win32工程,复制这三个文件。设置ffmpeg库的include和lib目录。
/*
author: ashleycoder
CSDN blog: https://blog.csdn.net/chenquangobeijing
*/
#pragma once
extern "C"
{
#include "libswresample/swresample.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libavdevice/avdevice.h"
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "libavutil/time.h"
#include "libavutil/timestamp.h"
#include "libavutil/pixfmt.h"
#include "libavutil/imgutils.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/fifo.h"
};
#include <thread>
#include <mutex>
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avdevice.lib")
#pragma comment(lib, "swresample.lib")
class CAudioLive
{
public:
CAudioLive(void);
~CAudioLive(void);
public:
void Start();
void Ffmpeg_Init();
int RTMP_Init(const char* pOutFileName, const char* pFormat);
int Write_Header();
int SoundCard_Open();
int Audio_Decode_Encode_Init();
int Audio_Swr_Init();
int Audio_Frame_Init();
void AudioCapture_Thread_Fun();
void AudioEncode_Write_Thread();
void Close();
std::string dup_wchar_to_utf8(const wchar_t* wstr);
public:
AVFormatContext* m_pOutputFormatCtx = nullptr;
AVFormatContext* m_pInputAudioFormatCtx = nullptr;
//音频解码
const AVCodec* m_pAudioDecodeCodec = nullptr;
AVCodecContext* m_pAudioDecodeCodecCtx = nullptr;
//音频编码
const AVCodec* m_pAudioEncodeCodec = nullptr;
AVCodecContext* m_pAudioEncodeCodecCtx = nullptr;
AVStream* m_pAudioStream = nullptr;
int m_nAudioStream = -1;
AVCodecID m_AudioCodecID = AV_CODEC_ID_AAC;
SwrContext* m_pSwr = nullptr;
AVPacket* m_pAudioDecodePacket = nullptr;
public:
//解码用的,
AVFrame* m_pDecodeAudioFrame = nullptr;
//swr_convert, 转成fltp有用到
AVFrame* m_pAudioConvertFrame = nullptr;
uint8_t* m_pOutAudioBuffer[2];
//avcodec_send_frame, 编码AAC有用到,
AVFrame* m_pAudioEncodeFrame = nullptr;
//音频缓存队列
AVAudioFifo* m_pAudioFifo = nullptr;
std::mutex audio_mutex;
int m_nAudioFramesize = 0;
public:
AVSampleFormat audio_input_format = AV_SAMPLE_FMT_S16;
AVSampleFormat audio_encode_format = AV_SAMPLE_FMT_FLTP;
const int AUDIO_SAMPLING_FREQUENCY = 44100;
const uint64_t audio_channel_type = AV_CH_LAYOUT_STEREO;
const int audio_channel_num = 2;
const int frame_size = 1024; //AAC LOW 要求1024
std::thread m_AudioCaptureThread;
std::thread m_AudioWriteThread;
public:
FILE* m_pPCM = nullptr; //test use
FILE* m_pPCM2 = nullptr;
int m_naudio_count = 0;
public:
char av_error[AV_ERROR_MAX_STRING_SIZE] = { 0 };
#define av_err2str2(errnum) av_make_error_string(av_error, AV_ERROR_MAX_STRING_SIZE, errnum)
};
/*
author: ashleycoder
CSDN blog: https://blog.csdn.net/chenquangobeijing
*/
#include "AudioLive.h"
#include <functional>
#include <codecvt>
#include <locale>
#include <string>
#include <Windows.h>
CAudioLive::CAudioLive(void)
{
fopen_s(&m_pPCM, "audio_read.pcm", "wb");
fopen_s(&m_pPCM2, "audio_f32le.pcm", "wb");
}
CAudioLive::~CAudioLive(void)
{
Close();
}
void CAudioLive::Start()
{
Ffmpeg_Init();
const char* pOutFileName = "output.aac";
const char* pFormat = nullptr;
//const char* pOutFileName = "rtmp://127.0.0.1/live/now";
//const char* pFormat = "flv";
RTMP_Init(pOutFileName, pFormat);
SoundCard_Open();
Audio_Decode_Encode_Init();
Audio_Swr_Init();
Audio_Frame_Init();
Write_Header();
m_AudioCaptureThread = std::thread(std::bind(&CAudioLive::AudioCapture_Thread_Fun, this));
//m_AudioCaptureThread.join();
//不能join, 线程死循环, 阻止了创建后面的线程
m_AudioWriteThread = std::thread(std::bind(&CAudioLive::AudioEncode_Write_Thread, this));
m_AudioWriteThread.join();
}
std::string CAudioLive::dup_wchar_to_utf8(const wchar_t* wstr)
{
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
return converter.to_bytes(wstr);
}
int CAudioLive::SoundCard_Open()
{
const AVInputFormat* pInputFormat = av_find_input_format("dshow");
std::string strAudioName = dup_wchar_to_utf8(L"audio=麦克风阵列 (英特尔® 智音技术)");
//采样频率设置成48000,返回-5
AVDictionary* options = NULL;
av_dict_set(&options, "sample_rate", "44100", 0);
av_dict_set(&options, "sample_fmt", "s16", 0);
av_dict_set(&options, "ac", "2", 0);
int nRet = avformat_open_input(&m_pInputAudioFormatCtx, strAudioName.c_str(), pInputFormat, &options);
if (nRet != 0)
{
char* err_str = av_err2str2(nRet);
printf("Error: %s\n", err_str);
return -1;
}
return 0;
}
int CAudioLive::Audio_Decode_Encode_Init()
{
int nRet = -1;
if (avformat_find_stream_info(m_pInputAudioFormatCtx, nullptr) < 0)
{
return -1;
}
//这两个函数等价
m_nAudioStream = av_find_best_stream(m_pInputAudioFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
printf("m_nAudioStream=%d\n", m_nAudioStream);
/*int i = 0;
for (; i < m_pInputAudioFormatCtx->nb_streams; ++i)
{
if (AVMEDIA_TYPE_AUDIO == m_pInputAudioFormatCtx->streams[i]->codec->codec_type)
{
m_nAudioStream = i;
break;
}
}
if (m_nAudioStream == -1)
{
return FALSE;
}*/
m_pAudioDecodeCodecCtx = avcodec_alloc_context3(nullptr);
nRet = avcodec_parameters_to_context(m_pAudioDecodeCodecCtx, \
m_pInputAudioFormatCtx->streams[m_nAudioStream]->codecpar);
m_pAudioDecodeCodecCtx->frame_size = frame_size;
//m_pAudioDecodeCodecCtx->codec_id=AV_CODEC_ID_FIRST_AUDIO
m_pAudioDecodeCodec = avcodec_find_decoder(m_pAudioDecodeCodecCtx->codec_id);
if (m_pAudioDecodeCodec == nullptr)
{
printf("Can not find audio decoder! \n");
return -1;
}
nRet = avcodec_open2(m_pAudioDecodeCodecCtx, m_pAudioDecodeCodec, nullptr);
if (nRet < 0)
{
printf("Can not open audio decoder! \n");
return -1;
}
//官方编译的dll,不支持libfdk_aac
//libfdk_aac支持编码s16
//m_pAudioEncodeCodec = avcodec_find_encoder_by_name("libfdk_aac");
m_pAudioEncodeCodec = avcodec_find_encoder(m_AudioCodecID);
if (!m_pAudioEncodeCodec)
{
printf("Can not find audio encoder! \n");
return -1;
}
AVChannelLayout layout = { 0 };
layout.order = AV_CHANNEL_ORDER_NATIVE;
layout.nb_channels = audio_channel_num;
layout.u.mask = (1 << AV_CHAN_FRONT_LEFT) | (1 << AV_CHAN_FRONT_RIGHT);
//layout.u.mask = AV_CHANNEL_LAYOUT_STEREO;
m_pAudioEncodeCodecCtx = avcodec_alloc_context3(m_pAudioEncodeCodec);
m_pAudioEncodeCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
m_pAudioEncodeCodecCtx->sample_fmt = audio_encode_format;
m_pAudioEncodeCodecCtx->sample_rate = AUDIO_SAMPLING_FREQUENCY;
m_pAudioEncodeCodecCtx->ch_layout = layout;
m_pAudioEncodeCodecCtx->ch_layout.nb_channels = audio_channel_num;
m_pAudioEncodeCodecCtx->bit_rate = 128000;
m_pAudioEncodeCodecCtx->profile = FF_PROFILE_AAC_LOW;
nRet = avcodec_open2(m_pAudioEncodeCodecCtx, m_pAudioEncodeCodec, nullptr);
if(nRet < 0)
{
printf("open audio encoder fail ! \n");
return -1;
}
m_pAudioStream = avformat_new_stream(m_pOutputFormatCtx, m_pAudioEncodeCodec);
nRet = avcodec_parameters_from_context(m_pAudioStream->codecpar, m_pAudioEncodeCodecCtx);
m_pAudioFifo = av_audio_fifo_alloc(m_pAudioDecodeCodecCtx->sample_fmt, \
audio_channel_num, 1000 * m_pAudioDecodeCodecCtx->frame_size);
return 0;
}
int CAudioLive::Audio_Swr_Init()
{
//声道、采样频率不变, 格式变了
m_pSwr = swr_alloc();
av_opt_set_int(m_pSwr, "in_channel_layout", audio_channel_type, 0);
av_opt_set_int(m_pSwr, "out_channel_layout", audio_channel_type, 0);
av_opt_set_int(m_pSwr, "in_sample_rate", AUDIO_SAMPLING_FREQUENCY, 0);
av_opt_set_int(m_pSwr, "out_sample_rate", AUDIO_SAMPLING_FREQUENCY, 0);
av_opt_set_sample_fmt(m_pSwr, "in_sample_fmt", audio_input_format, 0);
av_opt_set_sample_fmt(m_pSwr, "out_sample_fmt", audio_encode_format, 0);
swr_init(m_pSwr);
return 0;
}
int CAudioLive::Audio_Frame_Init()
{
int nRet = -1;
//4个属性都需要设置
m_pAudioConvertFrame = av_frame_alloc();
m_pAudioConvertFrame->format = audio_input_format; //s16
m_pAudioConvertFrame->sample_rate = AUDIO_SAMPLING_FREQUENCY;
m_pAudioConvertFrame->nb_samples = m_pAudioEncodeCodecCtx->frame_size;
m_pAudioConvertFrame->ch_layout.nb_channels = audio_channel_type;
nRet = av_frame_get_buffer(m_pAudioConvertFrame, 0); //分配内存
m_pAudioEncodeFrame = av_frame_alloc();
m_pAudioEncodeFrame->sample_rate = AUDIO_SAMPLING_FREQUENCY;
m_pAudioEncodeFrame->nb_samples = m_pAudioEncodeCodecCtx->frame_size;
m_pAudioEncodeFrame->format = m_pAudioEncodeCodecCtx->sample_fmt;//fltp
m_pAudioEncodeFrame->ch_layout.nb_channels = audio_channel_type;
nRet = av_frame_get_buffer(m_pAudioEncodeFrame, 0);
char* err_str = av_err2str2(nRet);
printf("av_frame_get_buffer fail: %s\n", err_str);
//1帧音频大小,2声道的:1024*4*2
m_nAudioFramesize = av_samples_get_buffer_size(nullptr, audio_channel_num, m_pAudioEncodeCodecCtx->frame_size,
m_pAudioEncodeCodecCtx->sample_fmt, 0);
printf("frame_size=%d, size=%d\n", m_pAudioEncodeCodecCtx->frame_size, m_nAudioFramesize);
m_pOutAudioBuffer[0] = (uint8_t*)malloc(m_nAudioFramesize);
m_pOutAudioBuffer[1] = (uint8_t*)malloc(m_nAudioFramesize);
return 1;
}
int CAudioLive::Write_Header()
{
int nRet = avformat_write_header(m_pOutputFormatCtx, nullptr);
if (nRet < 0)
{
char* err_str = av_err2str2(nRet);
printf("avformat_write_header fail: %s\n", err_str);
return -1;
}
return 1;
}
void CAudioLive::Ffmpeg_Init()
{
avdevice_register_all();
avformat_network_init();
av_log_set_level(AV_LOG_FATAL);
}
int CAudioLive::RTMP_Init(const char* pOutFileName, const char* pFormat)
{
avformat_alloc_output_context2(&m_pOutputFormatCtx, nullptr, pFormat, pOutFileName);
int nRet = -1;
if(!(m_pOutputFormatCtx->oformat->flags & AVFMT_NOFILE))
{
nRet = avio_open(&m_pOutputFormatCtx->pb, pOutFileName, AVIO_FLAG_WRITE);
if (nRet < 0)
{
char* err_str = av_err2str2(nRet);
printf("avio_open fail\n");
return -1;
}
}
return 0;
}
void CAudioLive::AudioCapture_Thread_Fun()
{
int nRet = -1;
int i = 0, count = 0;
m_pAudioDecodePacket = av_packet_alloc();
while (true)
{
nRet = av_read_frame(m_pInputAudioFormatCtx, m_pAudioDecodePacket);
if (nRet == AVERROR(EAGAIN) || nRet == AVERROR_EOF) {
av_packet_unref(m_pAudioDecodePacket);
printf("read_frame break");
break;
}
//m_pAudioDecodePacket->size=88200=44100*2=1秒的音频数据量
if (m_pAudioDecodePacket->stream_index == m_nAudioStream)
{
++m_naudio_count;
//printf("enter push audio count=%d\r\n", m_naudio_count);
//fwrite(m_pAudioDecodePacket->data, 1, m_pAudioDecodePacket->size, m_pPCM);
m_pDecodeAudioFrame = av_frame_alloc();
//pFrameTemp->format = audio_input_format;
//pFrameTemp->sample_rate = AUDIO_SAMPLING_FREQUENCY;
//pFrameTemp->channel_layout = audio_channel_type;// AV_CH_LAYOUT_MONO;
//pFrameTemp->ch_layout.nb_channels = audio_channel_num;
//解码
int audio_send_pkt_ret = avcodec_send_packet(m_pAudioDecodeCodecCtx, m_pAudioDecodePacket);
int audio_receive_frame_ret = avcodec_receive_frame(m_pAudioDecodeCodecCtx, m_pDecodeAudioFrame);
if (audio_receive_frame_ret < 0)
continue;
//m_pDecodeAudioFrame->data[0]地址有值,data[1]==0x0
//m_pDecodeAudioFrame->nb_samples = 22050, time_base=0/1
//解码后的数据,也可以用系统音频库函数替代
int space = av_audio_fifo_space(m_pAudioFifo);
//printf("space=%d, nb_samples=%d\r\n", space, pFrameTemp->nb_samples);
if (av_audio_fifo_space(m_pAudioFifo) > m_pDecodeAudioFrame->nb_samples * 2)
{
audio_mutex.lock();
int write_len = av_audio_fifo_write(m_pAudioFifo, \
(void**)&(m_pDecodeAudioFrame->data[0]), m_pDecodeAudioFrame->nb_samples);
audio_mutex.unlock();
}
av_frame_free(&m_pDecodeAudioFrame);
}
av_packet_unref(m_pAudioDecodePacket);
}
}
void CAudioLive::AudioEncode_Write_Thread()
{
int i = 0;
AVPacket* audio_pkt = av_packet_alloc();
int nAudioFrameCount = 0;
while (true)
{
//av_audio_fifo_size返回值是个数, 不是字节数
if (av_audio_fifo_size(m_pAudioFifo) >= m_pAudioStream->codecpar->frame_size *2)
{
audio_mutex.lock();
int read_size = av_audio_fifo_read(m_pAudioFifo, \(void**)m_pAudioConvertFrame->data, m_pAudioConvertFrame->nb_samples);
audio_mutex.unlock();
printf("audio_fifo_read size:%d, i=%d\r\n", read_size, i);
int count = swr_convert(m_pSwr, m_pOutAudioBuffer, m_pAudioConvertFrame->nb_samples,
(const uint8_t**)&(m_pAudioConvertFrame->data), m_pAudioConvertFrame->nb_samples);
printf("m_pAudioConvertFrame->nb_samples:%d\r\n", m_pAudioConvertFrame->nb_samples);
//fwrite(m_pOutAudioBuffer[0], 1, 1024 * 4, m_pPCM2);
m_pAudioEncodeFrame->data[0] = m_pOutAudioBuffer[0];
m_pAudioEncodeFrame->data[1] = m_pOutAudioBuffer[1];
//pts递增nb_samples
m_pAudioEncodeFrame->pts = nAudioFrameCount*m_pAudioEncodeFrame->nb_samples;
++nAudioFrameCount;
int audio_send_frame_ret = avcodec_send_frame(m_pAudioEncodeCodecCtx, m_pAudioEncodeFrame);
if (audio_send_frame_ret == 0) {
int audio_receive_pkt_ret = avcodec_receive_packet(m_pAudioEncodeCodecCtx, audio_pkt);
char* err_str = av_err2str2(audio_receive_pkt_ret);
printf("audio receive_packet_ret:%d, i=%d, pkt.size=%d\r\n", audio_receive_pkt_ret, i++, audio_pkt->size);
if (audio_pkt->size > 0) {
//cur_pts_a = audio_pkt->pts;
audio_pkt->stream_index = m_pAudioStream->index;
av_packet_rescale_ts(audio_pkt, m_pAudioEncodeCodecCtx->time_base, m_pAudioStream->time_base);
int write_ret = av_interleaved_write_frame(m_pOutputFormatCtx, audio_pkt);
printf("audio write_frame:%d\r\n", write_ret);
}
av_packet_unref(audio_pkt);
}
}
}
av_write_trailer(m_pOutputFormatCtx);
}
void CAudioLive::Close()
{
av_write_trailer(m_pOutputFormatCtx);
avformat_free_context(m_pInputAudioFormatCtx);
avformat_free_context(m_pOutputFormatCtx);
avcodec_close(m_pAudioDecodeCodecCtx);
avcodec_close(m_pAudioEncodeCodecCtx);
av_frame_free(&m_pDecodeAudioFrame);
av_frame_free(&m_pAudioEncodeFrame);
av_frame_free(&m_pAudioConvertFrame);
av_packet_free(&m_pAudioDecodePacket);
free(m_pOutAudioBuffer[0]);
free(m_pOutAudioBuffer[1]);
}
#include <iostream>
#include <Windows.h>
#include "3__AudioLive/AudioLive.h"
int main()
{
CAudioLive* m_pAudioLive = new CAudioLive();
m_pAudioLive->Start();
return 0;
}
二 demo需要注意的点
1 如何获取声卡名称?使用系列01讲的命令行。
采集音频数据用的dshow,声卡名称一般是中文,需要转换。
2 为什么要解码?
av_read_frame(AVFormatContext *s, AVPacket *pkt);
编码是AVFrame。
3 swr_convert转换前的音频数据:可以用不同操作系统的音频库函数替代。
注意取多少字节数,音频帧字节数即1024*2*2。
4 swr_convert作用:改声道数目、改采样频率--重采样, 改格式--S16这些。
这些也可以使用filter功能使用。
5 编码AAC,帧长度要求是1024,编码前的格式是FLTP。
S16、FLTP格式区别,可以参考系列 02讲的。
6 AVAudioFifo使用:是采样点的个数,< 不是字节数。
Operates at the sample level rather than the byte level. (源码写的注释)
int nb_samples; //number of audio samples (per channel) described by this frame
7 用VLC看aac文件,位每采样是32。fltp是float,4字节。
s16,位每采样是16。short,2字节。
8 AAC有头,直接写编码后的数据,播不了。
9 如果编码出的AAC文件播放不正常,定位那些地方?
答:av_read_frame、解码后的、swr_convert转换后的数据等。