当前位置: 首页 > news >正文

【FFmpeg】调用ffmpeg库进行RTMP推流和拉流

【FFmpeg】调用ffmpeg库实现RTMP推流

  • 1.FFmpeg编译
  • 2.RTMP服务器搭建
  • 3.调用FFmpeg库实现RTMP推流和拉流
    • 3.1 基本框架
    • 3.2 实现代码
    • 3.3 测试
      • 3.3.1 推流
      • 3.3.2 拉流

参考:雷霄骅博士, 调用ffmpeg库进行RTMP推流
====== 示例工程 ======
【FFmpeg】调用FFmpeg库实现264软编
【FFmpeg】调用FFmpeg库实现264软解

1.FFmpeg编译

参考: FFmpeg在Windows下的编译
本文使用FFmpeg-7.0版本

2.RTMP服务器搭建

将本机配置成为服务器,实现本地的推流和拉流操作。RTMP服务器的搭建参考:RTMP服务器的搭建

RTMP是Adobe提出的一种应用层的协议,用于解决多媒体数据传输流的多路复用(Multiplexing)和分包(packetizing)的问题传输,传输媒体的格式为FLV,因此本文推流的格式是flv格式。flv文件可以使用ffmpeg命令行,从yuv文件转换而来。

3.调用FFmpeg库实现RTMP推流和拉流

3.1 基本框架

在实现时,对参考博客中的部分函数进行了修改

  1. 不再使用av_register_all()函数对编解码器进行初始化
  2. 增加av_log_set_level(AV_LOG_TRACE)增加日志输出信息
  3. 使用本机IP127.0.0.1
  4. 修改部分参数的调用,因为部分变量存储的位置发生了变化
  5. 修改部分函数的调用,因为使用的FFmpeg版本不同
  6. 将推流和拉流的部分合并,用一套代码实现

在编码过程当中,主要使用了如下的函数

函数名作用
av_log_set_level配置输出日志级别 (AV_LOG_TRACE最详细)
avformat_network_init初始化网络模块
avformat_open_input打开输入文件,并且将文件信息赋值给AVFormatContext保存
avformat_find_stream_info根据AVFormatContext查找流信息
av_dump_format将AVFormatContext中的媒体文件的信息进行格式化输出
avformat_alloc_output_context2根据format_name(或filename或oformat)创建输出文件的AVFormatContext信息
avformat_new_stream根据AVFormatContext和AVCodecContext创建新的流
avcodec_parameters_copy拷贝AVCodecParameters
avio_open根据url进行AVIOContext的创建与初始化(这个url在推流时就是服务器地址)
avformat_write_header为流分配priv_data并且将流的头信息写入到输出媒体文件
av_read_frame根据AVFormatContext所提供的的信息读取一帧,存入AVPacket
av_interleaved_write_frame以交错的方式将帧送入到媒体文件中
av_packet_unref释放AVPacket
av_write_trailer将流的尾部写入到输出的媒体文件中,并且释放文件中的priv_data
avformat_close_input释放AVFormatContext

从使用的函数来看,主要的操作流程和数据流走向大约为:

  1. 初始化网络模块,为RTMP传输进行准备(avformat_network_init)
  2. 打开输入文件,创建输入文件结构体并且读取输入文件信息(avformat_open_input),此时也会创建输入的流信息结构体
  3. 根据输入文件查找流信息,赋值给流信息结构体(avformat_find_stream_info)
  4. 打印输入文件信息(av_dump_format)
  5. 根据输出文件信息来创建输出文件结构体(avformat_alloc_output_context2)
  6. 创建输出流(avformat_new_stream)
  7. 将输入流的参数拷贝给输出给输出流(avcodec_parameters_copy)
  8. 打印输出文件信息(av_dump_format)
  9. 打开输出口,准备推流(avio_open)
  10. 写入流的头部信息(avformat_write_header)
  11. 读取一帧信息,存储到AVPacket中(av_read_frame)
  12. 处理时间戳;PTS是播放时间戳,告诉播放器播放这一帧的时间;DTS是解码时间戳,告诉播放器解码这一帧的时间;PTS通常是按照递增顺序排列的。这里雷博士认为延时很重要,如果不对前后帧推流的时间进行控制,帧会瞬时推送到服务器端,会出现服务器无法正常接收帧的情况
  13. 将帧推流(av_interleaved_write_frame)
  14. 写入流的尾部信息(av_write_trailer)
  15. 释放结构体信息(av_packet_unref、av_write_trailer和avformat_close_input)

