一、前言

本系列文章是对音视频技能入门常识的整理和温习,为进一步深化体系研究音视频技能稳固根底。文章列表:

  • 01-音视频技能中心常识|了解音频技能【移动通讯技能的发展、声响的实质、深化了解音频】
  • 02-音视频技能中心常识|建立开发环境【FFmpeg与Qt、Windows开发环境建立、Mac开发环境建立、Qt开发根底】
  • 03-音视频技能中心常识|Qt开发根底【.pro文件的装备、Qt控件根底、信号与槽】
  • 04-音视频技能中心常识|音频录制【指令行、C++编程】
  • 05-音视频技能中心常识|音频播映【播映PCM、WAV、PCM转WAV、PCM转WAV、播映WAV】
  • 06-音视频技能中心常识|音频重采样【音频重采样简介、用指令行进行重采样、经过编程重采样】
  • 07-音视频技能中心常识|AAC编码【AAC编码器解码器、编译FFmpeg、AAC编码实战、AAC解码实战】
  • 08-音视频技能中心常识|成像技能【重识图片、详解YUV、视频录制、显现BMP图片、显现YUV图片】
  • 09-音视频技能中心常识|视频编码解码【了解H.264编码、H.264编码、H.264编码解码】
  • 10-音视频技能中心常识|RTMP服务器建立【流媒体、服务器环境】

二、经过指令行进行音频录制

总算要开端进行FFmpeg实战了,一起来感受一下FFmpeg的强大吧。

1. 指令简介

FFmpeg的bin目录中提供了3个指令(可履行程序),能够直接在指令行上运用。

04-音视频技术核心知识|音频录制【命令行、C++编程】

1.1 ffmpeg

ffmpeg的首要效果:对音视频进行编解码。

# 将MP3文件转成WAV文件
ffmpeg -i xx.mp3 yy.wav

当输入指令ffmpeg时,能够看到ffmpeg指令的运用格局是:

ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}...

简化一下,常用格局是:

ffmpeg arg1 arg2 -i arg3 arg4 arg5
  • arg1:大局参数
  • arg2:输入文件参数
  • arg3:输入文件
  • arg4:输出文件参数
  • arg5:输出文件

更多详细用法,能够参阅官方文档:ffmpeg-all.html,或许运用以下指令检查:

# 简易版
ffmpeg -h
# 详细版
ffmpeg -h long
# 完整版
ffmpeg -h full
# 或许运用
# ffmpeg -help
# ffmpeg -help long
# ffmpeg -help full

1.2 ffprobe

ffprobe的首要效果:检查音视频的参数信息。

# 能够检查MP3文件的采样率、比特率、时长等信息
ffprobe xx.mp3

当输入指令ffprobe时,能够看到ffprobe指令的运用格局是:

ffprobe [OPTIONS] [INPUT_FILE]
# OPTIONS:参数
# INPUT_FILE:输入文件

更多详细用法,能够参阅官方文档:ffprobe-all.html,或许运用以下指令检查:

# 简易版
ffprobe -h
# 详细版
ffprobe -h long
# 完整版
ffprobe -h full
# 或许运用
# ffprobe -help
# ffprobe -help long
# ffprobe -help full

1.3 ffplay

ffplay的首要效果:播映音视频。

# 播映MP3文件
ffplay xx.mp3

当输入指令ffplay时,能够看到ffplay指令的运用格局是:

ffplay [options] input_file
# options:参数
# input_file:输入文件

更多详细用法,能够参阅官方文档:ffplay-all.html,或许运用以下指令检查:

# 简易版
ffplay -h
# 详细版
ffplay -h long
# 完整版
ffplay -h full
# 或许运用
# ffplay -help
# ffplay -help long
# ffplay -help full

1.4 hide_banner

增加*-hide_bannder*参数能够隐藏一些冗余的描绘信息,能够去实践比较以下2条指令的差异:

ffprobe xx.mp3
ffprobe -hide_banner xx.mp3
# ffmpeg、ffprobe、ffplay都适用

2. 经过指令行录音

2.1 检查可用设备

运用指令行检查当时渠道的可用设备:

ffmpeg -devices

