视频编解码(一):ffmpeg编码H.264帧类型判断

2023-11-17

本文主要讲述ffmpeg编码过程中是如何设置I帧,B帧及P帧的,以及如何通过代码判断帧类型。

之前看过很多网上的文章,讲述如何判断I帧,B帧,P帧,然而都是停留在H.264官方文档中的定义,如果不结合ffmpeg,就仿佛纸上谈兵,有点不切实际,而且很多文章将I帧与I Slice混为一谈,将I Slice当做I帧,这其实是错的。本文就结合ffmpeg讲解ffmpeg中是如何编码各种帧类型的,并纠正其他一些文章的说法。

先有如下定义:

I帧:帧内预测

P帧:前向预测

B帧:前后双向预测

IDR图像:第一个I帧(IDR图像一定是I图像,但I图像不一定是IDR图像),具有随机访问的能力,在IDR帧之后的所有帧都不能引用任何IDR帧之前的帧的内容

(1)IDR帧肯定为I帧
(2)I帧包含了SPS, PPS, I条带
(3)P帧包含P条带
(4)B帧包含B条带


FFMPEG中有以下与帧类型相关的参数:

AVCodecContext* pCodecCtx;
pCodecCtx->gop_size = 25;
pCodecCtx->max_b_frames = 3;

gop_size设置一个gop中有多少帧,默认情况下,一个gop的第一帧为I帧,因此也可以理解为gop_size为相邻两个I帧之间的帧数。max_b_frames设置相邻两个非B帧之间最多出现的B帧数量。

gop有两种类型:封闭式与开放式。封闭式中每个gop的第一帧都是IDR图像,开放式中第一个gop的第一帧是IDR,后续的gop的第一帧非IDR图像。

 

再看以下编码函数:

int ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);

第2个参数pFrame类型为AVFrame*,其有参数 pFrame->pict_type,用于设置编码的帧类型(这个值由用户设置,默认值为0,编码器不会自动设置该值,即不会自动设置为I帧,B帧的类型),pict_type的类型为:

enum AVPictureType {
    AV_PICTURE_TYPE_NONE = 0, ///< Undefined
    AV_PICTURE_TYPE_I,     ///< Intra
    AV_PICTURE_TYPE_P,     ///< Predicted
    AV_PICTURE_TYPE_B,     ///< Bi-dir predicted
    AV_PICTURE_TYPE_S,     ///< S(GMC)-VOP MPEG4
    AV_PICTURE_TYPE_SI,    ///< Switching Intra
    AV_PICTURE_TYPE_SP,    ///< Switching Predicted
    AV_PICTURE_TYPE_BI,    ///< BI type
};

根据编码输出码流 pkt.data[4] 的值来判断帧类型,其中(pkt.data[4] & 0x1F)即为以下表中的nal_uint_type:

也可以看这个:

这里将一个yuv视频文件编码为H264文件,设置参数如下:

pCodecCtx->max_b_frames = 3;
pFrame->pict_type = AV_PICTURE_TYPE_NONE;
pCodecCtx->gop_size = 25;

以上参数,打印输出如下,其中cNalu =pkt.data[4], type = (pkt.data[4] & 0x1F)。

pFrame->pict_type = AV_PICTURE_TYPE_NONE时,pkt.data[4] 只有3个值:0x01, 0x41, 0x67,即(pkt.data[4] & 0x1F)的值为0x01和0x07,即分别表示非IDR与SPS,对于H.264来说,只有I帧才有SPS与PPS,因此这里的0x07即为I帧,而0x01为P或B帧。因此,pFrame->pict_type = AV_PICTURE_TYPE_NONE时,对于帧头来说,(pkt.data[4] & 0x1F)等于0x07表示I帧等于0x01表示B或P帧注意我这里说的是帧头,而对于slice头来说,(pkt.data[4] & 0x1F)等于0x1时,也有可能是非IDR的I Slice的。

编码后的帧序列图如下,一个Group的帧序列为:I  PBBB  PBBB PBBB PBBB PBBB PBBB

ffmpeg编码出来后第1帧(I帧)的长度为0x1F35,对应的十进制就是7989,跟上图中I帧 Size为7989对应。

