车载音视频App框架设计

简介

统一播放器提供媒体播放一致性的交互和视觉体验,减少各个媒体应用和场景独自开发的重复工作量,实现媒体播放链路的一致性,减少碎片化的Bug。本文面向应用开发者介绍如何快速接入媒体播放器。

主要功能:

  1. 新设计的统一播放UI组件,视频支持的手势操作,包括左右滑拖动进度、左上下滑调节亮度、右上下滑调节音量、双击暂停/播放,同时对外暴露了长按手势接口应用可以实现类似快进快退功能;
  2. 支持方控、Mini播放器、PSD的播控和状态显示,应用只需要申请媒体中心权限的license即可,支持媒体中心的状态保持和语音控制通道,需要APP中接入对应的接口;
  3. 支持视频行车娱乐限制,非P档播放视频会暂停播放弹框提示;
  4. 使用原生的的media3 1.3.0版本,支持原生的androidx 和framework media session;
  5. 支持音频焦点自管理,包括电话状态的播控也基于音频焦点统一处理;

整体架构图如下

  1. 应用通过配置UI,然后创建MediaController播放媒体;
  2. controler端接入了行车娱乐限制的检测和提示;
  3. service端实现了google原生的media3 session service;
  4. 媒体中心通过mediasession和播放器连接;
  5. 底层使用media3 exoplayer播放和音频焦点管理;

接入流程

1、配置依赖
implementation 'com.max.mediaplayer:uniteplayer:1.x.x

备注:uniteplayer中包含了视频控制UI组件,当只需要修复控制UI组件的bug时,可以更新整体uniteplayer播放器组件,也可以只增加UI组件的依赖,比如:implementation (‘com.max.media:media-video:1.2.0’)。

2、初始化

建议在application创建的时候调用初始化接口:

init(context: Context, isVideo: Boolean, enableMediaCenter: Boolean = false)

参数说明
context应用context
isVideo是否视频应用,视频会初始化车机相关的adapterapi,音乐目前不会初始化
enableMediaCenter需要远程方控、语音控制、mini播放器控制这些能力时设置为true,否则为false,默认为false
3、接入 UI 组件 (视频应用)

这里的UI组件只针对视频应用,音频应用的UI组件在媒体组件库中,参考媒体视频组件。

1)配置PlayerView

在布局中增加FlexPlayerView,视频内容显示和播控UI都在此view中,一般默认配置如下即可:

<com.max.uniteplayer.ui.FlexPlayerViewandroid:id="@+id/player_view"android:layout_width="match_parent"android:layout_height="match_parent"/>

其中播控UI是可单独接入的,若需定制,接入见视频组件接入文档。

2)配置视频渲染view

一般情况下不需要特别配置,支持的定制视频显示view参数如下:

比如在上面FlexPlayerView xml中用app:surface_type=“surface_view”

surface_type:surface类型,取值如下,默认是surface_view类型:

<attr name="surface_type" format="enum"> <enum name="none" value="0"/>   <enum name="surface_view" value="1"/>  <enum name="texture_view" value="2"/>  <enum name="spherical_gl_surface_view" value="3"/>  <enum name="video_decoder_gl_surface_view" value="4"/></attr>

resize_mode,取值如下,默认fit模式:

  <attr name="resize_mode" format="enum">  <enum name="fit" value="0"/>  <enum name="fixed_width" value="1"/><enum name="fixed_height" value="2"/> <enum name="fill" value="3"/> <enum name="zoom" value="4"/> </attr>

first_render_delay,首帧显示延迟时长,用于规避首帧渲染慢闪拉伸的问题,int型,单位ms,一舨不需要设置,默认0;

3)配置视频播控View

FlexPlayerView本身对外暴露了两个接口:

接口说明
fun setTitleBarVisible(visible: Boolean)因此视频播放器中顶部title和关闭按钮,一般应用自己实现的可以通过此接口隐藏
fun enableUpDownGesture(enable: Boolean)使能上下滑动,默认播放器是支持的,可以设置false关闭
bindControlView(listener: FlexPlayerView.ControlViewListener)获取视频播控view控制器,提供更多配置能力,比如增加手势监听回调、添加自定义的播控按钮等,具体可以参考视频组件接入文档。
4、使用播放器播放