Windows的输出成果如下所示:

  • 列表中有个dshow,全名叫DirectShow,是Windows渠道的多媒体体系库
  • 我们能够运用dshow去操作多媒体输入设备(比方录音设备)
Devices:
 D. = Demuxing supported
 .E = Muxing supported
 --
  E caca            caca (color ASCII art) output device
 D  dshow           DirectShow capture
 D  gdigrab         GDI API Windows frame grabber
 D  lavfi           Libavfilter virtual input device
 D  libcdio
  E sdl,sdl2        SDL2 output device
 D  vfwcap          VfW video capture

Mac的输出成果如下所示:

  • 列表中有个avfoundation,是Mac渠道的多媒体体系库
  • 我们能够运用avfoundation去操作多媒体输入设备(比方录音设备)
Devices:
 D. = Demuxing supported
 .E = Muxing supported
 --
 D  avfoundation    AVFoundation input device
 D  lavfi           Libavfilter virtual input device
  E sdl,sdl2        SDL2 output device

2.2 检查dshow支撑的设备

# 检查dshow支撑的设备
ffmpeg -f dshow -list_devices true -i dummy
# 或许
# ffmpeg -list_devices true -f dshow -i ''
# ffmpeg -list_devices true -f dshow -i ""
  • -f dshow

    • dshow支撑的
  • -list_devices true

    • 打印出一切的设备
  • -i dummy-i ”-i “”

    • 当即退出

我的笔记本外接了一只麦克风。

04-音视频技术核心知识|音频录制【命令行、C++编程】

因此,指令的履行成果大致如下所示:

DirectShow video devices (some may be both video and audio devices)
  "Integrated Camera"
DirectShow audio devices
  "线路输入 (3- 魅声T800)"
  "麦克风阵列 (Realtek(R) Audio)"
  • dshow支撑的视频设备
    • Integrated Camera:笔记本自带的摄像头
  • dshow支撑的音频设备
    • 线路输入 (3- 魅声T800):外接的麦克风
    • 麦克风阵列 (Realtek(R) Audio):笔记本自带的麦克风

2.3 检查avfoundation支撑的设备

在Mac渠道,运用的是avfoundation,而不是dshow。

ffmpeg -f avfoundation -list_devices true -i ''

输出成果如下所示:

AVFoundation video devices:
 [0] FaceTime高清摄像头(内建)
 [1] Capture screen 0
AVFoundation audio devices:
 [0] MS-T800
 [1] Edu Audio Device
 [2] MacBook Pro麦克风

列表中的MS-T800是外接的麦克风。在Mac上,FFmpeg还给每一个视频、音频设备进行了编号,比方MS-T800的编号是0、Mac自带麦克风的编号是2。

2.4 指定设备进行录音

# 运用外接的麦克风进行录音,最终生成一个wav文件
ffmpeg -f dshow -i audio="麦克风阵列 (Realtek(R) Audio)" out.wav
# 在Mac上经过编号指定设备
ffmpeg -f avfoundation -i :2 out.wav
# :0表明运用0号音频设备
# 0:2表明运用0号视频设备和2号音频设备
  • 能够运用快捷键Ctrl + C停止录音
  • 我这边的测试成果显现,音频参数是:
    • Windows:44100Hz采样率、16位深度、2声道、1411Kbps比特率
    • Mac:48000Hz采样率、16位深度、2声道、1536Kbps比特率

2.5 设置dshow的参数

先经过指令检查一下dshow能够运用的参数,详情能够检查官方文档:dshow参数。

# 从ffmpeg -devices指令的成果能够看得出来:dshow归于demuxer,而不是muxer
ffmpeg -h demuxer=dshow

部分输出成果如下所示:

# 采样率
-sample_rate         <int> set audio sample rate (from 0 to INT_MAX)
# 采样大小(位深度)
-sample_size         <int> set audio sample size (from 0 to 16)
# 声道数
-channels            <int> set number of audio channels, such as 1 or 2 (from 0 to INT_MAX)
# 列出特定设备支撑的参数
-list_options        <boolean> list available options for specified device (default false)

然后再看看你的设备支撑哪些参数。

