音视频开发_FFmpeg基石精讲

FFmpeg 框架

核心组件

  • libavcodec:一个编解码库,包含了众多的编码器和解码器用于编码和解码音视频流。
  • libavformat:一个封装格式库,用于处理各种音视频封装格式。
  • libavutil:一个工具库,提供了常见功能的简化接口,如数学计算、内存管理等。
  • libavfilter:滤镜库,用于对音视频数据进行过滤处理。
  • libavdevice:提供了捕捉与输出多媒体设备内容的接口。
  • libswscale:用于处理图像缩放以及色彩空间转换。
  • libswresample:用于处理音频采样数据的重采样、格式转换等。

FFmpeg工具集

  • ffmpeg:此命令行工具用于快速音视频转码、封装格式转换。
  • ffplay:一个简单的媒体播放器,基于SDL和FFmpeg库。
  • ffprobe:用于分析多媒体流信息的命令行工具。

关键功能

  • 转码:转换音视频编码格式,如将AVI转换为MP4。
  • 解码:根据编码格式将数据解码为原始音视频帧。
  • 编码:将原始音视频数据编码为特定格式。
  • 封装:将编码后的音视频数据封装到指定的容器格式。
  • 流化:处理音视频数据以适合网络流传输。
  • 滤镜处理:改变音视频数据的属性,如调整大小,裁剪,去除噪点等。

在这里插入图片描述

FFmpeg 内存引用计数模型

FFmpeg中的内存引用计数模型是一个核心的内存管理机制,用于确保音视频数据(如帧、包、缓冲区等)在多个组件之间安全地共享与释放。这个机制基于引用计数,可以防止资源的提早释放或内存泄漏。

AvBufferRef

在FFmpeg中,AvBufferRef类型的引用在各种数据结构(如AVFrame, AVPacket等)中被用作指向实际数据的指针。使用这种引用计数模型能够在不同的上下文中共享相同的数据,而无需复制数据。

引用计数工作原理

  1. 初始化: 当数据被分配时,它被包装在“buffer”的数据结构中,并且引用计数设置为1。
  2. 增加引用: 当另一个组件需要使用相同的数据时,引用计数会增加。
  3. 减少引用: 当组件完成对数据的使用,它将减少引用计数。这通常通过调用av_buffer_unref()或类似的函数完成。
  4. 释放内存: 当最后一个引用av_buffer_unref()被调用时,引用计数归零,相关数据结构与其所占用的内存将被释放。

为了使这个模型有效,所有组件在处理通过引用计数共享的数据时,都必须遵守增加和减少引用的规则,这确保了内存的正确管理。

这种模型在视频解码过程中尤为重要,因为解码视频通常会产生大量的视频帧数据,如果不进行合理管理,很容易造成内存不足的问题。使用AvBufferRef,多个组件可以同时访问帧数据而不需要进行数据的复制,这既减少了延迟也节约了资源。

解复用相关 AVFormat XXX等

AVFormatContext

AVFormatContext 结构体是FFmpeg库中一个核心的结构体,用于存储媒体文件或媒体流的信息以及对它们进行操作时所需的上下文信息。

AVFormatContext 结构体中关键的成员:

  • av_class: 一个指向 AVClass 的指针,用于日志记录和avoptions。通过它可以访问类的相关信息和方法。

  • iformat: 指向一个 AVInputFormat 结构体的指针。它描述了用于读取媒体文件的解复用器。

  • oformat: 指向一个 AVOutputFormat 结构体的指针。它描述了用于写入媒体文件的复用器。

  • streams: 指向 AVStream 结构体指针数组的一个指针,存储了文件中所有媒体流的信息。

  • nb_streams: 流的数量,说明 streams 数组含有多少个元素。

  • url: 一个表示媒体文件名或流的URL的字符串。

  • pb: 指向 AVIOContext 的一个指针,用于输入输出操作的抽象。

  • duration: 媒体文件的总时长。

  • bit_rate: 总比特率。

  • packet_size: 数据包的大小。

  • max_delay: 最大延迟。

  • flags: 各种标志,定义了上下文的额外属性。

  • probesize: 在读取流信息时确定要探测多少字节。

  • max_analyze_duration: 在读取流信息时设置最大分析时长。

  • metadata: 指向 AVDictionary 的指针,包含了文件的元数据,例如标题、作者、专辑等信息。

  • start_time: 流的开始时间。

  • pb: 指向一个 AVIOContext 结构体的指针,它表示内部I/O上下文,用于读写文件。

