ffmpegプラットフォーム間録音技術の詳細

21822 ワード

  • ffmpeg開発の基本知識
  • linuxでのレコーディング
  • windowsでのレコーディング
  • サンプルコード
  • で発生したいくつかの工事問題
  • MP 3ファイルにエンコードして保存するにはどうすればいいですか?
  • なぜコンパイル時にxxxxx関数の未定義の参照が表示されるのですか?


  • ffmpeg開発の基本知識
    この部分は私のもう一つのブログを参考にしてください.https://blog.csdn.net/FakeTaoZero/article/details/104001059
    linuxでのレコーディング
    linuxプラットフォームの下の録音は現在主にalsaとpulseaudioの2種類を使用しており、ネット上の録音チュートリアルはalsaを使用することが多いが、実際にはpulseaudioも可能である.この部分の内容は最近接触し始めたばかりで、わざわざ理解したが、必ずしも正しいとは限らないかもしれない.以下、両者の関係を簡単に述べる.実はこの問題はlinuxのオーディオ駆動システムから始めます.
  • linuxの初期のオーディオドライバはOSSであり、OSSのオーディオドライバの最大の問題はサウンドカード独占の問題である.すなわち、あるアプリケーションが再生/録音する場合、別のプログラムは再生/録音できないか、前のプログラムの機能を中断するかのいずれかである.そのため、linuxの発展に伴い、alsaという新しいドライバ技術は
  • 誕生した.
  • alsa技術は理論的にはサウンドカード独占問題を解決することができる.それはいくつかの論理設備の概念を導入し、自動混音などの機能を達成することができるからだ.しかし、太原は始まった.そしてalsaの中の単一設備も独占している.ただ、論理設備を新設し、論理設備が物理設備を引用する方法で間接的に問題を解決することができるだけだ.
    一方、pulseaudioはオーディオサーバであり、サウンドカードのプリエンプト問題により誕生した解決策であり、pulseaudioによってサウンドカードを制御し、他のアプリケーションをclientとしてpulseaudioサーバに接続し、間接性を導入し、オーディオミックス、録音などの機能をpulseaudioに渡して解決することを考えている.例えばlinuxの下の網易雲音楽のオーディオ再生機能はpulseaudioで再生されている.これは音楽を再生するときにpulseaudioを殺すことで検証でき、殺した後に音がないことがわかる.しかしpulseaudioは、一部のサーバシステムではデフォルトでインストール/起動されていません.pulseaudioベースで開発されたアプリケーションを使用する場合は、pulseaudioプログラムが起動していることを保証する必要があります.しかしlinuxの下の多くのデスクトップ環境ではpulseaudioが使用されています
    ffmpegではalsaもpulseaudioもサポートされていますが、コンパイル時に関連するライブラリをコンパイルする必要があります.devicesでalsa/pulse項目が見つからない可能性はありません.では、コンパイル時にコンパイルされているかどうかをどのように確認しますか?ffmpegコンパイルの前に./configureスクリプト、このスクリプトは依存ライブラリの存在を自動的に検出し、検出が完了すると今回のコンパイル時の機能コンポーネントの状況が印刷され、demuxer/device項目の下にalsa/pulse項目が存在するかどうかを見ることができます.
    ffmpeg開発の基本知識を知っていれば、AVInputFormat構造体は入力形式の構造体であり、av_find_input_format関数の設定linuxレコーディングでは、このパラメータは「alsa」/「pulse」に設定され、対応するサウンドカード名はavformat_に設定されます.open_inputで設定します.
    ここで、サウンドカードの名前の取得にはいくつかの方法があります.alsaではarecordツール、pulseaudioではpacmdツールを使用して表示できます.次に例を示します.
    tao@tao-PC:~$ arecord -l
    **** CAPTURE        ****
    card 0: PCH [HDA Intel PCH], device 0: ALC887-VD Analog [ALC887-VD Analog]
         : 1/1
          #0: subdevice #0
    card 0: PCH [HDA Intel PCH], device 2: ALC887-VD Alt Analog [ALC887-VD Alt Analog]
         : 1/1
          #0: subdevice #0
    
       ,       ,          :card {  1}: xxxx, device {  2}: xxxx,
                 ,        , alsa         :"hw:{  1},{  2}“
    
    tao@tao-PC:~$ pacmd list-sources
    2 source(s) available.
      * index: 0
      name: 
      driver: 
      flags: DECIBEL_VOLUME LATENCY DYNAMIC_LATENCY
      state: SUSPENDED
      suspend cause: IDLE 
      priority: 1030
      volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB
              balance 0.00
      base volume: 65536 / 100% / 0.00 dB
      volume steps: 65537
      muted: no
      current latency: 0.00 ms
      max rewind: 0 KiB
      sample spec: s16le 2ch 48000Hz
      channel map: front-left,front-right
                      
      used by: 0
      linked by: 0
      configured latency: 0.00 ms; range is 0.50 .. 341.33 ms
      monitor_of: 0
      card: 0 
      module: 7
      properties:
        device.description = "Monitor of           (IEC958)"
        device.class = "monitor"
        alsa.card = "0"
        alsa.card_name = "HDA Intel PCH"
        alsa.long_card_name = "HDA Intel PCH at 0xf7d00000 irq 27"
        alsa.driver_name = "snd_hda_intel"
        device.bus_path = "pci-0000:00:1b.0"
        sysfs.path = "/devices/pci0000:00/0000:00:1b.0/sound/card0"
        device.bus = "pci"
        device.vendor.id = "8086"
        device.vendor.name = "Intel Corporation"
        device.product.id = "8c20"
        device.product.name = "8 Series/C220 Series Chipset High Definition Audio Controller"
        device.form_factor = "internal"
        device.string = "0"
        module-udev-detect.discovered = "1"
        device.icon_name = "audio-card-pci"
    
    
        index: 1
      name: 
      driver: 
      flags: HARDWARE HW_MUTE_CTRL HW_VOLUME_CTRL DECIBEL_VOLUME LATENCY DYNAMIC_LATENCY
      state: SUSPENDED
      suspend cause: IDLE 
      priority: 9039
      volume: front-left: 9996 /  15% / -49.00 dB,   front-right: 9996 /  15% / -49.00 dB
              balance 0.00
      base volume: 6554 /  10% / -60.00 dB
      volume steps: 65537
      muted: no
      current latency: 0.00 ms
      max rewind: 0 KiB
      sample spec: s16le 2ch 48000Hz
      channel map: front-left,front-right
                      
      used by: 0
      linked by: 0
      configured latency: 0.00 ms; range is 0.50 .. 341.33 ms
      card: 0 
      module: 7
      properties:
        alsa.resolution_bits = "16"
        device.api = "alsa"
        device.class = "sound"
        alsa.class = "generic"
        alsa.subclass = "generic-mix"
        alsa.name = "ALC887-VD Analog"
        alsa.id = "ALC887-VD Analog"
        alsa.subdevice = "0"
        alsa.subdevice_name = "subdevice #0"
        alsa.device = "0"
        alsa.card = "0"
        alsa.card_name = "HDA Intel PCH"
        alsa.long_card_name = "HDA Intel PCH at 0xf7d00000 irq 27"
        alsa.driver_name = "snd_hda_intel"
        device.bus_path = "pci-0000:00:1b.0"
        sysfs.path = "/devices/pci0000:00/0000:00:1b.0/sound/card0"
        device.bus = "pci"
        device.vendor.id = "8086"
        device.vendor.name = "Intel Corporation"
        device.product.id = "8c20"
        device.product.name = "8 Series/C220 Series Chipset High Definition Audio Controller"
        device.form_factor = "internal"
        device.string = "front:0"
        device.buffering.buffer_size = "65536"
        device.buffering.fragment_size = "32768"
        device.access_mode = "mmap+timer"
        device.profile.name = "analog-stereo"
        device.profile.description = "     "
        device.description = "          "
        alsa.mixer_name = "Realtek ALC887-VD"
        alsa.components = "HDA:10ec0887,10438576,00100302"
        module-udev-detect.discovered = "1"
        device.icon_name = "audio-card-pci"
      ports:
        analog-input-front-mic:      (priority 8500, latency offset 0 usec, available: no)
          properties:
            device.icon_name = "audio-input-microphone"
        analog-input-rear-mic:      (priority 8200, latency offset 0 usec, available: no)
          properties:
            device.icon_name = "audio-input-microphone"
        analog-input-linein:      (priority 8100, latency offset 0 usec, available: no)
          properties:
    
      active port: 
    
      pulse,       index    ,      ,     
    

    サウンドカード名によって、alsaとpulseには異なるAVOptionsがあり、対応するAVOptionsはffmpeg-h demuxer=alsa/pulseで表示できます.以下にalsaとpulseに対応するパラメータを示します.
    Demuxer alsa [ALSA audio input]:
    ALSA demuxer AVOptions:
      -sample_rate               .D......  (from 1 to INT_MAX) (default 48000)
      -channels                  .D......  (from 1 to INT_MAX) (default 2)
    
    Demuxer pulse [Pulse audio input]:
    Pulse demuxer AVOptions:
      -server                 .D...... set PulseAudio server
      -name                   .D...... set application name (default "Lavf57.83.100")
      -stream_name            .D...... set stream description (default "record")
      -sample_rate               .D...... set sample rate in Hz (from 1 to INT_MAX) (default 48000)
      -channels                  .D...... set number of audio channels (from 1 to INT_MAX) (default 2)
      -frame_size                .D...... set number of bytes per frame (from 1 to INT_MAX) (default 1024)
      -fragment_size             .D...... set buffering size, affects latency and cpu usage (from -1 to INT_MAX) (default -1)
      -wallclock                 .D...... set the initial pts using the current time (from -1 to 1) (default 1)
    

    以上より、どのパラメータ項目があるかがわかりましたが、alsaでは、デフォルトパラメータが使用可能であるとは限らないため、パラメータの値も注意する必要があります.alsaは自動再サンプリング(pulseサポート)をサポートしていないため、alsaの2つのパラメータの値項目は次のコマンドで表示できます.
    tao@tao-PC:~$ arecord -D hw:0,0 --dump-hw-params
         WAVE 'stdin' : Unsigned 8 bit,   8000Hz, Mono
    HW Params of device "hw:0,0":
    --------------------
    ACCESS:  MMAP_INTERLEAVED RW_INTERLEAVED
    FORMAT:  S16_LE S32_LE
    SUBFORMAT:  STD
    SAMPLE_BITS: [16 32]
    FRAME_BITS: [32 64]
    CHANNELS: 2
    RATE: [44100 192000]
    PERIOD_TIME: (83 185760)
    PERIOD_SIZE: [16 8192]
    PERIOD_BYTES: [128 65536]
    PERIODS: [2 32]
    BUFFER_TIME: (166 371520)
    BUFFER_SIZE: [32 16384]
    BUFFER_BYTES: [128 65536]
    TICK_TIME: ALL
    --------------------
    arecord: set_params:1299:        
    Available formats:
    - S16_LE
    - S32_LE
    
       ,        ,                  ,         channels    rate sample_rate   ,
                   ,                    ,        ,             ,
      44100/48000/192000,                    ,            ffmpeg        ,  :
    ffmpeg -f alsa -sample_rate     -i hw:0,0 -t 10 test.wav -v trace
              ,            。
    

    Windowsでのレコーディング
    現在windowsの録音は広くて簡単なのはWAVEIN/OUTで、その上にdshowもありますが、dshowのapiは比較的難しいです.ffmpegはdshowを使っています.linuxの録音と似ています.そのAVInputFormatはav_を使っています.find_input_format(「dshow」)を設定し、
    dshowのAVOptionsは以下の通り.
    Demuxer dshow [DirectShow capture]:
    dshow indev AVOptions:
      -video_size         .D....... set video size given a string such as 640x480 or hd720.
      -pixel_format          .D....... set video pixel format (default none)
      -framerate              .D....... set video frame rate
      -sample_rate               .D....... set audio sample rate (from 0 to INT_MAX) (default 0)
      -sample_size               .D....... set audio sample size (from 0 to 16) (default 0)
      -channels                  .D....... set number of audio channels, such as 1 or 2 (from 0 to INT_MAX) (default 0)
      -audio_buffer_size         .D....... set audio device buffer latency size in milliseconds (default is the device's default) (from 0 to INT_MAX) (default 0)
      -list_devices          .D....... list available devices (default false)
      -list_options          .D....... list available options for specified device (default false)
      -video_device_number         .D....... set video device number for devices with same name (starts at 0) (from 0 to INT_MAX) (default 0)
      -audio_device_number         .D....... set audio device number for devices with same name (starts at 0) (from 0 to INT_MAX) (default 0)
      -crossbar_video_input_pin_number         .D....... set video input pin number for crossbar device (from -1 to INT_MAX) (default -1)
      -crossbar_audio_input_pin_number         .D....... set audio input pin number for crossbar device (from -1 to INT_MAX) (default -1)
      -show_video_device_dialog     .D....... display property dialog for video capture device (default false)
      -show_audio_device_dialog     .D....... display property dialog for audio capture device (default false)
      -show_video_crossbar_connection_dialog     .D....... display property dialog for crossbar connecting pins filter on video device (default false)
      -show_audio_crossbar_connection_dialog     .D....... display property dialog for crossbar connecting pins filter on audio device (default false)
      -show_analog_tv_tuner_dialog     .D....... display property dialog for analog tuner filter (default false)
      -show_analog_tv_tuner_audio_dialog     .D....... display property dialog for analog tuner audio filter (default false)
      -audio_device_load      .D....... load audio capture filter device (and properties) from file
      -audio_device_save      .D....... save audio capture filter device (and properties) to file
      -video_device_load      .D....... load video capture filter device (and properties) from file
      -video_device_save      .D....... save video capture filter device (and properties) to file
    

    対応するサウンドカード名は、次のコマンドラインコマンドで表示されます.
    PS C:\Users\Tao> ffmpeg -list_devices true -f dshow -i dummy                                                                                                                                                      ffmpeg version N-94652-g808a6717e0 Copyright (c) 2000-2019 the FFmpeg developers
      built with gcc 9.1.1 (GCC) 20190807
      configuration: --disable-static --enable-shared --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt
      libavutil      56. 33.100 / 56. 33.100
      libavcodec     58. 55.101 / 58. 55.101
      libavformat    58. 31.104 / 58. 31.104
      libavdevice    58.  9.100 / 58.  9.100
      libavfilter     7. 58.101 /  7. 58.101
      libswscale      5.  6.100 /  5.  6.100
      libswresample   3.  6.100 /  3.  6.100
      libpostproc    55.  6.100 / 55.  6.100
    [dshow @ 000001a0e0fc02c0] DirectShow video devices (some may be both video and audio devices)
    [dshow @ 000001a0e0fc02c0] Could not enumerate video devices (or none found).
    [dshow @ 000001a0e0fc02c0] DirectShow audio devices
    [dshow @ 000001a0e0fc02c0]  "      (Realtek High Definition Audio)"
    [dshow @ 000001a0e0fc02c0]     Alternative name "@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{28679435-B3DB-4479-90B4-8EBC4EB7C18E}"
    dummy: Immediate exit requested
    
      ,  windows          ,     chcp 65001          utf8,          。
      ,           "      (Realtek High Definition Audio)",  ,                u8"audio=      (Realtek High Definition Audio)"  
    

    サンプルコード
    次のコードはlinuxの下で録音する簡単な例で、44.1 KHzの2チャネルの元のオーディオから16 Khzの1チャネルへの変換を実現して、最終的にWAVファイルに保存して、もし他のタイプのファイルに保存するならば、エンコーダを変更する必要があって、windowsの下で下のコードを使うならば、devnameとalsaの2つのパラメータをwindowsの下で対応するパラメータに変更するだけでwindowsの下で録音機能を実現できます.
    #include 
    #include 
    extern "C" {
    #include 
    #include 
    #include 
    #include 
    #include 
    }
    
    #include 
    
    void call_back(void* avcl, int level, const char* fmt, va_list vl)
    {
        vfprintf(stdout, fmt, vl);
    }
    
    int main()
    {
        av_log_set_level(AV_LOG_TRACE);
        av_log_set_callback(call_back);
        avdevice_register_all();
        avcodec_register_all();
        av_register_all();
    
        AVInputFormat* in_fmt = nullptr;
        AVFormatContext* in_ctx = nullptr;
        AVFormatContext* out_ctx = nullptr;
        AVCodecContext* in_decode_ctx = nullptr;
        AVCodecContext* out_encode_ctx = nullptr;
        SwrContext* conv_ctx = nullptr;
        AVDictionary* opt_dict = nullptr;
        std::string devname = u8"hw:0,0";   //       
        std::string filename = "test.wav";  //         
        int stream_idx = -1;
    
        // format     
        {
            in_fmt = av_find_input_format("alsa");
            in_ctx = avformat_alloc_context();
    
            av_dict_set(&opt_dict, "sample_rate", "44100", 0);
            av_dict_set(&opt_dict, "sample_size", "16", 0);
            av_dict_set(&opt_dict, "channels", "2", 0);
        }
    
        //     
        {
            avformat_open_input(&in_ctx, devname.c_str(), in_fmt, &opt_dict);
            avformat_find_stream_info(in_ctx, nullptr);
        }
    
        //      
        {
            for (size_t i = 0; i < in_ctx->nb_streams; i++)
            {
                if (in_ctx->streams[i]->codecpar->codec_type == AVMediaType::AVMEDIA_TYPE_AUDIO)
                {
                    stream_idx = i;
                    break;
                }
            }
            av_dump_format(in_ctx, stream_idx, devname.c_str(), 0);
        }
    
        //      
        {
            AVCodec* codec = avcodec_find_decoder(in_ctx->streams[stream_idx]->codecpar->codec_id);
            in_decode_ctx = avcodec_alloc_context3(codec);
            avcodec_parameters_to_context(in_decode_ctx, in_ctx->streams[stream_idx]->codecpar);
            in_decode_ctx->channel_layout = av_get_default_channel_layout(2);
            avcodec_open2(in_decode_ctx, codec, nullptr);
        }
    
        //      
        {
            AVCodec* codec = avcodec_find_encoder(AVCodecID::AV_CODEC_ID_PCM_S16LE);
            out_encode_ctx = avcodec_alloc_context3(codec);
            out_encode_ctx->sample_rate = 16000;
            out_encode_ctx->channels = 1;
            out_encode_ctx->channel_layout = av_get_channel_layout("mono");
            out_encode_ctx->sample_fmt = AVSampleFormat::AV_SAMPLE_FMT_S16;
            out_encode_ctx->time_base = AVRational{ 1, 16000 };
            avcodec_open2(out_encode_ctx, codec, nullptr);
        }
    
        //       
        {
            AVCodec* codec = avcodec_find_encoder(AVCodecID::AV_CODEC_ID_PCM_S16LE);
            avformat_alloc_output_context2(&out_ctx, nullptr, nullptr, filename.c_str());
            AVStream* out_stream = avformat_new_stream(out_ctx, codec);
            //               
            avcodec_parameters_from_context(out_stream->codecpar, out_encode_ctx);
            out_stream->time_base = AVRational{ 1, 16000 };
            av_dump_format(out_ctx, 0, filename.c_str(), 1);
            if (!(out_ctx->flags & AVFMT_NOFILE))
            {
                avio_open2(&out_ctx->pb, filename.c_str(), AVIO_FLAG_WRITE, nullptr, nullptr);
            }
        }
    
        //      
        {
            avformat_write_header(out_ctx, nullptr);
        }
    
        //         
        {
            conv_ctx = swr_alloc_set_opts(
                nullptr,
                AV_CH_LAYOUT_MONO,
                AVSampleFormat::AV_SAMPLE_FMT_S16,
                16000,
                AV_CH_LAYOUT_STEREO,
                AVSampleFormat::AV_SAMPLE_FMT_S16,
                44100,
                0,
                nullptr);
            swr_init(conv_ctx);
        }
    
        AVPacket* in_pkt = nullptr;
        AVPacket* out_pkt = nullptr;
        AVFrame* raw_frm = nullptr;
        AVFrame* conv_frm = nullptr;
    
        {
            in_pkt = av_packet_alloc();
            out_pkt = av_packet_alloc();
            raw_frm = av_frame_alloc();
            conv_frm = av_frame_alloc();
        }
    
        {
            int ret = 0;
            auto start_time = std::chrono::system_clock::now();
            while (av_read_frame(in_ctx, in_pkt) == 0)
            {
                //   
                ret = avcodec_send_packet(in_decode_ctx, in_pkt);
                ret = avcodec_receive_frame(in_decode_ctx, raw_frm);
                //             
                {
                    conv_frm->pts = raw_frm->pts;
                    conv_frm->sample_rate = 16000;
                    conv_frm->channels = 1;
                    conv_frm->channel_layout = av_get_channel_layout("mono");
                    conv_frm->format = AVSampleFormat::AV_SAMPLE_FMT_S16;
                }
                //    
                ret = swr_convert_frame(conv_ctx, conv_frm, raw_frm);
                //            
                ret = avcodec_send_frame(out_encode_ctx, conv_frm);
                ret = avcodec_receive_packet(out_encode_ctx, out_pkt);
                if (ret == 0)
                {
                    //         
                    ret = av_write_frame(out_ctx, out_pkt);
                }
                av_packet_unref(in_pkt);
                av_packet_unref(out_pkt);
                av_frame_unref(raw_frm);
                av_frame_unref(conv_frm);
                auto end_time = std::chrono::system_clock::now();
                //   20s   
                if (std::chrono::duration_cast<:chrono::seconds>(end_time - start_time).count() > 20)
                {
                    break;
                }
            }
    
            {
                av_write_trailer(out_ctx);
            }
    
            {
                swr_free(&conv_ctx);
            }
    
            {
                av_packet_free(&in_pkt);
                av_packet_free(&out_pkt);
                av_frame_free(&raw_frm);
                av_frame_free(&conv_frm);
            }
    
            {
                avcodec_free_context(&in_decode_ctx);
                avcodec_free_context(&out_encode_ctx);
            }
    
            {
                avformat_free_context(in_ctx);
                avformat_free_context(out_ctx);
            }
        }
    }
    

    いくつかの工事問題に遭遇した.
    MP 3ファイルにエンコードして保存したい場合は、どうすればいいですか?
  • まずffmpeg-encodersでMP 3対応のエンコーダを表示します.エンコーダ名はlibmp 3 lame
  • です.
  • ffmpeg-h encoder=libmp 3 lameを使用してエンコーダでサポートされているrateおよびformatおよびlayoutを表示し、対応するパラメータ
  • を設定します.
  • オリジナルオーディオのパラメータとencoderのパラメータが一致しない場合、Swrcontextを使用して変換し、エンコーダ
  • に送る必要がある.
  • エンコーダがframeを間違えた可能性があります.sizeとsamplesの数は異なり、この場合fifoキューを導入する必要があります.具体的な実装はこの例を参照してください.https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/transcode_aac.c

  • なぜコンパイル時にxxxx関数の未定義の参照が表示されるのですか?
    これは,ライブラリのリンク順序の違いによりffmpegライブラリ間に一定の依存関係があり,リンクを調整する必要がある場合のリンク順序を調整すれば解決できるためである.