ffmpeg -f dshow -list_options true -i audio="麦克风阵列 (Realtek(R) Audio)"

输出成果如下所示:

DirectShow audio only device options (from audio devices)
  Pin "Capture" (alternative pin name "Capture")
  min ch=1 bits=8 rate= 11025 max ch=2 bits=16 rate= 44100
# 能够看出来:采样率范围是11025~44100Hz

接下来设置录音时的音频参数。

ffmpeg -f dshow -sample_rate 15000 -sample_size 16 -channels 1 -i audio="麦克风阵列 (Realtek(R) Audio)" out.wav

二、经过编程进行音频录制

1. 经过编程录音

开发录音功能的首要过程是:

    1. 翻开设备
    • 注册设备
    • 翻开设备
      • 设置 设备 收集的上下文
      • 设置 选中的设备
      • 设置 选中的输入格局(不同渠道的格局不同,要提早 装备好,若是要跨渠道,需求提早做好条件编译)
      • 设置收集的 装备信息
    1. 收集数据
    • 获取 设备 的收集上下文目标
    • 收集数据
      • 获取收集数据的每一个当时收集好好的数据包(收集的数据不是马上经过IO存储在体系硬盘的是先存储在缓冲区的,这归于不同渠道对文件IO的优化,因为一向进行体系的IO操作,会影响履行效率:存入缓存比存入硬盘快)
      • 将数据包写入文件体系
      • 开释写好的数据包
      • 持续收集下一个数据包/退出收集
      • ….(Tips:1. 要写好收集的开端和完毕的操控逻辑 2. 要避免主线程收集,卡顿主线程下)
    • 经过文件存储API 存储 收集好的 数据
      • 翻开文件操作
      • 写入数据
      • 写完要封闭文件操作
    1. 开释资源

04-音视频技术核心知识|音频录制【命令行、C++编程】

需求用到的FFmpeg库有4个。

extern "C" {
// 设备相关API
#include <libavdevice/avdevice.h>
// 格局相关API
#include <libavformat/avformat.h>
// 工具相关API(比方过错处理)
#include <libavutil/avutil.h>
// 编码相关API
#include <libavcodec/avcodec.h>
}

1.1 权限申请

在Mac渠道,有2个留意点:(能够在编译之后的软件包上改info.plist、也能够自界说info.plist)

  • 需求在Info.plist中添加麦克风的运用说明,申请麦克风的运用权限
  • 运用Debug形式运行程序
  • 不然会呈现闪退的情况
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>NSMicrophoneUsageDescription</key>
        <string>运用麦克风收集您的天籁之音</string>
</dict>
</plist>

1.2 注册设备

在整个程序的运行过程中,只需求履行1次注册设备的代码。

// 初始化libavdevice并注册一切输入和输出设备
avdevice_register_all();

1.3 获取输入格局目标

1.3.1 宏界说

Windows和Mac环境的格局称号、设备称号都是不同的,所以运用条件编译实现跨渠道。

// 格局称号、设备称号现在暂时运用宏界说固定死
#ifdef Q_OS_WIN
    // 格局称号
    #define FMT_NAME "dshow"
    // 设备称号
    #define DEVICE_NAME "audio=麦克风阵列 (Realtek(R) Audio)"
#else
    #define FMT_NAME "avfoundation"
    #define DEVICE_NAME ":0"
#endif

1.3.2 中心代码

依据格局称号获取输入格局目标,后面需求运用输入格局目标翻开设备。

AVInputFormat *fmt = av_find_input_format(FMT_NAME);
if (!fmt) {
    // 假如找不到输入格局
    qDebug() << "找不到输入格局" << FMT_NAME;
    return;
}

1.4 翻开设备

// 格局上下文(后面经过格局上下文操作设备)
AVFormatContext *ctx = nullptr;
// 翻开设备
int ret = avformat_open_input(&ctx, DEVICE_NAME, fmt, nullptr);
// 假如翻开设备失利
if (ret < 0) {
    char errbuf[1024] = {0};
    // 依据函数返回的过错码获取过错信息
    av_strerror(ret, errbuf, sizeof (errbuf));
    qDebug() << "翻开设备失利" << errbuf;
    return;
}