这些成员提供了对媒体文件和流的综合管理,包括读写、寻找、元数据处理和流信息获取等。

AVInputFormat

AVInputFormat结构体在FFmpeg中定义了一个特定媒体文件输入格式的解复用器(demuxer)接口。它包含了一系列的函数指针和变量,这些成员用于实现解复用的各个阶段功能,如打开文件、读取帧内容、寻找流信息等。

AVInputFormat结构体中的关键成员:

  • name: 指向一个字符串的指针,用来表示输入格式的名称,比如 “mp4” 或 “mpegts”。

  • long_name: 一个较为详细的格式描述,它通常是格式的完整名称。

  • flags: 标识输入格式的能力和限制,这可能包括对自定义IO支持、对查找操作的支持等。

  • extensions: 支持的文件扩展名,使用逗号分隔的字符串列表。

  • mime_type: 与输入格式相关的MIME类型。

  • priv_data_size: 针对此输入格式的私有数据结构的大小。

  • read_probe: 函数指针,用于探测输入格式是否支持处理给定的数据。

  • read_header: 函数指针,用于读取媒体文件的头部信息,设置AVFormatContext结构的其他成员。

  • read_packet: 函数指针,用于从媒体中读取一帧数据。

  • read_close: 函数指针,用于关闭解复用器并清理资源。

  • read_seek: 函数指针,用于支持在媒体文件中查找。

  • read_timestamp: 函数指针,用于获取特定位置的时间戳。

  • priv_class: 指向AVClass结构体的指针,用于支持私有选项设置。

AVInputFormat为不同的媒体文件类型提供了一组标准化的解复用API。不同的文件格式实现了不同的AVInputFormat实例,FFmpeg使用这些实例来实现对多种文件格式的支持。当用户打开一个媒体文件时,FFmpeg将会根据文件扩展名、文件内容等来选择合适的AVInputFormat结构体实例,以进行数据的解复用处理。

AVStream

AVStream结构体是FFmpeg中用于存储与特定媒体流(如视频流、音频流)相关的信息。它包含了一系列关键成员,这些成员提供了流的元数据和其他必要的信息,用于解复用和解码等操作。

AVStream结构体中的关键成员:

  • index: 流的索引编号,根据它在AVFormatContextstreams数组中的位置来确定。

  • id: 流的ID,用于在文件格式中识别流。

  • codecpar: 指向AVCodecParameters结构体的指针,包含了流的编解码器参数,如编解码器类型、视频的宽高、采样率等。

  • time_base: 流的时间基准,表示时间戳与实际时间的转换关系。这是一个AVRational类型的比率值,包含分子和分母。

  • start_time: 流中第一个可解码的帧的演示时间戳(Presentation Timestamp,PTS),以time_base为单位的。

  • duration: 该流在时间基准time_base单位下的总持续时间。

  • nb_frames: 流中的帧数,如果可知的话。

  • codec: (在新版FFmpeg中不建议使用,用codecpar代替)指向与流关联的AVCodecContext结构体,包含了所有与编解码器相关的详细信息和操作。

  • avg_frame_rate: 平均帧率,用AVRational类型表示。

  • r_frame_rate: 实际帧率,通常用于视频流。

  • disposition: 流的一些附加属性,例如是否有默认音频或是字幕。

  • metadata: 指向一个AVDictionary结构,包含了关于流的元数据键值对,例如语言或标题。

  • side_data: 一个指向AVPacketSideData数组的指针,存储了和流相关但不属于常规数据包的一些附加信息。

