車載音視頻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.pswp.cn/web/46359.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/46359.shtml
英文地址,請注明出處:http://en.pswp.cn/web/46359.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

新版本cesium編譯1.103之后的版本

cesium1.1之后的版本文件結構域1.1之前的版本有了很大的差別&#xff0c;源碼也全部移到了packages目錄中。有很多依賴包沒有寫在根目錄的package.json文件中。npm i 后直接編譯會保持。 cesium源碼git https://github.com/CesiumGS/cesium 1、添加缺少的包&#xff0c;缺少的…

4. 雙端口ram設計

1. 設計要求 設計一個位寬8bit&#xff0c;地址深度為128&#xff0c;可以同時讀寫的雙端口RAM 要求&#xff1a;模塊名字為RAM_DUAL 輸入端口&#xff1a;ADDR_W&#xff0c;ADDR_R CLK_R&#xff0c;CLK_W&#xff0c;RSTn ADDR_R[6:0]&#xff0c;ADDR_W[6:0] DATA_WR…

k8s學習——創建測試鏡像

創建一個安裝了ifconfig、telnet、curl、nc、traceroute、ping、nslookup等網絡工具的鏡像&#xff0c;便于集群中的測試。 創建一個Dockerfile文件 # 使用代理下載 Ubuntu 鏡像作為基礎 FROM docker.m.daocloud.io/library/ubuntu:latest# 設置環境變量 DEBIAN_FRONTEND 為 …

學習測試9-接口測試 2-抓包工具Fiddler

Fiddler 抓包工具的使用 怎么找接口信息&#xff0c;可以通過瀏覽器的開發者工具 Fiddler 是一個 HTTP 協議調試代理工具 File 菜單&#xff1a; Capture Traffic&#xff08;或 F12&#xff09;&#xff1a;是個開關&#xff0c;可以控制是否把 Fiddler 注冊為系統代理。當把…

淺談Open.Json.pickle.Os

一、Open函數使用 open函數是 Python 中用于打開文件的內置函數&#xff0c;它返回一個文件對象&#xff0c;該文件對象提供了對文件進行讀寫操作的方法。使用 open 函數時&#xff0c;通常需要指定至少兩個參數&#xff1a;文件名&#xff08;file&#xff09;和模式&#xf…

【網絡工具】Charles 介紹及環境配置

?個人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;專欄地址&#xff1a;http://t.csdnimg.cn/iAmAo &#x1f4da;專欄簡介&#xff1a;在這個專欄中&#xff0c;我將會整理一些工作或學習中用到的工具介紹給大家~ &#x1f4d8;Charles 系列其它文章&#xff1a;【網絡…

Git操縱本地倉庫和遠程倉庫

git是一個代碼托管的平臺&#xff0c;我們可以對我們的代碼進行分支 推送提交 打標簽等等操作&#xff0c;而且git使用過程中也是支持一些linux語言的 比如cd呀 touch mkdir啊等等等 git的具體安裝過程就不再贅述 我個人認為 好多東西就是 代碼也好 文字 文檔 也好&…

【C語言】結構體,枚舉,聯合超詳解!!!

目錄 結構體 結構體聲明 結構體成員的訪問 結構體自引用 結構體變量定義&#xff0c;初始化&#xff0c;傳參 結構體內存對齊 位段 枚舉 聯合(共用體) 結構體 結構體聲明 1. 概念 1. 結構體是一些值的集合&#xff0c;這些值稱為成員變量。 2. 結構體的每個成員可…

長難句打卡7.15

The trend was naturally most obvious in those areas of science based especially on a mathematical or laboratory training, and can be illustrated in terms of the development of geology in the United Kingdom 這一趨勢自然在以數學或實驗室訓練為基礎的科學領域里…

Unlink

Unlink 原理 我們在利用 unlink 所造成的漏洞時&#xff0c;其實就是對 chunk 進行內存布局&#xff0c;然后借助 unlink 操作來達成修改指針的效果。簡單回顧一下 unlink 的目的與過程&#xff0c;其目的是把一個雙向鏈表中的空閑塊拿出來&#xff08;例如 free 時和目前物理…

Leetcode二分搜索法淺析