有3种播放接口,选择其中一个:

1)FlexSimplePlayer

封装的最高层播放接口,内部封装好了原生MediaItem对象,简单的视频播放场景推荐使用此接口:

class FlexSimplePlayer(context: Context?,isVideo: Boolean,enableParkDetect: Boolean = true,enableParkDialog: Boolean = true,controllerCallback: FlexControllerCallback? = null)

参数说明:

参数说明
context上下文;
enableParkDetect只针对视频生效,是否启用行车娱乐限制,非驻车档会自动暂停视频播放(默认是强制要求的);
enableParkDialog只针对视频生效,是否弹出行车娱乐限制框,设置为disable后需要应用可以自己实现提示界面;

开始播放:

player.startPlay(context: Context,mediaData: List<MediaData>,view: FlexPlayerView?,listener: FlexMediaListener?,mediaCenterData: MediaCenterData? = null)

参数说明:

参数说明
context上下文,注意视频播放要使用activity的context,因为行车娱乐限制需要弹框
mediaData媒体列表,每个媒体item包括url、metadata、mimetiype,其中url是媒体地址,可以是本地、在线点播或直播地址,medadata是media3的原始类型,title和artist字段会显示在mini播放器和PSD上,mimeTypes指定媒体类型,url是对应后缀的无需设置,有些比如优酷投屏中有一个直播url是/m3u8结尾 而不是.m3u8结尾 需要设置mimeTypes = MimeTypes.APPLICATION_M3U8;
view类型为FlexPlayerView,自实现UI的此参数设置为null
listener媒体播放监听回调,具体回调接口后面详细展开,不需要监听设置为null
mediaCenterData媒体中心中需要用的数据,比如应用包名、图标等,具体类型后面详细展开,这里需要注意的是sourceType要设置为6,在媒体中心中对应SourceType.SOURCE_TYPE_ONLINE。
controllerCallback媒体中心回调的方法,包括播放/暂停、上/下曲切换,在此回调中可以实现自定义处理逻辑,并屏蔽底层播放器的响应;

退出界面停止播放,停止播放后会释放所有播放资源:

player.stopPlay()

页面切换的处理建议:

视频应用界面退到后台(onPuse)暂停: player.pause()

视频应用从后台回到前台(onResume): player.play()

更新播放列表:

fun updateMediaItems(mediaData: List<MediaData>)

在已经进入播放状态下,更换播放的视频建议使用该接口,避免重新startPlay()会更慢。

2)FlexMediaController

音频类应用复杂场景建议使用该接口。

FlexSimplePlayer下层的接口,使用该接口需要自己创建MediaItem,mimeType需要调用util接口设置。适合需要自己构建比较复杂的mediaitem场景,另外没有直接提供暂停和播放接口,需要调用成员player的暂停和播放接口。

构造方法解释同FlexSimplePlayer。

开始播放:

fun startPlay(context: Context,mediaItems: List<MediaItem>,view: FlexPlayerView?,listener: FlexMediaListener?,customData: MediaCenterData?,controllerCallback: FlexControllerCallback? = null)

参数说明:

参数说明
context上下文,注意视频播放要使用activity的context,因为行车娱乐限制需要弹框
mediaItems媒体列表,原生类型,包括媒体url,metadata,mimetype等
view类型为FlexPlayerView,自实现UI的此参数设置为null
listener媒体播放监听回调,具体回调接口后面详细展开,不需要监听设置为null
mediaCenterData媒体中心中需要用的数据,比如应用包名、图标等,具体类型后面详细展开,这里需要注意的是sourceType要设置为6,在媒体中心中对应SourceType.SOURCE_TYPE_ONLINE。
controllerCallback媒体中心回调的方法,包括播放/暂停、上/下曲切换,在此回调中可以实现自定义处理逻辑,并屏蔽底层播放器的响应;

FlexControllerCallback接口:

interface FlexControllerCallback {companion object {const val KEY_ACTION_TYPE = "actionType"const val KEY_CALLBACK_RESULT = "callbackResult"const val TYPE_PLAY = 1const val TYPE_NEXT = 2const val TYPE_PREVIOUS = 3const val RESULT_OK = 0const val RESULT_BLOCK = 1}fun onDefaultCallback(bundle: Bundle): Bundle?{return Bundle.EMPTY}fun onPlayAction(): Int?{return RESULT_OK}fun onSeekToNext(): Int?{return RESULT_OK}fun onSeekToPrevious(): Int?{return RESULT_OK}
}