常见解复用操作

以下是使用FFmpeg进行解复用操作可能进行的一些步骤:

  1. 打开媒体文件:使用avformat_open_input函数打开输入文件,并创建AVFormatContext
  2. 读取流信息:调用avformat_find_stream_info函数获取媒体文件中的流信息。
  3. 查找音视频流:检查AVFormatContext中的AVStream来识别各个音视频流。
  4. 读取数据包:通过av_read_frame循环读取媒体文件中的数据包。
  5. 关闭输入:使用avformat_close_input清理并关闭AVFormatContext

例:

下面是一个解复用过程的代码片段:

AVFormatContext *fmt_ctx = NULL;
if (avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL) < 0) {// 打开文件失败
}// 读取媒体文件信息
if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {// 读取流信息失败
}// 查找第一个视频流
AVStream *video_stream = NULL;
for (int i = 0; i < fmt_ctx->nb_streams; i++) {if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {video_stream = fmt_ctx->streams[i];break;}
}// 读取数据包
AVPacket pkt;
while (av_read_frame(fmt_ctx, &pkt) >= 0) {// 判断数据包是不是视频流的一部分if (pkt.stream_index == video_stream->index) {// 你可以在这里处理视频数据包}av_packet_unref(&pkt);
}// 关闭输入
avformat_close_input(&fmt_ctx);

编解码相关 AVCodec XXX等

AVCodec

AVCodec:代表一个编解码器,描述了编解码器的相关操作如打开、关闭、编解码等。

AVCodec 的一些关键成员:

  • name:编解码器的名称,用以表征编解码器的内容(例如 “h264”、“aac” 等)。
  • long_name:编解码器的长名称,用于更详尽地描述编解码器(例如 “H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10”)。
  • type:编解码器的类型,表示它是视频编解码器、音频编解码器还是字幕编解码器,类型为 AVMediaType 枚举中的一个值。
  • id:编解码器的特定ID,对应于 AVCodecID 枚举中

AVCodecContext

AVCodecContext:提供了编解码的上下文,包含了一系列进行编解码所需的参数和设置。

AVCodecContext 的一些关键成员:

  • codec_id:代表了使用的编解码器的 AVCodecID
  • codec_type:媒体类型(视频、音频、字幕等),对应 AVMediaType
  • codec:指向实际的编解码器的指针。
  • bit_rate:码率,表示编解码的比特率。
  • widthheight:对于视频流,分别表示视频的宽和高。
  • sample_aspect_ratio:表示视频像素的宽高比。
  • pix_fmt:对于视频流,表示像素格式(AVPixelFormat)。
  • sample_fmt:对于音频流,表示样本格式(AVSampleFormat)。
  • sample_rate:对于音频流,表示采样率。
  • channel_layout:对于音频流,表示通道布局。
  • channels:对于音频流,表示通道数量。
  • time_base:时间基准,用于时间戳的转换。
  • gop_size:关键帧间的最大帧数。
  • max_b_frames:B帧的最大数量。
  • profile:编解码器使用的配置文件。
  • level:编解码器级别。
  • delay:编解码的延迟。
  • priv_data:指向编解码器私有数据的指针。
  • flags:各种标志,控制编解码过程中的选项(例如:是否进行帧间预测)。
  • extradataextradata_size:一些编解码器要求的额外数据和其大小。

AVCodecParameters

AVCodecParameters:包含了流的编解码相关参数,如编解码器的ID、比特率、帧率、分辨率等。