我们再来看编码后文件的slice结构如下,I帧包含了SPS, PPS, SEI, I Slice四个结构,总长度为0x1F35(7989)。后面的P Slice 及B Slice 就各自单独作为一帧,第1个P帧的长度为0x797(1943),第1个B帧长度为0x3D8(984)。

其文件的十六进制码结构如下:

当参数设置为:

pCodecCtx->max_b_frames = 0;
pFrame->pict_type = AV_PICTURE_TYPE_NONE;
pCodecCtx->gop_size = 25;

这里设置b帧的数量为0,打印输出如下,输出只有I帧与P帧:

编码序列如下图,一个Group的帧序列为:I PPPPPPPPPPPPPPPPPPPP

 

其他设置的信息如下:

AV_PICTURE_TYPE_NONE:有三种帧类型输出:I帧,B帧,P帧,根据gop_size与max_b_frames设置I帧与B帧间隔。max_b_frames为0时,只有I帧与P帧

AV_PICTURE_TYPE_I:全都为I帧输出,这时max_b_frames要设置为0,不设置为0,tpye值由可能为0x1,但实际上这是个I帧

AV_PICTURE_TYPE_P:输出为I帧与P帧,max_b_frames要设置为0。I帧间隔由gop_size设置。

AV_PICTURE_TYPE_B:效果跟AV_PICTURE_TYPE_I时一致,max_b_frames要大于0

测试代码用的是雷霄华写的例子:

#include "stdafx.h"
#include <stdio.h>
#define __STDC_CONSTANT_MACROS

#ifdef _WIN32
//Windows
extern "C"
{
#include "libavutil/opt.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#ifdef __cplusplus
};
#endif
#endif

#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"swscale.lib")
#pragma comment(lib, "avformat.lib")

int flush_encoder(AVFormatContext *fmt_ctx, unsigned int stream_index) {
	int ret;
	int got_frame;
	AVPacket enc_pkt;
	if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &
		CODEC_CAP_DELAY))
		return 0;
	while (1) {
		enc_pkt.data = NULL;
		enc_pkt.size = 0;
		av_init_packet(&enc_pkt);
		ret = avcodec_encode_video2(fmt_ctx->streams[stream_index]->codec, &enc_pkt,
			NULL, &got_frame);
		av_frame_free(NULL);
		if (ret < 0)
			break;
		if (!got_frame) {
			ret = 0;
			break;
		}
		printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n", enc_pkt.size);
		/* mux encoded frame */
		ret = av_write_frame(fmt_ctx, &enc_pkt);
		if (ret < 0)
			break;
	}
	return ret;
}

