用流程與實戰(zhàn)(七十八))
簡介CSDN博客專家、《Android系統(tǒng)多媒體進階實戰(zhàn)》作者博主新書推薦《Android系統(tǒng)多媒體進階實戰(zhàn)》Android Audio工程師專欄地址Audio工程師進階系列【原創(chuàng)干貨持續(xù)更新中……】Android多媒體專欄地址多媒體系統(tǒng)工程師系列【原創(chuàng)干貨持續(xù)更新中……】專題一 二AAOS車載系統(tǒng)AOSP14系統(tǒng)攻城獅入門視頻實戰(zhàn)課專題三Android14 Binder之HIDL與AIDL通信實戰(zhàn)課專題四Android15快速自定義與集成音效實戰(zhàn)課專題五Android15音頻策略實戰(zhàn)課專題六Android15音頻性能實戰(zhàn)課(無聲/雜音/斷音/爆音實戰(zhàn)案例)人生格言人生從來沒有捷徑只有行動才是治療恐懼和懶惰的唯一良藥.更多原創(chuàng),歡迎關(guān)注Android系統(tǒng)攻城獅文章目錄1.前言2.應用場景和用法2.1控制RingBuffer啟動時機2.2函數(shù)原型與返回語義3.調(diào)用流程剖析3.1原子更新啟動許可3.2啟動流程讀取許可4.實戰(zhàn)案例4.1準備一個帶啟動計數(shù)的RingBuffer4.2切換許可并檢查啟動回調(diào)4.3完整代碼5.總結(jié)1.前言本篇目的理解gst_audio_ring_buffer_may_start()如何控制GstAudioRingBuffer是否允許啟動并通過狀態(tài)和啟動回調(diào)觀察這個許可如何影響播放流程。RingBuffer既保存音頻數(shù)據(jù)也管理設備啟動狀態(tài)。gst_audio_ring_buffer_may_start()只修改一個原子許可標志不會直接啟動設備或改變RingBuffer狀態(tài)。真正啟動時gst_audio_ring_buffer_start()和內(nèi)部等待流程會讀取這個標志再決定是否繼續(xù)。允許不允許音頻基類狀態(tài)變化gst_audio_ring_buffer_may_start更新may_start許可標志RingBuffer啟動流程讀取許可是否允許啟動調(diào)用設備啟動處理保持未啟動狀態(tài)2.應用場景和用法2.1控制RingBuffer啟動時機音頻播放和錄制通常要等到元素狀態(tài)進入PLAYING后才允許底層RingBuffer啟動。元素還在PAUSED時音頻基類會暫時關(guān)閉啟動許可避免設備過早開始處理數(shù)據(jù)。場景許可狀態(tài)作用元素進入READY_TO_PAUSEDFALSE阻止RingBuffer提前啟動元素進入PAUSED_TO_PLAYINGTRUE允許啟動或恢復RingBuffer元素進入PLAYING_TO_PAUSEDFALSE阻止后續(xù)啟動并配合暫停RingBuffer數(shù)據(jù)路徑需要等待空閑或可讀分段檢查當前許可決定等待流程能否觸發(fā)啟動may_start是啟動許可不等于RingBuffer當前的運行狀態(tài)。許可為TRUE只表示可以啟動設備是否已經(jīng)啟動還要看state以及底層啟動回調(diào)結(jié)果。2.2函數(shù)原型與返回語義函數(shù)原型如下voidgst_audio_ring_buffer_may_start(GstAudioRingBuffer*buf,gboolean allowed);參數(shù)含義buf要控制啟動許可的GstAudioRingBuffer對象allowedTRUE允許啟動FALSE暫不允許啟動函數(shù)沒有返回值。實現(xiàn)會檢查buf類型再通過g_atomic_int_set()更新buf-may_start。該標志以原子方式讀寫函數(shù)本身不獲取對象鎖也不調(diào)用設備的start虛函數(shù)。相鄰接口的職責不同接口職責gst_audio_ring_buffer_may_start()設置是否允許啟動gst_audio_ring_buffer_start()檢查許可、狀態(tài)和采集情況再執(zhí)行啟動或恢復gst_audio_ring_buffer_pause()暫停當前RingBuffergst_audio_ring_buffer_stop()停止當前RingBuffer3.調(diào)用流程剖析3.1原子更新啟動許可gst_audio_ring_buffer_may_start()本身非常短先檢查傳入對象再把allowed寫入buf-may_start。voidgst_audio_ring_buffer_may_start(GstAudioRingBuffer*buf,gboolean allowed){g_return_if_fail(GST_IS_AUDIO_RING_BUFFER(buf));g_atomic_int_set(buf-may_start,allowed);}它不做以下操作不調(diào)用gst_audio_ring_buffer_start()。不切換GST_AUDIO_RING_BUFFER_STATE_STOPPED或GST_AUDIO_RING_BUFFER_STATE_STARTED。不調(diào)用設備子類的start或resume回調(diào)。因此設置許可和實際啟動是兩個階段。設置為TRUE后RingBuffer仍可能處于停止狀態(tài)直到后續(xù)啟動流程讀取許可并執(zhí)行啟動。3.2啟動流程讀取許可顯式調(diào)用gst_audio_ring_buffer_start()時RingBuffer會依次檢查是否正在刷新、是否已經(jīng)采集以及may_start是否為真。許可為FALSE時啟動請求返回失敗不會調(diào)用子類的設備啟動回調(diào)。數(shù)據(jù)讀寫流程中的wait_segment()也會讀取同一標志。當RingBuffer尚未啟動且數(shù)據(jù)路徑需要等待分段時只有許可為TRUE內(nèi)部流程才會嘗試啟動RingBuffer。GstAudioBaseSink和GstAudioBaseSrc都會在READY_TO_PAUSED時關(guān)閉許可在PAUSED_TO_PLAYING時打開許可在PLAYING_TO_PAUSED時再次關(guān)閉許可。Sink在特定路徑下還會主動調(diào)用啟動接口。是否READY_TO_PAUSED設置許可為FALSEPAUSED階段準備數(shù)據(jù)PAUSED_TO_PLAYING設置許可為TRUE顯式啟動或等待流程嘗試啟動許可與狀態(tài)檢查通過調(diào)用start或resume回調(diào)不調(diào)用設備啟動回調(diào)PLAYING_TO_PAUSED關(guān)閉許可并暫停RingBuffer4.實戰(zhàn)案例4.1準備一個帶啟動計數(shù)的RingBuffer案例創(chuàng)建一個測試用GstAudioRingBuffer子類。子類提供最小的設備打開、采集、啟動、停止和釋放回調(diào)啟動回調(diào)只增加計數(shù)不連接真實音頻設備。這樣可以直接觀察許可關(guān)閉時啟動請求被攔截許可打開后啟動回調(diào)才被調(diào)用。4.2切換許可并檢查啟動回調(diào)測試使用以下順序打開測試設備并采集RingBuffer資源。將許可設為FALSE調(diào)用gst_audio_ring_buffer_start()檢查啟動失敗且回調(diào)計數(shù)仍為0。將許可設為TRUE再次調(diào)用啟動接口檢查啟動成功且回調(diào)計數(shù)變?yōu)?。釋放RingBuffer資源并關(guān)閉測試設備。這個案例驗證的是通用基類的許可門控。實際ALSA、PulseAudio或其他設備后端的啟動行為還取決于對應子類的回調(diào)實現(xiàn)。4.3完整代碼#includegst/gst.h#includegst/audio/audio.h#includestring.htypedefstruct_TestRingBuffer{GstAudioRingBuffer parent;guint start_calls;}TestRingBuffer;typedefstruct_TestRingBufferClass{GstAudioRingBufferClass parent_class;}TestRingBufferClass;G_DEFINE_TYPE(TestRingBuffer,test_ring_buffer,GST_TYPE_AUDIO_RING_BUFFER);/* 測試后端打開設備不訪問真實硬件 */staticgbooleantest_ring_buffer_open_device(GstAudioRingBuffer*buf){returnTRUE;}/* 為基類分段初始化提供一塊模擬緩沖區(qū) */staticgbooleantest_ring_buffer_acquire(GstAudioRingBuffer*buf,GstAudioRingBufferSpec*spec){buf-sizespec-segsize*spec-segtotal;buf-memoryg_malloc0(buf-size);returnTRUE;}/* 記錄真正進入設備啟動回調(diào)的次數(shù) */staticgbooleantest_ring_buffer_start(GstAudioRingBuffer*buf){TestRingBuffer*test_buffer(TestRingBuffer*)buf;test_buffer-start_calls;returnTRUE;}/* 測試后端停止處理 */staticgbooleantest_ring_buffer_stop(GstAudioRingBuffer*buf){returnTRUE;}/* 釋放模擬緩沖區(qū) */staticgbooleantest_ring_buffer_release(GstAudioRingBuffer*buf){g_free(buf-memory);buf-memoryNULL;buf-size0;returnTRUE;}/* 測試后端關(guān)閉設備 */staticgbooleantest_ring_buffer_close_device(GstAudioRingBuffer*buf){returnTRUE;}staticvoidtest_ring_buffer_class_init(TestRingBufferClass*klass){GstAudioRingBufferClass*ring_buffer_classGST_AUDIO_RING_BUFFER_CLASS(klass);ring_buffer_class-open_devicetest_ring_buffer_open_device;ring_buffer_class-acquiretest_ring_buffer_acquire;ring_buffer_class-starttest_ring_buffer_start;ring_buffer_class-stoptest_ring_buffer_stop;ring_buffer_class-releasetest_ring_buffer_release;ring_buffer_class-close_devicetest_ring_buffer_close_device;}staticvoidtest_ring_buffer_init(TestRingBuffer*test_buffer){}intmain(intargc,char*argv[]){TestRingBuffer*test_buffer;GstAudioRingBuffer*ring_buffer;GstAudioRingBufferSpec*spec;gboolean openedFALSE;gboolean acquiredFALSE;gboolean okFALSE;gboolean blocked_start;gboolean allowed_start;gst_init(argc,argv);test_bufferg_object_new(test_ring_buffer_get_type(),NULL);ring_bufferGST_AUDIO_RING_BUFFER(test_buffer);specring_buffer-spec;/* 準備S16雙聲道格式和4個緩沖段 */memset(spec,0,sizeof(*spec));gst_audio_info_init(spec-info);gst_audio_info_set_format(spec-info,GST_AUDIO_FORMAT_S16,48000,2,NULL);spec-typeGST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW;spec-segsize1024;spec-segtotal4;spec-seglatency-1;/* 啟動前先完成設備打開和資源采集 */if(!gst_audio_ring_buffer_open_device(ring_buffer))gotodone;openedTRUE;if(!gst_audio_ring_buffer_acquire(ring_buffer,spec))gotodone;acquiredTRUE;/* 許可關(guān)閉時啟動接口不應進入子類回調(diào) */gst_audio_ring_buffer_may_start(ring_buffer,FALSE);blocked_startgst_audio_ring_buffer_start(ring_buffer);if(blocked_start||test_buffer-start_calls!0)gotodone;/* 打開許可后啟動接口才會調(diào)用子類start回調(diào) */gst_audio_ring_buffer_may_start(ring_buffer,TRUE);allowed_startgst_audio_ring_buffer_start(ring_buffer);okallowed_starttest_buffer-start_calls1g_atomic_int_get(ring_buffer-state)GST_AUDIO_RING_BUFFER_STATE_STARTED;done:/* 按照資源生命周期逆序清理 */if(acquired)gst_audio_ring_buffer_release(ring_buffer);if(opened)gst_audio_ring_buffer_close_device(ring_buffer);gst_object_unref(ring_buffer);returnok?0:1;}5.總結(jié)gst_audio_ring_buffer_may_start()通過原子標志控制RingBuffer是否獲準啟動實際啟動仍由后續(xù)的gst_audio_ring_buffer_start()或等待流程完成。