FFmpeg介绍

本文介绍了FFmpeg,它是一套开源计算机程序,提供音视频录制、转换及流化解决方案。阐述了编解码基础知识,包括封装格式、编码格式,以及视频解码和音频解码的区别。还给出了代码实现步骤,最后提供了基于qt的FFmpeg客户端代码地址。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

FFmpeg介绍

一、ffmpeg简述

FFmpeg是一套可以用来记录转换数字音频视频,并能将其转化为的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。
框图如图所示:

二、编解码基础知识

(1)封装格式

所谓封装格式是指音视频的组合格式,例如最常见的封装格式有mp4mp3flv等。简单来说,我们平时接触到的带有后缀的音视频文件都是一种封装格式。不同的封装格式遵循不同的协议标准。有兴趣的可以自行扩展。

(2)编码格式

mp4为例,通常应该包含有视频和音频。视频的编码格式为YUV420P,音频的编码格式为PCM。再以YUV420编码格式为例。我们知道通常图像的显示为RGB(红绿蓝三原色),在视频压缩的时候会首先将代表每一帧画面的RGB压缩为YUV,再按照关键帧(I帧),过渡帧(P帧或B帧)进行运算和编码。解码的过程正好相反,解码器会读到I帧,并根据I帧运算和解码P帧以及B帧。并最终根据视频文件预设的FPS还原每一帧画面的RGB数据。最后推送给显卡。所以通常我们说的编码过程就包括:画面采集转码编码封装

(3)视频解码和音频解码有什么区别

FPS是图像领域中的定义,是指画面每秒传输帧数,通俗来讲就是指动画或视频的画面数。FPS太低画面会感觉闪烁不够连贯,FPS越高需要显卡性能越好。一些高速摄像机的采集速度能够达到11000帧/秒,那么在播放这类影片的时候我们是否也需要以11000帧/秒播放呢?当然不是,通常我们会按照25帧/秒或者60帧/秒设定图像的FPS值。但是由于视频存在关键帧过渡帧的区别,关键帧保存了完整的画面过渡帧只是保存了与前一帧画面的变化部分,需要通过关键帧计算获得。因此我们需要对每一帧都进行解码,即获取画面的YUV数据。同时只对我们真正需要显示的画面进行转码,即将YUV数据转换成RGB数据,包括计算画面的宽高等。

但是音频则不然,音频的播放必须和采集保持同步。提高或降低音频的播放速度都会让音质发生变化,这也是变声器的原理。因此在实际开发中为了保证播放的音视频同步,我们往往会按照音频的播放速度来控制视频的解码转码速度。

三、代码实现

(1)注册FFmpeg组件

//注册和初始化FFmpeg封装器和网络设备

av_register_all();
avformat_network_init();
avdevice_register_all();

(2)打开文件和创建输入设备

/*
AVFormatContext 表示一个封装器,
在读取多媒体文件的时候,它负责保存与封装和编解码有关的上下文信息。
avformat_open_input函数可以根据文件后缀名来创建封装器。
*/

AVFormatContext *pFormatCtx = NULL;
int errnum = avformat_open_input(&pFormatCtx, filename, NULL, NULL);
if (errnum < 0) {
	av_strerror(errnum, errbuf, sizeof(errbuf));
	cout << errbuf << endl;
}

(3)遍历流并初始化解码器

/*
封装器中保存了各种流媒体的通道,通常视频通道为0,音频通道为1。
除此以外可能还包含字幕流通道等。
第2步和第3步基本就是打开多媒体文件的主要步骤,
解码和转码的所有参数都可以在这里获取。
接下来我们就需要循环进行读取、解码、转码直到播放完成。
*/