接口描述:

接口说明
onPlayAction(): Int?媒体中心调用过来的播放/暂停接口
onSeekToNext(): Int?媒体中心调用过来的下一首接口
onSeekToPrevious(): Int?媒体中心调用过来的上一首接口

退出界面停止播放,停止播放后会释放所有播放资源:

player.stopPlay()

页面切换的处理建议:

视频应用界面退到后台(onPuse)暂停: player.player?.pause()

视频应用从后台回到前台(onResume): player.player?.play()

更新播放列表:

fun updateMediaItems(mediaItems: List<MediaItem>)

在已经进入播放状态下,更换播放的视频建议使用该接口,避免重新startPlay()会更慢。

3)FlexExoPlayerController

此接口一般不会用到,前面两个接口默认会启动sessionservice,支持媒体中心的连接,此接口直接返回exoplayer播放器,不会创建mediasession,不支持媒体中心连接,支持视频行车娱乐限制。使用媒体UI组件和播放器需要在应用中自行对接,适用于定制化比较多,不需要远程播放支持的视频播放场景,目前只用在赛道模式。建议优先选用前面两种方式播放。

构造方法解释同FlexSimplePlayer。

fun getExoPlayer(audioFocus: Boolean = false,mediaListener: FlexMediaListener? = null): ExoPlayer?

参数和返回值:

参数说明
audioFocus是否打开音频焦点自管理,默认是false。在赛道模式情况存在两个视频同时播的情况,需要设置为false否则因为音频焦点抢占不能同时播放,其他场景建议设置为true;
mediaListener状态回调,同前面两个接口,具体参数后面详细描述;
返回值media3 ExoPlayer对象。
5、状态回调接口

自定义的播放回调接口:

interface FlexMediaListener{//player加载完成fun onLoadPlayerFinished(player: Player?) {}//返回true不会自动播放,需要调用play()后才能播放,默认falsefun pauseWhenStart(): Boolean {return false}//直接返回player的状态接口,更多复杂场景建议拿player对象处理fun onPlaybackStateChange(state: Int){}fun onPlayWhenReadyChanged(ready: Boolean, reason: Int) {}fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {}//播放异常fun onPlayerError(errorCode: Int, errorData: Bundle?) {}//只针对视频,播放视频时检测到非驻车状态回调,会自动暂停播放fun onStartVideoWhenNoPark() {}//只针对视频,非驻车播放视频,弹框点击关闭按钮后的回调fun onStopVideoWhenNoPark() {}//关闭播放页面fun onClose(){}//退出应用fun onExitApp() {}//Mote: This is callback from media center, not from playerfun onSeekToNext(): Boolean {return false}fun onSeekToPrevious(): Boolean {return false}
}

接口描述:

接口说明
onLoadPlayerFinished(player: Player?)开始播放是异步的调用,此回调表示已完成了到服务端的连接返回了有效的player接口
fun pauseWhenStart(): Boolean开始播放后是否自动暂停,需要用户手动点击播放,默认false
fun onPlaybackStateChange(state: Int) fun onPlayWhenReadyChanged(ready: Boolean, reason: Int) fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int)原生Player接口播放状态变化的透传,state取值:Player.STATE_IDLE:还未开始播放STATE_BUFFERING:缓冲中STATE_READY:准备好播放,结合PlayWhenReady状态来判断,true为播放中,false为暂停STATE_ENDED:播放结束onPlayWhenReadyChanged取值: true为播放中,false为暂停onMediaItemTransition:切歌可以通过这里判断
fun onPlayerError(errorCode: Int, errorData: Bundle?)播放异常,errorCode时原生的错误码,errorData暂时没有用到
fun onStartVideoWhenNoPark()只针对视频,档检测到非P档播放视频时会回调此接口
fun onStopVideoWhenNoPark()只针对视频,非P播放视频弹框后,点击关闭按钮或弹框自动退出时的回调
fun onClose()用户点击左上角关闭按钮
fun onExitApp()退出应用,暂未用到
fun onSeekToNext()方控或PSD切换下一曲操作会回调,返回true表示应用来接管下一曲操作不会调用底层播放器的下一曲接口,返回false底层会调用播放器的下一曲接口, 目前投屏用到
fun onSeekToPrevious()方控或PSD切换上一曲操作会回调,返回true表示应用来接管上一曲操作不会调用底层播放器的上一曲接口,返回false底层会调用播放器的上一曲接口,,投屏用到
6、语音控制