3.2 实现代码

在调试时,发现如果AVPacket这里定义如果是指针的话,会出现av_read_frame第二帧读取失败的情况,这里有待进一步学习。

在代码中,利用bool is_sender来控制是发送还是接收,发送和接收都使用同一套代码,只是在时间戳部分有所区别,即发送端需要计算而接收端不需要使用。不过这里的in_url和out_url还是固定的,实际使用时得重新配置。

#pragma warning(disable : 4996)#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "streamer.h"#ifdef _WIN32
//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libavutil/opt.h"
#include "libavutil/time.h"
#include "libavutil/timestamp.h"
#include "libavutil/mathematics.h"
#include "libavutil/log.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libavutil/opt.h"
#ifdef __cplusplus
};
#endif
#endifint streamer_internal(const char* in_url, const char* out_url, bool is_sender)
{// set log level// av_log_set_level(AV_LOG_TRACE);AVOutputFormat* av_out_fmt = NULL;AVFormatContext* av_in_fmt_ctx = NULL;AVFormatContext* av_out_fmt_ctx = NULL;AVPacket av_pkt;const char* in_filename = in_url;const char* out_filename = out_url;int ret = 0;int i = 0;int video_idx = -1;int frame_idx = 0;int64_t start_time = 0;// bool b_sender = 0;//in_filename = "enc_in_all.flv"; // input flv file//out_filename = "rtmp://127.0.0.1:1935/live/stream"; // output url//in_filename = "rtmp://127.0.0.1:1935/live/stream"; // input flv file//out_filename = "receive.flv"; // output url// av_register_all(); // 新版本ffmpeg不再使用// init networkavformat_network_init();if ((ret = avformat_open_input(&av_in_fmt_ctx, in_filename, 0, 0)) < 0) {fprintf(stderr, "Could not open input file.");goto end;}if ((ret = avformat_find_stream_info(av_in_fmt_ctx, 0)) < 0) {fprintf(stderr, "Failed to retrive input stream information");goto end;}for (i = 0; i < av_in_fmt_ctx->nb_streams; i++) {if (av_in_fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {video_idx = i;break;}}// 将AVFormatContext结构体中媒体文件的信息进行格式化输出av_dump_format(av_in_fmt_ctx, 0, in_filename, 0);// Output// av_out_fmt_ctx是函数执行成功之后的上下文信息结构体// "flv"是输出格式// out_filename是输出文件ret = avformat_alloc_output_context2(&av_out_fmt_ctx, NULL, NULL, out_filename); // RTMP// ret = avformat_alloc_output_context2(&av_out_fmt_ctx, NULL, "flv", out_filename); // RTMP// avformat_alloc_output_context2(&av_out_fmt_ctx, NULL, "mpegts", out_filename); // UDPif (ret < 0) {fprintf(stderr, "Could not create output context, error code:%d\n", ret);//ret = AVERROR_UNKNOWN;goto end;}// av_out_fmt_ctx->oformat;for (i = 0; i < av_in_fmt_ctx->nb_streams; i++) {AVStream* in_stream = av_in_fmt_ctx->streams[i];// 为av_out_fmt_ctx创建一个新的流,第二个参数video_codec没有被使用AVStream* out_stream = avformat_new_stream(av_out_fmt_ctx, av_in_fmt_ctx->video_codec);//AVStream* out_stream = avformat_new_stream(av_out_fmt_ctx, in_stream->codec->codec);if (!out_stream) {fprintf(stderr, "Failed to allocating output stream\n");ret = AVERROR_UNKNOWN;goto end;}// Copy the setting of AVCodecContext// ret = avcodec_copy_context(out_stream->codecpar, in_stream->codecpar);ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);if (ret < 0) {fprintf(stderr, "Failed to copy context from input to output stream codec context\n");goto end;}out_stream->codecpar->codec_tag = 0;if (av_out_fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {// out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;// out_stream->event_flags |= AV_CODEC_FLAG_GLOBAL_HEADER;}}// Dump format// 将AVFormatContext结构体中媒体文件的信息进行格式化输出av_dump_format(av_out_fmt_ctx, 0, out_filename, 1);// Open output URL if (!(av_out_fmt_ctx->oformat->flags & AVFMT_NOFILE)) {// 打开文件ret = avio_open(&av_out_fmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);if (ret < 0) {fprintf(stderr, "Could not open output URL '%s'", out_filename);goto end;}}// Write file headerret = avformat_write_header(av_out_fmt_ctx, NULL);if (ret < 0) {fprintf(stderr, "Error occured when opening output URL\n");goto end;}if (is_sender) {start_time = av_gettime();}while(1) {AVStream* in_stream;AVStream* out_stream;// get an AVPacket// 这里如果使用av_pkt指针的话,第二帧时就会出错ret = av_read_frame(av_in_fmt_ctx, &av_pkt);if (ret < 0) {break;}// write ptsif (av_pkt.pts == AV_NOPTS_VALUE && is_sender) {// write ptsAVRational time_base1 = av_in_fmt_ctx->streams[video_idx]->time_base;// Duration between 2 frames (us)int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(av_in_fmt_ctx->streams[video_idx]->r_frame_rate);// parameters// pts是播放时间戳,告诉播放器什么时候播放这一帧视频,PTS通常是按照递增顺序排列的,以保证正确的时间顺序和播放同步// dts是解码时间戳,告诉播放器什么时候解码这一帧视频av_pkt.pts = (double)(frame_idx * calc_duration) / (double)(av_q2d(time_base1) * AV_TIME_BASE);av_pkt.dts = av_pkt.pts;av_pkt.duration = (double)calc_duration / (double)(av_q2d(time_base1) * AV_TIME_BASE);}// important: delayif (av_pkt.stream_index == video_idx && is_sender) {AVRational time_base = av_in_fmt_ctx->streams[video_idx]->time_base;AVRational time_base_q = { 1, AV_TIME_BASE };int64_t pts_time = av_rescale_q(av_pkt.dts, time_base, time_base_q);int64_t now_time = av_gettime() - start_time;if (pts_time > now_time) {av_usleep(pts_time - now_time);}// av_usleep(50);}in_stream = av_in_fmt_ctx->streams[av_pkt.stream_index];out_stream = av_out_fmt_ctx->streams[av_pkt.stream_index];// copy packet// convert PTS/DTSav_pkt.pts = av_rescale_q_rnd(av_pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));av_pkt.dts = av_rescale_q_rnd(av_pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));av_pkt.duration = av_rescale_q(av_pkt.duration, in_stream->time_base, out_stream->time_base);av_pkt.pos = -1;// print to screenif (av_pkt.stream_index == video_idx) {if (is_sender) {fprintf(stdout, "Send %8d video frames to output URL\n", frame_idx);}else {fprintf(stdout, "Receive %8d video frames from input URL\n", frame_idx);}frame_idx++;}ret = av_interleaved_write_frame(av_out_fmt_ctx, &av_pkt);// ret = av_write_frame(av_out_fmt_ctx, av_pkt);if (ret < 0) {fprintf(stderr, "Error muxing packet, error code:%d\n", ret);break;}// av_packet_free(&av_pkt);av_packet_unref(&av_pkt);} // write file trailerav_write_trailer(av_out_fmt_ctx);end:avformat_close_input(&av_in_fmt_ctx);// close output/*if (av_out_fmt_ctx && !(av_out_fmt->flags & AVFMT_NOFILE)) {avio_close(av_out_fmt_ctx->pb);}*//*avformat_free_context(av_out_fmt_ctx);if (ret < 0 && ret != AVERROR_EOF) {fprintf(stderr, "Error occured\n");return -1;}*/return 0;
}int streamer()
{const char* in_url = "rtmp://127.0.0.1:1935/live/stream"; // input flv fileconst char* out_url = "receive.flv"; // output urlbool is_sender = 0;streamer_internal(in_url, out_url, is_sender);return 0;
}

