ffmpeg 使用dshow采集音频,我是用C#写的代码,先说说我遇到的坑。
1.我获得的麦克风设备名含中文,怎么转utf8都失败了,幸好还有个设备别名都是英文字母。
2.我获得的设备别名需要把字符“:”替换成“_”才能正常使用。
3.我采样率设置8000的时候打开设备失败,跟了ffmpeg的代码,发现我的设备最低只支持16000采样率。
代码中包含声音的重采样,因为我需要8000的采样率,重采样类链接:
https://blog.csdn.net/wer85121430/article/details/79672930
贴代码:
public unsafe interface ACCallBack
{
void CBAudioPcm(byte[] capture_data, ulong pts);
}
public unsafe class AudioCapture
{
#region 类成员变量
//回调采集数据
private ACCallBack m_callback = null;
//线程相关
private Thread m_thread = null;
private bool m_is_thread_exit = false;
//重采样
private AudioResample m_audio_resample = null;
//ffmpeg容器格式
private AVFormatContext* m_ffmpeg_fct = null;
//ffmpeg容器格式中音频的AVStream索引
private int m_audio_stream_index = -1;
//原始音频参数和重采样音频参数
private int m_src_channel_layout = 0;
private int m_src_sample_rate = 0;
private AVSampleFormat m_src_sample_fmt = 0;
private int m_dst_channel_layout = 0;
private int m_dst_sample_rate = 0;
private AVSampleFormat m_dst_sample_fmt;
//一个音频采样包的大小和时间
private int m_package_size;
private ulong m_package_ms;
#endregion
///
/// 开始音频采集
///
/// 采样率
/// 一个采样的位数
/// 通道数
/// 采样数据回调
public int ACStart(int sample_rate, short a_sample_bits, short channels, ACCallBack callback)
{
if (m_ffmpeg_fct != null)
return -1;
int ret = 0;
string audio_device_name;
AVCodecContext* ffmpeg_cct = null;
#region 获取第一个音频设备名
FilterInfoCollection audio_devices = new FilterInfoCollection(FilterCategory.AudioInputDevice);
if (audio_devices.Count < 1)
{
return -1;
}
audio_device_name = audio_devices[0].MonikerString;
audio_device_name = audio_device_name.Replace(":", "_");
audio_device_name = "audio=" + audio_device_name;
#endregion
#region ffmpeg采集初始化
var ffmpeg_ift = ffmpeg.av_find_input_format("dshow");
AVDictionary* tmp = null;
ffmpeg.av_dict_set_int(&tmp, "sample_rate", (long)44100, 0); //我的设备不支持8000,先采44100在重采样成8000
ffmpeg.av_dict_set_int(&tmp, "sample_size", (long)16, 0);
ffmpeg.av_dict_set_int(&tmp, "channels", (long)1, 0);
ffmpeg.av_dict_set_int(&tmp, "audio_buffer_size", (long)40, 0); //buffer大小是以采样率44100计算的,设置了采样率也无效
m_package_ms = 40;
m_package_size = sample_rate * 2 / 25;
fixed (AVFormatContext** ffmpeg_fct = &m_ffmpeg_fct)
ret = ffmpeg.avformat_open_input(ffmpeg_fct, audio_device_name, ffmpeg_ift, &tmp);
if (ret != 0)
{
return -1;
}
for (int i = 0; i < m_ffmpeg_fct->nb_streams; i++)
{
if (m_ffmpeg_fct->streams[i]->codec->codec_type == AVMediaType.AVMEDIA_TYPE_AUDIO)
{
m_audio_stream_index = i;
break;
}
}
if (m_audio_stream_index == -1)
{
return -1;
}
ffmpeg_cct = m_ffmpeg_fct->streams[m_audio_stream_index]->codec;
#endregion
#region 重采样
m_src_channel_layout = ffmpeg.AV_CH_LAYOUT_MONO;
m_src_sample_rate = ffmpeg_cct->sample_rate;
m_src_sample_fmt = ffmpeg_cct->sample_fmt;
m_dst_sample_rate = sample_rate;
if (channels == 1)
m_dst_channel_layout = ffmpeg.AV_CH_LAYOUT_MONO;
else
m_dst_channel_layout = ffmpeg.AV_CH_LAYOUT_STEREO;
if(a_sample_bits == 8)
m_dst_sample_fmt = AVSampleFormat.AV_SAMPLE_FMT_U8;
else
m_dst_sample_fmt = AVSampleFormat.AV_SAMPLE_FMT_S16;
m_audio_resample = new AudioResample();
ret = m_audio_resample.ARInit(m_src_channel_layout, m_src_sample_rate, m_src_sample_fmt,
m_dst_channel_layout, m_dst_sample_rate, m_dst_sample_fmt,
40);
if(ret < 0)
{
goto fail;
}
#endregion
#region 取数据线程
m_is_thread_exit = false;
m_thread = new Thread(() =>
{
try
{
this.ThreadRun();
}
catch (Exception ex)
{
}
});
m_thread.IsBackground = true;
m_thread.Start();
#endregion
m_callback = callback;
return 0;
fail:
ACStop();
return ret;
}
///
/// 停止采集
///
public void ACStop()
{
if(m_thread != null)
{
m_is_thread_exit = true;
int wait_thread_exit = 0;
while (m_thread.IsAlive && wait_thread_exit++ < 2)
{
Thread.Sleep(50);
}
m_thread = null;
}
if(m_ffmpeg_fct != null)
{
fixed(AVFormatContext** ffmpeg_fct = &m_ffmpeg_fct)
ffmpeg.avformat_close_input(ffmpeg_fct);
m_ffmpeg_fct = null;
}
if(m_audio_resample != null)
{
m_audio_resample.ARDeinit();
m_audio_resample = null;
}
}
#region 测试写声音文件
private bool test_is_wirte_wav_file = false;
private AudioWriteWavFile test_write_orig_wav = null;
private AudioWriteWavFile test_write_resample_wav = null;
private String test_orig_file = "G:\\test_file\\capture_orig.wav";
private String test_samp_file = "G:\\test_file\\capture_samp.wav";
private int test_capture_orig_count = 0;
private int text_capture_samp_count = 0;
#endregion
///
/// 读取采集数据线程
///
private void ThreadRun()
{
AVPacket capture_pkt;
bool time_first_write = true;
DateTime time_start = DateTime.Now;
ulong time_write = 0;
ffmpeg.av_init_packet(&capture_pkt);
capture_pkt.data = null;
capture_pkt.size = 0;
while (!m_is_thread_exit)
{
ffmpeg.av_read_frame(m_ffmpeg_fct, &capture_pkt);
#region 测试写声音文件
if (test_is_wirte_wav_file)
{
test_capture_orig_count++;
if (test_write_orig_wav == null)
{
test_write_orig_wav = new AudioWriteWavFile();
test_write_orig_wav.AWWFOpen(test_orig_file);
}
if(test_capture_orig_count < 200)
test_write_orig_wav.AWWFWrite(capture_pkt.data, capture_pkt.size);
if (test_capture_orig_count == 200)
test_write_orig_wav.AWWFClose(44100, 88200);
}
#endregion
byte[] capture_data = m_audio_resample.ARConvert(capture_pkt.data, capture_pkt.size);
ffmpeg.av_packet_unref(&capture_pkt);
if (m_package_size != capture_data.Length)
{
continue;
}
#region 测试写声音文件
if (test_is_wirte_wav_file)
{
text_capture_samp_count++;
if (test_write_resample_wav == null)
{
test_write_resample_wav = new AudioWriteWavFile();
test_write_resample_wav.AWWFOpen(test_samp_file);
}
if (text_capture_samp_count < 200)
test_write_resample_wav.AWWFWrite(capture_data);
if (text_capture_samp_count == 200)
test_write_resample_wav.AWWFClose();
}
#endregion
#region 调整时间戳
if (time_first_write)
{
time_first_write = false;
time_start = DateTime.Now;
time_write = 0;
}
else
{
//时间戳按m_package_ms一个采集包的毫秒数增长
time_write += m_package_ms;
ulong time_interval = Convert.ToUInt64((DateTime.Now - time_start).TotalMilliseconds);
//与实际时间对比调整
if (time_write > time_interval + m_package_ms)
{
time_write -= m_package_ms;
}
if (time_write < time_interval - m_package_ms)
{
time_write = time_interval;
}
Console.WriteLine(time_write + " " + time_interval);
}
#endregion
m_callback.CBAudioPcm(capture_data, time_write);
}
}
}