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、基…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