for (int i = 0; i < pFormatCtx->nb_streams; ++i) {
	AVCodecContext *pCodecCtx = pFormatCtx->streams[i]->codec; // 解码器上下文
	if (pCodecCtx->codec_type == AVMEDIA_TYPE_VIDEO) {
		// 视频通道
		int videoIndex = i;
		// 视频的宽,高
		int srcWidth = pCodecCtx->width;
		int srcHeight = pCodecCtx->height;
		// 创建视频解码器,打开解码器
		AVCodec *codec = avcodec_find_decoder(pCodecCtx->codec_id);
		if (!codec) {
			// 无法创建对应的解码器
			errnum = avcodec_open2(pCodecCtx, codec, NULL);
			if (errnum < 0) {
				av_strerror(errnum, errbuf, sizeof(errbuf));
				cout << errbuf << endl;
			}
			cout << "video decoder open success!" << endl;
		}
	}
	if (pCodecCtx->codec_type == AVMEDIA_TYPE_AUDIO) {
		// 音频通道
		int audioIndex = i;        // 创建音频解码器,打开解码器
		AVCodec *codec = avcodec_find_decoder(pCodecCtx->codec_id);
		if (!codec) {
			// 无法创建对应的解码器
			errnum = avcodec_open2(pCodecCtx, codec, NULL);
			if (errnum < 0) {
				av_strerror(errnum, errbuf, sizeof(errbuf));
				cout << errbuf << endl;
			}
			int sampleRate = pCodecCtx->sample_rate; // 音频采样率
			int channels = pCodecCtx->channels; // 声道数
			AVSampleFormat fmt = pCodecCtx->sample_fmt; // 样本格式
			cout << "audio decoder open success!" << endl;
		}
	}
}

(4)读取压缩数据

/*之所以称为压缩数据主要是为了区分AVPacket和AVFrame两个结构体。
AVPacket表示一幅经过了关键帧或过渡帧编码后的画面,
AVFrame表示一个AVPacket经过解码后的完整YUV画面*/


AVPacket *pkt = NULL;
pkt = av_packet_alloc(); // 初始化AVPacket// 读取一帧数据
errnum = av_read_frame(pFormatCtx, pkt);
if (errnum == AVERROR_EOF) {    // 已经读取到文件尾
	av_strerror(errnum, errbuf, sizeof(errbuf));
	cout << errbuf << endl;
}
if (errnum < 0) {
	av_strerror(errnum, errbuf, sizeof(errbuf));
	cout << errbuf << endl;
}

(5)解码

errnum = avcodec_send_packet(pCodecCtx, pkt);
if (errnum < 0) {
	av_strerror(errnum, errbuf, sizeof(errbuf));
	cout << errbuf << endl;
}

AVFrame *yuv = av_frame_alloc();
AVFrame *pcm = av_frame_alloc();
if (pkt->stream_index == videoIndex) { // 判断当前解码帧为视频帧
	errnum = avcodec_receive_frame(pCodecCtx, yuv); // 解码视频
	if (errnum < 0) {
		av_strerror(errnum, errbuf, sizeof(errbuf));
		cout << errbuf << endl;
	}
}
if (pkt->stream_index == audioIndex) { // 判断当前解码帧为音频帧
	errnum = avcodec_receive_frame(pCodecCtx, pcm); // 解码音频
	if (errnum < 0) {
		av_strerror(errnum, errbuf, sizeof(errbuf));
		cout << errbuf << endl;
	}
}

(6)视频转码

// 720p输出标准
/*
这里需要解释一下outWidth * outHeight * 4计算理由:
720p标准的视频画面包含720 * 480个像素点,
每一个像素点包含了RGBA4类数据,每一类数据分别由1个byte即8个bit表示。
因此一幅完整画面所占的大小为outWidth * outHeight * 4。
*/
int outWidth = 720;
int outHeight = 480;
char *outData = new char[outWidth * outHeight * 4]

SwsContext *videoSwsCtx = NULL;
videoSwsCtx = sws_getCachedContext(videoSwsCtx, srcWidth, srcHeight, (AVPixelFormat)pixFmt, // 输入
                                   outWidth, outHeight, AV_PIX_FMT_BGRA, // 输出
                                   SWS_BICUBIC, // 算法
                                   0, 0, 0);// 分配数据空间