AVCodecParameters 结构体的关键成员包括:

  • codec_type:此流的媒体类型,例如视频、音频或字幕。
  • codec_id:编解码器的标识符,指定了使用哪种编解码器。
  • codec_tag:四字节码(fourcc),用来表示媒体的编码格式。
  • bit_rate:平均码率(比特/秒)。
  • format:对于视频流,表示像素格式;对于音频流,表示音频样本格式(sample format)。
  • widthheight:视频流的宽和高。
  • field_order:视频的字段排序(如逐行、交错等)。
  • color_rangecolor_primariescolor_trccolor_spacechroma_location:视频的色彩相关参数。
  • video_delay:视频流中的初始延迟帧数。

对于音频流,AVCodecParameters 还包括:

  • channel_layout:指明了音频流通道的布局。
  • channels:通道数。
  • sample_rate:音频采样率。
  • block_align:音频流中单个样本的字节数。
  • frame_size:音频帧的大小,对于固定帧率音频编码是一致的。

压缩数据 AVPacket

AVPacket 是 FFmpeg 中用来存储压缩数据(例如一个视频帧或者一个音频帧)的数据结构。它包含了编解码器所需的压缩数据,并附带时间戳、持续时间以及其他编解码过程中必须的元数据。

AVPacket 结构体的关键成员包括:

  • pts(Presentation Time Stamp):展示时间戳,表示这个数据包应该被展示的时间。它是基于流的 time_base 表达的。
  • dts(Decoding Time Stamp):解码时间戳,用于指定数据包解码的顺序。对于没有B帧的简单流,ptsdts 相同。
  • duration:数据包的持续时间,基于流的 time_base
  • data:指向存储压缩数据的实际字节序列的指针。
  • sizedata 指向的数据的大小,以字节为单位。
  • stream_index:这个 AVPacket 从属的流的索引,用于从 AVFormatContext 中识别出相应的流。
  • flags:包括一些标志位,比如是否是关键帧(AV_PKT_FLAG_KEY)。
  • side_data:一个指向额外数据数组的指针,以及这个数组中的元素数量。额外数据用于传输不在主要数据流中的其他信息,例如颜色空间信息、HDR元数据等。
  • pos:这个 AVPacket 在媒体文件中的字节位置,有助于调试或者定位。

通过 AVPacket,FFmpeg 可以将压缩数据从编码器传输到多路复用器(muxer)或从解复用器(demuxer)传输到解码器,同样也用于过滤器之间的数据传递。

未压缩数据 AVFrame

AVFrame 结构体在 FFmpeg 库中用于存储未压缩的数据,比如解码后的视频帧或音频样本。它是媒体处理中一个非常重要的组成部分,尤其是在解码、转码和过滤等操作中。

下面是 AVFrame 结构体的一些关键成员:

  • data:这是一个指针数组,用于存储实际的音视频帧数据。对于视频来说,data[0]data[1]data[2] 通常分别用于存储 Y、U、V 组件(对于 YUV 格式的视频)。对于音频来说,这个数组中的每个指针对应一个声道的样本数据。
  • linesize:这个整数数组存储了每个数据平面的步长(每行的字节数)。这对于视频帧是必须的,因为图像行不一定是紧凑排列的。
  • extended_data:这是 data 数组的一个扩展版本,它对于包含更多声道数据的平面格式音频尤其有用。
  • widthheight:对于视频帧,这代表了图像的宽度和高度。
  • nb_samples:对于音频帧,这表示帧中含有的样本数量。
  • format:对视频来说,这是一个指明像素格式(AVPixelFormat)的枚举值;对音频来说,这是一个指明样本格式(AVSampleFormat)的枚举值。
  • key_frame:一个标志,指示这个帧是否是关键帧。
  • pts:表示这个帧的演示时间戳,基于编解码器上下文的 time_base
  • best_effort_timestamp:解码器设置的最佳尝试时间戳,这有助于无 pts 的情况下估算实际的时间戳。
  • pkt_duration:基于流的 time_base,这个帧的持续时间。
  • pkt_pos:帧原始数据在媒体文件中的位置,用于调试。
  • channel_layout:对于音频帧,指示声道布局。
  • channelssample_rate:音频帧的声道数和样本率。
  • metadata:帧相关的元数据。