1.5 收集数据

1.5.1 宏界说

#ifdef Q_OS_WIN
    // PCM文件的文件名
    #define FILENAME "F:/out.pcm"
#else
    #define FILENAME "/Users/mj/Desktop/out.pcm"
#endif

1.5.2 中心代码

#include <QFile>
// 文件
QFile file(FILENAME);
// WriteOnly:只写形式。假如文件不存在,就创立文件;假如文件存在,就删去文件内容
if (!file.open(QFile::WriteOnly)) {
    qDebug() << "文件翻开失利" << FILENAME;
    // 封闭设备
    avformat_close_input(&ctx);
    return;
}
// 暂时假定只收集50个数据包
int count = 50;
// 数据包
AVPacket *pkt = av_packet_alloc();
while (count-- > 0) {
    // 从设备中收集数据,返回值为0,代表收集数据成功
    ret = av_read_frame(ctx, pkt);
    if (ret == 0) { // 读取成功
        // 将数据写入文件
        file.write((const char *) pkt->data, pkt->size);
        // 开释资源
        av_packet_unref(pkt);
    } else if (ret == AVERROR(EAGAIN)) { // 资源暂时不可用
        continue;
    } else { // 其他过错
        char errbuf[1024];
        av_strerror(ret, errbuf, sizeof (errbuf));
        qDebug() << "av_read_frame error" << errbuf << ret;
        break;
    }
}

1.6 开释资源

// 封闭文件
file.close();
// 开释资源
av_packet_free(&pkt);
// 封闭设备
avformat_close_input(&ctx);

想要了解每一个函数的详细效果,能够查询:官方API文档。

1.7 获取录音设备的相关参数

// 从AVFormatContext中获取录音设备的相关参数
void showSpec(AVFormatContext *ctx) {
    // 获取输入流
    AVStream *stream = ctx->streams[0];
    // 获取音频参数
    AVCodecParameters *params = stream->codecpar;
    // 声道数
    qDebug() << params->channels;
    // 采样率
    qDebug() << params->sample_rate;
    // 采样格局
    qDebug() << params->format;
    // 每一个样本的一个声道占用多少个字节
    qDebug() << av_get_bytes_per_sample((AVSampleFormat) params->format);
    // 编码ID(能够看出采样格局)
    qDebug() << params->codec_id;
    // 每一个样本的一个声道占用多少位(这个函数需求用到avcodec库)
    qDebug() << av_get_bits_per_sample(params->codec_id);
}

2. 多线程

录音归于耗时操作,为了避免阻塞主线程,最好在子线程中进行录音操作。这儿创立了一个承继自QThread的线程类,线程一旦启动(start),就会自动调用 run 函数。

2.1 .h

#include <QThread>
class AudioThread : public QThread {
    Q_OBJECT
private:
    void run();
public:
    explicit AudioThread(QObject *parent = nullptr);
    ~AudioThread();
};

2.2 .cpp

AudioThread::AudioThread(QObject *parent,
                         AVInputFormat *fmt,
                         const char *deviceName)
    : QThread(parent), _fmt(fmt), _deviceName(deviceName) {
    // 在线程完毕时自动收回线程的内存
    connect(this, &AudioThread::finished,
            this, &AudioThread::deleteLater);
}
AudioThread::~AudioThread() {
    // 线程目标的内存收回时,正常完毕线程
    requestInterruption();
    quit();
    wait();
}
void AudioThread::run() {
    // 录音操作
    // ...
}

2.3 敞开线程

AudioThread *audioThread = new AudioThread(this);
audioThread->start();

2.4 完毕线程

// 外部调用线程的requestInterruption,请求完毕线程
audioThread->requestInterruption();
// 线程内部的逻辑
void AudioThread::run() {
    // 能够经过isInterruptionRequested判断是否要完毕线程
    // 当调用过线程的requestInterruption时,isInterruptionRequested返回值就为true,否则为false
    while (!isInterruptionRequested()) {
    	// ...
    }
}

2.5 改造录音代码

