[ffmpeg系列 05] 录音、声音直播(音频推流)

一  代码

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,声卡名称一般是中文,需要转换。

为什么要解码?

   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讲的。

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转换后的数据等。

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值