AVFrame 通常用在视频/音频解码器的输出端或编码器的输入端。它提供了一个通用并且灵活的方式来处理未压缩的音视频数据。

FFmpeg 面向对象思想

封装(Encapsulation)

FFmpeg 中的数据结构像 AVCodecContext, AVFrame, AVPacket 等封装了与编解码器、数据帧和数据包相关的状态和行为。对这些结构体的操作通常通过 API 函数进行,而不是直接访问内部成员,这类似于面向对象中的封装概念。

继承(Inheritance)

虽然 C 不支持正式的继承,FFmpeg 使用了指针来模拟继承。例如,所有的解码器(AVCodec)都有共同的函数和成员,但也可以定义额外的成员来扩展基本功能,类似于在面向对象编程中的基类和派生类概念。

多态(Polymorphism)

FFmpeg 通过函数指针在结构体中实现了多态。比如 AVCodec 结构体中包含多个函数指针,编解码器可以通过设置这些指针到特定函数来定义特定的行为。例如,不同的编解码器可以有不同的 encode2decode 函数实现。这使得代码可以在运行时根据不同编解码器的具体实现选择正确的函数来调用。

抽象(Abstraction)

FFmpeg 抽象出了一套 API,隐藏了底层的复杂实现细节,提供了相对简单的接口供开发者使用。例如,不管背后具体是什么编解码器,开发者都可以通过统一的 avcodec_open2avcodec_decode_video2avcodec_encode_audio2 等函数来执行操作。

总而言之,即使 FFmpeg 是用 C 语言编写的,它也在设计上模拟了一些 OOP 的概念,提高了代码的模块性、可读性和可维护性。然而,需要注意的是,这种模拟并不等同于真正的面向对象编程语言中的实现,如 C++,其中有对继承、多态、封装等特性的原生支持。

Packet/Frame 数据零拷贝

“零拷贝”(Zero-Copy)是一种优化技术,旨在降低数据传输过程中的CPU消耗,减少内存使用,降低延迟。在处理 AVPacketAVFrame 数据时,零拷贝的目的是尽可能减少数据从一个缓冲区到另一个缓冲区的复制操作。

如何实现零拷贝:

  1. 引用计数(Reference Counting): FFmpeg 为 AVFrameAVPacket 结构提供了引用计数机制。通过增加引用计数来“共享”数据而不实际复制数据,这可以通过使用 av_frame_refav_packet_ref 函数来实现。

  2. 直接缓冲区访问(Direct Buffer Access): 当解码器或编码器处理数据时,而不需要真正的数据所有权转移,它可以直接在原始缓冲区上操作,通过提供数据的指针而不是数据本身的副本。

  3. 自定义缓冲区分配(Custom Buffer Allocation): 通过接管缓冲区的分配和释放,用户可以控制数据的存储方式,例如,可以重用或池化 buffers 来减少频繁的内存分配。

  4. 内存映射(Memory Mapped I/O): 在适当的硬件和操作系统支持下,可以直接映射文件或设备内存到进程的地址空间,从而避免了数据在内核和用户空间之间的拷贝。

FFmpeg 中零拷贝的例子:

  • 解码器零拷贝:某些解码器支持零拷贝解码,即直接在 GPU 的内存中解码视频帧,然后通过硬件加速接口如 VDPAU, DXVA2, 或 VideoToolbox 将帧传递给显示或者进一步处理。

  • 过滤器零拷贝:FFmpeg 的过滤器图可以被配置为在过滤器之间传递 AVFrame 的引用而非数据副本。

  • 硬件加速:使用 FFmpeg 的硬件加速API(如 AVHWDeviceContext 和 AVHWFramesContext)可以实现数据在硬件加速的解码器和编码器间的零拷贝传输。

