FFmpeg基础:获取音视频的各种编码参数

Posted 码农飞飞

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FFmpeg基础:获取音视频的各种编码参数相关的知识,希望对你有一定的参考价值。

文章目录


上一篇文章中介绍了音视频的各种编码参数的概念,这里介绍一下如何通过ffmpeg库获取一个视频文件的各种音视频编码参数。在对视频文件进行处理和转码的时候这些参数很重要。

获取视频编码参数

视频编码参数主要包括:帧率、分辨率、编码格式、码率等,对应的概念如下。
帧率(Frame Rate)
每秒显示帧数(Frames Per Second)。电影的帧率一般是25fps和29.97fps,3D游戏要保持流畅则需要30fps以上的效果。

分辨率
指视频宽高的像素数值。标准1080P的分辨率为1920×1080,帧率为60fps,也就是真高清。而最常见的网络传播的1080P高清片帧率通常为 23.976 fps。

封装格式
多媒体封装格式也称多媒体容器 (Multimedia Container),它不同于H.264、 AAC这类编码格式,它只是为多媒体编码提供了一个“外壳”,也就是所谓的视频格式。如MP4、AVI、MKV、FLV、WMA等。

码率(Bit Rate)
指视频或音频文件在单位时间内使用的数据流量(单位通常是Kbps也就是千比特每秒)。通常2000kbps~3000kbps就已经足以将画质效果表现到极致了。码率参数与视频文件最终体积大小有直接性的关系。

编码格式
所谓视频编码方式就是指通过压缩技术,将原始视频格式的文件转换成另一种视频格式文件的方式。视频流传输中最为重要的编解码标准有国际电联的H.261、H.263、H.264。

通过ffmpeg获取视频编码参数的方法如下所示:

#include "libavutil/log.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"

//按照固定格式打印
static void print_fps(double d, const char *postfix)

	uint64_t v = lrintf(d * 100);
	if (!v)
		av_log(NULL, AV_LOG_INFO, "%1.4f %s", d, postfix);
	else if (v % 100)
		av_log(NULL, AV_LOG_INFO, "%3.2f %s", d, postfix);
	else if (v % (100 * 1000))
		av_log(NULL, AV_LOG_INFO, "%1.0f %s", d, postfix);
	else
		av_log(NULL, AV_LOG_INFO, "%1.0fk %s", d / 1000, postfix);


int main(int argc, char* argv[])

	int ret;
	int streams;
	char buf[256];
	AVFormatContext *fmt_ctx = NULL;
	AVCodecContext *avctx = NULL;
	const AVInputFormat *fmt = NULL;
	
	//设置日志的输出级别
	av_log_set_level(AV_LOG_INFO);
	
	//打开视频的上下文
	//@1音视频上下文 
	//@2文件路径 
	//@3文件格式(不指定根据文件名判断)
	//@4获取配置项的字典
	ret = avformat_open_input(&fmt_ctx, "./test.mp4", fmt, NULL);
	if (ret < 0)
	
		av_log(NULL, AV_LOG_ERROR, "Can't open file:%s\\n", av_err2str(ret));
		return -1;
	

	//获取封装格式
	const char* format_name = fmt_ctx->iformat->name;

	//通过上下文获取视频时长
	if (fmt_ctx->duration != AV_NOPTS_VALUE)
	
		int64_t hours, mins, secs, us;
		int64_t duration = fmt_ctx->duration + (fmt_ctx->duration <= INT64_MAX - 5000 ? 5000 : 0);
		secs = duration / AV_TIME_BASE;
		us = duration % AV_TIME_BASE;
		mins = secs / 60;
		secs %= 60;
		hours = mins / 60;
		mins %= 60;
		av_log(NULL, AV_LOG_INFO, "duration:  %02"PRId64":%02"PRId64":%02"PRId64".%02"PRId64"\\n", hours, mins, secs,
			(100 * us) / AV_TIME_BASE);
	
	else 
	
		av_log(NULL, AV_LOG_INFO, "N/A");
	

	//查看视频文件中流的个数
	streams = fmt_ctx->nb_streams;
	av_log(NULL, AV_LOG_INFO, "file has:%d streams\\n",streams);

	//查看流的类型,判断是视频流还是音频流
	for (int index = 0; index < streams; ++index)
	
		const AVStream *stream = fmt_ctx->streams[index];
		if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
		
			av_log(NULL, AV_LOG_INFO, "streams %d is Video Stream\\n",index);
		
		else if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
		
			av_log(NULL, AV_LOG_INFO, "streams %d is Audio Stream\\n",index);
		
	

	//获取视频帧率fps
	const AVStream *stream = fmt_ctx->streams[0];
	double fps = av_q2d(stream->avg_frame_rate);
	print_fps(fps, "fps\\n");


	//获取流的类型名称(video/audio)
	avctx = avcodec_alloc_context3(NULL);
	ret = avcodec_parameters_to_context(avctx, stream->codecpar);
	const char* codec_type = av_get_media_type_string(avctx->codec_type);

	//获取视频编码类型(h264/h265)
	const char*codec_name = avcodec_get_name(avctx->codec_id);
	av_log(NULL, AV_LOG_INFO, "stream type:%s, stream codec:%s\\n", codec_type,codec_name);

	//获得视频的码率
	int bitrate = avctx->bit_rate;
	av_log(NULL, AV_LOG_INFO,"%"PRId64" kb/s\\n", bitrate / 1000);

	//获取视频的分辨率
	int width = avctx->width;
	int height = avctx->height;
	av_log(NULL, AV_LOG_INFO, "%dx%d\\n", width, height);
	avcodec_free_context(&avctx);

	//关闭上下文
	avformat_close_input(&fmt_ctx);
	getchar();

