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

[ffmpeg系列 03] 文件、流地址(视频)解码为YUV

一  代码

ffmpeg版本5.1.2,dll是:ffmpeg-5.1.2-full_build-shared。x64的。

文件、流地址对使用者来说是一样。
流地址(RTMP、HTTP-FLV、RTSP等):信令完成后,才进行音视频传输。信令包括音视频格式、参数等协商。

接流的在实际中的应用:1 展示,播放。2 给算法用,一般是需要RGB格式的。

#ifndef _DECODE_H264_H_
#define _DECODE_H264_H_
#include <string>extern  "C"
{
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavutil/avutil.h"
#include "libavutil/mathematics.h"
#include "libavutil/time.h"
#include "libavutil/pixdesc.h"
#include "libavutil/display.h"
};#pragma  comment(lib, "avformat.lib")
#pragma  comment(lib, "avutil.lib")
#pragma  comment(lib, "avcodec.lib")
#pragma  comment(lib, "swscale.lib")class CDecodeH264
{
public:CDecodeH264();~CDecodeH264();public:public:int DecodeH264();int  Start();int  Close();int  DecodeH264File_Init();int  ReleaseDecode();void H264Decode_Thread_Fun();std::string  dup_wchar_to_utf8(const wchar_t* wstr);double get_rotation(AVStream *st);public:AVFormatContext*       m_pInputFormatCtx = nullptr;AVCodecContext*        m_pVideoDecodeCodecCtx = nullptr;const AVCodec*           m_pCodec = nullptr;	SwsContext*                m_pSwsContext = nullptr;AVFrame*                    m_pFrameScale = nullptr;AVFrame*                    m_pFrameYUV = nullptr;AVPacket*                   m_pAVPacket = nullptr;enum AVMediaType		m_CodecType;int                                    m_output_pix_fmt;int                                    m_nVideoStream = -1;int                                    m_nFrameHeight = 0;int                                    m_nFrameWidth = 0;int                                    m_nFPS;int                                    m_nVideoSeconds;FILE*         m_pfOutYUV = nullptr;FILE*         m_pfOutYUV2 = nullptr;
};
#endif
#include "DecodeH264.h"
#include <thread>
#include <functional>
#include <codecvt>
#include <locale>char av_error2[AV_ERROR_MAX_STRING_SIZE] = { 0 };
#define av_err2str2(errnum) av_make_error_string(av_error2, AV_ERROR_MAX_STRING_SIZE, errnum)CDecodeH264::CDecodeH264()
{	
}CDecodeH264::~CDecodeH264()
{ReleaseDecode();
}std::string  CDecodeH264::dup_wchar_to_utf8(const wchar_t* wstr)
{std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;return converter.to_bytes(wstr);
}//Side data : 
//displaymatrix: rotation of - 90.00 degrees
double CDecodeH264::get_rotation(AVStream *st)
{uint8_t* displaymatrix = av_stream_get_side_data(st,AV_PKT_DATA_DISPLAYMATRIX, NULL);double theta = 0;if (displaymatrix)theta = -av_display_rotation_get((int32_t*)displaymatrix);theta -= 360 * floor(theta / 360 + 0.9 / 360);if (fabs(theta - 90 * round(theta / 90)) > 2)av_log(NULL, AV_LOG_WARNING, "Odd rotation angle.\n""If you want to help, upload a sample ""of this file to https://streams.videolan.org/upload/ ""and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)");return theta;
}int CDecodeH264::DecodeH264File_Init()
{avformat_network_init(); //流地址需要m_pInputFormatCtx = avformat_alloc_context();std::string strFilename = dup_wchar_to_utf8(L"测试.h264");//std::string strFilename = dup_wchar_to_utf8(L"rtmp://127.0.0.1/live/now");	int ret = avformat_open_input(&m_pInputFormatCtx, strFilename.c_str(), nullptr, nullptr);if (ret != 0) {char* err_str = av_err2str2(ret);printf("fail to open filename: %s, return value: %d,  %s\n", strFilename.c_str(), ret, err_str);return -1;}ret = avformat_find_stream_info(m_pInputFormatCtx, nullptr);if (ret < 0) {char* err_str = av_err2str2(ret);printf("fail to get stream information: %d, %s\n", ret, err_str);		return -1;}for (int i = 0; i < m_pInputFormatCtx->nb_streams; ++i) {const AVStream* stream = m_pInputFormatCtx->streams[i];if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {m_nVideoStream = i;printf("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 (m_nVideoStream == -1) {printf("no video stream\n");return -1;}printf("m_nVideoStream=%d\n", m_nVideoStream);//获取旋转角度double theta = get_rotation(m_pInputFormatCtx->streams[m_nVideoStream]);m_pVideoDecodeCodecCtx = avcodec_alloc_context3(nullptr);avcodec_parameters_to_context(m_pVideoDecodeCodecCtx,\m_pInputFormatCtx->streams[m_nVideoStream]->codecpar);m_pCodec = avcodec_find_decoder(m_pVideoDecodeCodecCtx->codec_id);if (m_pCodec == nullptr){return -1;}m_nFrameHeight = m_pVideoDecodeCodecCtx->height;m_nFrameWidth = m_pVideoDecodeCodecCtx->width;printf("w=%d h=%d\n", m_pVideoDecodeCodecCtx->width, m_pVideoDecodeCodecCtx->height);if (avcodec_open2(m_pVideoDecodeCodecCtx, m_pCodec, nullptr) < 0){return -1;}//读文件知道视频宽高m_output_pix_fmt = AV_PIX_FMT_YUV420P; //AV_PIX_FMT_NV12;m_pSwsContext = sws_getContext(m_pVideoDecodeCodecCtx->width, m_pVideoDecodeCodecCtx->height,m_pVideoDecodeCodecCtx->pix_fmt, m_pVideoDecodeCodecCtx->width, m_pVideoDecodeCodecCtx->height,(AVPixelFormat)m_output_pix_fmt, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);//解码后的视频数据m_pFrameScale = av_frame_alloc();m_pFrameScale->format = m_output_pix_fmt;m_pFrameYUV = av_frame_alloc();m_pFrameYUV->format = m_output_pix_fmt; //mAVFrame.format is not setm_pFrameYUV->width = m_pVideoDecodeCodecCtx->width;m_pFrameYUV->height = m_pVideoDecodeCodecCtx->height;printf("m_pFrameYUV pix_fmt=%d\n", m_pVideoDecodeCodecCtx->pix_fmt);av_frame_get_buffer(m_pFrameYUV, 64);char cYUVName[256];sprintf_s(cYUVName, "%d_%d_%s.yuv", m_nFrameWidth, m_nFrameHeight, av_get_pix_fmt_name(m_pVideoDecodeCodecCtx->pix_fmt));fopen_s(&m_pfOutYUV, cYUVName, "wb");char cYUVName2[256];sprintf_s(cYUVName2, "%d_%d_%s_2.yuv", m_nFrameWidth, m_nFrameHeight, av_get_pix_fmt_name(m_pVideoDecodeCodecCtx->pix_fmt));fopen_s(&m_pfOutYUV2, cYUVName2, "wb");printf("leave init\n");return   0;
}void CDecodeH264::H264Decode_Thread_Fun()
{int   nFrameFinished = 0;int i = 0;int  ret;m_pAVPacket = av_packet_alloc();while (true) {ret = av_read_frame(m_pInputFormatCtx, m_pAVPacket);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {av_packet_unref(m_pAVPacket);printf("read_frame break");break;}if (m_pAVPacket->stream_index == m_nVideoStream){int  send_packet_ret = avcodec_send_packet(m_pVideoDecodeCodecCtx, m_pAVPacket);printf("encode video send_packet_ret %d\n", send_packet_ret);int receive_frame_ret = avcodec_receive_frame(m_pVideoDecodeCodecCtx, m_pFrameScale);char* err_str = av_err2str2(receive_frame_ret);printf("frame w=%d, h=%d, linesize[0]=%d, linesize[1]=%d\n", m_pFrameScale->width, m_pFrameScale->height, m_pFrameScale->linesize[0], m_pFrameScale->linesize[1]);if (receive_frame_ret == 0){++i;int iReturn = sws_scale(m_pSwsContext, m_pFrameScale->data,m_pFrameScale->linesize, 0, m_nFrameHeight,m_pFrameYUV->data, m_pFrameYUV->linesize);printf("frame w=%d, h=%d, linesize[0]=%d, linesize[1]=%d\n", m_pFrameYUV->width, m_pFrameYUV->height, m_pFrameYUV->linesize[0], m_pFrameYUV->linesize[1]);/*if (0 != iReturn){fwrite(m_pFrameYUV->data[0], 1, m_nFrameWidth * m_nFrameHeight, m_pfOutYUV);fwrite(m_pFrameYUV->data[1], 1, m_nFrameWidth * m_nFrameHeight /4, m_pfOutYUV);fwrite(m_pFrameYUV->data[2], 1, m_nFrameWidth * m_nFrameHeight /4, m_pfOutYUV);}*///用linesize更能兼容特殊的宽if (0 != iReturn){					for (int i = 0; i < m_nFrameHeight; ++i) {fwrite(m_pFrameYUV->data[0] + i * m_pFrameYUV->linesize[0], 1, m_nFrameWidth, m_pfOutYUV2);}for (int i = 0; i < m_nFrameHeight / 2; ++i) {fwrite(m_pFrameYUV->data[1] + i * m_pFrameYUV->linesize[1], 1, m_nFrameWidth / 2, m_pfOutYUV2);}for (int i = 0; i < m_nFrameHeight / 2; ++i) {fwrite(m_pFrameYUV->data[2] + i * m_pFrameYUV->linesize[2], 1, m_nFrameWidth / 2, m_pfOutYUV2);}}				}}av_packet_unref(m_pAVPacket);}
}int CDecodeH264::DecodeH264()
{if (DecodeH264File_Init() != 0){return   -1;}auto video_func = std::bind(&CDecodeH264::H264Decode_Thread_Fun, this);std::thread  video_thread(video_func);video_thread.join(); return 0;
}int CDecodeH264::Start()
{DecodeH264();return  1;
}int CDecodeH264::Close()
{return   0;
}int  CDecodeH264::ReleaseDecode()
{if (m_pSwsContext){sws_freeContext(m_pSwsContext);m_pSwsContext = nullptr;}if (m_pFrameScale){av_frame_free(&m_pFrameScale);//av_frame_alloc()对应}if (m_pFrameYUV){av_frame_free(&m_pFrameYUV);}avcodec_close(m_pVideoDecodeCodecCtx);avformat_close_input(&m_pInputFormatCtx);return 0;}
#include <iostream>
#include <Windows.h>#include "1__DecodeH264/DecodeH264.h"int main()
{CDecodeH264* m_pDecodeVideo = new CDecodeH264();m_pDecodeVideo->Start();return 0;
}

图是雷神博客的:注册函数废弃了,解码函数变了。

二  相关的结构体,方便记忆


1 AVFrame是未压缩的,解码后的数据。AVPacket是压缩的,解码前的数据。知道了这个,编码的send_frame、receive_packet,解码的send_packet、receive_frame,容易记住了。2 2个Context(上下文):Format(混合文件、流地址)、Codec(单个编码格式,比如H264、AAC,编解码实现)AVFormatContext*  m_pInputFormatCtx;AVCodecContext*   m_pVideoDecodeCodecCtx;m_pInputFormatCtx会用到的函数:avformat_open_input、avformat_find_stream_info、 av_read_frame、avformat_close_input。m_pOutputFormatCtx会用到的函数:avcodec_find_decoder、avcodec_open2、avcodec_send_packet、 avcodec_receive_frame。3 AVCodec结构体
const  AVCodec  ff_h264_decoder = {.name                  = "h264",.long_name             = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),.type                  = AVMEDIA_TYPE_VIDEO,.id                    = AV_CODEC_ID_H264,.priv_data_size        = sizeof(H264Context),.init                  = h264_decode_init,.close                 = h264_decode_end,.decode                = h264_decode_frame,……
}static const AVCodec * const codec_list[] = {
...&ff_h264_decoder,
...
};

三 兼容性问题

1 文件名带中文,需要转换。

2 播放竖屏视频(手机录的那种),获取旋转角度。

截图是ffmpeg做法:获取角度后,使用filter调整。

3 宽比较特殊,不是16,32的整数。(比如544x960,544是32的倍数)。用linesize[i]代替宽。

linesize跟cpu有关,是cpu 16、32的倍数。

其它,待更新。

四  为什么需要sws_scale?转换到统一格式I420

sws_scale作用:1 分辨率缩放、 2 不同YUV、RGB格式转换。

H264有记录编码前的YUV采样格式,chroma_format_idc,在sps里。如果没有这个字段,说明该字段用的默认值1,即yuv 4:2:0。

如果YUV的采样格式是yuv 4:2:0,也不要求缩放,不需要sws_scale。

avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);

frame->format记录了yuv的类型。

ffmpeg默认解码成:编码前的yuv格式。即m_pVideoDecodeCodecCtx->pix_fmt。

int ff_decode_frame_props(AVCodecContext *avctx, AVFrame *frame)
{...frame->format              = avctx->pix_fmt;...
}

五  不同格式的time_base

H264的time_base:1/1200000。

flv:音视频都是1/1000。

mp4:视频1/12800(帧率25,怎么算出来的?),音频:1/48000(1/采样频率)。

相关文章:

[ffmpeg系列 03] 文件、流地址(视频)解码为YUV

一 代码 ffmpeg版本5.1.2&#xff0c;dll是&#xff1a;ffmpeg-5.1.2-full_build-shared。x64的。 文件、流地址对使用者来说是一样。 流地址(RTMP、HTTP-FLV、RTSP等)&#xff1a;信令完成后&#xff0c;才进行音视频传输。信令包括音视频格式、参数等协商。 接流的在实际…...

python算法每日一练:连续子数组的最大和

这是一道关于动态规划的算法题&#xff1a; 题目描述&#xff1a; 给定一个整数数组 nums&#xff0c;请找出该数组中连续子数组的最大和&#xff0c;并返回这个最大和。 示例&#xff1a; 输入&#xff1a;[-2, 1, -3, 4, -1, 2, 1, -5, 4] 输出&#xff1a;6 解释&#xff…...

一个vue3的tree组件

https://download.csdn.net/download/weixin_41012767/88709466...

新手练习项目 4:简易2048游戏的实现(C++)

名人说&#xff1a;莫听穿林打叶声&#xff0c;何妨吟啸且徐行。—— 苏轼《定风波莫听穿林打叶声》 Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#xff09; 目录 一、效果图二、代码&#xff08;带注释&#xff09;三、说明 一、效果图 二、代码&#xff08;带…...

2023年度总结:技术沉淀、持续学习

2023年度总结&#xff1a;技术沉淀、持续学习 一、引言 今年是我毕业的第二个年头&#xff0c;也是完整的一年&#xff0c;到了做年终总结的时候了 这一年谈了女朋友&#xff0c;学习了不少技术&#xff0c;是充实且美好的一年&#xff01; 首先先看年初定的小目标&#xf…...

Unity 利用UGUI之Slider制作进度条

在Unity中使用Slider和Text组件可以制作简单的进度条。 首先在场景中右键->UI->Slider&#xff0c;新建一个Slider组件&#xff1a; 同样方法新建一个Text组件&#xff0c;最终如图&#xff1a; 创建一个进度模拟脚本&#xff0c;Slider_Progressbar.cs using System.C…...

OCS2 入门教程(四)- 机器人示例

系列文章目录 前言 OCS2 包含多个机器人示例。我们在此简要讨论每个示例的主要特点。 System State Dim. Input Dim. Constrained Caching Double Integrator 2 1 No No Cartpole 4 1 Yes No Ballbot 10 3 No No Quadrotor 12 4 No No Mobile Manipul…...

FreeRTOS学习第6篇–任务状态挂起恢复删除等操作

目录 FreeRTOS学习第6篇--任务状态挂起恢复删除等操作任务的状态设计实验IRReceiver_Task任务相关代码片段实验现象本文中使用的测试工程 FreeRTOS学习第6篇–任务状态挂起恢复删除等操作 本文目标&#xff1a;学习与使用FreeRTOS中的几项操作&#xff0c;有挂起恢复删除等操作…...

BLE Mesh蓝牙组网技术详细解析之Access Layer访问层(六)

目录 一、什么是BLE Mesh Access Layer访问层&#xff1f; 二、Access payload 2.1 Opcode 三、Access layer behavior 3.1 Access layer发送消息的流程 3.2 Access layer接收消息的流程 3.3 Unacknowledged and acknowledged messages 3.3.1 Unacknowledged message …...

Netlink 通信机制

文章目录 前言一、Netlink 介绍二、示例代码参考资料 前言 一、Netlink 介绍 Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。 在Linux 内核中&#xff0c;使用netlink 进行应用与内核通信的应用有…...

2024.1.8每日一题

LeetCode 回旋镖的数量 447. 回旋镖的数量 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给定平面上 n 对 互不相同 的点 points &#xff0c;其中 points[i] [xi, yi] 。回旋镖 是由点 (i, j, k) 表示的元组 &#xff0c;其中 i 和 j 之间的距离和 i 和 k 之间的欧式…...

看了致远OA的表单设计后的思考

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; https://gitee.com/nbacheng/n…...

mmdetection训练自己的数据集

mmdetection训练自己的数据集 这里写目录标题 mmdetection训练自己的数据集一&#xff1a; 环境搭建二&#xff1a;数据集格式转换(yolo转coco格式)yolo数据集格式coco数据集格式yolo转coco数据集格式yolo转coco数据集格式的代码 三&#xff1a; 训练dataset数据文件配置config…...

MySQL取出N列里最大or最小的一个数据

如题&#xff0c;现在有3列&#xff0c;都是数字类型&#xff0c;要取出这3列里最大或最小的的一个数字 -- N列取最小 SELECT LEAST(temperature_a,temperature_b,temperature_c) min FROM infrared_heat-- N列取最大 SELECT GREATEST(temperature_a,temperature_b,temperat…...

编写.NET的Dockerfile文件构建镜像

创建一个WebApi项目&#xff0c;并且创建一个Dockerfile空文件&#xff0c;添加以下代码&#xff0c;7.0代表的你项目使用的SDK的版本&#xff0c;构建的时候也需要选择好指定的镜像tag FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443F…...

【C语言】浙大版C语言程序设计(第三版) 练习7-4 找出不是两个数组共有的元素

前言 最近在学习浙大版的《C语言程序设计》&#xff08;第三版&#xff09;教材&#xff0c;同步在PTA平台上做对应的练习题。这道练习题花了比较长的时间&#xff0c;于是就写篇博文记录一下我的算法和代码。 2024.01.03 题目 练习7-4 找出不是两个数组共有的元素 作者 张彤…...

7.27 SpringBoot项目实战 之 整合Swagger

文章目录 前言一、Maven依赖二、编写Swagger配置类三、编写接口配置3.1 控制器Controller 配置描述3.2 接口API 配置描述3.3 参数配置描述3.4 忽略API四、全局参数配置五、启用增强功能六、调试前言 在我们实现了那么多API以后,进入前后端联调阶段,需要给前端同学提供接口文…...

创建第一个SpringMVC项目,入手必看!

文章目录 创建第一个SpringMVC项目&#xff0c;入手必看&#xff01;1、新建一个maven空项目&#xff0c;在pom.xml中设置打包为war之前&#xff0c;右击项目添加web框架2、如果点击右键没有添加框架或者右击进去后没有web框架&#xff0c;点击左上角file然后进入项目结构在模块…...

go 切片长度与容量的区别

切片的声明 切片可以看成是数组的引用&#xff08;实际上切片的底层数据结构确实是数组&#xff09;。在 Go 中&#xff0c;每个数组的大小是固定的&#xff0c;不能随意改变大小&#xff0c;切片可以为数组提供动态增长和缩小的需求&#xff0c;但其本身并不存储任何数据。 …...

回归和分类区别

回归任务&#xff08;Regression&#xff09;&#xff1a; 特点&#xff1a; 输出是连续值&#xff0c;通常是实数。任务目标是预测或估计一个数值。典型应用包括房价预测、销售额预测、温度预测等。 目标&#xff1a; 最小化预测值与真实值之间的差异&#xff0c;通常使用…...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...