媒体中心语音控制接口,声明支持的语音语义,以及处理语音控制的回调

fun declareVrSemanticsCapability(channelInfo: MCVrChannelInfo?,vrSemanticCallback: VrSemanticCallback)

参数说明:

参数说明
channelInfoMCVrChannelInfo类型,具体字段: mediaPackageName: 应用包名 mediaVersion: 应用版本 mediaDescription: 应用描述 channelDataType: 通道类型,一般设置为0 semantics: IntArray 支持的语义数组,MCSemanticsType.CONTROL_PLAY等
vrSemanticCallback语音的回调,具体的实现可参考媒体中心接入文档
7、播放状态保持

媒体中心的状态保持功能可实现应用的自启动(通过媒体中心拉起),首先在开始播放的时候需要在mediacenterdata中设置recoveryIntent;

示例:

        val intent = Intent()intent.setPackage("com.max.example")intent.action = "com.max.example.action.RecoverService"intent.putExtra("type", "StateRecover")

后面在车机重启后,通过如下接口获取之前的播放状态:

fun getRecoveryPlaybackInfo (infoCallback: Consumer<MCPlaybackInfo?>)

其中MCPlaybackInfo类型和媒体中心中的MusicPlaybackInfo对应,通过其中的包名可以判断上一次是否自己播放然后更新媒体中心到迷你播放器,实现重启播放恢复的功能。

8、自定义通道
1)媒体中心通道

媒体中心通道用于更新Mini播放器和PSD状态

接口:FlexMediaController.sendCustomAction(action: Int, bundle: Bundle? = null)

  1. 更新歌词:
    val bd = Bundle()bd.putString(Constants.KEY_LYRIC_STRING, "hello lyric")mediaController?.sendCustomAction(Constants.ACTION_UPDATE_LYRIC, bd)
  1. 更新播放状态:

用于在非播放状态下强制更新媒体信息到媒体中心。

    mediaController?.sendCustomAction(Constants.ACTION_UPDATE_MEDIACENTER, null)
2)远程MediaSession通道

远程Mediasession主要用于更新RSD的信息。

Androidx原生接口:MediaController.sendCustomCommand(command: SessionCommand, bundle: Bundle)

  1. 设置歌词:
    val bd = Bundle()bd.putString("lyrics", lyric)bd.putString("lyrics_tr", lyricTr)  //英文歌词it.sendCustomCommand(SessionCommand(Constants.COMMAND_SET_SESSION_EXTRA,Bundle.EMPTY), bd)
  1. 设置试看点:
    val bd = Bundle()bd.putBoolean("isCanTrail", isCanTrail)//true试听歌曲,false非试听歌曲bd.putLong("trail_start", start)bd.putLong("trail_end", end)it.sendCustomCommand(SessionCommand(Constants.COMMAND_SET_SESSION_EXTRA,Bundle.EMPTY), bd)
9、更多功能使用原生Player/MediaController接口

有其他更多播放场景的需求,可以通过FlexSimplePlayer/FlexMediaController/player直接拿到media3的原始player对象实现。

注意事项

  1. Media3要求工程的compileSDK>=34,这个改动会影响编译过程,不影响运行时。可能会涉及少量系统接口参数适配否则编译会报错,targetSDK建议不动;
  2. Player的接口调用在主线程进行,对应的状态listener回调也会在主线程中(需要在同一个线程中,否则会报异常);
  3. startPlay()和stopPlay()调用时机需要匹配,比如在onCreateView中调用startPlay() 在onDestoryView中调用stopPlay(),或者在startPlay()之前先调用stopPlay();
  4. 全屏的视频播放,activity的decorView.windowSystemUiVisibility需要设置View.SYSTEM_UI_FLAG_FULLSCREEN,这样行车娱乐限制的弹框会根据全屏的状态隐藏状态栏和docker栏;
  5. 当前版本还不支持mediasession service多进程;