获取音频编码参数

音频编码参数主要包括:声道数、码率、采样率、采样位数等,对应的概念如下。
声道数
现在主要有单声道和双声道之分。双声道又称为立体声,在硬件中要占两条线路,音质、音色好,但立体声数字化后所占空间比单声道多一倍。

码率
比特率也叫码率,指音乐每秒播放的数据量,单位用bit表示。一般mp3在128比特率左右为益。

采样率
指每秒钟取得声音样本的次数,22050 的采样频率是常用的,44100已是CD音质。为了保证声音不失真,采样频率应该在40kHz左右。

采样位数
采样位数也叫采样大小或量化位数。它是用来衡量声音波动变化的一个参数,也就是声卡的分辨率或可以理解为声卡处理声音的解析度。它的数值越大,分辨率也就越高,常见的声卡主要有8位和16位两种。

通过ffmpeg获取音频编码参数方法如下所示:

#include "libavutil/log.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"

//按照固定格式打印
static void print_fps(double d, const char *postfix)

	uint64_t v = lrintf(d * 100);
	if (!v)
		av_log(NULL, AV_LOG_INFO, "%1.4f %s", d, postfix);
	else if (v % 100)
		av_log(NULL, AV_LOG_INFO, "%3.2f %s", d, postfix);
	else if (v % (100 * 1000))
		av_log(NULL, AV_LOG_INFO, "%1.0f %s", d, postfix);
	else
		av_log(NULL, AV_LOG_INFO, "%1.0fk %s", d / 1000, postfix);


