终于要开始进行FFmpeg实战了,一起来感受一下FFmpeg的强大吧。
命令简介
FFmpeg的bin目录中提供了3个命令(可执行程序),可以直接在命令行上使用。
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
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
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
hide_bannder
增加-hide_bannder参数可以隐藏一些冗余的描述信息,可以去实践比较以下2条命令的区别:
ffprobe xx.mp3
ffprobe -hide_banner xx.mp3
# ffmpeg、ffprobe、ffplay都适用
通过命令行录音
查看可用设备
使用命令行查看当前平台的可用设备:
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
查看dshow支持的设备
# 查看dshow支持的设备
ffmpeg -list_devices true -f dshow -i dummy
# 或者
# ffmpeg -list_devices true -f dshow -i ''
# ffmpeg -list_devices true -f dshow -i ""
-
-list_devices true
- 打印出所有的设备
-
-f dshow
- dshow支持的
-
-i dummy 或 -i '' 或 -i ""
- 立即退出
我的笔记本外接了一只麦克风。
因此,命令的执行结果大致如下所示:
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):笔记本自带的麦克风
查看avfoundation支持的设备
在Mac平台,使用的是avfoundation,而不是dshow。
ffmpeg -list_devices true -f avfoudation -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。
指定设备进行录音
# 使用外接的麦克风进行录音,最后生成一个wav文件
ffmpeg -f dshow -i audio="麦克风阵列 (Realtek(R) Audio)" out.wav
# 在Mac上通过编号指定设备
ffmpeg -f avfoundation -i :0 out.wav
# :0表示使用0号音频设备
# 0:2表示使用0号视频设备和2号音频设备
- 可以使用快捷键Ctrl + C终止录音
- 我这边的测试结果显示,默认的音频参数是:
- Windows:44100Hz采样率、16位深度、2声道、1411Kbps比特率
- Mac:48000Hz采样率、16位深度、2声道、1536Kbps比特率
设置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 -list_options true -f dshow -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
通过编程录音
主要步骤
开发录音功能的主要步骤是:
- 注册设备
- 查找输入格式
- 打开设备
- 读取数据
- 释放资源
以后的开发过程中,经常需要查询的资料:官方API文档。
AudioThread
录音是个耗时操作,因此最好放到一个子线程中进行。这里创建了一个继承自QThread的线程类,线程一旦启动(start),就会自动调用run函数。
- AudioThread.h
#include <QThread>
class AudioThread : public QThread
{
Q_OBJECT
private:
void run();
public:
explicit AudioThread(QObject *parent = nullptr);
~AudioThread();
};
- AudioThread.cpp
#include "audiothread.h"
#include <QDebug>
#include <QFile>
extern "C" {
// 编解码相关API
#include <libavcodec/avcodec.h>
// 格式相关API
#include <libavformat/avformat.h>
// 工具API(比如错误处理)
#include <libavutil/avutil.h>
}
AudioThread::AudioThread(QObject *parent) : QThread(parent)
{
// 在线程结束时自动回收线程的内存
connect(this, &AudioThread::finished,
this, &AudioThread::deleteLater);
}
AudioThread::~AudioThread()
{
// 线程销毁时,正常退出线程
quit();
requestInterruption();
wait();
}
void AudioThread::run()
{
// 格式名称
const char *fmtName;
#if defined (Q_OS_WIN)
fmtName = "dshow";
#else
fmtName = "avfoundation";
#endif
// 根据名称找到输入格式对象
AVInputFormat *fmt = av_find_input_format(fmtName);
if (fmt == NULL) {
qDebug() << "找不到输入格式:" << fmtName;
return;
}
// 设备名称
const char *deviceName;
#if defined (Q_OS_WIN)
deviceName = "audio=麦克风阵列 (Realtek(R) Audio)";
#else
deviceName = ":0";
#endif
// 格式上下文
AVFormatContext *ctx = NULL;
// 选项
AVDictionary *options = NULL;
// 打开输入流
int ret = avformat_open_input(&ctx, deviceName, fmt, &options);
// 如果打开输入流失败
if (ret < 0) {
char errbuf[1024] = {0};
av_strerror(ret, errbuf, sizeof(errbuf));
qDebug() << "打开输入流失败:" << errbuf;
return;
}
// 定义文件
QFile file("F:/out.pcm");
// 删除旧文件
if (file.exists()) {
file.remove();
}
// 打开文件
if (!file.open(QIODevice::WriteOnly)){
qDebug() << "文件打开失败";
// 关闭输入流
avformat_close_input(&ctx);
return;
}
// 数据包
AVPacket pkt;
while (!isInterruptionRequested()
&& av_read_frame(ctx, &pkt) == 0) {
// 写入数据
file.write((char *) pkt.data, pkt.size);
}
// 关闭文件
file.close();
// 关闭输入流
avformat_close_input(&ctx);
}
MainWindow
在MainWindow控制开始、结束录音。
- MainWindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_audioBtn_clicked();
private:
Ui::MainWindow *ui;
AudioThread *_audioThread = nullptr;
};
- MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include "audiothread.h"
extern "C" {
// 设备相关API
#include <libavdevice/avdevice.h>
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 初始化libavdevice并注册所有输入和输出设备
avdevice_register_all();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_audioBtn_clicked()
{
// 没有线程
if (_audioThread == nullptr) {
// 开启新线程
_audioThread = new AudioThread(this);
_audioThread->start();
// 设置按钮文字
ui->audioBtn->setText("停止录音");
} else {
// 停止录音
_audioThread->requestInterruption();
_audioThread = nullptr;
// 设置按钮文字
ui->audioBtn->setText("开始录音");
}
}
播放PCM
ffplay -ar 44100 -ac 2 -f s16le out.pcm