参考Demo

uniteplayerdemo(视频)

音频类可参考杜比播放器和网易云音乐APP等应用,附网易云APP的播放架构:

遗留问题

1、电话中播放控制还未在框架中实现,需要应用来处理;

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

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

相关文章

Facebook Dating:社交平台的约会新体验

随着社交媒体的普及和技术的发展&#xff0c;传统的社交方式正在经历革新&#xff0c;尤其是在约会这个领域。Facebook作为全球领先的社交平台&#xff0c;推出了Facebook Dating&#xff0c;旨在为用户提供一个全新的约会体验。本文将探讨Facebook Dating如何重新定义社交平台…

Gettler‘s Screep World 笔记 Ⅰ

夏促时候刚刚入坑&#xff0c;写个笔记叭~ 环境配置 参考 HoPGoldy 大佬的简书&#xff0c;先配置下开发环境 萌新去看大佬的详细教程&#xff0c;我这里比较简单&#xff0c;有前端基础的可以直接抄 VSCode 跳过 node 我配的是v18.18.2 换源 npm config set registry h…

层次分析法模型【清风数模学习笔记】

视频学习&#xff1a;【强烈推荐】清风&#xff1a;数学建模算法、编程和写作培训的视频课程以及Matlab等软件教学_哔哩哔哩_bilibili 适用于&#xff1a;评价类问题 评价目标 可选方案 评价的准则 如何确定评价标准 题目 文献搜索&#xff08;知网&#xff09; 网站搜索…

【C语言】动态内存管理(上)

文章目录 前言1.为什么要存在动态内存2. malloc和free2.1 malloc2.2 free2.3 使用实例&#xff08;malloc和free&#xff09; 3. calloc3.1 calloc例子 前言 本文开始将开始学习C语言中一个比较重要的知识点或者是操作——动态内存管理。由于本次的知识比较重要&#xff0c;为…

JDK8升级到JDK17,报错Error:java:错误:不支持的发行版本5

1 问题描述&#xff1a; 我原来用到是JDK8,后来重新安装了JDK17后&#xff0c;并更换了JAVA_HOME的配置&#xff0c;在CDM上面查看JAVA版本确认安装无误。 当我打开IDEA运行代码时&#xff0c;就报错java&#xff1a;错误&#xff1a;不支持的发行版本5&#xff0c;至始至终我都…

汽车免拆诊断案例 | 2017 款林肯大陆车发动机偶尔无法起动

故障现象 一辆2017款林肯大陆车&#xff0c;搭载2.0T发动机&#xff0c;累计行驶里程约为7.5万km。车主进厂反映&#xff0c;有时按下起动按钮&#xff0c;起动机不工作&#xff0c;发动机无法起动&#xff0c;组合仪表点亮正常&#xff1b;多次按下起动按钮&#xff0c;发动机…

笔记:Few-Shot Learning小样本分类问题 + 孪生网络 + 预训练与微调

内容摘自王老师的B站视频&#xff0c;大家还是尽量去看视频&#xff0c;老师讲的特别好&#xff0c;不到一小时的时间就缕清了小样本学习的基础知识点~Few-Shot Learning (1/3): 基本概念_哔哩哔哩_bilibili Few-Shot Learning&#xff08;小样本分类&#xff09; 假设现在每类…

【学习笔记】无人机系统(UAS)的连接、识别和跟踪(五)-无人机跟踪

目录 引言 5.3 无人机跟踪 5.3.1 无人机跟踪模型 5.3.2 无人机位置报告流程 5.3.3 无人机存在监测流程 引言 3GPP TS 23.256 技术规范&#xff0c;主要定义了3GPP系统对无人机&#xff08;UAV&#xff09;的连接性、身份识别、跟踪及A2X&#xff08;Aircraft-to-Everyth…

<数据集>pcb板缺陷检测数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;693张 标注数量(xml文件个数)&#xff1a;693 标注数量(txt文件个数)&#xff1a;693 标注类别数&#xff1a;6 标注类别名称&#xff1a;[missing_hole, mouse_bite, open_circuit, short, spurious_copper, spur…

C++笔试强训7