// 数据包
AVPacket *pkt = av_packet_alloc();
while (!isInterruptionRequested()) {
    // 从设备中收集数据,返回值为0,代表收集数据成功
    ret = av_read_frame(ctx, pkt);
    if (ret == 0) { // 读取成功
        // 将数据写入文件
        file.write((const char *) pkt->data, pkt->size);
        // 开释资源
        av_packet_unref(pkt);
    } else if (ret == AVERROR(EAGAIN)) { // 资源暂时不可用
        continue;
    } else { // 其他过错
        char errbuf[1024];
        av_strerror(ret, errbuf, sizeof (errbuf));
        qDebug() << "av_read_frame error" << errbuf << ret;
        break;
    }
}

专题系列文章

1. 前常识

  • 01-探求iOS底层原理|综述
  • 02-探求iOS底层原理|编译器LLVM项目【Clang、SwiftC、优化器、LLVM】
  • 03-探求iOS底层原理|LLDB
  • 04-探求iOS底层原理|ARM64汇编

2. 根据OC言语探求iOS底层原理

  • 05-探求iOS底层原理|OC的实质
  • 06-探求iOS底层原理|OC目标的实质
  • 07-探求iOS底层原理|几种OC目标【实例目标、类目标、元类】、目标的isa指针、superclass、目标的办法调用、Class的底层实质
  • 08-探求iOS底层原理|Category底层结构、App启动时Class与Category装载过程、load 和 initialize 履行、关联目标
  • 09-探求iOS底层原理|KVO
  • 10-探求iOS底层原理|KVC
  • 11-探求iOS底层原理|探求Block的实质|【Block的数据类型(实质)与内存布局、变量捕获、Block的种类、内存办理、Block的修饰符、循环引用】
  • 12-探求iOS底层原理|Runtime1【isa详解、class的结构、办法缓存cache_t】
  • 13-探求iOS底层原理|Runtime2【消息处理(发送、转发)&&动态办法解析、super的实质】
  • 14-探求iOS底层原理|Runtime3【Runtime的相关应用】
  • 15-探求iOS底层原理|RunLoop【两种RunloopMode、RunLoopMode中的Source0、Source1、Timer、Observer】
  • 16-探求iOS底层原理|RunLoop的应用
  • 17-探求iOS底层原理|多线程技能的底层原理【GCD源码分析1:主行列、串行行列&&并行行列、大局并发行列】
  • 18-探求iOS底层原理|多线程技能【GCD源码分析1:dispatch_get_global_queue与dispatch_(a)sync、单例、线程死锁】
  • 19-探求iOS底层原理|多线程技能【GCD源码分析2:栅栏函数dispatch_barrier_(a)sync、信号量dispatch_semaphore】
  • 20-探求iOS底层原理|多线程技能【GCD源码分析3:线程调度组dispatch_group、事件源dispatch Source】
  • 21-探求iOS底层原理|多线程技能【线程锁:自旋锁、互斥锁、递归锁】
  • 22-探求iOS底层原理|多线程技能【原子锁atomic、gcd Timer、NSTimer、CADisplayLink】
  • 23-探求iOS底层原理|内存办理【Mach-O文件、Tagged Pointer、目标的内存办理、copy、引用计数、weak指针、autorelease

3. 根据Swift言语探求iOS底层原理

关于函数枚举可选项结构体闭包特点办法swift多态原理StringArrayDictionary引用计数MetaData等Swift根本语法和相关的底层原理文章有如下几篇:

  • 01-Swift5常用中心语法|了解Swift【Swift简介、Swift的版本、Swift编译原理】
  • 02-Swift5常用中心语法|根底语法【Playground、常量与变量、常见数据类型、字面量、元组、流程操控、函数、枚举、可选项、guard语句、区间】
  • 03-Swift5常用中心语法|面向目标【闭包、结构体、类、枚举】
  • 04-Swift5常用中心语法|面向目标【特点、inout、类型特点、单例形式、办法、下标、承继、初始化】
  • 05-Swift5常用中心语法|高档语法【可选链、协议、过错处理、泛型、String与Array、高档运算符、扩展、拜访操控、内存办理、字面量、形式匹配】
  • 06-Swift5常用中心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