3.3 测试

3.3.1 推流

使用代码进行推流,可以访问http://localhost/stat地址查看推流的状态。

...
...Metadata:encoder         : Lavf61.3.100Duration: 00:00:40.00, start: 0.000000, bitrate: 18849 kb/sStream #0:0: Video: h264 (High), yuv420p(progressive), 1920x1200, 25 fps, 25 tbr, 1k tbn
Output #0, flv, to 'rtmp://127.0.0.1:1935/live/stream':Stream #0:0: Video: h264 (High), yuv420p(progressive), 1920x1200, q=2-31
Send        0 video frames to output URL
Send        1 video frames to output URL
Send        2 video frames to output URL
Send        3 video frames to output URL
Send        4 video frames to output URL
Send        5 video frames to output URL
Send        6 video frames to output URL
Send        7 video frames to output URL
Send        8 video frames to output URL
Send        9 video frames to output URL
Send       10 video frames to output URL
Send       11 video frames to output URL
Send       12 video frames to output URL
Send       13 video frames to output URL
Send       14 video frames to output URL
Send       15 video frames to output URL
Send       16 video frames to output URL
Send       17 video frames to output URL
Send       18 video frames to output URL
...
...

3.3.2 拉流

拉流时,需要对齐推流和拉流时的RTMP地址。如果不对齐,拉流将会一直处于idel状态。

