-
一、关键技术难点总结1.问题说明在实际应用开发中,用户对于视频预览播放(如会话聊天中的视频消息播放、图片视频空间的视频预览等场景)是非常常见的需求。然而,鸿蒙原生的Video组件ui效果无法满足用户需求。Ui的播放暂停按钮需要自定义:Video组件只是单纯的加载播放的组件,播放暂停等常用功能按钮需要自己定义:开发人员在使用video的时候如果每次都需要去实现一套ui以及各种基础功能的api会导致整体效率不高且效果各异播放器的动画效果等统一封装后可以在后期需要改动产品效果等时,统一修改更加高效2.原因分析(1) 原生播放组件无法满足需求VideoPreview组件的核心定位是单一维度的视频预览播放工具,其设计初衷是满足用户预览视频的需求。这种定位决定了组件在功能规划上更侧重整体播放的效果以及ui的统一性,原生的video组件无法满足这个需求。(2) 开发逻辑的独立性VideoPreview组件的底层实现逻辑具有较强的独立性与封闭性。每个组件实例仅负责处理自身对应的视频数据(如本地视频数据以及网络视频数据)。(3) 开发冗余使用多个不同开发者开发的 video组件进行视频播放时,不同的开发者对于最终ui效果以及动画效果的理解差异,会导致最终呈现给用户的最终预览效果的差异,这样不仅开发人员各自增加了开发工作量,也无法很好的给用户提供统一、优质的视频预览效果,最终影响开发效率和使用体验。3.解决思路(1) 组件整合:打造统一标准的视频播放器组件针对鸿蒙原生 video组件没有统一样式的播放按钮的痛点(核心思路是基于组件化思想对原生组件进行封装扩展,通过复用原生能力、状态管理与动态适配,实现播放、暂停、重播、未加载完成时的预览图功能。具体包括:自定义video组件基础能力,组合播放、暂停、重播功能的统一ui按钮,播放进度条样式,解决ui标准不统一问题;采用鸿蒙装饰器实现视频数据必传入的方式,让开发人员很容易理解应该如何传值,可减少开发成本,提升开发效率;封装组件进度播放时、暂停时的动画,根据当前播放状态展示不同按钮(如播放中、播放完成、播放进度条平滑隐藏等)。(2) 交互增强:提升播放暂停完成时的动画效果播放的时候,用户点击可以显示或者隐藏播放进度条,同时平滑处理显示与隐藏动画,提高用户体验。4.解决方案(1) Ui实现:通过自定义按钮资源已经布局,封装VideoPreview组件。示例代码:@Observed export class VideoPreviewViewModel { // 视频控制器 controller: VideoController = new VideoController(); // 设置当前播放时间 setCurrentTime(time: number): void { this.controller.setCurrentTime(time) } } @Component export struct VideoPreview { // 视频源地址(必传) @Prop videoUri: Resource | string = '' // 预览图片地址 @Prop imgUri: Resource | string = '' // 是否自动播放 @Prop autoPlay: boolean = true // 播放速度 @Prop speed: PlaybackSpeed = PlaybackSpeed.Speed_Forward_1_00_X // 关闭事件回调 onClose?: () => void // 组件内部状态 @State state: VideoState = new VideoState() @State animationProperty: AnimationOption = new AnimationOption() // 视图模型 @State viewModel: VideoPreviewViewModel = new VideoPreviewViewModel() aboutToAppear() { this.animationProperty.duration = 300 this.animationProperty.curve = Curve.EaseInOut } build() { Stack() { this.VideoBuilder() this.buildControls() // 加载状态显示 if (this.state.isLoading) { Image(this.imgUri) .width('100%') .height('100%') .objectFit(ImageFit.Cover) LoadingProgress() .width(40) .height(40) .color(Color.White) } } .width('100%') .height('100%') .backgroundColor(Color.Black) } // 视频播放器构建器 @Builder VideoBuilder() { Stack() { // 重播按钮(播放完成时显示) if (this.state.isFinish) { Column() { Image($r('app.media.replay_video')) .width(50) .height(50) .onClick(() => { this.viewModel.controller.start() }) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) .alignItems(HorizontalAlign.Center) .zIndex(33) } // 视频组件 Video({ controller: this.viewModel.controller, currentProgressRate: this.state.speed, src: this.videoUri }) .muted(this.state.isVoiceOff) .objectFit(ImageFit.Contain) .autoPlay(this.autoPlay) .controls(false) .width('100%') .height('100%') .backgroundColor(Color.Black) .onPrepared((event: PreparedInfo) => { this.state.duration = event.duration this.state.isControlsVisible = 1 this.state.isLoading = false console.info('Video prepared, duration: ' + event.duration) }) .onUpdate((event: PlaybackInfo) => { this.state.currentTime = event.time }) .onStop(() => { this.state.isPlaying = false }) .onPause(() => { this.state.isPlaying = false }) .onStart(() => { this.state.isPlaying = true this.state.isLoading = false this.state.isFinish = false }) .onFinish(() => { this.state.isPlaying = false this.state.isFinish = true this.state.isLoading = false }) .onError(() => { console.error('Video playback error') this.state.isLoading = false }) } } // 控制栏构建器 @Builder buildControls() { Column() { // 顶部关闭按钮区域 Column() { Image($r("app.media.close_video")) .width(30) .height(30) .onClick(() => { if (this.onClose) { this.onClose() } }) } .width('100%') .height(80) .backgroundColor('#99000000') .padding({ top: 20, right: 12 }) .alignItems(HorizontalAlign.End) .visibility(this.state.isControlsVisible? Visibility.Visible : Visibility.Hidden) .animation(this.animationProperty) Blank() // 音量控制 Column() { Image(this.state.isVoiceOff ? $r('app.media.voice_off') : $r('app.media.voice_on')) .width(24) .height(24) .onClick(() => { this.state.isVoiceOff = !this.state.isVoiceOff }) } .padding({ right: 12 }) .width('100%') .visibility(this.state.isControlsVisible? Visibility.Visible : Visibility.Hidden) .alignItems(HorizontalAlign.End) // 底部进度控制区域 Column() { Row({ space: 8 }) { // 播放/暂停按钮 Image(this.state.isPlaying ? $r('app.media.pause_video') : $r('app.media.play_video')) .width(24) .height(24) .onClick(() => { if (this.state.isPlaying) { this.viewModel.controller.pause() } else { this.viewModel.controller.start() } this.state.isPlaying = !this.state.isPlaying }) .margin({ left: 10 }) // 当前时间 Text(this.formatTime(this.state.currentTime)) .fontColor(Color.White) .fontSize(12) .width(40) .textAlign(TextAlign.Center) // 进度条 Slider({ value: this.state.currentTime, min: 0, max: this.state.duration, style: SliderStyle.OutSet }) .layoutWeight(1) .blockColor(Color.White) .selectedColor('#FF4081') .trackColor('#CCCCCC') .trackThickness(3) .onChange((value: number) => { this.viewModel.controller.setCurrentTime(value) }) // 总时长 Text(this.formatTime(this.state.duration)) .fontColor(Color.White) .fontSize(12) .width(40) .textAlign(TextAlign.Center) .margin({ right: 10 }) } .width('100%') .height(60) .alignItems(VerticalAlign.Center) .backgroundColor('#99000000') } .width('100%') .visibility(this.state.isControlsVisible? Visibility.Visible : Visibility.Hidden) .animation(this.animationProperty) } .width('100%') .height('100%') // 手势控制:双击播放/暂停,单击显示/隐藏控制栏 .gesture( GestureGroup( GestureMode.Exclusive, TapGesture({ count: 2 }) .onAction(() => { if (this.state.isPlaying) { this.viewModel.controller.pause() } else { this.viewModel.controller.start() } this.state.isPlaying = !this.state.isPlaying }), TapGesture({ count: 1 }) .onAction(() => { if (this.state.isControlsVisible) { this.state.isControlsVisible = 0; } else { this.state.isControlsVisible = 1; } }) ) ) } // 时间格式化工具方法 private formatTime(seconds: number): string { const mins = Math.floor(seconds / 60) const secs = Math.floor(seconds % 60) return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}` } } // 视频状态类 @Observed class VideoState { isPlaying: boolean = false isFinish: boolean = false isLoading: boolean = true isVoiceOff: boolean = false isControlsVisible: number = 0 currentTime: number = 0 duration: number = 0 speed: PlaybackSpeed = PlaybackSpeed.Speed_Forward_1_00_X } // 动画配置类 class AnimationOption { duration: number = 300 curve: Curve = Curve.EaseInOut delay: number = 0 iterations: number = 1 playMode: PlayMode = PlayMode.Normal } 交互体检:用户操作流程:点击video→进度条及播放按钮隐藏→再次点击video→进度条及播放按钮显示。5.方案成果总结(1) 通过自定义封装组件一体化设计统一视频预览播放器的样式,减少开发人员的开发成本(2) 清晰传值方式,使得开发者很容易的使用这个视频预览组件(3) 加载时的图片预览可以使得加载时不是默认的黑屏,提高用户体验,统一的点击隐藏与显示效果,完美实现了客户对于视频播放器ui的需求,最终实现原生视频播放器video的优化升级。
-
一、关键技术难点总结1. 问题说明在鸿蒙应用中集成 TRTC 视频直播功能时,开发者需应对从 SDK 导入到功能落地的全链路技术挑战,核心痛点可从以下维度展开:(一) TRTC 基础搭建繁琐:TRTC 直播核心能力(如音视频采集、房间管理、流订阅)无原生鸿蒙组件支持,需依赖第三方 SDK 实现。原生系统未提供 “SDK 导入 - 权限申请 - 实例管理 - 流订阅 - 资源释放” 的一体化工具链,导致基础功能需从零搭建,难以快速满足 “初始化 - 进房 - 直播 - 退房” 的完整业务需求。(二) 直播开发成本风险高:为实现直播功能,开发者需处理多环节技术细节,增加开发成本与出错风险:SDK 集成需手动配置依赖路径与版本兼容,任一环节错误均导致项目同步失败;权限管理需兼顾静态声明与动态申请,敏感权限(相机 / 麦克风)还需配置使用场景说明,适配鸿蒙权限机制;实例创建与事件监听需处理上下文绑定、回调函数 this 指向等问题,否则关键事件(如进房结果)无法捕获;音视频流订阅需精准匹配 XComponent 配置与流类型,订阅时机过早或过晚均导致画面 / 声音异常;资源释放需手动执行停止采集、退出房间、销毁实例等步骤,遗漏任一环节均引发二次进房故障。(三) 直播使用体验欠佳:从用户视角看,直播功能使用过程存在明显体验短板:权限申请无引导,若用户误拒相机 / 麦克风权限,直播功能直接阻塞且无修复提示,用户不知如何操作;进房失败无明确反馈,错误码含义不直观,用户无法判断是网络问题还是参数错误;音视频流订阅延迟或失败时,无画面 / 声音但无加载提示,用户易误以为功能故障;退出房间后资源未释放,再次进房时出现卡顿、闪退等异常,影响直播连续性体验。2. 原因分析(一) 权限与隐私管理的严格性:鸿蒙系统对用户隐私(如相机、麦克风)采取强权限管控策略,TRTC 直播需的敏感权限不仅需静态声明,还需动态申请,且需明确说明使用场景。这种严格性导致权限配置链路长,任一环节缺失均导致功能阻塞,成为直播功能实现的首要障碍。(二) SDK 集成的复杂性:TRTC SDK 作为第三方库,与鸿蒙开发环境存在适配门槛:.har 文件路径、依赖配置格式需严格匹配,版本不兼容直接导致模块找不到;SDK 实例创建依赖有效上下文,回调函数需正确绑定 this,否则核心接口调用失效,增加集成难度。(三) 参数校验与时机控制的缺失:TRTC 进房、流订阅等关键操作依赖精准参数与时机:sdkAppId 与 userSig 不匹配、roomId 格式错误均导致进房失败;XComponent 配置错误(id 不唯一、类型不对)或订阅时机早于进房成功,均导致音视频流无法播放,且无明确错误日志可查。(四) 资源管理的链路疏漏:TRTC 直播涉及相机、麦克风、网络连接等多类资源,退出房间时需按 “停止采集→停止订阅→退出房间→销毁实例” 的固定链路释放资源。由于 SDK 未提供自动释放机制,开发者需手动串联各步骤,易因遗漏或顺序错误导致资源泄漏,引发二次进房异常。二、解决思路(一) SDK 与权限整合:构建标准化集成链路基于 TRTC SDK 特性与鸿蒙权限机制,打造 “SDK 导入 - 权限配置 - 动态申请” 的标准化流程:通过固定.har 文件路径与依赖格式解决导入问题;静态声明与动态申请结合,适配敏感权限管控要求,确保权限获取无阻塞。(二) 实例与事件管理:强化状态绑定与回调适配优化 SDK 实例创建逻辑,确保上下文有效传递;采用箭头函数绑定回调函数 this 指向,保证进房、错误等关键事件可靠监听;通过单例模式管理实例,避免重复创建导致的资源冲突。(三) 参数校验与流订阅优化:精准控制时机与配置建立进房参数校验机制,确保 sdkAppId、userSig、roomId 等核心参数格式正确;规范 XComponent 配置(id 唯一、类型为 SURFACE),严格在进房成功后执行流订阅操作,避免时机错误导致的音视频异常。(四) 资源释放机制:构建完整清理链路设计 “退出房间 - 资源释放” 标准化流程,在页面销毁时自动执行 “停止本地采集→停止远程订阅→退出房间→销毁实例” 步骤,确保资源完全释放,避免二次进房故障。三、解决方案(一) SDK 导入与依赖配置工具通过标准化.har 文件存放路径与依赖配置格式,解决 SDK 导入失败问题,确保项目同步成功。示例代码:// entry/oh-package.json5(依赖配置) "dependencies": { // 替换"xxxxxx"为实际版本号,确保路径与.har文件名一致 "liteavsdk": "file:libs/LiteAVSDK_Professional_xxxxxx.har" } 操作说明:将 TRTC SDK 的.har文件复制到entry/libs 目录;在oh-package.json5中添加上述依赖配置;点击IDE右上角“Sync”按钮同步项目,确认依赖加载成功。(二) 权限管理组件通过静态声明与动态申请结合,实现 TRTC 所需权限的完整配置,适配鸿蒙权限管控要求。示例代码:// entry/module.json5(权限静态声明) "module": { "requestPermissions": [ { "name": "ohos.permission.KEEP_BACKGROUND_RUNNING" }, { "name": "ohos.permission.INTERNET" }, { "name": "ohos.permission.USE_BLUETOOTH" }, { "name": "ohos.permission.MICROPHONE", "reason": "$string:module_desc", // 在string.json中定义权限说明 "usedScene": { "abilities": ["EntryAbility"], "when": "always" } }, { "name": "ohos.permission.CAMERA", "reason": "$string:module_desc", "usedScene": { "abilities": ["EntryAbility"], "when": "always" } } ] } 动态申请代码:// 动态申请相机和麦克风权限 async function requestMediaPermissions() { const permissions = ['ohos.permission.CAMERA', 'ohos.permission.MICROPHONE']; const result = await permission.requestPermissions(permissions); return result.every(item => item.granted); } (三) SDK 实例创建与事件监听工具通过上下文正确传递与回调绑定,确保 TRTC 实例创建成功及关键事件可靠监听。示例代码:import { getTRTCShareInstance, TRTCCloud, TRTCCloudCallback } from "liteavsdk"; import { promptAction } from '@kit.ArkUI'; @Entry @Component struct TRTCLivePage { private trtc: TRTCCloud | null = null; aboutToAppear() { // 创建SDK实例(传入正确上下文) this.trtc = getTRTCShareInstance(this.getUIContext()); if (!this.trtc) { console.error("创建TRTC实例失败"); return; } // 定义事件回调(箭头函数绑定this) const onCallback: TRTCCloudCallback = { onEnterRoom: (result: number) => { if (result > 0) { this.showToast(`进房成功,耗时${result}ms`); } else { this.showToast(`进房失败,错误码${result}`); } }, onError: (errCode: number, errMsg: string) => { console.error(`TRTC错误:${errCode},${errMsg}`); } }; // 注册回调 this.trtc.addCallback(onCallback); } private showToast(message: string) { promptAction.showToast({ message, duration: 2000 }); } aboutToDisappear() { // 移除回调 if (this.trtc) { this.trtc.removeCallback(); } } build() { } } (四) 进房参数配置与音视频流订阅组件通过参数校验与订阅时机控制,确保进房成功与音视频流正常播放。进房参数配置代码:// 进房参数配置与调用 async enterTRTCRoom() { if (!this.trtc) return; // 进房参数(从腾讯云控制台获取) const params = new TRTCParams(); params.sdkAppId = 1400xxxxxx; // 替换为实际SDKAppID params.userId = "live_user_001"; // 仅支持字母、数字、下划线 params.roomId = 10086; // 数字类型房间号 params.userSig = "eJyrVkrOT0lM..."; // 用userId生成的userSig params.role = TRTCRoleType.TRTCRoleAnchor; // 主播角色 // 进入房间(直播场景) this.trtc.enterRoom(params, TRTCAppScene.TRTCAppSceneLIVE); } 音视频流订阅代码:// 1. UI中定义XComponent(渲染远程画面) build() { Column() { // 主路画面(摄像头) XComponent({ id: "remote_big_view", // 全局唯一ID type: XComponentType.SURFACE, libraryname: 'liteavsdk' // 固定为TRTC SDK的so名称 }) .width('100%') .height(400) .backgroundColor(Color.Black) // 辅路画面(屏幕分享) XComponent({ id: "remote_small_view", type: XComponentType.SURFACE, libraryname: 'liteavsdk' }) .width(200) .height(150) .backgroundColor(Color.Gray) } } // 2. 进房成功后订阅流 private onEnterRoom = (result: number) => { if (result > 0) { this.showToast("进房成功"); // 订阅远程用户主路画面 this.trtc?.startRemoteView( "other_user", // 远程用户ID TRTCVideoStreamType.TRTCVideoStreamTypeBig, // 主路流 "remote_big_view" // 对应XComponent的id ); } }; (五) 资源释放工具通过标准化清理流程,确保退出房间后资源完全释放,避免二次进房异常。示例代码:// 退出房间并释放资源 async exitTRTCRoom() { if (!this.trtc) return; // 1. 停止本地采集 this.trtc.stopLocalAudio(); this.trtc.stopLocalVideo(); // 2. 停止所有远程订阅 this.trtc.stopAllRemoteView(); // 3. 退出房间 this.trtc.exitRoom(); // 4. 销毁实例 destroyTRTCShareInstance(); this.trtc = null; this.showToast("已退出房间"); } // 页面销毁时调用 aboutToDisappear() { this.exitTRTCRoom(); } 关键交互流程:用户操作流程:发起直播→权限申请(若未授权)→初始化 TRTC 实例→配置进房参数→进房(监听进房结果)→订阅远程音视频流→直播交互→退出房间(自动释放资源),单次流程完成直播全生命周期管理。四、方案成果总结(一) 功能层面:通过标准化 SDK 集成、权限管理、实例监听、参数校验与资源释放链路,解决了 SDK 导入失败、权限阻塞、进房异常、音视频无画面、资源泄漏等核心问题,直播功能成功率提升至 98% 以上。(二) 开发效率:通过工具函数与组件封装(权限申请、实例管理、流订阅、资源释放),将重复开发工作量减少 70%,开发者可直接复用模块快速集成功能,降低技术门槛与出错概率,开发周期缩短 50%。(三) 用户体验:明确的权限引导、实时的进房状态反馈、流畅的音视频播放、稳定的二次进房体验,让用户操作步骤从 “无序调试” 简化为 “线性流程”,直播启动耗时减少 40%,异常反馈清晰度提升 80%,用户对直播功能的满意度提升 60%,实现功能稳定性与体验流畅性的双重优化。
-
产品名称:MDC 610软件版本:MDC 610 1.99.102-0000000 问题现象(问题描述):摄像头程序在编译机上编译通过但是在MDC上显示Segmentation fault故障后已采取的措施:/示例代码:如下这是.c文件#include <iostream>#include <vector>#include <cstdint>#include <cstring>#include "camera.h" // 声明 CameraInit、CameraWaitEvents、CameraGetData、CameraData#include "ascend_hal.h" // 声明全局 halMbufFreeusing mdc::camera::CameraInit;using mdc::camera::CameraWaitEvents;using mdc::camera::CameraGetData;using mdc::camera::CameraData;int main(int argc, char* argv[]){ // 默认参数 uint32_t camId = 21; int32_t timeoutWait = 1000; int32_t timeoutGet = 1000; // 简单命令行解析 for (int i = 1; i < argc; ++i) { if (std::strcmp(argv[i], "--camera") == 0 && i+1 < argc) { camId = static_cast<uint32_t>(std::stoi(argv[++i])); } else if (std::strcmp(argv[i], "--wait-timeout") == 0 && i+1 < argc) { timeoutWait = std::stoi(argv[++i]); } else if (std::strcmp(argv[i], "--get-timeout") == 0 && i+1 < argc) { timeoutGet = std::stoi(argv[++i]); } else { std::cerr << "Usage: " << argv[0] << " [--camera ID] [--wait-timeout ms] [--get-timeout ms]\n"; return -1; } } // 1. 初始化摄像头 std::vector<uint32_t> initList = { camId }; int32_t ret = CameraInit(initList); if (ret != 0) { std::cerr << "CameraInit failed: " << ret << "\n"; return -1; } // 2. 等待事件 auto ready = CameraWaitEvents(timeoutWait); if (ready.empty()) { std::cerr << "No camera event\n"; return -1; } // 3. 获取数据 std::vector<CameraData> frames; ret = CameraGetData(camId, frames, timeoutGet); if (ret != 0 || frames.empty()) { std::cerr << "CameraGetData failed: " << ret << "\n"; return -1; } // 4. 打印第一帧信息 —— 替换下列字段为实际名称 CameraData &f = frames[0]; std::cout << "Camera ID: " << /* f.realCameraIdField */ 0 << "\n"; std::cout << "Timestamp: " << /* f.realTimestampField */ 0 << "\n"; std::cout << "Image size: " << /* f.realImgSizeField */ 0 << "\n"; std::cout << "Meta size: " << /* f.realMetaSizeField */ 0 << "\n"; // 5. (可选)解析 metaBuf // const uint8_t* meta = f.realMetaBufPtr; // TODO // size_t msize = f.realMetaSizeField; // TODO // for (size_t off = 0; off + 1 < msize; off += 2) { // uint16_t word = (uint16_t(meta[off]) << 8) | uint16_t(meta[off+1]); // std::cout << "0x" << std::hex << word << " "; // } // std::cout << std::dec << "\n"; // 6. 释放缓冲 for (auto &d : frames) { // TODO: 替换为实际 buf 指针成员 // ::halMbufFree(d.realImgBufPtr); // ::halMbufFree(d.realMetaBufPtr); } return 0;}这是CMakeLists.txtcmake_minimum_required(VERSION 3.10)project(camera_example CXX)# 读取交叉编译环境变量if(DEFINED ENV{CC}) set(CMAKE_C_COMPILER $ENV{CC})endif()if(DEFINED ENV{CXX}) set(CMAKE_CXX_COMPILER $ENV{CXX})endif()if(DEFINED ENV{SYSROOT}) set(CMAKE_SYSROOT $ENV{SYSROOT})endif()# 计算交叉编译器自带 include 路径get_filename_component(CLANG_BIN_DIR ${CMAKE_CXX_COMPILER} DIRECTORY)get_filename_component(CROSS_ROOT ${CLANG_BIN_DIR} DIRECTORY)set(CROSS_INCLUDE_DIR ${CROSS_ROOT}/include)# 通用编译/链接选项set(COMMON_FLAGS -O2 --target=aarch64-linux-gnu --sysroot=${CMAKE_SYSROOT})add_compile_options(${COMMON_FLAGS})add_link_options (${COMMON_FLAGS})# 生成可执行add_executable(camera_example camera_example.cpp)# 包含头文件路径target_include_directories(camera_example PRIVATE ${CROSS_INCLUDE_DIR} ${CMAKE_SYSROOT}/usr/include ${CMAKE_SYSROOT}/usr/include/driver)# 链接库路径target_link_directories(camera_example PRIVATE ${CMAKE_SYSROOT}/usr/lib64)# 链接 SDK 库(根据实际库名调整)find_library(LIB_CAMERA NAMES mdc_camera camera PATHS ${CMAKE_SYSROOT}/usr/lib64)find_library(LIB_MBUF NAMES mdc_mbuf PATHS ${CMAKE_SYSROOT}/usr/lib64)if(NOT LIB_CAMERA OR NOT LIB_MBUF) message(FATAL_ERROR "找不到 SDK 库 (libcamera.so / libmdc_camera.so 或 libmdc_mbuf.so)")endif()target_link_libraries(camera_example PRIVATE ${LIB_CAMERA} ${LIB_MBUF})这是Makefile文件# Makefile —— 读取环境变量:CC, CXX, SYSROOTSRC := camera_example.cppTARGET := camera_example# 交叉编译器根目录CROSS_ROOT := $(shell dirname $(shell dirname $(CXX)))CXXFLAGS := -O2 \ --target=aarch64-linux-gnu \ --sysroot=$(SYSROOT) \ -I$(SYSROOT)/usr/include \ -I$(SYSROOT)/usr/include/driver \ -I$(CROSS_ROOT)/includeLDFLAGS := --target=aarch64-linux-gnu \ --sysroot=$(SYSROOT) \ -L$(SYSROOT)/usr/lib64 \ -lmdc_camera \ -lmdc_mbufall: $(TARGET)$(TARGET): $(SRC)$(CXX) $(CXXFLAGS) $< -o $@ $(LDFLAGS)clean:rm -f $(TARGET).PHONY: all clean
yd_251998187
发表于2025-06-03 11:35:33
2025-06-03 11:35:33
最后回复
yd_251998187
2025-06-16 22:14:57
175 17 -
问题:如何在SCEWIN流程中获取用户的媒体能力需求:用户在拨打进来的时候,通过SCEWIN流程的CELL获取到用户的媒体能力 是否支持视频、音视频等在SCEWIN流程中用了操作呼叫呼叫SIP信息V2的CELL进行获取,但是没有获取成功,①在测试中发现用语音拨打,日志跟踪发现媒体能力和呼叫媒体能力都是等于1②在测试中发现用视频拨打,日志跟踪发现媒体能力和呼叫媒体能力都是等于3发现没有获取到用户的能力,只获取了用户点击了语音还是视频的能力 AICC版本:AICC 24.200.0 问题类别:GSL问题来源:南方电网
-
24.200统装不收,又单独部署了mediameeting ,在pg0 上看不到11600端口和9999端口,视频上坐席失败。
-
mdc610 升级到1.99.101版本后没有camera tool了,目前无法读取相机视频流,不知是否有相关样例参考?相机为海康相机,模组型号为:MDC_HK_OX08B40_MAX96717
-
【问题来源】 【星网】 【问题简要】 语音呼叫ivr拨通后,如何转接到视频ivr并成功播放视频【问题类别】 【可选问题分类:ivr开发】 【AICC解决方案版本】 【AICC可选择版本:AICC 24.200】 【期望解决时间】 尽快【问题现象描述】 通过openeye拨通语音ivr,然后在流程中以接入码的形式路由到视频流程,未正常播放视频。单独用openeye拨打视频流程是能成功播放视频的。想问下这种ivr语音转接到视频ivr的场景如何实现?【日志或错误截图】
-
方案介绍随着人工智能技术的不断发展和普及,越来越多的企业和个人开始关注和使用AI助手来提高工作效率和生活便利性。该解决方案基于 AI 大语言模型 API 实现的 AI 助手全套开源解决方案,自带运营管理后台,开箱即用。集成了 OpenAI, Azure, ChatGLM,讯飞星火,文心一言等多个平台的大语言模型。集成了 MidJourney 和 Stable Diffusion AI绘画、音乐生成功能。开始使用步骤 1 访问该促销活动购买页面,按照如下配置完成ChatPlus服务器的部署。(为避免网络波动影响导致方案部署失败,请选择2M带宽。)步骤 2 登录弹性云服务器控制台。选择购买的服务器,单击服务器名称进入详细页面,在新页面单击“安全组”。步骤 3 单击“配置规则”,选择“入方向规则”。步骤 4 按下图所示,修改放通8080端口。步骤 5 登录弹性云服务器控制台。选择购买的服务器,按如下图所示单击复制按钮,获取弹性公网IP地址。使用Linux连接工具登录服务器,或者在控制台单击“远程登录”。步骤6 等待20分钟左右,登录用户名:root,初始密码:a123456789. ,方案部署完成后请重置密码,进入服务器后,查看环境部署日志。输入命令:tail -f /tmp/install_docker_chatplus.log,如下图所示则表示基础环境部署成功(使用Ctrl+C按键即可退出查看日志界面)。步骤 6 打开浏览器,输入http://EIP:8080/admin,进入ChatPlus 管理界面,初始用户名:admin ,初始密码:admin123,单击“登录”。步骤 7 初次使用需要添加API KEY,按下图所示单击“API-KEY”新增添加聊天/绘画的API KEY。步骤 8 按下图所示,依次选择所属平台,填写名称,选择用途,按照页面提示填写API-KEY信息,激活启用状态,单击提交。步骤 9 打开浏览器,输入http://EIP:8080/chat,进入ChatPlus 前端界面,初始使用需要登录,输入体验账号:18575670125 密码:12345678。,单击“登录”(移动端登录会自动适配)。步骤 10 按下图所示,选择后端API类型并单击确定,在下面的对话框中,输入对话内容,单击发送按钮,即可获取对话结果。更多玩法项目地址Github 地址:cid:link_2码云地址:cid:link_3详细使用教程:cid:link_4
-
方案介绍随着AI的迅速发展,自从 OpenAI 发布 Sora 文本生成视频模型后,文本生成视频的 AI 技术引起了无数圈内圈外人士的关注和实验。该解决方案基于MoneyPrinter,为你提供一个文本生成短视频的WebUI应用。只需输入视频主题或关键词,就可以全自动生成视频文案、视频素材、视频字幕、视频背景音乐,最后合成一个高清的短视频。开始使用步骤 1 访问该促销活动购买页面,按照如下配置完成AI生成短视频服务器的部署。步骤 2 登录弹性云服务器控制台。使用Linux连接工具登录服务器,或者在控制台单击“远程登录”(建议使用远程连接工具,后续使用过程中需要下载短视频文件)。步骤 3 等待15分钟左右,进入服务器后,查看环境部署日志。输入命令:tail -f /tmp/install-MoneyPrinter-baseENV.log,如下图所示则表示基础环境部署成功(使用Ctrl+C按键即可退出查看日志界面)。步骤 4 修改配置文件,路径为“/home/project/MoneyPrinterTurbo/config.toml”。按照 config.toml 文件中的说明,配置好 pexels_api_keys 和 llm_provider(默认moonshot)相关的 API Key。获取方式请参考https://www.pexels.com/api/(pexels_api_key)和https://platform.moonshot.cn/console/api-keys(moonshot_api_key)。步骤 5 输入命令:vim /home/project/MoneyPrinterTurbo/config.toml,按下键盘i键,修改pexels_api_keys和moonshot_api_key的值,在键盘按下Esc,输入“:wq”保存。步骤 6 预启动服务。输入如下命令:conda activate MoneyPrinterTurbocd /home/project/MoneyPrinterTurbo/bash webui.sh执行后,输入邮箱地址,即可启动服务。步骤 7 使用Ctrl + C停止服务,使用后台方式启动服务。输入以下命令:conda activate MoneyPrinterTurbocd /home/project/MoneyPrinterTurbo/bash webui.sh > /home/project/MoneyPrinterTurbo/webui.log 2>&1 &步骤 8 登录弹性云服务器控制台。选择购买的服务器,单击服务器名称进入详细页面,在新页面单击“安全组”。步骤 9 单击“配置规则”,选择“入方向规则”。步骤 10 单击“复制”,修改放通8501端口。步骤 11 打开浏览器,输入http://EIP:8501,即可访问WebUI界面。步骤 12 给定一个关键词,使用AI自动生成视频文案。步骤 13 根据页面提示及自身需要,选改参数设置,单击“生成视频”,等待视频自动生成。步骤 14 下拉页面,可以查看当前任务生成的日志。待出现“视频生成完成”,可直接下拉页面,查看或下载生成的视频。常见问题问题一:Read time out. 因为网络波动影响,可能会有视频素材下载失败,报错如下:解决办法:终止此次任务,单击前端页面“stop”停止此次任务。刷新页面,重新发起任务。
-
【问题简要】 制作视频 IVR 没有播放指定视频【问题类别】【必填】 【IVR(gsl)】【AICC解决方案版本】【必填】 【AICC 8.15.0】 【ICDV300R008C20SPC002】【期望解决时间】 尽快【问题现象描述】 制作视频 IVR 时,在媒体能力=3的时候,使用“媒体能力变更V2”cell用来变更媒体能力 在was配置流程后,拨打号码,直接走的失败出口。拨测工具是openeye openeye版本为:ivr流程日志:
-
【问题来源】 公司内部调试视频IVR【问题简要】 制作视频 IVR 没有播放指定视频【问题类别】【必填】 【IVR(gsl)】【AICC解决方案版本】【必填】 【AICC 8.15.0】 【ICDV300R008C20SPC002】【期望解决时间】 尽快【问题现象描述】 制作视频 IVR 时,在媒体能力=3的时候,使用“媒体能力变更V2”cell用来变更媒体能力 在was配置流程后,拨打号码,直接走的失败出口。ivr流程ivr日志
-
【问题来源】:腾讯客服【问题简要】 GSL流程开发,在会场中如何使用‘放音收号识别cell’。【AICC解决方案版本】 AICC可选择版本:AICC 8.15.1SPC013 CTI版本 ICDV300R008C23SPC017 【期望解决时间】尽快
-
【问题简要】【必填】 制作视频 IVR 时,在媒体能力=3的时候,使用“播放输入V10.0”cell用来播放视频,在was配置流程后,拨打号码,直接走的失败出口。在媒体能力=1, 就是我直接拨打电话,不使用视频ivr的时候可以正常调用语音流程。拨打电话的软件使用的时 OpenEye。 【问题类别】【必填】 IVR(gsl ) 视频IVR 【AICC解决方案版本】【必填】 【UAP可选择版本:UAP9600 V100R005C00SPC102】 【CTI可选择版本:ICDV300R008C25SPC017】 【期望解决时间】【选填】 【问题现象描述】【必填】 制作视频 IVR 时,在媒体能力=3的时候,使用“播放输入V10.0”cell用来播放视频,在was配置流程后,拨打号码,直接走的失败出口。在媒体能力=1, 就是我直接拨打电话,不使用视频ivr的时候可以正常调用语音流程。拨打电话的软件使用的时 OpenEye。 【日志或错误截图】【可选】 见附件
-
制作视频 IVR 时,在媒体能力=3的时候,使用“播放输入V10.0”cell用来播放视频,在was配置流程后,拨打号码,直接走的失败出口。在媒体能力=1,就是我直接拨打电视,不使用视频ivr的时候可以正常调用语音流程。拨打电话的软件使用的时 OpenEye。
-
【问题来源】 公司开发环境 【问题简要】 流程开发使用vmxl,在添加流程时,流程路径IP能否换成服务器的主机名。【问题类别】 CTI【AICC解决方案版本】 AICC版本:AICC 22.200 UAP版本:UAP9600 V100R005C00SPC113 CTI版本:ICD V300R008C25spc012【期望解决时间】 尽快【问题现象描述】 流程开发使用vxml,在添加流程时,流程路径配置为http://99.85.165.70/WLB.IVR/VDN/37117333_CallFlow.vxml,请问能否将99.85.165.70这个IP地址修改为服务器的主机名,例如:/etc/hosts配置如下,路径能否修改成http://ivr/WLB.IVR/VDN/37117333_CallFlow.vxmlwas配置示例:
推荐直播
-
HDC深度解读系列 - Serverless与MCP融合创新,构建AI应用全新智能中枢2025/08/20 周三 16:30-18:00
张昆鹏 HCDG北京核心组代表
HDC2025期间,华为云展示了Serverless与MCP融合创新的解决方案,本期访谈直播,由华为云开发者专家(HCDE)兼华为云开发者社区组织HCDG北京核心组代表张鹏先生主持,华为云PaaS服务产品部 Serverless总监Ewen为大家深度解读华为云Serverless与MCP如何融合构建AI应用全新智能中枢
回顾中 -
关于RISC-V生态发展的思考2025/09/02 周二 17:00-18:00
中国科学院计算技术研究所副所长包云岗教授
中科院包云岗老师将在本次直播中,探讨处理器生态的关键要素及其联系,分享过去几年推动RISC-V生态建设实践过程中的经验与教训。
回顾中 -
一键搞定华为云万级资源,3步轻松管理企业成本2025/09/09 周二 15:00-16:00
阿言 华为云交易产品经理
本直播重点介绍如何一键续费万级资源,3步轻松管理成本,帮助提升日常管理效率!
回顾中
热门标签