int main(int argc, char* argv[])

	int ret;
	int streams;
	char buf[256];
	AVFormatContext *fmt_ctx = NULL;
	AVCodecContext *avctx = NULL;
	const AVInputFormat *fmt = NULL;
	
	//设置日志的输出级别
	av_log_set_level(AV_LOG_INFO);
	
	//打开视频的上下文
	//@1音视频上下文 
	//@2文件路径 
	//@3文件格式(不指定根据文件名判断)
	//@4获取配置项的字典
	ret = avformat_open_input(&fmt_ctx, "./test.mp4", fmt, NULL);
	if (ret < 0)
	
		av_log(NULL, AV_LOG_ERROR, "Can't open file:%s\\n", av_err2str(ret));
		return -1;
	

	//获取封装格式
	const char* format_name = fmt_ctx->iformat->name;

	//通过上下文获取视频时长
	if (fmt_ctx->duration != AV_NOPTS_VALUE)
	
		int64_t hours, mins, secs, us;
		int64_t duration = fmt_ctx->duration + (fmt_ctx->duration <= INT64_MAX - 5000 ? 5000 : 0);
		secs = duration / AV_TIME_BASE;
		us = duration % AV_TIME_BASE;
		mins = secs / 60;
		secs %= 60;
		hours = mins / 60;
		mins %= 60;
		av_log(NULL, AV_LOG_INFO, "duration:  %02"PRId64":%02"PRId64":%02"PRId64".%02"PRId64"\\n", hours, mins, secs,
			(100 * us) / AV_TIME_BASE);
	
	else 
	
		av_log(NULL, AV_LOG_INFO, "N/A");
	

	//查看视频文件中流的个数
	streams = fmt_ctx->nb_streams;
	av_log(NULL, AV_LOG_INFO, "file has:%d streams\\n",streams);

	//查看流的类型,判断是视频流还是音频流
	for (int index = 0; index < streams; ++index)
	
		const AVStream *stream = fmt_ctx->streams[index];
		if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
		
			av_log(NULL, AV_LOG_INFO, "streams %d is Video Stream\\n",index);
		
		else if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
		
			av_log(NULL, AV_LOG_INFO, "streams %d is Audio Stream\\n",index);
		
	
	//获取音频流的类型名称(video/audio)
	const AVStream *audio_stream = fmt_ctx->streams[1];
	avctx = avcodec_alloc_context3(NULL);
	ret = avcodec_parameters_to_context(avctx, audio_stream->codecpar);
	const char* audio_codec_type = av_get_media_type_string(avctx->codec_type);

	//获取音频流的编码类型
	const char*audio_codec_name = avcodec_get_name(avctx->codec_id);
	av_log(NULL, AV_LOG_INFO, "audio stream type:%s, stream codec:%s\\n", audio_codec_type, audio_codec_name);

	//获得音频流的码率
	int audio_bitrate = avctx->bit_rate;
	av_log(NULL, AV_LOG_INFO, "%"PRId64" kb/s\\n", audio_bitrate / 1000);

	//获得音频的采样率
	int sample_rate = avctx->sample_rate;
	av_log(NULL, AV_LOG_INFO, "%dHZ\\n",sample_rate);

	//采样位数
	int sample_bits = avctx->bits_per_coded_sample;
	av_log(NULL, AV_LOG_INFO, "%d\\n", sample_bits);

	//获取音频的通道数量
	char channel_buf[512];
	ret = av_channel_layout_describe(&avctx->ch_layout, channel_buf, sizeof(channel_buf));
	avcodec_free_context(&avctx);

	//关闭上下文
	avformat_close_input(&fmt_ctx);
	getchar();

除了上面的方法之外,ffmpeg还提供了一个直接输出视频文件编码参数的接口,该接口可以直接输出音视频信息到终端,对应的接口调用方法如下:

#include "libavutil/log.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"

int main(int argc, char* argv[])

	int ret;
	int streams;
	char buf[256];
	AVFormatContext *fmt_ctx = NULL;
	AVCodecContext *avctx = NULL;
	const AVInputFormat *fmt = NULL;
	
	//设置日志的输出级别
	av_log_set_level(AV_LOG_INFO);
	
	//打开视频的上下文
	//@1音视频上下文 
	//@2文件路径 
	//@3文件格式(不指定根据文件名判断)
	//@4获取配置项的字典
	ret = avformat_open_input(&fmt_ctx, "./test.mp4", fmt, NULL);
	if (ret < 0)
	
		av_log(NULL, AV_LOG_ERROR, "Can't open file:%s\\n", av_err2str(ret));
		return -1;
	
	//输出视频信息参数
	//@1上下文
	//@2文件索引
	//@3文件路径
	//@4输入流还是输出流
	av_dump_format(fmt_ctx, 0, "./test.mp4", 0);
	//关闭上下文
	avformat_close_input(&fmt_ctx);
	getchar();

输出结果如下所示:

FFmpeg-视频编码-YUV编码出H264

参考技术A

编码出来的h264数据可以直接使⽤ffplay播放

int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align);

函数的作⽤是通过指定像素格式、图像宽、图像⾼来计算所需的内存⼤⼩

重点说明⼀个参数 align :此参数是设定内存对⻬的对⻬数,也就是按多⼤的字节进⾏内存对⻬:

av_image_alloc()是这样定义的。此函数的功能是按照指定的宽、⾼、像素格式来 分配图像内存

int av_image_alloc(uint8_t *pointers[4], int linesizes[4], int w, int h, enum AVPixelFormat pix_fmt, int align);

int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4], const uint8_t *src, enum AVPixelFormat pix_fmt, int width, int height, int align);

av_image_fill_arrays()函数 ⾃身不具备内存申请的功能 ,此函数类似于格式化已经申请的内存,即通过 av_malloc()函数申请的内存空间,或者av_frame_get_buffer()函数申请的内存空间。

av_image_fill_arrays()中 参数具体说明

视频码率是视频数据(包含视频⾊彩量、亮度量、像素量)每秒输出的位数。⼀般⽤的单位是kbps。