Input #0, flv, from 'rtmp://127.0.0.1:1935/live/stream':Metadata:|RtmpSampleAccess: trueServer          : NGINX RTMP (github.com/arut/nginx-rtmp-module)displayWidth    : 1920displayHeight   : 1200fps             : 25profile         :level           :Duration: 00:00:00.00, start: 59.120000, bitrate: N/AStream #0:0: Video: h264 (High), yuv420p(progressive), 1920x1200, 25 fps, 25 tbr, 1k tbn
Output #0, flv, to 'receive.flv':Stream #0:0: Video: h264 (High), yuv420p(progressive), 1920x1200, q=2-31
Receive        0 video frames from input URL
Receive        1 video frames from input URL
Receive        2 video frames from input URL
Receive        3 video frames from input URL
Receive        4 video frames from input URL
Receive        5 video frames from input URL
Receive        6 video frames from input URL
Receive        7 video frames from input URL
Receive        8 video frames from input URL
Receive        9 video frames from input URL
Receive       10 video frames from input URL
Receive       11 video frames from input URL
Receive       12 video frames from input URL

另外,推流和拉流也可以使用其他已有工具,例如推流直接使用ffmpeg.exe,拉流使用ffplay.exe(或VLC Media Player)

CSDN: https://blog.csdn.net/weixin_42877471
Github: https://github.com/DoFulangChen/

相关文章:

【FFmpeg】调用ffmpeg库进行RTMP推流和拉流

【FFmpeg】调用ffmpeg库实现RTMP推流 1.FFmpeg编译2.RTMP服务器搭建3.调用FFmpeg库实现RTMP推流和拉流3.1 基本框架3.2 实现代码3.3 测试3.3.1 推流3.3.2 拉流 参考&#xff1a;雷霄骅博士, 调用ffmpeg库进行RTMP推流 示例工程 【FFmpeg】调用FFmpeg库实现264软编 【FFmpeg】…...

Multisim 14 常见电子仪器的使用和Multisim的使用

