FFmpeg将编码后数据保存成mp4
以下测试代码实现的功能是:持续从内存块中获取原始数据,然后依次进行解码、编码、最后保存成mp4视频文件。
可保存成单个视频文件,也可指定每个视频文件的总帧数,保存多个视频文件。
为了便于查看和修改,这里将可独立的程序段存放在单个函数中:
1.线程函数set_packet:独立线程,用于用户向指定的内存块中持续写入数据,这里通过调用队列类PacketScaleQueue,可参考:https://blog.csdn.net/fengbingchun/article/details/132128885 中说明。这里填充的数据参考FFmpeg源码中的doc/examples/encode_video.c:每次向队列中写入一帧数据
void set_packet(PacketScaleQueue& packet_encode)
{while (packet_encode_flag) {Buffer buffer;packet_encode.popPacket(buffer);static int i = 0;// refrence: ffmpeg/doc/examples/encode_video.c// prepare a dummy image: Yunsigned char* p1 = buffer.data;for (auto y = 0; y < height; ++y) {for (auto x = 0; x < width; ++x) {p1[y * width + x] = x + y + i * 3;}}// Cb and Crunsigned char* p2 = buffer.data + width * height;unsigned char* p3 = buffer.data + width * height + width * height / 4;for (auto y = 0; y < height / 2; ++y) {for (auto x = 0; x < width / 2; ++x) {p2[y * width / 2 + x] = 128 + y + i * 2;p3[y * width / 2 + x] = 64 + x + i * 5;}}packet_encode.pushScale(buffer);if (++i > 25) i = 0;std::this_thread::sleep_for(std::chrono::milliseconds(40));}
}
2.回调函数read_packet:avio_alloc_context中使用,不断从队列中获取数据,av_read_frame中也会从这里获取数据:每次从队列中获取一帧数据
int read_packet(void* opaque, uint8_t* buf, int buf_size)
{PacketScaleQueue* packet_encode = static_cast<PacketScaleQueue*>(opaque);Buffer buffer;packet_encode->popScale(buffer);memcpy(buf, buffer.data, buf_size);packet_encode->pushPacket(buffer);return buf_size;
}
3.函数get_input_format_context:创建输入AVFormatContext,需要调用av_dict_set设置video_size和pixel_format
AVFormatContext* get_input_format_context(AVIOContext* avio_ctx)
{AVFormatContext* ifmt_ctx = avformat_alloc_context();if (!ifmt_ctx) {print_error_string(AVERROR(ENOMEM));return nullptr;}ifmt_ctx->pb = avio_ctx;AVDictionary* dict = nullptr;av_dict_set(&dict, "video_size", video_size, 0);av_dict_set(&dict, "pixel_format", pixel_format, 0);auto ret = avformat_open_input(&ifmt_ctx, nullptr, av_find_input_format("rawvideo"), &dict);if (ret < 0) {fprintf(stderr, "Could not open input\n");print_error_string(ret);return nullptr;}av_dict_free(&dict);ret = avformat_find_stream_info(ifmt_ctx, nullptr);if (ret < 0) {fprintf(stderr, "Could not find stream information\n");print_error_string(ret);return nullptr;}for (unsigned int i = 0; i < ifmt_ctx->nb_streams; ++i) {const AVStream* stream = ifmt_ctx->streams[i];if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {video_stream_index = i;fprintf(stdout, "type of the encoded data: %d, dimensions of the video frame in pixels: width: %d, height: %d, pixel format: %d\n",stream->codecpar->codec_id, stream->codecpar->width, stream->codecpar->height, stream->codecpar->format);}}if (video_stream_index == -1) {fprintf(stderr, "error: no video stream\n");return nullptr;}if (ifmt_ctx->streams[video_stream_index]->codecpar->codec_id != AV_CODEC_ID_RAWVIDEO) {fprintf(stderr, "error: this test code only support rawvideo encode: %d\n", ifmt_ctx->streams[video_stream_index]->codecpar->codec_id);return nullptr;}av_dump_format(ifmt_ctx, 0, "nothing", 0);return ifmt_ctx;
}
4.函数get_decode_context:创建解码AVCodecContext
AVCodecContext* get_decode_context(AVFormatContext* ifmt_ctx)
{AVCodec* decoder = nullptr;auto ret = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0);if (ret < 0) {fprintf(stderr, "fail to av_find_best_stream: %d\n", ret);print_error_string(ret);return nullptr;}AVCodecContext* dec_ctx = avcodec_alloc_context3(decoder);if (!dec_ctx) {fprintf(stderr, "fail to avcodec_alloc_context3\n");return nullptr;}ret = avcodec_parameters_to_context(dec_ctx, ifmt_ctx->streams[video_stream_index]->codecpar);if (ret < 0) {fprintf(stderr, "fail to avcodec_parameters_to_context: %d\n", ret);print_error_string(ret);return nullptr;}dec_ctx->framerate = av_guess_frame_rate(ifmt_ctx, ifmt_ctx->streams[video_stream_index], nullptr);ret = avcodec_open2(dec_ctx, decoder, nullptr);if (ret != 0) {fprintf(stderr, "fail to avcodec_open2: %d\n", ret);print_error_string(ret);return nullptr;}return dec_ctx;
}
5.函数get_encode_context:创建编码AVCodecContext,注意对enc_ctx的相关成员的赋值
AVCodecContext* get_encode_context(AVFormatContext* ifmt_ctx)
{AVCodec* encodec = avcodec_find_encoder_by_name("mpeg1video"); // ffmpeg.exe -encodersif (!encodec) {fprintf(stderr, "fail to avcodec_find_encoder_by_name\n");return nullptr;}AVCodecContext* enc_ctx = avcodec_alloc_context3(encodec);if (!enc_ctx) {fprintf(stderr, "fail to avcodec_alloc_context3\n");return nullptr;}enc_ctx->bit_rate = 400000;enc_ctx->framerate = ifmt_ctx->streams[video_stream_index]->r_frame_rate;enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P;enc_ctx->height = ifmt_ctx->streams[video_stream_index]->codecpar->height;enc_ctx->width = ifmt_ctx->streams[video_stream_index]->codecpar->width;enc_ctx->time_base = av_inv_q(av_d2q(frame_rate, 4096));enc_ctx->max_b_frames = 1;//if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) // trueenc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;auto ret = avcodec_open2(enc_ctx, encodec, nullptr);if (ret != 0) {fprintf(stderr, "fail to avcodec_open2: %d\n", ret);print_error_string(ret);return nullptr;}return enc_ctx;
}
6.函数get_output_format_context:创建输出AVFormatContext
AVFormatContext* get_output_format_context(const AVCodecContext* enc_ctx, const char* filename)
{AVFormatContext* ofmt_ctx = nullptr;auto ret = avformat_alloc_output_context2(&ofmt_ctx, nullptr, nullptr, filename);if (ret < 0 || !ofmt_ctx) {fprintf(stderr, "fail to avformat_alloc_output_context2: %d\n", ret);print_error_string(ret);return nullptr;}AVStream* out_stream = avformat_new_stream(ofmt_ctx, nullptr);if (!out_stream) {fprintf(stderr, "fail to avformat_new_stream\n");return nullptr;}ret = avcodec_parameters_from_context(out_stream->codecpar, enc_ctx);if (ret < 0) {fprintf(stderr, "fail to avcodec_parameters_from_context: %d\n", ret);print_error_string(ret);return nullptr;}out_stream->time_base = enc_ctx->time_base;if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) { // trueret = avio_open(&ofmt_ctx->pb, filename, AVIO_FLAG_WRITE);if (ret < 0) {fprintf(stderr, "fail to avio_open: %d\n", ret);print_error_string(ret);return nullptr;}}ret = avformat_write_header(ofmt_ctx, nullptr);if (ret < 0) {fprintf(stderr, "fail to avformat_write_header: %d\n", ret);print_error_string(ret);return nullptr;}av_dump_format(ofmt_ctx, 0, filename, 1);return ofmt_ctx;
}
7.函数decode: 注意对packet的判断,它有可能为nullptr;调用一次avcodec_send_packet,可能需调用多次avcodec_receive_frame,因此需要将avcodec_receive_frame放在while中;需要对dec_ctx->time_base进行设置
int decode(AVPacket* packet, AVFormatContext* ifmt_ctx, AVCodecContext* dec_ctx, AVFrame* frame)
{if (packet)av_packet_rescale_ts(packet, ifmt_ctx->streams[video_stream_index]->time_base, dec_ctx->time_base);auto ret = avcodec_send_packet(dec_ctx, packet);if (ret < 0) {fprintf(stderr, "fail to avcodec_send_packet: %d\n", ret);print_error_string(ret);return -1;}while (1) {ret = avcodec_receive_frame(dec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { // AVERROR(EAGAIN): decode is not yet complete//fprintf(stderr, "Warning: avcodec_receive_frame: %d\n", ret);//print_error_string(ret);break;}else if (ret < 0) {fprintf(stderr, "fail to avcodec_receive_frame: %d\n", ret);print_error_string(ret);return ret;}frame->pts = frame->best_effort_timestamp;break;}if (packet)av_packet_unref(packet);return 0;
}
8.函数encode:注意:调用一次avcodec_send_frame,可能需要调用多次avcodec_receive_packet,因此需要将avcodec_receive_packet放在while中;需要对ofmt_ctx->streams[0]->time_base进行设置,否则生成的mp4中无法获取帧速率;也可在调用avformat_write_header时设置video_track_timescale指定
int encode(AVCodecContext* enc_ctx, AVFrame* frame, AVPacket* packet, AVFormatContext* ifmt_ctx, AVFormatContext* ofmt_ctx)
{auto ret = avcodec_send_frame(enc_ctx, frame);if (ret < 0) {if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {fprintf(stderr, "Warning: avcodec_send_frame: %d\n", ret);print_error_string(ret);ret = 0;}else {fprintf(stderr, "fail to avcodec_send_frame: %d\n", ret);print_error_string(ret);return ret;}}while (1) {ret = avcodec_receive_packet(enc_ctx, packet);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { // AVERROR(EAGAIN): encode is not yet complete//fprintf(stderr, "Warning: avcodec_receive_packet: %d\n", ret);//print_error_string(ret);break;}if (ret < 0) {fprintf(stderr, "fail to avcodec_receive_packet: %d\n", ret);print_error_string(ret);return ret;}packet->stream_index = 0;av_packet_rescale_ts(packet, ifmt_ctx->streams[video_stream_index]->time_base, ofmt_ctx->streams[0]->time_base);//packet2->pts = packet2->dts = frame->pts *// ofmt_ctx->streams[0]->time_base.den / ofmt_ctx->streams[0]->time_base.num / // (enc_ctx->framerate.num / enc_ctx->framerate.den);ret = av_interleaved_write_frame(ofmt_ctx, packet);if (ret < 0) {print_error_string(ret);return ret;}av_packet_unref(packet);}return 0;
}
9.主函数test_ffmpeg_save_video_slice:注意:在退出前需要flush decoder和encoder;写入视频文件需先调用avformat_write_header,然后持续调用av_interleaved_write_frame,退出前再需调用av_write_trailer;每次新文件的写入,需重新调用get_decode_context、get_encode_context、get_output_format_context并在之前作相应free
int test_ffmpeg_save_video_slice()
{PacketScaleQueue packet_encode;packet_encode.init(queue_size, block_size);std::thread thread_packet(set_packet, std::ref(packet_encode));uint8_t* avio_ctx_buffer = static_cast<uint8_t*>(av_malloc(block_size));if (!avio_ctx_buffer) {print_error_string(AVERROR(ENOMEM));return -1;}AVIOContext* avio_ctx = avio_alloc_context(avio_ctx_buffer, block_size, 0, &packet_encode, &read_packet, nullptr, nullptr);if (!avio_ctx) {print_error_string(AVERROR(ENOMEM));return -1;}AVFormatContext* ifmt_ctx = get_input_format_context(avio_ctx);if (!ifmt_ctx) {fprintf(stderr, "fail to get_input_format_context\n");return -1;}AVPacket *packet = av_packet_alloc(), *packet2 = av_packet_alloc();if (!packet || !packet2) {fprintf(stderr, "fail to av_packet_alloc\n");return -1;}AVFrame* frame = av_frame_alloc();if (!frame) {fprintf(stderr, "fail to av_frame_alloc\n");return -1;}int count = 0, name = 1;AVCodecContext* enc_ctx = nullptr;AVFormatContext* ofmt_ctx = nullptr;AVCodecContext* dec_ctx = nullptr;while (1) {auto ret = av_read_frame(ifmt_ctx, packet);if (ret < 0) {break;}if (packet->stream_index != video_stream_index) {av_packet_unref(packet);continue;}if (count % slice_size == 0) {enc_ctx = get_encode_context(ifmt_ctx);if (!enc_ctx) {fprintf(stderr, "fail to avcodec_alloc_context3\n");return -1;}std::string filename = std::to_string(name) + ".mp4";filename = std::string(path) + filename;ofmt_ctx = get_output_format_context(enc_ctx, filename.c_str());if (!ofmt_ctx) {fprintf(stderr, "fail to get_output_format_context\n");return -1;}dec_ctx = get_decode_context(ifmt_ctx);if (!dec_ctx) {fprintf(stderr, "fail to get_decode_context\n");return -1;}++name;}if (decode(packet, ifmt_ctx, dec_ctx, frame) != 0) return -1;if (encode(enc_ctx, frame, packet2, ifmt_ctx, ofmt_ctx) != 0) return -1;//fprintf(stdout, "count: %d\n", count);if (count + 1 == total_frames) { // terminate looppacket_encode_flag = false;// flush the decoderdecode(nullptr, ifmt_ctx, dec_ctx, frame);if (frame->data[0])encode(enc_ctx, frame, packet2, ifmt_ctx, ofmt_ctx);// flush the encoderencode(enc_ctx, nullptr, packet2, ifmt_ctx, ofmt_ctx);av_write_trailer(ofmt_ctx);break;}++count;if (count > 1 && count % slice_size == 0) {// flush the decoderdecode(nullptr, ifmt_ctx, dec_ctx, frame);if (frame->data[0])encode(enc_ctx, frame, packet2, ifmt_ctx, ofmt_ctx);// flush the encoderencode(enc_ctx, nullptr, packet2, ifmt_ctx, ofmt_ctx);av_write_trailer(ofmt_ctx);avcodec_free_context(&dec_ctx);avcodec_free_context(&enc_ctx);avio_closep(&ofmt_ctx->pb);avformat_free_context(ofmt_ctx);}}av_packet_free(&packet);av_packet_free(&packet2);av_frame_free(&frame);avcodec_free_context(&dec_ctx);avcodec_free_context(&enc_ctx);avio_closep(&ofmt_ctx->pb);avformat_close_input(&ofmt_ctx);avformat_close_input(&ifmt_ctx);// note: the internal buffer could have changed, and be != avio_ctx_bufferif (avio_ctx) {av_freep(&avio_ctx->buffer);av_freep(&avio_ctx);}thread_packet.join();fprintf(stdout, "test finish\n");return 0;
}
执行结果如下图所示:

可调用ffprobe.exe验证每个生成的视频文件的总帧数,如1.mp4,执行如下命令:
ffprobe.exe -v error -select_streams v:0 -show_entries stream=nb_frames -of default=nokey=1:noprint_wrappers=1 1.mp4
GitHub:https://github.com/fengbingchun/OpenCV_Test
相关文章:
FFmpeg将编码后数据保存成mp4
以下测试代码实现的功能是:持续从内存块中获取原始数据,然后依次进行解码、编码、最后保存成mp4视频文件。 可保存成单个视频文件,也可指定每个视频文件的总帧数,保存多个视频文件。 为了便于查看和修改,这里将可独立的…...
设置VsCode 将打开的多个文件分行(栏)排列,实现全部显示
目录 1. 前言 2. 设置VsCode 多文件分行(栏)排列显示 1. 前言 主流编程IDE几乎都有排列切换选择所要查看的文件功能,如下为Visual Studio 2022的该功能界面: 图 1 图 2 当在Visual Studio 2022打开很多文件时,可以按照图1、图2所示找到自…...
Vue.js2+Cesium1.103.0 六、标绘与测量
Vue.js2Cesium1.103.0 六、标绘与测量 点,线,面的绘制,可实时编辑图形,点击折线或多边形边的中心点,可进行添加线段移动顶点位置等操作,并同时计算出点的经纬度,折线的距离和多边形的面积。 De…...
【redis 延时队列】使用go-redis的list做异步,生产消费者模式
分享一个用到的,使用go-redis的list做异步,生产消费者模式,接着再用 go 协程去检测队列里是否有东西去消费 如果队列为空,就会一直pop,空轮询导致 cpu 资源浪费和redis qps无效升高,所以可以通过 time.Sec…...
激光焊接塑料多点测试全画面穿透率测试仪
工程塑料由于其具有高比强度、电绝缘性、耐磨性、耐腐蚀性等优点,已广泛应用于各个重要领域。另一方面,工程塑料还具有良好的焊接性,是制成复合材料的基体材料的优良选择,因此目前已成为国内外新型复合材料的研究热点。 工程塑料…...
用 Uno 当烧录器给 atmega328 烧录 bootloader
用 Uno 当烧录器给 atmega328 烧录 bootloader date: 2023-8-10 https://backmountaindevil.github.io/#/hackaday/arduino/isp 引脚接线 把两个板子的 11(MOSI)、12(MISO)、13(SCK)、5V、GND 两两相连,还要把 Uno(烧录器)的 10 接到atmeg…...
spring boot策略模式实用: 告警模块为例
spring boot策略模式实用: 告警模块 0 涉及知识点 策略模式, 模板方法, 代理, 多态, 反射 1 需求概括 场景: 每隔一段时间, 会获取设备运行数据, 如通过温湿度计获取到当前环境温湿度;需求: 对获取回来的进行分析, 超过配置的阈值需要产生对应的告警 2 方案设计 告警的类…...
Camunda 7.x 系列【10】使用 Rest API 运行流程实例
有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 2.7.9 本系列Camunda 版本 7.19.0 源码地址:https://gitee.com/pearl-organization/camunda-study-demo 文章目录 1. 前言2. 官方接口文档3. 本地接口文档3.1 Postman3.2 Camunda Platform Run Swagger3.3 S…...
Python-OpenCV中的图像处理-边缘检测
Python-OpenCV中的图像处理-边缘检测 边缘检测Canny算子 边缘检测Canny算子 Canny 边缘检测是一种非常流行的边缘检测算法,是 John F.Canny 在 1986 年提出的。它是一个有很多步构成的算法:噪声去除、计算图像梯度、非极大值抑制、滞后阀值等。 Canny(i…...
一文了解Java序列化和反序列化:对象的存储与传输
一文了解Java序列化和反序列化:对象的存储与传输 作者:Stevedash 发布时间:2023年8月9日 21点30分 前言 Java序列化是一项强大而重要的技术,它允许我们将对象转换为字节流,以便在存储、传输和重建时使用。在本文中&…...
react-codemirror2 编辑器需点击一下或者延时才显示数据的问题
现象: <Codemirror/>组件的数据已经赋上值的情况下,初始状态不渲染数据,需要点击编辑框获取焦点后才展示,或者延迟了几秒才显示出来。 原因: 指定了一些依赖的版本,可能不兼容了一些功能,…...
火山引擎联合Forrester发布《中国云原生安全市场现状及趋势白皮书》,赋能企业构建云原生安全体系
国际权威研究咨询公司Forrester 预测,2023年全球超过40%的企业将会采用云原生优先战略。然而,云原生在改变企业上云及构建新一代基础设施的同时,也带来了一系列的新问题,针对涵盖云原生应用、容器、镜像、编排系统平台以及基础设施…...
需要数电发票接口的,先熟悉下数电发票基本常识
最近有一些技术小伙伴来咨询数电发票接口的时候,对数电发票的一些常识不太了解, 导致沟通起来比较困难。比较典型的这三个问题: 一、开具数电票时,如何设置身份认证频次? 请公司的法定代表人或财务负责人登录江苏省电…...
node-sass是什么
一、Sass(Syntactically Awesome Style Sheets) 是一种CSS预处理器,它扩展了CSS的功能并提供了更强大的样式表语言。Sass允许开发人员使用变量、嵌套规则、混合(Mixins)、继承等高级功能来编写更简洁、可维护的样式代…...
C语言指针之 进阶
前言 今天来较为深入的介绍一下指针,希望大家能有所收获~ 那么,先进行一些简单的基础知识复习吧。 字符指针 格式:char * 补充: 表达式“abcdef”的值是首字符a的地址 所以当像下面这么使用时,它的含…...
C++单例模式
文章目录 1、什么是单例2、一个好的单例应该具备的条件3、懒汉模式与饿汉模式4、单例实现:线程安全、内存安全的懒汉式单例(基于C11的智能指针和互斥锁) 1、什么是单例 单例 Singleton 是设计模式的一种,其特点是只提供唯一一个类…...
C++ 析构函数
析构函数 析构函数于构造函数相对应,构造函数是对象创建的时候自动调用的,而析构函数就是对象在销毁的时候自动调用的 特点: 1)构造函数可以有多个来构成重载,但析构函数只能有一个,不能构成重载 2&…...
CSS——字体选择
在网页设计和开发中,字体选择是一个非常重要的因素。字体不仅仅是文字的表现形式,它们还能够传达出一种特定的情感和风格。在CSS中,我们可以通过使用字体代码来定义网页中使用的字体。 CSS提供了一种简单而灵活的方式来设置字体。通过使用fo…...
SpringBoot自动装配及run方法原理探究
自动装配 1、pom.xml spring-boot-dependencies:核心依赖在父工程中!我们在写或者引入一些SpringBoot依赖的时候,不需要指定版本,就因为有这些版本仓库 1.1 其中它主要是依赖一个父工程,作用是管理项目的资源过滤及…...
Mybatis实现JsonObject对象与JSON之间交互
项目中使用PostGresql数据库进行数据存储,表中某字段为Json类型,用于存储Json格式数据。PG数据库能够直接存储Json算是一大特色,很多特定情境下使用直接存储Json字段数据能够大量节省开发时间,提高后台数据查询和转换效率。 1、基…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
tomcat指定使用的jdk版本
说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...
路由基础-路由表
本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中,往往存在多个不同的IP网段,数据在不同的IP网段之间交互是需要借助三层设备的,这些设备具备路由能力,能够实现数据的跨网段转发。 路由是数据通信网络中最基…...
Easy Excel
Easy Excel 一、依赖引入二、基本使用1. 定义实体类(导入/导出共用)2. 写 Excel3. 读 Excel 三、常用注解说明(完整列表)四、进阶:自定义转换器(Converter) 其它自定义转换器没生效 Easy Excel在…...
多模态学习路线(2)——DL基础系列
目录 前言 一、归一化 1. Layer Normalization (LN) 2. Batch Normalization (BN) 3. Instance Normalization (IN) 4. Group Normalization (GN) 5. Root Mean Square Normalization(RMSNorm) 二、激活函数 1. Sigmoid激活函数(二分类&…...
