音视频录制的多线程实现
主要类
- AudioRecorder:音频采集线程
- VideoRecorder:视频采集线程
- MainWindow:控制音频和视频的采集
AudioRecorder 类详解
头文件部分
-
构造函数:
1
AudioRecorder(QObject *parent = nullptr, AVFormatContext **ofmt_ctx = nullptr);
-
主要变量:
bool flage = false;// 用于控制采集的开始与结束AVFormatContext *fmt_ctx = NULL;// 输入上下文AVFormatContext **ofmt_ctx = NULL;// 输出上下文QObject *parent = NULL;// 用于回调函数
源文件部分
采集音频的具体步骤可以分为10个步骤,使用到的变量有:
AVPacket pkt;:音频包int nb_samples = 22050;:每帧样本数SwrContext *swr_ctx = nullptr;:重采样上下文uint8_t **src_data = nullptr; uint8_t **dst_data = nullptr;:音频数据缓冲区AVCodecContext *codec_ctx = nullptr;:编码上下文AVCodecContext *dec_ctx = nullptr;:解码上下文AVFrame *frame = av_frame_alloc();:音频帧AVAudioFifo* fifo = nullptr;:音频FIFO缓冲区const char* outFilename = "output.mp4";:输出文件名
音频采集步骤
-
打开设备
1
open_device(&fmt_ctx);
-
查找并打开音频解码器
1
open_Audio_decoder(fmt_ctx, &dec_ctx);
-
打开AAC编码器
1
open_encoder(&codec_ctx);
-
建立FIFO缓冲区
1
fifo = init_audio_fifo(codec_ctx->sample_fmt, codec->ch_layout.nb_channels);
-
配置重采样上下文(如果需要,根据编码器的输入要求)
1
init_resampler(codec_ctx, &swr_ctx, &src_data, &dst_data);
-
创建输出流
1
AVStream *outStream = avformat_new_stream(*ofmt_ctx, NULL);
-
写头文件(通过回调方式)
为了确保音频流和视频流都创建好后再写入头文件,我采用了回调函数的方式。FFmpeg在写入头文件时要求所有流都已经创建好,因此我们通过回调机制确保音频流和视频流的创建顺序。
回调函数的实现
在代码中,我们定义了一个回调函数别名,并将其与MainWindow的Main_avformat_write_header方法绑定,确保在合适的时机调用该函数来写入头文件。
1 | // 定义回调函数别名,绑定到 MainWindow::Main_avformat_write_header 方法 |
解释
std::bind:该方法将MainWindow::Main_avformat_write_header函数与parent(MainWindow)对象绑定,并传入必要的参数。- 通过回调的方式,只有在音频和视频流都准备好后才会调用
Main_avformat_write_header方法进行头文件写入,确保FFmpeg正确地写入头文件。
为什么要使用回调
FFmpeg的avformat_write_header()函数要求在写入文件头之前,所有流都必须被创建并初始化。因此,在创建音频和视频流后,我们通过回调函数来确保头文件的写入时机是正确的。
- 配置解码后数据的frame容器及音频帧
1 | //获取frame_size |
-
用
av_read_frame()读取音频数据并写入文件 -
释放分配的空间
1 | // 释放缓冲区的数据和相关上下文 |
open_device(&fmt_ctx) 的具体实现
1 | int AudioRecorder::open_device(AVFormatContext **fmt_ctx) { |
open_Audio_decoder() 的具体实现
1 | int AudioRecorder::open_Audio_decoder(AVFormatContext **fmt_ctx) { |
open_encoder() 的具体实现
1 | int AudioRecorder::open_encoder(AVCodecContext **codec_ctx) { |
FIFO缓冲区实现
1 | AVAudioFifo* AudioRecorder::init_audio_fifo(AVSampleFormat sample_fmt, int channels) { |
init_resampler() 的具体实现
1 | int AudioRecorder::init_resampler(AVCodecContext *codec_ctx, SwrContext **swr_ctx, uint8_t ***src_data, uint8_t ***dst_data) { |
音频数据读取与写入文件过程
1 | //开始获取音频帧数据,并进行转换 |
说些什么吧!