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

REDPlayer 鸿蒙原生视频播放库组件介绍与使用指南

REDPlayer 是由小红书独立开发的一款跨平台媒体播放器,支持 Android、iOS、HarmonyOS 等多种操作系统。与市面上其他媒体播放器相比,REDPlayer 具有简洁的结构、低耦合度和清晰的功能边界的特点,这使得它在集成与开发上更加灵活。此外REDPlayer 还支持多种音视频协议和格式,能够满足不同场景下的需求。

接下来,本文将详细介绍 REDPlayer 的安装、功能特点以及集成与使用的具体步骤,帮助开发者快速上手并根据需要进行扩展。并以一个爱影家影视app的练手项目为例,详细介绍下REDPlayer 的使用。

REDPlayer 三方库地址:https://ohpm.openharmony.cn/#/cn/detail/@rte-xhs%2Fredplayer

爱影家App的项目地址: https://gitcode.com/nutpi/hmmovie

在这里插入图片描述

REDPlayer 介绍

REDPlayer 是一款专门为开发人员设计的媒体播放器组件,其主要目的是提供一个简洁、清晰的播放器结构,使得开发人员能够快速理解并在此基础上进行功能扩展。REDPlayer 的设计理念是简单易用,同时支持丰富的媒体格式和协议,满足从学习到商业应用的多样化需求。具体来说,REDPlayer 支持以下功能:

  • 丰富的媒体格式:支持包括 FLV、HLS、MP4、MP3、Vorbis 在内的多种音频和视频格式。
  • HDR 支持:提供对 HDR10/HLG 等 HDR 格式的支持,根据不同的设备型号提供播放和分发支持。
  • URL 播放:支持通过 URL 播放本地和网络视频。
  • 日志报告:支持播放器日志的报告和播放点信息相关的统计。
  • 异常分析:通过日志分析获取对应的异常信息。
  • 硬件解码:支持 H.264 和 H.265 视频源的播放和硬件解码。
  • 自动切换解码方式:当终端不支持硬件解码时,会自动切换到软件解码。
  • 播放控制:支持播放控制功能,包括开始、暂停、恢复和停止等。
  • 精准定位:提供精准的跳转功能,能够精确到帧级别。
  • 动态丢帧:当帧率超过 60fps 时,开始动态丢帧。
  • 循环播放:支持视频播放结束后的自动循环播放,但需要进行参数配置。
  • 自适应码率:在播放 HLS/DASH 时,支持根据带宽预测自动选择播放码率,但目前只支持播放前的码率选择。
  • 加密流播放:支持对加密流的按需转码,但需要进行定制开发。
安装 REDPlayer for HarmonyOS

在 HarmonyOS 平台上,开发者可以通过 ohpm(OH Package Manager)来安装 REDPlayer。ohpm 是 HarmonyOS 的包管理工具,用于管理和安装应用的依赖包。安装 REDPlayer 的命令如下:

ohpm install @rte-xhs/redplayer

执行上述命令后,ohpm 会自动从指定的仓库下载并安装 REDPlayer SDK,使得开发者可以在自己的项目中引用和使用。

REDPlayer 的使用

REDPlayer 的集成与使用步骤如下:

第一步:构建 SDK HAR 文件

首先需要配置编译环境,这可以通过编辑 build.sh 文件来完成。配置完成后,运行 build.sh 脚本,HAR 文件(HarmonyOS Archive)会在 /output/redplayerproxy.har 路径下生成。HAR 是 HarmonyOS 的标准库文件格式,用于在应用中集成和使用第三方库。

第二步:将 SDK HAR 集成到项目中
  1. 将 HAR 文件复制到项目的目标模块下的 libs 文件夹。
  2. 打开项目目标模块下的 oh-package.json5 文件,添加 HAR 文件作为依赖:
{// ..."dependencies": {"@xhs/redplayer": "file:./libs/redplayerproxy.har" // your HAR path}// ...
}
  1. 从 REDPlayer SDK HAR 文件中导入所需的模块:
