【音视频】FFmpeg 编码H265 - 实践
一、概述
实现了读入本地yuv文件,通过libx265编码为H265格式,并存储到本地文件中
二、实现流程
准备文件
在build
路径下准备yuv文件
在项目中添加文件参数,输出为h265文件,使用libx265编码
初始化解码器
- 通过传进来的
libx265
找到指定的编码器
codec = avcodec_find_encoder_by_name(codec_name)
;
if (!codec) {
fprintf(stderr
, "Codec '%s' not found\n"
, codec_name)
;
exit(1
)
;
}
- 为编码器分配上下文
codec_ctx = avcodec_alloc_context3(codec)
;
if (!codec_ctx) {
fprintf(stderr
, "Could not allocate video codec context\n"
)
;
exit(1
)
;
}
- 绑定解码器和解码器上下文
ret = avcodec_open2(codec_ctx, codec, NULL
)
;
if (ret <
0
) {
fprintf(stderr
, "Could not open codec: %s\n"
, av_err2str(ret)
)
;
exit(1
)
;
}
- 设置编码的视频参数,如分辨率,帧率,时间基、比特率等
#
define ENCODE_TIME_BASE 1000 // 设置时间基数,编码需要根据时间来判断码率
#
define ENCODE_FRAME_RATE 25 // 设置帧率
#
define YUV_WIDTH 1280
#
define YUV_HEIGH 720
/* 设置分辨率*/
codec_ctx->width = YUV_WIDTH;
// 根据实际去写入
codec_ctx->height = YUV_HEIGH;
/* 设置time base */
codec_ctx->time_base = (AVRational){
1
, ENCODE_TIME_BASE
}
;
// 1/1000
codec_ctx->framerate = (AVRational){
ENCODE_FRAME_RATE, 1
}
;
codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
- 也可以设置编码的码率,可以设置一个码率的范围
- 也可以设置编码的缓存大小,用于在码率波动的时候做缓存
codec_ctx->bit_rate = 3000000
;
codec_ctx->rc_max_rate = 3000000
;
codec_ctx->rc_min_rate = 3000000
;
codec_ctx->rc_buffer_size = 2000000
;
- 配置编码器的高级参数,这里指的是而外的设置
- 比如编码模式,GOP大小,B帧大小、I帧间隔、编码
profile
级别、画质等等 - preset参数是影响编码速度的,比如ultrafast编码最快,但是画质最差
codec_ctx->gop_size = 25
;
codec_ctx->max_b_frames = 0
;
codec_ctx->keyint_min = 25
;
ret = av_opt_set(codec_ctx->priv_data, "preset"
, "medium"
, 0
)
;
ret = av_opt_set(codec_ctx->priv_data, "profile"
, "main"
, 0
)
;
ret = av_opt_set(codec_ctx->priv_data, "tune"
,"film"
,0
)
;
- 也可以通过av_opt_set的方法,将修改的内容直接发送到
libx265
编码器内部进行修改
ret = av_opt_set(codec_ctx->priv_data, "x265-param"
, "--keyint=25, --bframes=2"
,0
)
;
ret = av_opt_set(codec_ctx->priv_data, "x265-params"
, "--keyint=25:--frame-threads=4"
, 0
)
;
ret = av_opt_set(codec_ctx->priv_data, "x265-params"
, "--keyint=25:--bframes=2"
, 0
)
;
ret = av_opt_set(codec_ctx->priv_data, "x265-params"
, "--keyint=25:--frame-threads=4"
, 0
)
;
- 比如我们设置了B帧为0,并且关闭了多线程,此时的延迟就为0了,观察如下
- 可以发现,一帧数据传入编码器,立马就编码出一帧数据
- 初始之外,还可以通过设置零延迟的方法,当同时必须关闭多线程
ret = av_opt_set(codec_ctx->priv_data, "tune"
,"zerolatency"
,0
)
;
- 如果开启多线程,就会存在编码延迟,不过可以提高编码速度
- 打印发现,传入多帧数据后,才从编码器中取出编码后的帧
ret = av_opt_set(codec_ctx->priv_data, "x265-params"
, "--keyint=25:--frame-threads=4"
, 0
)
;
- 也可以这样设置多线程
codec_ctx->thread_count = 4
;
// 开了多线程后也会导致帧输出延迟, 需要缓存thread_count帧后再编程。
codec_ctx->thread_type = FF_THREAD_FRAME;
// 并 设置为FF_THREAD_FRAME
- 以下的设置,是将SPS、PPS、VPS放入扩展变量里面,即
codec->extradata
里面 - 此时就不会而外编码一帧了,观察打印结果
codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
启动后,没发现SPS、PPS、VPS
关闭后,前几帧为SPS、PPS、VPS
更多的libx265的设置,可以使用对应的命令查询
ffmpeg -h encoder=libx265 > libx265.log
编码H265
- 打开输入、输出文件
// 打开输入和输出文件
infile = fopen(in_yuv_file, "rb"
)
;
if (!infile) {
fprintf(stderr
, "Could not open %s\n"
, in_yuv_file)
;
exit(1
)
;
}
outfile = fopen(out_h264_h265_file, "wb"
)
;
if (!outfile) {
fprintf(stderr
, "Could not open %s\n"
, out_h264_h265_file)
;
exit(1
)
;
}
- 分配对应的AVFrame和AVPacket的内存
// 分配pkt和frame
pkt = av_packet_alloc(
)
;
if (!pkt) {
fprintf(stderr
, "Could not allocate video frame\n"
)
;
exit(1
)
;
}
frame = av_frame_alloc(
)
;
if (!frame) {
fprintf(stderr
, "Could not allocate video frame\n"
)
;
exit(1
)
;
}
- 设置frame的参数,如分辨率、像素
// 为frame分配buffer
frame->format = codec_ctx->pix_fmt;
frame->width = codec_ctx->width;
frame->height = codec_ctx->height;
ret = av_frame_get_buffer(frame, 0
)
;
if (ret <
0
) {
fprintf(stderr
, "Could not allocate the video frame data\n"
)
;
exit(1
)
;
}
- 通过分辨率和像素格式,计算出一帧数据对应的buffer大小,并且分配对应的缓冲区
int frame_bytes = av_image_get_buffer_size(frame->format, frame->width,
frame->height, 1
)
;
uint8_t *yuv_buf = (uint8_t *
)malloc(frame_bytes)
;
- 循环编码文件,直到读到文件结束
av_frame_make_writable
确保帧可用,不开启的话可能会写入失败
for (
;
;
) {
memset(yuv_buf, 0
, frame_bytes)
;
size_t read_bytes = fread(yuv_buf, 1
, frame_bytes, infile)
;
if(read_bytes <= 0
) {
printf("read file finish\n"
)
;
break
;
}
/* 确保该frame可写, 如果编码器内部保持了内存参考计数,则需要重新拷贝一个备份
目的是新写入的数据和编码器保存的数据不能产生冲突
*/
ret = av_frame_make_writable(frame)
;
if(ret != 0
) {
printf("av_frame_make_writable failed, ret = %d\n"
, ret)
;
break
;
}
int need_size = av_image_fill_arrays(frame->data, frame->linesize, yuv_buf,
frame->format,
frame->width, frame->height, 1
)
;
if(need_size != frame_bytes) {
printf("av_image_fill_arrays failed, need_size:%d, frame_bytes:%d\n"
,
need_size, frame_bytes)
;
break
;
}
pts += (ENCODE_TIME_BASE/ENCODE_FRAME_RATE)
;
// 设置pts
frame->pts = pts;
// 使用采样率作为pts的单位,具体换算成秒 pts*1/采样率
begin_time = get_time(
)
;
ret = encode(codec_ctx, frame, pkt, outfile)
;
end_time = get_time(
)
;
printf("encode time:%lldms\n"
, end_time - begin_time)
;
if(ret <
0
) {
printf("encode failed\n"
)
;
break
;
}
}
encode
函数如下,主要是送入一帧数据,然后循环读取AVPacket
,直到输出AVERROR_EOF
表示当前帧编码完成
static
int encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,
FILE *outfile)
{
int ret;
/* send the frame to the encoder */
if (frame)
printf("send frame pts:%3"PRId64"\n"
, frame->pts)
;
/* 通过查阅代码,使用x265进行编码时,具体缓存帧是在x265源码进行,
* 不会增加avframe对应buffer的reference*/
ret = avcodec_send_frame(enc_ctx, frame)
;
if (ret <
0
)
{
fprintf(stderr
, "Error sending a frame for encoding\n"
)
;
return -1
;
}
while (ret >= 0
)
{
ret = avcodec_receive_packet(enc_ctx, pkt)
;
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
return 0
;
}
else
if (ret <
0
) {
fprintf(stderr
, "Error encoding audio frame\n"
)
;
return -1
;
}
printf("avcodec_receive_packet:pts:%3"PRId64" dts:%3"PRId64" (size:%5d)\n"
, pkt->pts, pkt->dts, pkt->size)
;
fwrite(pkt->data, 1
, pkt->size, outfile)
;
print_h265_nal_unit_type(pkt->data, pkt->size)
;
// 只针对H265
}
return 0
;
}
print_h265_nal_unit_type
函数主要是通过H265的结构,找到startcode,然后解析NALU的头部,用于输出调试
void print_h265_nal_unit_type(uint8_t *data, size_t size)
{
int i = 0
;
while (i+3 < size ) {
if(data[i] == 0 && data[i+1]==0 && data[i+2] == 0 && data[i+3] == 1
) {
i += 4
;
printf("%02x nal_type:%d, pos:%d\n"
, data[i]
,(data[i]&
0x7e
)>>
1
, i)
;
continue
;
}
if(data[i] == 0 && data[i+1]==0 && data[i+2] == 1
) {
i += 3
;
printf("%02x nal_type:%d, pos:%d\n"
,data[i]
, (data[i]&
0x7e
)>>
1
, i)
;
continue
;
}
i++
;
}
}
冲刷编码器
- 编码结束后,编码器内部可能还缓存了部分帧,此时需要传入NULL,将编码器中剩余的帧冲刷出来
/* 冲刷编码器 */
encode(codec_ctx, NULL
, pkt, outfile)
;
关闭文件
- 关闭文件、并且释放相关内存,确保正确结束程序
// 关闭文件
fclose(infile)
;
fclose(outfile)
;
// 释放内存
if(yuv_buf) {
free(yuv_buf)
;
}
av_frame_free(&frame)
;
av_packet_free(&pkt)
;
avcodec_free_context(&codec_ctx)
;
完整代码
/**
* @projectName 02-encode_h265
* @brief 视频编码,从本地读取YUV数据进行H265编码
* @author Liao Qingfu
* @date 2022-09-16
*/
#
include <stdio.h>#include <stdlib.h>#include <string.h>#include <libavcodec/avcodec.h>#include <libavutil/time.h>#include <libavutil/opt.h>#include <libavutil/imgutils.h>#define ENCODE_TIME_BASE 1000 // 设置时间基数,编码需要根据时间来判断码率#define ENCODE_FRAME_RATE 25 // 设置帧率#define YUV_WIDTH 1280#define YUV_HEIGH 720void print_h265_nal_unit_type(uint8_t *data, size_t size);int64_t get_time(){return av_gettime_relative() / 1000;// 换算成毫秒}staticint encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,FILE *outfile){int ret;/* send the frame to the encoder */if (frame)printf("send frame pts:%3"PRId64"\n", frame->pts);/* 通过查阅代码,使用x264进行编码时,具体缓存帧是在x264源码进行,* 不会增加avframe对应buffer的reference*/ret = avcodec_send_frame(enc_ctx, frame);if (ret <0){fprintf(stderr, "Error sending a frame for encoding\n");return -1;}while (ret >= 0){ret = avcodec_receive_packet(enc_ctx, pkt);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {return 0;}elseif (ret <0) {fprintf(stderr, "Error encoding audio frame\n");return -1;}printf("avcodec_receive_packet:pts:%3"PRId64" dts:%3"PRId64" (size:%5d)\n", pkt->pts, pkt->dts, pkt->size);fwrite(pkt->data, 1, pkt->size, outfile);print_h265_nal_unit_type(pkt->data, pkt->size);// 只针对H265}return 0;}/*** @brief 提取测试文件:ffmpeg -i test_1280x720.flv -t 5 -r 25 -pix_fmt yuv420p yuv420p_1280x720.yuv* 参数输入: yuv420p_1280x720.yuv yuv420p_1280x720.h265 libx265* @param argc* @param argv* @return*/int main(int argc,char **argv){char *in_yuv_file = NULL;char *out_h264_h265_file = NULL;FILE *infile = NULL;FILE *outfile = NULL;constchar *codec_name = NULL;const AVCodec *codec = NULL;AVCodecContext *codec_ctx= NULL;AVFrame *frame = NULL;AVPacket *pkt = NULL;int ret = 0;if (argc <4) {fprintf(stderr, "Usage: %s <input_file out_file codec_name >, argc:%d\n",argv[0], argc);return 0;}in_yuv_file = argv[1];// 输入YUV文件out_h264_h265_file = argv[2];codec_name = argv[3];/* 查找指定的编码器 */codec = avcodec_find_encoder_by_name(codec_name);if (!codec) {fprintf(stderr, "Codec '%s' not found\n", codec_name);exit(1);}codec_ctx = avcodec_alloc_context3(codec);if (!codec_ctx) {fprintf(stderr, "Could not allocate video codec context\n");exit(1);}/* 设置分辨率*/codec_ctx->width = YUV_WIDTH;// 根据实际去写入codec_ctx->height = YUV_HEIGH;/* 设置time base */codec_ctx->time_base = (AVRational){1, ENCODE_TIME_BASE};// 1/1000codec_ctx->framerate = (AVRational){ENCODE_FRAME_RATE, 1};/* 设置I帧间隔* 如果frame->pict_type设置为AV_PICTURE_TYPE_I, 则忽略gop_size的设置,一直当做I帧进行编码*/codec_ctx->gop_size = 25;// I帧间隔, H265单独设置这里不起作用codec_ctx->keyint_min = 25;codec_ctx->max_b_frames = 0;// 如果不想包含B帧则设置为0codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;//if (codec->id == AV_CODEC_ID_H264) {// 相关的参数可以参考libx264.c的 AVOption options// ultrafast all encode time:2270ms// medium all encode time:5815ms// veryslow all encode time:19836msret = av_opt_set(codec_ctx->priv_data, "preset", "medium", 0);if(ret != 0) {printf("av_opt_set preset failed\n");}ret = av_opt_set(codec_ctx->priv_data, "profile", "main", 0);// 默认是highif(ret != 0) {printf("av_opt_set profile failed\n");}// ret = av_opt_set(codec_ctx->priv_data, "tune","zerolatency",0); // 直播是才使用该设置// ret = av_opt_set(codec_ctx->priv_data, "tune","film",0); // 画质filmif(ret != 0) {printf("av_opt_set tune failed\n");}}elseif (codec->id == AV_CODEC_ID_H265) {// 相关的参数可以参考libx265.c的 AVOption options// ultrafast all encode time:// medium all encode time:// veryslow all encode time:ret = av_opt_set(codec_ctx->priv_data, "preset", "medium", 0);if(ret != 0) {printf("av_opt_set preset failed\n");}ret = av_opt_set(codec_ctx->priv_data, "profile", "main", 0);// 默认是highif(ret != 0) {printf("av_opt_set profile failed\n");}ret = av_opt_set(codec_ctx->priv_data, "tune","zerolatency",0);// 直播是才使用该设置// ret = av_opt_set(codec_ctx->priv_data, "tune","film",0); // 画质filmif(ret != 0) {printf("av_opt_set tune failed\n");}// libx265/source\common\param.cpp// ret = av_opt_set(codec_ctx->priv_data, "x265-param", "--keyint=25, --bframes=2",0);// ret = av_opt_set(codec_ctx->priv_data, "x265-params", "--keyint=25:--frame-threads=4", 0);// ret = av_opt_set(codec_ctx->priv_data, "x265-params", "--keyint=25:--bframes=2", 0);// ret = av_opt_set(codec_ctx->priv_data, "x265-params", "--keyint=25:--frame-threads=4", 0);if(ret != 0) {printf("av_opt_set x265-param failed\n");return -1;}}else {printf("no support the codec :%s\n", codec_name);return -1;}/** 设置编码器参数*//* 设置bitrate */codec_ctx->bit_rate = 3000000;//3000k// codec_ctx->rc_max_rate = 3000000;// codec_ctx->rc_min_rate = 3000000;// codec_ctx->rc_buffer_size = 2000000;// codec_ctx->thread_count = 4; // 开了多线程后也会导致帧输出延迟, 需要缓存thread_count帧后再编程。// codec_ctx->thread_type = FF_THREAD_FRAME; // 并 设置为FF_THREAD_FRAME/* 对于H264 AV_CODEC_FLAG_GLOBAL_HEADER 设置则只包含I帧,此时sps pps需要从codec_ctx->extradata读取* 不设置则每个I帧都带 sps pps sei*/codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;// 存本地文件时不要去设置/* 将codec_ctx和codec进行绑定 */ret = avcodec_open2(codec_ctx, codec, NULL);if (ret <0) {fprintf(stderr, "Could not open codec: %s\n", av_err2str(ret));exit(1);}printf("thread_count: %d, thread_type:%d\n", codec_ctx->thread_count, codec_ctx->thread_type);// 打开输入和输出文件infile = fopen(in_yuv_file, "rb");if (!infile) {fprintf(stderr, "Could not open %s\n", in_yuv_file);exit(1);}outfile = fopen(out_h264_h265_file, "wb");if (!outfile) {fprintf(stderr, "Could not open %s\n", out_h264_h265_file);exit(1);}// 分配pkt和framepkt = av_packet_alloc();if (!pkt) {fprintf(stderr, "Could not allocate video frame\n");exit(1);}frame = av_frame_alloc();if (!frame) {fprintf(stderr, "Could not allocate video frame\n");exit(1);}// 为frame分配bufferframe->format = codec_ctx->pix_fmt;frame->width = codec_ctx->width;frame->height = codec_ctx->height;ret = av_frame_get_buffer(frame, 0);if (ret <0) {fprintf(stderr, "Could not allocate the video frame data\n");exit(1);}// 计算出每一帧的数据 像素格式 * 宽 * 高// 1382400int frame_bytes = av_image_get_buffer_size(frame->format, frame->width,frame->height, 1);printf("frame_bytes %d\n", frame_bytes);uint8_t *yuv_buf = (uint8_t *)malloc(frame_bytes);if(!yuv_buf) {printf("yuv_buf malloc failed\n");return 1;}int64_t begin_time = get_time();int64_t end_time = begin_time;int64_t all_begin_time = get_time();int64_t all_end_time = all_begin_time;int64_t pts = 0;printf("start enode\n");for (;;) {memset(yuv_buf, 0, frame_bytes);size_t read_bytes = fread(yuv_buf, 1, frame_bytes, infile);if(read_bytes <= 0) {printf("read file finish\n");break;}/* 确保该frame可写, 如果编码器内部保持了内存参考计数,则需要重新拷贝一个备份目的是新写入的数据和编码器保存的数据不能产生冲突*/ret = av_frame_make_writable(frame);if(ret != 0) {printf("av_frame_make_writable failed, ret = %d\n", ret);break;}int need_size = av_image_fill_arrays(frame->data, frame->linesize, yuv_buf,frame->format,frame->width, frame->height, 1);if(need_size != frame_bytes) {printf("av_image_fill_arrays failed, need_size:%d, frame_bytes:%d\n",need_size, frame_bytes);break;}pts += (ENCODE_TIME_BASE/ENCODE_FRAME_RATE);// 设置ptsframe->pts = pts;// 使用采样率作为pts的单位,具体换算成秒 pts*1/采样率begin_time = get_time();ret = encode(codec_ctx, frame, pkt, outfile);end_time = get_time();printf("encode time:%lldms\n", end_time - begin_time);if(ret <0) {printf("encode failed\n");break;}}/* 冲刷编码器 */encode(codec_ctx, NULL, pkt, outfile);all_end_time = get_time();printf("all encode time:%lldms\n", all_end_time - all_begin_time);// 关闭文件fclose(infile);fclose(outfile);// 释放内存if(yuv_buf) {free(yuv_buf);}av_frame_free(&frame);av_packet_free(&pkt);avcodec_free_context(&codec_ctx);printf("main finish, please enter Enter and exit\n");getchar();return 0;}void print_h265_nal_unit_type(uint8_t *data, size_t size){int i = 0;while (i+3 < size ) {if(data[i] == 0 && data[i+1]==0 && data[i+2] == 0 && data[i+3] == 1) {i += 4;printf("%02x nal_type:%d, pos:%d\n", data[i],(data[i]&0x7e)>>1, i);continue;}if(data[i] == 0 && data[i+1]==0 && data[i+2] == 1) {i += 3;printf("%02x nal_type:%d, pos:%d\n",data[i], (data[i]&0x7e)>>1, i);continue;}i++;}}
更多资料:https://github.com/0voice
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/928441.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!相关文章
做网站关键字租域名多少钱
Python编程中常用的12种基础知识,其中肯定有你不会的!人生苦短,我用Python1、正则表达式替换目标: 将字符串line中的 overview.gif 替换成其他字符串。人生苦短,我用Python2、遍历目录方法在某些时候,我们需要遍历某个…
专业网站建设平台做微商怎么样引流人脉
一台脂肪秤通过测试体重、体脂、BMI、水分等数据并给出相应提示,并且许多人都将体脂检测数据作为身体健康指数衡量标准,辅助用户来关注身体健康,同时可以通过蓝牙与手机APP应用相连,记录日常身体变化情况,根据变化情况…
WinRAR去广告版保姆级安装教程
WinRAR去广告版保姆级安装教程背景
之前一直用的2345解压缩软件,结果发现这个软件在后台一直自启更改浏览器主页,还有安装其他软件跟弹窗广告,流氓软件无疑,所以卸载2345压缩软件,在网上找了一圈之后,发现还是…
英语_作文_8BU2_My View on Social Media
My View on Social Media
Social media is now important to everyone, especially teenagers. According to a recent report, over 95% of teens lives are connected with it. It can be helpful. When we are busy…
小程序在哪个网站做wordpress图片清理
好 本文 我们来说说触摸事件 字面意思也非常好理解 就是我们手机手指触摸物体触发
我们先在编辑器组件介绍中 找到这个东西的基本用法
Button("跳转").onTouch((event: TouchEvent) > {})最明显的就是 event 的类型变了 点击事件的是 ClickEvent 而这里是 Touc…
C/C++与Java、Python、Go在各个阶段的区别
不同编程语言在预处理、编译、汇编、链接和运行等阶段存在显著差异,这主要源于语言设计目标(如开发效率、运行效率、跨平台性等)的不同。下面对比C/C++与Java、Python、Go在这些阶段的区别:
1. 预处理阶段
C/C++:…
个性创意网站wordpress tag生成的链接乱
参考链接: 参考文章
该参考文章的第一种方法:设置win10环境变量。 在设置完环境变量后,在pycharm终端上不能有效读取到刚刚设置的环境变量的,需要启动win的cmd,在项目路径下执行脚本。如下所示的对比: cm…
校园二手网站源码免费学编程网站
Thrift快速入门开发demo
一、认识Thrift
thrift是什么?一个RPC 代码生成框架,使用它的IDL(Interface Defination Language,接口定义语言)定义你想要实现的接口,然后它就会生成对应语言的远程调用框架代码,用户只需要实现接口逻辑,不用关心具体的细节。
tutorial:htt…
织梦网站怎么做索引地图wordpress 注册不了
Semaphone应用&源码分析
3.1 Semaphore介绍
sync,ReentrantLock是互斥锁,保证一个资源同一时间只允许被一个线程访问
Semaphore(信号量)保证1个或多个资源可以被指定数量的线程同时访问
底层实现是基于AQS去做的。
Semap…
[省选联考 2025] 图排列 题解
2025 省选 D1T3[省选联考 2025] 图排列
闲话
一想到考场上自己以为直接输出最小 dfn 序就有 \(52pts\) 我就想笑。
洛谷题解区有一个码量小的分讨做法,但是因为我不想分讨所以还是选择了这个实现起来不太用脑子的做法…
Windows下安装并采用kubectl查看K8S日志
Windows下安装并采用kubectl查看K8S日志pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Mo…
实用指南:UV 包管理工具:替代 pip 的现代化解决方案
实用指南:UV 包管理工具:替代 pip 的现代化解决方案pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&qu…
做网站的职位叫什么问题建一个团购网站需要多少钱
目录 在日常开发中,Date工具类使用频率相对较高,大家通常都会这样写:这很简单啊,有什么争议吗?格式化后出现的时间错乱。看看Java 8是如何解决时区问题的:在处理带时区的国际化时间问题,推荐使用…
做餐饮企业网站的费用wordpress主题电影
本节,我们将跟随数据流向讲解UEP管线中的烘培光照。 文章目录 一、URP烘培光照1. 搭建场景2. 烘培光照参数设置MixedLight光照设置:直观感受 Lightmapping Settings参数设置: 3. 我们如何记录次表面光源颜色首先我们提取出相关URP代码&#…
vi/vim文本编辑器
Vim是从 vi 发展出来的一个文本编辑器,vim 具有程序编辑的能力,可以主动的以字体颜色辨别语法的正确性
vi/vim 共分为三种模式:
命令模式、 输入模式、底线命令模式(末行模式)
命令模式:刚刚启动 vi/vim,便进入…
B3869 [GESP202309 四级] 进制转换-题解
题目
题目描述
$N$ 进制数指的是逢 $N$ 进一的计数制。例如,人们日常生活中大多使用十进制计数,而计算机底层则一般使用二进制。除此之外,八进制和十六进制在一些场合也是常用的计数制(十六进制中,一般使用字母 A…
LeetCode 139. 单词拆分(Word Break) - 动态规划深度解析 - 详解
pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …