关于c#:asyncawait-在-C-语言中是如何工作的中

接《async/await 在 C# 语言中是如何工作的?(上)》,明天咱们持续介绍 C# 迭代器和 async/await under the covers。 C# 迭代器这个解决方案的伏笔实际上是在 Task 呈现的几年前,即 C# 2.0,过后它减少了对迭代器的反对。 迭代器容许你编写一个办法,而后由编译器用来实现 IEnumerable<T> 和/或 IEnumerator<T>。例如,如果我想创立一个产生斐波那契数列的枚举数,我能够这样写: public static IEnumerable<int> Fib(){ int prev = 0, next = 1; yield return prev; yield return next; while (true) { int sum = prev + next; yield return sum; prev = next; next = sum; }}而后我能够用 foreach 枚举它: foreach (int i in Fib()){ if (i > 100) break; Console.Write($"{i} ");}我能够通过像 System.Linq.Enumerable 上的组合器将它与其余 IEnumerable<T> 进行组合: ...

April 11, 2023 · 9 min · jiezi

关于c++:ACM博弈论SG函数入门2博弈树SG函数的转移与子游戏的合并

上一篇文章咱们讲了两种经典的博弈模型:《【ACM博弈论】SG函数入门(1):从巴什博奕到尼姆游戏》,这一节咱们开始解说SG函数。 作者:Eriktse 简介:19岁,211计算机在读,现役ACM银牌选手力争以通俗易懂的形式解说算法!❤️欢送关注我,一起交换C++/Python算法。(优质好文继续更新中……) 浏览原文取得更好浏览体验:https://www.eriktse.com/algorithm/1111.html在理解SG函数之前,咱们须要晓得博弈图。 博弈图就比方Bash博弈,当n=7,m=3时,咱们能够画出如下的博弈图。 咱们能够发现,每一个点都有至少2个后继状态(即出点),这个是能够通过Bash推出来的。 其余博弈题大多也能够相似的推出一个这样的图。 SG函数SG函数能够了解为一个用于示意博弈图中节点状态的一个函数。同时sg(x) = n还示意节点x的出点形成一个汇合{y | 0 <= sg(y) <= n - 1},也就是说x能够达到所有sg小于它本人的sg的点。 就比方上图,咱们规定必败态的sg = 0,必胜态的sg != 0。于是咱们能够晓得sg(0) = 0,而后往回推。 sg函数转移方程 $$sg(x) = mex({y | y \in out[x]})$$ 说人话就是x的sg是其所有出点的sg形成的汇合做mex运算,mex示意汇合中最小的没呈现过的自然数。 代码个别为: int mex(set<int>& st){ for(int i = 0;; ++ i) if(st.find(i) == st.end())//如果找不到i return i;}于是咱们能够推出下面这个博弈图的所有点的sg函数。 留神是依据所有出点推出以后点,只有所有出点都确定了,以后点的sg能力确定,有点像建反图而后topo,然而个别咱们会间接写一个记忆化搜寻而后打表找法则。在解决带环的图时须要具体情况具体分析。 下面这张图咱们很容易找出法则,就是0 1 2 0 1 2....子游戏的合并Nim定理:全局后果等于子游戏SG的异或和。 咱们昨天学过Nim博弈,他是有n堆石子,每次能够选一堆拿走若干个。那么咱们能够将子游戏看做是一堆石子,每堆石子的个数是 (sg) 个,而后取走若干个石子类比为将sg转移到更小的sg。 当初咱们就能够解决一些形象的博弈问题了。 做题个别思路个别是三步:找出SG转移方程,打表找法则,子游戏合并。 为什么须要打表找法则呢,因为个别题目给的数据会很大,且个别会有较强的规律性,打表找到法则就行无需证实,证实对于比赛来说太侈靡了,而且没太大意义。 例题:AtCoder Beginner Contest 297 - Constrained Nim 2 ...

April 11, 2023 · 2 min · jiezi

关于c++:学懂现代CEffective-Modern-C之类型推导和auto

前言之前分享过Scott Meyers的两本书《Effective C++》和《More Effective C++》。这两本书对我深刻学习C++有着很大的帮忙,倡议所有想进阶C++技术的同学都能够看看。然而,这两本书是大神Scott在C++11之前出的,而C++11对于C++社区来说是一次重大的改革,被称为古代C++,用以辨别C++11之前的传统C++。 好在Scott在之后也带来了全新的《Effective Modern C++》,其中也是连续了作者一贯的格调和品质。带来了42个独家技巧,助你改善C++11和C++14的高效用法(封面语)。 本文首先就带同学们一起看看这本书的前两章——类型推导和auto。 首先申明本文只是做知识点的总结,书中有更具体的推导和解说过程,感兴趣的同学还是强烈建议大家去读原书。 类型推导条款1:了解模板类型推导模板类型推导是C++11利用最宽泛的个性之一——auto的根底。所以,了解auto的推导规定和正确应用形式的根底就是了解模板类型推导的规定。 先来看看模板和其调用的个别模式。 template<typename T>void f(ParamType param);f(expr);这里须要T的类型推导后果,依赖于expr的类型和ParamType的模式。其中,ParamType的模式须要分三种状况探讨。 状况1:ParamType是个指针或者援用,但不是万能援用(模式如T&&)。在这种状况下,模板类型推导具备以下规定: expr的援用属性会被疏忽。疏忽expr的援用性后,expr的类型和ParamType的类型进行模式匹配,由此决定T的类型。举个例子: // 申明模板template<typename T>void f(T& param);// 申明变量int a = 1;const int ca = a;const int& cra = a;//调用模板f(a); //a的类型是int,T的类型是int,param的类型是int&。f(ca); //ca的类型是const int,T的类型是const int,param的类型是const int&。f(cra); //cra的类型是const int&,T的类型是const int,param的类型是const int&。要点1:在模板类型推导过程中,具备援用类型的实参会被当成非援用类型来解决。状况2:ParamType是个万能援用。在这种状况下,模板类型推导规定如下: 如果expr是个左值,则T和ParamType都会被推到为左值援用。如果expr是个右值,则和状况1中的推导规定雷同。举个同状况1相似的例子: // 申明模板template<typename T>void f(T&& param);// 申明变量int a = 1;const int ca = a;const int& cra = a;//调用模板f(a); //a是左值,类型是int,T的类型是int&,param的类型是int&。f(ca); //ca是左值,类型是const int,T的类型是const int&,param的类型是const int&。f(cra); //cra是左值,类型是const int&,T的类型是const int&,param的类型是const int&。f(1); //1是右值,类型是int,T的类型是int,param的类型是int&&。要点2:对万能援用形参进行推导时,左值实参会进行非凡解决。状况3:ParamType即不是指针也不是援用。这种状况就是按值传递,其指标推导规定如下: ...

April 10, 2023 · 2 min · jiezi

关于c#:使用nssm将net-core的woker-service-注册为windows服务

应用 NSSM (Non-Sucking Service Manager) 能够将 .NET Core Worker Service 注册成 Windows 服务。请依照以下步骤操作: 下载 NSSM:首先,从官方网站下载 NSSM: https://nssm.cc/download。依据您的零碎是 32 位还是 64 位,抉择适合的版本。解压 NSSM:解压下载的 NSSM 压缩包,并将解压后的 nssm.exe 文件挪动到一个适当的文件夹,例如 C:\NSSM\。增加 NSSM 的文件夹门路到环境变量:将 NSSM 的门路增加到零碎环境变量的 Path 中。这能够让您在命令提示符中间接调用 nssm 命令。构建 .NET Core Worker Service:确保您的 .NET Core Worker Service 利用曾经构建并公布,例如,将其公布到 C:\MyWorkerService\ 文件夹下。应用 NSSM 注册 .NET Core Worker Service:要应用 NSSM 注册 Worker Service,请关上一个管理员权限的命令提示符,而后执行以下命令(这只是一个示例,请依据理论状况批改门路): nssm install MyWorkerService "C:\Program Files\dotnet\dotnet.exe" "C:\MyWorkerService\MyWorkerService.dll"在此命令中,MyWorkerService 是您为 Windows 服务设置的名称。请确保应用正确的 .NET Core 运行时门路和 Worker Service 应用程序 DLL 的门路。 ...

April 10, 2023 · 1 min · jiezi

关于c++:ACM博弈论SG函数入门1从巴什博奕到尼姆游戏

在我小时候以前做题的时候,遇到博弈题往往都是漫无目的地打表找法则,或者找一些非凡状况然而没有很好的分析方法。 其实博弈题是有比拟套路的解题办法的,那就是利用SG函数,第一节不会讲到SG函数的具体用法,咱们先来博弈入个门,学习一下最根本的博弈类型:Nim游戏。 作者:Eriktse 简介:19岁,211计算机在读,现役ACM银牌选手力争以通俗易懂的形式解说算法!❤️欢送关注我,一起交换C++/Python算法。(优质好文继续更新中……) 浏览原文取得更好浏览体验:https://www.eriktse.com/algorithm/1110.html巴什博奕在进入Nim游戏之前,咱们先看一个简略的博弈:巴什博奕。 看这道例题:http://acm.hdu.edu.cn/showproblem.php?pid=1846 因为HDUOJ常常打不开,我这里复制一下题意: 1、 本游戏是一个二人游戏;2、 有一堆石子一共有n个;3、 两人轮流进行;4、 每走一步能够取走1…m个石子;5、 最先取光石子的一方为胜;如果游戏的单方应用的都是最优策略,请输入哪个人能赢。假如此时的n = 10, m = 3,咱们来剖析一下这个场面。 咱们设一个布尔函数f(x),示意当n = x时的输赢。 f(0) = 0,因为无奈持续操作了,显然是0,也就是说这是一个必败态。f(1) = 1,能够取走一个石子,使对手陷入必败态,所以这是一个必胜态。f(2) = 1,能够取走两个。f(3) = 1,能够取走三个。f(4) = 0,无论取走一个、两个或三个都必然使得对手进入必胜态,所以这是一个必败态。f(5) = 1f(6) = 1f(7) = 1f(8) = 0,无论取走一个、两个或三个都必然使得对手进入必胜态,所以这是一个必败态。f(9) = 1f(10) = 1至此,咱们失去了答案,f(n) = f(10) = 1所以先手必胜,不难发现下面这个函数f(x)的法则,仅当x % 4 == 0时为0,其余状况都为1。 于是咱们只须要判断n % (m + 1)是否为0就能判断先手是否获胜。 这就是巴什博奕模型,是不是很简略,咱们简略打个表就找到了法则。 Nim游戏仍然看这道例题:https://www.luogu.com.cn/problem/P2197 咱们这么剖析: 败局肯定是全为0的状况,此时所有数字的异或和为0(不晓得怎么写异或和的latex,大家凑合着看): $$\oplus_{i=1}^{n}a_i = 0$$ 那么想一下那些状态能够到这个败局呢?咱们反向的思考,往任意一个地位加上一个数字,就能够作为前一个状态,也就是一个必胜态(因为那个状态必然能够达到败局的状态)。 往任意一个地位加上一个数字之后就必然有: $$\oplus_{i=1}^{n}a_i \ne 0$$ ...

April 10, 2023 · 1 min · jiezi

关于c#:盘点-C-80-中好用的特性

减少援用类型可为null://如果参数t是null,则会收回正告public static void Test<T?>(T t){ }模式匹配1、swith 多条件匹配 ## 1 元组模式 int a = 1;int b = 10; string c = (a, b) switch { (1, 10) => "123", _ => "default",};### 应用弃元模式_ = (a, b) switch { (1, 10) => "123", _ => "default",};## 2 地位模式 public class PointA { public int X { get; } public int Y { get; } public int Z { get; } public PointA(int x, int y) => (X, Y) = (x, y); public PointA(int x, int y,int z) => (X, Y,Z) = (x, y,z); public void Deconstruct(out int x, out int y) => (x, y) = (X, Y); public void Deconstruct(out int x, out int y,out int z) => (x, y,z) = (X, Y,Z); }static string GetLocation(PointA point) => point switch { (0, 0) => "1", (0, 0, 0) => "1", var (x, y) when x > 0 && y > 0 =>"2", var (_, _) => "3",// 当 x 或 y 不为null时 _ => "4" };Using 申明## 先相熟下应用 此处 C#7就能用了using static System.Math;using Tasks = System.Threading.Tasks;using (FileStream fileStream = new FileStream("", FileMode.Open)) { }## C#8using FileStream fileStream2 = new FileStream("", FileMode.Open);接口个性减少--- 能够申明一个属性 --- 办法能够实现(不再是只定义) ...

April 9, 2023 · 5 min · jiezi

关于c:MIT-6181068286S081-操作系统工程-Labs

久闻MIT 6.828操作系统工程试验的盛名,今日终于可能亲手实际。 每个试验我都将要求、提醒、实现思路和步骤、问题和后果记录下来,一方面为了将试验要求和提醒翻译后记录下来以便后续研读,另一方面也是为了加深试验中学习到的知识点: MIT 6.1810(6.828/6.S081) 操作系统工程 Lab1 UtilitiesMIT 6.1810(6.828/6.S081) 操作系统工程 Lab2 System callsMIT 6.1810(6.828/6.S081) 操作系统工程 Lab3 Page tablesMIT 6.1810(6.828/6.S081) 操作系统工程 Lab4 TrapsMIT 6.1810(6.828/6.S081) 操作系统工程 Lab5 Copy-on-Write ForkMIT 6.1810(6.828/6.S081) 操作系统工程 Lab6 MultithreadingMIT 6.1810(6.828/6.S081) 操作系统工程 Lab7 NetworkingMIT 6.1810(6.828/6.S081) 操作系统工程 Lab8 LocksMIT 6.1810(6.828/6.S081) 操作系统工程 Lab9 file systemMIT 6.1810(6.828/6.S081) 操作系统工程 Lab10 mmap

April 9, 2023 · 1 min · jiezi

关于c++:牛客小白月赛70AF题解小d和超级泡泡堂小d和孤独的区间小d的博弈小d和送外卖

较量传送门:https://ac.nowcoder.com/acm/contest/53366 难度适中。 作者:Eriktse 简介:19岁,211计算机在读,现役ACM银牌选手力争以通俗易懂的形式解说算法!❤️欢送关注我,一起交换C++/Python算法。(优质好文继续更新中……) 浏览原文取得更好浏览体验:https://www.eriktse.com/algorithm/1109.htmlA - 小d和答案批改Tag:签到 略。 Code: #include <bits/stdc++.h>#define int long longusing namespace std;const int N = 1e5 + 9;char s[N];signed main(){ cin >> s + 1; for(int i = 1; s[i]; ++ i) { if('a' <= s[i] && s[i] <= 'z')printf("%c", s[i] - 'a' + 'A'); else printf("%c", s[i] - 'A' + 'a'); } return 0;}B - 小d和图片压缩Tag:签到 略。 Code: #include <bits/stdc++.h>#define int long longusing namespace std;const int N = 1e3 + 9;int a[N][N];signed main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n, m;cin >> n >> m; for(int i = 1;i <= n; ++ i) for(int j = 1;j <= m; ++ j) cin >> a[i][j]; for(int i = 1;i <= n; i += 2) { for(int j = 1;j <= m;j += 2) { int sum = a[i][j] + a[i + 1][j] + a[i][j + 1] + a[i + 1][j + 1]; cout << sum / 4 << ' '; } cout << '\n'; } return 0;}C - 小d和超级泡泡堂Tag:dfs,联通块 ...

April 8, 2023 · 4 min · jiezi

关于c#:浅谈-Spring-如何解决-Bean-的循环依赖问题

什么是循环依赖?艰深来讲,循环依赖指的是一个实例或多个实例存在相互依赖的关系(类之间循环嵌套援用)。举个例子public class AService { private BService bService;}public class BService { private AService aService;}复制代码上述例子中 AService 依赖了 BService,BService 也依赖了 AService,这就是两个对象之间的相互依赖。当然循环依赖还包含 本身依赖、多个实例之间相互依赖。 失常运行下面的代码调用 AService 对象并不会呈现问题,也就是说一般对象就算呈现循环依赖也不会存在问题,因为对象之间存在依赖关系是很常见的,那么为什么被 Spring 容器治理后的对象会呈现循环依赖问题呢? Spring Bean 的循环依赖问题被 Spring 容器治理的对象叫做 Bean,为什么 Bean 会存在循环依赖问题呢?想要理解 Bean 的循环依赖问题,首先须要理解 Bean 是如何创立的。2.1 Bean 的创立步骤为了能更好的展现呈现循环依赖问题的环节,所以这里的 Bean 创立步骤做了简化:在创立 Bean 之前,Spring 会通过扫描获取 BeanDefinition。BeanDefinition就绪后会读取 BeanDefinition 中所对应的 class 来加载类。实例化阶段:依据构造函数来实现实例化 (未属性注入以及初始化的对象 这里简称为 原始对象)属性注入阶段:对 Bean 的属性进行依赖注入 (这里就是产生循环依赖问题的环节)如果 Bean 的某个办法有AOP操作,则须要依据原始对象生成代理对象。最初把代理对象放入单例池(一级缓存singletonObjects)中。 下面的步骤次要是为了突出循环依赖问题,如果想理解 Bean 的残缺生命周期能够看这一篇文章:浅谈 Spring Bean 的生命周期 - 掘金 (juejin.cn)两点阐明: 下面的 Bean 创立步骤是对于 单例(singleton) 作用域的 Bean。Spring 的 AOP 代理就是作为 BeanPostProcessor 实现的,而 BeanPostProcessor 是产生在属性注入阶段后的,所以 AOP 是在 属性注入 后执行的。 ...

April 7, 2023 · 4 min · jiezi

关于c:MIT-6181068286S081-操作系统工程-Lab10-mmap

Lab: mmap (hard)mmap 和 munmap 零碎调用容许 UNIX 程序对其地址空间进行具体管制。它们可用于在过程之间共享内存,将文件映射到过程地址空间,以及作为用户级页面谬误计划(如课程中探讨的垃圾回收算法)的一部分。在本试验中,你将向 xv6 增加 mmap 和 munmap,重点关注内存映射文件。 运行man 2 mmap可失去手册中mmap的申明: void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);要求能够通过多种形式调用 mmap,但此练习只须要与文件内存映射相干的性能子集。 您能够假如 addr 将始终为零,这意味着内核应决定映射文件的虚拟地址。mmap 返回该地址,如果失败,则返回0xffffffffffffffff。length是要映射的字节数; 它可能与文件的长度不同。prot 批示内存是否应映射为可读、可写和/或可执行; 您能够假如 prot 是 PROT_READ 或 PROT_WRITE 或两者兼而有之。flags要么是MAP_SHARED,这意味着对映射内存的批改应该写回文件,要么是MAP_PRIVATE,这意味着它们不应该写回文件。你不用在flags中实现任何其余位。fd 是要映射的文件的关上文件描述符。您能够假如偏移量为零(文件中要映射的终点)。对于映射同一MAP_SHARED文件的不同过程,禁止共享物理页。 munmap(addr,length)应该删除指定地址范畴内的mmap映射。如果过程批改了内存且为MAP_SHARED映射,则应首先将批改写入文件。munmap 调用可能只笼罩已被映射的区域的一部分,但您能够假如它会在开始时、结尾或整个区域勾销映射(但不会在区域两头打一个洞)。 您应该实现足够的 mmap 和 munmap 性能,以使 mmaptest 测试程序失常工作。无需实现mmaptest 不应用 mmap 的性能。实现后,应会看到以下输入: $ mmaptestmmap_test startingtest mmap ftest mmap f: OKtest mmap privatetest mmap private: OKtest mmap read-onlytest mmap read-only: OKtest mmap read/writetest mmap read/write: OKtest mmap dirtytest mmap dirty: OKtest not-mapped unmaptest not-mapped unmap: OKtest mmap two filestest mmap two files: OKmmap_test: ALL OKfork_test startingfork_test OKmmaptest: all tests succeeded$ usertests -qusertests starting...ALL TESTS PASSED$ 提醒首先向UPROGS增加_mmaptest,以及mmap和munmap零碎调用,以便使user/mmaptest.c可能编译。当初,只需从 mmap 和 munmap 返回谬误。咱们在kernel/fcntl.h中为您定义了PROT_READ等。运行 mmaptest,这将在第一次 mmap 调用时失败。惰性地填写页表,以响应页面谬误。也就是说,mmap 不应调配物理内存或读取文件。相同,请在 usertrap 中(或由 usertrap 调用)的页面错误处理代码中执行此操作,就像在惰性页面调配试验中一样。惰性的起因是确保大文件的 mmap 是疾速的,并且大于物理内存的文件的 mmap 是可能的。跟踪 mmap 为每个过程映射的内容。定义与第 15 讲中形容的 VMA(虚拟内存区域)对应的构造,记录 mmap 创立的虚拟内存范畴的地址、长度、权限、文件等。因为 xv6 内核中没有内存分配器,因而能够申明一个固定大小的 VMA 数组,并依据须要从该数组进行调配。大小为 16 就足够了。实现mmap:在过程的地址空间中查找要在其中映射文件的未应用区域,并将 VMA 增加到过程的映射区域表中。VMA 应蕴含指向要映射的文件的struct file的指针; mmap 应减少文件的援用计数,以便在敞开文件时构造不会隐没(提醒:请参阅 filedup)。运行 mmaptest:第一个 mmap 应该胜利,但第一次拜访已映射的内存会导致页面谬误并 kill mmaptest。增加代码,以在拜访已映射区域中导致的页面谬误时,调配一页物理内存,将相干文件的 4096 字节读取到该页面中,并将其映射到用户地址空间。应用 readi 读取文件,它须要一个偏移参数来读取文件(但您必须锁定/解锁传递给 readi 的 inode)。不要遗记在页面上正确设置权限。运行 mmaptest;它应该达到第一个munmap。实现munmap:找到地址范畴的 VMA 并勾销映射指定的页面(提醒:应用 uvmunmap)。如果 munmap 删除了前一个 mmap 的所有页面,它应该缩小相应struct file的援用计数。如果已批改未映射的页面并且文件已映射MAP_SHARED,请将该页面写回该文件。查看filewrite以取得灵感。现实状况下,您的实现只会写回真正被程序修改的MAP_SHARED页面。RISC-V PTE 中的脏位 (D) 批示是否已写入页面。然而,mmaptest 不会查看非脏页面是否没有写回; 因而,您能够在不查看 D 位的状况下从新编写页面。批改 exit 以勾销映射过程的映射区域,就像调用 munmap 一样。运行 mmaptest; mmap_test应该通过,但可能不会通过fork_test。批改fork以确保子级与父级具备雷同的映射区域。不要遗记递增 VMA struct file的援用计数。在子级的页面谬误处理程序中,能够调配新的物理页面,而不是与父级共享页面。后者会更酷,但须要更多的工作。运行 mmaptest; 它应该通过mmap_test和fork_test。实现增加mmap和munmap零碎调用:增加零碎调用通用步骤(在用户空间申明零碎调用、增加条目,在内核空间减少零碎调用命令序号以及对应的sys_mmap()和sys_munmap()函数)在proc.h中struct proc中增加映射区域:首先定义映射区域构造体struct MapArea及一个过程映射区域的数量: ...

April 5, 2023 · 4 min · jiezi

关于c++:C统计文件内给定关键词

博主刚开始学习c++,前段时间老师安排了c++的一个作业: 给定两个文件(一个源文件text4search.txt,一个文件keywords.txt蕴含须要在源文件中搜寻的关键词),要求输入keywords.txt中每个关键词在源文件中呈现的行号。举个例子,如果keywords.txt中有一个关键词是c++,在text4search.txt中第1,7,9,43,543,586,2445行都呈现了c++,那么应该输入c++:{1 7 9 43 543 586 2445 }。 先上完整版代码(代码已在vscode c++11规范运行胜利) #include <algorithm>#include <fstream>#include <iostream>#include <map>#include <sstream>#include <vector>using namespace std;// 报错函数void error(const char *p, const char *p2 = "") { std::cerr << p << ' ' << p2 << std::endl; std::exit(1);}map<string, vector<int>> keyCount(ifstream &inputFile, vector<string> &keywords) { map<string, vector<int>> resultMap; string line; int lineNum = 0; while (getline(inputFile, line)) { ++lineNum; // 读入文件的每一行 std::istringstream sin(line); string word; //每行单词一一查看是不是在keywords外面呈现,而不是统计keyword在一行中呈现的次数 //这样不必独自写一个函数统计一行中某个关键词呈现次数,也可能呈现一次关键词就压入行号一次 while (sin >> word) { auto it = find(keywords.begin(), keywords.end(), word); if (it != keywords.end()) { resultMap[word].push_back(lineNum); } } } return resultMap;}int main() { // 建设三个文件流对象并关联相应文件,留神这里大家要批改为本人的文件门路(或者应用命令行输出) std::ifstream fin1; fin1.open("D:/University/Code/cpp_vscode/lab4/keywords.txt"); // 和keywords.txt建设关联 if (!fin1) { error("cannot open input file", "D:/University/Code/cpp_vscode/lab4/keywords.txt"); } std::ifstream fin2; fin2.open("D:/University/Code/cpp_vscode/lab4/text2search.txt"); // 和text2search.txt建设关联 if (!fin2) { error("cannot open input file", "D:/University/Code/cpp_vscode/lab4/text2search.txt"); } std::ofstream fout; fout.open("D:/University/Code/cpp_vscode/lab4/result.txt"); // 和result.txt建设关联 if (!fout) { error("cannot open output file", "D:/University/Code/cpp_vscode/lab4/result.txt"); } // 将keywords存入keys这个字符串向量中(用fin1) vector<string> keys; string s; while (fin1 >> s) { keys.push_back(s); } fin1.close(); // 利用fin2和keys产生后果map map<string, vector<int>> result = keyCount(fin2, keys); fin2.close(); //输入,因为map会主动依照字典序排序,而行号也是依照升序逐行检测,因而输入不必再排序 map<string, vector<int>>::iterator it1 = result.begin(); while (it1 != result.end()) { fout << it1->first << " : " << "{"; //it1->second是vector<int>类型,不能间接用fout输入 for (const auto &it2 : it1->second) { fout << it2 << ","; } fout << "}" << endl; it1++; } fout.close(); system("pause"); return 0;}上面联合代码讲一讲我的思路。这道题的难点在于如何选取正确、高效的存储办法和搜寻办法:显然咱们不能简略的挨个读取text中的单词,找到一个关键词就输入一个行号——这样输入的行号是凌乱的。 ...

April 5, 2023 · 2 min · jiezi

关于c#:asyncawait-在-C-语言中是如何工作的上

前不久,咱们公布了《抉择 .NET 的 n 个理由》。它提供了对平台的高层次概述,总结了各种组件和设计决策,并承诺对所波及的畛域发表更深刻的文章。这是第一篇这样深入探讨 C# 和 .NET 中 async/await 的历史、背地的设计决策和实现细节的文章。 对 async/await 的反对曾经存在了十年之久。在这段时间里,它扭转了为 .NET 编写可扩大代码的形式,而在不理解其底层逻辑的状况下应用该性能是可行的,也是十分常见的。在这篇文章中,咱们将深入探讨 await 在语言、编译器和库级别的工作原理,以便你能够充分利用这些有价值的性能。 不过,要做到这一点,咱们须要追溯到 async/await 之前,以理解在没有它的状况下最先进的异步代码是什么样子的。 最后的样子早在 .NET Framework 1.0中,就有异步编程模型模式,又称 APM 模式、Begin/End 模式、IAsyncResult 模式。在高层次上,该模式很简略。对于同步操作 DoStuff: class Handler{ public int DoStuff(string arg);}作为模式的一部分,将有两个相应的办法:BeginDoStuff 办法和 EndDoStuff 办法: class Handler{ public int DoStuff(string arg); public IAsyncResult BeginDoStuff(string arg, AsyncCallback? callback, object? state); public int EndDoStuff(IAsyncResult asyncResult);}BeginDoStuff 会像 DoStuff 一样承受所有雷同的参数,但除此之外,它还会承受 AsyncCallback 委托和一个不通明的状态对象,其中一个或两个都能够为 null。Begin 办法负责初始化异步操作,如果提供了回调(通常称为初始操作的“连续”),它还负责确保在异步操作实现时调用回调。Begin 办法还将结构一个实现了 IAsyncResult 的类型实例,应用可选状态填充 IAsyncResult 的 AsyncState 属性: namespace System{ public interface IAsyncResult { object? AsyncState { get; } WaitHandle AsyncWaitHandle { get; } bool IsCompleted { get; } bool CompletedSynchronously { get; } } public delegate void AsyncCallback(IAsyncResult ar);}而后,这个 IAsyncResult 实例将从 Begin 办法返回,并在最终调用 AsyncCallback 时传递给它。当筹备应用操作的后果时,调用者将把 IAsyncResult 实例传递给 End 办法,该办法负责确保操作已实现(如果没有实现,则通过阻塞同步期待操作实现),而后返回操作的任何后果,包含流传可能产生的任何谬误和异样。因而,不必像上面这样写代码来同步执行操作: ...

April 4, 2023 · 6 min · jiezi

关于c#:我没能实现始终在一个线程上运行-task

前文咱们总结了在应用常驻工作实现常驻线程时,应该留神的事项。然而咱们最终没有提到如何在解决对于带有异步代码的方法。本篇将承受笔者对于该内容的总结。 如何辨认以后代码跑在什么线程上所有开始之前,咱们先来应用一种简略的形式来辨认以后代码运行在哪种线程上。 最简略的形式就是打印以后线程名称和线程ID来辨认。 private static void ShowCurrentThread(string work){ Console.WriteLine($"{work} - {Thread.CurrentThread.Name} - {Thread.CurrentThread.ManagedThreadId}");}通过这段代码,咱们能够非常容易的辨认三种不同状况下的线程信息。 [Test]public void ShowThreadMessage(){ new Thread(() => { ShowCurrentThread("Custom thread work"); }) { IsBackground = true, Name = "Custom thread" }.Start(); Task.Run(() => { ShowCurrentThread("Task.Run work"); }); Task.Factory.StartNew(() => { ShowCurrentThread("Task.Factory.StartNew work"); }, TaskCreationOptions.LongRunning); Thread.Sleep(TimeSpan.FromSeconds(1));}// output// Task.Factory.StartNew work - .NET Long Running Task - 17// Custom thread work - Custom thread - 16// Task.Run work - .NET ThreadPool Worker - 12别离为: ...

April 3, 2023 · 3 min · jiezi

关于c++:ACM数论和式变换技术也许是最好的讲解之一

在做数论题时,往往须要进行和式变换,而后变换成咱们能够解决的和式,再针对和式做筛法、整除分块等操作。 本文将介绍一些常见的和式变换技术。 以下呈现的概念大部分为集体总结,未必是学术界/比赛界的对立说法,有不谨严的中央请谅解。 作者:Eriktse 简介:19岁,211计算机在读,现役ACM银牌选手力争以通俗易懂的形式解说算法!❤️欢送关注我,一起交换C++/Python算法。(优质好文继续更新中……) 原文链接(浏览原文取得更好浏览体验):https://www.eriktse.com/algorithm/1101.html 和式的根本模式和式个别有两种:区间枚举型和整除枚举型。 区间枚举型咱们的前缀和就是一个典型的区间枚举型和式。 假如咱们有一个定义域为$x\in[1, n],x\in Z^+$的函数$f(x)$,那么咱们能够设一个前缀和函数$F(x)$,定义为: $$F(x) = \sum_{i=1}^{x}f(i) = f(1) + f(2) + ... + fx()$$ 求和符号中,如果没有非凡阐明,个别枚举的都是整数,且步长为1。整除枚举型约数个数是一个典型的整除枚举型和式,咱们能够容易的写出它的表达式: $$f(n) = \sum_{d|n}1$$ 其中 $d|n$ 示意 $i$ 能够整除 $n$ ,即 $i$ 是 $n$ 的因子。约数之和也是一个整除枚举型和式,表达式如下: $$g(n) = \sum_{d|n}d$$ 和式的根本性质可拆分性质第一种拆分如下: $$\sum_{i=1}^{n}a_i = \sum_{i=1}^{m}a_i + \sum_{i=m+1}^{n}a_i$$ 这是显然的,然而基本上用不着。 第二种拆分如下: $$\sum_{i=1}^{n}(a_i + b_i) = \sum_{i=1}^{n}a_i + \sum_{i=1}^{n}b_i$$ 这也是显然的。 常数可提取当咱们的和式外面乘上了一个常数$k$,那么这个常数是能够提出来的,因为咱们探讨的数域是整数域,这个$k$个别为整数。(其实对于实数也是满足条件的)。 $$\sum_{i=1}^{n}ka_i = k\sum_{i=1}^{n}a_i$$ 整除枚举型变换为区间枚举型(重要)就比方下面那个约数之和的函数: $$g(i) = \sum_{d|n}d = \sum_{i=1}^{n}[d|n]$$ 咱们晓得$d$的取值肯定在$[1, n]$,所以咱们能够转换枚举类型,此时枚举指标的范畴就要扭转,同时加上一个布尔函数来限定。 ...

April 2, 2023 · 2 min · jiezi

关于c++:C面试必备常见C面试题汇总及详细解析

C++作为一门重要的编程语言,其在面试中经常是热门的考查对象。本文将会介绍一些常见的C++面试题,帮忙C++面试者防止很多不必要的困惑和蛊惑。每个问题都有绝对应的答案,以便各位同学疾速查阅。 C++和C的区别是什么?C++是C的超集,也就是说,C++包含了C的所有根底个性,并且还减少了一些新的个性。上面列举一些C和C++之间的次要区别: 面向对象编程C++ 是一种面向对象的编程语言,而 C 不是。因而,C++ 反对类、继承、封装、多态等一系列面向对象的概念和个性,这些能力使 C++ 更加灵便和弱小。 规范库C++ 规范库比 C 规范库更加欠缺和弱小。C++ 规范库包含了很多容器类,如 vector、map、set 等,以及输入输出流、字符串解决等罕用性能。这些库函数能够在许多状况下进步开发效率。 命名空间C++ 引入了命名空间的概念,能够防止函数命名雷同的抵触。应用命名空间能够将代码依照逻辑分组,并更好地组织代码。 异样解决C++ 反对异样解决机制,这个机制能够加强程序的容错性和可靠性。当程序产生异样时,能够抛出异样并在可控范畴内进行解决,防止程序解体。而 C 不反对异样解决机制。 运算符重载C++ 容许对运算符进行重载,能够使得运算符在解决特定类型的数据时更具备描述性。而 C 不反对运算符重载。什么是指针? 指针是C++中的一种数据类型,指针变量存储了一个内存地址,该地址指向某个变量或者对象。指针能够用来拜访和批改内存中的数据,同时也能够通过指针来传递参数和返回值。对于C++程序员来说,精通指针的应用是十分重要的。 重写和重载的区别重写指的是在派生类中从新定义基类的虚函数的行为。当基类中的某个虚函数在派生类中被从新定义时,如果派生类对象调用该函数,则会笼罩掉基类中的实现,执行派生类中的实现代码。在进行对象的多态性转换时,重写十分重要。重载则指的是在同一个作用域内申明几个同名然而参数列表不同的函数。通过函数名雷同但参数类型、个数或程序的不同,能够让多个函数具备不同的行为。例如,C++ 中能够重载函数来解决不同类型的数据,如整数、浮点数等。在应用函数时,依据传递给函数的参数类型和个数来主动抉择对应的函数进行调用。因而,重写和重载的次要区别在于,重写是通过派生类从新定义基类虚函数的行为,以实现运行时多态性;而重载是在同一作用域内申明几个雷同名称的函数,以实现编译时多态性。 讲讲面向对象面向对象编程有3大个性: 封装:封装是指将对象的属性和办法绑定在一起,造成一个独立的、关闭的单元。内部只能通过对象提供的公共接口来拜访或操作对象的外部状态,而无奈间接拜访或批改对象的数据。这样能够保障对象的外部状态不受内部烦扰,从而进步了程序的安全性和可靠性,简化了代码的调用形式。继承:通过继承机制,一个类能够从另一个类中继承某些属性和办法,并在此基础上增加新的属性和办法,从而防止了反复编写代码的冗余,进步了代码的可重用性和可维护性。多态:多态是指同一个音讯能够被不同的对象解释执行,即不同的对象对同一音讯作出不同的响应。具体来说,多态能够通过虚函数和模板等机制实现。通过多态,能够使代码更加灵便、可扩大,同时也可能使程序更易读懂和保护。这三个个性是面向对象编程的外围,它们相互配合,独特组成了一个残缺的面向对象编程体系,可能无效地进步程序的可靠性、可重用性、可扩展性等方面。 什么是援用?援用也是C++中的一种数据类型,它提供了一种简洁而高效的形式来操作变量和对象,而不须要拷贝它们自身。援用被视为原变量的一个别名,其操作相似于指针,然而援用不能被赋值为NULL,也不能进行指针运算。 C/C++援用和指针的区别?指针是一个实体,须要分配内存空间;援用只是变量的别名,不须要分配内存空间。援用在定义的时候必须进行初始化,并且不可能扭转;指针在定义的时候不肯定要初始化,并且指向的空间可变。有多级指针,然而没有多级援用,只能有一级援用。指针和援用的自增运算后果不一样。 内联函数和一般函数有什么区别?内联函数和一般函数的区别在于是否进行了“内联优化”。内联函数是一种非凡的函数,编译器会在编译时将其整个函数体插入到调用该函数的中央,从而节俭了函数调用的开销。一般函数则须要在调用时进行函数栈的压栈和出栈操作,这样会带来额定的工夫和空间开销。因而,对于简略的函数或者频繁被调用的函数,咱们能够思考应用内联函数来进步程序的性能。 常量指针和指针常量?常量指针是指针指向的地址不能扭转,然而能够通过指针批改地址对应的值。而指针常量则是指针指向的地址能够扭转,然而不能通过指针批改地址对应的值。 在C语言中,常量指针的定义形式为const int* ptr,示意指向int类型的常量指针,指针所指向的地址不能扭转,然而能够通过指针批改地址对应的值。 而指针常量的定义形式为int* const ptr,示意指向int类型的指针常量,指针所指向的地址能够扭转,然而不能通过指针批改地址对应的值。 如果想要定义既不能批改地址,也不能批改地址对应的值的指针,能够应用const int* const ptr。 常量指针和指针常量的区别在于指针所指向的内容可不可变,须要依据具体情况而定。 P.S. 感兴趣的同学能够看我之前帖子,有具体介绍 如何防止野指针?野指针是指指向曾经被开释或者有效的内存空间的指针,这是 C++ 中常见的一个程序谬误。当咱们拜访野指针时,程序会呈现不可预期的行为,甚至解体。 为了防止野指针,咱们能够采取以下措施: 在指针应用前初始化在定义一个指针变量的时候,咱们应该立刻将其初始化为一个无效的地址。如果不能确定指针的初始值,能够将其初始化为 nullptr 或 0,防止野指针的产生。 int* p = nullptr; // 初始化为空指针在指针应用后及时置空当指针变量不再应用时,咱们应该将其置为空指针,避免误用。这样能够无效地防止产生野指针。 int* p = new int;*p = 10;delete p;p = nullptr; // 置空指针,防止野指针产生不要反复开释曾经开释的内存在开释指针所指向的内存空间之后,咱们应该将该指针赋值为 NULL 或 nullptr,以避免该指针被误用。 ...

March 31, 2023 · 1 min · jiezi

关于c#:C滑动拼图验证码实现笔记

前言C# 是一个古代的、通用的、面向对象的编程语言,它是由微软(Microsoft)开发的,由 Ecma 和 ISO 核准认可的。突发奇想,入手开发一个C#滑动拼图验证码,上面是我开发过程的记录。 筹备工作本文应用IIS搭建环境,同时确保我的项目运行失常。 目录构造 外围代码noramal.html<!doctype html><html><head><meta charset="utf-8"><title>凯格行为验证码 - Net C# demo</title><link rel="stylesheet" href="./style/demo.css" /><!--将以下域名替换成你的“应用服务器域名”将以下 appid 替换成你的 AppID服务器域名和appid在你的利用治理中获取示例:<script src="captcha.js?appid=xxx"></script>--><script src="captcha.js?appid=appId"></script><script>kg.captcha({ // 绑定显示区域 bind: "#captchaBox", // 验证胜利事务处理 success: function (e) { console.log(e); // 将验证胜利后的 token 通过暗藏域传递到后端 kg.$("#kgCaptchaToken").value = e["token"]; }, // 验证失败事务处理 failure: function (e) { console.log(e); }, // 点击刷新按钮时触发 refresh: function (e) { console.log(e); }});// 查看表单提交function check() { if (kg.$("#kgCaptchaToken").value == "") { alert("请实现图形验证后提交") return false; } else { return true; }}</script></head><body> <form action="demo.aspx?cty=1" method="post" id="form" onsubmit="return check();"> <!-- 将验证胜利后的 token 通过暗藏域传递到后端 --> <input type="hidden" name="kgCaptchaToken" id="kgCaptchaToken" value="" /> <div class="inputForm"> <input type="text" name="username" placeholder=" 例:填写登录帐号" /> <br/> <input type="password" name="password" placeholder=" 例:填写登录明码" /> </div> <!-- 绑定显示区域 --> <div id="captchaBox"></div> <input type="submit" value="提 交" class="btn" /> </form></body></html>demo.aspx.csusing System;using KgCaptchaSDK;public partial class _Default : System.Web.UI.Page{ protected void Page_Load(object sender, EventArgs e) { // 后端解决 string html, appId, appSecret, Token; if (Request.Form.ToString().Length > 0){ // 有数据处理 string cty = Request.QueryString["cty"]; // 设置 AppId 及 AppSecret,在利用治理中获取 if (cty == "1"){ appId = "appId"; appSecret = "appSecret"; } // 填写你的 AppId 和 AppSecret,在利用治理中获取 var request = new kgCaptcha(appId, appSecret); // 前端验证胜利后颁发的 token,有效期为两分钟 request.token = Request.Form["kgCaptchaToken"]; // 填写应用服务域名,在利用治理中获取 request.appCdn = "https://cdn.kgcaptcha.com"; // 当安全策略中的防控等级为3时必须填写,个别状况下能够疏忽 // 能够填写用户输出的登录帐号(如:$_POST["username"]),可拦挡同一帐号屡次尝试等行为 request.userId = "kgCaptchaDemo"; // 申请超时工夫,秒 request.connectTimeout = 5; // 发送验证申请 var requestResult = request.sendRequest(); if (requestResult.code == 0) { // 验签胜利逻辑解决 *** // 这里做验证通过后的数据处理 // 如登录/注册场景,这里通常查询数据库、校验明码、进行登录或注册等动作解决 // 如短信场景,这里能够开始向用户发送短信等动作解决 // ... html = "<script>alert('验证通过');history.back();</script>"; } else { // 验签失败逻辑解决 html = "<script>alert(\"" + requestResult.msg + " - " + requestResult.code + "\");history.back();</script>"; } // 输入后果 Response.Write(html); } else { Response.Redirect("index.html"); } }}成果展现 ...

March 31, 2023 · 2 min · jiezi

关于c++:ACM算法竞赛日常训练DAY5题解与分析储物点的距离糖糖别胡说我真的不是签到题目-前缀和-思维

DAY5共2题: 储物点的间隔(前缀和)糖糖别胡说,我真的不是签到题目(multiset,思维) 作者:Eriktse 简介:19岁,211计算机在读,现役ACM银牌选手力争以通俗易懂的形式解说算法!❤️欢送关注我,一起交换C++/Python算法。(优质好文继续更新中……) 原文链接(浏览原文取得更好浏览体验):https://www.eriktse.com/algorithm/1096.html 储物点的间隔题目链接:https://ac.nowcoder.com/acm/problem/14683 预处理出各点搬运到点1和点n的代价前缀和,以及区间分量和。 如果咱们要将区间[5, 7]的物品全副搬运到3,代价应该是区间[5, 7]的物品全副搬运到的1,而后减去多搬的代价:[5, 7]的分量和 * dist(1, 3)。 下面是x < l的状况,x > r的状况相似。 当l <= x <= r只需将区间[l, r]分为两局部求和即可。 记得取模,间隔要取模,前缀和也要取模。 #include <bits/stdc++.h>#define int long longusing namespace std;const int maxn = 2e5 + 9, p = 1e9 + 7;//sum_l[i]示意区间[1, i]的物品都运到点1的代价之和//prefix_a[i]示意区间[1, i]的物品分量之和//pos[i]是第i个点的地位,通过a[]作前缀和失去int a[maxn], pos[maxn], sum_l[maxn], sum_r[maxn], prefix_a[maxn];int n, m;//取模函数int mo(int x){return (x % p + p) % p;}int f(int x, int l, int r){ if(l > r)return 0; int res = 0; if(x <= l) { res = mo(sum_l[r] - sum_l[l - 1]); res = mo(res - mo(pos[x] - pos[1]) * mo(prefix_a[r] - prefix_a[l - 1]) % p); } else if(x >= r) { res = mo(sum_r[r] - sum_r[l - 1]); res = mo(res - mo(pos[n] - pos[x]) * mo(prefix_a[r] - prefix_a[l - 1]) % p); } return res;}signed main(){ scanf("%lld %lld",&n, &m); pos[1] = 1; for(int i = 2, d;i <= n; ++ i)scanf("%lld", &d), pos[i] = pos[i - 1] + d; for(int i = 1;i <= n; ++ i)scanf("%lld", a + i); for(int i = 1;i <= n; ++ i) { sum_l[i] = mo(sum_l[i - 1] + a[i] * mo(pos[i] - pos[1]) % p); sum_r[i] = mo(sum_r[i - 1] + a[i] * mo(pos[n] - pos[i]) % p); } for(int i = 1;i <= n; ++ i)prefix_a[i] = mo(prefix_a[i - 1] + a[i]); while(m --) { int x, l, r;scanf("%lld %lld %lld", &x, &l, &r); int ans = 0; if(l <= x and x <= r)ans = mo(f(x, l, x - 1) + f(x, x + 1, r)); else ans = f(x, l, r); printf("%lld\n", ans); } return 0;}糖糖别胡说,我真的不是签到题目题目链接:https://ac.nowcoder.com/acm/problem/14583 ...

March 28, 2023 · 3 min · jiezi

关于c++:JAVA让-ChatGPT-来浅说-AQS

前言又迎来了一年一度的金三银四,尽管说往年的大环境不好,然而招聘还是在炽热进行中。面试过 Java 工程师的小伙伴都晓得,Java 中的 AQS 是面试高频题,面试官上来就间接了当地问,AQS 晓得是什么吧,来讲讲它是怎么实现的,以及哪些地方用到了它。那么接下来,让咱们应用 ChatGPT 并联合本人的了解来讲述一下 AQS 的相干内容。什么是 AQS当博主发问 ChatGPT 什么是 AQS 时,ChatGPT 给出了如下答复: 大抵意思就是说,AQS 全称 AbstractQueuedSynchronizer,是 Java 中并发包中用于实现锁和其余同步器的根底框架。它应用了 CAS 操作和 unsafe 类来实现同步器的状态更新以及线程的挂起和唤醒等操作,提供了一种通用的、高效且可扩大的同步机制,能够用来构建各种同步组件。AQS 外部保护了一个 FIFO 队列,用于存储期待获取同步状态的线程。同时定义了一些模板办法,这些办法被子类实现,用于实现不同类型的同步器,例如 ReentrantLock、CountDownLatch、Semaphore 等。AQS 应用了一种独特的模板办法设计模式,应用外部状态(一个 volatile 润饰的 state 变量)来管制同步器的行为,子类通过实现模板办法来管制同步器的状态变动。AQS 的外部状态能够被子类用于实现独占式、共享式的同步器。综上,想要了解 AQS,以下几个方面是必要的: 同步器:AQS 是同步器的一个形象基类,通过继承 AQS 能够构建各种同步组件,如锁、信号量等。状态:AQS 外部保护了一个状态变量,示意同步器的状态。同步器的具体含意由子类来定义。队列:AQS 外部应用 FIFO 队列来存储期待获取同步状态的线程。当多个线程同时申请同步状态时,AQS 会将其中一个线程设置为独占模式,即该线程成为获取到同步状态的惟一持有者,其余线程则会被退出到期待队列中。模板办法:AQS 采纳了模板办法设计模式,在 AQS 中定义了一系列形象办法和钩子办法,子类须要实现这些办法来定义本人的同步逻辑。CAS 和 volatile:AQS 外部应用了 CAS 和 volatile 等原语来保障同步器的正确性和并发性能。 总之,AQS 是 Java 中并发包中实现锁和其余同步器的根底框架,应用模板办法设计模式和 CAS 操作实现了高效、可扩展性高的同步器。了解 AQS 对于了解 Java 中并发编程的原理和实现十分重要。AQS 如何实现那接下来问一下 ChatGPT AQS 是如何实现地: ...

March 28, 2023 · 2 min · jiezi

关于c:MIT-6181068286S081-操作系统工程-Lab9-file-system

Large files (moderate)要求在此作业中,您将减少 xv6 文件的最大大小。目前 xv6 文件限度为 268 个块,或 268*BSIZE 字节(在 xv6 中 BSIZE 为 1024)。此限度来自这样一个事实,即 xv6 inode 蕴含 12 个“间接”块号和一个“单间接”块号,它指的是最多可包容 256 个块号的块,总共 12+256=268 个块。 bigfile 命令创立它所能创立的最长文件,并报告该大小: $ bigfile..wrote 268 blocksbigfile: file is too small$测试失败,因为 bigfile 心愿可能创立蕴含 65803 个块的文件,但未修改的 xv6 将文件限度为 268 个块。您将更改 xv6 文件系统代码以反对每个 inode 中的“双间接”块,其中蕴含 256 个单间接块地址,每个地址最多能够蕴含 256 个数据块地址。后果将是文件将可能蕴含多达 65803 个块,或 256*256+256+11 块(11 而不是 12,因为咱们将就义一个间接块号来换取双间接块)。 筹备工作mkfs 程序创立 xv6 文件系统磁盘映像,并确定文件系统总共有多少块; 此大小由kernel/param.h中的 FSSIZE 管制。你将看到此试验存储库中的 FSSIZE 设置为 200,000 个块。您应该在 make 输入中看到以下来自 mkfs/mkfs 的输入:nmeta 70 (boot, super, log blocks 30 inode blocks 13, bitmap blocks 25) blocks 199930 total 200000 ...

March 28, 2023 · 4 min · jiezi

关于c++:C开发方向书籍推荐

如果你正在学习C++,那么一本好的教材或参考书能够事倍功半。以下是几本我集体举荐的C++书籍或视频: C++根底看书C++ PrimerC++程序设计语言Effective C++More Effective C++Effective STLSTL源码剖析深度摸索C++对象模型看视频黑马程序员(B站)C++内存治理(候捷)STL源码剖析(候捷)C++ STL与泛型编程高级(候捷)C++11 新个性(候捷)C++进阶书籍C++语言的设计与演变C++深思录C++ TemplatesC++ 模版元编程视频CppCon数据结构与算法书籍大话数据结构算法图解数据结构与算法剖析算法第4版算法导论视频浙大数据结构网课刷题LeetCode操作系统深刻了解计算机系统操作系统精华与设计原理古代操作系统(选读)程序员的自我涵养LinuxLinux/UNIX零碎编程手册Linux内核设计与实现深刻了解Linux内核计算机网络计算机网络自顶向下TCP/IP详解:卷1网络是怎么连贯的图解HTTP网络编程Unix网络编程Unix环境高级编程Linux多线程服务器端编程数据库数据库系统概念mysql必知必会高性能MySQLMqSQL技术底细设计模式大话设计模式Head First设计模式其余Redis设计与实现完结以上是我集体举荐的几本C++书籍。心愿这些举荐对你有所帮忙!

March 28, 2023 · 1 min · jiezi

关于c:MIT-6181068286S081-操作系统工程-Lab8-Locks

Memory allocator (moderate)要求程序 user/kalloctest 强调 xv6 的内存分配器:三个过程增长和放大它们的地址空间,导致对 kalloc 和 kfree 的许多调用。kalloc 和 kfree 获取kmem.lock 。Kalloctest 打印(作为“#test-and-set”)在acquire中因为尝试获取另一个内核曾经持有的锁(用于 kmem 锁和其余一些锁)而循环迭代的次数。获取中的循环迭代次数是锁争用的粗略度量。kalloctest 的输入如下所示: $ kallocteststart test1test1 results:--- lock kmem/bcache statslock: kmem: #test-and-set 83375 #acquire() 433015lock: bcache: #test-and-set 0 #acquire() 1260--- top 5 contended locks:lock: kmem: #test-and-set 83375 #acquire() 433015lock: proc: #test-and-set 23737 #acquire() 130718lock: virtio_disk: #test-and-set 11159 #acquire() 114lock: proc: #test-and-set 5937 #acquire() 130786lock: proc: #test-and-set 4080 #acquire() 130786tot= 83375test1 FAILstart test2total free number of pages: 32497 (out of 32768).....test2 OKstart test3child done 1child done 100000test3 OKstart test2total free number of pages: 32497 (out of 32768).....test2 OKstart test3child done 1child done 100000test3 OK您可能会看到与此处显示的计数不同,并且前 5 个争用锁的程序也不同。 ...

March 27, 2023 · 6 min · jiezi

关于c:MIT-6181068286S081-操作系统工程-Lab7-Networking

networking在本试验中,你将为网络接口卡 (NIC) 编写 xv6 设施驱动程序。 要求前言您将应用名为 E1000 的网络设备来解决网络通信。对于xv6(以及您编写的驱动程序),E1000看起来像连贯到实在以太网局域网(LAN)的实在硬件。实际上,您的驱动程序将与之通信的 E1000 是 qemu 提供的模仿,连贯到 LAN 也由 qemu 模仿。在此模仿 LAN 上,xv6(“客机”) 的 IP 地址为 10.0.2.15。Qemu 还安顿运行 qemu 的计算机呈现在 IP 地址为 10.0.2.2 的局域网上。当 xv6 应用 E1000 将数据包发送到 10.0.2.2 时,qemu 会将数据包传送到运行 qemu 的(实在)计算机上的相应应用程序(“主机”)。 您将应用 QEMU 的“用户模式网络堆栈”。QEMU 的文档在此处提供了无关用户模式堆栈的更多信息。咱们更新了 Makefile 以启用 QEMU 的用户模式网络堆栈和 E1000 网卡。 Makefile 将 QEMU 配置为将所有传入和传出数据包记录到试验目录中的 packets.pcap 文件。查看这些记录以确认 xv6 正在传输和接管您冀望的数据包可能会有所帮忙。显示记录的数据包的命令:`tcpdump -XXnr packets.pcap` 咱们已将一些文件增加到此试验的 xv6 存储库中。文件 kernel/e1000.c 蕴含 E1000 的初始化代码以及用于发送和接管数据包的空函数,您将补充这些函数。kernel/e1000_dev.h 蕴含由 E1000 定义并在英特尔 E1000 软件开发人员手册中形容的寄存器和标记位的定义。kernel/net.c 和 kernel/net.h 蕴含一个简略的网络堆栈,用于实现 IP、UDP 和 ARP 协定。这些文件还蕴含用于保留数据包的灵便数据结构的代码,称为 mbuf。最初,kernel/pci.c 蕴含当 xv6 启动时在 PCI 总线上搜寻 E1000 卡的代码。 ...

March 27, 2023 · 4 min · jiezi

关于c++:ACM算法竞赛日常训练DAY4题解与分析树子序列-组合数学-动态规划

DAY4共2题: 树(组合数学)子序列(dp,数学) 作者:Eriktse 简介:19岁,211计算机在读,现役ACM银牌选手力争以通俗易懂的形式解说算法!❤️欢送关注我,一起交换C++/Python算法。(优质好文继续更新中……) 原文链接(浏览原文取得更好浏览体验):https://www.eriktse.com/algorithm/1095.html树题目传送门:https://ac.nowcoder.com/acm/problem/13611 通过观察条件“一个染色计划是非法的,当且仅当对于所有雷同色彩的点对(x,y),x到y的门路上的所有点的色彩都要与x和y雷同。”咱们能够发现,当且仅当染色的点能够全副连通时能够满足条件。 所以当初问题是如何将n个点划分为k块。 咱们能够发现在树上,任意删除一条边都会使得联通块个数 + 1。 其实块数只有<= k即可,因为咱们能够有一些色彩不应用。所以要划分为i块,只须要从n - 1条边中任选i - 1条进行删除即可,计划数是C(n - 1, i - 1)。 假如当初咱们失去了i (i <= k)个联通块,须要将i种颜色染上去,首先须要C(k, i)种办法取出色彩,而后A(i, i)一个全排列将色彩染上去。 所以答案公式如下: $$ans=\sum_{i=1}^{k}C(n - 1, i - 1)C(k, i)i!$$ 可能波及一些疾速幂、乘法逆元的常识,须要自行学习。 #include <bits/stdc++.h>#define int long longusing namespace std;const int maxn = 350, p = 1e9 + 7;int fac[maxn];int qmi(int a, int b){ int res = 1; while(b) { if(b & 1)res = res * a % p; a = a * a % p, b >>= 1; } return res;}int inv(int x){return qmi(x, p - 2);}int C(int n, int m){ if(n < m || n < 0 || m < 0)return 0; return fac[n] * inv(fac[n - m] * fac[m] % p) % p;}signed main(){ int n, k;scanf("%lld %lld", &n, &k); fac[0] = 1; for(int i = 1;i <= n; ++ i)fac[i] = fac[i - 1] * i % p; int ans = 0; for(int i = 1;i <= n; ++ i)//分为i块 { int tmp = C(n - 1, i - 1) * C(k, i) % p * fac[i] % p; ans = (ans + tmp) % p; } printf("%lld\n", ans); return 0;}子序列题目传送门:https://ac.nowcoder.com/acm/problem/17065 ...

March 27, 2023 · 2 min · jiezi

关于c++:C学习路线

C++是一种高级编程语言,宽泛用于开发操作系统、应用程序、游戏和各种工具。如果你想学习这门语言,以下是一个适宜初学者的学习路线: 第一步:学习C++基础知识在学习C++之前,你须要把握一些基础知识,如计算机科学和编程方面的根底概念。你能够通过浏览相干书籍、观看视频教程或加入在线课程来学习这些常识。这些资源应该涵盖以下主题: 数据类型、变量和表达式管制语句(如if语句和循环)函数和参数数组和字符串指针和援用除此之外,初学者还能够学习一些其余的编程语言,比方Python。学习Python能够帮忙你更好地了解编程的基本概念和原理。 第二步:学习C++规范库一旦你把握了基础知识,你就能够开始学习C++规范库了。C++规范库是C++编程的外围,蕴含各种有用的函数和类型,可用于开发各种类型的应用程序。你须要学习以下内容: 输出和输入(如cin和cout)字符串和字符处理函数容器(如vector和map)算法(如排序和搜寻)此外,你还能够学习一些其余的库和框架,如Boost和STL等。这些库和框架能够帮忙你更好地了解C++编程和进步编程效率。 第三步:学习C++高级主题一旦你把握了基础知识和规范库,你就能够开始学习C++的高级主题了。这些主题可能包含以下内容: 面向对象编程模板和泛型编程异样解决多线程编程除此之外,你还能够学习一些其余的高级主题,如网络编程、图形界面编程等。这些主题能够帮忙你更好地利用C++编程。 第四步:练习和实际学习C++须要大量的实际和练习。你能够实现一些课程作业或参加开源我的项目,或者本人设计和实现一些小型应用程序。这样能够帮忙你坚固所学常识并进步编程技能。 除此之外,你还能够加入一些编程较量或者挑战赛,这样能够帮忙你更好地利用所学常识。 总结以上是一个适宜初学者的C++学习路线,当然还有很多其余的资源和办法能够帮忙你学习C++。最重要的是,继续一直地学习和实际,能力成为一名优良的C++编程人员。一直地浏览相干的书籍和文章,与其余编程人员交换,也能够帮忙你更好地学习和成长。

March 27, 2023 · 1 min · jiezi

关于c++:常用Linux系统命令vim编辑命令汇总看一遍用时候来查

Linux操作基本操作pwd命令作用:显示当前工作目录 用法:pwd cd命令作用:扭转目录地位 用法:cd [option] [dir] cd 目录门路 -进入指定目录 cd .. -返回父目录 cd / -进入根目录 cd或cd ~ -进入用户主目录 ls命令用法:ls [option] [file] 罕用参数: 不带任何参数:列出当前目录下的所有文件和子目录-F:分类显示,不便浏览-a:显示隐含文件 (.结尾的文件是暗藏文件)-l:查看文件的各种属性dir和vidr命令用法: dir [option] [file] dir [option] [file] 阐明:dir和ls差不多,比ls性能少;vdir相当于ls -l mkdir命令用法:mkdir [option] [file] mkdir 一次能够建设一个或者几个目录 罕用参数: 不带任何参数:创立相应目录,如果目录父级目录不存在,则创立失败; -p:创立相应目录,如果父级目录不存在,则一起创立 mv命令用法:mv [option] [源文件] [指标] 罕用参数: 不带任何参数:将源文件挪动到指标文件(能够挪动的同时改名字),留神:如果指标文件存在,则替换! -i:将源文件复制到指标文件,如果指标文件存在则提醒是否替换; -b:将源文件复制到指标文件,如果指标文件存在则不进行笼罩,而是在指标文件后加~ cp命令用法:cp [option] [源文件] [指标]罕用参数: 不带任何参数:将源文件复制到指标文件,留神:如果指标文件存在则替换; -i:将源文件复制到指标文件,留神:如果指标文件存在则提醒是否替换; -b:将源文件复制到指标文件,如果指标文件存在则不进行笼罩,而是在指标文件后加~ -r:将子目录及其中的文件一起复制到另一个子目录下 rm命令用法:rm [option] [file] 永恒删除文件! 罕用参数: 不带任何参数:删除文件或目录,不予任何提醒(然而我的带提醒,不知为何) -i :删除文件或者目录,删除是进行揭示 -f:强制性删除文件或者目录 -r:将子目录及其中的文件一并删除 ln命令用法:ln [option] [源文件] [指标文件] 建设文件链接 ...

March 26, 2023 · 1 min · jiezi

关于c#:VSTO部署方案

VSTO部署计划背景:须要应用VSTO开发一个Excel插件,插件具备自动更新的性能VisualStudio: 2022针对Excel版本:2016ClickOnce部署该计划应用了一个文件服务器用来拜访部署文件。(后以www.example.com/Files作为示例地址) 应用VS模板,创立一个Excel AddIn我的项目(在其中填充你想要的性能,此处略过不提)右键我的项目,关上属性界面选中公布页签,进行设置并点击公布公布后的文件复制到文件服务器的目录下即可用户从对应URL下载Setup.exe进行装置即可;后续也会主动从对应URL拉取更新的问题如果呈现从不信赖的地位或起源装置的报错,导致无奈进行装置,能够间接采纳信赖证书的形式装置右键下载下来的Setup.exe,找到证书进行装置 起因:公布的软件都有证书,在属性界面的签名页签里进行设置。默认应用的是测试证书,用户机器不信赖。须要手动装置信赖下才能够失常。后续不更改证书,就不必从新信赖了dll部署ClickOnce部署很不便,然而没有版本倒退的性能。在理论的我的项目中,插件版本往往和数据版本有关联的,切换到指定的svn或git版本,心愿也能有对应版本的插件性能。以svn版本控制为例,阐明怎么应用dll部署。 插件本体我的项目依然采纳ClickOnce部署的形式,不过能够不填近程Url,因为不存在插件更新的状况;界面及性能采纳Dll的形式书写 Dll我的项目创立创立一个Net Framework类库引入相干援用;将失常的VSTO我的项目中的援用都增加进来创立绘制的函数(从VSTO设计器产生的函数间接复制过去就行) using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Office.Tools.Ribbon; namespace ClassLibrary1 { public static class Class1 { /// <summary> /// 绘制tab,返回提供给VSTO应用 /// </summary> /// <param name="factory"></param> /// <returns></returns> public static RibbonTab DrawTab(RibbonFactory factory) { RibbonTab tab1 = factory.CreateRibbonTab(); RibbonGroup group1 = factory.CreateRibbonGroup(); tab1.SuspendLayout(); tab1.ControlId.ControlIdType = Microsoft.Office.Tools.Ribbon.RibbonControlIdType.Office; tab1.Groups.Add(group1); tab1.Label = "TabAddIns"; tab1.Name = "tab1"; group1.Label = "group1"; group1.Name = "group1"; tab1.ResumeLayout(false); tab1.PerformLayout(); return tab1; } } }VSTO我的项目中的功能区代码略作调整设计器产生代码批改前 ...

March 25, 2023 · 1 min · jiezi

关于c++:windows安装最新pangolin-v08

如果想在windows上配置slam的开发环境,以前是比拟麻烦的,然而当初有了微软vcpkg的反对,在win上装置依赖也能够像pip和apt那样难受,尽管还不算那么欠缺,但微软将来还会加大反对。 pangolin是slam零碎实时可视化的一个重要工具,简直各个支流开源我的项目都会用它,但网上对于怎么在windows环境装置pangolin的博客不太多,大多步骤都十分繁琐,为了躲避编译问题还要使出各种奇技淫巧,集成到我的项目中时也要费一番功夫。 我在vcpkg反对的软件包list里能看到pangolin的最新开源,所以打算用vcpkg一键装置。 装置间接在vcpkg根目录下执行:.\vcpkg.exe install pangolin:x64-windows 就会主动装置pangolin,主动下载相干依赖,主动实现编译,超级不便。目前最新的版本是0.8。 能够执行:.\vcpkg.exe list 看看装置状况。 pangolin会依赖opengl和glew,所以它们也会被装置。 接着别忘了集成到VS中,对于vcpkg的应用能够去找博客疾速理解一下。我这里是集成到全局的,所以在任何我的项目里都能够间接应用装置好的依赖。 测试新建一个我的项目,用上面的代码测试一下pangolin的应用。 #include <pangolin/pangolin.h>int main(int /*argc*/, char** /*argv*/){ // 创立名称为“Main”的GUI窗口,尺寸为640×640 pangolin::CreateWindowAndBind("Main", 640, 480); // 启动深度测试 glEnable(GL_DEPTH_TEST); // 创立一个察看相机视图 pangolin::OpenGlRenderState s_cam( pangolin::ProjectionMatrix(640, 480, 420, 420, 320, 320, 0.2, 100), pangolin::ModelViewLookAt(2, 0, 2, 0, 0, 0, pangolin::AxisY) ); // 创立交互视图 pangolin::Handler3D handler(s_cam); //交互相机视图句柄 pangolin::View& d_cam = pangolin::CreateDisplay() .SetBounds(0.0, 1.0, 0.0, 1.0, -640.0f / 480.0f) .SetHandler(&handler); while (!pangolin::ShouldQuit()) { // 清空色彩和深度缓存 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); d_cam.Activate(s_cam); // 在原点绘制一个立方体 pangolin::glDrawColouredCube(); // 绘制坐标系 glLineWidth(3); glBegin(GL_LINES); glColor3f(0.8f, 0.f, 0.f); glVertex3f(-1, -1, -1); glVertex3f(0, -1, -1); glColor3f(0.f, 0.8f, 0.f); glVertex3f(-1, -1, -1); glVertex3f(-1, 0, -1); glColor3f(0.2f, 0.2f, 1.f); glVertex3f(-1, -1, -1); glVertex3f(-1, -1, 0); glEnd(); // 运行帧循环以推动窗口事件 pangolin::FinishFrame(); } return 0;}问题编译时会报错,一大堆GL结尾的未定义标识符,发现是pangolin的头文件中找不到位于gl.h中的宏定义,对于这个问题我搜寻了很久才找到答案。 ...

March 25, 2023 · 1 min · jiezi

关于c++:面试高频问题之C编译过程

C++编译过程C++是一种高级编程语言,然而计算机并不能间接了解它。因而,须要将C++代码翻译成计算机能够了解的机器语言。这个过程就是编译过程,是C++程序从源代码到可执行文件的转换过程,包含预处理、编译、汇编和链接四个阶段。 1. 预处理在编译器开始编译之前,会先进行预处理。预处理器会解决代码中的所有预处理指令,例如#include和#define等。它会将这些指令替换成对应的代码,生成一个新的文本文件。这个新的文本文件就是编译器的输出。在预处理的过程中,还会进行条件编译,即依据条件来抉择是否编译某些代码块。预处理实现后,会生成一个没有宏定义和条件编译的两头文件。 预处理器的次要作用是解决代码中的宏定义,它会将宏定义开展成对应的代码。此外,预处理器还会解决#include指令,将指定的头文件插入到源文件中,以便让编译器可能对头文件中的函数和变量进行编译。 2. 编译编译器将预处理后的文件作为输出,对其进行词法剖析、语法分析和语义剖析等解决。这些解决会将代码转化成中间代码,中间代码是一种相似于汇编语言的低级代码。编译器将代码分成多个模块,每个模块编译成一个对象文件。在编译的过程中,编译器会对代码进行优化,以进步程序的运行效率和节俭内存空间。 在词法剖析和语法分析的过程中,编译器会对代码进行查看,以确保代码的正确性和合法性。语义剖析的工作是在代码的语法结构上进行剖析,以确定代码的含意和作用。 3. 汇编中间代码会被汇编器转化成机器语言指令,这些指令能够被计算机间接执行。汇编的过程将每个对象文件转换成机器语言的指标文件。每个指标文件蕴含机器指令和数据,以及其余的管制信息。指标文件能够被链接器用于构建可执行文件。 汇编器的次要工作是将中间代码翻译成机器语言指令,以便让计算机可能执行代码。在这个过程中,汇编器会将每个源文件编译成一个指标文件,而后将所有的指标文件组合成一个可执行文件。 4. 链接在编译过程中,可能会用到其余的库文件和对象文件。链接器会将这些文件与生成的可执行文件进行链接,生成最终的可执行文件。链接器的次要工作是解决符号援用问题,即通过在不同的指标文件中查找符号的定义,使得所有的符号都可能正确地被解析和链接。链接器还会对代码进行优化和压缩,以减小可执行文件的体积,并进步程序的运行效率。 链接器的工作包含将每个指标文件中的符号解析成地址,并将它们组合成一个独自的地址空间。链接器还会解决代码中的重定位信息,以确保代码可能正确地加载和执行。 论断C++编译过程是一个非常复杂的过程,它须要通过屡次解决能力最终生成可执行文件。然而,理解这个过程对于了解C++代码和调试程序都十分有帮忙。在理论的开发中,程序员须要把握编译工具链的应用,以便可能更好地进行调试和优化。同时,程序员还须要理解编译器的工作原理和优化技术,以写出高效的C++代码。

March 25, 2023 · 1 min · jiezi

关于c:MIT-6181068286S081-操作系统工程-Lab6-Multithreading

Uthread: switching between threads (moderate)要求在本练习中,您将为用户级线程零碎设计上下文切换机制,而后实现它。为了帮忙您入门,您的 xv6 有两个文件 user/uthread.c 和 user/uthread_switch.S,以及Makefile中用于构建 uthread 程序的规定。uthread.c 蕴含大部分用户级线程包,以及三个简略测试线程的代码。线程包短少一些用于创立线程和在线程之间切换的代码。 您的工作是制订一个打算来创立线程并保留/复原寄存器以在线程之间切换,并实现。当你实现后,在xv6上运行uthread将看到下列输入(三个测试秩序不确定): $ uthreadthread_a startedthread_b startedthread_c startedthread_c 0thread_a 0thread_b 0thread_c 1thread_a 1thread_b 1...thread_c 99thread_a 99thread_b 99thread_c: exit after 100thread_a: exit after 100thread_b: exit after 100thread_schedule: no runnable threads$此输入来自三个测试线程,每个测试线程都有一个循环,该循环打印一行,而后将 CPU 提供给其余线程。 然而,此时,如果没有上下文切换代码,则不会看到任何输入。 你将须要在user/uthread.c中的thread_create()、thread_schedule()以及user/uthread_switch.S中的thread_switch中增加代码。一个指标是确保当 thread_schedule() 首次运行给定线程时,线程在其本人的堆栈上执行传递给 thread_create() 的函数。另一个指标是确保thread_switch保留被切换的线程的寄存器,复原正在切换到的线程的寄存器,并返回到后一个线程指令中上次中断的地位。您必须决定在何处保留/复原寄存器;批改struct thread以保留寄存器是一个很好的打算。您须要在thread_schedule中增加对thread_switch的调用; 您能够传递您须要的任何参数到thread_switch中,但目标是从线程 t 切换到next_thread。提醒thread_switch只须要保留/复原被调用方保留寄存器。为什么?您能够在user/uthread.asm中看到uthread的汇编代码,这对于调试可能很不便。对于gdb调试实现线程实现的思路:依据提醒、参考内核中过程的切换,可确定思路: 线程状态与上下文:创立一个构造体记录线程本人的上下文和状态。在一个全局的该构造体数组中保护所有线程线程调度:在全局数组中寻找下一个可运行的线程,而后进行线程的切换操作线程切换:取出上一个线程寄存器中的值(状态)保留,将要执行的线程上一次保留的状态放入寄存器线程创立(线程初始化):须要让线程执行传入的函数指针所指的函数、并实现线程堆栈初始化 通过ra寄存器实现线程的命令初始化:ra寄存器示意函数返回地址,因而可用此寄存器保留要执行的函数地址,在thread_switch后将会返回thread_schedule()函数,若将ra更改为传入函数地址,则程序也会跳到传入函数的地址去执行命令堆栈初始化:每个线程都该当有本人独立的堆栈,线程运行时通过寄存器sp来读写堆栈。寄存器sp保留指向堆栈顶部的指针,但线程的堆栈增长方向是从下向上增长的(即从大内存编号向向内存编号增长),因而sp中的指针该当指向struct thread中堆栈数组的最初一个元素。线程状态和上下文:创立构造体struct context保留线程上下文,并在线程构造体中退出该字段 struct context { uint64 ra; // return address uint64 sp; // stack pointer // callee-saved uint64 s0; uint64 s1; uint64 s2; uint64 s3; uint64 s4; uint64 s5; uint64 s6; uint64 s7; uint64 s8; uint64 s9; uint64 s10; uint64 s11;};struct thread { char stack[STACK_SIZE]; /* the thread's stack */ int state; /* FREE, RUNNING, RUNNABLE */ struct context context;};线程初始化:在thread_create()中实现线程初始化:①将函数地址放入保留函数返回地址的ra寄存器;②将堆栈数组最初一个元素的指针放入保留堆栈顶指针的sp寄存器 ...

March 24, 2023 · 4 min · jiezi

关于c:MIT-6181068286S081-操作系统工程-Lab5-CopyonWrite-Fork

前言虚拟内存提供某种间接援用:内核能够通过将PTE标记为有效或只读来截获内存援用,从而导致页面谬误,也能够通过批改PTE来更改地址含意。计算机系统中有一个说法:任何零碎问题都能通过某种间接援用来解决。本试验探讨了一个示例:copy-on-write(COW)fork 问题xv6中的fork()syscall复制父过程所有用户空间的内存到子过程中。如果父过程很大,这个拷贝过程就会花很长时间。更蹩脚的是,这个拷贝常常被节约:fork()函数后子过程通常追随一个exec(),这会抛弃复制的内存、通常不应用大部分内存。另一方面,若父子过程其中一个或都写入了这个复制的页面,则这个拷贝过程是须要的。 解决方案你在实现COW fork()过程中的指标是,推延开拓空间和拷贝物理内存页直到真正须要这些拷贝时。 COW fork()仅为子过程创立一个页表,其中用于用户内存的PTE指向父过程物理页。COW fork()将父子过程所有用户PTE都标记为只读。当任一过程尝试写入这些 COW 页之一时,CPU 将强制呈现页谬误。内核页面谬误处理程序检测到这种状况,为谬误过程调配一页物理内存,将原始页面复制到新页面中,并在谬误过程中批改相干 PTE 以援用新页面,这次 PTE 标记为可写。当页面谬误处理程序返回时,用户过程将可能写入其页面正本。 COW fork()使开释实现用户内存的物理页面变得更加辣手。给定的物理页可能由多个过程的页表援用,并且仅当最初一个援用隐没时才应开释。在像 xv6 这样的简略内核中,这种相当简略,但在生产内核中,这可能很难正确。例如,Patching until the COWs come home. 要求在xv6内核中实现copy-on-write fork。为帮忙你的实现,咱们曾经提供了一个程序指令cowtest(源码在user/cowtest.c)。cowtest运行各种测试,但如果过没有更改xv6即使第一个测试也会失败。这个“简略的”测试开拓超过一半的可用物理内存,而后fork()s。个别状况下,fork会因为没有足够的闲暇物理内存给子过程一个残缺的拷贝而失败。当你实现后,该当通过cowtest usertests -q的所有测试。 正当的attack打算批改 uvmcopy() 以将父级的物理页面映射到子级,而不是调配新页面。革除子级和父级的 PTE 中已被置位PTE_W的PTE_W位。批改 usertrap() 以辨认页面谬误。当最后可写的 COW 页面上产生写入页面谬误时,应用 kalloc() 调配一个新页面,将旧页面复制到新页面,并在被置位为PTE_W的 PTE 中装置新页面。最后为只读的页面(未映射PTE_W,如文本段中的页面)应放弃只读并在父过程和子过程之间共享; 尝试编写此类页面的过程应该被kill()。确保每个物理页在最初一个 PTE 援用隐没时被开释--但不是之前。执行此操作的一个好办法是为每个物理页面保留援用该页面的用户页表数量的“援用计数”。将页面的援用计数设置为 1 当 kalloc() 调配它时。当 fork 导致子级共享页面时,递增页面的援用计数,并在每次任何过程从其页表中删除页面时递加页面的计数。kfree()应该只在援用计数为零时将页面放回自在列表。能够将这些计数保留在固定大小的整数数组中。您必须制订一个如何索引数组以及如何抉择其大小的计划。例如,你能够用页面的物理地址除以 4096 来索引数组,并为数组提供一个数字,这个数等于 kalloc.c 中的kinit()搁置在闲暇列表中的任何页面的最高位物理地址。能够随便批改kalloc.c(例如kalloc(), kfree())来保护援用计数。批改 copyout() 以在遇到 COW 页面时应用与页面谬误雷同的计划。提醒记录每个PTE是否是COW映射会很有用。为此,你能够应用RISC-V PTE的RSW(软件预留)置位。无关页表flags的一些有用的宏和定义位于kernel/riscv.h开端。若产生一个COW页面谬误且内存不够,则该过程该当被kill实现复制页表但推延复制页表内容:依据提醒,我从fork()中调用的uvmcopy()动手。uvmcopy()本来的作用是:把父过程页表中每个PTE所指内容都拷贝到新开拓的空间内并在子过程的页表上造成映射。当初须要把开拓空间这一步去掉,间接在子页表造成映射,然而要扭转flags。为了让代码难看点、放弃逻辑清晰,我创立一个新函数cow_uvmcopy()并在uvmcopy()中调用它。新函数根本与老函数保持一致,不过去掉开拓空间、加上扭转flags: int cow_uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) { pte_t *pte; uint64 pa, i; for(i = 0; i < sz; i += PGSIZE){ if((pte = walk(old, i, 0)) == 0) panic("uvmcopy: pte should exist"); if((*pte & PTE_V) == 0) panic("uvmcopy: page not present"); pa = PTE2PA(*pte); // 扭转flags // *pte = (*pte & (~PTE_W)) | PTE_C; // 错的!(后续阐明) if (*pte & PTE_W) { // 把父过程PTE的PTE_W地位0,再加上PTE_C位 *pte = (*pte & (~PTE_W)) | PTE_C; } // 间接将父过程物理地址在子过程页表中建设映射 if(mappages(new, i, PGSIZE, (uint64)pa, PTE_FLAGS(*pte)) != 0) { goto err; } increaseReference(pa); // 援用计数+1(后续阐明) } return 0;err: uvmunmap(new, 0, i / PGSIZE, 1); return -1;}无关flags的设置:首先要把父子过程的PTE_W给去掉,这样能力在未来触发page fault。而后就是加上对于cow的标记,须要在riscv.h中自定义,能够定义在预留位上(详见Lab3 page tables)#define PTE_C (1L << 8)。未来可通过该bit位判断是本来就不可读的还是cow机制强加的不可读。 ...

March 23, 2023 · 3 min · jiezi

关于c:MIT-6181068286S081-操作系统工程-Lab4-Trapsdoing

RISC-V assembly (easy)问题哪些寄存器保留函数的参数?例如,在main函数对printf的调用中,哪个寄存器保留13?main函数中哪里调用了汇编代码中的f函数和g函数?(编译器可能内联函数)printf位于哪个地址?紧接着在main中跳转到printf之后,寄存器ra中保留的值是什么?运行下列代码: unsigned int i = 0x00646c72;printf("H%x Wo%s", 57616, &i);输入什么? 输入取决于RISC-V是小端序(little-endian)的事实。若RISC-V是大端序(big-endian),则i该当设置成什么能力给出雷同的输入?须要扭转57616吗? 下列代码中,y=之后将会输入什么?(非特定值)为何? printf("x=%d y=%d", 3);答复a0 a1 a2(main函数只有这仨,实际上到a7); a2保留13在0x26处,看到间接将12给寄存器a1,阐明f的调用被优化掉了在0x34处,可知jalr跳转到1554 + ra地址处。而依据0x30处,寄存器ra保留了0x30的地址,因而jalr跳转到1554(dec)+0x30(hex)=0x642(hex)的指标地址,因而printf的地址是0x642,发现与正文雷同0x38输入:HE110 World 首先%x输入57616的十六进制模式而后是值为0x00646c72的i,%s实际上读取的是字符串,也就是一个又一个char,所以i这个int类型实际上能够看作由4个char类型组成,而后就是这4个char怎么排列的问题大端序即:0x12345678 -> 0x 12 34 56 78(从左到右地址序号增大)小端序即:0x12345678 -> 0x 78 56 34 12(从左到右地址序号增大)材料指出RISC-V采纳小端序,由此可知i在内存当中的贮存为:0x 72 6c 64 00因而会先读取值为0x72的char类型,在ascii中对应字母r;而后是0x6c,对应字母l;0x64对应d;0x00对应终止因而若是大端序,则i应设置为:i = 0x646c7200。而57616不须要变,因为没有产生扭转大小的类型转变。 实践上讲,因为传入两个参数,因而a0和a1寄存器都保留了确定的值,但在printf中因为应用了第三个参数,因而会应用a2的值作为y=后边的输入值;但a2寄存器中的值并不能确定,因而不确定输入后果。 但实际上,我尝试屡次都是1。可能是因为没有函数应用寄存器a2,a2就始终放弃值为1的状态吧Backtrace (moderate)要求回溯对debug十分有用:在error产生时刻的堆栈上零碎调用的一个列表。为实现回溯,编译器生成机器码,在堆栈上保护以后调用链上每个函数对应的堆栈帧。每个堆栈帧由返回地址和一个指向调用者堆栈帧的帧指针组成。寄存器s0包含一个指向以后堆栈帧的指针(理论指向堆栈上保留的返回地址+8).你的backtrace该当应用帧指针遍历堆栈并打印堆栈帧上保留的返回地址。 在kernel/printf.c中实现backtrace()。在sys_sleep中调用此函数。运行命令bttest,会调用sys_sleep。输入格局该当如下所示: backtrace:0x0000000080002cda0x0000000080002bb60x0000000080002898在终端运行命令addr2line -e kernel/kernel并将backtrace输入的地址复制粘贴过去就能失去以下输入: kernel/sysproc.c:74kernel/syscall.c:224kernel/trap.c:85提醒在defs.h中增加定义。GCC编译器将以后函数的帧指针保留在寄存器s0中。在kernel/risv.h中增加上面的函数: static inline uint64 r_fp() { uint64 x; asm volatile("mv %0, s0" : "=r" (x) ); return x;}在backtrace中调用该函数可通过内联的机器码读取寄存器s0 堆栈帧布局示意图(如下)。留神,返回地址位于堆栈帧帧指针固定偏移-8的中央;以及保留的帧指针位于(以后)帧指针固定偏移-16的地位。 Stack . . +-> . | +-----------------+ | | | return address | | | | previous fp ------+ | | saved registers | | | local variables | | | ... | <-+ | +-----------------+ | | | return address | | +------ previous fp | | | saved registers | | | local variables | | +-> | ... | | | +-----------------+ | | | return address | | | | previous fp ------+ | | saved registers | | | local variables | | | ... | <-+ | +-----------------+ | | | return address | | +------ previous fp | | | saved registers | | | local variables | |$fp --> | ... | | +-----------------+ | | return address | | | previous fp ------+ | saved registers |$sp --> | local variables | +-----------------+backtrace须要辨认最初的栈帧以进行递归。一个有用的事实是,为每个内核堆栈调配的内存由单个对齐内存页组成,因而给定堆栈上的所有堆栈帧都在同一内存页上。你能够应用PGROUNDDOWN(fp)辨认帧指针援用的内存页如果backtrace运行失常,能够在panic中调用它。 ...

March 21, 2023 · 2 min · jiezi

关于c++:面试高频问题之C11新特性

背景C++11是C++语言的一个重大更新,引入了许多新个性,包含主动类型推导、lambda表达式、右值援用、智能指针等等。这些新个性使得C++更加现代化、高效、易用。也是面试容很容易被问到一个问题,上面我将简要介绍一些C++11的新个性: 主动类型推导C++11引入了auto关键字,能够让编译器主动推导变量的类型。这样能够缩小代码中的反复代码,进步代码的可读性和可维护性。例如: auto i = 42; // 推导为int类型auto d = 3.14; // 推导为double类型auto s = "hello"; // 推导为const char*类型lambda表达式C++11引入了lambda表达式,能够不便地定义匿名函数。这样能够缩小代码中的反复代码,进步代码的可读性和可维护性。例如: auto f = [](int x, int y) { return x + y; };int z = f(1, 2); // z的值为3右值援用C++11引入了右值援用,能够不便地实现挪动语义和完满转发。这样能够进步代码的性能和效率。例如: class MyString {public: MyString() : data_(nullptr), size_(0) {} MyString(const char* str) : data_(new char[strlen(str) + 1]), size_(strlen(str)) { strcpy(data_, str); } MyString(MyString&& other) : data_(other.data_), size_(other.size_) { other.data_ = nullptr; other.size_ = 0; } ~MyString() { delete[] data_; }private: char* data_; size_t size_;};更多对于右值援用的内容,感兴趣的小伙伴能够翻看我之前的帖子,有专门的介绍。 ...

March 20, 2023 · 1 min · jiezi

关于c#:实现常驻任务除了避免昙花线程还需要避免重返线程池

后面咱们应用简略的例子演示了 Task 和 Thread 的两种制作昙花线程的形式。那么除了防止昙花线程,在实现常驻工作的时候,还须要防止重返线程池。本文将介绍如何防止重返线程池。 常驻工作常驻工作十分常见,比方: 咱们正在编写一个日志文件库,咱们心愿在后盾一直的将日志写入文件,尽可能不影响业务线程的执行。因而,须要一个写文件的常驻工作。咱们对接了一个近程 TCP 服务,对方要求咱们每隔一段时间发送一个心跳包,以放弃连贯。因而,须要一个发送心跳包的常驻工作。咱们编写了一个简略的内存缓存,通过一个后台任务来定期清理过期的缓存。因而,须要一个清理缓存的常驻工作。相似的场景还有很多。因而,咱们须要一个可能实现常驻工作的办法。 而实现常驻工作的次要要点是: 常驻工作必须防止影响业务线程的执行,因而须要在后盾执行。常驻工作不能被业务线程影响,无论以后业务如许忙碌,常驻工作都必须可能失常执行。否则会呈现日志不落盘,心跳包不发送,缓存不清理等问题。实现常驻工作的伎俩有很多。本文将围绕如何应用常驻繁多线程来实现常驻工作。 所谓常驻繁多线程,就是指始终应用一个线程来执行常驻工作。从而达到: 防止频繁的创立和销毁线程,从而防止频繁的线程切换。更容易的解决背压问题。更容易的解决线程平安问题。评测主体咱们将采纳如下状况来评测如何编写常驻工作的正确性。 private int _count = 0;private void ProcessTest(Action<CancellationToken> action, [CallerMemberName] string methodName = ""){ var cts = new CancellationTokenSource(); // 启动常驻线程 action.Invoke(cts.Token); // 严架给压力 YanjiaIsComing(cts.Token); // 期待一段时间 Thread.Sleep(TimeSpan.FromSeconds(5)); cts.Cancel(); // 输入后果 Console.WriteLine($"{methodName}: count = {_count}");}private void YanjiaIsComing(CancellationToken token){ Parallel.ForEachAsync(Enumerable.Range(0, 1_000_000), token, (i, c) => { while (true) { // do something c.ThrowIfCancellationRequested(); } });}这里咱们定义了一个 ProcessTest 办法,用于评测常驻工作的正确性。咱们将在这个办法中启动常驻工作,而后执行一个严架给压力的办法,来模仿十分忙碌的业务操作。最初咱们将输入常驻工作中的计数器的值。 能够初步看一下严架带来的压力有多大: ...

March 20, 2023 · 2 min · jiezi

关于c++:Visual-Studio-2023-激活下载安装教程内附Visual-Studio激活码密钥

visual studio 2022(vs 2022)是由微软官网出品的最新版本的开发工具包系列产品。它是一个残缺的开发工具集,囊括了整 visual studio 2022是一款由微软全新研发推出的编程开发软件,该软件可能为程序开发人员提供一个绝佳的IDE开发环境,可帮忙用户高效率地杜绝错误代码,当用户输出错误代码的时候,零碎即会主动将其标红,可完满反对C#、C++、Python、Visual Basic、Node.js、HTML、JavaScript等支流的编程语言,帮忙程序员轻松地实现调试、探查和诊断程序,进步代码的准确率和工作效率,明天小编为大家带来了这款软件的专业版,附带了专业版激活密钥,激活后即可永恒收费应用,喜爱的小伙伴千万不要错过哦。 外围亮点1、外围调试器中的性能改良 2、UI 更新,旨在缩小复杂性,这将减少与 Accessibility Insights 的集成。图标更新,并减少对 Cascadia Code 的反对,这是一种新的 fixed-width 字体,可进步可读性 3、对 .NET 6 的反对,Windows 和 Mac 开发人员都能够应用它来构建 Web、客户端和挪动应用程序,并为开发 Azure 应用程序提供了更好的反对反对热更新预览,无需重启我的项目即可查看更改 4、更弱小的代码预测能力 5、对 C++ 工作负载的弱小反对,包含新的 productivity features、C++20 工具和 IntelliSense。还集成了对 CMake、Linux 和 WSL 的反对,使用户更容易创立、编辑、构建和调试跨6、平台应用程序 7、将文本聊天集成到 Live Share 合作性能中 8、反对 Git 和 GitHub 9、改良代码搜寻性能。 10、Visual Studio for Mac 将迁徙至原生 macOS UI,领有更好的性能和可靠性。 Visual Studio 2022密钥PS: 密钥没有间接贴出来,是为了回绝一部分白嫖党,因为有的人即使是你帮了他他还骂你,所以我把密钥放在了公众号里,这样我既能够帮忙到那些信赖我的人,同时也能够给公众号涨涨粉,两败俱伤,不丢人。 须要的小伙伴,扫描下方公众号二维码,或者关注公众号: 徐公,回复关键字:vs2 即可收费无套路获取,继续更新中~ visual studio 2022 最新破解装置教程,附激活码(2023 年 2 月 10 号,亲测无效 ...

March 19, 2023 · 1 min · jiezi

关于c:MIT-Lab3-Page-tables编辑中

Detect which pages have been accessed (hard)要求为xv6实现一个新性能:通过查看 RISC-V 页表中的拜访位,检测并报告用户空间拜访了哪些页。 实现零碎调用pgacess,报告拜访过哪些页面。该零碎调用须要三个参数:(1)第一个用户须要检测的页面的起始虚拟地址;(2)须要检测的页面数量;(3)一个指向缓存的用户地址,用以贮存位掩码后果。提醒: 在user/pgtlbtest.c中理解如何应用 pgaccess。在kernel/sysproc.c中实现sys_pgacess,可用argaddr()或argint()传递参数对于位掩码,可保留在内核长期缓存区,在获取正确的位掩码之后通过copyout()拷贝给用户可设置扫描页面数的下限kernel/vm.c中的walk()有助于找到正确的PTEs(页表项)在kernel/riscv.h 定义拜访位PTE_A,参考手册确定其值在查看一个页面的PTE_A位是否被设置后,切记要复原它。否则无奈检测自上次pgacess之后用户是否再次拜访它vmprint()可能对debug有帮忙实现后果A kernel page table per process (hard)要求为每个过程提供一个内核页表。 为了让每个过程在内核运行时可能应用本人拷贝的内核页表,咱们须要批改内核。批改struct proc为每个过程保护一个内核页表、批改调度程序好让它在切换时切换内核页表。对于此步骤,每个过程的内核页表该当与现有的全局页表雷同。提醒: 为struct proc增加字段正当为新过程产生一个内核页表的办法是批改kvminit而不是批改kernel_pagetable。你须要在allocproc调用此函数确保每个过程的内核页表都有一个到内核堆栈的映射。在未修改的xv6中,内核堆栈在procinit实现设置。你须要将这部分性能的局部或全副移到allocproc中。批改scheduler()以将过程的内核页表加载到外围的satp寄存器(参考kvminithart)。切记在调用w_satp()后调用sfence_vma()scheduler()在没有过程运行时该当应用kernel_pagetable在freeproc中开释内核页表你须要一个开释页表但不开释页物理内存页的办法vmprint可能对debug有用至多须要批改的中央:kernel/vm.c, kernel/proc.c; 不能动的中央:kernel/vimcopyin.c, kernel/stats.c/ user/usertests.c, user/stats.c实现后果Simplify copyin/copyinstr (hard)要求内核中的copyin函数通过将用户指针转变为内核能间接解援用的物理地址,进而读取用户指针所指的内存。这种转变可通过在软件中遍历过程页表实现。你须要为每个过程的内核页表(之前实现的)增加用户映射,以容许copyin间接解援用用户指针。 将kernel/vm.c中的copyin的函数体替换为对copyin_new(在kernel/vmcopyin.c中定义)的调用;copyinstr和copyinstr_new同理。为使copyin_new和copyinstr_new失常工作,须要增加用户地址到每个过程的内核页表之间的映射。该计划依赖于用户虚拟地址范畴与内核虚拟地址范畴不重合。xv6将从0开始的虚拟地址作为用户地址空间,而内核内存从更高的地址开始。然而,该计划必须限度用户过程的大小小于内核最低虚拟地址。在内核被疏导后,在xv6中那个地址是0xC000000,即PLIC寄存器的地址。参考 kernel/vm.c 中的 kvminit()、kernel/memlayout.h 和文本中的图 3-4。你须要批改xv6来爱护用户过程不能大于PLIC地址。提醒: 在移用到copyinstr之前,首先将copyin()替换为对copyin_new的调用并使其失效。每当内核切换过程的用户映射时,也都该当以同样的形式切换过程的内核页表。(这些状况包含fork() exec() sbrd())切记在userinit中将第一个过程的用户页表包含在内核页表中在内核页表中的用户地址PTE须要哪些权限?(内核无法访问PTE_U的内存页)切记PLIC限度实现后果

March 19, 2023 · 1 min · jiezi

关于c#:C-tuple元组详解

概念实质就是个数据结构,它是将多个数据元素分组成一个轻型数据结构。 如何申明元组变量(针对.net framework 4.7+ 和 .net core 2.0+)不带字段名称元组## t1就是个变量 它的类型是元组类型## 左侧括号定义的是参数列表 等于号右侧就是个t1赋值## 须要留神等号左右侧 参数个数要统一 且对应位的数据类型要匹配统一(double, int) t1 = (4.5, 3);不带字段名称元组的取值## 应用 变量,ItemN 其中第一位就是Item1 第二位就是Item2 顺次类推var ontValue =t1.Item1;var twoValue =t1.Item2;...var NValue =t1.ItemN;带字段名称的元组## 定义形式一 其中A和B就是元组的字段(double A, int B) t1 = (4.5, 3);## 定义形式二 其中A和B就是元组的字段var t1 = (A:4.5, B:3);带字段名称元组的取值### 获取字段A和Bdouble _A=t1.A;int _B =t1.B;带字段名称元组的赋值t1.A =30.8;t1.B =90;在.net framework 4.7以下版本(反正2023-03-18之前没有把上述的新个性退出)在低版本下须要应用Tuple(动态类) 语法:Tuple<T1, T2, T3, T4, T5, T6, T7, TRest> 由下面能够看出 除非是嵌套的元组 ,一般的最多只能有8位元素 如何申明元组变量## 形式一 应用静态方法Createvar t2= Tuple.Create(90, "程序员Ken", 8.05);## 形式二 Tuple<int, string, double> t2 = new Tuple<int, string, double>(90, "程序员Ken", 8.05);取值### 跟下面的相似 能够看看“不带字段名称元组的取值”int _item1=t2.Item1;string _item2 =t2.Item2;double _item3 =t2.Item3;嵌套元组如果要在一个元组中蕴含八个以上的元素,则能够通过嵌套另一个元组对象作为第八个元素来实现。能够应用Rest属性拜访最初一个嵌套元组。要拜访嵌套元组的元素,请应用 ...

March 18, 2023 · 1 min · jiezi

关于c++:CSTL教程7priorityqueue优先队列入门学习零基础都能听懂的教程

人不知;鬼不觉C++STL教程系列曾经第7期了。之前咱们介绍过:vector, queue, stack, set, map等等数据结构。 明天咱们来学习一个新的stl容器:priority_queue优先队列。 作者:Eriktse 简介:19岁,211计算机在读,现役ACM银牌选手力争以通俗易懂的形式解说算法!❤️欢送关注我,一起交换C++/Python算法。(优质好文继续更新中……) 集体博客:www.eriktse.compriority_queue介绍priority_queue是STL中的一个重要容器,它相似于一般的队列,反对弹出堆顶元素和插入元素的操作,然而有一个很大的不同:队列中元素依照优先级参数排序,最小(或者最大)元素总是在队列的前端,并且会在每次出队的时候被删除。 priority_queue领有两种不同的形式:一种是依照大顶堆(max heap)形式排序,另一种是依照小顶堆(min heap)形式排序。STL提供了基于容器改编的priority_queue,因而能够在传递给priority_queue的容器中,反对不同类型的元素。 初始化在应用priority_queue之前须要引入头文件#include <priority_queue>。 用以下代码初始化一个空的priority_queue。 priority_queue<int> pq;//默认为大根堆priority_queue<int, vector<int>, greater<int> > pq2;//批改为小根堆能够用适当类型的对象初始化一个优先级队列: string wrds[] { "one", "two", "three", "four"};priority_queue<string> words {start(wrds), end(wrds)}; // "two" "three" "one" "four" 如果是寄存构造体,且要自定义比拟函数,能够用仿函数,就是用写一个构造体,而后重载()运算符。 struct Node{ int a, b;};//申明Node构造体struct cmp{ bool operator () (const Node &u, const Node &v)const { return u.a < v.a; }};priority_queue<Node, vector<Node>, cmp> pq_Node;罕用操作函数priority_queue仅保护一个顶部。 插入元素push()办法:用于将一个新元素增加到堆中。 pq1.push(1);//将元素1插入到pq1中。获取堆顶元素top()办法:获取堆顶的元素,然而不会自动弹出,如果须要弹出请再加一个pop()。 留神当堆为空时,不容许获取堆顶元素,或弹出堆顶元素,须要判断堆非空能力对堆顶操作。获取堆内元素个数size()办法:返回一个非负整数,示意堆内元素个数。 当size() == 0时,示意堆为空。 判断堆是否为空empty()办法:返回一个bool值,示意堆是否为空。 ...

March 17, 2023 · 1 min · jiezi

关于c++:CSTL教程6bitset是什么和bool有什么区别零基础都能看懂的入门教程

之前咱们介绍过vector, queue, stack,map,set,明天咱们介绍另外一个stl容器:bitset。 作者:Eriktse 简介:19岁,211计算机在读,现役ACM银牌选手力争以通俗易懂的形式解说算法!❤️欢送关注我,一起交换C++/Python算法。(优质好文继续更新中……) 集体博客:www.eriktse.com本文仅从入门和实用角度进行解说,次要针对初学者或比赛向。如有不谨严的中央欢送斧正! bitset 简介bitset是C++规范模板库(STL)中的一种数据结构,它以十进制数的模式存储bit(二进制位)的值。它的目标是包容在肯定的空间中存储和操作十分小的数字,因为常常只须要一位来示意某个值是true还是false。 bitset能够存储大小可变的位,每一位存储一位数据,可用 true, false, 0, 1 来示意,可能灵便的操作每一位。 bitset提供一个十分无效的接口,用于在bit向量中疾速地实现检索和更新操作,是对内存和硬件资源的高效利用。 初始化应用bitset须要引入头文件#include <bitset>。 用以下代码初始化一个bitset变量。 bitset<4> bs;//初始化一个大小为4的bitsetbitset即便初始化在栈空间(非全局变量)里,也会全副初始化为0。 bitset对象的构造函数要求参数为定义一个数字,这个数字就是你要应用的位的个数。例如, bitset<4> bs;这个bitset<4> bs定义一个bitset,它具备4个二进制位,每个位都是0。 bitset<4> bs(5);//0101要定义一个bitset并为每个位设定初始值,能够把对应的0和1用字符串来写 bitset<10> bs2("1010101010");用法其实能够间接当做一个能够做位运算的bool数组用,它常常用于动静布局中。 批改bs[0] = 1;cout << bs << '\n';//0001位运算运算符个别能够分为两类:流运算符,例如<<和>>; bs <<= 1;cout << bs << '\n';//0010另一类是比拟、赋值运算符,例如&、|、^等等。 bs |= (bitset<4>(1) << 3);cout << bs << '\n';//1010bs &= (bitset<4>(1) << 4);cout << bs << '\n';//1000其余办法bitset还有几个有用的办法: count():用于计算bitset中1的个数。any():如果bitset中有一个位是1,那么返回true,否则返回false。none ():如果bitset中每个位都是0,那么返回true,否则返回false。set ():用于将bitset中的所有地位设置成1。reset ():用于将bitset中的所有地位设置成0。flip ():用于将bitset中的所有地位反转。和bool数组的区别STL中的 bitset 是由固定长度的二进制位组成的数据类型,位数的范畴由模板参数确定,最多可示意2^[模板参数]个值; bool 是c++用作布尔型的类型,只能示意真或假,两个类型之间惟一的不同就是:bitset 是变长度,bool 是定长度。这意味着对于 bool,每个值都须要占用8位,而bitset能够依据理论值须要而扭转长度,比方只有1位保留即可。 ...

March 17, 2023 · 1 min · jiezi

关于c:从Dwarf-Error说开去

背景近期我开发的一个C程序,在生产环境产生了coredump,然而在调试该core文件时,打出的debug信息并不全。 这种debug信息失落,其实说白了,就是符号表失落。个别由两种状况造成,一种是编译的时候没有加-g参数,另一种是dwarf版本不对。首先排除第一种可能,因为编译脚本是我本人写的,-g参数是有的。而惟一可能出问题的中央,就是dwarf版本不对。而之所以呈现dwarf版本不对,还是编译环境的问题。我为了兼容编译C++17规范的另外一个cpp我的项目,就对编译环境做了容器化解决,在镜像里装置了gcc11.3,而在生产环境应用的时候,gdb版本依然是4.8.5,因为gcc版本和gdb版本不匹配,就造成了该问题的呈现。为了验证这一点,我在物理机上重现了这种景象: [root@ck08 ctest]# gcore `pidof flow`Dwarf Error: wrong version in compilation unit header (is 5, should be 2, 3, or 4) [in module /root/chenyc/src/flow/flow][New LWP 3048][New LWP 3047][New LWP 3046][New LWP 3045][Thread debugging using libthread_db enabled]Using host libthread_db library "/lib64/libthread_db.so.1".0x00007f50dfd850e3 in epoll_wait () from /lib64/libc.so.6warning: target file /proc/3044/cmdline contained unexpected null charactersSaved corefile core.3044[Inferior 1 (process 3044) detached]我的物理机的gdb版本也是4.8.5, 我应用gcore命令生成core文件的时候,呈现了上面的正告:Dwarf Error: wrong version in compilation unit header (is 5, should be 2, 3, or 4),这句话从字面意思很好了解,就是说,gdb反对的dwarf版本应该是2,3,或者4,然而以后二进制文件的dwarf版本是5,无奈调试。那么,何为dwarf?什么又是dwarf版本呢? ...

March 17, 2023 · 5 min · jiezi

关于c++:CSTL教程5set是什么怎么用零基础都能看懂的入门教程

之前咱们介绍过vector, queue, stack,map。咱们晓得前三者是线性构造,而map是一种树状构造,明天咱们要介绍另外一个树状构造实现的stl容器:set。 作者:Eriktse 简介:19岁,211计算机在读,现役ACM银牌选手力争以通俗易懂的形式解说算法!❤️欢送关注我,一起交换C++/Python算法。(优质好文继续更新中……) 集体博客:www.eriktse.com本文仅从入门和实用角度进行解说,次要针对初学者或比赛向。如有不谨严的中央欢送斧正! 什么是 set?set意为汇合,在高中咱们学习过汇合的三大性质:确定性、互同性、无序性。在C++的STL中的汇合,容器内会默认依照“升序”排列,但同样遵循确定性和互同性。 C++ STL SetSet是C++规范模板库(STL)中较为重要的容器,原生反对有序,惟一。 一个大小为n的set所需的空间约为nlogn个单位。set的插入、删除、查找操作复杂度均为O(logn)。 要害个性唯一性:Set容器内的元素都是惟一的,也就是说,每个元素都是不同的有序性:Set容器内的元素总是排序的,向Set中增加元素,它将主动插入到正确的地位中,不须要手动排序查找/插入疾速:因为Set容器的元素是排序的,所以在Set中查找和插入元素都很快实用场景Set容器的有序性和唯一性个性极大地缩小了大量反复和排序等工作,在很多场景下Set容器更具劣势,下列状况是应用Set容器适合的状况: 存储元素类型不可能反复的场景,比方存储用户的惟一ID操作多个对象时,必须应用排序算法的场景须要疾速查找和插入元素的场景通过Set容器,能够疾速获取惟一和有序的后果,同时在大数据量下性能也绝对较高,因而应用场景宽泛。 初始化 set在应用set之前,须要引入头文件#include <set>,当然你也能够用#include <bits/stdc++.h>(比赛选手举荐)。 用以下代码,申明一个空的set。 set<int> st;//申明一个存储类型为 int 的 set 容器插入元素insert(x)办法:将元素x插入到set对象中,如果set中曾经有了x元素,则不会插入(保障set中每个元素最多呈现一次)。 C++中的STL中提供了一种汇合容器——Set,static set它是一个领有非凡性能(无序、不容许反复)的容器。STL中Set如何插入元素呢? 1.应用insert函数插入元素:(1)一个元素的插入:set容器提供的insert函数能够将一个元素插入到容器中,代码如下: set<int> a;a.insert(10);此外,也能够插入pair类型的数据,如: set<pair<int, int> > b;b.insert(std::make_pair(2, 3));(2)多个元素的插入:vector<int> array{1, 2, 3};set<int> a;a.insert(array.begin(), array.end());2.应用emplace函数插入元素:emplace函数向set容器中增加一个新元素,但不复制或挪动现有元素,而是间接在容器存储区中结构元素。它的作用和insert函数雷同,但emplace函数的效率会更高。 set<int> a;//a : {empty}a.emplace(10);//a : {10}删除元素在应用set删除元素之前,须要了解对于set的删除元素形式,目前c++ STL提供了三种删除set元素的形式: clear() 、erase() 、 和 pop_back() 。 要删除set中的元素,通常是应用erase()函数: // 申明一个set容器std::set<int> st;// 增加元素到容器st.insert(9);st.insert(5);st.insert(1);// 如果要删除set中的某个值int value = 5;st.erase(value);//删除st中value = 5的数据项// 如果要删除set中的某个指定的地位的值std::set<int>::iterator it = /* Set Iterator */;st.erase(it);// 此外,还提供了clear()函数来清空set中的所有元素:st.clear();// 最初,也能够应用pop_back()函数来删除set中的一个元素:st.pop_back();查找元素C++STL的set提供了十分不便的查找操作,也即咱们常说的find操作。find操作的应用非常简单,能够应用Iterator类型的游标变量,对Set成员汇合进行定位,若要查找的元素存在,则会返回该元素的地位信息,若不存在,则返回set开端迭代器(未设定取值)。 ...

March 17, 2023 · 1 min · jiezi

关于c++:CSTL教程4map超强的容器它终于来了零基础都能理解的入门教程

之前咱们介绍过vector, queue, stack,他们都有一个独特的特点,就是都能够用线性表来模仿。明天咱们来学习一个全新且高封装性的容器:map。 作者:Eriktse 简介:19岁,211计算机在读,现役ACM银牌选手力争以通俗易懂的形式解说算法!❤️欢送关注我,一起交换C++/Python算法。(优质好文继续更新中……) 集体博客:www.eriktse.com什么是 mapstd::map是C++规范库中的一个容器,数据以<key, value>的模式存储,也就是咱们常说的“键值对”模式,且其“键值对”是有序的,也就是能够程序遍历的。 这意味着一个key只能对应一个value,而一个value可能对应了多个key,其关系有点像高中学过的函数的关系。 map的底层个别实现为红黑树,这个仅作理解即可。搜寻、移除和插入操作领有log级别复杂度。 初始化 map首先引入头文件: #include <map>用以下代码申明一个空的map: map<int, string> mp;//申明一个类型为<int, string>的map留神这里应用了string,也就须要引入头文件#include <string>。 插入数据map有一个函数是insert(),反对将数据插入。工夫复杂度O(logn),n为map中已有的数据个数。 mp.insert({0, "张三"});//插入一条数据当然还有另外一种方法来插入数据,就是间接赋值,像操作数组一样操作map,然而这个map的下标可不是间断的,能够是任意符合条件的key。 mp[2] = "李四";//当初map中的数据:{0: "张三", 2: "李四"}可能会有小伙伴纳闷,这里没有1的吗?在这里map的key只有int类型即可,就算是正数都能够! mp[-1] = "王五";//mp = {-1: "王五", 0: "张三", 2: "李四"};mp[-1] = "eriktse";//mp = {-1: "eriktse", 0: "张三", 2: "李四"};值得注意的是,value是可笼罩的,且这里的key是有序的,尽管我的-1这个key是前面退出的,然而却排在了第一个,如果程序遍历这个mp的话,{-1: "eriktse"}会是第一个被遍历到的。前面会讲到如何遍历map。 删除数据 & 清空maperase(key)办法:删除key所对应的数据。工夫复杂度O(logn)。 clear()办法:清空整个map。 mp.earse(-1);////mp = {0: "张三", 2: "李四"};获取map大小(元素个数)size()办法:返回map的大小,是一个非负整数。 查看容器是否无元素,即是否 begin() == end() 。 获取map中的数据间接像用数组一样获取就行了。 mp[key]示意map中这个key所对应的value。 cout << mp[0] << '\n';//输入: 张三遍历输入map遍历map须要用到std::iterator迭代器,没有接触过的同学可能不太理解,能够先看代码,或者用第二种办法。 ...

March 16, 2023 · 1 min · jiezi

关于c:MIT-618106828-操作系统工程-Lab1-Utilities

Lab1 Xv6 and Unix utilitiesBoot xv6 (easy)从指定仓库clone代码,而后编译-运行,尝试一部分命令,没问题之后就能够正式开始了! sleep (easy)试验要求实现Unix程序sleep,使程序暂定指定数量的ticks。并将解决方案放在文件user/sleep.c中。一些提醒: 参考其余代码如果获取用户输出参数,并对用户输出参数做出肯定判断(未指定工夫报错exit(1))能够应用零碎调用sleepmain函数最初退出应调用exit(0)将程序增加到Makefile中具体实现留神参数判断和工夫小于0的状况。主函数实现如下: int main(int argc, char* argv[]) { if (argc < 2) { printf("Usage: sleep [time]\n"); exit(1); } else { int time = atoi(argv[1]); if (time < 0) exit(1); sleep(time); exit(0); }}试验后果程序成果:测试后果:pingpong (easy)试验要求实现父子过程之间传输数据并打印过程ID和后果。具体为:父过程发一字节,子过程收到打印"<pid>: receive ping",再把数据发回父过程,退出;父过程收到数据打印"<pid>: receive pong"并退出。文件保留在user/pingpong.c中。一些提醒: 创立管道pipe创立子线程forkread从管道读取,write写入管道getpid获取过程ID具体实现注意事项: 管道读写的方向(0读1写)留神敞开文件描述符父过程wait期待子过程完结,这样可保障输入程序具体实现: int main() { int fd1[2], fd2[2], pid; char buf; pipe(fd1); pipe(fd2); pid = fork(); if (pid < 0) { fprintf(2, "fork() error\n"); exit(1); } else if (pid > 0) { // father close(fd1[0]); close(fd2[1]); write(fd1[1], "a", 1); read(fd2[0], &buf, 1); close(fd1[1]); close(fd2[0]); wait(0); printf("%d: received pong\n", getpid()); } else { // child close(fd2[0]); close(fd1[1]); read(fd1[0], &buf, 1); write(fd2[1], &buf, 1); close(fd2[1]); close(fd1[0]); printf("%d: received ping\n", getpid()); } exit(0);}试验后果失败后果其实最开始我没有加wait期待子过程完结,这样的后果就会造成最根本的并发编程谬误,导致父子过程输入的后果可能是交错在一起的,比方这样:当然也能够通过sleep或其余形式解决失常后果测试后果primes (moderate)/(hard)试验要求实现一个并发的素数筛选程序。具体思路参考:Hins: ...

March 16, 2023 · 4 min · jiezi

关于c++:BoostAsyncSocket-异步反弹通信案例

Boost 利用ASIO框架实现一个跨平台的反向远控程序,该远控反对保留套接字,当有套接字连入时,主动存储到map容器,当客户下线时主动从map容器中移除,当咱们须要与特定客户端通信时,只须要指定客户端ID号即可。 AsyncTcpServer服务端首先定义CEventHandler类并继承自CAsyncTcpServer::IEventHandler接口,该类内须要咱们实现三个办法,办法ClientConnected用于在客户端连贯时触发,办法ClientDisconnect则是在登录客户端来到时触发,而当客户端有数据发送过去时则ReceiveData办法则会被触发。 办法ClientConnected当被触发时主动将clientId客户端Socket套接字放入到tcp_client_id全局容器内存储起来,而当ClientDisconnect客户端退出时,则间接遍历这个迭代容器,找到序列号并通过tcp_client_id.erase将其剔除; // 客户端连贯时触发virtual void ClientConnected(int clientId){ // 将登录客户端退出到容器中 tcp_client_id.push_back(clientId);} // 客户端退出时触发virtual void ClientDisconnect(int clientId){ // 将登出的客户端从容器中移除 vector<int>::iterator item = find(tcp_client_id.begin(), tcp_client_id.end(), clientId); if (item != tcp_client_id.cend()) tcp_client_id.erase(item);}而ReceiveData一旦收到数据,则间接将其打印输出到屏幕,即可实现客户端参数接管的目标; // 客户端获取数据virtual void ReceiveData(int clientId, const BYTE* data, size_t length){ std::cout << std::endl; PrintLine(80); std::cout << data << std::endl; PrintLine(80); std::cout << "[Shell] # ";}绝对于接收数据而言,发送数据则是通过同步的形式进行,当咱们须要发送数据时,只须要将数据字符串放入到一个BYTE*字节数组中,并在调用tcpServer.Send时将所需参数,套接字ID,缓冲区Buf数据,以及长度传递即可实现将数据发送给指定的客户端; // 同步发送数据到指定的线程中void send_message(CAsyncTcpServer& tcpServer, int clientId, std::string message, int message_size){ // 获取长度 BYTE* buf = new BYTE(message_size + 1); memset(buf, 0, message_size + 1); for (int i = 0; i < message_size; i++) { buf[i] = message.at(i); } tcpServer.Send(clientId, buf, message_size);}AsyncTcpClient客户端首先咱们封装实现AsyncConnect类,该类内次要实现两个性能,其中aysnc_connect办法用于实现异步连贯到服务端,而port_is_open办法则用于验证服务器特定端口是否凋谢,在调用boost::bind绑定套接字时传入&AsyncConnect::timer_handle设置一个超时等待时间。 ...

March 16, 2023 · 5 min · jiezi

关于c++:CSTL教程3stack栈入门简明教程小白都能理解

在学习之前,先理解一下什么是stack。 std::stack 类是容器适配器,它给予程序员栈的性能——特地是 FILO (先进后出)数据结构。该类模板体现为底层容器的包装器——只提供特定函数汇合。栈从被称作栈顶的容器尾部推弹元素。FILO指的是First In Last Out,也就是说第一个进来的,是最初一个进来的。咱们能够将stack了解为一个上端闭口的铁箱子,咱们能够从顶部拿出物品或放入物品,且记录物品个数。 stack仅保护一个栈顶,意味着咱们只能从一端对数据进行操作。 本文仅从入门和实用角度介绍queue的用法,次要针对初学者或比赛向。如有不谨严的中央欢送斧正!初始化在应用stack之前,须要先引入头文件。 #include <stack>初始化的语法如下: stack<T> stk;// T 为数据类型stack<int> stk_int;//申明一个栈,寄存类型为int和其余的stl容器一样,stack只能寄存雷同类型的元素,默认初始化为空栈。 入栈stk.push(x)将元素x推入栈stk的栈顶,复杂度O(1)。 每入栈一个新元素,会使得栈的大小+1。 // 右边为栈顶// stk: emptystk.push(1);// stk : 1stk.push(5);// stk : 5 1还有一种办法是用emplace()函数进行入栈,两者差异能够临时疏忽。 stk.emplace(7);// stk : 7 5 1出栈stk.pop()将stk的栈顶元素弹出栈,复杂度O(1)。 // stk : 7 5 1stk.pop();// stk : 5 1stk.pop();// stk : 1在出栈时要留神栈是否为空,空栈不容许出栈。这和queue出队相似,感兴趣的能够看看这篇文章:https://www.eriktse.com/technology/1063.html 你问一个人“push”的反义词是什么?如果他答复“pop”那他肯定是程序员。(答案兴许是“pull”)获取栈顶元素语法:stk.top()能够获取栈顶元素,然而不会主动将栈顶元素弹出。须要自行pop()。复杂度O(1)。 // stk : 7 5 1cout << stk.top() << '\n';// 7留神获取栈顶元素的时候也须要保障栈不为空,否则将导致谬误!获取栈的大小语法:stk.size(),返回值为一个非负整数,当返回0时示意栈为空,复杂度O(1)。 // stk : 1cout << stk.size() << '\n';// 1stk.push(9);// stk : 9 1cout << stk.size() << '\n';// 2本文由eriktse原创,未经容许禁止转载!判断栈是否为空语法:stk.empty(),返回值为一个bool值,当返回true时示意栈为空,当返回值为false时示意栈非空。复杂度O(1)。 ...

March 16, 2023 · 1 min · jiezi

关于c++:CSTL教程2queue队列容器小白都能看懂的讲解

在学习数据结构的时候咱们会听到这样一个词:队列。 本文将介绍STL中的队列:queue 本文仅从入门和实用角度介绍queue的用法,次要针对初学者或比赛向。如有不谨严的中央欢送斧正!本文长度约2000字,浏览大概需5分钟。什么是队列?队列是一种FIFO,即First In First Out的数据结构,就像是小朋友排队一样,所有元素都只能从队尾(rear / back)进,队头(front)出,队列内的元素放弃着入队时的程序。 这时候有小伙伴可能会问:队列能做的,数组都能模仿,为什么还要队列呢? 咱们要晓得的是,咱们学习队列并非学习队列这个构造自身, 而是学习其中蕴含的思维,用于解决简单的问题。 知乎答复:https://www.zhihu.com/question/457210423 队列和其余C++的规范库容器一样,都只能寄存雷同的数据类型。如果要尝试寄存任意类型,能够去理解一下\<any\>这个库。队列的初始化在应用queue前,须要引入头文件。 #include <queue>用以下的代码初始化一个空队列: queue<T> q; // 其中T为数据类型接下来的操作都针对q这个实例化对象。 插入元素语法:q.push(x), x为q中寄存的类型的变量。 q.push(1);q.push(4);q.push(5);//q: 1(front) 4 5 (rear)咱们认为队头的元素为front,而队尾元素的前面一个地位是rear,合乎计算机中常见的“左闭右开”的规范。 还有一种方法,就是用q.emplace()函数进行入队,它和push用法雷同单有稍微差别然而初学者能够疏忽。 此函数用于将新元素插入队列容器,并将新元素增加到队列的开端。 q.emplace(1);q.emplace(2);q.emplace(3);//这个q接着上一个例子//q: 1 4 5 1 2 3弹出元素语法:q.pop(),将队头元素弹出。 //q: 1(front) 4 5 (rear)q.pop();//q: 4(front) 5 (rear)q.pop();//q: 5(front) (rear)q.pop();//q: (empty)q.pop();//谬误!值得注意的是,当队列为空的时候弹出元素会导致谬误,所以咱们在每一步pop()操作的时候肯定要判断队列是否为空,除非你有十足的把握。 本文为eriktse原创,未经容许禁止转载。获取队列大小语法:size(),返回值为一个非负整数。 cout << q.size() << '\n';当q.size() == 0时,队列为空。 判断队列是否为空语法:empty(),返回值为bool。 用法和vector相似,感兴趣的能够看这篇文章:[[C++STL教程]1.vector容器是什么?实用教程来啦!](https://www.eriktse.com/algorithm/1051.html) if(q.empty())cout << "队列为空" << '\n';else cout << "队列非空" << '\n';当然还能够通过判断队列的大小来判断是否为空。 //当q.size() > 0时,其bool值为true,队列非空if(q.size())cout << "队列非空" << '\n';else cout << "队列为空" << '\n';清空队列有时候咱们须要将队列清空,然而queue并没有像vector那样的clear函数,怎么做呢? ...

March 15, 2023 · 1 min · jiezi

关于c++:SkeyeIVMS-SkeyeVSS视频安防综合管理系统打造工业联网数字化智慧工厂

 随着古代工厂信息化的倒退,工厂治理也进入智能化阶段,越来越多的工厂开始利用物联网技术和设施监控技术增强信息管理和服务。智慧工厂视频安防零碎通过装置在工厂生产施工作业现场的各个监控安装,构建智能监控剖析预警和防备体系,及时、高效地反映重要地点区域的现场状况,实现对人员、机械、资料、环境的全方位实时监控,变被动“监督”为被动“监控”;真正做到事先预警,事中常态检测,预先标准治理,无效补救了传统办法和技术在治理中的缺点,将工厂平安生产做到信息化治理。 视开科技针对“智慧工厂”这一制造业新概念,提出了“SkeyeIVMS+ SkeyeVSS”视频安防综合管理系统解决方案,通过利用5G、AI、互联网+等技术手段,在保障安防管理系统稳固、牢靠的根底上,实现工厂重点区域全天候实时视频监控,满足视频文件海量存储、回放需要,并且具备报警联动、操作标准辨认、火灾辨认等平安生产保障性能。 1、实时监管:通过接入在企业厂区门口、厂房、办公楼、周界围墙、仓库等工厂重点区域部署的监控设施,SkeyeVSS视频安防综合治理平台可实现目标区域实时全天候视频监控,打造智慧工厂近程监管模式,可疾速解决、排查前端设施问题,升高运维老本。 2、海量视频文件存储:SkeyeVSS视频安防综合治理平台的对象存储(OSS)可能提供海量的文件存储,同时具备10个9的数据可靠性,并提供同城热备和异地灾备计划,使海量监控视频永恒存储,随时可得。工厂监管人员可随时对工厂各个区域、车间等现场情况及生产过程中遇到的突发安全事故进行起因剖析和视频调阅、问责、取证,实现对生产过程的治理及监督。3、反对子系统交融接入:SkeyeVSS视频安防综合治理平台零碎低耦合,采纳散布式微服务架构,可灵便配置安防零碎中的各个子系统服务,如:(1)报警联动零碎:SkeyeVSS视频安防综合治理平台配置多样化的报警触发规定,如挪动侦测报警、遮挡报警等。当零碎侦测到监控异样时,可实现报警信号联动,上传报警信息,自动弹出报警地位的视频图像,帮助工厂管理人员迅速定位并记录报警信息。(2)火灾预防零碎:SkeyeVSS视频安防综合治理平台可接入火灾预防零碎,依据人工智能技术图像识别算法全自动捕捉浓烟或火苗等违章行为,智能化取得监控视频周边的出现异常点,并产生警示,通告无关人员避免劫难,正当晋升工厂查验高效率,革除安全隐患。(3)操作标准识别系统:零碎可接入各类识别系统,如:工厂作业人员安全帽辨认、口罩辨认、工装穿戴辨认、安全带穿戴辨认等,零碎获取未满足操作标准的行为数据后,信息推送记录至管理人员,从而达到工厂操作标准监管、记录的目标。 3、安全可靠:SkeyeIVMS分布式集群视频云管控零碎采纳Spring Security架构,集流媒体服务集群、视频资源管理、软硬件服务节点保障、容灾双机热备、主备主动切换为一体,保障智慧工厂视频安防零碎的稳固运行,在呈现故障或中断时,能确保数据的准确性和完整性,并能迅速复原运行。 

March 15, 2023 · 1 min · jiezi

关于c++:CSTL教程1vector容器是什么实用教程来啦超简单易懂拿来就用

C++与传统的C语言有一个很大的区别,就是新增了规范模板库 STL(Standard Template Library),它是 C++ 规范库的一部分,不须要独自装置,只须要 #include 对应的头文件即可。 本文将介绍STL中最根底的一个容器:vector 留神:本文仅从入门和实用角度介绍vector的用法。如有不谨严的中央欢送斧正! 引入头文件在应用vector之前须要用#include <vector>来引入头文件。 如果你是比赛选手,也能够用万能头#include <bits/stdc++.h>其中蕴含了<vector>。 vector简介vector能够了解为动静数组,它的大小会随着元素的减少而主动增大。下标从0开始,大小为n的vector的可用范畴是[0, n - 1]。 vector中不仅能够寄存int, char等根底数据类型,还能够寄存构造体、类等等。 然而一个vector中只能寄存一种类型。 初始化 当初咱们能够入手来生成一个vector试试: vector<int> v(3, 5);//生成一个vector,其中有3个值为5的元素二维数组前面会讲到。 遍历数组既然是数组必定少不了遍历嘛对吧~思路是,先用v.size()获取vector的大小,而后用for循环遍历。 //v.size()返回值为unsigned intfor(int i = 0;i < v.size(); ++ i){ printf("%d", v[i]);}//后果为: 5 5 5在C++11之后,能够应用auto关键字进行遍历,这个在前面会讲到。 插入元素void push_back(const T& x)vector有一个叫push_back()的办法,能够在数组尾部插入一个元素且令数组大小+1。举个例子: printf("%d\n", (int)v.size());//v.size() = 3for(int i = 1;i <= 3; ++ i)v.push_back(i);//当初 v 是: 5 5 5 1 2 3//v.size() = 6个别只向尾部插入元素,因为向其余地位插入元素的复杂度较高。 删除元素void pop_back()删除最初一个元素。void clear()清空vector,大小变为0void erase(iter l, iter r)清空迭代器指向的区间$[l, r)$的元素。 ...

March 14, 2023 · 2 min · jiezi

关于c++:通过摄像机视频设备或者流媒体服务器SDK获取到数据转换成RTMP流实现网页手机微信播放

写这篇博客次要是为了给新入门的流媒体开发者解惑,当初看到各种开发者的需要:网页播放RTSP摄像机、微信播放RTSP摄像机、网页播放摄像机SDK输入的视频流、网页播放第三方流媒体平台输入的视频流、包含Github有一些所谓的H5RTSPPlayer,这些都有一个共同点,就是H.264+AAC进行RTMP推流,当然SkeyeWebPlayer除外,SkeyeWebPlayer开创性的反对在浏览器端采纳H5间接播放器RTSP流,下载地址:SkeyeWebPlayer: 永恒收费H5直播点播播放器,反对FLV、HLS、RTSP、WS-FLV、WS-RTSP、WEBRTC、HEVC/H265https://gitee.com/visual-opening/skeyewebplayer 将整个过程进行拆分为二: 一、通过RTSP或者SDK获取H.264视频流和AAC音频流,获取RTSP流咱们能够用ffmpeg、SkeyeRTSPClient(https://gitee.com/visual-opening/SkeyeRTSPClient)等计划,获取SDK流咱们就能够用各个摄像机厂家或者平台厂家提供的各种平台SDK(海康NetSDK、大华NetSDK、雄迈LocalSDK)等等,这些都能无一例外地有独特的流程: 初始化登录设置数据回调开始实时流解决实时返回的音视频数据开释Handle视频数据大多返回的是H.264数据流,音频就各种各样了,有返回G.711、G.726、AAC的,如果是返回的G.711或者G.726时,就须要调用AAC的编码器(如ffmpeg等)将安防的音频流转换成AAC的音频编码格局; 二、将编码好的H.264视频和AAC音频以RTMP/FLV的形式推流到通用的RTMP服务器(nginx-rtmp、srs、SkeyeDSS、CRTMPD等),进行RTMP/HLS的直播,这里就次要是RTMP推流性能的实现,咱们能够采纳ffmpeg、librtmp、SkeyeRTMPPusher等多种计划推流,如果采纳ffmpeg为工具进行RTSP拉流的话,咱们倡议推流也用ffmpeg,这样间接将音频数据在ffmpeg外部进行一次filter转码,就能够实现rtmp的output了,如果采纳librtmp的话,也是比拟好的计划,毕竟市面上90%的RTMP推流都是Based On librtmp,包含SkeyeRTMPPusher,之所以举荐用SkeyeRTMPPusher一方面是因为是SkeyOpenSKEYE团队其余搭档的产品,另一方面,的确好用,有十分齐备的多平台调用示例(Gitee - OpenSKEYE/SkeyeRTMPPusher: SkeyeRTMPPusher是一套调用简略、功能完善、运行高效稳固的RTMP性能组件,通过多年实战和线上运行打造,反对RTMP推送断线重连、环形缓冲、智能丢帧、网络事件回调,反对Windows、Linux、arm(hisiv100/hisiv200/hisiv300/hisiv400/hisiv500/hisiv600/etc..)、Android、iOS平台,反对市面上绝大部分的RTMP流媒体服务器,包含Wowza、Red5、ngnix_rtmp、crtmpserver等支流RTMP服务器,可能完满利用于各种行业的直播需要,手机直播、桌面直播、摄像机直播、课堂直播等等方面! ): SkeyeRTMP_File:读取MP4文件推RTMP直播流,实现虚构直播;SkeyeRTMP_RTSP:读取RTSP流并进行转换成RTMP推流到RTMP服务器;SkeyeRTMP_SDK:调用SDK获取音视频流,再推流RTMP;SkeyeRTMP Android:安卓推送摄像头/屏幕进行RTMP直播;SkeyeRTMP iOS:iOS苹果推送摄像头进行RTMP直播;齐备的调用示例,加上全平台的反对(Windows、Linux、arm、Android、iOS),给了开发者十分好的抉择和帮忙; 依照以上的两个流程,咱们就能够将RTSP/SDK获取到的数据进行RTMP/HLS公布,实现全终端、无插件化的播放了,这也是所谓的H5 RTSP播放器的实现计划,RTSP要H5播放,必须借助于服务器的转换;

March 14, 2023 · 1 min · jiezi

关于c++:SkeyeRTMPPusher推送RTMP扩展支持HEVCH265

不久前刚实现SkeyeRTMPPusher扩大反对h265推送,过后在网上也查找了很多材料,发现都不尽具体,而官网也没有更新对HEVC(H265,后文统称HEVC)tag的反对,反正是走了不少弯路,当然,在宽广网友以及ffmpeg代码的帮忙下我最终实现了通过SkeyeRTMPPusher推送HEVC视频帧数据到SkeyeSMS,这里我将把实现过程具体的记录下来,供宽广网友参考。 首先, RTMP头部信息封装并没有定义HEVC,咱们采纳CDN联盟的HEVC扩大规范,将HEVC的VideoTagHeader定义为12,详见下图:![在这里插入图片形容](https://img-blog.csdnimg.cn/img_convert/3319b358ebe3b50efd59cb16cb18a292.png)而后,咱们在H264封装的根底上进行改良,以反对HEVC头部的封装,而HEVC头有SPS PPS VPS,咱们参考ffmpeg的HEVCDecoderConfigurationRecord构造对metadata进行封装,该构造体代码如下: typedef struct HVCCNALUnitArray { uint8_t array_completeness; uint8_t NAL_unit_type; uint16_t numNalus; uint16_t *nalUnitLength; uint8_t **nalUnit;} HVCCNALUnitArray;typedef struct HEVCDecoderConfigurationRecord { uint8_t configurationVersion; uint8_t general_profile_space; uint8_t general_tier_flag; uint8_t general_profile_idc; uint32_t general_profile_compatibility_flags; uint64_t general_constraint_indicator_flags; uint8_t general_level_idc; uint16_t min_spatial_segmentation_idc; uint8_t parallelismType; uint8_t chromaFormat; uint8_t bitDepthLumaMinus8; uint8_t bitDepthChromaMinus8; uint16_t avgFrameRate; uint8_t constantFrameRate; uint8_t numTemporalLayers; uint8_t temporalIdNested; uint8_t lengthSizeMinusOne; uint8_t numOfArrays; HVCCNALUnitArray *array;} HEVCDecoderConfigurationRecord;参考ffmeg对该构造进行初始化如下: static void hvcc_init(HEVCDecoderConfigurationRecord *hvcc){ memset(hvcc, 0, sizeof(HEVCDecoderConfigurationRecord)); hvcc->configurationVersion = 1; hvcc->lengthSizeMinusOne = 3; // 4 bytes /* * The following fields have all their valid bits set by default, * the ProfileTierLevel parsing code will unset them when needed. */ hvcc->general_profile_compatibility_flags = 0xffffffff; hvcc->general_constraint_indicator_flags = 0xffffffffffff; /* * Initialize this field with an invalid value which can be used to detect * whether we didn't see any VUI (in which case it should be reset to zero). */ hvcc->min_spatial_segmentation_idc = MAX_SPATIAL_SEGMENTATION + 1;}须要留神的是,该构造其余参数咱们其实能够不特地关怀,咱们只须要在HVCCNALUnitArray数组中把HEVC的VPS,SPS和PPS信息填入即可。 ...

March 14, 2023 · 2 min · jiezi

关于c++:SkeyeRTMPLive流媒体直播软件应用解决方案

上一篇咱们讲到了RTMPLive多流媒体协定转无插件直播协定(RTMP)解决方案,当初咱们能够通过RTMPLive将网络摄像机IPC的流,以及网络电视流,或者点播服务器的回放流,甚至本地文件转发成RTMP流,进行互联网直播以及网页无插件观看,本篇将重点讲述RTMPLive软件的理论场景利用。 首先,从Giuhub下载RTMPLive v2.0 64位版本,而后关上软件主界面如下图所示: 如上图所示,软件默认曾经增加几个测试的数据源,当初咱们手动来增加一个网络摄像机,右键点击"数据源列表"上司的“本地数据源”,在弹出菜单中选择“增加数据源”,弹出窗口如下图所示:如上图所示,别离填入数据源名称为“海康IPC”以及数据源URL流地址“rtsp://admin:Ds123456@192.168.0.102:554/h264/ch1/main/av_stream”,点击“确定”保留。 实现后咱们就在“本地数据源”目录下看到咱们新增加的数据源“海康IPC”,点击该数据源,则切换到该数据源通道,因为咱们没有为该通道增加任何的转发任何,所以咱们看到的数据源列表为空; 同时,右键点击该数据源项,弹出菜单咱们可对该项数据源进行编辑和删除。 当初咱们为该数据源通道配置一个转发工作,右键点击工作转发列表框,在弹出菜单中选择“增加工作”或者点击按钮“增加工作”,弹出增加工作对话框如下图所示:输出转发RTMP流地址“rtmp://demo.easydss.com:10085/hls/hikIPC”如上图所示,点击“确定”保留。实现后咱们在转发流列表内就能看到一个转发工作。 抉择刚增加的转发工作项,点击“开始推流”按钮,或者右键点击该转发项,在弹出菜单中当初“开始推流”,同时,咱们也能够在弹出菜单中选择项对该项数据源进行编辑和删除;当转发工作项状态显示为"推流中"的时候,阐明流曾经在转发了,关上SkeyeVSS的后盾,查看推流列表,则能看到咱们刚推送上来的流"hikIPC",如下图所示: 点击播放按钮,即可播放从本地网络摄像机转发到SkeyeVSS的RTMP流,如下图所示:源码地址:https://gitee.com/visual-opening/RTMPLive

March 14, 2023 · 1 min · jiezi

关于c++:思维提升干货All-in6种算法解决LeetCode困难题滑动窗口最大值

为了更好的浏览体验,欢送浏览原文:[[思维晋升|干货All in]6种算法解决LeetCode艰难题:滑动窗口最大值 (eriktse.com)](https://www.eriktse.com/algorithm/1039.html) 最近在leetcode遇到一道十分经典的题目:239. 滑动窗口最大值 - 力扣(LeetCode) 以前只会看题解用枯燥队列做,最近钻研一下发现是一道很好的题,能够帮忙咱们晋升“保护区间最值”的算法思维。 先介绍一下我解决这题所用的算法及其复杂度: 枯燥队列 O(n)st表 O(nlogn)树状数组 O(n(logn)^2)多重集非法 O(nlogn)莫队O (n sqrt{n})优先队列 O(nlogn)首先确定一点,枯燥队列是解决这道题最好的方法,然而其余的办法的适用范围更广。 1、枯燥队列首先能够参考几篇优良的文章: 算法学习笔记(66): 枯燥队列 - 知乎 (zhihu.com) 枯燥队列 - OI Wiki (oi-wiki.org) 我这里提几点值得注意的中央: 1.枯燥队列中寄存的是下标,而不是元素值 2.枯燥队列是一个双端队列,尾插前先查队头后查队尾 3.枯燥队列保护的是元素值的枯燥性 有了这几点留神,代码就很好写了: class Solution {public: static const int maxn = 1e5 + 9; int a[maxn]; deque<int> dq; vector<int> maxSlidingWindow(vector<int>& nums, int k) { vector<int> res; int n = nums.size(); for(int i = 1;i <= n; ++ i)a[i] = nums[i - 1]; for(int i = 1;i <= n; ++ i) { int x = a[i]; while(!dq.empty() and dq.front() < i - k + 1)dq.pop_front(); while(!dq.empty() and x >= a[dq.back()])dq.pop_back(); dq.push_back(i); if(i >= k)res.push_back(a[dq.front()]); } return res; }};我做题习惯把输出数组存一个array,大家请勿介意。 ...

March 14, 2023 · 4 min · jiezi

关于c++:SkeyeRTMPLive多流媒体协议转无插件直播协议RTMP解决方案

随着互联网以及科技程度的倒退,对于互联网服务,PC不再是惟一抉择,智能手机、平板电脑、特定的挪动终端等都是可抉择的用户终端硬件形式,因而,咱们须要一款能将安防协定,电视广播协定以及其余各种格局的流媒体协定接入到互联网上来,通过一种对立格局的协定进行多平台多终端直播。RTMPLive流媒体直播软件(以下简称“RTMPLive”)将RTSP/RTP/RTMP/HTTP/UDP等协定的音视频数据进行拉取、转换,再转换成RTMP的形式推送到RTMP服务器/CDN等。以实现网页端无插件直播以及多平台多终端观看。 RTMPLive反对多种流格局,以及H264,H265多种编码格局推送RTMP;其中蕴含SkeyeStreamClient拉流、SkeyeRTMP推流: SkeyeStreamClient拉流模块智能解析多种流媒体协定格局,反对重连并可能超低延时拉取多种格局流地址并解析出视频帧和音频帧数据;SkeyeRTMPPusher推流模块集成了包含:RTMP协定封装、断线重连、异步推送、环形缓冲区、推送网络拥塞主动丢帧、缓冲区关键帧检索、事件回调(断线、音视频数据回调),反对推流到市面上绝大部分的RTMP流媒体服务器;RTMPLive流媒体直播框架如下:RTMPLive反对多通道拉流集中式治理,以及各通道流可配置化进行分布式转发;其弱小的性能同时反对上千路的拉流转发,各路流转发状态高深莫测,软件界面如下图所示:

March 14, 2023 · 1 min · jiezi

关于c++:SkeyeRTMPClient拉取RTMP流扩展支持HEVCH265解决方案

不久前咱们曾经在RTMP推送端扩大反对了HEVC(H.265 后文统称H265)编码格局,然而,因为RTMP官网指定的协定格局曾经不再更新,官网的播放器的Flash播放器并不反对H265格局的编码数据进行解码播放;当初,咱们须要在播放器端解析RTMP流时对H265编码格局进行扩大反对。 首先,咱们能够通过扩大ffmpeg,让其反对拉H265封装的RTMP流进行解码播放,咱们能够通过金山云对FFmepg的扩大反对H265来解决。 而后,咱们通过批改SkeyeRTMPClient的代码实现对H265 的反对,因为咱们曾经实现了推送端的扩大反对,所以,播放端反对则绝对比较简单;不过仍然有几个方面咱们须要留神;上面将对整个扩大流程进行介绍。 1. 扩大编码ID和推送端一样,须要先扩大反对H265的编码ID,咱们定义为12,如下代码所示: enum FlvVideoCodecId{ e_FlvVideoCodecId_None = 0, e_FlvVideoCodecId_Jpeg = 1, e_FlvVideoCodecId_H263 = 2, e_FlvVideoCodecId_Screen = 3, e_FlvVideoCodecId_Vp6 = 4, e_FlvVideoCodecId_Vp6Alpha = 5, e_FlvVideoCodecId_ScreenV2 = 6, e_FlvVideoCodecId_Avc = 7, // RTMP扩大反对HEVC(H.265) e_FlvVideoCodecId_Hevc = 12, };enum FlvCodeId{ FlvCodeId_Jpeg = 1, FlvCodeId_Sorenson = 2, FlvCodeId_ScreenVideo = 3, FlvCodeId_On2Vp6 = 4, FlvCodeId_On2Vp6Alpha = 5, FlvCodeId_ScreenVideoV2 = 6, FlvCodeId_AVC = 7, // RTMP扩大反对HEVC(H.265) FlvCodeId_Hevc = 12,};2. 扩大H265特有头部数据结构VPS因为H264只有SPS和PPS,所以在兼容H265的时候,咱们须要定义其特有的头VPS: char vps_buf_[MAX_VPS_LEN];//256 int vps_len_; 3. MetaData解析扩大反对H265在推送端,咱们扩大H265发送的MetaData构造定义如下: ...

March 14, 2023 · 2 min · jiezi

关于c++:SkeyeRTMPClient扩展支持HEVCH265解决方案之兼容H264和H265帧数据解析详解-1

在之前两篇对于SkeyeRTMPClient扩大反对HEVC(H.265)解决方案的文章中,咱们曾经实现了对H265的反对,本文次要论述将H26和H265反对兼容起来,实现不同视频编码格局的自适应兼容适配。 1. 依据CodecId判断数据编码类型依据视频编码ID判断视频编码类型,如果视频编码ID==FlvCodeId_Hevc(12),则判断视频编码格局为H265,反之则为H264(因为目前咱们只反对这两种编码格局的视频推送),如下代码所示: parser_VideoTag *video_tag = (parser_VideoTag*)(buf+parser_offset); FlvCodeId video_code_id = (FlvCodeId)(video_tag->code_id&0x0f); if (video_code_id == FlvCodeId_Hevc) { av_frame.u32AVFrameFlag = SKEYE_SDK_VIDEO_CODEC_H265;// HEVC; } else { av_frame.u32AVFrameFlag = SKEYE_SDK_VIDEO_CODEC_H264;// 默认h264, 其余类型是否须要判断?!; }2. 数据帧头部判断依据FLV/RTMP扩大反对H265规范,反对HEVC的VideoTagHeader定义如下图所示:即 当CodecID == 12时,AVCPacketType为HEVCPacketType: 如果HEVCPacketType为0,示意HEVCVIDEOPACKET中寄存的是HEVC sequence header;如果HEVCPacketType为1,示意HEVCVIDEOPACKET中寄存的是HEVC NALU;如果HEVCPacketType为2,示意HEVCVIDEPACKET中寄存的是HEVC end of sequence,即HEVCDecoderConfigurationRecord;而当CodecID == 7时,AVCPacketType为AVCPacketType: 如果AVCPacketType为0,示意HEVCVIDEOPACKET中寄存的是AVC sequence header;如果AVCPacketType为1,示意HEVCVIDEOPACKET中寄存的是AVC NALU;如果AVCPacketType为2,示意HEVCVIDEPACKET中寄存的是AVC end of sequence,即AVCDecoderConfigurationRecord;SkeyeRTMPClient对sequence header的解析函数如下代码段所示: int ParserVideoSequencePacket(FlvCodeId video_code_id, char *buf,int len){ int parser_offset = 0; char *parser_config = buf; if (video_code_id == FlvCodeId_Hevc) { if(len <= sizeof(Parser_HEVCDecoderConfigurationRecord)) { return -1001; } ......’ //Parser HEVCDecoderConfigurationRecord ...... rtmpclient_h265_decode_sps((unsigned char *)sps_buf_, sps_len_, width_, height_); } else { if(len <= sizeof(parser_AVCDecoderHeader)) { return -1001; } ......’ //Parser HEVCDecoderHeader ...... rtmpclient_h264_decode_sps((unsigned char *)sps_buf_, sps_len_, width_, height_); } return 0;}3. 视频数据体帧数据nalu类型判断依据FLV/RTMP扩大反对协定规范,反对H265的VideoTagBody定义如下, 扩大后的VideoTagBody如下图所示(红色字体为HEVC新增内容):: ...

March 14, 2023 · 2 min · jiezi

关于c++:SkeyeRTMPClient扩展支持HEVC解决方案之HEVCDecoderConfigurationRecord结构详解

在上一篇 SkeyeRTMPClient拉取RTMP流扩大反对HEVC(H.265)解决方案 中对于HEVCDecoderConfigurationRecord构造解析的解说存在一些表述上不分明的中央,本文为之续篇,重点对HEVC格局的MetaData构造的解析进行解说。在SkeyeRTMPPusher扩大反对H265的解决方案讲述时。咱们对Metadata构造进行过详解,大家能够回顾一下这篇文章RTMP推送扩大反对HEVC(H265)之Metadata构造填写详解,重点来了,因为失常状况下,咱们只须要从MetaData中取出对咱们解码有用的数据头(即VPS,SPS和PPS),所以咱们对HEVCDecoderConfigurationRecord填充的MetaData其余数据并不关怀,然而,在解析时,咱们须要对该构造所有数据都解析进去,以保障能准确无误的获取到咱们所须要的数据头信息。 再次回顾HEVCDecoderConfigurationRecord构造: typedef struct HEVCDecoderConfigurationRecord { uint8_t configurationVersion; uint8_t general_profile_space; uint8_t general_tier_flag; uint8_t general_profile_idc; uint32_t general_profile_compatibility_flags; uint64_t general_constraint_indicator_flags; uint8_t general_level_idc; uint16_t min_spatial_segmentation_idc; uint8_t parallelismType; uint8_t chromaFormat; uint8_t bitDepthLumaMinus8; uint8_t bitDepthChromaMinus8; uint16_t avgFrameRate; uint8_t constantFrameRate; uint8_t numTemporalLayers; uint8_t temporalIdNested; uint8_t lengthSizeMinusOne; uint8_t numOfArrays; HVCCNALUnitArray *array;} HEVCDecoderConfigurationRecord;而事实上,该构造如果间接填入到MetaData中是不正确的,咱们看ffmpeg中hevc.c文件中的实现,该构造具体申明如下: // The CodecPrivate syntax shall follow the// syntax of HEVCDecoderConfigurationRecord// defined in ISO/IEC 14496-15.//// The number zero (0) shall be written to// the configurationVersion variable until// official finalization of 14496-15, 3rd ed.//// After its finalization, this field and the// following CodecPrivate structure shall// follow the definition of the// HEVCDecoderConfigurationRecord in 14496-15.unsigned int(8) configurationVersion;unsigned int(2) general_profile_space;unsigned int(1) general_tier_flag;unsigned int(5) general_profile_idc;unsigned int(32) general_profile_compatibility_flags;unsigned int(48) general_constraint_indicator_flags;unsigned int(8) general_level_idc;bit(4) reserved = ‘1111’b;unsigned int(12) min_spatial_segmentation_idc;bit(6) reserved = ‘111111’b;unsigned int(2) parallelismType;bit(6) reserved = ‘111111’b;unsigned int(2) chromaFormat;bit(5) reserved = ‘11111’b;unsigned int(3) bitDepthLumaMinus8;bit(5) reserved = ‘11111’b;unsigned int(3) bitDepthChromaMinus8;bit(16) avgFrameRate;bit(2) constantFrameRate;bit(3) numTemporalLayers;bit(1) temporalIdNested;unsigned int(2) lengthSizeMinusOne;unsigned int(8) numOfArrays;for (j=0; j < numOfArrays; j++) { bit(1) array_completeness; unsigned int(1) reserved = 0; unsigned int(6) NAL_unit_type; unsigned int(16) numNalus; for (i=0; i< numNalus; i++) { unsigned int(16) nalUnitLength; bit(8*nalUnitLength) nalUnit; }}从上代码段咱们能够看出,以general_constraint_indicator_flags这个参数为例,构造体申明位宽64,而理论位宽是48,,所以构造体申明的参数位宽和理论位宽可能是不对等的,这就将导致解析MetaData时产生错位,从而解析产生谬误,从而,咱们从新意识HEVCDecoderConfigurationRecord,并申明其构造如下: ...

March 14, 2023 · 2 min · jiezi

关于c++:LyMemory-内核级内存读写驱动

一款完全免费的内核级内存读写工具,可冲破驱动爱护,强制读写应用层任意过程内存数据,驱动工具目前反对读写整数,字节,字节集,单精度浮点数,双精度浮点数,多级偏移读写,取模块地址,调配近程内存等性能,读写效率高,速度快,兼容性好,应用时需本人签名或在测试模式下。 C++ 调用接口目前驱动读写反对的读写函数如下表所示,须要留神的是SwitchDriver在根底版本中不存在,如需应用请购买Pro专业版,专业版与根底版惟一的区别是在读写形式上,专业版具备更强的读写模式,而根底版则只反对Cr3读写模式; 导出函数函数作用BOOL SwitchDriver(PCHAR pSwitch)切换内存条模式(Pro)BOOL SetPid(DWORD Pid)设置全局过程PIDBOOL Read(ULONG64 address, T* ret)自定义读内存BOOL Write(ULONG64 address, T data)自定义读内存BOOL ReadMemoryDWORD(ULONG64 addre, DWORD * ret)读内存DWORDBOOL ReadMemoryDWORD64(ULONG64 addre, DWORD64 * ret)读内存DWORD64BOOL ReadMemoryBytes(ULONG64 addre, BYTE **ret, DWORD sizes)读内存字节BOOL ReadMemoryFloat(ULONG64 addre, float* ret)读内存浮点数BOOL ReadMemoryDouble(ULONG64 addre, double* ret)读内存双精度浮点数BOOL WriteMemoryBytes(ULONG64 addre, BYTE * data, DWORD sizes)写内存字节BOOL WriteMemoryDWORD(ULONG64 addre, DWORD ret)写内存DWORDBOOL WriteMemoryDWORD64(ULONG64 addre, DWORD64 ret)写内存DWORD64BOOL WriteMemoryFloat(ULONG64 addre, float ret)写内存浮点数BOOL WriteMemoryDouble(ULONG64 addre, double ret)写内存双精度浮点数DWORD ReadDeviationMemory32(ProcessDeviationMemory *read_offset_struct)计算32位偏移数据基址DWORD64 ReadDeviationMemory64(ProcessDeviationMemory *read_offset_struct)计算64位偏移数据基址DWORD64 GetModuleAddress(std::string dllname)驱动读取过程模块基地址DWORD64 GetSystemRoutineAddress(std::string funcname)获取零碎函数内存地址DWORD64 CreateRemoteMemory(DWORD length)在对端分配内存空间DWORD DeleteRemoteMemory(DWORD64 address, DWORD length)销毁对端内存新版本读写API接口在读写内存之前须要提前设置过程PID号,前期的调用将不须要再传入过程PID,此类读写适宜长期读,某些FPS射击类游戏的人物数组,3D类游戏坐标因为坐标会频繁挪动,需继续不间断读取,此读写模块将很适,接下来将带大家剖析并简略应用这些API接口实现性能。 ...

March 13, 2023 · 7 min · jiezi

关于c:c语言for循环语句的运用

咱们晓得在C语言中有while循环语句和for循环语句。本篇次要讲讲for循环语句的使用。 例如求阶乘:n!=1x2x3x...xn #include <stdio.h>int main (){ int n; int i=1; int ret; scanf("%d",&n); for (i=1;i<=n;i++){ //i++是在每次循环完结之后才执行的// ret*=i; } printf("%d\n",ret); return 0;}for和while语句有相似之处,都有小括号和大括号,不同之处在于for中的小括号蕴含了三个局部,这三个局部用 ;隔开,他们别离为:初始动作、循环持续的条件、循环每一轮要做的事。如上示代码,初始动作是i=1,循环持续的条件是i<=n,循环每一轮要做的事i++ 简略来说for语句的应用框架如下: for(初始动作;循环持续的条件;循环每一轮要做的事){ 语句}

March 13, 2023 · 1 min · jiezi

关于c++:驱动开发配置Visual-Studio驱动开发环境

在正式开始驱动开发之前,须要自行搭建驱动开发的必要环境,首先咱们须要装置Visual Studio 2013这款功能强大的程序开发工具,在课件内请双击ISO文件并运行外部的vs_ultimate.exe安装包,Visual Studio的装置十分的简略,您只须要依照提醒全副抉择默认参数即可,依据机器配置不同可能须要期待一段时间; 配置驱动开发环境在正式开始驱动开发之前,须要自行搭建驱动开发的必要环境,首先咱们须要装置Visual Studio 2013这款功能强大的程序开发工具,在课件内请双击ISO文件并运行外部的vs_ultimate.exe安装包,Visual Studio的装置十分的简略,您只须要依照提醒全副抉择默认参数即可,依据机器配置不同可能须要期待一段时间; 接着读者还须要持续装置Windows Driver Kit 8.1工具包,请将该工具包解压缩到桌面,并双击wdksetup.exe进行装置,过程中只须要始终下一步,并期待WDK工具包装置实现; WDK就是内核编程开发工具包,某些读者可能据说过DDK或者IFSDDK,最典型的开发工具包莫过于DDK7600,直到目前此类工具包依然能够失常应用,但并不举荐。 为了能测试驱动程序运行状态,读者需装置VMWare虚拟机,双击附件中的VMware-workstation-full-16.2.4-20089737.exe安装程序始终点击下一步即可,须要留神的是在如下选项中请在增强型键盘驱动程序上打对勾,之后期待装置结束即可; 接着关上VMware虚拟机,并在【文件】处抉择【新建虚拟机】,单机下一步并选中【稍后装置操作系统】,在操作系统抉择页面抉择【Win10 x64】版本。 在硬件配置处,读者可依据本人电脑的配置灵便的抉择,当自定义配置实现后,则虚拟机模板将被创立。 虚拟机模板创立实现后,读者可依据如下配置抉择编辑虚拟机设置,并在磁盘地位处将课件中的cn_windows_10_consumer_editions_version_1903_x64_dvd_8f05241d.iso挂载到虚拟机上; 点击开启虚拟机,并依照提醒将Windows零碎正确的装置,须要留神的是在抉择版本时,读者最好应用教育版与笔者开发环境保持一致,至此只需期待零碎装置结束,依据零碎差别安装时间可能有所差异,耐性期待即可; 当所有装置就绪后咱们须要在零碎中装置VMware Tools工具,该组件在装置后可让虚拟机具备有拖拽上传文件的性能,且鼠标键盘将能够自在切换,该性能是咱们必须要用到的; 装置VMware Tools工具很容易,只须要点击装置菜单,后会在虚拟机中呈现DVD驱动器,此时双击驱动器并依照要求装置即可,装置实现后重启零碎,此时则具备了拖拽上传性能; 当这些都做好当前,倡议用户敞开虚拟机,并点击【虚拟机】菜单,找到【快照】并拍摄一个快照,快照的作用是当虚拟机零碎呈现问题后可疾速复原到初始模式,防止重装系统,在后续课程中读者会呈现无数次的蓝屏,而虚拟机快照的疾速复原性能则是一个很好的抉择; 配置驱动开发模板1.关上Visual Studio开发工具,而后抉择【文件】菜单新建我的项目,并在已装置模板中选中【Visual C++】新建空我的项目,并将项目名称命名为【WinDDK】点击确定。 2.顺次抉择【解决方案视图-源文件-增加新建项】选项卡,或者间接按下Ctrl + Shift + A快捷关上菜单,并创立main.c文件。 3.接着须要批改配置管理器,增加自定义配置管理,抉择【生成-配置管理器-新建】选项卡,此处咱们命名为WinDDK即可。 4.批改配置属性中的【惯例】属性,点击菜单栏中的调试,抉择【WinDDK属性-配置-惯例】批改为标黄处所示内容即可。 5.配置可执行文件门路与导入库门路,这里咱们抉择【配置属性-VC++目录】顺次将如下信息填入配置项。 可执行目录C:\Program Files (x86)\Windows Kits\8.1\bin\x64C:\Program Files (x86)\Windows Kits\8.1\bin蕴含目录C:\Program Files (x86)\Windows Kits\8.1\Include\kmC:\Program Files (x86)\Windows Kits\8.1\Include\sharedC:\Program Files (x86)\Windows Kits\8.1\Include\umC:\Program Files (x86)\Windows Kits\8.1\Include\wdf\kmdf\1.13C:\Program Files (x86)\Windows Kits\8.1\Include\wdf\umdf\2.0C:\Program Files (x86)\Windows Kits\8.1\Include\winrt援用目录C:\Program Files (x86)\Windows Kits\8.1\Lib\win7\km\x64库目录C:\Program Files (x86)\Windows Kits\8.1\Lib\win7\km\x64C:\Program Files (x86)\Windows Kits\8.1\Lib\wdf\kmdf\x64\1.13C:\Program Files (x86)\Windows Kits\8.1\Lib\wdf\umdf\x64\2.0C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x64C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\km\x64当如上文件配置实现后,最终成果如下图所示; ...

March 13, 2023 · 2 min · jiezi

关于c#:async-与-Thread-的错误结合

在 TAP 呈现之前,咱们能够通过 Thread 来实现一些线程操作,从而实现多线程和异步操作。在 TAP 呈现之后,有时候为了更高精度的控制线程,咱们还是会应用到 Thread 。文本讲介绍一种谬误的应用形式,作为读者的一个参考。 和 TaskCreateOptions.LongRunning 相似不应该尝试应用 Thread 执行相似的异步操作。因为这节约了开启线程的花销。 有的时候,你可能会这么写: var thread = new Thread(async () =>{ while (true) { // do something await Task.Delay(1000); }}){ IsBackground = true};thread.Start();但其实,这是个谬误的写法。 IDE 提醒和 TaskCreateOptions.LongRunning 略有不同,采纳这种写法,IDE 会给出一个提醒,表明心愿勾销 async 关键字。因为实际上 Thread 的所有重载中并没有反对 Task 相干的重载。async void 除了在 event handler 中应用,其余中央都是不举荐的。所以这种做法实际上并不举荐。 而 TaskFactory.StartNew() 的重载中,因为存在一个 Func<T> 的重载,所以导致尽管这种这种应用形式谬误,却被 IDE 所承受。 所以这里其实就能够总结一个简略的规定:当考查一组 API 是否原生反对 TAP 操作的时候,应该查看这组 API 中是否存在 Task 相干的重载。如果没有,那么阐明原生并不能良好反对,如果应用则可能会出现意外的状况 同样的,当咱们本人在设计 API 的时候也应该参考该准则,对于本人心愿反对 TAP 的 API,应该提供 Task 相干的重载。 ...

March 13, 2023 · 2 min · jiezi

关于c#:分享一款漂亮的-C-Net-图形验证码

网上大部分验证码都是PHP的,基于C# .Net开发的很少,举荐一款很漂亮且实用的C#图形验证码,能够自定义背景图库,性能还是挺弱小的。1、成果截图 2、前端接入脚本<script src="https://cdn.kgcaptcha.com/captcha.js?appid=xxx"></script><script>kg.captcha({ // 绑定元素,验证框显示区域 bind: "#captchaBox", // 验证胜利事务处理 success: function(e) { console.log(e); }, // 验证失败事务处理 failure: function(e) { console.log(e); }, // 点击刷新按钮时触发 refresh: function(e) { console.log(e); }});</script><div id="captchaBox">载入中 ...</div> 3、C# 验证我的项目集成using System;using KgCaptchaSDK;public partial class _Default : System.Web.UI.Page{ protected void Page_Load(object sender, EventArgs e) { // 后端解决 string html, appId, appSecret, Token; if (Request.Form.ToString().Length > 0){ // 有数据处理 // 填写你的 AppId,在利用治理中获取 appId = "L001"; // 填写你的 AppSecret,在利用治理中获取 appSecret = "W68oJi0iqT2C3BFRGirO1IaYCDvsYEED"; var request = new kgCaptcha(appId, appSecret); // 前端验证胜利后颁发的 token,有效期两分钟 request.token = Request.Form["kgCaptchaToken"]; // 填写应用服务域名,在利用治理中获取 request.appCdn = "https://cdn.kgcaptcha.com"; // 当安全策略中的防控等级为3时必须填写,个别状况下能够疏忽 // 能够填写用户输出的登录帐号(如:Request.Form["username"]),可拦挡同一帐号屡次尝试等行为 request.userId = "kgCaptchaDemo"; // 申请超时工夫,秒 request.connectTimeout = 5; // 发送验证申请 var requestResult = request.sendRequest(); if (requestResult.code == 0) { // 验签胜利逻辑解决 *** // 这里做验证通过后的数据处理 // 如登录/注册场景,这里通常查询数据库、校验明码、进行登录或注册等动作解决 // 如短信场景,这里能够开始向用户发送短信等动作解决 // ... html = "<script>alert(´验证通过´);history.back();</script>"; } else { // 验签失败逻辑解决 html = "<script>alert(\"" + requestResult.msg + " - " + requestResult.code + "\");history.back();</script>"; } // 输入后果 Response.Write(html); } Response.Redirect("index.html"); }} ...

March 10, 2023 · 1 min · jiezi

关于c:C4-布尔类型和引用

布尔类型C++中的布尔类型C++在C语言的根本类型零碎之上减少了boolC++中的bool可获得值true和false实践上bool只占用一个字节bool类型只有true和false两个值 #include "stdio.h"int main(int argc,char *argv[]){ bool b = 0; printf("b=%d\n",b); b++; printf("b=%d\n",b); b=b-3; printf("b=%d\n",b); return 0;}输入后果:011 #include "stdio.h"int main(int argc,char *argv[]){ bool b= false; int a = b; printf("sizeof(b) = %d\n",sizeof(b)); printf("b=%d,a=%d\n",b,a); b = 3; a = b; printf("b=%d,a=%d\n",b,a); b = -5; a = b; printf("b=%d,a=%d\n",b,a); a = 10; b = a; printf("a=%d,b=%d\n",a,b); a = 0; b = a; printf("a=%d,b=%d\n",a,b); return 0;}输入后果: 布尔类型是C++中的根本数据类型能够定义bool类型的全局变量能够定义bool类型的常量能够定义bool类型的指针能够定义bool类型的数组C++对三目运算符进行了降级 三目运算符C语言中的三目运算符返回的是变量值不能作为左值应用 C++中的三目运算符可间接返回变量自身即可作为右值应用,也能够作为左值应用留神:三目运算符可能返回的值如果有一个是常量值,则不能作为左值应用; #include "stdio.h"int main(int argc,char *argv[]){ int a = 1; int b = 2; (a<b?a:b) = 3; printf("a = %d,b = %d\n",a,b);} ...

March 7, 2023 · 1 min · jiezi

关于c#:这样在-C-使用-LongRunnigTask-是错的

Task.Factory.StartNew 有一个重载,是反对 TaskCreationOptions.LongRunning 参数来指定 Task 的特色的。然而可能在没有留神的状况下,你就应用了谬误的用法。那么本文咱们来简略论述一下这个参数的作用,和应用的留神要点。 这样其实是谬误的有的时候,你可能会这么写: Task.Factory.StartNew(async () =>{ while (true) { // do something await Task.Delay(1000); }}, TaskCreationOptions.LongRunning);但其实,这是个谬误的写法。 为什么须要 LongRunning咱们通常两种状况下会想到应用 TaskCreationOptions.LongRunning 参数: 你的工作须要长时间运行,比方一个循环,或者一个死循环。用来从队列中取数据,而后解决数据,或者是一些定时工作。你的工作须要占用大量的 CPU 资源,是一个很大的循环,比方要遍历一个很大的数组,并做一些解决。那么这个时候,咱们就须要应用 TaskCreationOptions.LongRunning 参数来指定 Task。 因为咱们可能学习到了,Task 默认的 Scheduler 是 ThreadPool,而 ThreadPool 的线程是无限的,如果你的工作须要长时间运行,或者是须要占用大量的 CPU 资源,那么就会导致 ThreadPool 的线程不够用。导致线程饥饿,或者是线程池的线程被占用,导致其余的工作无奈执行。 于是咱们很聪慧的就想到了,咱们能够应用 TaskCreationOptions.LongRunning 参数来指定 Task,这样就能够防止线程饥饿。 画蛇添足然而实际上,开篇的写法并不能达到咱们的目标。 咱们能够通过以下代码来验证一下: var task = Task.Factory.StartNew(async () =>{ while (true) { // do something await Task.Delay(1000); }}, TaskCreationOptions.LongRunning);Thread.Sleep(3000);Console.WriteLine($"Task Status: {task.Status}");// Task Status: RanToCompletion咱们能够看到,Task 的状态是并非是 Running,而是 RanToCompletion。 ...

March 6, 2023 · 1 min · jiezi

关于c++:Visual-studio配置第三方库的步骤总结

1 设置第三方库的头文件目录(Header文件目录),有两个地位能够设置,两者成果雷同,二选一即可。VC++ Directories中的include directories 或者 C/C++==>General==>addition include directories。 2 设置第三方库的库目录(lib),有两个地位能够设置,两者成果雷同,二选一即可。VC++ Directories中的library directories 或者 Linker==>General==>addition library directories。 就第三方库目录设置的话,1、2中,上述两组地位设置成果是一样的,没有区别。那么,集体的话,习惯哪个就用那个,公司我的项目的话,跟曾经有的设置放弃对立即可。 3 设置第三方库的具体的lib依赖名称。Linker==>Input==>Additional Dependencies,录入2中lib目录下所有的lib文件名称。 4 如果生成的可执行文件,依赖第三方库的可执行文件(exe)或者动态链接库(dll)。把第三方库中的exe或者dll文件,放到咱们的我的项目生成exe所在的目录即可,或者把第三方库的可执行文件(exe)与动态链接库(dll)所在目录,设置为环境变量。 注意事项:在vs界面顶部地位上的configuration与platform选项,与我的项目==>右键==>properties配置界面中的configuration、platform选项要保持一致。不保持一致的话,vs老手可能会误以为对第三方库的配置没有失效。 参考文献:https://blog.csdn.net/Dontla/article/details/122745857 (VS里的VC++属性和C/C++属性有什么区别?_Dontla的博客-CSDN博客)https://www.cnblogs.com/JMLiu/p/7954630.html (VS2010中VC++目录和C/C++之间的区别。VC++ Directories和C/C++的区别。 - J.M.Liu - 博客园)https://blog.csdn.net/HandsomeHong/article/details/114157201 (面向小白visual studio 2019 增加第三方库教程_vs链接第三方库_微妙之二进制的博客-CSDN博客)https://blog.csdn.net/raodotcong/article/details/8998379 (Visual Studio下C++第三方库的配置办法总结_贝塔酷狗的博客-CSDN博客)https://www.jianshu.com/p/92eb3379a381 (Visual Studio配置C++第三方库开发指南 - 简书)

March 4, 2023 · 1 min · jiezi

关于c:C3-进化后const分析

C语言中的constconst润饰的变量是只读的,实质还是变量const润饰的局部变量在栈上调配空间const润饰的全局变量在只读存储区调配空间const只在编译器有用,再运行期无用const润饰的变量不是真的变量,只是通知编译器该变量不能呈现再赋值符号的右边const使得变量只有只读属性const将具备全局生命周期的变量存在只读存储区const不是真正意义上的常量,只有枚举是真正意义的常量 C编译器: #include"stdio.h"int main(){ const int c = 0; int * p= (int*)&c; printf("Begin...\n"); *p = 5; printf("c = %d\n",c); printf("End...\n"); return 0; }输入后果:Begin...c = 5End... C++编译器: #include"stdio.h"int main(){ const int c = 0; int * p= (int*)&c; printf("Begin...\n"); *p = 5; printf("c = %d\n",c); printf("End...\n"); return 0; }输入后果:Begin...c = 0End... C++中的constC++在C的根底上对const进行了进化解决当碰见const申明时在符号表中放入常量编译过程中若发现应用常量则间接以符号表中的值替换编译过程中若发现下述状况给对应的常量调配存储空间对const常量应用了extern对const常量应用&操作符 C++编译器尽管可能为const常量调配空间,但不会应用其存储空间中的值。 C语言中的const变量C语言中const变量是只读变量,会调配存储空间C++中的const常量 -可能调配存储空间当const常量全局,并且须要在其余文件中应用当应用&操作符对const常量取地址 C++中const常量相似于宏定义const int c = 5; ≈#define c 5 C++中的const常量在宏定义中不同const常量是由编译器解决编译器对const常量进行类型检查和作用域查看宏定义由预处理器解决,单纯的文本替换 #include "stdio.h"void f(){ #define a 3 const int b = 4;}void g(){ printf("a=%d\n",a);}int main(){ const int A = 1; const int B = 2; int array[A+B] = {0}; int i = 0; for(i = 0;i<(A+B);i++) { printf("array[%d] = %d\n",i,array[i]); } f(); g(); return 0;} ...

March 2, 2023 · 1 min · jiezi

关于c++:Skeye全景AR及IVMS技术助力地铁安防视频监控系统建设

 地铁近程视频监控零碎是保障城市轨道交通保护和运输平安的重要伎俩,它可能无效地监控各个地铁站的出入情况,为地铁控制中心的调度员、各车站值班员、列车司机等提供无关列车运行、防灾救灾、旅客疏导以及社会治安等方面的视觉信息,是进步行车指挥工作效率的辅助通信工具,为保障地铁平安作出安防奉献。 个别状况,地铁处于关闭环境,人流量大,如果产生突发事件,救济难度极大;另外地铁零碎智能化水平高,软硬件零碎宏大,整个零碎长期处于半自动和全自动运行状态。因而地铁的视频监控零碎,一方面要对人流进行24小时全方位监控,对突发事件做出预警,视频存储7*24小时以便帮助公安人员更直观高效的解决各类安全事件。另一方面随着治安局势的日益严厉,地铁视频监控零碎中,摄像设施数量多,因而对视频监控零碎的稳定性、一致性、并发性、安全性等性能都具备较强要求。 SkeyeARS 全景AR加强监视系统是视开科技携手电子科技大学长三角研究院智能交通研究所独特研制的一款基于宽场景多路视频无缝拼接、视频实时加强、监督指标加强显示、指标主动跟踪、视频存储回放、近程数据传输和多通道全景视频同步显示等性能的综合视频AR加强监视系统,反对全景拼接最大反对程度视场角360度,反对超高清8K全景视频(7680*1080)多路路编码存储与回放,让乘客在享受地铁带来的高速便捷的出行时多一分平安保障。  SkeyeIVMS分布式集群视频云管控平台是一套集流媒体服务集群、视频资源管理、软硬件服务节点保障、容灾双机热备、主备主动切换为一体的综合性流媒体集群管控云服务,零碎采纳Spring Security架构,平安有保障;反对私有云、私有化部署,保障系统的稳定性、数据强一致性、高并发应用性、高安全性等准则。 1、 稳定性:该平台保障系统的可用性,健壮性为准则。任何状况只有部署服务器的散布状况不同,则应用不变,确保地铁场景监控不间断;2、 数据强一致性:在集群部署的零碎中,某个服务在宕机正在解决的业务局部也会容许ZK选举机制进行节点数据的同步,保障用户所看到的与操作的工夫都是统一的;3、 高并发应用性:基于集群的分布式部署架构,利用负载平衡技术,零碎很轻松在高并发场景下应用,正当防止了因地铁监控设施泛滥导致的并发应用问题;4、 安全性:零碎采纳Spring Security架构,零碎接口和验证鉴权更加严格,即便在公共网络中应用,也能保障地铁零碎数据、文件等信息安全不泄露。

March 2, 2023 · 1 min · jiezi

关于c++:SkeyePlayer源码解析系列之支持H265

近期SkeyePlayer(windows)更新已全面反对H265的RTSP流的解码播放,这里就反对H265过程做简要介绍; 一、 libSkeyeRTSPClient库已反对H265视频源的RTSP流的拉取和解析 二、H265头解析 H265和H264相似,不过其NAL type格局更多样化,除了SPS,PPS之外,还减少了VPS,上面就针对H265帧nal 头做简略剖析;首先,看X265源码中的H265nal头格局定义://H265 NAL type//this enum have been defined in x265.htypedef enum tagH265NalUnitType{ NAL_UNIT_CODED_SLICE_TRAIL_N = 0, // 0 NAL_UNIT_CODED_SLICE_TRAIL_R, // 1 NAL_UNIT_CODED_SLICE_TSA_N, // 2 NAL_UNIT_CODED_SLICE_TLA, // 3 // Current name in the spec: TSA_R NAL_UNIT_CODED_SLICE_STSA_N, // 4 NAL_UNIT_CODED_SLICE_STSA_R, // 5 NAL_UNIT_CODED_SLICE_RADL_N, // 6 NAL_UNIT_CODED_SLICE_DLP, // 7 // Current name in the spec: RADL_R NAL_UNIT_CODED_SLICE_RASL_N, // 8 NAL_UNIT_CODED_SLICE_TFD, // 9 // Current name in the spec: RASL_R NAL_UNIT_RESERVED_10, NAL_UNIT_RESERVED_11, NAL_UNIT_RESERVED_12, NAL_UNIT_RESERVED_13, NAL_UNIT_RESERVED_14, NAL_UNIT_RESERVED_15, NAL_UNIT_CODED_SLICE_BLA, // 16 // Current name in the spec: BLA_W_LP NAL_UNIT_CODED_SLICE_BLANT, // 17 // Current name in the spec: BLA_W_DLP NAL_UNIT_CODED_SLICE_BLA_N_LP, // 18 NAL_UNIT_CODED_SLICE_IDR, // 19 // Current name in the spec: IDR_W_DLP NAL_UNIT_CODED_SLICE_IDR_N_LP, // 20 NAL_UNIT_CODED_SLICE_CRA, // 21 NAL_UNIT_RESERVED_22, NAL_UNIT_RESERVED_23, NAL_UNIT_RESERVED_24, NAL_UNIT_RESERVED_25, NAL_UNIT_RESERVED_26, NAL_UNIT_RESERVED_27, NAL_UNIT_RESERVED_28, NAL_UNIT_RESERVED_29, NAL_UNIT_RESERVED_30, NAL_UNIT_RESERVED_31, NAL_UNIT_VPS, // 32 NAL_UNIT_SPS, // 33 NAL_UNIT_PPS, // 34 NAL_UNIT_ACCESS_UNIT_DELIMITER, // 35 NAL_UNIT_EOS, // 36 NAL_UNIT_EOB, // 37 NAL_UNIT_FILLER_DATA, // 38 NAL_UNIT_SEI, // 39 Prefix SEI NAL_UNIT_SEI_SUFFIX, // 40 Suffix SEI NAL_UNIT_RESERVED_41, NAL_UNIT_RESERVED_42, NAL_UNIT_RESERVED_43, NAL_UNIT_RESERVED_44, NAL_UNIT_RESERVED_45, NAL_UNIT_RESERVED_46, NAL_UNIT_RESERVED_47, NAL_UNIT_UNSPECIFIED_48, NAL_UNIT_UNSPECIFIED_49, NAL_UNIT_UNSPECIFIED_50, NAL_UNIT_UNSPECIFIED_51, NAL_UNIT_UNSPECIFIED_52, NAL_UNIT_UNSPECIFIED_53, NAL_UNIT_UNSPECIFIED_54, NAL_UNIT_UNSPECIFIED_55, NAL_UNIT_UNSPECIFIED_56, NAL_UNIT_UNSPECIFIED_57, NAL_UNIT_UNSPECIFIED_58, NAL_UNIT_UNSPECIFIED_59, NAL_UNIT_UNSPECIFIED_60, NAL_UNIT_UNSPECIFIED_61, NAL_UNIT_UNSPECIFIED_62, NAL_UNIT_UNSPECIFIED_63, NAL_UNIT_INVALID,}H265NalUnitType;#endif咱们能够看到其中VPS, SPS和PPS的定义: ...

March 2, 2023 · 5 min · jiezi

关于c++:SkeyePlayer源码解析系列之录像写MP4

SkeyePlayer(Windows)中录像采纳GPAC的MP4Box库来封装MP4,上面我将简略介绍MP4的封装调用流程和须要留神的点; 一、GPAC库的编译,GPAC是跨平台的库,windows和linux都能很不便多编译,再次不做过多赘述,大家可去GPAC官网或者Github上下载; 二、创立MP4 bool SkeyeMP4Writer::CreateMP4File(char*filename,int flag){ SaveFile(); m_audiostartimestamp=-1; m_videostartimestamp=-1; if(filename==NULL) { char filename2[256]={0}; sprintf(filename2,"%d-gpac%d.mp4",time(NULL),rand()); p_file=gf_isom_open(filename2,GF_ISOM_OPEN_WRITE,NULL);//关上文件 }else p_file=gf_isom_open(filename,GF_ISOM_OPEN_WRITE,NULL);//关上文件 if (p_file==NULL) { return false; } gf_isom_set_brand_info(p_file,GF_ISOM_BRAND_MP42,0); //if(flag&ZOUTFILE_FLAG_VIDEO) //{ // m_videtrackid=gf_isom_new_track(p_file,0,GF_ISOM_MEDIA_VISUAL,1000); // gf_isom_set_track_enabled(p_file,m_videtrackid,1); //} //if(flag&ZOUTFILE_FLAG_AUDIO) //{ // m_audiotrackid=gf_isom_new_track(p_file,0,GF_ISOM_MEDIA_AUDIO,1000); // gf_isom_set_track_enabled(p_file,m_audiotrackid,1); //} m_nCreateFileFlag = flag; return true;}创立MP4很简略,调用gf_isom_open函数就能轻松搞定,gf_isom_set_brand_info函数设置以后写MP4的版本为MP4V2;值得注意的中央是: 1>. 创立文件之前须要对所有的参数进行初始化,以及如果文件正在写入则须要将其敞开,这个操作次要是32位程序写的MP4文件大于4G可能呈现不能播放的问题,为了不便写MP4文件进行分片,这个将在系列文章后续中进行解说;2>. 大家能够看到上段代码有屏蔽了局部代码flag&ZOUTFILE_FLAG_VIDEO和flag&ZOUTFILE_FLAG_AUDIO的判断,这两段代码是用来在MP4文件中创立音频轨和视频轨(默认各只创立一个),请留神:如果这里曾经创立了音频和视频轨,然而后续的写入过程中如果只写音频或者视频的话,某些播放器可能是播不进去的(比方windows自带的播放器),所以,如果只写音频的话只须要创立音频轨就能够了,视频同理。三、写入视频H264的SPS和PPS头信息 bool SkeyeMP4Writer::WriteH264SPSandPPS(unsigned char*sps,int spslen,unsigned char*pps,int ppslen,int width,int height){ if (m_nCreateFileFlag&ZOUTFILE_FLAG_VIDEO) { m_videtrackid = gf_isom_new_track(p_file, 0, GF_ISOM_MEDIA_VISUAL, 1000); gf_isom_set_track_enabled(p_file, m_videtrackid, 1); } else { return false; } p_videosample=gf_isom_sample_new(); p_videosample->data=(char*)malloc(1024*1024); p_config=gf_odf_avc_cfg_new(); gf_isom_avc_config_new(p_file,m_videtrackid,p_config,NULL,NULL,&i_videodescidx); gf_isom_set_visual_info(p_file,m_videtrackid,i_videodescidx,width,height); GF_AVCConfigSlot m_slotsps={0}; GF_AVCConfigSlot m_slotpps={0}; p_config->configurationVersion = 1; p_config->AVCProfileIndication = sps[1]; p_config->profile_compatibility = sps[2]; p_config->AVCLevelIndication = sps[3]; m_slotsps.size=spslen; m_slotsps.data=(char*)malloc(spslen); memcpy(m_slotsps.data,sps,spslen); gf_list_add(p_config->sequenceParameterSets,&m_slotsps); m_slotpps.size=ppslen; m_slotpps.data=(char*)malloc(ppslen); memcpy(m_slotpps.data,pps,ppslen); gf_list_add(p_config->pictureParameterSets,&m_slotpps); gf_isom_avc_config_update(p_file,m_videtrackid,1,p_config); free(m_slotsps.data); free(m_slotpps.data); return true;}首先,通过gf_odf_avc_cfg_new()创立一个设置AVC信息的配置构造p_config,而后对构造中指定的信息,如:长,宽,SPS和PPS等要害参数写入配置构造,调用gf_isom_avc_config_update函数写入参数信息;当然这里只是H264格局的参数设置,像其余的格局比方H265的设置也相似,这将在后续系列中进行解说; ...

March 2, 2023 · 3 min · jiezi

关于c++:SkeyePlayer渲染引擎D3DRender实现视频图像高效率渲染解决方案附源码

SkeyePlayer播放器以其低延时播放RTSP、RTMP等流而闻名,而低延时播放除了低延时的拉流库以及高编解码效率外,视频图像的高速渲染也尤为重要; 本篇以及系列文章次要解说SkeyePlayer播放器的渲染引擎D3DRender,该渲染引擎反对基于GDI和D3D两种渲染形式,GDI形式也是咱们熟知的windows图像绘制接口,广泛利用于win32以及MFC界面编程,以及图形元素的绘制;然而因为应用纯软的windows零碎api接口进行绘制,其效率通常比拟低,而D3D能够启用硬件加速,所以,通常其效率会比拟高,上面咱们将具体解说D3DRende视频图像渲染流程。 1. D3DRender初始化D3DRender库别离为GDI和D3D两种渲染模式提供不同API接口进行渲染初始化,调用如下代码段实现其初始化: //创立D3dRender if (pThread->renderFormat == GDI_FORMAT_RGB24) { if (NULL == pThread->d3dHandle) RGB_InitDraw(&pThread->d3dHandle); } else if ( (NULL == pThread->d3dHandle) && ((unsigned int)time(NULL)-deviceLostTime >= 2) ) { D3D_FONT font; memset(&font, 0x00, sizeof(D3D_FONT)); font.bold = 0x00; wcscpy(font.name, TEXT("Arial Black")); font.size = (int)(float)((width)*0.2f);// 32; if (pThread->showOSD) { font.size = pThread->osd.size;// 32; } font.width = (int)(float)((font.size)/2.5f);//13; if (NULL!=pThread->hWnd && (IsWindow(pThread->hWnd)) ) { D3D_Initial(&pThread->d3dHandle, pThread->hWnd, width, height, 0, 1, pThread->renderFormat, &font); }留神D3D_Initial初始化D3D渲染模式须要设置OSD叠加的相干参数,如OSD叠加文字的字体、大小等参数,OSD叠加具体解说将在后续的文章中进行解说,本文不做过多赘述。 ...

March 2, 2023 · 1 min · jiezi

关于c++:SkeyePlayer渲染引擎D3DRender电子放大功能实现解决方案附源码-1

SkeyePlayer依附D3DRender弱小的渲染能力咱们能够实现很多视频编辑性能,比方电子放大性能,本文将深刻D3DRender渲染引擎库代码,重点讲述其如何采纳surface离屏外表技术来实现渲染视频图像出现,以实现在surface上做电子放大缩略图显示等性能。 1. D3DRender初始化D3D创立设施首先,咱们须要创立一个D3D9设施用于操作系统软硬件资源来为咱们的视频渲染服务,这个代码很简略,依照Direct3D教程即可实现,如下代码所示: pD3D = Direct3DCreate9(D3D_SDK_VERSION); if (NULL == pD3D) { errCode = D3D_NOT_ENABLED; return false; } //获取显卡张数 int adaptnum = pD3D->GetAdapterCount(); //_TRACE("共[%d]张显卡.\n", adaptnum); //获取显卡反对的显示格局 if (FAILED(pD3D->GetAdapterDisplayMode(nAdapterNo, &d3dDisplayMode )) ) { __SAFE_RELEASE(pD3D); return false; } //_TRACE("显卡信息: %d X %d\tRefreshRate: %d\tFormat: %d\n", d3dDisplayMode.Width, d3dDisplayMode.Height, d3dDisplayMode.RefreshRate, d3dDisplayMode.Format); if (FAILED(pD3D->CheckDeviceFormat(nAdapterNo, D3DDEVTYPE_HAL, d3dDisplayMode.Format, 0, D3DRTYPE_SURFACE, d3dFormat))) { _TRACE("CheckDeviceFormat 不反对指定的格局..\n"); errCode = D3D_FORMAT_NOT_SUPPORT; __SAFE_RELEASE(pD3D); return false; } //查看输出格局到显示格局的转换是否反对 if ( FAILED(pD3D->CheckDeviceFormatConversion(nAdapterNo, D3DDEVTYPE_HAL, d3dFormat, d3dDisplayMode.Format) ) ) { errCode = D3D_FORMAT_NOT_SUPPORT; __SAFE_RELEASE(pD3D); return false; } //查看是否反对硬件顶点渲染形式 if ( FAILED(pD3D->GetDeviceCaps(nAdapterNo, D3DDEVTYPE_HAL, &d3dCaps) ) ) { errCode = D3D_VERTEX_HAL_NOT_SUPPORT; __SAFE_RELEASE(pD3D); return false; } //检测硬件是否反对变换和灯光 INT vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; if ( d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) { vp = D3DCREATE_HARDWARE_VERTEXPROCESSING; //硬件反对 } else { vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; //软件反对 } memset(&d3dParameters, 0, sizeof(D3DPRESENT_PARAMETERS)); D3DFORMAT d3dFormatRender = D3DFMT_UNKNOWN; d3dParameters.Windowed = TRUE; d3dParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;//D3DSWAPEFFECT_DISCARD;//D3DSWAPEFFECT_COPY;//D3DSWAPEFFECT_DISCARD; //如果想通过GetBackBuffer取得后备缓冲内容打印屏幕画面,则不能应用 D3DSWAPEFFECT_DISCARD d3dParameters.BackBufferFormat = d3dFormatRender;//D3DFMT_R5G6B5;//D3DFMT_R5G6B5;//D3DFMT_UNKNOWN; //应用桌面格局 d3dParameters.Flags = D3DPRESENTFLAG_VIDEO;//D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;//D3DPRESENTFLAG_VIDEO;// | D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; d3dParameters.BackBufferCount = 1; d3dParameters.BackBufferWidth = width; d3dParameters.BackBufferHeight = height; if (NULL == pD3dDevice) { HRESULT hr = pD3D->CreateDevice(nAdapterNo, D3DDEVTYPE_HAL, m_hWnd, vp, &d3dParameters, &pD3dDevice); if ( FAILED(hr) ) { _TRACE("D3D Create Device fail.."); switch (hr) { case D3DERR_DEVICELOST: { _TRACE("D3DERR_DEVICELOST\n"); } break; case D3DERR_INVALIDCALL: { _TRACE("D3DERR_INVALIDCALL\n"); } break; case D3DERR_NOTAVAILABLE: { _TRACE("D3DERR_NOTAVAILABLE\n"); } break; case D3DERR_OUTOFVIDEOMEMORY: { _TRACE("D3DERR_OUTOFVIDEOMEMORY\n"); } break; default: break; } errCode = D3D_DEVICE_CREATE_FAIL; __SAFE_RELEASE(pD3D); return false; } } if ( FAILED( pD3dDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pD3dBackBuffer ) ) ) { errCode = D3D_GETBACKBUFFER_FAIL; __SAFE_RELEASE(pD3dDevice); __SAFE_RELEASE(pD3D); return false; } if ( FAILED( pD3dBackBuffer->GetDesc( &surfaceDesc ) ) ) { errCode = D3D_GETBACKBUFFER_FAIL; __SAFE_RELEASE(pD3dDevice); __SAFE_RELEASE(pD3D); return false; }如上代码所示,咱们依据须要渲染的视频长宽创立一个反对硬件加速D3D渲染设施,为视频图像渲染做筹备。 ...

March 2, 2023 · 3 min · jiezi

关于c++:SkeyePlayer插件使用说明

鉴于大家对于SkeyePlayer插件的应用还不太熟悉,特此写一篇插件的应用文档,供大家参考;SkeyePlayer插件有两种,一种是基于IE的ActiveX控件,一种是基于FireFox(也反对多浏览器)的npAPI插件;两种插件均蕴含源码,因为FireBreath框架生成代码比拟多,npAPI插件只蕴含要害代码,后续会提供专门解说FireBreath生成SkeyePlayer npAPI插件的文档,上面咱们开始解说两种插件如何应用。 ActiveX OCX控件 ActiveX控件应用MFC ActiveX框架生成非常简单,这里就不做过多赘述,感兴趣的同学能够下载SkeyePlayer源码;留神,IE11曾经不反对AttachEvent的形式触发浏览器页面事件,所以,须要导出播放事件的同学在前端页面编写的时候须要做好IE11的兼容性解决; npAPI插件 npAPI插件由FireBreath框架生成,该框架提供了十分具体的步骤即可生成咱们想要的播放器插件框架,框架生成好当前将SkeyePlayer源码的SkeyePlayerPlugin目录下的代码替换掉生成的代码即可。插件导出接口阐明 [1] LONG Start(LPCTSTR sURL, LPCTSTR sRenderFormat, LPCTSTR sUserName, LPCTSTR sPassword, LPCTSTR sHardDecord);Start函数对SkeyePlayer_OpenStream函数进行了封装,其作用是关上一个RTSP流; 参数阐明:[sURL]:关上的流地址,以rtsp://结尾;[sRenderFormat]:播放渲染格局,枚举对应格局:D3D反对格局如下: YV12: 0YUY2: 1UYVY: 2A8R8G8B8: 3X8R8G8B8: 4RGB565: 5RGB555: 6GDI反对格局: RGB24: 7留神:OCX接口函数所有参数均为字符串类型,次要是为了不便页面js调用,上面所有接口参数皆是如此; [2] void Config(LPCTSTR sFrameCache, LPCTSTR sPlaySound, LPCTSTR sShowToScale, LPCTSTR sShowStatisticInfo);Config函数配置播放器的一些参数,诸如缓存,是否播放音频,是否按比列显示,是否显示码率信息;参数阐明:[sFrameCache]:缓存帧数,1-n ,该数值越小,延时越小,当然播放画面可能再网络带宽不现实的状况下会比拟卡,反之则延时越大,相应的播放也会比拟晦涩;[sPlaySound]:是否播放音频,1=播放,0=不播放[sShowToScale]:是否按比列显示,1=是 0=否 ,软解码时无效[sShowStatisticInfo]:是否显示码率信息,1=是 0=否 ,软解码时无效 [3] void SetOSD(LPCTSTR show, LPCTSTR alpha, LPCTSTR red, LPCTSTR green, LPCTSTR blue, LPCTSTR left, LPCTSTR top, LPCTSTR right, LPCTSTR bottom, LPCTSTR strOSD);SetOSD是新增接口,用于设置视频显示时的OSD显示;参数阐明:[show]:是否显示OSD,1=显示 0=不显示[alpha]:OSD显示叠加透明度, 0-255,0是齐全通明。255齐全不通明[red]:OSD字幕色彩RGB重量中的R重量,[green]:OSD字幕色彩RGB重量中的G重量,[blue]:OSD字幕色彩RGB重量中的B重量,[left]:OSD基于视频的显示地位坐标的左上角x轴坐标[top]:OSD基于视频的显示地位坐标的左上角y轴坐标[right]:OSD基于视频的显示地位坐标的右下角x轴坐标[bottom]:OSD基于视频的显示地位坐标的右下角y轴坐标[strOSD]:OSD字幕 ...

March 2, 2023 · 1 min · jiezi

关于c++:SkeyePlayer-rtsp播放器源码解析之64位编译方案

SkeyePlayer反对多路拉流播放,而拉流解码的过程须要占用系统资源,而咱们都晓得32位程序最多只能利用4G的内存,而当初动则8G,16G的内存空间是齐全不能被利用的;所以,为了充分利用内存资源,咱们筹备将SkeyePlayer改成64位版本。 新建64位编译平台所有依赖库编译成64位 1> gpac库编译64位 官网下载gpac源码,通过cmake生成对应的VS版本,而后编译64位平台即可;2> libSkeyeAACEncoder库编译64位 3> ffmpeg编译64位 ffmpeg可从官网下载源码,通过穿插编译或者cmake编译64位;4> IntelHardCodec编译64位 5> SaveJPGDll库编译64位 6> FFDecoder编译64位 7> D3DRender编译64位 这里郑重感激Gavin大神百忙之中抽出工夫帮忙编译64位版本库;8> SkeyeRTSPClient编译64位 程序编译64位并兼容32位调试 程序编译很简略,指定编译平台,而后附加对应平台的依赖项的头文件和库文件即可; 通过本次编译64位版本,遇到的几个问题及注意事项: (1)程序依赖的DLL库编译64位的通过要留神其所依赖的库(零碎的或者第三方的)都要编译的64位反对; (2)如果没有源码,由第三方提供的库,如果须要兼容编译,能够通过宏定义的形式解决,如:#ifdef _WIN64#include "D3DRender\include\x64\D3DRenderAPI.h"#pragma comment(lib, "SkeyeRTSPClient/lib/x64/libSkeyeRTSPClient.lib")#pragma comment(lib, "FFDecoder/lib/x64/SkeyeDecoder.lib")#pragma comment(lib, "./D3DRender/lib/x64/D3DRender.lib")#pragma comment(lib, "IntelHardCodec/lib/x64/IntelHardCodec.lib")#pragma comment(lib, "libSkeyeAACEncoder/lib/x64/libSkeyeAACEncoder.lib")// 减少MP4box和SaveJPG库的反对 [9/20/2016 dingshuai]// JPGSave#pragma comment(lib, "SaveJPGDll/lib/x64/SaveJpgDll.lib")#else#include "D3DRender\include\x86\D3DRenderAPI.h"#pragma comment(lib, "SkeyeRTSPClient/lib/x86/libSkeyeRTSPClient.lib")#pragma comment(lib, "FFDecoder/lib/x86/SkeyeDecoder.lib")#pragma comment(lib, "D3DRender/lib/x86/D3DRender.lib")#pragma comment(lib, "IntelHardCodec/lib/x86/IntelHardCodec.lib")#pragma comment(lib, "libSkeyeAACEncoder/lib/x86/libSkeyeAACEncoder.lib")// 减少MP4box和SaveJPG库的反对 [9/20/2016 dingshuai]// JPGSave#pragma comment(lib, "SaveJPGDll/lib/x86/SaveJpgDll.lib")#endif目前x64的零碎已宽泛应用,将来支流程序都应该是64位的,为了充分利用内存资源,也倡议大家都将目前的32位程序升级成64位;最新兼容64和32位的SkeyePlayer将在近期公布。

March 2, 2023 · 1 min · jiezi

关于c++:SkeyePlayer-RTSP播放器源码解析系列之效率优化方案

测试发现,通过SkeyePlayer拉取网络摄像机的流, 其音频可能是G711,G726等,而写MP4或者转推RTMP等都不反对这些音频格式,那么咱们就须要将其音频转码成AAC,能够应用libSkeyeAACEncoder库进行转码,而后写MP4或者推送;然而,在理论利用中,咱们发现转码过程其实还是比拟耗时的,它甚至会导致解码线程来不及从而使直播延时增大,所以,咱们采纳队列缓存+线程的形式来优化录像和抓图。 实现如下: 录像优化1> 开启录像 if (pThread->manuRecording == 0x01 && NULL==pThread->m_pMP4Writer && frameinfo.type==Skeye_SDK_VIDEO_FRAME_I)//开启录制 { //EnterCriticalSection(&pThread->critRecQueue); if (!pThread->m_pMP4Writer) { pThread->m_pMP4Writer = new SkeyeMP4Writer(); } unsigned int timestamp = (unsigned int)time(NULL); time_t tt = timestamp; struct tm *_time = localtime(&tt); char szTime[64] = {0,}; strftime(szTime, 32, "%Y%m%d%H%M%S", _time); int nRecordPathLen = strlen(pThread->manuRecordingPath); if (nRecordPathLen==0 || (pThread->manuRecordingPath[nRecordPathLen-1] != '/' && pThread->manuRecordingPath[nRecordPathLen-1] != '\\') ) { pThread->manuRecordingPath[nRecordPathLen] = '/'; } char sFileName[512] = {0,}; sprintf(sFileName, "%sch%d_%s.mp4", pThread->manuRecordingPath, pThread->channelId, szTime); if (!pThread->m_pMP4Writer->CreateMP4File(sFileName, ZOUTFILE_FLAG_FULL)) { delete pThread->m_pMP4Writer; pThread->m_pMP4Writer = NULL; //return -1; } else { } //LeaveCriticalSection(&pThread->critRecQueue); }2> 录像数据写缓存 ...

March 2, 2023 · 2 min · jiezi

关于c++:SkeyePlayer-RTSP播放器源码解析系列之H264一帧多NAL写MP4录像花屏问题解决方案

接上一篇[SkeyePlayer源码解析系列之录像写MP4]之续篇,咱们来解说一下对于H264编码格局中的一帧多nal(Network Abstract Layer, 即网络形象层),对于H264和NAL,这里援用一段话来科普一下: 【转】 在H.264/AVC视频编码标准中,整个零碎框架被分为了两个层面:视频编码层面(VCL)和网络形象层面(NAL)。其中,前者负责无效示意视频数据的内容,而后者则负责格式化数据并提供头信息,以保证数据适宜各种信道和存储介质上的传输。因而咱们平时的每帧数据就是一个NAL单元(SPS与PPS除外)。在理论的H264数据帧中,往往帧后面带有00 00 00 01 或 00 00 01分隔符,一般来说编码器编出的首帧数据为PPS与SPS,接着为I帧…… 个别状况下,一个H264帧间接以00 00 00 01结尾作为一个NAL作为网络传输单元,而在有些H264的编码器则编码进去的H264帧蕴含了多个NAL,这个时候每个分片的NAL(留神是分片的)则是是以00 00 01结尾作为网络传输单元,通过分片的NAL数据量更小,从而更加不便进行网络;然而,咱们在接管到带有多个NAL的H264帧的时候进行写MP4则不能简略是只通过将头部的00 00 00 01标记转换从AVC的长度标识,而须要将所有的00 00 00 01和00 00 01都须要转换成该NAL单元的长度,否则就会呈现视频解码只能播放头部一小部分,其余局部全副花屏的状况,如下图所示: 说了这么多,大家是否明确了呢,如果不明确的(文字描述比拟虚),咱们间接看SkeyePlayer代码实现: int SkeyeMP4Writer::WriteMp4File(unsigned char* pdata, int datasize, bool keyframe, long nTimestamp, int nWidth, int nHeight){ if (nTimestamp==0||(pdata==NULL)||datasize<=0) { return -1; } int inlen=datasize; unsigned char*pin=pdata; int outlen=0; unsigned char*pout=NULL; bool bend = false; int datalen=0; bool bSPSOrPPS = false; int iOutLen = datasize; unsigned char* pRealData = new unsigned char[datasize<<1]; int nRealDataSize = 0; memset(pRealData,0x00, datasize+4); do { int nal_start = 0; int nal_end = 0; outlen = find_nal_unit(pin,inlen, &nal_start, &nal_end ); if (outlen<=0) { break; } pout = pin+nal_start; if(pout!=NULL) { unsigned char naltype = ( pout[0] & 0x1F); if (naltype==0x07)//0x67 {// m_psps=pout;// m_spslen=outlen; //pout[0] = 0x67; if(m_bwritevideoinfo==false) { m_psps = new unsigned char[outlen]; memcpy(m_psps, pout, outlen); m_spslen=outlen; } bSPSOrPPS = true; } else if (naltype==0x08)//0x68 { // m_ppps=pout; // m_ppslen=outlen; //pout[0] = 0x68; if(m_bwritevideoinfo==false) { m_ppps = new unsigned char[outlen];//outlen memcpy(m_ppps, pout, outlen); m_ppslen = outlen; } bSPSOrPPS = true; }// else if (pout[0] == 0x06)//SEI// {// // }// else { memcpy(pRealData+nRealDataSize, &outlen, 4); //写入头4个字节==nal内容的长度(H264数据的长度) unsigned char byte0 = pRealData[nRealDataSize+3]; unsigned char byte1 = pRealData[nRealDataSize+2]; pRealData[nRealDataSize+3] = pRealData[nRealDataSize+0]; pRealData[nRealDataSize+2] = pRealData[nRealDataSize+1]; pRealData[nRealDataSize+1] = byte1; pRealData[nRealDataSize+0] = byte0; nRealDataSize += 4; memcpy(pRealData+nRealDataSize, pout, outlen); nRealDataSize += outlen; } inlen=inlen-outlen-(pout-pin); pin=pout+outlen; } } while (bend!=true); if (m_bwritevideoinfo==false&&m_ppps&&m_psps) { // PPS开端的0过滤,否则VLC可能播放不进去 [12/22/2015 Dingshuai] int nPPSSize = m_ppslen; int nZeroCount = 0; for (int nI = nPPSSize-1; nI>=0; nI--) { if (m_ppps[nI] == 0x00) { nZeroCount++; } else { break; } } m_ppslen = m_ppslen-nZeroCount; WriteH264SPSandPPS(m_psps,m_spslen,m_ppps,m_ppslen,nWidth,nHeight); m_bwritevideoinfo = true; } if (m_bwritevideoinfo==false||nRealDataSize<=0 ) { return 0;//获取sps pps失败 }// if(/*bSPSOrPPS*/pout[0]==0x67 || pout[0]==0x68)// {// return 0;// } WriteH264Frame(pRealData, nRealDataSize, keyframe, nTimestamp);//左移4单位,加上数据长度头? if (pRealData) { delete []pRealData; pRealData = NULL; } return true;}其中find_nal_unit()函数是从H264帧中剖析出以00 00 00 01和00 00 01结尾的NAL单元,而后间接填充成该NAL单元的长度,留神字节程序为大端程序://写入头4个字节==nal内容的长度(H264数据的长度) ...

March 2, 2023 · 2 min · jiezi

关于c++:C2-C到C的升级

C与C++的关系C++继承了所有的C个性C++在C的根底上提供了更多的语法和个性C++设计指标是运行效率与开发效率的对立 C++强调语言的实用性C++所有的变量都能够再须要应用时在定义C语言中变量都必须在作用域开始的地位定义 register 关键字申请编译器将局部变量存储于寄存器中在C++中仍然反对register关键字 C++编译器有本人的优化形式C语言中无奈获取register变量的地址C++中能够获得register变量的地址 C到C++的降级在C语言中,反复定义多个同名的全局变量是非法的C语言中,多个同名的全局变量会被链接到全局数据区的同一个地址空间上在C++中,不容许多个同名的全局变量 #include <stdio.h>int g_v;//int g_v;int main(int argc, char *argv[]) { printf("Begin..\n"); int c = 0; for (int i = 1; i <= 3; i++) { for (int j = 1; j <= 3; j++) { c += i * j; } } printf("c = %d\n",c); register int a = 0; printf("c=%p\n",&a); printf("end....\n"); return 0;}struct关键字的增强C语言中的struct定义了一组变量的汇合C语言中struct定义的标识符并不是一种新的类型C++中的struct用于定义一个全新的类型C代码: typedef struct _tag_student Student;struct _tag_student{ const char* name; int age; };C++代码: ...

March 1, 2023 · 1 min · jiezi

关于c++:SkeyePlayer-RTSP播放器库API接口说明

概述libSkeyePlayer实现对RTSP直播流进行实时采集和解码显示,稳固,高效,低延时;解码可采纳intel硬件解码和软件解码两种形式,能实时进行录像和快照抓图,OSD叠加等性能。 API接口函数定义 int SkeyePlayer_Init();函数阐明:播放器初始化,播放器应用之前调用;参数阐明:  void SkeyePlayer_Release();函数阐明:播放器资源开释,播放器不再应用当前调用;参数阐明: int SkeyePlayer_OpenStream(const char url, HWND hWnd, RENDER_FORMAT renderFormat, int rtpovertcp, const char username, const char password, MediaSourceCallBack callback, void userPtr, bool bHardDecode);函数阐明:播放器开始进行流播放;返回值为以后播放的通道ID,该ID在进行推流时须要用到;参数阐明:Url:[IN] 字符串类型,示意以后要播放的流地址,Eg: rtsp://127.0.0.1:554/stream.sdpHWnd: [IN] 窗口句柄类型,示意为以后播放器将显示的窗口的句柄;renderFormat:[IN] 播放渲染类型,详见RENDER_FORMAT构造;Rtpovertcp:[IN] 整数型,拉取流的传输模式,0=udp, 1=tcpUsername:[IN] 字符串,拜访流的用户名(如果存在)Password:[IN] 字符串,拜访流的用户名(如果存在)Callback:[IN] 播放器回调音视频数据回调函数userPtr:[IN] 用户自定义传入数据bHardDecode:[IN] 是否采纳硬件解码 1=是,0=否 void SkeyePlayer_CloseStream(int channelId);函数阐明:播放器进行流播放;参数阐明:channelId [IN] 以后播放的流通道ID,该ID是SkeyePlayer_OpenStream函数关上流的返回值;int SkeyePlayer_SetFrameCache(int channelId, int cache);函数阐明:播放器设置以后流播放缓存帧数;参数阐明:channelId [IN] 以后播放的流通道ID,该ID是SkeyePlayer_OpenStream()函数关上 流的返回值;cache [IN] 以后通道的流播放设置的缓存的视频帧数,Eg: 缓存10帧,则cache = 10; int SkeyePlayer_SetShownToScale(int channelId, int shownToScale);函数阐明:播放器按比例进行显示;参数阐明:channelId [IN] 以后播放的流通道ID,该ID是SkeyePlayer_OpenStream()函数关上 流的返回值;shownToScale [IN] 0=整个窗口区域显示,1=按比例显示; ...

March 1, 2023 · 2 min · jiezi

关于c++:SkeyePlayer-RTSP-Windows播放器抓图代码重构

SkeyePlayer RTSP Windows端(下文简称:SkeyePlayer)播放器之前抓图代码次要通过OpenCV来实现,且数据格式转换的效率过于低下;故而在过后的代码中采纳线程机制来解决抓图导致视频播放时卡顿的问题;而最新版的SkeyePlayer为了精简代码也为了进步抓图效率,咱们采纳ffmpeg进行抓图,为了保障视频播放的流畅性,线程机制咱们依然保留。 采纳ffmpeg进行抓图代码如下 // 抓图函数实现int take_snapshot(char *file, int w, int h, uint8_t *buffer, AVPixelFormat Format){ char *fileext = NULL; enum AVCodecID codecid = AV_CODEC_ID_NONE; struct SwsContext *sws_ctx = NULL; AVPixelFormat swsofmt = AV_PIX_FMT_NONE; AVFrame picture = {}; int ret = -1; AVFormatContext *fmt_ctxt = NULL; AVOutputFormat *out_fmt = NULL; AVStream *stream = NULL; AVCodecContext *codec_ctxt = NULL; AVCodec *codec = NULL; AVPacket packet = {}; int retry = 8; int got = 0; // init ffmpeg av_register_all(); fileext = file + strlen(file) - 3; if (_stricmp(fileext, "png") == 0) { codecid = AV_CODEC_ID_APNG; swsofmt = AV_PIX_FMT_RGB24; } else { codecid = AV_CODEC_ID_MJPEG; swsofmt = AV_PIX_FMT_YUVJ420P; } AVFrame video; int numBytesIn; numBytesIn = av_image_get_buffer_size(Format, w, h, 1); av_image_fill_arrays(video.data, video.linesize, buffer, Format, w, h, 1); video.width = w; video.height = h; video.format = Format; // alloc picture picture.format = swsofmt; picture.width = w > 0 ? w : video.width; picture.height = h > 0 ? h : video.height; int numBytes = av_image_get_buffer_size(swsofmt, picture.width, picture.height , 1); buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t)); av_image_fill_arrays(picture.data, picture.linesize, buffer, swsofmt, picture.width, picture.height, 1); // scale picture sws_ctx = sws_getContext(video.width, video.height, (AVPixelFormat)Format/*video->format*/, picture.width, picture.height, swsofmt, SWS_FAST_BILINEAR, NULL, NULL, NULL); if (!sws_ctx) { //av_log(NULL, AV_LOG_ERROR, "could not initialize the conversion context jpg\n"); goto done; } sws_scale(sws_ctx, video.data, video.linesize, 0, video.height, picture.data, picture.linesize); // do encoding fmt_ctxt = avformat_alloc_context(); out_fmt = av_guess_format(codecid == AV_CODEC_ID_APNG ? "apng" : "mjpeg", NULL, NULL); fmt_ctxt->oformat = out_fmt; if (!out_fmt) { //av_log(NULL, AV_LOG_ERROR, "failed to guess format !\n"); goto done; } if (avio_open(&fmt_ctxt->pb, file, AVIO_FLAG_READ_WRITE) < 0) { //av_log(NULL, AV_LOG_ERROR, "failed to open output file: %s !\n", file); goto done; } stream = avformat_new_stream(fmt_ctxt, 0); if (!stream) { //av_log(NULL, AV_LOG_ERROR, "failed to create a new stream !\n"); goto done; } codec_ctxt = stream->codec; codec_ctxt->codec_id = out_fmt->video_codec; codec_ctxt->codec_type = AVMEDIA_TYPE_VIDEO; codec_ctxt->pix_fmt = swsofmt; codec_ctxt->width = picture.width; codec_ctxt->height = picture.height; codec_ctxt->time_base.num = 1; codec_ctxt->time_base.den = 25; codec = avcodec_find_encoder(codec_ctxt->codec_id); if (!codec) { //av_log(NULL, AV_LOG_ERROR, "failed to find encoder !\n"); goto done; } if (avcodec_open2(codec_ctxt, codec, NULL) < 0) { //av_log(NULL, AV_LOG_ERROR, "failed to open encoder !\n"); goto done; } while (retry-- && !got) { if (avcodec_encode_video2(codec_ctxt, &packet, &picture, &got) < 0) { //av_log(NULL, AV_LOG_ERROR, "failed to do picture encoding !\n"); goto done; } if (got) { ret = avformat_write_header(fmt_ctxt, NULL); if (ret < 0) { //av_log(NULL, AV_LOG_ERROR, "error occurred when opening output file !\n"); goto done; } av_write_frame(fmt_ctxt, &packet); av_write_trailer(fmt_ctxt); } } // ok ret = 0;done: avcodec_close(codec_ctxt); if (fmt_ctxt) { avio_close(fmt_ctxt->pb); } avformat_free_context(fmt_ctxt); av_packet_unref(&packet); sws_freeContext(sws_ctx); av_free(buffer); return ret;}借助ffmpeg弱小的视频解决和转换性能,咱们能够将一帧图像转换成任意格局的图片,当然如代码所示咱们只选择性地反对了“jpeg”和“png”两种格局的图片格式;采纳ffmpeg抓图的步骤分两步: ...

March 1, 2023 · 3 min · jiezi

关于c++:SkeyePlayer-RTSP-Windows播放器OSD字幕叠加接口方法和使用效果全解析

SkeyePlayer RTSP Windows播放器新增OSD字幕叠加接口办法,这个接口和码率信息显示接口办法相似,都是调用FFRender库的接口实现的多OSD叠加,上面解说下该办法的调用和注意事项; OSD叠加办法申明如下: LIB_SkeyePLAYER_API int SkeyePlayer_ShowOSD(int channelId, int show, Skeye_PALYER_OSD osd);其中,channelId: 播放器通道ID,标识以后的播放器实例;show:标识是否显示OSD叠加,0=不事实 1=显示osd:显示信息填充构造,定义如下: typedef struct tagSkeye_PALYER_OSD { char stOSD[1024]; //OSD字幕信息 DWORD alpha; //通明通到0-255 0=通明 255=齐全不通明DWORD color; //RGB(0xf9,0xf9,0xf9) DWORD shadowcolor; //OSD背景色彩RGB(0x4d,0x4d,0x4d) 全为0背景通明 RECT rect; //OSD基于图像右上角显示区域 int size; //OSD字体的大小 }Skeye_PALYER_OSD; 留神:osd字幕叠加通过”\r\n“结束符进行换行,一行的长度不能超过128个字节,总的OSD叠加不能超过1024个字节。其中OSD大小设置只有D3D渲染模式能力失效; 咱们曾经对OSD叠加的接口有所理解,上面咱们来写一段调用代码来看看成果: //OSD Example Skeye_PALYER_OSD osd; osd.alpha = 255; osd.size = 35; osd.color = RGB(255,0,255); osd.rect.left = 10; osd.rect.right = 5000; osd.rect.top = 100; osd.rect.bottom = 800; osd.shadowcolor = RGB(0,0,0); char* ss = "这是SkeyePlayer-RTSP-Win播放器 \r\n的字幕叠加接口的成果!!!\r\n以\"\\r\\n\"为换行完结符号\r\n留神:每行的长度不能超过128个字节\r\n总的OSD长度不能超过1024个字节"; strcpy(osd.stOSD ,ss); SkeyePlayer_ShowOSD(m_ChannelId, 1, osd);如上代码段所示,次要对OSD_PLAYER_OSD构造的参数进行设置,就能失去咱们想要的成果,如下图所示:(1)GDI显示[外链图片转存中...(img-6MyHsd96-1652407820539)] ...

March 1, 2023 · 1 min · jiezi

关于c++:SkeyePlayer-RTSP-Windows播放器D3DGDI的几种渲染方式的选择区别-2

SkeyePlayer RTSP windows播放器反对D3D和GDI两种渲染形式,其中D3D反对格局如下: DISPLAY_FORMAT_YV12 DISPLAY_FORMAT_YUY2 DISPLAY_FORMAT_UYVY DISPLAY_FORMAT_A8R8G8B8 DISPLAY_FORMAT_X8R8G8B8 DISPLAY_FORMAT_RGB565 DISPLAY_FORMAT_RGB555 GDI反对格局如下: DISPLAY_FORMAT_RGB24_GDIGDI渲染形式则是咱们熟知的采纳GDI进行图像绘制,其劣势就是通用性强,只有是目前罕用的windows操作系统基本上都反对;其劣势就是效率比拟低下,也只反对RGB24一种色调格局显示;D3D渲染形式则刚好相同,其劣势就是效率比拟高,反对多种色调格局进行渲染;劣势就是通用性较差,windows零碎必须要反对D3D才能够应用,须要肯定的硬件撑持。 GDI渲染格局界面抉择如下: D3D渲染格局界面抉择如下: 从界面上能够看出GDI形式的OSD字幕叠加比D3D形式有显著的区别,从SkeyePlayer渲染库FFRender的接口能够看进去两种形式尽管出现形式相似,然而接口是齐全不同的,这再渲染形式和OSD叠加的时候都能够看出显著的区别,这些区别再后文OSD叠加解说的时候也会提到;二者的性能比拟如下: SkeyePlayer拉的同样的流,上图是GDI渲染形式,下图是D3D渲染形式;咱们能够看出GDI形式所耗费的CPU资源均比D3D形式要高,然而不耗GPU,正好相同, D3D形式占用的CPU资源低,然而比拟耗GPU; 综上所述,应用SkeyePlayer抉择渲染形式的时候,如果电脑的配置较低抉择D3D会占用较低的资源,如果电脑不反对D3D渲染的话也只能抉择GDI渲染形式;当然如果机器反对D3D也配置还能够的话两种形式都没有显著的区别;

March 1, 2023 · 1 min · jiezi

关于c++:SkeyeLive中DShow本地采集视频参数设置及可能出现的错误提示详解

在近期公布的SkeyeLive多窗口版本中,因为界面的局限性,选择性的将本地采集的音视频参数设置在界面上剔除掉了(临时还没想好放在哪里,后续版本会在界面调整后增加),大家能够查看SkeyeLive源码中的StartDSCapture函数的参数设置代码进行相应设置;也应近期SkeyeSMS流媒体服务器群(QQ群:102644504,欢送大家进群交换,任何技术问题,在所知的范畴内都能给予解答)中大家提到的参数设置的谬误提醒以及参数该如何设置的问题,上面我将就参数设置的具体细节和代码关联做具体介绍。 一、查看设施属性 在Windows零碎中大多数音视频采集设施都能反对通过DirecShow进行捕捉,在捕捉之前,咱们首先应该理解设施的性能参数,如:视频采集设施的长,宽,色调格局,图像格式等,以及音频设备的采样率,采样位宽,声道数等;要理解这些,咱们能够简略的通过一款微软提供的DShow设施采集小工具“amcap.exe”查看,如下图所示:在“设施”菜单栏中能够查看到枚举的以后零碎中所有的音视频采集设施(包含虚构设施),抉择一个设施即可进行捕捉,而后在“选项”一栏中能够进行“预览”或者相应的参数设置,如下图所示:其中,“视频捕捉接口”项是咱们常常用到的查看视频的长,宽,色调格局,图像格式等参数的设施信息,如下图所示:此外,还有其余一些图像参数的设置,有趣味的也能够理解下;当然,咱们齐全能够通过代码来实现这个小工具的性能,在我的另一片文章“SkeyLive中DirectShow采集音视频流程及几种采集形式介绍”中有具体的阐明,这里就不做过多赘述。 二、DShow采集参数设置 参数设置咱们定义了一个构造,其原型如下: // 设施捕捉参数信息typedef struct tagDEVICE_CONFIG_INFO{ int nVideoId;//视频 ID -1==不必,0-n == id int nAudioId;//音频id -1==不必,0-n == id int nDeviceId;//设施序号 1,2,3,4,5.... struct tagVideoInfo { int nWidth;//视频高度 int nHeight;//视频宽度 int nVideoWndId;//视频关联窗口ID int nFrameRate;//视频帧率 char strDataType[64];//标识 色调格局 数据类型,如"YUY2" "YUYV" "UYVY等" int nRenderType;//出现形式 int nPinType;//捕捉口 }VideoInfo; struct tagAudioInfo { int nChannaels;//通道数 int nBytesPerSample;//采样位数 int nSampleRate;//采样率 DWORD nAudioBufferType;//音频缓存区的大小 int nPinType;//捕捉口 }AudioInfo;}DEVICE_CONFIG_INFO;构造中参数均有具体阐明,这里就比拟重要的色调格局进行举例说明:-> 色调格局(strDataType)色调格局是指DShow捕捉输入的图像数据的色调格局,次要是RGB和YUV两种,这个参数很重要,在显示和编码时咱们都会用到,设置不当可能导致色调显示不对甚至未知的程序解体(个别是指针越界),在SkeyeStreamPusher中反对两种罕用的格局YUY2和RGB24,次要是编码时须要进行格局转换或者分支解决。 咱们在程序中中进行了简略的设施配置: //1. 咱们来简略配置一个设施信息 m_sDevConfigInfo.nDeviceId = 1; m_sDevConfigInfo.nVideoId = nCamId;//摄像机视频捕捉ID m_sDevConfigInfo.nAudioId = nAudioId;//音频捕捉ID m_sDevConfigInfo.VideoInfo.nFrameRate = nFps; m_sDevConfigInfo.VideoInfo.nWidth = nVideoWidth; m_sDevConfigInfo.VideoInfo.nHeight = nVideoHeight; strcpy_s(m_sDevConfigInfo.VideoInfo.strDataType, 64, "YUY2"); m_sDevConfigInfo.VideoInfo.nRenderType = 1; m_sDevConfigInfo.VideoInfo.nPinType = 1; m_sDevConfigInfo.VideoInfo.nVideoWndId = 0; m_sDevConfigInfo.AudioInfo.nAudioBufferType = 4096; m_sDevConfigInfo.AudioInfo.nBytesPerSample = 16; m_sDevConfigInfo.AudioInfo.nSampleRate = 16000;//44100; m_sDevConfigInfo.AudioInfo.nChannaels = 2; m_sDevConfigInfo.AudioInfo.nPinType = 2; //初始化Pusher构造信息 memset(&m_mediainfo, 0x00, sizeof(Skeye_MEDIA_INFO_T)); m_mediainfo.u32VideoCodec = Skeye_SDK_VIDEO_CODEC_H264;//0x1C; m_mediainfo.u32VideoFps = nFps; m_mediainfo.u32AudioCodec = Skeye_SDK_AUDIO_CODEC_AAC; m_mediainfo.u32AudioChannel = 2; m_mediainfo.u32AudioSamplerate = 16000;//44100;如果参数设置不胜利,将会呈现谬误(或者正告)提醒,有一种提醒是设施不反对外部显示(经测试某些虚构设施可能呈现),这在DShow采集库底层代码中曾经做过解决,进行内部关上窗口显示,不影响采集;另有一种提醒为“应用默认参数”,这种提醒就表明咱们设置的参数失败了,可能是设施不反对这种色调格局或者分辨率;当然,捕捉设施通常是胜利的,然而,要留神默认的色调格局和分辨率将有可能和咱们的设置参数不统一,这时候就要理解设施的具体参数,而后在编码时进行相应解决,否则可能导致编码失败,或者编码后推送进去的视频色调不对。参数设置代码如下: ...

February 28, 2023 · 1 min · jiezi

关于c++:SkeyeLive中DirectShow采集音视频流程及几种采集方式介绍

前段时间SkeyeLive凋谢了DirectShow采集库,这个库底层采纳DirectShow SDK的接口实现音视频的预览(播放)和采集;很多人可能还不太理解这个封装库的回调形式和之前的DShow线程采集形式有什么不同,或者说对DirectShow的采集流程还不太熟悉,上面我将就Windows平台下用应用DirectShow的过滤器(滤波器)进行流媒体开发的前端采集局部进行简要介绍,如果大家想深刻的学习和摸索,举荐大家去看看《Visual C++音频/视频解决技术及工程实际》这本书,第9章有具体的流程解说。 一、枚举采集设施 应用采集设施前,须要首先确定零碎曾经装置的采集设施:视频、音频采集设施。零碎设施枚举器为按类型枚举已注册在零碎中的滤波器提供了对立的办法。而且它可能辨别不同的硬件设施,即使是同一个滤波器反对它们。这对那些应用Windows驱动模型、KSProxy Filter的设施来说是十分有用的,零碎设施枚举器对它们按不同的设施实例进行看待。 当利用零碎设施枚举器查问设施的时候,零碎设施枚举器为特定类型的设施(如音频捕捉和视频压缩)生成了一张枚举表(Enumerator)。类型枚举器(Category Enumerator)为每个这种类型的设施返回一个Moniker,类型枚举器主动把每种即插即用的设施蕴含在内。 调用规范办法CoCreateInstance生成零碎设施枚举器(Device Enumerator),类标识(CLSID)为CLSID_SystemDeviceEnum,办法如下: CAMERA_LIST_T *CCameraDS::GetCameraList(){ if (NULL != cameraList.pCamera || cameraList.count > 0) { return &cameraList; } if (NULL == cameraList.pCamera) { cameraList.count = 0; // enumerate all video capture devices CComPtr<ICreateDevEnum> pCreateDevEnum; HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pCreateDevEnum); CComPtr<IEnumMoniker> pEm; hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEm, 0); if (hr != NOERROR) { return &cameraList; } pEm->Reset(); CAMERA_INFO_T *pCameraList = cameraList.pCamera; CAMERA_INFO_T *pCameraInfo = NULL; ULONG cFetched; IMoniker *pM = NULL; while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK) { pCameraInfo = new CAMERA_INFO_T; memset(pCameraInfo, 0x00, sizeof(CAMERA_INFO_T)); IPropertyBag *pBag=0; hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag); if(SUCCEEDED(hr)) { VARIANT var; var.vt = VT_BSTR; hr = pBag->Read(L"FriendlyName", &var, NULL); //还有其余属性,像形容信息等等... if(hr == NOERROR) { //获取设施名称 WideCharToMultiByte(CP_ACP,0,var.bstrVal,-1,pCameraInfo->friendlyName, sizeof(pCameraInfo->friendlyName) ,"",NULL); SysFreeString(var.bstrVal); } pBag->Release(); cameraList.count++; { pCameraList = cameraList.pCamera; if (NULL == cameraList.pCamera) cameraList.pCamera = pCameraInfo; else { while (NULL != pCameraList->pNext) { pCameraList = pCameraList->pNext; } pCameraList->pNext = pCameraInfo; } } } pM->Release(); } pCreateDevEnum = NULL; pEm = NULL; } return &cameraList;}这是视频设施枚举局部,当然音频也是一样的,只须要把CreateClassEnumerator函数的第一个参数换成CLSID_AudioInputDeviceCategory即可。留神:调用ICreateDevEnum::CreateClassEnumerator办法生成类型枚举器,参数为用户想要失去的类的ID(CLSID),该办法返回一个IEnumMoniker接口指针。如果指定的类型是空的或不存在,则函数ICreateDevEnum::CreateClassEnumerator将返回S_FALSE而不是错误代码,同时IEnumMoniker指针也是空的,这就要求咱们在调用CreateClassEnumerator的时候明确用S_OK进行比拟而不应用宏SUCCEEDED;(扯远了...)而在SkeyeLive中还提供了另外一种枚举音频设备的形式,那就是采纳DirectSound的枚举形式:DirectSoundCaptureEnumerate这个函数来实现的,须要留神,这个函数枚举出的设施GUID有可能是空的,设施名称可能表象为”声卡主设施驱动“,经测试:这个设施是不能用于采集,也是不存在的,枚举过程中应该抛弃。当然,其实DirectShow也是封装了底层的DirectSound的接口来实现的COM接口的对立封装。(须要重点阐明的是:枚举设施这一块不是DShow封装库中的代码,这是由咱们EasyDarwin团队的Gavin大神之前的DShow采集局部代码中提供的(前身是EasyCamera_win),我只是鸠占鹊巢的给大家解说,向大神致敬~~~哈哈哈!) ...

February 28, 2023 · 5 min · jiezi

关于c++:SkeyeLive同屏直播库库功能介绍和接口说明与调用

SkeyeLive是OpenSKEYE开源流媒体团队开发的一个集采集,编码,RTSP/RTMP推流和流媒体RTSP服务于一身的通用库,目前反对Windows,Android平台,通过SkeyeLive咱们就能够防止接触到稍显简单的音视频源采集,编码和流媒体推送以及RTSP/RTP/RTCP服务流程,只须要调用SkeyeLive的几个API接口,就能轻松、稳固地把流媒体音视频数据推送给SkeyeSMS服务器以及公布RTSP服务,RTSP服务反对组播和单播两种模式,可用于同屏直播,延时在300ms以内。 libSkeyeLive API接口阐明: · SkeyeLive_Create 接口申明: LIB_SKEYELIVE_API SKEYELIVE_HANDLE SkeyeLive_Create();接口阐明:创立一个SkeyeLive实例句柄,并返回,在性能模块接口调用时都要用到,一个句柄代表一个实例,该函数能够屡次调用,用SkeyeLive_Release接口进行销毁。 · SkeyeLive_Release 接口申明: LIB_SKEYELIVE_API void SkeyeLive_Release(SKEYELIVE_HANDLE handler);接口阐明:销毁一个由SkeyeLive_Create()创立的实例;参数阐明:handler: [IN] SKEYELIVE_HANDLE(void*)类型, 由SkeyeLive_Create()创立的实例句柄 · SkeyeLive_StartCapture 接口申明: LIB_SKEYELIVE_API int SkeyeLive_StartCapture(SKEYELIVE_HANDLE handler, SOURCE_TYPE eSourceType, int nCamId, int nAudioId, SKEYELIVE_HANDLE hCapWnd, int nEncoderType, int nVideoWidth=640, int nVideoHeight=480, int nFps=25, int nBitRate=2048, char* szDataType = "YUY2", //VIDEO PARAM int nSampleRate=44100, int nChannel=2 );接口阐明:开始一个数据源的采集,并初始化编码器;采集胜利返回1,采集失败返回负值,正在采集返回0;参数阐明:handler:[IN] SKEYELIVE_HANDLE(void*)类型, 由SkeyeLive_Create()创立的实例句柄eSourceType:[IN] 数据源类型,参考以下数据结构:typedef enum tagSOURCE_TYPE{ SOURCE_LOCAL_CAMERA = 0,//本地音视频SOURCE_SCREEN_CAPTURE =1,//屏幕捕捉SOURCE_FILE_STREAM = 2, //文件流推送(mp4,ts,flv???)SOURCE_RTSP_STREAM=3,//RTSP流SOURCE_RTMP_STREAM=4,//RTMP流// Any other Source to push}SOURCE_TYPE; ...

February 28, 2023 · 2 min · jiezi

关于c++:SkeyeLive开源流媒体同屏直播软件源码功能框架解析

SkeyeLive是OpenSKEYE开源流媒体团队开发的一款功能丰富的开源PC端流媒体推流拉流直播软件我的项目,目前反对Windows、Android版本,后续将反对ios版本,其中Windows版本的SkeyeLive反对多种源接入,包含Windows摄像头、麦克风、RTSP摄像机、屏幕桌面等,采集后通过x264编码、SkeyePusher推送到OpenSKEYE流媒体服务器进行转发,同时SkeyeLive还反对通过SkeyeRTSPClient拉取OpenSKEYE直播流,进行显示、播放,十分稳固、易用,用户能够基于SkeyeLive我的项目,开发相似于课堂直播、视频对讲等我的项目! 性能解说 一、程序框架SkeyeLive次要包含三个模块:采集、推送和直播,次要性能封装治理类Class CSourceManager中实现,接口非常简单,各模块别离提供开始和完结接口函数,不便界面调用;界面调用接口: //开始捕捉(采集) int StartCapture(SOURCE_TYPE eSourceType, int nCamId, int nAudioId, HWND hCapWnd, char* szURL, int nVideoWidth, int nVideoHeight, int nFps=, int nBitRate); //进行采集 void StopCapture(); //开始推流 int StartPush(char* ServerIp, int nPushPort, char* sPushName, int nPushBufSize = 1024); //进行推流 void StopPush(); //开始播放 int StartPlay(char* szURL, HWND hShowWnd); //进行播放 void StopPlay();1、采集模块采集分为本地音视频采集和RTSP流采集本地音视频次要通过DShow进行采集,函数如下: int CSourceManager::StartDSCapture(int nCamId, int nAudioId,HWND hShowWnd,int nVideoWidth, int nVideoHeight, int nFps, int nBitRate)该函数次要实现本地音视频采集和音视频编码器的初始化(详见SkeyeLive源码),须要留神的是这里的参数设置: (1) 本地采集的视频宽高和x264编码器的宽高需统一,数据格式倡议设为YUY2(程序中默认为“YUY2"),因为在DShow的数据采集线程中须要进行编码前的格局转换(YUY2->I420),如果格局不对立,这里将要重写转换函数; (2) 本地音频采样率默认为16000,这个设置在SkeyePusher中体现最佳,其余采样率还有待测试; (3) 其余设置请参照DEVICE_CONFIG_INFO构造和Encoder_Config_Info构造的具体阐明; ...

February 28, 2023 · 1 min · jiezi

关于c++:SkeyeExPlayerWindows开发之跨语言调用

上面咱们来解说一下对于SkeyeExPlayer接口的调用,次要分为C++和C#两种语言,C++也能够基于VC和QT进行开发,C++以VC MFC框架为例进行解说,C#以Winform框架为例进行解说。 VC开发SkeyeExPlayer 首先建一个基于MFC Dialog的工程,取名叫SkeyeExPlayer,对于界面逻辑的处理过程就不做过多赘述了,大家有趣味的能够去看SkeyeExPlayer我的项目代码;上面咱们解说一下SkeyeExPlayer的调用流程: (1) 关上一个流或文件进行播放 通过SkeyeExPlayer_Open关上一个流或者本地文件,关上当前即播放,不须要调用SkeyeExPlayer_Play办法,调用实现后,留神,比方拉网络流的时候,因为Open函数是非阻塞而立刻返回的,所以,播放过程可能尚未初始化实现,从而获取流的信息可能获取不到,正确的做法是在线程或者计时器外面轮询获取;Open实现后,咱们能够对一些参数进行设置,比方设置OSD和图片叠加,显示模式,音量大小等: // player open file m_player = SkeyeExPlayer_Open(str, m_stcVideoWnd->GetSafeHwnd()); if (m_player) { m_bPlayPause = FALSE; SetTimer(TIMER_ID_PROGRESS, 1000, NULL); m_bOpening = TRUE; //字幕和图片叠加 // // SkeyeExPlayer_SetLogo 设置台标/LOGO // player - 指向 SkeyeExPlayer_Open 返回的 player 对象 // bIsUse - 是否应用水印 1=启用 0=不启用 // ePos - 台标地位:1==leftttop 2==righttop 3==leftbottom 4==rightbottom // eStyle - 水印的格调,见WATERMARK_ENTRY_TYPE申明 // x - 水印左上角地位x坐标 // y - 水印左上角地位y坐标 // width - 宽 // height - 高 // logopath - 水印图片门路 SkeyeExPlayer_SetLogo (m_player, 1, 2, 3, 0, 0, 0, 0, "C:\\Dingshuai\\Project\\Skeyelogo.png"); // SkeyeExPlayer_SetOSD 设置叠加字幕 // player - 指向 SkeyeExPlayer_Open 返回的 player 对象 // bIsUse - 是否应用水印 1=启用 0=不启用 -1=删除 // nMoveType - 挪动类型:0--固定地位,1--从左往右,2--从右往左, // R,G,B - 字体色彩对应三个重量红绿蓝0-255 // x - 字幕显示左上角地位x坐标 // y - 字幕显示左上角地位y坐标 // weight - 字体权重,见如下申明 // /* Font Weights */ // #define FW_DONTCARE 0 // #define FW_THIN 100 // #define FW_EXTRALIGHT 200 // #define FW_LIGHT 300 // #define FW_NORMAL 400 // #define FW_MEDIUM 500 // #define FW_SEMIBOLD 600 // #define FW_BOLD 700 // #define FW_EXTRABOLD 800 // #define FW_HEAVY 900 // #define FW_ULTRALIGHT FW_EXTRALIGHT // #define FW_REGULAR FW_NORMAL // #define FW_DEMIBOLD FW_SEMIBOLD // #define FW_ULTRABOLD FW_EXTRABOLD // #define FW_BLACK FW_HEA // width - 宽 // height - 高 // fontname - 字体名称,如“宋体”“楷体”“隶书”“华文行楷”...... // tittleContent - OSD显示内容 SkeyeExPlayer_SetOSD (m_player, 1, 1, 0, 255, 0, 700, 100, 200, 0, -29, "隶书", "你说你爱了不该爱的人,你的心中满是创痕!"); }(2) 暂停和单步播放 ...

February 28, 2023 · 6 min · jiezi

关于c++:SkeyeExPlayerWindows开发之接口说明

SkeyeExPlayer(windows)接口阐明如下: SkeyeExPlayer_Open 阐明:关上一个媒体流或者媒体文件进行播放,同时返回一个 player 对象指针参数阐明: fileUrl - 文件门路(能够是网络流媒体的 URL) hWnd - Win32 的窗口句柄/其余平台渲染显示设施句柄 返回值: Easy_PlayerPro_Handle 指针类型,指向 SkeyeExPlayer 对象句柄申明如下:SkeyeExPlayer_Handle SkeyeExPlayer_Open(char *fileUrl, EASY_HANDLE hWnd);SkeyeExPlayer_Close 阐明: 敞开播放参数阐明: player - 指向 SkeyeExPlayer_Open 返回的 player 对象 申明如下: void SkeyeExPlayer_Close(SkeyeExPlayer_Handle player);SkeyeExPlayer_Play 阐明:开始播放,留神:媒体流或者文件关上后不须要调用此函数即开始播放,此函数在暂停、单步播放的时候调用,返回失常播放逻辑参数阐明: player - 指向 SkeyeExPlayer_Open 返回的 player 对象 申明如下: void SkeyeExPlayer_Play(SkeyeExPlayer_Handle player);SkeyeExPlayer_StepPlay 阐明:单步播放,一次播放一帧,调用SkeyeExPlayer_Play返回失常播放参数阐明: player - 指向 SkeyeExPlayer_Open 返回的 player 对象申明如下: void SkeyeExPlayer_StepPlay(SkeyeExPlayer_Handle player);SkeyeExPlayer_Pause 阐明:暂停播放,调用SkeyeExPlayer_Play返回失常播放参数阐明: player - 指向 SkeyeExPlayer_Open 返回的 player 对象申明如下:void SkeyeExPlayer_Pause(SkeyeExPlayer_Handle player);SkeyeExPlayer_Seek 阐明:播放进度跳转到指定地位参数阐明: ...

February 28, 2023 · 3 min · jiezi

关于c++:SkeyeExPlayerWindows开发之ffmpeg-log输出报错

SkeyeExPlayer次要基于ffmpeg进行开发,在SkeyeExPlayer开发过程中,曾遇到一个绝对比拟辣手的问题,该问题个别在播放不是很规范的流或者网络状况较差,容易呈现丢帧的状况特地容易呈现; 根本表象在,播放一段时间程序会弹出谬误或者解体,查代码逻辑根本无法查起,且不易重现(比方,我当初就想重现,也未能重现进去--!前面补上),不过在播放没有音频的rtsp流的时候比拟容易呈现,报错定位output.c源文件中,应该是字符串格式化输入的时候出错,通过排查,定位到ffmpeg的日志输入,为了便于查看谬误,我在avformat_open_input()关上流函数前加了日志输入打印函数调用: av_log_set_level(AV_LOG_WARNING); av_log_set_callback(callback); 在callback回调函数中输入的日志打印,从而能看到FFMPEG的外部报错信息,而屏蔽这两句,尽管在丢帧的状况下视频卡帧了,然而上文提到的报错的问题却不再呈现了,所以,咱们猜想在ffpeg外部应该存在一个中央的日志打印呈现谬误的中央,当然也有可能是我的调用办法不合理导致的(谁晓得呢),总之,问题是临时失去了解决,后续找到真正报错的问题在补上。

February 28, 2023 · 1 min · jiezi

关于c++:SkeyeExPlayerWindows开发系列之解决ffmpeg接口调用卡住的问题

在SkeyeExPlayer的开发过程中,经测试发现ffmpeg的读取网络流以及网络数据的接口都有较大概率呈现阻塞的问题,ffmpeg也提供了设置阻塞回调或者设置超时等形式来跳出阻塞而不会导致接口永恒卡住;而在某些时候,比方,网络断开工夫过长的时候,这个时候阻塞回调将不在有用而且阻塞的接口也不再返回数据,呈现"永久性"假死的状况,针对这些问题,本文将对其解决形式进行一一解说。 1.播放器完结时接口导致线程卡住针对该问题,咱们通常能够在ffmpeg的阻塞回调函数中设置退出标记来解决,如下代码所示: //播放器退出状态标记,解除阻塞 if(pPlayer->player_status & PS_CLOSE) { return AVERROR_EOF; }2.播放器因为接口卡住而呈现断线这个问题也就是咱们通常状况下所说的断线重连的解决,断线重来你分两步走,第一步,判断呈现断线的机会;第二布,断线进行重连的解决;第一步,通常认定读取的网络流数据失落肯定的工夫为断线,阻塞回调函数解决如下: int64_t curTime = av_gettime(); //5s超时退出 if ((curTime - pPlayer->cur_read_time) > pPlayer->reconnect_time * 1000 * 1000)//5秒一次的重连 { pPlayer->error_flag = 1; char sErrorInfo[100] = { 0, }; sprintf(sErrorInfo, "interrupt_cb() enter,流已断开,正在尝试重连......curtime=%lld\n", curTime); OutputDebugStringA(sErrorInfo); return AVERROR_STREAM_NOT_FOUND;//AVERROR_EOF; }cur_read_time在初始读取网络流时去一个以后的工夫戳,以及av_read_frame时每一帧进行工夫戳的更新,如果过肯定的工夫仍未更新该值,咱们则认定网络曾经断开,置error_flag =1进行重连,重连过程如下代码所示: while (!(player->player_status & PS_CLOSE)) { usleep(1000*1000); if (player->error_flag>0)//must be sth error { if (player->player_status & PS_CLOSE) goto error_handler; player->b_ready = 0; player_pause(player); usleep(500*1000); if (player->player_status & PS_CLOSE) goto error_handler; int64_t media_duration = -1; int64_t media_seek_pos = -1; if (player->avformat_context) media_duration = (player->avformat_context->duration * 1000 / AV_TIME_BASE); render_getparam(player->render, PARAM_MEDIA_POSITION, &media_seek_pos); if (media_seek_pos > 0) media_seek_pos += 500; if (player->acodec_context) avcodec_close(player->acodec_context); player->acodec_context = NULL; if (player->vcodec_context) avcodec_close(player->vcodec_context); player->vcodec_context = NULL; if (player->avformat_context) { avformat_close_input(&player->avformat_context); avformat_free_context(player->avformat_context); } player->avformat_context = NULL; //++ for avdevice char *url = player->file_url; AVInputFormat *fmt = NULL; void *win = player->render_hwnd; player->avformat_context = avformat_alloc_context(); player->avformat_context->interrupt_callback.callback = interrupt_cb; player->avformat_context->interrupt_callback.opaque = player; // open input file AVDictionary *options = NULL; //++ for find trsp if ((strstr(url, "rtsp") == url) || (strstr(url, "RTSP") == url)) { if (player->link_mode == STREAM_LINK_TCP) av_dict_set(&options, "rtsp_transport", "tcp", 0); else av_dict_set(&options, "rtsp_transport", "udp", 0); } //-- for find trsp player->cur_read_time = av_gettime(); if (avformat_open_input(&player->avformat_context, url, fmt, &options) != 0) { continue; //goto error_handler; } if (player->player_status & PS_CLOSE) goto error_handler; // find stream info if (avformat_find_stream_info(player->avformat_context, NULL) < 0) { continue; //goto error_handler; } if (player->player_status & PS_CLOSE) goto error_handler; // set current audio & video stream player->astream_index = -1; reinit_stream(player, AVMEDIA_TYPE_AUDIO, 0); player->vstream_index = -1; reinit_stream(player, AVMEDIA_TYPE_VIDEO, 0); // for audio if (player->astream_index != -1) { arate = player->acodec_context->sample_rate; aformat = player->acodec_context->sample_fmt; alayout = player->acodec_context->channel_layout; //++ fix audio channel layout issue if (alayout == 0) { alayout = av_get_default_channel_layout(player->acodec_context->channels); } //-- fix audio channel layout issue } // for video if (player->vstream_index != -1) { vrate = player->avformat_context->streams[player->vstream_index]->r_frame_rate; if (vrate.num / vrate.den >= 100) { vrate.num = 25; vrate.den = 1; } player->vcodec_context->pix_fmt = vformat; width = player->vcodec_context->width; height = player->vcodec_context->height; }#if 0 // open render player->render = render_open(ADEV_RENDER_TYPE_WAVEOUT, arate, (AVSampleFormat)aformat, alayout, player->render_mode/*VDEV_RENDER_TYPE_GDI*//*VDEV_RENDER_TYPE_D3D*/, win, vrate, vformat, width, height, player->speed); if (player->vstream_index == -1) { int effect = VISUAL_EFFECT_WAVEFORM; render_setparam(player->render, PARAM_VISUAL_EFFECT, &effect); }#endif if (player->player_status & PS_CLOSE) goto error_handler; player->b_ready = 1; } }3.avformat_open_input以及av_read_frame接口呈现永久性阻塞的解决 ...

February 28, 2023 · 2 min · jiezi

关于c++:SkeyeExPlayer-RTSP-RTMP-HLS-HTTP全功能流媒体播放器开发计划

目前OpenSKEYE团队筹备推出一款全平台的面向多种流和媒体的全能播放器SkeyeExPlayer,上面列出SkeyeExPlayer(for Windows)的开发计划; SkeyeExPlayer开发将满足以下需要: .反对rtsp/ rtmp/ hls/ http协定,反对大部分媒体文件的播放. 视频按比例显示,扩大显示(充斥整个窗口区域),全屏显示.视频osd叠加性能,加载台标,字幕,画中画缩放,丑化渲染,特效等. 反对视频截图保留.网络直播流反对本地视频录像保留.反对音量调整/静音.rtsp反对tcp/udp.反对网络断线重连.文件播放反对播放地位管制(seek).文件播放反对快放/慢放 (2倍/4倍/8倍/16倍/32倍/64倍, 1/2倍,1/4倍,逐帧).文件播放反对暂停/持续/进行.文件播放反对播完告诉(例如文件播放实现,弹出信息框通知用户播放实现).音频反对RAW PCM / G711 / G726 / AAC等;视频反对H.264/H.265.局域网内网络播放演示小于500ms.网络播放和文件播放放弃视音频同步初步预计开发周期两周左右,公布能够不便调用的Skeye系列接口API和残缺调用程序,敬请期待。

February 28, 2023 · 1 min · jiezi

关于c++:nodejs实现国标GB28181设备接入sip服务器解决方案SkeyeVSS国标视频云平台

GB28181接入服务器是SkeyeVSS接入GB28181设施/平台的信令交互服务器,GB28181将 SIP定位为联网零碎的次要信令根底协定,并利用 SIP协定的无关扩大,实现了对非会话业务的兼顾,例如,对报警业务、历史视音频回放、下载等的反对。目前有GB28181-2011和 GB28181-2016两个版本。 GB28181接入服务器对接入零碎的GB28181设施的治理,全副通过一个20位的设施ID号来治理;以SIP协定为载体,以REGISTER、INVITE、MESSAGE等命令实现与28181设施和GB28181流媒体服务器的交互。 随着node.js社区的一直壮大,借助其弱小的事件驱动和IO并发能力,曾经衍生出了很多很弱小的模块(包),实现各种各样的性能,使得服务端程序开发变得非常容易,习惯了 C/C++编程的程序员相对会感到非常诧异,因为竟然有一种语言开发能够如此高效且简略(PS: 我也就刚学习一个月node.js而已- --!);而本文将要解说的是一种通过node.js实现接入国标设施以及平台的sip信令服务器的计划。筹备工作首先,下载node.js并装置,windows,linux平台均反对; 最好有一个比拟弱小的JS编辑器或者IDE,我举荐一个非常弱小且轻量级的IDE兼编辑神器Visual Studio Code。而后,相熟npm管理工具的命令,通过npm装置各个须要依赖的node.js模块,其中最重要的sip模块,通过如下命令装置: npm install sipnode.js领有弱小的包管理工具,能够通过如下命令搜寻咱们可能须要的node.js模块: npm search xxx如下图所示:其余node.js相干学习大家感兴趣能够在网上找到非常丰盛的材料,比方举荐一本比拟好书《深入浅出node.js》, 当然最好的倡议是:看个毛线的书,老夫都是间接撸代码! 国标接入流程1 承受上级的注册和登记首先,咱们须要建设一个sip服务来检测和承受上级设施或者平台的注册命令的解决,如下代码所示: sip.start(option, async request => { switch (request.method) { case common.SIP_REGISTER: this.emit('register', request); break; case common.SIP_MESSAGE: this.emit('message', request); break; case common.SIP_INVITE: this.emit('invite', request); break; case common.SIP_ACK: this.emit('ack', request); break; case common.SIP_BYE: this.emit('bye', request); break; default: console.log('unsupported: ' + request.method); break; } });而后,sip服务接管设施端注册申请,回调函数中进行解决: case common.SIP_REGISTER: try { const username = sip.parseUri(request.headers.to.uri).user; const userinfo = config.userinfos[username]; const session = { realm: config.server.realm }; if (!userinfo) { sip.send(digest.challenge(session, sip.makeResponse(request, 401, common.messages[401]))); this.session_.set(username,session); return; } else { if(!this.session_.has(username)){ this.session_.set(username,session); } userinfo.session = userinfo.session || this.session_.get(username); if (!digest.authenticateRequest(userinfo.session, request, { user: username, password: userinfo.password })) { sip.send(digest.challenge(userinfo.session, sip.makeResponse(request, 401, common.messages[401]))); this.session_.set(username,userinfo.session); return; } else { this.session_.delete(username); if(request.headers.expires === '0'){ this.emit('unregister', request); } else{ this.emit('register', request); } let response = sip.makeResponse(request, 200, common.messages[200]); sip.send(response); } } } catch (e) { //输入到控制台 console.log(e); } break;如上代码所示,依据国标gb28181规范解决逻辑如下:1) SIP 代理向SIP 服务器发送REGISTER 申请,申请中未蕴含Authorization 字段;SIP 服务器向SIP 代理发送响应401,并在响应的音讯头WWW_Authenticate 字段中给出适宜SIP 代理的认证体制和参数;2) SIP 代理从新向SIP 服务器发送REGISTER 申请,在申请的Authorization 字段给出信赖书,蕴含认证信息;SIP 服务器对申请进行验证,如果查看出SIP 代理身份非法,向SIP 代理发送胜利响应200 OK ,如果身份不非法则发送拒绝服务应答。值得注意的是,有些国标设施接入并不遵循以上注册逻辑,这种多见于老旧的国标设施或者平台,其注册甚至都不会携带认证信息,而是通过双向注册实现验证。 ...

February 28, 2023 · 4 min · jiezi

关于c++:用于双目重建中的GPU编程juliacuda

作者:京东科技 李大冲 一、Julia是什么julia是2010年开始面世的语言,作为一个10后,Julia必然有前辈们没有的特点。Julia被冀望塑造成原生的有C++的运行速度、python的易交互性以及胶水性。最重要的是,julia也是nVidia-cuda官网选中的接口语言,这点让cuda编程变得略微容易一些。 二、我的项目背景双目平面视觉是比拟惯例获取深度信息的算法。这是一种模拟人的双眼的办法,依据对同一个点在不同的视角进行察看,并在不同的察看视角上的察看差别推算出深度,这在图1中更清晰一点。被动双目的毛病是须要纹理特色去判断左右相机里是否为同一个点。也就是,当没有问题、纹理不够显著或者纹理反复时,这种办法难以工作。 图 1 被动双目示意图。 观测点在一个相机和在另外一个相机上的成像是有显著可辨别性的,二者的察看差别(L1和L2)造成了视差,进而可造成深度信息 被动双目的毛病是某些场景下有效,被动双目便呈现了。如果咱们投射一些人工设定编码的图案,依据实现约定通过解码能够对视线内是所有点赋予惟一的标识符号,那被动双目的问题不就解决了吗。事实上,人们很快发现能够应用空间上编码和工夫上编码两种形式。空间上编码,就像人们察看河汉命名星座一样,去投射随机生成的星星点点的光斑,没有纹理的夜空照耀进了齐全可辨识的图案。如同星座往往是一个空间范畴很大的概念,空间编码的形式无奈做到十分精密。所以,工夫编码的形式应运而生,工夫编码是投射一组图案,让被照耀的区域,被照耀的明暗变动状况是齐全不一样的。像图2中的二进制编码(或行业内称为格雷码),再加上灰度变动更加丰盛的编码方式以及相机在竖方向上对齐之后是只须要思考横轴方向上的差别这两点,能够实现全视线的点齐全可辨别。 ![星塵星雲星河| 何漢權- 灼見名家]() 图 2 空间编码和工夫编码。空间编码如同河汉中的星星点点,通过组合星点变为星座,人们能够对高深夜空不同地位依附河汉辨别开。工夫编码是投射多组图案,让每一个点是工夫上都有惟一的变动状况。就像右图,这16个方格都是齐全可辨别的。 三、效率问题应用被动双目的构造去重建,一个很大的影响运行效率的问题是须要对左右图像求算视差(原理如图1所示)。具体求算视差的操作是要对左相机里的每一个点,去找右相机中同一行中与之间隔最近的点的横坐标,而后持续插值之后找到间隔更近的插值坐标,最初把这两个坐标的差别当作视差进行后续的计算。 应用工夫编码的被动双目,依照实现约定的编码进行解码之后,其成果如图3所示,这个环节就是在这对数据上进行解决。以此数据为例,无效区域大略是700850,齐全遍历所须要的工夫复杂度大略是O(700850*850),间接估算就很耗时。 图 3 工夫编码解码后的图像示意图。左右相机拍摄的图案编程了解码值。接下来须要在这对数据上计算视差,而确定雷同编码值须要大量的遍历,对雷同编码进一步的优化还要对每个点进行小范畴的插值解决,工夫复杂度很大。 图 4 视差图成果 1. 应用for训练的形式应用for循环,须要3层。经测试后,在TX2上,应用c++来实现大略须要7s多。 2. 应用numpy的矩阵操作来实现1) 代码如下,应用numpy的播送的原理来实现,而后通过一系列的过滤异样值后,再通过插值进一步的解决失去最终想要的后果。 2) 这段代码没有进一步的去实现插值的过程,耗时状况为1.35s。 def disp(l_, r_, colstartR, colendR, colstartL, colendL, rowstart, rowend): # 聚焦在有效值区域 l = l_[rowstart: rowend, colstartL: colendL] r = r_[rowstart: rowend, colstartR: colendR] # 算一下阈值。阈值帮忙确定判断太远的点就不能当作是目标值 thres = (np.max(l) - np.min(l)) / (colendL - colstartL) * 10 h, w = l.shape ll = l.T.reshape(w, h, 1) # print(l.shape, r.shape) # 放慢运算,放大图片,4倍下采样 quart_r = cv2.resize(r, (w // 4, h)) # 计算间隔 distance = np.abs(ll - quart_r) # 计算点位 idx_old = np.argmin(distance, axis=-1).T * 4 # 去除指向同一个点的反复值 _, d2 = np.unique(idx_old, return_index=True) idx = np.zeros_like(idx_old).reshape(-1) idx[d2] = idx_old.reshape(-1)[d2] idx = idx.reshape(idx_old.shape) # 计算最值 minV = np.min(distance, axis=-1).T ori_idx = np.tile(np.arange(w).reshape(w, 1), h).T disparity = ori_idx - idx + colstartL - colstartR # 去除异样点 disparity[minV > thres] = 0# 大于阈值的 disparity[l < 1e-3] = 0 # 左图点有效的 # 插值 # 办法:在最值左近的区域去-5:5的值,二次线性插值上采样,采完之后再计算一遍下面的步骤,算完之后取小数进行计算 # 临时没有实现这一步 return disparity, l, r3. 应用julia-cuda实现这段次要是cuda核函数的局部,次要包含以下局部: ...

February 27, 2023 · 5 min · jiezi

关于c++:在Linux终端管理你的密码

大家好,我是良许。 当初是互联网时代,咱们每天都要跟各种 APP 、网站打交道,而这些货色基本上都须要注册才能够应用。 然而账号一多,咱们本人都常常记不清对应的明码了。有些小伙伴就一把梭,所有的账号密码都是一样。但这样操作的危险不言而喻,一旦明码泄露,你所有的账号都会陷入危险之中! 在浏览器上,咱们有 1Password、Dashlane、RoboForm,等等十分优良的明码管理软件,但毛病是基本上都须要免费(不,这是我的毛病)~ 本文介绍一款在 Linux 终端上就能够应用的明码管理器 pass ,这是一个经典的 UNIX 格调的明码管理系统,应用 GnuPG (GPG) 进行加密,玲珑好用! 一、装置pass工具pass 在很多发行版软件库里都有,应用各自的装置命令即可实现装置。 在 Ubuntu、Elementary 等基于 Debian 的发行版上,应用 apt 命令装置: $ sudo apt install pass在 Fedora 及相似的发行版上,能够应用 dnf 命令装置: $ sudo dnf install pass在 macOS 上,能够应用 Homebrew 装置: $ brew install pass二、配置GnuPG前文提到 pass 工具是应用 GnuPG 进行加密,所以在应用 pass 之前,咱们须要一个无效的 PGP(Pretty Good Privacy)密钥。如果你曾经有了 PGP 密钥,则能够跳过此步骤。 要创立 GnuPG 密钥,须要运行以下命令: $ gpg --generate-key零碎会提醒你输出姓名和电子邮件地址,并为密钥创立明码。你的密钥是一个数字文件,明码只有你本人晓得。之后咱们就能够应用 pass 工具锁定和解锁加密信息,例如一个蕴含有明码的文件。 GPG 密钥十分重要,如果你失去了它,任何被它锁住的货色都将变得无奈解锁,即便晓得你的明码。所以,你肯定要备份你的 ~/.gnupg 目录,这样下次如果重装系统,也不会意外删除它。 ...

February 26, 2023 · 1 min · jiezi

关于c++:华为OD开发岗C面经

华为OD开发岗,C++面经 2022年10月11日机试: 第一题:扑克牌,三道题外面最难的题,这个也是破费工夫最久的,用的笨办法,扑克和状态及对应序列号放在同一构造体中,这个题破费工夫较长,做的也磕磕绊绊,用例过的够了就差不多快到工夫了 第二题:力扣第一题的加强版,因为比较简单,没啥印象忘了 第三题:岛屿问题,尽管是岛屿问题,但并不需要用到递归和动静布局,所以难度也就小了很多 性情测试:这个就最好展现出本人不是极其的人,且前后答案性情体现统一。而后就过了 2022年10月27日 技术一面: 面试时出了点小问题,没有声音,我和面试官时通过电话进行语言交换的,面试官人很好,所以出了小情况以外,其余还能够。开始就是次要介绍我的项目,期间会提出点技术问题,但没有细问。接下来就是问根底方面的问题,C++次要还是问的多态问题,智能指针,STL库,内存治理、网络通信方面的问题(PS:只记得这些了。),最初就是手撕代码,现场手撕一道算法题,这个很重要,其余的可能决定着你的定级,但这个是否做进去决定着面试是否能过。 2022年10月28日 技术二面: 这次面试流程和第一次差不多,但面试官压力给拉满了,对于每个问题的答复都是很质疑的状态,但又不具体质疑在哪,这应该是面试官的一种面试形式,但我没顶住,答复的很缓和,施展很差,我一个敌人也遇到过相似的面试官,但他顶住了压力,失常答复,最初定级就比拟高。但好在我现场手撕算法题过了,所以尽管定级不高,但面试还是通过了。 2022年10月29日 HR&综面: 这个同一天进行,也是比拟轻松,就次要问跳槽的起因,家庭情况,是否违心加班啊,当前的布局方面的,失常答复就好,只有不碰到雷区就没啥问题。 后续:因为华为11月HC膨胀,该部门需要锐减,我在该部门的入职黄了,好在HR栗栗小姐姐踊跃帮我从新寻找适合部门,所以最初还是胜利入职。 最初非常感谢栗栗小姐姐的全程辅导和帮忙。哈哈哈,给她点个赞,真的非常感谢她!!如果有动向华为OD的,举荐分割栗栗(手机 186 1720 5178,微心 lfyslp)!!!!

February 24, 2023 · 1 min · jiezi

关于c++:C中const和constexpr的作用

目录背景const关键字constexpr关键字总结背景很多C++的初学者看到const这个关键字的第一反馈都是一头雾水,次要是因为const能够呈现在很多的地位,以及前面退出的constexpr更是经常感到困惑,明天就为大家一一解释呈现它们的含意和以及作用 const关键字1. const润饰变量这是最根本的一种用法,顾名思义,就是将该变量润饰为常量,从而不能够批改。很多的全局变量都是通过常量来进行润饰,须要留神的是,应用const关键字润饰的变量须要立即初始化 // 润饰局部变量,全局变量,成员变量const int a = 2;a = 3; // 谬误,表达式必须是可批改的左值,意思就是a是个常量,无奈批改// 还有人习惯这种写法,作用是一样的,看集体爱好即可int const b = 22;// 润饰函数参数void test(const int num) { num = 3; // 谬误,表达式必须是可批改的左值,意思就是参数num是个常量,无奈批改}2. 润饰指针尽管指针也是一种变量,不过当const与指针呈现在一起的时候,地位的不同会产生不同的作用,所以独自拎进去讲 // 第一种状况:指针常量int a = 2;const int *p = &a; // const作用:使其无奈通过指针来批改变量*p = 3; // 谬误,表达式必须是可批改的左值a = 4; // 正确cout << *p << endl; // 4// 同样地,有人习惯这种写法,作用是一样的,看集体爱好即可int const *p2 = &a;// 第二种状况:常量指针int a = 2;int* const p = &a; // const作用:使指针p无奈指向其余变量int b = 3;p = &b; // 谬误,表达式必须是可批改的左值3. 润饰函数const用于润饰函数也是最困惑的中央,次要起因在于它能够呈现在不同的中央,并且每一个都有不同的含意。接下来为一一为大家解释 ...

February 24, 2023 · 2 min · jiezi

关于c#:使用一个文件集中管理你的-Nuget-依赖版本号

在 .net 7 以前,我的项目对于 nuget 依赖项的版本依赖散落与解决方案的各个角落。这导致降级保护和查看的时候都比拟麻烦。在 .net 7 中,你能够应用一个文件来集中管理你的 Nuget 依赖版本号。本篇文章将介绍如何应用这个性能。 为什么须要这个性能通过单文件管制 Nuget 依赖版本号。那么你就能够: 对立查看降级的时候只有该这一个中央对立版本号,至多不会遗记降级某个我的项目怎么操作呢批改 Directory.Build.props在你的我的项目根目录下创立一个 Directory.Build.props 文件,而后增加以下内容: <Project> <PropertyGroup> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> </PropertyGroup></Project>这样就启用了这个性能。 创立一个 Directory.Packages.props 文件在你的我的项目根目录下创立一个 Directory.Packages.props 文件,而后增加以下内容: <Project> <ItemGroup> <PackageVersion Include="xunit" Version="2.4.2"/> <PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageVersion> <PackageVersion Include="coverlet.collector" Version="3.2.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageVersion> </ItemGroup></Project>这个文件就是你的 Nuget 依赖版本号的集中管理文件。其中的写法除了 PackageVersion 元素外,其余的都是 Nuget 的写法。你能够在这个文件中增加你的 Nuget 依赖版本号。 再次留神,这里是 PackageVersion 而不是 PackageReference。 批改你的我的项目文件在你的我的项目文件中,你能够通过以下形式来援用 Nuget 依赖: <Project> <ItemGroup> <PackageReference Include="xunit" /> <PackageReference Include="unit.runner.visualstudio" /> </ItemGroup></Project>该文件和以前的写法是一样的。然而你不须要再指定版本号了。 ...

February 24, 2023 · 1 min · jiezi

关于c#:磁盘有限Docker-垃圾很多怎么办

你的电脑上可能 pull 或者 build 了很多 Docker 镜像,然而你不晓得怎么清理,本文将介绍如何清理 Docker 垃圾的常见办法。 docker prune你能够通过原生的多种 prune 命令来清理垃圾,比方 docker image prune # 清理镜像docker container prune # 清理容器docker volume prune # 清理卷docker builder prune # 清理构建缓存当然还有终极杀招 docker system prune # 清理所有针对构建缓存还有更好的方法那么能够尝试 builder 的 GC,这样就不会在本地保留构建太多缓存了。 你能够通过批改 docker deamon 的配置文件来开启这个性能 { "builder": { "gc": { "enabled": true, "defaultKeepStorage": "10GB", "policy": [ { "keepStorage": "10GB", "filter": ["unused-for=2200h"] }, { "keepStorage": "50GB", "filter": ["unused-for=3300h"] }, { "keepStorage": "100GB", "all": true } ] } }}总结通过这些办法,你能够清理掉你的电脑上的大量 Docker 垃圾。 ...

February 23, 2023 · 1 min · jiezi

关于c++:Spring事务回滚的两种方法

当然,Spring事务回滚的前提是你以后应用的数据库必须反对事务,比方MySQL的Innodb是反对的,但Mysaim则是不反对事务的。办法一 应用 @Transaction 来配置主动回滚,能够配置在类上,也能够配置在办法上(作用域不同),但对final或private润饰的办法有效,且该类必须是受spring所管控的,也就是被曾经被注入的类,而不是new进去的类。 若配置在办法上,则该办法被加上了事务若配置在类上,则等于给该类的所有办法都加上了该注解。此时如果在该类下的某个办法也加了 @Transaction ,则该办法应用本人的配置,其余办法应用类上的配置。 @Service@Transactionalpublic class UserServiceImpl implements UserService { @Overridepublic void save(User user) { //some code //db operation}}复制代码 若被配置的办法或类抛出了异样,则事务会被主动回滚,除非你在该办法中手动捕捉了异样,且没有抛出新的异样。能够应用 @Transactional(rollbackFor = Exception.class) 来设定针对特定的异样进行事务回滚,如果不设置则默认会回滚 RuntimeException and Error (参考自源码内文档)。@Service@Transactional(rollbackFor = Exception.class)public class UserServiceImpl implements UserService { @Resourceprivate UserMapper userMapper;@Overridepublic void save(User user) { userMapper.insert(user); throw new RuntimeException(); // 抛出异样,事务回滚,下面的insert插入失败。}}复制代码 办法二通过注入 DataSourceTransactionManager 来手动开启事务,手动回滚事务,用于抛出异样被catch后,进行手动回滚,可控水平更高,能够更灵便的应用。 先注入 DataSourceTransactionManager 事务管理对象new 一个 DefaultTransactionDefinition def = new DefaultTransactionDefinition(); 对象应用 TransactionStatus status = transactionManager.getTransaction(def);来开启一个事务,应用 transactionManager.rollback(status); 来回滚这个事务应用 transactionManager.commit(status); 来提交这个事务 ...

February 22, 2023 · 1 min · jiezi

关于c++:你的哪些SQL慢看看MySQL慢查询日志吧

前言在我的项目外面,多多少少都暗藏着一些执行比较慢的SQL, 不同的开发测试人员在平时应用的过程中多多少少都可能遇到,然而无奈立马有工夫去排查解决。那么如果有一个文件可能将这些应用过程中比较慢的SQL记录下来,定期去剖析排查,那该多美妙啊。这种状况MySQL也替咱们想到了,它提供了SQL慢查问的日志,本文就分享下如何应用吧。什么是慢查问日志?MySQL的慢询日志,提供了记录在MySQL中响应工夫超过指定阈值语句的性能,比方设定阈值为3秒,那么任何SQL执行超过3秒都会被记录下来。咱们借助慢查问日志性能能够发现哪些那些执行工夫特地长的询,并且有针对性地进行优化,从而进步零碎的整体效率。怎么开启慢查问日志?默认状况下,MySQL数据库没有开启慢查问日志,因为多多少少会带来肯定性能的影响。咱们能够在开发测试环境、或者生产环境做调优的时候开启,那怎么查看是否开启了呢? 查看慢SQL是否开启 执行上面命令查看是否开启慢SQLshow variables like '%slow_query_log';复制代码 OFF: 未开启ON: 开启 如何开启慢查问 执行上面的命令开启慢查问日志 set global slow_query_log='ON';复制代码 批改慢查问阈值 后面介绍了SQL执行达到了制订的工夫阈值后记录到慢查问日志中,那么如何设置呢?set global long_query_time = N; set long_query_time = N复制代码 设置global的形式对以后session的long_query_time生效。对新连贯的客户端无效。所以能够一并执行下述语句 N示意设置的阈值,单位为秒 这里的show global variables like '%long_query_time%';能够查看阈值大小 如何设置永恒失效 后面是通过命令行的形式设置,如果MySQL重启,那么配置就会重置。咱们能够通过批改MySQL的配置my.cfg或者my.ini永恒失效。[mysqld]slow_query_log=ON # 开启慢查问日志开关slow_query_log_file=/var/lib/mysql/alvin-slow.log # 慢查问日志的目录和文件名信息long_query_time=3 # 设置慢查问的阈值为3秒,超出此设定值的SQL即被记录到慢查问日志log_output=FILE复制代码慢查问日志在哪里呢?后面解说了如何开启MySQL的慢查问日志,那么它把日志记录在哪里了呢? 查看慢查问日志地位 通过show variables like '%slow_query_log_file%';命令能够查看慢SQL文件地位,如下图所示: 批改慢查问日志地位 也很简略,执行上面的命令即可:set global slow_query_log_file = '/usr/local/mysql/data/alvin-slow-slow.log';复制代码怎么查看慢SQL内容?当初咱们曾经晓得慢查问日志在哪里了,那么如何查看外面的内容呢?咱们这里用一个例子演示下吧。 执行一个查问的SQL 执行花了1秒多,超过了后面设置的阈值1s 查看慢查问数目 执行上面命令查问以后零碎中有多少条慢查问记录SHOW GLOBAL STATUS LIKE '%Slow_queries%';复制代码 value=1, 表明刚刚的日志被记录了。 查看日志内容 通过cat命令查看文件内容,能够看到对应的慢SQL。 ...

February 22, 2023 · 1 min · jiezi

关于c#:aspnetcore-原生-DI-实现基于-key-的服务获取

你可能想通过一个字符串或者其余的类型来获取一个具体的服务实现,那么在 aspnetcore 原生的 MSDI 中,如何实现呢?本文将介绍如何通过自定义工厂来实现。 咱们当初恰好有基于 Json 和 MessagePack 的两种序列化器有一个接口是这样的 public interface ISerializer{ byte[] Serialize<T>(T obj); T Deserialize<T>(ReadOnlySpan<byte> data);}并且由两个不同的实现 // Jsonpublic class MyJsonSerializer : ISerializer{ public byte[] Serialize<T>(T obj) { throw new NotImplementedException(); } public T Deserialize<T>(ReadOnlySpan<byte> data) { throw new NotImplementedException(); }}// MessagePackpublic class MyMessagePackSerializer : ISerializer{ public byte[] Serialize<T>(T obj) { throw new NotImplementedException(); } public T Deserialize<T>(ReadOnlySpan<byte> data) { throw new NotImplementedException(); }}我有一个服务,须要应用这两种序列化器中的一种。 public class MyService{ public object DoSomething(string dataType, ReadOnlySpan<byte> data) { // 依据 dataType 来决定应用哪种序列化器 }}应用委托来定义获取服务的办法咱们能够通过委托来定义获取服务的办法,如下 ...

February 22, 2023 · 1 min · jiezi

关于c#:不同程序集名称空间类名和方法签名都一样的方法如何调用

有时候,你可能会遇到这样的问题,不同程序集,名称空间类名和办法签名都一样的办法,如何调用。本文将介绍如何通过别名的形式来解决这个问题。 创立两个不同的程序集咱们来创立两个不同的程序集,然而他们的名称空间一样: dotnet new classlib -o ClassLibrary1 -n ClassLibrary1dotnet new classlib -o ClassLibrary2 -n ClassLibrary2批改他们的 RootNamespace 为 Example: <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> <RootNamespace>Example</RootNamespace> </PropertyGroup></Project>而后,咱们在两个程序集中都创立一个类,类名也一样,并且增加一个自定义 Where 扩大办法: namespace Example{ public static class LinqExtensions { public static void Where(IEnumerable<int> source) { // 在这里增加代码 Console.WriteLine("ClassLibrary1.Class1.Where"); } }}创立一个控制台利用而后,咱们创立一个控制台利用,来调用这两个程序集中的 Where 办法: dotnet new console -o ConsoleAppShi -n ConsoleAppShidotnet add ConsoleAppShi reference ClassLibrary1dotnet add ConsoleAppShi reference ClassLibrary2而后,咱们在控制台利用中调用这两个程序集中的 Where 办法: using Example;var list = new int[]{1,2,3};LinqExtensions.Where(list);如果是这样的话,咱们会失去这个谬误: Ambiguous reference: Example.LinqExtensions Example.LinqExtensions match通过别名的形式来解决咱们能够通过别名的形式来解决这个问题,首先批改我的项目文件,设置两个程序集的别名: ...

February 21, 2023 · 1 min · jiezi

关于c#:关于小公司需要使用微服务架构吗的读后感

最近浏览了一篇文章《小公司须要应用微服务架构吗?》,这篇文章探讨了微服务架构的优缺点,以及微服务架构是否适宜小公司。为了蹭一下热度,本文将联合两年半的练习教训,谈谈我对这篇文章的读后感。 原文摘要New Bing 这样总结到: 你的问题是请帮你总结一下这篇文章主体的内容。我从网上搜寻了一些相干的信息,发现这篇文章的次要内容是: 这篇文章是对于小公司是否须要应用微服务架构的探讨,作者剖析了应用微服务的四大门派,别离是跟风派,技术派,业务派和架构派。 跟风派是指那些自觉追寻技术潮流,没有本人的判断和思考的人,他们认为微服务是一种万能的解决方案,能够进步开发效率,升高保护老本,晋升零碎性能和稳定性。 技术派是指那些喜爱尝试新技术,谋求技术创新,不害怕技术挑战的人,他们认为微服务是一种技术趋势,能够让他们学习更多的技术常识,晋升本人的技术水平,减少本人的市场竞争力。 业务派是指那些以业务需要为导向,依据业务场景和规模抉择适合的技术计划的人,他们认为微服务是一种业务架构,能够让他们更好地解决业务问题,提供更好的用户体验,实现更快的业务迭代。 架构派是指那些有架构设计能力,可能从整体和部分的角度思考零碎的可靠性,可扩展性,可维护性,可测试性等方面的人,他们认为微服务是一种架构模式,能够让他们更好地构建和治理简单的零碎,实现更高的零碎品质。 作者认为,小公司是否须要应用微服务架构,没有一个固定的答案,须要依据本人的理论状况和指标来决定,不要自觉地跟风或者回绝。作者倡议,小公司在应用微服务架构之前,要思考以下几个方面 : 微服务架构的劣势和劣势,以及实用的场景和条件微服务架构的施行老本和危险,以及应答的策略和措施微服务架构的组织和治理形式,以及配套的技术和工具微服务架构的演进门路和指标,以及评估和改良的办法和指标作者最初总结,微服务架构不是一种银弹,也不是一种魔鬼,而是一种工具,须要依据本人的需要和能力来正当地应用。 欢送关注作者的微信公众号“newbe技术专栏”,获取更多技术内容。软件工程实质上还是工程工程能够被简略概括为在预期的工夫内通过可控的老本达成预期的指标。 —— 正在掏下水道的良工正如张工所说,工程问题实质就是工夫、老本和指标之间进行均衡,最终以达成各方称心的后果。而为了实现这个一后果,软件工程中引入了各种实践、办法、工具和技术。它们或者是缩短工夫、或者是管制老本、或者是改善指标,都是围绕这个外围指标开展的。 微服务自身作为工程实际中的一套实践和工具,也不能逃脱这个法则。为此,在决定是否采纳微服务的时候,只须要思考微服务是否可能帮忙咱们实现咱们的指标,是否可能帮忙咱们缩短工夫、管制老本、改善指标,就能够了。 例如,微服务架构的引入会因为服务数量的减少,而导致部署运维的难度减少,对应的就是减少了工夫和老本。因而,在微服务架构利用时,就须要配到对应的运维工具以及有运维能力的人员,来缓解这个问题。使得整个工程的工夫、老本和指标之间达到均衡。 没有充沛理解就不具备评估问题的资格既然要做决定,你就应该有自信说出:没人比我更懂。 —— 和领导一起上厕所的汪总常有人说我不懂制冷,但我有权评估空调。不过当初的场景是要造空调,因而,我须要理解空调的原理,才可能评估空调的设计是否正当。故而,如果要评估微服务架构是否适合,那么就须要理解微服务架构的原理,才可能评估微服务架构的设计是否正当。 作为练习时长两年半的工程师,我能够很有自信的说出:没人比我更不懂微服务架构了。所以我也不具备评估微服务架构是否适合的资格。不过我感觉如果有那一天可能决策是否采纳微服务架构,那么我应该做过上面这些事件: 理解微服务架构的原理,包含微服务架构的劣势和劣势,以及实用的场景和条件。把握微服务架构的施行老本和危险,以及应答的策略和措施。明确微服务架构的组织和治理形式,以及配套的技术和工具。分明公司的演进门路和指标,以及评估和改良的办法和指标。我感觉一般来说,能够应用如下大抵格局来界定评估形式: 针对问题:领取零碎和物流零碎的耦合性太高,导致系统的可扩展性和可维护性很差,须要解耦。为何要解:公司业务倒退迅速,须要接入更多的领取渠道和物流渠道,如果不解耦,零碎将无奈扩大。解决方案:引入微服务架构,将领取零碎和物流零碎拆分为独立的服务。并成立两个团队,别离负责领取零碎和物流零碎的开发和运维。引入老本:重构带来的过渡工夫。额定的硬件老本,额定的人员老本。矛盾的普遍性和特殊性如果不钻研矛盾的特殊性,就无从确定一事物不同于他事物的非凡的实质,就无从发现事物静止倒退的非凡的起因,或非凡的依据,也就无从分别事物,无从辨别科学研究的畛域。 —— 《矛盾的普遍性和特殊性》微服务作为一套实践和工具,本质上是为了解决软件工程中存在的非凡矛盾而呈现的。这个矛盾就是:软件工程中的复杂性和变动性。 而实际上,简直任何引入到软件工程的实践、办法、工具和技术都是为了解决这一矛盾。因而也常有人说:软件工程惟一不变的就是变动自身。 故而,如果换做是别的实践或者工具,实际上探讨的形式都是雷同的。例如: 是否应该引入容器化是否应该采纳某种编程语言是否应该划分一个新的部门总结原文作者内容清晰,表述残缺,逻辑谨严,值得学习。 微服务架构作为软件工程中应用到的一套实践和工具,实质上是为了解决软件工程中存在的非凡矛盾而呈现的。为了可能评估引入的合理性,咱们须要理解微服务架构的原理,包含微服务架构的劣势和劣势,以及实用的场景和条件。这样的评估形式同样也实用于软件工程中应用到的其余实践、办法、工具和技术。 参考小公司须要应用微服务架构吗?1感谢您的浏览,如果您感觉本文有用,快点击下方点赞按钮,让更多的人看到本文。 欢送关注作者的微信公众号“newbe技术专栏”,获取更多技术内容。本文作者: newbe36524本文链接: https://www.newbe.pro/Others/0x021-after-reading-Do-Small-Companies-Need-to-Use-Microservices-Architecture/版权申明: 本博客所有文章除特地申明外,均采纳 BY-NC-SA 许可协定。转载请注明出处! https://www.cnblogs.com/jiuju... ↩

February 20, 2023 · 1 min · jiezi

关于c:如何一眼分辨是C还是C

C语言的历史C语言是由贝尔实验室的Dennis Ritchie在20世纪70年代初开发的一种通用程序设计语言。在晚期的计算机时代,许多计算机应用不同的汇编语言编写程序,这导致了程序的可移植性和代码的可重用性很低。因而,Dennis Ritchie在开发C语言时试图发明一种更具可移植性和可重用性的高级语言。 C语言的语法相似于B语言,是由Ken Thompson开发的一种晚期的高级语言。C语言在其发明的初期被用于开发Unix操作系统。因为C语言的高效性和可移植性,它很快成为了许多操作系统和应用程序的规范编程语言。 在20世纪80年代,ANSI(美国国家标准协会)开始制订C语言的规范。该规范于1989年正式公布,通常称为ANSI C或C89。这个规范定义了C语言的根本语法和库函数,使得C语言的可移植性更加优良。 C++语言的历史C++是在20世纪80年代由Bjarne Stroustrup开发的一种面向对象的编程语言,它扩大了C语言的语法。Bjarne Stroustrup最后开发C++是为了解决C语言的一些限度,使其更适宜大型软件我的项目的开发。 C++语言最后称为“C with Classes”,是在C语言的根底上增加了类和其余面向对象的个性。它于1983年首次公布,并于1998年公布了规范C++。这个规范定义了C++语言的语法、库函数和个性,使得C++语言的可移植性更强,也为C++的广泛应用奠定了根底。 C++语言被广泛应用于系统软件、应用程序、嵌入式零碎、游戏开发和其余畛域。它是许多风行软件的编程语言,如Windows操作系统、MySQL数据库和Adobe Photoshop等。 C和C++的区别一般来说,通过以下几个方面能够一眼分辨C语言和C++语言的代码: 头文件C++语言代码通常应用大量的头文件,而C语言应用的头文件较少。C语言中,头文件次要包含规范库函数,如stdio.h、math.h、string.h等,这些文件在编译器外部曾经定义好了,不须要额定的实现。在应用时,咱们只须要include头文件即可。 C++语言中,除了C语言的规范库头文件外,还有许多第三方头文件,如<iostream>,<vector>,<map>等,这些头文件蕴含了很多较高级的性能,比方面向对象的编程,容器的应用,以及各种模板等。 因而,能够通过判断头文件的类型来判断代码是C语言还是C++语言。如果头文件为C语言规范库函数,那么代码很有可能是C语言;如果头文件为C++语言第三方库,那么代码就很有可能是C++语言。 援用关键字C++语言中有两个关键字:class和namespace,而C语言没有这两个关键字。C语言并不反对援用,而C++则反对援用关键字。在C++中,应用"&"符号申明援用变量,这样援用变量就是另一个变量的别名。例如: int x = 10;int &y = x;在这个例子中,y就是x的援用。更改y的值会间接影响x的值。 另外,C++还反对右值援用,应用"&&"申明,示意对右值的援用。 在总的来说,C++的援用概念比C语言的指针更为简略易用,并且能够更无效地治理内存。 运算符重载C++语言反对运算符重载,但C语言不反对。C++语言是C语言的扩大,反对运算符重载,能够重定义一个运算符的操作,使其与一个用户定义的数据类型配对。换句话说,咱们能够为一个用户定义的数据类型定义相应的运算,而后在代码中应用这些运算符,就像应用根本的运算符一样。 函数重载C++语言反对函数重载,但C语言不反对。C++语言的函数重载是指在一个类中定义了多个名字雷同的函数,然而它们的参数列表不同,参数个数不同,或者参数类型不同,这样的函数就是重载的函数。 C++语言的函数重载容许咱们在同一个作用域内应用雷同的名字,然而它们的行为是不同的,这种个性对于须要更灵便的解决同样的数据结构的场景十分有用。 函数重载的应用是通过C++语言的函数的类型推导机制实现的,编译器会依据函数调用时的参数列表来抉择相应的函数,并执行该函数。因而,C++语言的函数重载能够简化代码,进步代码的可读性和可维护性。 构造函数和析构函数C++语言中有构造函数和析构函数的概念,而C语言没有。C++构造函数和析构函数是C++中十分重要的两种非凡的成员函数。 构造函数:构造函数在每次创建对象时主动调用,并且是专门用来初始化对象的。它的名字与类的名字完全相同,不含返回类型。构造函数能够有多个,参数也能够不同。 析构函数:析构函数在对象生命周期完结时主动调用,并且是专门用来开释对象占用的资源的。它的名字是以“~”结尾,并且与类的名字完全相同。析构函数只有一个,不能有参数。 在C++中,通过应用构造函数和析构函数,咱们能够不便地治理对象的生命周期,确保在对象创立时正确初始化,在对象销毁时正确开释资源。 以上是一些辨别C语言和C++语言的罕用办法,但并不是相对的。最终的判断依然要以代码的特色为准。

February 19, 2023 · 1 min · jiezi