文章目录 一、选择题1-5题6-10题 二、编程题题目一题目二 一、选择题 1-5题 基础知识&#xff0c;函数代码少&#xff0c;频繁调用的时候才适合定义内联函数。 故选C。 在C中&#xff0c;inline关键字是用来向编译器建议将函数体在每个调用点“内联展开”的。这意味着编译器会…

操作系统之基础IO

基础IO 一、C语言文件操作1、接口2、默认打开的流 二、Linux系统接口1、open&#xff08;1&#xff09;函数&#xff08;2&#xff09;参数 2、close&#xff08;1&#xff09;函数&#xff08;2&#xff09;参数 3、write和read&#xff08;1&#xff09;函数 三、文件描述符f…

vue使用x6画流程图,简单使用

官网 https://x6.antv.antgroup.com/tutorial/getting-started 安装 npm install antv/x6 --save 使用 <template><div>3333<div id"container" style"width: 800px;height: 800px;"></div></div> </template> <…

Linux-CentOS7忘记密码找回步骤

虚拟机版本 一、进入开机页面&#xff0c;先按上下&#xff08;↑↓&#xff09;键&#xff0c;以免系统自动启动。 二、按“e”键进入编辑页面,找到如下图位置&#xff0c;输入&#xff1a;init/bin/sh 按CTRLX 进入单用户模式。 三、 输入 mount -o remount,rw / 然后按 ent…

山东齐鲁文化名人起名大师颜廷利:中国最厉害的著名哲学家思想家教育家

昔日&#xff0c;祖籍齐鲁大地山东济南的世界级文化名人颜廷利教授儿时仰慕且追随的榜样&#xff0c;经过他的坚持不懈的努力&#xff0c;今朝终于比肩同框精神意识思想灵魂并行了… 山东济南最出名的起名大师的老师&#xff0c;21世纪全球知名哲学家思想家教育家颜廷利教授表示…

视频联网共享平台LntonCVS视频监控汇聚平台视频云解决方案

LntonCVS流媒体平台是一款遵循国家GB28181标准协议的先进视频监控与云服务平台。该平台设计独特&#xff0c;能够同时接入并处理多路设备的视频流&#xff0c;支持包括RTSP、RTMP、FLV、HLS、WebRTC在内的多种视频流格式的分发。其功能丰富多样&#xff0c;涵盖了视频直播监控、…

C++入门基础

目录 一、命名空间 1.命名空间的作用 2.命名空间的定义 3.命名空间的使用 二、输入输出 1.输出运算符<< 2.输入运算符>> 三、缺省参数 四、函数重载 五、引用 1.引用简介 2.引用的注意事项 3.const引用 4.引用与指针的关系 六、inline 一、命名空间 …

什么是离线语音识别芯片?与在线语音识别的区别

离线语音识别芯片是一种不需要联网和其他外部设备支持&#xff0c;‌上电即可使用的语音识别系统。‌它的应用场合相对单一&#xff0c;‌主要适用于智能家电、‌语音遥控器、‌智能玩具等&#xff0c;‌以及车载声控和一部分智能家居。‌离线语音识别芯片的特点包括小词汇量、…

【总结】nginx源码编译安装报错./configure: error: SSL modules require the OpenSSL library.

问题现象 源码编译安装nginx时&#xff0c;执行./configure …… --with-http_ssl_module 命令安装https模块&#xff0c;需要用到openssl&#xff0c;由于机器缺少openssl库&#xff0c;报如下错误。 …… checking for openat(), fstatat() ... found checking for getaddr…

3、宠物商店智能合约实战(truffle智能合约项目实战)

3、宠物商店智能合约实战&#xff08;truffle智能合约项目实战&#xff09; 1-宠物商店环境搭建、运行2-webjs与宠物逻辑实现3-领养智能合约初始化4-宠物领养实现5-更新宠物领养状态 1-宠物商店环境搭建、运行 https://www.trufflesuite.com/boxes/pet-shop 这个还是不行 或者…

【开发实战】QT5 + OpenCV4 开发环境配置应用演示

前言 作为深度学习算法工程师&#xff0c;必须要掌握应用开发技能吗&#xff1f;搞工程肯定是必须要会界面开发&#xff0c;QT就是一个很不错的选择。本文以QT5.15 OpenCV4.8 OpenVINO2023为例&#xff0c;搭建应用开发环境&#xff0c;演示深度学习模型的QT应用案例。 开发…