4. C++中心语法

  • 01-C++中心语法|C++概述【C++简介、C++来源、可移植性和标准、为什么C++会成功、从一个简略的程序开端知道C++】
  • 02-C++中心语法|C++对C的扩展【::效果域运算符、姓名操控、struct类型加强、C/C++中的const、引用(reference)、函数】
  • 03-C++中心语法|面向目标1【 C++编程标准、类和目标、面向目标程序设计事例、目标的构造和析构、C++面向目标模型初探】
  • 04-C++中心语法|面向目标2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转化、 C++标准、过错&&异常、智能指针】
  • 05-C++中心语法|面向目标3【 承继和派生、多态、静态成员、const成员、引用类型成员、VS的内存窗口】

5. Vue全家桶

  • 01-Vue全家桶中心常识|Vue根底【Vue概述、Vue根本运用、Vue模板语法、根底事例、Vue常用特性、归纳事例】
  • 02-Vue全家桶中心常识|Vue常用特性【表单操作、自界说指令、计算特点、侦听器、过滤器、生命周期、归纳事例】
  • 03-Vue全家桶中心常识|组件化开发【组件化开发思想、组件注册、Vue调试工具用法、组件间数据交互、组件插槽、根据组件的
  • 04-Vue全家桶中心常识|多线程与网络【前后端交互形式、promise用法、fetch、axios、归纳事例】
  • 05-Vue全家桶中心常识|Vue Router【根本运用、嵌套路由、动态路由匹配、命名路由、编程式导航、根据vue-router的事例】
  • 06-Vue全家桶中心常识|前端工程化【模块化相关标准、webpack、Vue 单文件组件、Vue 脚手架、Element-UI 的根本运用】
  • 07-Vue全家桶中心常识|Vuex【Vuex的根本运用、Vuex中的中心特性、vuex事例】

6. 音视频技能中心常识

  • 01-音视频技能中心常识|了解音频技能【移动通讯技能的发展、声响的实质、深化了解音频】
  • 02-音视频技能中心常识|建立开发环境【FFmpeg与Qt、Windows开发环境建立、Mac开发环境建立、Qt开发根底】
  • 03-音视频技能中心常识|Qt开发根底【.pro文件的装备、Qt控件根底、信号与槽】
  • 04-音视频技能中心常识|音频录制【指令行、C++编程】
  • 05-音视频技能中心常识|音频播映【播映PCM、WAV、PCM转WAV、PCM转WAV、播映WAV】
  • 06-音视频技能中心常识|音频重采样【音频重采样简介、用指令行进行重采样、经过编程重采样】
  • 07-音视频技能中心常识|AAC编码【AAC编码器解码器、编译FFmpeg、AAC编码实战、AAC解码实战】
  • 08-音视频技能中心常识|成像技能【重识图片、详解YUV、视频录制、显现BMP图片、显现YUV图片】
  • 09-音视频技能中心常识|视频编码解码【了解H.264编码、H.264编码、H.264编码解码】
  • 10-音视频技能中心常识|RTMP服务器建立【流媒体、服务器环境】

其它底层原理专题

1. 底层原理相关专题

  • 01-计算机原理|计算机图形烘托原理这篇文章
  • 02-计算机原理|移动终端屏幕成像与卡顿

2. iOS相关专题

  • 01-iOS底层原理|iOS的各个烘托结构以及iOS图层烘托原理
  • 02-iOS底层原理|iOS动画烘托原理
  • 03-iOS底层原理|iOS OffScreen Rendering 离屏烘托原理
  • 04-iOS底层原理|因CPU、GPU资源消耗导致卡顿的原因和解决计划

3. webApp相关专题

  • 01-Web和类RN大前端的烘托原理

4. 跨渠道开发计划相关专题

5. 阶段性总结:Native、WebApp、跨渠道开发三种计划功能比较

  • 01-Native、WebApp、跨渠道开发三种计划功能比较

6. Android、HarmonyOS页面烘托专题

  • 01-Android页面烘托原理
  • 02-HarmonyOS页面烘托原理 (待输出)

7. 小程序页面烘托专题

  • 01-小程序结构烘托原理