multisim multisim&#xff0c;即电子电路仿真设计软件。Multisim是美国国家仪器&#xff08;NI&#xff09;有限公司推出的以Windows为基础的仿真工具&#xff0c;适用于板级的模拟/数字电路板的设计工作。它包含了电路原理图的图形输入、电路硬件描述语言输入方式&#xff0…...

【2024高校网络安全管理运维赛】巨细记录!

2024高校网络安全管理运维赛 文章目录 2024高校网络安全管理运维赛MISC签到考点&#xff1a;动态图片分帧提取 easyshell考点&#xff1a;流量分析 冰蝎3.0 Webphpsql考点&#xff1a;sql万能钥匙 fileit考点&#xff1a;xml注入 外带 Cryptosecretbit考点&#xff1a;代码阅读…...

Nuxt.js实战:Vue.js的服务器端渲染框架

创建Nuxt.js项目 首先&#xff0c;确保你已经安装了Node.js和yarn或npm。然后&#xff0c;通过命令行创建一个新的Nuxt.js项目&#xff1a; yarn create nuxt-app my-nuxt-project cd my-nuxt-project在创建过程中&#xff0c;你可以选择是否需要UI框架、预处理器等选项&…...

提高Rust安装与更新的速度

一、背景 因为rust安装过程中&#xff0c;默认的下载服务器为crates.io&#xff0c;这是一个国外的服务器&#xff0c;国内用户使用时&#xff0c;下载与更新的速度非常慢&#xff0c;因此&#xff0c;我们需要使用一个国内的服务器来提高下载与更新的速度。 本文推荐使用字节…...

【linux软件基础知识】内核代码中的就绪队列简化示例

