摘要:隨著無線和移動網(wǎng)絡的迅速發(fā)展,流媒體在嵌入式平臺上的應用和發(fā)展已日益引起各方面的重視,高質量音頻的實時傳輸作為流媒體的一個重要分支也在不斷的發(fā)展,然而大多數(shù)的應用都是在PC平臺上,隨著嵌入式平臺的不斷發(fā)展,流媒體的應用也在逐步邁向嵌入式系統(tǒng),該文將以當前熱門的Android系統(tǒng)為平臺,提出一種高質量音頻的實時自適應傳輸解決方案。
關鍵詞:流媒體;音頻;實時傳輸;自適應;嵌入式;Android
中圖分類號:TP309文獻標識碼:A文章編號:1009-3044(2011)04-0890-05
Audio Real-time Adaptive Transmission Technology Based on Android
SONG Ran-xin1,2, XIE Wei-bo1,2
(1.College of Computer Science Technology, Huaqiao University, Quanzhou 362021,China; 2.Laboratory of Embedded Technology, Xiamen Software Park, Huaqiao University, Xiamen 361008, China)
Abstract: With the development of the wireless and the mobile networks, the streaming media technology on embedded platform has drawn the attention of most developers. The transmission of the high-quality audio as an important branch is also developing, however, most applications are on the PC platform. With the continuous development of the embedded platform, streaming media applications are also progressing towards embedded systems, This article will present a solution on high-quality real-time adaptive transmission based on Android.
Key words: streaming media; audio; real-time transmission; adaptive; embedded; android
近年來,移動網(wǎng)絡的發(fā)展已進入一個空前活躍的時期,尤其是2009年,國內3G網(wǎng)絡的開放大大加速了無線網(wǎng)絡的應用和普及。隨著網(wǎng)絡的迅速發(fā)展,網(wǎng)絡中的實時的高質量音頻(Audio)傳輸已日益引起各方的關注,尤其是在嵌入式平臺上。本文將就Android平臺下如何實現(xiàn)高質量音頻的實時自適應傳輸提出一種解決方案。
1 Android平臺簡介
1.1 Android 簡介
Android是Google于2007年11月05日宣布的基于Linux平臺的開源手機操作系統(tǒng)的名稱,該平臺由操作系統(tǒng)、中間件、用戶界面和應用軟件組成。它采用軟件堆層(Software Stack,又名軟件疊層)的架構,主要分為三部分。底層以Linux內核工作為基礎,由C語言開發(fā),只提供基本功能;中間層包括函數(shù)庫Library和虛擬機Virtual Machine,由C++開發(fā)。最上層是各種應用軟件,包括通話程序,短信程序等,應用軟件則由各公司自行開發(fā),以Java作為編寫程序的主要語言。目前,Android已經(jīng)進入了自己預定的和諧的發(fā)展軌道,逐漸地發(fā)展壯大起來,成為了移動及嵌入式平臺中一顆璀璨的新星。
1.2 Android 平臺應用程序架構
圖1為Android 平臺應用程序架構。
Linux Kernel: 是Linux Kernel在移動平臺的一個移植,它隱藏了硬件、網(wǎng)絡等相關的細節(jié),為上層提供了一個相對純潔的統(tǒng)一接口。
Libraries: 是一些核心的和擴展的類庫,它們都是原生的C++實現(xiàn)。在這一層,包含眾多開源項目,如SQLite、WebKit、OpenGL 等。如果,該層類庫需要被上層函數(shù)調用,就必須要通過JNI導出相應的接口函數(shù),否則就只能在層次內部之間調用。
在此層中,還有為上層Java程序服務的運行庫。Dalvik虛擬機,是Android的Java虛擬機,之所以不采用 J2ME的虛擬機,一方面是因為J2ME的設計是為了低端機器而優(yōu)化,而Dalvik則是為了高端一些的機器進行優(yōu)化,提供更好的性能。另一方面,從商業(yè)角度來看,必須繞開J2ME虛擬機,Android才能徹底開源。
Application Framework:框架層,這里包含所有開發(fā)所用的SDK類庫,另外還有一些未公開接口的類庫和實現(xiàn),它們是整個Android平臺核心機制的體現(xiàn)。
Application: 應用層,系統(tǒng)的一些應用和第三方開發(fā)的所有應用都是位于這個層次上。
大多數(shù)情況下,開發(fā)者可以依靠框架層提供的SDK使用Java語言進行應用程序設計,但是在某些特殊情況下,比如性能優(yōu)化或者平臺移植,這種編程方式便不能滿足開發(fā)需要。除了SDK,Android還提供了NDK(Native Development Kit),NDK的出現(xiàn)意味著,最上面應用層的內容,可以穿越Java部署的框架層,直接和底層暴露出來的,或者自行開發(fā)的C++庫直接對話,當然在這些庫中需要包含JNI(Java Native Interface)的接口。
那么Android為什么不直接用C++進行應用開發(fā)呢?純C++應用,在Android中是無法被接受的。因為在Android中,大量的核心機制部署在框架層,它們都是用Java實現(xiàn)的,比如控件庫,Activity的調度之類的,沒了界面,就沒了調度,因此,C++的程序作為類庫比較合適。
1.3 JNI技術
Java本地接口(Java Native Interface (JNI))允許運行在Java虛擬機(Java Virtual Machine (JVM))上的代碼調用本地程序和類庫,或者被它們調用,這些程序和類庫可以是其它語言編寫的,比如C、C++或者匯編語言。
當一個程序無法完全使用Java編寫時,開發(fā)者可以通過JNI來編寫本地方法,比如標準Java類庫并不支持的依賴于平臺的特色或者程序庫。 JNI還可以用于修改現(xiàn)有的使用其它語言編寫的程序,使它們可以通過Java編寫的程序來訪問。
很多基本類庫都依賴JNI來為開發(fā)者和用戶提供服務,比如文件的輸入/輸出和音頻功能。在基本類庫中包含的對于性能和平臺敏感的API可以允許 所有的Java程序以安全和平臺無關的方式來使用這些功能。
2 程序的構架設計與具體實現(xiàn)
2.1 需要解決的問題
2.1.1 程序的基本框架
考慮到Android平臺的構架特點,程序將采用Java和嵌入式C混合編程,由于Java語言的虛擬機機制,使其在多媒體編解碼運算時無法充分發(fā)揮硬件性能,導致運算性能偏低,所以在程序的編解碼部分將通過嵌入式C編寫庫來實現(xiàn)。程序包含網(wǎng)絡傳輸在內的其他部分將有Java語言完成。最后通過JNI(Java Native Interface)將C庫與Java進行銜接。
2.1.2 音頻質量與編碼算法
為確保音頻的高質量傳輸,在編碼的選擇上,傳統(tǒng)的語音音頻采用 Speech Code 如(G.711, G.723, G.726 , G.729, ILBC, QCELP)這些編碼格式雖然小巧,但是采樣率大多都只有8KHz,處理后對音頻損失較大,為實現(xiàn)高質量的語音,可采用 Audio Code 如 (AAC, OGG, MP3, WMA, SBC 等)。同時,為了方便針對嵌入式平臺運行的調整和優(yōu)化,在選擇編碼的時候盡量選擇開源項目。
在經(jīng)過各種測試后,程序決定采用MP3算法作為音頻的編解碼。測試過的MP3編解碼庫主要有以下幾個:
編碼庫:
Shine:是一款專為ARM優(yōu)化的MP3定點編碼庫,在保證音質可以接受的情況下盡量簡化MP3的編碼算法。故它的編碼運算速度較快,音質從測試看來也在可接受的范圍內。
BladeEnc:一款跨平臺的免費MP3編碼庫,遵循LGPL,速度快,但不支持定點運算。
Comprec:是Shine針對某移動設備的優(yōu)化修改版,編碼更加簡化速度也更快。
解碼庫:
MAD(libmad):是一個開源的高精度 MPEG 音頻解碼庫,支持 MPEG-1(Layer I, Layer II 和 LayerIII(MP3)。libmad 提供 24-bit 的 PCM 輸出,完全是定點計算。未經(jīng)優(yōu)化的libmad音質還原很好,但是解碼速度稍慢。因此需針對平臺優(yōu)化后使用。
Real: 為32位定點處理器提供高性能的MP3解碼支持(僅支持MP3,對MP2和MP1不支持),并且為多種處理器和平臺提供了優(yōu)化配置方案,如(ARM,x86等)。
FFmpeg:開源的跨平臺音視頻編解碼庫,為多種平臺提供配置編譯支持,但由于支持格式眾多,需自行分離及編譯優(yōu)化。
根據(jù)硬件設備及開發(fā)環(huán)境,可按需要選取合適的編解碼庫。
2.1.3 傳輸?shù)膶崟r性
多媒體流的實時傳輸對帶寬和延遲比較敏感,要求有一定服務質量保證(QoS),傳統(tǒng)的互聯(lián)協(xié)議只能提供盡力而為的服務,對于具有實時性、連續(xù)性、交互性的流媒體服務已無法滿足需要。因此,擬采用RTP(Real-time Transpot Protocol)實時流媒體傳輸協(xié)議進行音頻的傳輸。RTP報文用來做實時傳輸時,可以靈活改變速率、防止亂序。同時輔以RTCP(Real-time Transpot Control Protocol)協(xié)議,其報文在傳輸過程中可以為RTP數(shù)據(jù)提供網(wǎng)絡狀況和服務質量的反饋。
利用RTP/RTCP的反饋結果,設計合適的網(wǎng)絡自適應算法計算網(wǎng)絡的擁塞程度。
由于Android本身并沒有提供對RTP協(xié)議的支持,因此,Java中實現(xiàn)RTP協(xié)議傳輸可以使用開源的Jlibrtp庫,Jlibrtp為Java編寫的sourceforge開源項目,提供了RTP數(shù)據(jù)包定義,RTP傳輸,及RTCP反饋等符合RTP協(xié)議傳輸規(guī)范的大多功能。
2.1.4 自適應傳輸原理與算法
考慮到嵌入式設備的性能,網(wǎng)絡擁塞算法不應過于復雜,可將網(wǎng)絡擁塞算法和音頻編碼算法相結合,動態(tài)調整音頻的編碼和發(fā)送,已達到較為流暢的媒體流,下面為自適應算法原理及簡單描述:
由于RTP/RTCP是運行在UDP協(xié)議之上的,而UDP是不可靠的協(xié)議,它沒有TCP的流量控制等能力,因此常用的算法是基于已丟失報文的評估算法。
數(shù)據(jù)包丟失率是監(jiān)控器估計網(wǎng)絡信道狀況的指標,但不能直接利用丟失率判斷網(wǎng)絡信道狀況并據(jù)此調整音頻的發(fā)送,因為會使發(fā)送數(shù)據(jù)變動得過于頻繁,也使接收端的音頻產生波動,質量不穩(wěn)定。在利用數(shù)據(jù)包丟失率估計網(wǎng)絡信道狀況之前,先對其作平滑處理。[2]
根據(jù)平滑后的數(shù)據(jù)包丟失率與事先確定的閾值的關系,可以判斷網(wǎng)絡上的負載情況,依此將網(wǎng)絡狀態(tài)分為若干類:
假定暫分為三類(輕載,滿載和阻塞),網(wǎng)絡狀態(tài)的分類算法:設定2 個閾值k1、k2(測試給定值),平滑后RTP 數(shù)據(jù)包的丟失率為t(n)。音頻編碼算法為A(i),其中i為調整編碼的系數(shù),i越大,壓縮比越高,對應于三種狀態(tài)的i值分別為i1 < i2 < i3。(i值由實驗統(tǒng)計得出),D為網(wǎng)絡常規(guī)傳輸速率。
1) 當t(n) < k1 時,視為輕載;
2) 當t(n) > k1 且t(n) < k2 時,視為滿載;
3) 當t(n) > k2 時,視為阻塞。
則自適應算法描述為:
if ( t(n) < k1 )
A(i1) ;AIMD(D);
else if ( k1 < t(n) < k2 )
A(i2) ;D;
else
A(i3) ;AIMD(D);
算法中D的增減由 AIMD(加性增乘性減)的算法或其優(yōu)化算法控制。其算法表述為:
其中a,b均為常數(shù), a>=0; 0
2.2 程序整體架構與實現(xiàn)
程序主要包含三個模塊,程序界面,網(wǎng)絡收發(fā)模塊及音頻編解碼模塊。如圖2。
2.2.1 開發(fā)環(huán)境
開發(fā)平臺:Windows 7企業(yè)版;開發(fā)工具:Eclipse + ADT;開發(fā)環(huán)境:Java 2 SDK ver.1.6,Android SDK v2.1,Android NDK v1.6,Cygwin(其中Cygwin和NDK用來構建C/C++的交叉編譯環(huán)境)
2.2.2 程序的設計與實現(xiàn)
1)MP3編解碼庫的優(yōu)化:
嵌入式平臺MP3編碼庫算法的優(yōu)化根據(jù)平臺和需求不同,需要做的調整也不一樣,一般主要都包含以下幾個方面:
① 浮點運算改為定點運算:嵌入式平臺多數(shù)CPU對浮點運算的支持較差或者不支持,因此,如果選擇的編碼是浮點的,需要對此部分進行定點改寫,否則運算效率將非常低
② 對常用的運算函數(shù)用ARM匯編語言重寫:對部分底層常用函數(shù)如加減乘除基本運算,用匯編語言改寫可提高程序執(zhí)行效率。
③ 添加JNI接口,使其可以在Java中調用:自行編寫的C或C++庫在執(zhí)行時位于系統(tǒng)庫中,不能直接使用,因此在編寫優(yōu)化完庫后,需對其添加JNI接口,以供應用程序穿過框架層直接進行調用。
C庫中的JNI接口設計與調用:
在Android 的NDK中提供了完善的JNI支持,使得在應用程序中,可以使用Java方便的調用Linux的基礎C庫,但是首先要對C庫進行改寫,提供JNI的接入支持。
C庫中添加JNI接口:
JNIEXPORT jint JNICALL
Java_com_codec_mp3dec_Mp3dec_write(JNIEnv* env,jobject thiz,
jobject inputStream,
jint len)
{unsigned char* input= (unsigned char*)
((*env)->GetDirectBufferAddress(env,inputStream));
memcpy(readBuf + bytesLeft,input,len);
bytesLeft += len;
fillSize = READBUF_SIZE - bytesLeft;
return fillSize;}
Java中通過JNI調用:
static
{System.loadLibrary(\"mp3dec\");}
while(getFillSize()>1024) {
buffOut=buffList.take();
nRead = buffOut.length;
inputBuffer.clear();
inputBuffer.put(buffOut);
write(inputBuffer,nRead);} /* 調用上面C庫中定義的write方法*/
2)程序實現(xiàn)的關鍵類與部分代碼示例
圖3 程序模塊結構圖
AudioRecord和AudioTrack類是Android獲取和播放音頻流的重要類,放置在android.media包中。該包中還包含 MediaRecorder和MediaPlayer類,這幾個類都可實現(xiàn)音頻的錄制與回放,不同的是AudioRecord和AudioTrack類在獲取和播放音頻數(shù)據(jù)流時無需通過文件保存和文件讀取,可以動態(tài)地直接獲取和播放音頻流,減少了媒體對于磁盤的讀寫,因此非常適合用來實時處理音頻數(shù)據(jù)流。下面是創(chuàng)建兩個類的對象的具體代碼:
AudioRecord類:
audio_in_buff_size = AudioRecord.getMinBufferSize(8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
audio_in_rec = new AudioRecord(MediaRecorder.AudioSource.MIC,8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT,
audio_in_buff_size) ;
AudioTrack類:
audio_out_buff_size = android.media.AudioTrack.getMinBufferSize(8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
audio_out_trk = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT,
audio_out_buf_size,AudioTrack.MODE_STREAM);
音頻回放與RTP傳輸模塊示例:
public LiveMP3Play(int rtpPort, int rtcpPort){
try {
rtpSocket = new DatagramSocket(rtpPort);
rtcpSocket = new DatagramSocket(rtcpPort);
} catch (Exception e) {
System.out.println(e.toString());}
rtpSession = new RTPSession(rtpSocket, rtcpSocket);
rtpSession.naivePktReception(true);
rtpSession.RTPSessionRegister(this,1, 1);
Participant p = new Participant(\"10.0.2.2\", 16386, 16387);
rtpSession.addParticipant(p); }; /*用jlibrtp中的類創(chuàng)建RTP連接*/
public void run() {
if ( aAudioTrack02 != 1 ) {
aAudioTrack02.release();
aAudioTrack02 = 1;}
int iMinBufSize = AudioTrack.getMinBufferSize(44100,
AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT);
if ( iMinBufSize == AudioTrack.ERROR_BAD_VALUE||
iMinBufSize == AudioTrack.ERROR ) {
return;}/*構建AudioTrack對象*/
try {
aAudioTrack02 = new AudioTrack(AudioManager.STREAM_MUSIC, 44100,AudioFormat.CHANNEL_CONFIGURATION_STEREO,AudioFormat.ENCODING_PCM_16BIT, iMinBufSize,AudioTrack.MODE_STREAM);
} catch (IllegalArgumentException iae) {
myTextView.setText(\"new AudioTrack Exceeption:\" + iae.toString());
iae.printStackTrace();}
try {
while(!stopLive) {
Message msPackCount = new Message();
msPackCount.what = Mp3dec.GUI_NOTIFIER_PACK; Mp3dec.this.myMessageHandler.sendMessage(msPackCount);
if(pktCount>20)
{ int nRead = 0,rec = 0;
while(getFillSize()>1024) {
buffOut=buffList.take();
nRead = buffOut.length;
inputBuffer.clear();
inputBuffer.put(buffOut);
write(inputBuffer,nRead);} /*將MP3數(shù)據(jù)寫入解碼緩沖區(qū)*/
rec = read(outputBuffer); /*解碼MP3音頻并輸出到緩沖區(qū)*/
outputBuffer.flip();
outputBuffer.limit(rec);
outputBuffer.get(outputByte);
outputBuffer.clear();
aAudioTrack02.write(outputByte, 0, outputByte.length);
aAudioTrack02.play();}}} /*回放解碼后的音頻數(shù)據(jù)*/
catch (InterruptedException e) {
System.out.println(\"Error Taked !!!!\");
e.printStackTrace();}
rtpSession.endSession();
rtpSocket.close();
rtcpSocket.close();}};
3 結束語
Android系統(tǒng)以其自由的開放性及良好的應用程序構架正穩(wěn)健而迅速地發(fā)展著,短短幾年時間中運行其上的應用程序已達十萬以上,然而在流媒體實時傳輸方面的應用程序卻不多見,隨著移動網(wǎng)絡的不斷發(fā)展,流媒體在手機及平板電腦等移動設備上的應用前景顯而易見,本文針對Android 平臺的特點,提出了一種高質量音頻的實時傳輸?shù)慕鉀Q方案,旨在拋磚引玉,共同完善和豐富Android 這個開放性平臺上的軟件應用。
參考文獻:
[1] 葛艷紅,李文鋒,劉旭光.基于RTP流媒體實時傳輸?shù)腏ava實現(xiàn)[J].計算機與現(xiàn)代化,2007,2:59-61.
[2] 高東日,魏海平,姜東.網(wǎng)絡自適應傳輸控制策略研究[J].計算機工程,2006.11(32):141-143.
[3] 杜根遠,邱穎豫,基于RTP的實時音頻傳輸系統(tǒng)研究[J].微計算機信息,2006(15):94-96.