int main(int argc, char* argv[])
{
	AVFormatContext* pFormatCtx;
	AVOutputFormat* fmt;
	AVStream* video_st;
	AVCodecContext* pCodecCtx;
	AVCodec* pCodec;
	AVPacket pkt;
	uint8_t* picture_buf;
	AVFrame* pFrame;
	int picture_size;
	int y_size;
	int framecnt = 0;
	//FILE *in_file = fopen("src01_480x272.yuv", "rb");	//Input raw YUV data 
	FILE *in_file = fopen("../akiyo_cif.y4m", "rb");   //Input raw YUV data
	int in_w = 352, in_h = 288;                              //Input data's width and height
	int framenum = 300;                                   //Frames to encode
	const char* out_file = "ds.h264";

	av_register_all();
	//Method1.
	pFormatCtx = avformat_alloc_context();
	//Guess Format
	fmt = av_guess_format(NULL, out_file, NULL);
	pFormatCtx->oformat = fmt;

	//Open output URL
	if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0) {
		printf("Failed to open output file! \n");
		return -1;
	}

	video_st = avformat_new_stream(pFormatCtx, 0);
	//video_st->time_base.num = 1; 
	//video_st->time_base.den = 25;  

	if (video_st == NULL) {
		return -1;
	}
	//Param that must set
	pCodecCtx = video_st->codec;
	pCodecCtx->codec_id = fmt->video_codec;
	pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
	pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
	pCodecCtx->width = in_w;
	pCodecCtx->height = in_h;
	pCodecCtx->bit_rate = 400000;
	pCodecCtx->gop_size = 25;

	pCodecCtx->time_base.num = 1;
	pCodecCtx->time_base.den = 25;

	//H264
	//pCodecCtx->me_range = 16;
	//pCodecCtx->max_qdiff = 4;
	//pCodecCtx->qcompress = 0.6;
	pCodecCtx->qmin = 10;
	pCodecCtx->qmax = 51;

	//Optional Param
	pCodecCtx->max_b_frames = 3;

	// Set Option
	AVDictionary *param = 0;
	//H.264
	if (pCodecCtx->codec_id == AV_CODEC_ID_H264) {
		av_dict_set(&param, "preset", "slow", 0);
		av_dict_set(&param, "tune", "zerolatency", 0);
		//av_dict_set(¶m, "profile", "main", 0);
	}
	//H.265
	//if (pCodecCtx->codec_id == AV_CODEC_ID_H265) {
	//	av_dict_set(&param, "preset", "ultrafast", 0);
	//	av_dict_set(&param, "tune", "zero-latency", 0);
	//}

	//Show some Information
	av_dump_format(pFormatCtx, 0, out_file, 1);

	pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
	if (!pCodec) {
		printf("Can not find encoder! \n");
		return -1;
	}
	if (avcodec_open2(pCodecCtx, pCodec, &param) < 0) {
		printf("Failed to open encoder! \n");
		return -1;
	}
	pFrame = av_frame_alloc();
	picture_size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
	picture_buf = (uint8_t *)av_malloc(picture_size);
	avpicture_fill((AVPicture *)pFrame, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);

	//Write File Header
	avformat_write_header(pFormatCtx, NULL);

	av_new_packet(&pkt, picture_size);

	y_size = pCodecCtx->width * pCodecCtx->height;
	pFrame->width = pCodecCtx->width;
	pFrame->height = pCodecCtx->height;
	pFrame->format = pCodecCtx->pix_fmt;
	int iPFrame = 0;
	pFrame->pict_type = AV_PICTURE_TYPE_NONE;
	for (int i = 0; i<framenum; i++) {
		//Read raw YUV data
		if (fread(picture_buf, 1, y_size * 3 / 2, in_file) <= 0) {
			printf("Failed to read raw data! \n");
			return -1;
		}
		else if (feof(in_file)) {
			break;
		}
		pFrame->data[0] = picture_buf;              // Y
		pFrame->data[1] = picture_buf + y_size;      // U 
		pFrame->data[2] = picture_buf + y_size * 5 / 4;  // V
														 //PTS
														 //pFrame->pts=i;
		pFrame->pts = i*(video_st->time_base.den) / ((video_st->time_base.num) * 25);
		int got_picture = 0;
		//Encode
		int ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);
		if (ret < 0) {
			printf("Failed to encode! \n");
			return -1;
		}
		if (got_picture == 1) {
			
			framecnt++;
			pkt.stream_index = video_st->index;
			char cNalu = pkt.data[4];
			char type = (cNalu & 0x1f);

				if (type == 7)
				{					
					iPFrame = 0;
					printf("Succeed to encode frame: %5d\tsize:%5d, cNalu = 0x%-2x, type = 0x%-2x,=====i帧\n", framecnt, pkt.size, cNalu, type);
				}
				else if (type == 1)
				{
					iPFrame++;
					printf("Succeed to encode frame: %5d\tsize:%5d, cNalu = 0x%-2x, type = 0x%-2x, p帧:%d\n", framecnt, pkt.size, cNalu, type, iPFrame);
				}
				else
				{
					printf("其他帧类型:cNalu = 0x%-2x, type = 0x%-2x\n", cNalu, type);
				}


			ret = av_write_frame(pFormatCtx, &pkt);
			av_free_packet(&pkt);
		}
	}
	
	//Flush Encoder
	int ret = flush_encoder(pFormatCtx, 0);
	if (ret < 0) {
		printf("Flushing encoder failed\n");
		return -1;
	}

	//Write file trailer
	av_write_trailer(pFormatCtx);

	//Clean
	if (video_st) {
		avcodec_close(video_st->codec);
		av_free(pFrame);
		av_free(picture_buf);
	}
	avio_close(pFormatCtx->pb);
	avformat_free_context(pFormatCtx);

	fclose(in_file);

	return 0;
}

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