文章目錄 1.二分搜索法1.1什么是二分搜索法&#xff1f;1.2解法思路 1.二分搜索法 題目原文&#xff1a; 給定一個 n 個元素有序的&#xff08;升序&#xff09;整型數組 nums 和一個目標值 target &#xff0c;寫一個函數搜索 nums 中的 target&#xff0c;如果目標值存在返…

從PyTorch官方的一篇教程說開去(1 - 初心)

原文在此&#xff0c;喜歡讀原汁原味的可以自行去跟&#xff0c;這是一個非常經典和有學習意義的例子&#xff0c;在此向老爺子們致敬 - https://pytorch.org/tutorials/intermediate/reinforcement_q_learning.html 開源文化好是好&#xff0c;但是“公地的悲哀”這點避不開…

每天一個數據分析題(四百三十七)- 統計量

若x1、x2、x3取自某總體的樣本&#xff0c;當期望a已知&#xff0c;方差c未知時&#xff0c;則以下不是統計量的是 A. x1x2x3 B. min{x1&#xff0c;x2&#xff0c;x3} C. a(x1x2)/2 D. (x1x2x3)/c 數據分析認證考試介紹&#xff1a;點擊進入 題目來源于CDA模擬題庫 點擊…

用c語言實現一個alexnet

在C語言中實現一個完整的AlexNet神經網絡是一個復雜的任務,涉及大量的矩陣運算和卷積操作。這個示例代碼將涵蓋AlexNet的主要組件,包括卷積層、ReLU激活函數、池化層和全連接層。 由于C語言本身沒有像Python的NumPy那樣的矩陣庫,我們需要自己編寫矩陣運算函數。另外,C語言…

高效運維:構建全面監控與自動化管理體系

在當今的數字化時代&#xff0c;運維管理已成為企業IT架構中不可或缺的一環。它不僅關乎系統的穩定運行&#xff0c;更直接影響到業務的響應速度、故障處理時間以及客戶滿意度等多個方面。因此&#xff0c;構建一套全面監控與自動化管理體系&#xff0c;對于提升企業運維效率、…

無人機之多旋翼與固定翼的區別

多旋翼無人機和固定翼無人機是無人機技術中的兩種主要形式&#xff0c;各自有獨特的優勢和應用場景。 一、飛行原理與結構 多旋翼無人機&#xff1a;依靠多個旋翼產生升力來平衡飛行器的重力&#xff0c;通過改變每個旋翼的轉速控制飛行器的姿態和平穩&#xff0c;使其能夠垂…

C++ //練習 15.22 對于你在上一題中選擇的類,為其添加合適的虛函數及公有成員和受保護的成員。

C Primer&#xff08;第5版&#xff09; 練習 15.22 練習 15.22 對于你在上一題中選擇的類&#xff0c;為其添加合適的虛函數及公有成員和受保護的成員。 環境&#xff1a;Linux Ubuntu&#xff08;云服務器&#xff09; 工具&#xff1a;vim 代碼塊 class Shape {public:S…

PDF文件無法編輯?3步快速移除PDF編輯限制

正常來說,我們通過編輯器打開pdf文件后,就可以進行編輯了&#xff61;如果遇到了打開pdf卻不能編輯的情況,那有可能是因為密碼或是掃描件的原因&#xff61;小編整理了一些pdf文件無法編輯&#xff0c;以及pdf文件無法編輯時我們要如何處理的方法&#xff61;下面就隨小編一起來…

[word] word如何編寫公式? #微信#知識分享

word如何編寫公式&#xff1f; word如何編寫公式&#xff1f;Word中數學公式是經常會使用到的&#xff0c;若是要在文檔中錄入一些復雜的公式&#xff0c;要怎么做呢&#xff1f;接下來小編就來給大家講一講具體操作&#xff0c;一起看過來吧&#xff01; 方法一&#xff1a;…

stm32學習:(寄存器3)系統架構

時鐘系統 時鐘樹 在STM32中有3種不同的時鐘源用來驅動系統時鐘(SYSCLK)&#xff1a; HSI振蕩器時鐘&#xff08;High Speed Internal oscillator&#xff0c;高速內部時鐘&#xff09;HSE振蕩器時鐘&#xff08;High Speed External&#xff08;Oscillator / Clock&#xff…