import { IMediaPlayer } from '@xhs/redplayer'
第三步:初始化 RedPlayer 全局配置

在使用 REDPlayer 之前,需要先初始化其组件。通常建议在应用启动时(即在 AbilityStage#onCreate 方法中)进行初始化,以确保在应用启动时就能准备好播放器。

class YourAbilityStage extends AbilityStage {onCreate() {let configBuilder = new RedPlayerConfigBuilder()configBuilder.cachePath = 'your player cache path'configBuilder.logger = 'your custom logger'// TIPS: See more global config in RedPlayerInitManager.RedPlayerConfigBuilderRedPlayerInitManager.initPlayer(configBuilder) // Init RedPlayerRedPlayerPreload.globalInit('your preload cache path', 20, 4, 0) // Init RedPlayer preload component}
}
第四步:创建播放器并配置

创建播放器实例并设置监听器:

let listener: IMediaPlayerStateListener = new YourListenerImpl()
let player: IMediaPlayer | null = null
RedPlayerFactory.createMediaPlayer(listener).then((p) => {// player createdplayer = p
})
第五步:添加和配置播放渲染视图

在页面中添加 XComponent 并配置其用于播放渲染:

@Entry
@Component
struct PlayerPage {// step1: create controller for XComponentxComponentController: RedPlayerXComponentController = new RedPlayerXComponentController()build() {Stack() {// ...// step2: add and config XComponent to your page for player renderXComponent({id: this.xComponentController.getSurfaceId(),type: this.xComponentController.getSurfaceType(),libraryname: this.xComponentController.getLibraryName(),controller: this.xComponentController}).onLoad(() => {// step3: setSurfaceId to player after XComponent onLoadplayer.setSurfaceId(this.xComponentController._id)// if AVPlayer core:// this.playerController.setSurfaceId(this.xComponentController.getXComponentSurfaceId())}).width('100%').height('100%')// ...}}
}
第六步:设置播放源并进行配置

设置播放源及配置选项:

// Must call after player created
player.setDataSource({url: this.url, // url to play useSoftDecoder: false, // If use soft decoder to play. Hard decoder is used by default// other play option
}).then(() => {// source data set finished
})

在开发过程中,如果使用的是 DevEco Studio 模拟器进行调试,建议强制使用软件解码,因为目前模拟器不支持硬件解码。

第七步:准备播放器

准备播放器以进行播放:

// Must call after source data set finished
player.prepare().then(() => {// player prepared
})
第八步:开始播放

当播放器准备好后,可以调用 start() 方法开始播放:

// Must call after player prepared
player.start()
第九步:其他核心播放控制 API

REDPlayer 提供了丰富的控制 API,可以用来控制播放器的行为:

player.pause() // 暂停播放
player.stop() // 停止播放
player.release() // 释放播放器资源
简单示例

下面是一个简单的 REDPlayer 使用示例,展示了如何控制播放器以及如何配置播放视图:

播放控制部分:
let listener: IMediaPlayerStateListener = new YourListenerImpl()
RedPlayerFactory.createMediaPlayer(listener).then((player) => {player.setDataSource({url: this.url, // 要播放的 URL}).then(() => {// 源数据设置完毕player.prepare().then(() => {// 播放器准备完毕player.start()})})
})
播放视图部分:
@Entry
@Component
struct PlayerPage {// step1: create controller for XComponentxComponentController: RedPlayerXComponentController = new RedPlayerXComponentController()build() {Stack() {// ...// step2: add and config XComponent to your page for player renderXComponent({id: this.xComponentController.getSurfaceId(),type: this.xComponentController.getSurfaceType(),libraryname: this.xComponentController.getLibraryName(),controller: this.xComponentController}).onLoad(() => {// step3: setSurfaceId to player after XComponent onLoadplayer.setSurfaceId(this.xComponentController._id)// if AVPlayer core:// this.playerController.setSurfaceId(this.xComponentController.getXComponentSurfaceId())}).width('100%').height('100%')// ...}}
}

爱影家影视播放RedPlayer 使用

RedPlayer 是一个跨平台的媒体播放器 SDK,支持多种音视频格式和协议,具有高性能和易于集成的特点。

在这里插入图片描述

以下为使用RedPlayer 实现的播放源码:

import { VideoItem } from "../../common/bean/ApiTypes";
import { window } from "@kit.ArkUI";
import { common } from "@kit.AbilityKit";
import { EpisodeDialogView } from "../../common/dialog/EpisodeDialogView";
import { IMediaPlayer, RedPlayerConfigBuilder, RedPlayerFactory, RedPlayerInitManager, RedPlayerXComponentController } from "@rte-xhs/redplayer";@Builder
export function VideoPlayerPageBuilder() {return VideoPlayer();
}@Component
struct VideoPlayer {@State title: string = '';private context: common.BaseContext = getContext(this) as common.BaseContext;private controller: IMediaPlayer | undefined;@State previewUri: Resource = $r('app.media.play_circle_fill');@State videoSrc: string = 'http://staticpip.iyuba.cn/video/small/202412/1009544_c.mp4'; // 使用时请替换为实际视频加载网址@State tvUrls?: string[] = [];@State isTv: boolean = false;@State tvIndex: number = 0;private description: string = '';private rawTitle: string = '';private isToggle = false;@State toggleText: string = '';@State toggleBtn: string = '展开';@State isShowCtrlBar: boolean = false;aboutToAppear() {this.initRedPlayer();}async initRedPlayer() {let configBuilder = new RedPlayerConfigBuilder();configBuilder.cachePath = 'your player cache path';configBuilder.logger = 'your custom logger';RedPlayerInitManager.initPlayer(configBuilder); // 初始化 RedPlayerRedPlayerFactory.createMediaPlayer(this.listener).then((p) => {this.controller = p;this.setupVideoSource();});}private setupVideoSource() {if (this.controller) {this.controller.setDataSource({url: this.videoSrc,useSoftDecoder: false, // 硬件解码}).then(() => {this.controller.prepare().then(() => {this.controller.start();});});}}private changeOrientation(isLandscape: boolean) {let context = getContext(this) as common.UIAbilityContext;window.getLastWindow(context).then((lastWindow) => {lastWindow.setPreferredOrientation(isLandscape ? window.Orientation.LANDSCAPE : window.Orientation.PORTRAIT);});}// 电视剧集数选择框episodeDialogController: CustomDialogController = new CustomDialogController({builder: EpisodeDialogView({current: this.tvIndex,tvUrls: this.tvUrls,clickCallback: (item: string, index: number) => { this.onDialogClickCallback(item, index) }}),alignment: DialogAlignment.Bottom,offset: { dx: 0, dy: -30 },customStyle: true});// 电视剧集播放按钮回调private onDialogClickCallback(item: string, index: number) {this.videoSrc = item;this.tvIndex = index;this.title = this.rawTitle + ' 第' + (index + 1) + '集';this.episodeDialogController.close();this.setupVideoSource();}// 添加一个方法来获取按钮的背景颜色private getButtonColor(idx: number): Color {if (this.tvIndex === idx) {return Color.Blue; // 当前选中的按钮背景色为蓝色} else {return Color.White; // 其他按钮背景色为白色}}private listener: IMediaPlayerStateListener = {onPlayerError: () => {console.info("Video error.");this.keepScreenOn(false);},onPlayerStart: () => {this.keepScreenOn(true);},onPlayerPause: () => {this.keepScreenOn(false);},onFullscreenChange: (event) => {if (event.fullscreen) {this.changeOrientation(true);} else {this.changeOrientation(false);}},onTouch: () => {this.isShowCtrlBar = true;setTimeout(() => {this.isShowCtrlBar = false;}, 3000);}};async keepScreenOn(isOn: boolean) {let windowClass = await window.getLastWindow(this.context); // 获取窗口实例await windowClass.setWindowKeepScreenOn(isOn); // 设置窗口常亮或取消}build() {NavDestination() {Column() {Row() {Stack() {XComponent({id: this.xComponentController.getSurfaceId(),type: this.xComponentController.getSurfaceType(),libraryname: this.xComponentController.getLibraryName(),controller: this.xComponentController}).width('100%').height('100%').onLoad(() => {if (this.controller) {this.controller.setSurfaceId(this.xComponentController.getSurfaceId());}}).zIndex(1);Text(this.title).fontColor(Color.White).width('100%').padding(5).alignSelf(ItemAlign.Start).margin({ bottom: 280 }).zIndex(2);}}.width('100%').height('40%');Column() {Text('简介').fontSize(18).padding({ bottom: 10 }).fontWeight(FontWeight.Bold).alignSelf(ItemAlign.Start);Text(this.toggleText).fontSize(14).lineHeight(20).alignSelf(ItemAlign.Start);Text(this.toggleBtn).fontSize(14).fontColor(Color.Gray).padding(10).alignSelf(ItemAlign.End).onClick(() => {this.isToggle = !this.isToggle;if (this.isToggle) {this.toggleBtn = '收起';this.toggleText = this.description;} else {this.toggleBtn = '展开';this.toggleText = this.description.substring(0, 100) + '...';}});}.padding(10);if (this.isTv) {Flex({direction: FlexDirection.Row,justifyContent: FlexAlign.SpaceBetween,alignContent: FlexAlign.SpaceBetween}) {Text('选集').fontSize(18).fontWeight(FontWeight.Bold);SymbolGlyph($r('sys.symbol.more')).fontWeight(FontWeight.Lighter).fontSize(32).fontColor(['#fffab52a']).onClick(() => {this.episodeDialogController.open();});}.padding(10);Scroll() {Row({ space: 10 }) {ForEach(this.tvUrls, (item: string, idx) => {Button('第' + (idx + 1) + '集', { buttonStyle: ButtonStyleMode.NORMAL, role: ButtonRole.NORMAL }).borderRadius(5).fontColor('#fffab52a').width(80).height(40).padding(5).backgroundColor(this.getButtonColor(idx)).onClick(() => {this.onDialogClickCallback(item, idx);});});}.alignItems(VerticalAlign.Center).padding(10);}.scrollable(ScrollDirection.Horizontal);}}.width('100%');}.width("100%").height("100%").onReady(ctx => {interface params {item: VideoItem;}let par = ctx.pathInfo.param as params;this.videoSrc = par.item.video;this.rawTitle = par.item.title;this.title = this.rawTitle;this.description = par.item.desc;this.tvUrls = par.item.tvurls;if (this.tvUrls?.length ?? 0 > 0) {this.isTv = true;this.videoSrc = this.tvUrls?.[this.tvIndex] ?? '';}this.tvIndex = 0;this.toggleText = this.description.substring(0, 100) + '...';this.setupVideoSource();}).onShown(() => {console.info('VideoPlayer onShown');}).onBackPressed(() => {this.keepScreenOn(false);return false;});
}struct EpisodeDialogView {@Prop current: number;@Prop tvUrls: string[];@Prop clickCallback: (item: string, index: number) => void;build() {Column() {ForEach(this.tvUrls, (item: string, index: number) => {Button('第' + (index + 1) + '集').onClick(() => {this.clickCallback(item, index);}).width('100%').height(40).padding(10).fontColor(index === this.current ? Color.Blue : Color.Black);});}.padding(10).backgroundColor('#ffffff');}
}class YourListenerImpl implements IMediaPlayerStateListener {onPlayerError() {console.info("Video error.");}onPlayerStart() {console.info("Video started.");}onPlayerPause() {console.info("Video paused.");}onFullscreenChange(event: FullscreenChangeEvent) {console.info("Fullscreen change:", event.fullscreen);}onTouch() {console.info("Video touched.");}
}class RedPlayerXComponentController {private _id: string = '';private _type: string = '';private _libraryName: string = '';getSurfaceId(): string {return this._id;}getSurfaceType(): string {return this._type;}getLibraryName(): string {return this._libraryName;}setSurfaceId(id: string) {this._id = id;}
}

详细使用说明

1. 初始化 RedPlayer

aboutToAppear 生命周期方法中调用 initRedPlayer 方法来初始化 RedPlayer。

aboutToAppear() {this.initRedPlayer();
}
2. 配置 RedPlayer

initRedPlayer 方法中,首先创建并配置 RedPlayer 的全局配置,然后创建 IMediaPlayer 实例。

private initRedPlayer() {let configBuilder = new RedPlayerConfigBuilder();configBuilder.cachePath = 'your player cache path';configBuilder.logger = 'your custom logger';RedPlayerInitManager.initPlayer(configBuilder); // 初始化 RedPlayerRedPlayerFactory.createMediaPlayer(this.listener).then((p) => {this.controller = p;this.setupVideoSource();});
}
3. 设置视频源

在 RedPlayer 实例创建后,设置视频源并准备播放。

private setupVideoSource() {if (this.controller) {this.controller.setDataSource({url: this.videoSrc,useSoftDecoder: false, // 使用硬件解码}).then(() => {this.controller.prepare().then(() => {this.controller.start();});});}
}
4. 处理视频事件

定义 listener 对象来处理视频的各种事件,包括错误、开始、暂停和全屏变化。

private listener: IMediaPlayerStateListener = {onPlayerError: () => {console.info("Video error.");this.keepScreenOn(false);},onPlayerStart: () => {console.info("Video started.");this.keepScreenOn(true);},onPlayerPause: () => {console.info("Video paused.");this.keepScreenOn(false);},onFullscreenChange: (event) => {if (event.fullscreen) {this.changeOrientation(true);} else {this.changeOrientation(false);}},onTouch: () => {this.isShowCtrlBar = true;setTimeout(() => {this.isShowCtrlBar = false;}, 3000);}
};
5. 触摸事件处理

通过 onTouch 事件控制控制条的显示与隐藏。

.onTouch(() => {this.isShowCtrlBar = true;setTimeout(() => {this.isShowCtrlBar = false;}, 3000);
})
6. 使用 XComponent 渲染视频

在 HarmonyOS 应用中,使用 XComponent 来渲染视频内容。在 onLoad 事件中设置 surfaceId

XComponent({id: this.xComponentController.getSurfaceId(),type: this.xComponentController.getSurfaceType(),libraryname: this.xComponentController.getLibraryName(),controller: this.xComponentController
}).width('100%').height('100%').onLoad(() => {if (this.controller) {this.controller.setSurfaceId(this.xComponentController.getSurfaceId());}}).zIndex(1);
7. 电视剧集数选择框

使用 CustomDialogController 来管理电视剧集数选择对话框。

episodeDialogController: CustomDialogController = new CustomDialogController({builder: EpisodeDialogView({ current: this.tvIndex, tvUrls: this.tvUrls, clickCallback: (item: string, index: number) => { this.onDialogClickCallback(item, index) } }),alignment: DialogAlignment.Bottom,offset: { dx: 0, dy: -30 },customStyle: true
});
8. 电视剧集播放按钮回调

当用户选择某个集数后,更新 videoSrctvIndex,并重新设置视频源并开始播放。

private onDialogClickCallback(item: string, index: number) {this.videoSrc = item;this.tvIndex = index;this.title = this.rawTitle + ' 第' + (index + 1) + '集';this.episodeDialogController.close();this.setupVideoSource();
}
9. 获取按钮背景颜色

根据当前选中的集数,设置按钮的背景颜色。

private getButtonColor(idx: number): Color {if (this.tvIndex === idx) {return Color.Blue; // 当前选中的按钮背景色为蓝色} else {return Color.White; // 其他按钮背景色为白色}
}
10. 使用 NavDestinationColumn

build 方法中,使用 NavDestinationColumn 来构建页面布局。

build() {NavDestination() {Column() {Row() {Stack() {XComponent({id: this.xComponentController.getSurfaceId(),type: this.xComponentController.getSurfaceType(),libraryname: this.xComponentController.getLibraryName(),controller: this.xComponentController}).width('100%').height('100%').onLoad(() => {if (this.controller) {this.controller.setSurfaceId(this.xComponentController.getSurfaceId());}}).zIndex(1);Text(this.title).fontColor(Color.White).width('100%').padding(5).alignSelf(ItemAlign.Start).margin({ bottom: 280 }).zIndex(2);}}.width('100%').height('40%');Column() {Text('简介').fontSize(18).padding({ bottom: 10 }).fontWeight(FontWeight.Bold).alignSelf(ItemAlign.Start);Text(this.toggleText).fontSize(14).lineHeight(20).alignSelf(ItemAlign.Start);Text(this.toggleBtn).fontSize(14).fontColor(Color.Gray).padding(10).alignSelf(ItemAlign.End).onClick(() => {this.isToggle = !this.isToggle;if (this.isToggle) {this.toggleBtn = '收起';this.toggleText = this.description;} else {this.toggleBtn = '展开';this.toggleText = this.description.substring(0, 100) + '...';}});}.padding(10);if (this.isTv) {Flex({direction: FlexDirection.Row,justifyContent: FlexAlign.SpaceBetween,alignContent: FlexAlign.SpaceBetween}) {Text('选集').fontSize(18).fontWeight(FontWeight.Bold);SymbolGlyph($r('sys.symbol.more')).fontWeight(FontWeight.Lighter).fontSize(32).fontColor(['#fffab52a']).onClick(() => {this.episodeDialogController.open();});}.padding(10);Scroll() {Row({ space: 10 }) {ForEach(this.tvUrls, (item: string, index: number) => {Button('第' + (index + 1) + '集', { buttonStyle: ButtonStyleMode.NORMAL, role: ButtonRole.NORMAL }).borderRadius(5).fontColor('#fffab52a').width(80).height(40).padding(5).backgroundColor(this.getButtonColor(index)).onClick(() => {this.onDialogClickCallback(item, index);});});}.alignItems(VerticalAlign.Center).padding(10);}.scrollable(ScrollDirection.Horizontal);}}.width('100%');}.width("100%").height("100%").onReady(ctx => {interface params {item: VideoItem;}let par = ctx.pathInfo.param as params;this.videoSrc = par.item.video;this.rawTitle = par.item.title;this.title = this.rawTitle;this.description = par.item.desc;this.tvUrls = par.item.tvurls;if (this.tvUrls?.length ?? 0 > 0) {this.isTv = true;this.videoSrc = this.tvUrls?.[this.tvIndex] ?? '';}this.tvIndex = 0;this.toggleText = this.description.substring(0, 100) + '...';this.setupVideoSource();}).onShown(() => {console.info('VideoPlayer onShown');}).onBackPressed(() => {this.keepScreenOn(false);return false;});
}

总结

REDPlayer 是一款功能强大且易于集成的媒体播放器 SDK,适用于多种操作系统平台。通过本文的详细介绍,相信开发者们已经掌握了 REDPlayer 的基本使用和集成方法。REDPlayer 的低耦合度和模块化设计使得它成为了一个优秀的开发工具,无论是用于学习还是商业开发,都能提供强大的功能支持。开发者们可以通过自定义解码模块、渲染器等进一步扩展 REDPlayer 的功能,满足更多的应用场景需求。希望 REDPlayer 能够帮助开发者们在媒体播放领域取得更大的帮助。


文章转载自:

http://9ULiK8qA.ctxym.cn
http://tOVMp7Vk.ctxym.cn
http://Z0lUk4L5.ctxym.cn
http://9MBV5NzC.ctxym.cn
http://pSOvz2Uz.ctxym.cn
http://0jFDPzXL.ctxym.cn
http://tq0CtOrH.ctxym.cn
http://UTeRfri0.ctxym.cn
http://WQbw8O4l.ctxym.cn
http://8LonWwpB.ctxym.cn
http://SGIgMxLG.ctxym.cn
http://nXRUrP0j.ctxym.cn
http://Wpwat2JL.ctxym.cn
http://rFCHJQyC.ctxym.cn
http://v8L0r2Su.ctxym.cn
http://49QAM9RG.ctxym.cn
http://hErY6o7y.ctxym.cn
http://Xd2I797w.ctxym.cn
http://Dgm6qxfP.ctxym.cn
http://rSwde40h.ctxym.cn
http://dD9aDFMj.ctxym.cn
http://5yQYVVVJ.ctxym.cn
http://TjbDPtIl.ctxym.cn
http://PP5zssSI.ctxym.cn
http://BCLLlmMM.ctxym.cn
http://Swf1svVl.ctxym.cn
http://uL0vIkbm.ctxym.cn
http://eXqd5mlQ.ctxym.cn
http://TnYonkNr.ctxym.cn
http://AH4ld1tI.ctxym.cn
http://www.dtcms.com/a/378601.html

相关文章:

  • HarmonyOS 应用开发深度解析:ArkUI 声明式 UI 与现代化状态管理最佳实践
  • redis 入门-1
  • Json-rpc通信项目(基于C++ Jsoncpp muduo库)
  • TODO的面试(dw三面、sqb二面、ks二面)
  • Vibe Coding实战项目:用Qwen3-Coder做了个AI跳舞视频生成器
  • Vue 封装Input组件 双向通信
  • 【混合开发】进阶到【大前端++】
  • ZooKeeper Java客户端与分布式应用实战
  • 【复习】计网每日一题---传输层无连接不可靠服务
  • 2025年秋招答疑:AI面试如何破解在线作弊难题?
  • KafKa01:在Windows系统上安装Kafka
  • 【Big Data】Amazon S3 专为从任何位置检索任意数量的数据而构建的对象存储
  • C++:模版进阶
  • 【Canvas与旗帜】圆角红面白边蓝底梅花五星旗
  • 不同局域网远程桌面连接:设置让外网电脑直接windows自带远程桌面访问内网计算机,简单3步实现通用详细教程
  • set 认识及使用
  • 如何打造“高效、安全、精准、可持续”的智能化实验室?
  • 究竟什么时候用shared_ptr,什么时候用unique_ptr?
  • 前端抽象化,打破框架枷锁:react现代化项目中的思想体现
  • 基于开源AI智能名片、链动2+1模式与S2B2C商城小程序的流量运营与个人IP构建研究
  • gstreamer:创建组件、管道和总线,实现简单的播放器(Makefile,代码测试通过)
  • Kibana 双栈网络(Dual-Stack)支持能力评估
  • go 日志的分装和使用 Zap + lumberjack
  • 河北智算中心绿色能源占比多少?
  • 在能源互联网时代天硕工业级SSD固态硬盘为何更受青睐?
  • 关于rust的crates.io
  • 使用Rust实现服务配置/注册中心
  • C++ 类与对象(下):从构造函数到编译器优化深度解析
  • DNS 域名解析
  • EasyDSS重装系统后启动失败?解决RTMP推流平台EasyDss服务启动失败的详细步骤