windows下获取系统需要安装软件:Setup Screen Capturer Recorder
安装之后使用ffmpeg命令查看设备可以发现
ffmpeg -list_devices true -f dshow -i dummy
注意我们在写代码获取音频设备的时候,我暂时的方法找不到该设备名,因此暂时是写死的。
bool CCameraRecord::InitAudioSysInput()
{
dzlog_info("%s(%d) Start", __FUNCTION__, __LINE__);
string strAudioDeviceName = "virtual-audio-capturer";
if (strAudioDeviceName == "")
{
dzlog_info("%s(%d) m_strAudioDeviceName Fail", __FUNCTION__, __LINE__);
return false;
}
//m_bAudioInit = false;
AVDictionary *device_param = 0;
char szDeviceName[1024] = { 0 };
sprintf_s(szDeviceName, 5096, "audio=%s", strAudioDeviceName.c_str());
string device_name_utf8 = AnsiToUTF8(szDeviceName, strlen(szDeviceName));
dzlog_info("%s(%d) m_strAudioDeviceName=%s", __FUNCTION__, __LINE__, strAudioDeviceName.c_str());
dzlog_info("%s(%d) szDeviceName=%s", __FUNCTION__, __LINE__, szDeviceName);
dzlog_info("%s(%d) device_name_utf8=%s", __FUNCTION__, __LINE__, device_name_utf8.c_str());
if (avformat_open_input(&m_pAudSysFmtCtx, device_name_utf8.c_str(), m_pInputFormat, &device_param) != 0)
{
dzlog_info("%s(%d) avformat_open_input Fail", __FUNCTION__, __LINE__);
return false;
}
if (avformat_find_stream_info(m_pAudSysFmtCtx, NULL) < 0)
{
dzlog_info("%s(%d) avformat_find_stream_info Fail", __FUNCTION__, __LINE__);
return false;
}
int m_audioindex = -1;
for (int i = 0; i < m_pAudSysFmtCtx->nb_streams; i++)
{
if (m_pAudSysFmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{
m_audioindex = i;
break;
}
}
if (m_audioindex == -1)
{
dzlog_info("%s(%d) m_audioindex == -1", __FUNCTION__, __LINE__);
return false;
}
if (avcodec_open2(m_pAudSysFmtCtx->streams[m_audioindex]->codec, avcodec_find_decoder(m_pAudSysFmtCtx->streams[m_audioindex]->codec->codec_id), NULL) < 0)
{
dzlog_info("%s(%d) avcodec_open2 Fail", __FUNCTION__, __LINE__);
return false;
}
m_nSample_rateSys = m_pAudSysFmtCtx->streams[m_audioindex]->codec->sample_rate;
m_nChannelsSys = m_pAudSysFmtCtx->streams[m_audioindex]->codec->channels;
dzlog_info("%s(%d) End", __FUNCTION__, __LINE__);
m_bAudioSysInit = true;
return m_bAudioSysInit;
}
void CCameraRecord::HandleReadAudioSysPacket()
{
int ret;
int encode_audio = 1;
int dec_got_frame_a = 0;
dzlog_info("%s(%d) Start", __FUNCTION__, __LINE__);
while (encode_audio)
{
if (m_bAudioExit)
break;
AVFrame *input_frame = av_frame_alloc();
AVFrame *output_frame;
if (!input_frame)
{
ret = AVERROR(ENOMEM);
dzlog_info("%s(%d) av_frame_alloc Fail", __FUNCTION__, __LINE__);
return;
}
AVPacket input_packet;
av_init_packet(&input_packet);
input_packet.data = NULL;
input_packet.size = 0;
if ((ret = av_read_frame(m_pAudSysFmtCtx, &input_packet)) < 0)
{
if (ret == AVERROR_EOF)
encode_audio = 0;
else
{
dzlog_info("%s(%d) av_read_frame Fail", __FUNCTION__, __LINE__);
return;
}
}
if ((ret = avcodec_decode_audio4(m_pAudSysFmtCtx->streams[m_audioindex]->codec, input_frame, &dec_got_frame_a, &input_packet)) < 0)
{
dzlog_info("%s(%d) avcodec_decode_audio4 Fail", __FUNCTION__, __LINE__);
return;
}
av_packet_unref(&input_packet);
if (dec_got_frame_a)
{
//m_queAudioFrame.push(input_frame);
//if (m_bIsRecord && m_bflagAudioSys)
if (m_bIsRecord)
{
//AudioConvert(input_frame, AV_SAMPLE_FMT_S16, m_nChannels, m_nSample_rate, &output_frame);
//这就是将音频帧送往混音filter的步骤
if (av_buffersrc_add_frame_flags(buffersrc_ctx2, input_frame, 0) < 0) {
av_log(NULL, AV_LOG_ERROR, "Error while feeding the audio filtergraph\n");
continue;
}
//write_audio_frame(m_pAudSysFmtCtx->streams[m_audioindex], output_frame, av_gettime() - m_start_time);
OutputDebugStringA("m_pAudSysFmtCtx :\n ");
}
}
if(input_frame != NULL)
av_frame_free(&input_frame);
//av_frame_free(&output_frame);
}
dzlog_info("%s(%d) End", __FUNCTION__, __LINE__);
return;
}
//混音过滤器
char *filter_descr = "[in0][in1]amix=inputs=2[out]";
//混音过滤器初始化
int CCameraRecord::init_filters(const char *filters_descr)
{
char args1[512];
char args2[512];
int ret = 0;
const AVFilter *abuffersrc1 = avfilter_get_by_name("abuffer");
const AVFilter *abuffersrc2 = avfilter_get_by_name("abuffer");
const AVFilter *abuffersink = avfilter_get_by_name("abuffersink");
AVFilterInOut *outputs1 = avfilter_inout_alloc();
AVFilterInOut *outputs2 = avfilter_inout_alloc();
AVFilterInOut *inputs = avfilter_inout_alloc();
//输出格式
static const enum AVSampleFormat out_sample_fmts[] = { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE };
static const int64_t out_channel_layouts[] = { AV_CH_LAYOUT_STEREO, -1 };
static const int out_sample_rates[] = { 44100, -1 };
const AVFilterLink *outlink;
AVRational time_base_1 = m_pAudFmtCtx->streams[m_audioindex]->time_base;//第一路音频时间戳
AVRational time_base_2 = m_pAudSysFmtCtx->streams[m_audioindex]->time_base;//第二路音频时间戳
filter_graph = avfilter_graph_alloc();
if (!outputs1 || !inputs || !filter_graph) {
ret = AVERROR(ENOMEM);
goto end;
}
//根据第一路音频相关参数创建filter
/* buffer audio source: the decoded frames from the decoder will be inserted here. */
if (!m_pAudFmtCtx->streams[m_audioindex]->codec->channel_layout)
m_pAudFmtCtx->streams[m_audioindex]->codec->channel_layout = av_get_default_channel_layout(m_pAudFmtCtx->streams[m_audioindex]->codec->channels);
snprintf(args1, sizeof(args1),
"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%d",
time_base_1.num, time_base_1.den, m_pAudFmtCtx->streams[m_audioindex]->codec->sample_rate,
av_get_sample_fmt_name(m_pAudFmtCtx->streams[m_audioindex]->codec->sample_fmt), m_pAudFmtCtx->streams[m_audioindex]->codec->channel_layout);
ret = avfilter_graph_create_filter(&buffersrc_ctx1, abuffersrc1, "in1",
args1, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source\n");
goto end;
}
//根据第二路音频相关参数创建filter
//#if (ENABLE_FILTERS)
/* buffer audio source: the decoded frames from the decoder will be inserted here. */
if (!m_pAudSysFmtCtx->streams[m_audioindex]->codec->channel_layout)
m_pAudSysFmtCtx->streams[m_audioindex]->codec->channel_layout = av_get_default_channel_layout(m_pAudSysFmtCtx->streams[m_audioindex]->codec->channels);
snprintf(args2, sizeof(args2),
"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%d",
time_base_2.num, time_base_2.den, m_pAudSysFmtCtx->streams[m_audioindex]->codec->sample_rate,
av_get_sample_fmt_name(m_pAudSysFmtCtx->streams[m_audioindex]->codec->sample_fmt), m_pAudSysFmtCtx->streams[m_audioindex]->codec->channel_layout);
ret = avfilter_graph_create_filter(&buffersrc_ctx2, abuffersrc1, "in2",
args2, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source\n");
goto end;
}
//#endif
//创建输出filter 及filter的参数设置
/* buffer audio sink: to terminate the filter chain. */
ret = avfilter_graph_create_filter(&buffersink_ctxAudio, abuffersink, "out",
NULL, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer sink\n");
goto end;
}
ret = av_opt_set_int_list(buffersink_ctxAudio, "sample_fmts", out_sample_fmts, -1,
AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot set output sample format\n");
goto end;
}
ret = av_opt_set_int_list(buffersink_ctxAudio, "channel_layouts", out_channel_layouts, -1,
AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot set output channel layout\n");
goto end;
}
ret = av_opt_set_int_list(buffersink_ctxAudio, "sample_rates", out_sample_rates, -1,
AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot set output sample rate\n");
goto end;
}
/*
* Set the endpoints for the filter graph. The filter_graph will
* be linked to the graph described by filters_descr.
*/
/*
* The buffer source output must be connected to the input pad of
* the first filter described by filters_descr; since the first
* filter input label is not specified, it is set to "in" by
* default.
*/
outputs1->name = av_strdup("in0");
outputs1->filter_ctx = buffersrc_ctx1;
outputs1->pad_idx = 0;
//#if (ENABLE_FILTERS)
outputs1->next = outputs2;
outputs2->name = av_strdup("in1");
outputs2->filter_ctx = buffersrc_ctx2;
outputs2->pad_idx = 0;
outputs2->next = NULL;
//#else
//outputs1->next = NULL;
//#endif
/*
* The buffer sink input must be connected to the output pad of
* the last filter described by filters_descr; since the last
* filter output label is not specified, it is set to "out" by
* default.
*/
inputs->name = av_strdup("out");
inputs->filter_ctx = buffersink_ctxAudio;
inputs->pad_idx = 0;
inputs->next = NULL;
AVFilterInOut* filter_outputs[2];
filter_outputs[0] = outputs1;
//#if (ENABLE_FILTERS)
filter_outputs[1] = outputs2;
//#endif
if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr,
&inputs, &outputs1, NULL)) < 0)//filter_outputs
{
av_log(NULL, AV_LOG_ERROR, "parse ptr fail, ret: %d\n", ret);
goto end;
}
if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
{
av_log(NULL, AV_LOG_ERROR, "config graph fail, ret: %d\n", ret);
goto end;
}
/* Print summary of the sink buffer
* Note: args buffer is reused to store channel layout string */
outlink = buffersink_ctxAudio->inputs[0];
av_get_channel_layout_string(args1, sizeof(args1), -1, outlink->channel_layout);
/*av_log(NULL, AV_LOG_INFO, "Output: srate:%dHz fmt:%s chlayout:%s\n",
(int)outlink->sample_rate,
(char *)av_x_if_null(av_get_sample_fmt_name(outlink->format), "?"),
args1);*/
end:
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs1);
return ret;
}
往混音filter中添加音频帧
av_buffersrc_add_frame_flags(buffersrc_ctx1, input_frame, 0)
av_buffersrc_add_frame_flags(buffersrc_ctx2, input_frame, 0)
从filter中取出混音后的音频帧 然后再进行编码
void CCameraRecord::HandleHunyin()
{
int encode_audio = 1;
int ret;
while (encode_audio)
{
if (m_bAudioExit)
break;
ret = av_buffersink_get_frame(buffersink_ctxAudio, m_filt_frame);//接收混音后的音频帧
if (ret >= 0)
{
write_audio_frame(m_pAudFmtCtx->streams[m_audioindex], m_filt_frame, av_gettime() - m_start_time);//将音频帧进行重采样编码写入文件
av_frame_unref(m_filt_frame);
}
else
{
Sleep(10);
continue;
}
}
}
混音的具体操作是
PCM的叠加原理:假设混合PCM1和PCM2,则MIX_PCM=PCM1/2 + PCM2/2。
因此,如果需要在混音的时候只要一路的时候需要将另外一路进行静音,也就是传0进去就ok了
if (m_bflagAudioSys)//控制声音
{
memset(input_frame->data[0], 0, input_frame->nb_samples*2*2);
}
if (av_buffersrc_add_frame_flags(buffersrc_ctx1, input_frame, 0) < 0) {
av_log(NULL, AV_LOG_ERROR, "Error while feeding the audio filtergraph\n");
continue;
}
https://www.cnblogs.com/ranson7zop/p/7725324.html