iOS在線識別:http://www.lxweimin.com/u/3c2a0bd52ebc
kaldi靜態庫集成到項目中沒問題后,現在就需要采集音頻發送給解碼器去解碼。
我這里對音頻的傳輸采用的是GStreamer的庫,我們也可以采用自己的方式去傳輸音頻,GStreamer的好處就是能將音頻進行緩存,每次都能有序的從GstBufferSource中去讀取數據進行解碼,我們能控制好這個傳輸的管道,自己實現完全沒問題。
采集
iOS采集音頻的方式有很多種,我這里簡單貼出我采集的方式:
- (void)startCapture {
self.capture = [AVCaptureSession new];
AVCaptureDevice *audioDev = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
AVCaptureDeviceInput *audioIn = [[AVCaptureDeviceInput alloc] initWithDevice:audioDev error:nil];
if (![self.capture canAddInput:audioIn]) {
NSLog(@"不能添加 input device");
return;
}
[self.capture addInput:audioIn];
AVCaptureAudioDataOutput *audioOut = [AVCaptureAudioDataOutput new];
[audioOut setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
if (![self.capture canAddOutput:audioOut]) {
NSLog(@"不能添加 audio output");
return;
}
[self.capture addOutput:audioOut];
[audioOut connectionWithMediaType:AVMediaTypeAudio];
[self.capture startRunning];
}
傳輸
因為傳輸我是用的GStreamer,所以這里需要我們對GStreamer有個了解,不然不知道怎么去跟解碼器串聯起來,出了問題也不知道怎么查。
通俗的說,GStreamer在這里起到了一個管道的作用,連通采集跟解碼器,管道內部有很多的元件,都是對音頻的處理,這個我們不用去關心它是怎么去處理的,我們只需要知道這個管道里面都要需要哪些元件,怎么把他們串起來就可以了。
我這里用的元件有:
-
appsrc
:這個是必須的元件,主要是將數據插入管道中 -
decodebin
:這個是一個通過decoders和demuxers自動解碼的元件 -
audioresample
:使用可配置的窗口方法將原始音頻緩沖區重新采樣到不同的采樣率,以提高質量 -
audioconvert
:轉換原始音頻緩沖區之間的各種可能的格式,它支持integer到float轉換,width/depth轉換,signedness和endianness轉換和channel轉換(例如:upmixing和downmixing),以及抖動(dithering)和噪聲(noise-shaping)。 -
tee
:將數據分割到多個pad。對數據流1路分成多路的分路器 -
fakesink
:一個數據只進不出的sink,吞噬掉其他的流,讓音頻流向前推進 -
queue
:數據排隊,直到達到“max-size-buffer”、“max-size-bytes”和/或“max-size-time”屬性指定的限制之一。任何將更多緩沖區推入隊列的嘗試都會阻塞推線程,直到有更多空間可用為止。 -
自定義的解碼插件
:這個是將識別解碼器生成一個GStreamer插件,加入管道中
我們了解了這些元件,通過下面方法創建元件:
gst_element_factory_make(const gchar *factoryname, const gchar *name)
我們獲得了這些元件,接下來我們就需要將這些元件加入到管道(pipeline)中:
gst_bin_add_many(GstBin *bin, GstElement *element_1, ...)
接下來是將每個元件關聯起來:
gst_element_link(GstElement *src, GstElement *dest)
以上我們就做好了這個傳輸數據的管道,有些事件的監聽和狀態的設置也需要在這里做,因為都是一些標準的設置,我這里就不贅述了。
推送數據
采集到數據后,我們可以通過如下方式推送數據:
g_signal_emit_by_name( G_OBJECT(self->appsrc), "push-buffer", buffer, &ret );
這樣數據通過管道就會到達識別解碼器,當然解碼器也要做好接收的準備,這個后面講自定義解碼器插件的時候會說。
至此,我們就完成了音頻的傳輸。