视频编解码(一):ffmpeg编码H.264帧类型判断 的相关文章

  • 在流 #1 中找不到编解码器 pcm_alaw 的标签,使用 ffmpeg 连接 2 个文件时容器当前不支持编解码器 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在尝试连接两个文件 第一个文件包含音频和视频 第二个文件仅包含视频 我用以下ffmpeg命令 ffmpeg command ffmpe
  • 在 R 中导入 png 文件并转换为动画(.mp4)

    我正在尝试用 R 中的几个 png 文件创建一个简短的动画 我尝试了 packagemagick但只有当我将它们保存为 gif 时它才有效 当我尝试另存为 mp4 时 它将生成一个 mp4 文件 但一旦打开它 只会显示第一张图像 我的代码是
  • 将每分钟的 MP3 导出为单独的 WAV

    这绝对是一个奇怪的问题 但我正在寻找一种方法 将 60 分钟的 mp3 混合拆分为 60 个单独的 1 分钟长的 wav 文件 以便与 Echonest 等音频指纹识别 API 一起使用 这是否可以在单个 ffmpeg 命令中实现 或者我是
  • 合并来自 ffmpeg 的两个视频

    我想使用 ffmpeg 将两个 mp4 视频组合成一个 mp4 视频 到目前为止我尝试过的是 ffmpeg i input1 mp4 i input2 mp4 output mp4 但是 每次我获取带有第一个输入的视频编解码器的视频而不是另
  • 使用快速同步 h264_qsv 编码器时 ffmpeg avcodec_encode_video2 挂起

    当我使用 mpeg4 或 h264 编码器时 我能够使用 ffmpeg 3 1 0 的 API 成功编码图像以生成有效的 AVI 文件 但是 当我使用快速同步编码器 h264 qsv 时 avcodec encode video2 有时会挂
  • 将 H264 视频转换为原始 YUV 格式

    是否可以使用 ffmpeg 从 H264 编码视频创建原始 YUV 视频 我想用 matlab 打开视频并逐帧访问 Luma Cb 和 Cr 分量 是的 您可以 您只需指定像素格式即可 要获取格式的完整列表 ffmpeg pix fmts
  • Google Cloud Platform:将上传的 MP4 文件转换为 HLS 文件

    我正在构建一个平台 允许用户将一些视频文件 20 40 秒 从手机上传到服务器 所有这些上传目前都运行良好 文件通过nodejs云功能存储在谷歌存储桶中 现在我想创建一个 gcp 转码器作业 它将上传的 mp4 视频文件转换为 hls 视频
  • 如何使用 ffmpeg 提取时间精确的视频片段?

    这并不是一个特别新的问题领域 但我已经尝试过那里建议的内容 但运气不佳 那么 我的故事 我有一大段 15 秒的直接来自camera mov 视频 我想从中提取特定的块 我可以通过开始时间和停止时间 以秒为单位 来识别该块 我首先尝试执行我称
  • C# - 捕获 RTP 流并发送到语音识别

    我正在努力实现的目标 在 C 中捕获 RTP 流 将该流转发到 System Speech SpeechRecognitionEngine 我正在创建一个基于 Linux 的机器人 它将接受麦克风输入 将其发送给 Windows 机器 Wi
  • 访问 AVFrame 的每个像素

    我需要访问 FFMPEG 的 AVFrame 对象的每个像素信息 如果是 RGB 则每个像素的每种颜色 R G 和 B 我怎样才能做到这一点 如果您已经前进到正确的框架 类似于 img convert ctx ffmpeg sws getC
  • 消除 ffmpeg 和 image-magic 命令中的慢速因素

    这些命令的基本思想是创建一个比较 比较定义了过去的一张 jpeg 和现在的一张 jpeg 组合起来 例如它们将相互滑动并显示前后图像 e g https media evercam io v1 cameras 1lowe scnoe com
  • VLC 和 ffmpeg 如何协同工作?

    我从源代码编译了VLC 它运行良好 当我执行 Vlc 时 vlc 运行 我还从源代码编译了 ffmpeg 它也运行良好 当我执行 ffmpeg i f toto flv mp3 vn acodec copy new toto mp3 时 会
  • 重新采样 H264 视频以降低帧速率,同时保持高图像质量

    以下是感兴趣的视频的 mplayer 输出 br carina tmp mplayer foo mov mplayer Symbol ff codec bmp tags has different size in shared object
  • MP4 到 DASH(bash 脚本)

    我有一个网站 用户可以在其中上传视频文件 我想使用 DASH 流式传输所有内容以获得自适应比特率流式传输 因此 我编写了一个 bash 脚本 由 cron 运行 将所有 mp4 文件转换为 DASH 但它无法正常工作 出了什么问题 例如 使
  • 使用 ffmpeg 或 OpenCV 处理原始图像

    看完之后维基百科页面 http en wikipedia org wiki Raw image format原始图像格式 是任何图像的数字负片 为了查看或打印 相机图像传感器的输出具有 进行处理 即转换为照片渲染 场景 然后以标准光栅图形格
  • FFmpeg - 来自 NodeJS 的 RTMP 流,流比实时更快

    我的目标是在 Node 中渲染画布 并将该画布流式传输到 RTMP 服务器 最终是 Twitch 但现在我正在在本地 RTMP 服务器上测试 流式传输到 RTMP 的标准方式似乎是ffmpeg 所以我使用它 从 NodeJS 中作为子进程生
  • 如何仅在使用 ffmpeg 添加相同速度的分离音频流时更改视频速度?

    我正在使用 ffmpeg 作为视频过滤器 但是 当我改变视频速度时 我遇到了问题 音频比视频长 ffmpeg i INPUT mp4 filter v setpts 0 5 PTS shortest y preset ultrafast O
  • 同时从多个流中捕获、最佳方法以及如何减少 CPU 使用率

    我目前正在编写一个应用程序 该应用程序将捕获大量 RTSP 流 在我的例子中为 12 个 并将其显示在 QT 小部件上 当我超过大约 6 7 个流时 问题就会出现 CPU 使用率激增并且出现明显的卡顿 我认为它不是 QT 绘制函数的原因是因
  • OpenCV VideoWriter 未写入 Output.avi

    我正在尝试编写一段简单的代码来获取视频 裁剪视频并写入输出文件 系统设置 OS Windows 10 Conda Environment Python Version 3 7 OpenCV Version 3 4 2 ffmpeg Vers
  • 如何在Mac上使用AVFoundation将图片编码为H264,而不是使用x264

    我正在尝试制作一个 Mac 广播客户端 使用 FFmpeg 但不使用 x264 库编码为 H264 所以基本上 我可以从 AVFoundation 中获取原始帧CMSampleBufferRef or AVPicture 那么有没有一种方法