在视频会议应用中,视频质量和网络带宽占用是矛盾的,通常情况下视频流占用的带宽越高则视频质量也越高;如要求高质量的视频效果,那么需要的网络带宽也越大;解决这一矛盾的钥匙当然是视频编解码技术。评判一种视频编解码技术的优劣,是比较在相同的带宽条件下,哪个视频质量更好;在相同的视频质量条件下,哪个占用的网络带宽更少。
是不是视频码率越高,质量越好呢?理论上是这样的,然而在我们肉眼分辨的范围内,当码率高到一定程度,感觉没有什么差别。所以码率设置有它的最优值,H.264(也叫AVC或X.264)的文档中,视频的建议码率如下:

鉴于x264的参数众多,各种参数的配合复杂,为了使⽤者⽅便,x264建议如⽆特别需要可使 ⽤preset和tune设置。这套开发者推荐的参数较为合理,可在此基础上在调整⼀些具体参数以符合⾃⼰需要,⼿动设定的参数会覆盖preset和tune⾥的参数。

使⽤ ffmpeg -h encoder=libx264 命令查询相关⽀持的参数

x264是⼀个 H.264/MPEG4 AVC 编码器,本指南将指导新⼿如何创建⾼质量的H.264视频。 对于普通⽤户通常有两种码率控制模式:CRF(Constant Rate Factor)和Two pass ABR。码率控制是⼀种决定为每⼀个视频帧分配多少⽐特数的⽅法,它将决定⽂件的⼤⼩和质量的分配。

如果你在编译和安装libx264 ⽅⾯需要帮助,请查看ffmpeg和x264编译指南: http://ffmpeg.org/trac/ffmpeg/wiki/CompilationGuide

量化⽐例的范围为0~51,其中0为⽆损模式, 23 为缺省值,51可能是最差的。该数字越⼩,图像质量越 好。从主观上讲,18~28是⼀个合理的范围。18往往被认为从视觉上看是⽆损的,它的输出视频质量和输 ⼊视频⼀模⼀样或者说相差⽆⼏。但从技术的⻆度来讲,它依然是有损压缩。

若CRF值加6,输出码率⼤概减少⼀半;若CRF值减6,输出码率翻倍。通常是在保证可接受视频质量的前提下选择⼀个最⼤的CRF值,如果输出视频质量很好,那就尝试⼀个更⼤的值,如果看起来很糟,那就尝 试⼀个⼩⼀点值。

预设是⼀系列参数的集合,这个集合能够在编码速度和压缩率之间做出⼀个权衡。⼀个编码速度稍慢的预 设会提供更⾼的压缩效率(压缩效率是以⽂件⼤⼩来衡量的)。这就是说,假如你想得到⼀个指定⼤⼩的⽂ 件或者采⽤恒定⽐特率编码模式,你可以采⽤⼀个较慢的预设来获得更好的质量。同样的,对于恒定质量编码模式,你可以通过选择⼀个较慢的预设轻松地节省⽐特率。

如果你很有耐⼼,通常的建议是使⽤最慢的预设。⽬前所有的预设按照编码速度降序排列为:

tune是x264中重要性仅次于preset的选项,它是视觉优化的参数,tune可以理解为视频偏好(或者视频类型),tune不是⼀个单⼀的参数,⽽是由⼀组参数构成 -tune 来改变参数设置。当前的 tune包括:

如果你不确定使⽤哪个选项或者说你的输⼊与所有的tune皆不匹配,你可以忽略--tune 选项。 你可以使⽤-tune来查看tune列表,也可以通过x264 --fullhelp来查看tune所采⽤的参数配置。

另外⼀个可选的参数是-profile:v,它可以将你的输出限制到⼀个特定的 H.264 profile。⼀些⾮常⽼的或者 要被淘汰的设备仅⽀持有限的选项,⽐如只⽀持baseline或者main。

所有的profile 包括:

查找指定的编码器

初始化

设置编码器参数

将codec_ctx和codec进行绑定

分配pkt和frame

计算出每一帧的数据 像素格式 * 宽 * 高

读取YUV数据

格式化已经申请的内存,将YUV数据,格式化放入frame中

发送YUV数据进编码器

从编码器中获取encode的packet数据

encode的packet数据写入文件

以上是关于FFmpeg基础:获取音视频的各种编码参数的主要内容,如果未能解决你的问题,请参考以下文章

FFMpeg SDK使用3调用FFmpeg SDK实现视频编码

H264编码系列之ffmpeg和x264码率控制分析

音视频处理 ffmpeg中级开发 视频转图片

FFmpeg获取视频正确的宽高比

ffmpeg

ffmpeg 基本用法