MediaStream類
MediaStream作為流媒體的基類,實(shí)現(xiàn)了Stream接口惫叛,封裝了對Srteam對象的基本操作亩鬼。同時(shí)它又作為一個抽象類饥脑,抽象出更為細(xì)分的操作動作讓子類進(jìn)行實(shí)現(xiàn)。
static {
// We determine wether or not the MediaCodec API should be used
try {
Class.forName("android.media.MediaCodec");
// Will be set to MODE_MEDIACODEC_API at some point...
sSuggestedMode = MODE_MEDIACODEC_API;
Log.i(TAG,"Phone supports the MediaCoded API");
} catch (ClassNotFoundException e) {
sSuggestedMode = MODE_MEDIARECORDER_API;
Log.i(TAG,"Phone does not support the MediaCodec API");
}
}
MediaStream在靜態(tài)塊中確定流媒體使用的編解碼器晴圾。默認(rèn)優(yōu)先選擇MediaCodec颂砸,若不支持MediaCodec才選擇MediaRecorder。
在傳輸流媒體的過程中,為了節(jié)約帶寬和傳輸效率人乓,一般都會對流媒體數(shù)據(jù)進(jìn)行編解碼操作勤篮。在這里編解碼又分為軟編解碼和硬編解碼。
以視頻編碼舉例,軟編碼就是使用CPU進(jìn)行編碼,實(shí)現(xiàn)直接色罚、簡單碰缔,參數(shù)調(diào)整方便,但CPU負(fù)載重戳护,性能較硬編碼低金抡;硬編碼就是使用非CP(顯卡GPU、專用編解碼芯片等)進(jìn)行編碼腌且,這種方式性能更高梗肝,但不便于移植。在Android平臺上的效果切蟋,主流配置設(shè)配在使用軟編碼運(yùn)行幾分鐘后便會出現(xiàn)發(fā)燙统捶、掉幀等問題,而硬編碼出現(xiàn)上述問題的程度要輕微得多柄粹。所以在spydroid-ipcamera項(xiàng)目這種需要長時(shí)間運(yùn)行流媒體編碼的場景下喘鸟,顯然硬編碼更合適一些。
Android系統(tǒng)內(nèi)部提供了兩種視頻編碼方式:MediaCodec和MediaRecorder驻右。他們都可以對視頻進(jìn)行編碼什黑,但是唯一不同的是MediaCodec更偏向原生,而MediaRecorder偏向的上層封裝堪夭。MediaCodec類處理視頻的時(shí)候可以接觸到視頻流數(shù)據(jù)愕把,比如一些特殊需求:視頻的疊加技術(shù),添加字幕等就可以在這里處理森爽。而MediaRecorder類相對于MediaCodec簡單恨豁,且封裝得更好,直接用幾個接口就可以完成對視頻的錄入和編碼爬迟,用法簡單橘蜜,但就是不能接觸到視頻流數(shù)據(jù),處理不了原生的視頻數(shù)據(jù)付呕。
/** Starts the stream. */
public synchronized void start() throws IllegalStateException, IOException {
if (mDestination==null)
throw new IllegalStateException("No destination ip address set for the stream !");
if (mRtpPort<=0 || mRtcpPort<=0)
throw new IllegalStateException("No destination ports set for the stream !");
mPacketizer.setTimeToLive(mTTL);
if (mMode != MODE_MEDIARECORDER_API) {
encodeWithMediaCodec();
} else {
encodeWithMediaRecorder();
}
}
/** Stops the stream. */
@SuppressLint("NewApi")
public synchronized void stop() {
if (mStreaming) {
try {
if (mMode==MODE_MEDIARECORDER_API) {
mMediaRecorder.stop();
mMediaRecorder.release();
mMediaRecorder = null;
closeSockets();
mPacketizer.stop();
} else {
mPacketizer.stop();
mMediaCodec.stop();
mMediaCodec.release();
mMediaCodec = null;
}
} catch (Exception e) {
e.printStackTrace();
}
mStreaming = false;
}
}
實(shí)現(xiàn)Stream接口中start()和stop()的實(shí)現(xiàn)计福。start()需要子類去實(shí)現(xiàn)encodeWithMediaRecorder()和encodeWithMediaCodec()方法。stop()主要是釋放多媒體編碼類對象徽职。
protected abstract void encodeWithMediaRecorder() throws IOException;
protected abstract void encodeWithMediaCodec() throws IOException;
MediaStream類中兩個核心抽象方法象颖,就是使用MediaCodec和MediaRecorder兩種編碼方式的進(jìn)行實(shí)現(xiàn)。好比同一個目的地姆钉,兩條不同的路说订。
protected void createSockets() throws IOException {
final String LOCAL_ADDR = "net.majorkernelpanic.streaming-";
for (int i=0;i<10;i++) {
try {
mSocketId = new Random().nextInt();
mLss = new LocalServerSocket(LOCAL_ADDR+mSocketId);
break;
} catch (IOException e1) {}
}
mReceiver = new LocalSocket();
mReceiver.connect( new LocalSocketAddress(LOCAL_ADDR+mSocketId));
mReceiver.setReceiveBufferSize(500000);
mReceiver.setSoTimeout(3000);
mSender = mLss.accept();
mSender.setSendBufferSize(500000);
}
protected void closeSockets() {
try {
mReceiver.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
mSender.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
mLss.close();
} catch (Exception e) {
e.printStackTrace();
}
mLss = null;
mSender = null;
mReceiver = null;
}
如果是通過MediaRecoder采集視頻抄瓦,需要再將視頻流映射到LocalSocket上來實(shí)現(xiàn)收發(fā)。所以當(dāng)使用MediaRecorder方式實(shí)現(xiàn)時(shí)克蚂,start()和stop()需要使用到createSockets()和closeSockets()闺鲸。具體細(xì)節(jié)在后面的文章會詳細(xì)分析。
到這里我們大致了解了MediaStream埃叭,主要采用了Android的MediaCodec和MediaRecorder兩種方式實(shí)現(xiàn)流媒體的采集和編解碼摸恍,下一步我們將詳細(xì)分析音頻流AudioStream和它的子類,了解音頻流采集和編碼的主要過程赤屋。