随机推荐

  • Scala深入浅出——从Java到Scala

    本文适合有一定Java基础的 并想系统学习Scala的小伙伴借鉴学习 文章有大量实例 建议自己跑一遍 Scala深入浅出 从Java到Scala Scala 一 介绍 1 什么是Scala 2 特点 3 安装 二 Scala特点 三 sca
  • SecureCRT9.1高亮配色设置

    参考 http zh cjh com qita 1623 html https download csdn net download qq 45698138 88310255 spm 1001 2014 3001 5503 1 创建文件co
  • fork的例子

    以下是下列代码的头文件 forks c Examples of Unix process control include
  • Ruoyi-cloud集成Sa-Token SSO单点登录

    文章目录 服务端 客户端前端 客户端后端 https github com dromara Sa Token Sa Token SSO 模式三 修改本地hosts 127 0 0 1 sa sso server com 127 0 0 1
  • ionic3代码压缩和apk优化

    我们在做ionic打包的时候 通常执行这条命令 ionic cordova build android release prod 使用这个命令生成的apk是ionic项目导出的最优化的apk 但是如果还想继续压缩 那么还可以借助Androi
  • Unity 空气墙Shader

    废话不多说 先上效果图 具体代码如下 Shader Hidden AirWall Properties Color Color Color 1 1 1 1 颜色 Interval Interval float 10 间隔 SubShader
  • springmvc注解和参数传递

    一 SpringMVC注解入门 创建web项目 在springmvc的配置文件中指定注解驱动 配置扫描器 Xml代码 收藏代码
  • FFmpeg 实战指南

    文章目录 表达式 滤镜效果 zoompan 中心视距由远及近 中心视距由近及远 水平视距从左到右 水平视距从右到左 垂直视距从上到下 垂直视距从下到上 rotate 顺时针旋转 PI 6 弧度 逆时针旋转 PI 6 弧度 顺时针旋转 45
  • 【Flink】处理函数Process

    目录 处理函数 基本处理函数 ProcessFunction 处理函数的功能 ProcessFunction解析 处理函数的分类 按键分区处理函数 KeyedProcessFunction 定时器Timer 和定时服务 TimerServi
  • 几种css炫酷背景欣赏

    这里为大家带来几种表现惊人的css背景效果 纯css表现效果 有桌布效果 星空效果 心形效果 砖墙效果等 请欣赏 background radial gradient rgba 255 255 255 0 0 rgba 255 255 25
  • 2020-10-29 org.apache.commons.lang3.StringUtils

    public static void TestStr null 和 操作 判断是否Null 或者 System out println StringUtils isEmpty null System out println StringUt
  • 基于神经网络的模式识别

    一 项目设计的目的 通过构建BP网络和离散Hopfield网络模式识别实例 输出稳定结果 二 相关原理知识介绍 BP学习算法是通过反向学习过程使误差最小 其算法过程从输出节点开始 反向地向第一隐含层 即最接近输入层的隐含层 传播由总误差引起
  • Ceres Solver从零开始手把手教学使用

    目录 一 简介 二 安装 三 介绍 四 Hello Word 五 导数 1 数值导数 2解析求导 六 实践 Powell函数 一 简介 笔者已经半年没有更新新的内容了 最近学习视觉SLAM的过程中发现自己之前学习的库基础不够扎实 Ceres
  • 用JS获取移动设备信息

    获取设备信息少不了的一个JS脚本就是 mobile detect js 如图第二个script链接就是mobile detect js的网上链接 它的官方链接我给大家放在这里了 mobile detect js官方地址https www m
  • 【毕业设计】深度学习图像语义分割算法研究与实现 - python 机器视觉

    文章目录 0 前言 2 概念介绍 2 1 什么是图像语义分割 3 条件随机场的深度学习模型 3 1 多尺度特征融合 4 语义分割开发过程 4 1 建立 4 2 下载CamVid数据集 4 3 加载CamVid图像 4 4 加载CamVid像
  • SpringBoot学习笔记35——实现List校验@Validated

    在 Controller 类上 加上 Validated 在需要校验的参数上加上 Valid 就可以校验list里的实体类的属性 还需要在统一异常处理类中添加异常处理 参数校验异常类 param exception return autho
  • redis未授权访问漏洞利用+redis日志分析

    redis未授权访问漏洞利用 redis日志分析 redis未授权访问 远程连接redis kali redis cli h IP redis常用语句 set key value 设置键值对 get key 获得值 incr intkey
  • 超分辨率学习记录

    超分辨率学习记录 超分定义 经典模型 前上采样 SRCNN 后上采样 FSRCNN 这篇博客主要内容来自于天池网站的超分辨率理论基础 同时对于其中涉及的学术名词也进行了解释 作为自己学习的记录 注 所有名词右上方带 的下面都有详细解释 博客
  • 最近点对问题

    分而治之 题目来源 Quoit Design Problem Description Have you ever played quoit in a playground Quoit is a game in which flat ring
  • 视频编解码(一):ffmpeg编码H.264帧类型判断

    本文主要讲述ffmpeg编码过程中是如何设置I帧 B帧及P帧的 以及如何通过代码判断帧类型 之前看过很多网上的文章 讲述如何判断I帧 B帧 P帧 然而都是停留在H 264官方文档中的定义 如果不结合ffmpeg 就仿佛纸上谈兵 有点不切实际