在内核代码中,就绪队列通常使用允许高效插入和删除进程的数据结构来表示。 用于表示就绪队列的一种常见数据结构是链表。 以下是如何使用链表在内核代码中表示就绪队列的简化示例: struct task_struct {// Process control block (PCB) fields// ...struct task_struct *nex…...

《C++学习笔记---初阶篇6》---string类 上

目录 1. 为什么要学习string类 1.1 C语言中的字符串 2. 标准库中的string类 2.1 string类(了解) 2.2 string类的常用接口说明 2.2.1. string类对象的常见构造 2.2.2. string类对象的容量操作 2.2.3.再次探讨reserve与resize 2.2.4.string类对象的访问及遍历操作 2.2.5…...

mysql中的页和行

页 行即表中的真实行&#xff0c;‘行式数据库’的由来 虽然MySQL的数据文件&#xff08;例如.ibd文件&#xff09;中的数据页在物理上是通过链表连接的&#xff0c;但是在逻辑上&#xff0c;MySQL使用B树来组织和访问数据。 行&#xff1a;主要是dynamic类型...

Vim常用快捷键

这个是我的草稿本记录一下防止丢失&#xff0c;以后有时间进行整理 0 或功能键[Home]这是数字『 0 』&#xff1a;移动到这一行的最前面字符处 (常用)$ 或功能键[End]移动到这一行的最后面字符处(常用)G移动到这个档案的最后一行(常用)nGn 为数字。移动到这个档案的第 n 行。例…...

力扣题目汇总分析 利用树形DP解决问题

树里 任意两个节点之间的问题。而不是根节点到叶子节点的问题或者是父节点到子节点的问题。通通一个套路&#xff0c;即利用543的解题思路。 543.二叉树的直径 分析 明确&#xff1a;二叉树的 直径 是指树中任意两个节点之间最长路径的 长度。两个节点之间的最长路径是他们之…...

GO语言核心30讲 实战与应用 (第二部分)

原站地址&#xff1a;Go语言核心36讲_Golang_Go语言-极客时间 一、sync.WaitGroup和sync.Once 1. sync.WaitGroup 比通道更加适合实现一对多的 goroutine 协作流程。 2. WaitGroup类型有三个指针方法&#xff1a;Wait、Add和Done&#xff0c;以及内部有一个计数器。 (1) Wa…...

linux设置挂载指定的usb,自动挂载

一、设置指定的USB 在Linux系统中&#xff0c;如果您只想让系统挂载特定的USB设备&#xff0c;而忽略其他的USB设备&#xff0c;可以通过创建自定义的udev规则来实现。以下是设置系统只能挂载指定USB设备的基本步骤&#xff1a; 确定USB设备的属性&#xff1a; 首先&#xff0…...

简站WordPress主题

简站WordPress主题是一种专为建立网站而设计的WordPress模板&#xff0c;它旨在简化网站建设过程&#xff0c;使得用户能够更容易地创建和管理自己的网站。简站WordPress主题具有以下特点&#xff1a; 易用性&#xff1a;简站WordPress主题被设计为简单易用&#xff0c;适合各…...

is和==的关系

Python中is和的关系 is判断两个变量是不是指的是同一个内存地址&#xff0c;也就是通过id()函数判断 判断两个变量的值是不是相同 a [1, 2, 3, 4] b [1, 2, 3, 4] print(id(a)) # 2298268712768 print(id(b)) # 2298269716992 print(a is b) # False print(a b) # Tr…...

璩静是为了薅百度羊毛

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 百度副总裁璩静离职了&#xff0c;网传她的年薪是1500万&#xff0c;而璩静在4月24日注册了一个文化传媒公司&#xff0c;大家都认为璩静是在为离职做准备。但松松我认为不是。 我认为&#xff1a;璩静成立新公司是…...

Element ui input 限制只能输入数字,且只能有两位小数

<el-form-item label"整体进度&#xff1a;" prop"number"> <el-input v-model"formInline.number" input"handleInput" placeholder"百分比" clearable></el-input>% </el-form-item&g…...

吃掉 N 个橘子的最少天数

代码实现&#xff1a; 方法一&#xff1a;递归——超时 #define min(a, b) ((a) > (b) ? (b) : (a))int minDays(int n) {if (n 1 || n 2) {return n;}if (n % 3 0) {if (n % 2 0) {return min(min(minDays(n - 1), minDays(n / 2)), minDays(n - 2 * (n / 3))) 1;} e…...

JavaScript 之 toString()方法详解

一、前言&#xff1a; ​ 在 JavaScript 中&#xff0c;toString() 方法是很多数据类型内置的方法&#xff0c;它被用于将特定的数据类型转换为字符串。但是在不同的数据类型中的作用并非完全相同&#xff0c;下面就来详细讲解一下 toString() 方法在各种数据类型中的使用和作用…...

PPMP_char3

PMPP char3 – Multidimensional grids and data ​ 五一过后&#xff0c;有些工作要赶&#xff0c;抽出时间更新一下。这一章基本都熟练掌握&#xff0c;在做习题过程中有一些思考。这里涉及到了一点点GEMM&#xff08;矩阵乘&#xff09;&#xff0c;GEMM有太多可深挖的了&a…...

VulkanSDK Demos vkcube 编译失败

操作系统: Windows 11 23H2 Vulkan 版本: 1.3.2.280.0 Visual Studio 版本: 2022 在VulkanSDK/Demos目录下存在一个demo solution,其中包含两个project, vkcube和vkcubepp,两个分别为C语言和C写的示例程序, 但是直接编译这两个project时会编译失败,报了以下错误: fatal err…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)

RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发&#xff0c;后来由Pivotal Software Inc.&#xff08;现为VMware子公司&#xff09;接管。RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写。广泛应用于各种分布…...

Caliper 配置文件解析:fisco-bcos.json

config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)

引言 工欲善其事&#xff0c;必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后&#xff0c;我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集&#xff0c;就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...

在 Spring Boot 中使用 JSP

jsp&#xff1f; 好多年没用了。重新整一下 还费了点时间&#xff0c;记录一下。 项目结构&#xff1a; pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]

报错信息&#xff1a;libc.so.6: cannot open shared object file: No such file or directory&#xff1a; #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...