ffmpeg 采集麦克风设备声音

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);
        }

    }
}


你可能感兴趣的:(ffmpeg)