参考资料:

音视频流媒体开发课程(从基础到高级,从理论到实践)学习计划、一对一答疑
音视频开发(FFmpeg/WebRTC/RTMP)

整理了一些音视频开发学习资料、面试题 如有需要自行添加群:739729163 领取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/763399.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

牛客周赛 Round 37 E.魔法之森的蘑菇

广搜板子&#xff0c;加个方向就好了 注意多测清空问题 #include<bits/stdc.h> using namespace std; using ll long long; #define int long long const int N 1e510; const int inf 0x3f3f3f3f; const int mod 1e97;int n,m; char g[1010][1010]; bool vis[1010][1…

CI/CD实战-jenkins部署 3

安装 软件下载地址&#xff1a;Index of /jenkins/redhat/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 启动服务 安装推荐插件 不新建用户&#xff0c;使用admin账号登录 修改一下初始密码 新建项目测试 安装git命令 生成密钥 在gitlab中上传公钥 修改ssh 创建中…

蓝桥杯(3.22 刷真题)

P8682 [蓝桥杯 2019 省 B] 等差数列 RE是因为除以0的情况 import java.util.Arrays; import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();int[] res new int[n1];for(int i1;i&l…

城管智慧执法系统源码有演示,自主研发,功能完善,正版授权,可商用上项目。

城管智慧执法系统源码有演示&#xff0c;自主研发&#xff0c;功能完善&#xff0c;正版授权&#xff0c;可商用上项目。 一套数字化的城管综合执法办案系统源码&#xff0c;提供了案件在线办理、当事人信用管理、文书电子送达、沿街店铺视频智能分析等功能&#xff0c;全面赋能…

软考 网络工程师 每日学习打卡 2024/3/22

学习内容 第9章 网络操作系统与应用服务器 本章主要讲解&#xff1a;了Windows和Linux操作系统的基础知识&#xff0c;并详细讲述了常用的各种服务器的 配置方法。这一章的内容主要是在具体操作方面&#xff0c;网络工程师要能够熟练地配置各种网络服务 器&#xff0c;排除网络…

(2024,Mamba,DiT,之字扫描与空间连续性)ZigMa:之字形 Mamba 扩散模型

ZigMa: Zigzag Mamba Diffusion Model 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 1. 简介 2. 相关工作 3. 方法 3.1 背景&#xff1a;状态空间模型 3.2 扩散主干&…

6.shell case控制语句

case控制语句 1.什么是case case条件语句相当于多分支的if/elif/else条件语句&#xff0c;主要还是用来做条件判断的,常被应用于实现系统服务启动脚本。 case语句中&#xff0c;会将case获取的变量值与表达式部分的值1、值2、值3等逐个进行比较&#xff0c;如果变量值和某个表…

【算法刷题】Day33

文章目录 1. 最长湍流子数组题干&#xff1a;算法原理&#xff1a;1. 状态表示&#xff1a;2. 状态转移方程3. 初始化4. 填表顺序5. 返回值 代码&#xff1a; 2. 最长递增子序列题干&#xff1a;算法原理&#xff1a;1. 状态表示&#xff1a;2. 状态转移方程3. 初始化4. 填表顺…

自学Python第二十九天-feapder框架创建爬虫

自学Python第二十九天-feapder框架创建爬虫 安装feapder 的设计架构feapder 框架的简单使用简单创建爬虫简单爬取数据简单的数据保存 中间件校验浏览器渲染使用浏览器渲染获取接口数据 feapder 项目 feapder是一款上手简单&#xff0c;功能强大的 Python爬虫框架&#xff0c;内…

鸿蒙Harmony应用开发—ArkTS-全局UI方法(日期滑动选择器弹窗)

根据指定的日期范围创建日期滑动选择器&#xff0c;展示在弹窗上。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 本模块功能依赖UI的执行上下文&#xff0c;不可在UI上下文不明确的地方使用&…

【JS】如何避免输入中文拼音时触发input事件

现有一段代码&#xff0c;监听input事件。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" con…

SpringBoot+Vue项目(后端项目搭建 + 添加家居)

文章目录 1.使用版本控制管理该项目1.创建远程仓库2.克隆到本地 2.后端项目环境搭建1.创建一个maven项目2.删除不必要的文件夹3.pom.xml文件引入依赖4.application.yml 配置数据源&#xff08;注意&#xff0c;数据库名还没写&#xff09;5.com/sun/furn/Application.java 编写…

蓝桥杯算法心得——游戏(优先队列)

大家好&#xff0c;我是晴天学长&#xff0c;优先队列的题&#xff0c;式子化简非常重要&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 1) .游戏 2) .算法思路 附近最小 1.接收数据 2.找出最小的&#…

安防监控平台EasyCVR使用管理员权限登录后,平台菜单栏显示不全是什么原因?

安防视频监控系统EasyCVR视频综合管理平台&#xff0c;采用了开放式的网络结构&#xff0c;平台能在复杂的网络环境中&#xff08;专网、局域网、广域网、VPN、公网等&#xff09;将前端海量的设备进行统一集中接入与视频汇聚管理&#xff0c;平台支持设备通过4G、5G、WIFI、有…

I2C协议

一.硬件连接 I2C必须使用开漏&#xff08;或集电极开路&#xff09;的引脚&#xff0c;其引脚框图如下所示。 SCL0对应78K0的P6.0引脚&#xff0c;SDA0对应78K0的P6.1引脚。 在使用开漏引脚通信时&#xff0c;需注意如下事项&#xff1a; 1&#xff09;两条总线须外接…

【Java - API - 多线程】(01) 通过Java 8完成多线程的创建,快速上手

通过"Java 8"完成多线程的创建&#xff0c;快速上手&#xff1b; 实操 【前提】 使用"Windows 11"系统通过"IntelliJ IDEA"软件完成&#xff1b; 【目录】 “方式1”&#xff1a;通过继承"Thread"完成-正常操作&#xff1b;“方式2”…

【QT入门】 Qt自定义信号后跨线程发送信号

往期回顾&#xff1a; 【QT入门】 lambda表达式(函数)详解-CSDN博客 【QT入门】 Qt槽函数五种常用写法介绍-CSDN博客 【QT入门】 Qt实现自定义信号-CSDN博客 【QT入门】 Qt自定义信号后跨线程发送信号 由于Qt的子线程是无法直接修改ui&#xff0c;需要发送信号到ui线程进行修改…

【Unity投屏总结】投屏方案总结

【背景】 想方便自己在VR中工作&#xff0c;打算做一个能够挂多个屏幕的远程控制VR桌面。研究下来发现细分场景有很多&#xff0c;有点鱼和熊掌不可兼得的意味&#xff0c;细分如下。 【投屏场景与解决方案】 希望多人能够同时观看我的屏幕&#xff0c;也就是一屏投多屏&…

C语言知识复盘— 递归算法 | 函数 | 数组

递归 ○ 算法思想&#xff1a; 递归算法分为两大阶段 : 递和归&#xff0c;即就是有去&#xff08;递去&#xff09;有回&#xff08;归来&#xff09;。 递去&#xff1a;将递归问题分解为若干个规模较小,与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决。归…

spring cloud项目微服务间互相调用使用自定义标注进行鉴权方案

来吧&#xff0c;贴代码。 一、背景 我们有一个项目使用了spring cloud&#xff0c;有的微服务需要调用别的微服务&#xff0c;但这些调用没有鉴权&#xff1b;当初项目时间非常紧&#xff0c;同时这部分微服务有的对外也没有鉴权&#xff0c;在代码中设置了无须鉴权&#xf…