uint8_t *dstData[AV_NUM_DATA_POINTERS] = { 0 };
dstData[0] = (uint8_t *)outData;
int dstStride[AV_NUM_DATA_POINTERS] = { 0 };
dstStride[0] = outWidth * 4;
int h = sws_scale(videoSwsCtx, yuv->data, yuv->linesize, 0, srcHeight, dstData, dstStride);
if (h != outHeight) {   } // 转码失败

(7)音频转码

char *outData = new char[10000];//输出指针
AVCodecContext *pCodecCtx = pFormatCtx->streams[audioIndex]->codec; // 获取音频解码器上下文
SwrContext *audioSwrCtx = NULL;
audioSwrCtx = swr_alloc();
audioSwrCtx = swr_alloc_set_opts(audioSwrCtx,
                                 AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, 44100, // 输出参数:双通道立体声 CD音质
                                 pCodecCtx->channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, // 输入参数
                                 0, 0);
swr_init(audioSwrCtx);

uint8_t *out[AV_NUM_DATA_POINTERS] = { 0 };
out[0] = (uint8_t *)outData;// 计算输出空间
int dst_nb_samples = av_rescale_rnd(pcm->nb_samples, pCodecCtx->sample_rate, pCodecCtx->sample_rate, AV_ROUND_UP);
int len = swr_convert(audioSwrCtx,    out, dst_nb_samples,
                      (const uint8_t **)pcm->data, pcm->nb_samples);
int channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO); // AV_CH_LAYOUT_STEREO -> 2 根据声道类型得到声道数// 实际音频数据长度
int dst_bufsize = av_samples_get_buffer_size(NULL,
                  channels, // 通道数
                  pcm->nb_samples,// 1024
                  AV_SAMPLE_FMT_S16,    0);
if (dst_bufsize < 0) {    }// 音频转码错误

四、代码地址

基于qt的FFmpeg客户端(Linux版本):
https://github.com/wengmq/ffmpeg_demo

服务端可采用LIVE555服务器 ,参考博文:
https://blog.youkuaiyun.com/qq_37266079/article/details/90524579


参考来源:
https://blog.youkuaiyun.com/muyuyuzhong/article/details/79462310
https://www.imooc.com/article/details/id/28012

### FFmpeg 的基本信息 FFmpeg 是一个非常强大和灵活的开源工具集,用于处理音频和视频文件[^2]。该工具集不仅限于简单的文件转换,还涵盖了录制、转换、流式传输和播放音频和视频等多种功能。 #### 主要特点 跨平台性使得 FFmpeg 可以在多种操作系统上运行,包括但不限于 Linux、Windows 和 macOS 等,这赋予其广泛的应用范围[^1]。此外,FFmpeg 支持几乎所有常见的音视频格式和编解码器,如 MP4、AVI、FLV、MKV 以及 H.264、H.265、AAC 编解码器等,从而满足不同用户的多样化需求。 高效稳定性也是 FFmpeg 的一大亮点,在音视频处理方面表现尤为突出,能有效应对大量音视频数据的处理任务。与此同时,FFmpeg 提供了极其丰富的命令行选项,允许用户根据具体应用场景自定义复杂的音视频处理流程,极大地增强了使用的灵活性。 #### 使用场景 由于具备上述特性,FFmpeg 广泛应用于多个领域: - **视频格式转换**:通过特定命令实现不同类型之间的相互转换,例如将 AVI 文件转成 MP4 格式。 ```bash ffmpeg -i input.avi output.mp4 ``` - **实时屏幕捕捉与录制**:利用 `-f gdigrab` 参数可轻松完成桌面活动记录并保存为指定格式的视频文件[^4]。 ```bash ffmpeg -f gdigrab -framerate 30 -i desktop output.mp4 ``` - **音频信息查询及编辑**:不仅可以读取音频文件的相关元数据,还能执行诸如剪辑、混音等一系列操作来优化声音效果[^5]。 综上所述,无论是个人爱好者还是专业开发者都能借助 FFmpeg 实现高效的多媒体资源管理与创作工作。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值