ffmpegプラットフォーム間録音技術の詳細
ffmpeg開発の基本知識
この部分は私のもう一つのブログを参考にしてください.https://blog.csdn.net/FakeTaoZero/article/details/104001059
linuxでのレコーディング
linuxプラットフォームの下の録音は現在主にalsaとpulseaudioの2種類を使用しており、ネット上の録音チュートリアルはalsaを使用することが多いが、実際にはpulseaudioも可能である.この部分の内容は最近接触し始めたばかりで、わざわざ理解したが、必ずしも正しいとは限らないかもしれない.以下、両者の関係を簡単に述べる.実はこの問題はlinuxのオーディオ駆動システムから始めます.
一方、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ファイルにエンコードして保存したい場合は、どうすればいいですか?
なぜコンパイル時にxxxx関数の未定義の参照が表示されるのですか?
これは,ライブラリのリンク順序の違いによりffmpegライブラリ間に一定の依存関係があり,リンクを調整する必要がある場合のリンク順序を調整すれば解決できるためである.