MediaStream类
MediaStream作为流媒体的基类,实现了Stream接口,封装了对Srteam对象的基本操作。同时它又作为一个抽象类,抽象出更为细分的操作动作让子类进行实现。
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在静态块中确定流媒体使用的编解码器。默认优先选择MediaCodec,若不支持MediaCodec才选择MediaRecorder。
在传输流媒体的过程中,为了节约带宽和传输效率,一般都会对流媒体数据进行编解码操作。在这里编解码又分为软编解码和硬编解码。
以视频编码举例,软编码就是使用CPU进行编码,实现直接、简单,参数调整方便,但CPU负载重,性能较硬编码低;硬编码就是使用非CP(显卡GPU、专用编解码芯片等)进行编码,这种方式性能更高,但不便于移植。在Android平台上的效果,主流配置设配在使用软编码运行几分钟后便会出现发烫、掉帧等问题,而硬编码出现上述问题的程度要轻微得多。所以在spydroid-ipcamera项目这种需要长时间运行流媒体编码的场景下,显然硬编码更合适一些。
Android系统内部提供了两种视频编码方式:MediaCodec和MediaRecorder。他们都可以对视频进行编码,但是唯一不同的是MediaCodec更偏向原生,而MediaRecorder偏向的上层封装。MediaCodec类处理视频的时候可以接触到视频流数据,比如一些特殊需求:视频的叠加技术,添加字幕等就可以在这里处理。而MediaRecorder类相对于MediaCodec简单,且封装得更好,直接用几个接口就可以完成对视频的录入和编码,用法简单,但就是不能接触到视频流数据,处理不了原生的视频数据。
/** 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;
}
}
实现Stream接口中start()和stop()的实现。start()需要子类去实现encodeWithMediaRecorder()和encodeWithMediaCodec()方法。stop()主要是释放多媒体编码类对象。
protected abstract void encodeWithMediaRecorder() throws IOException;
protected abstract void encodeWithMediaCodec() throws IOException;
MediaStream类中两个核心抽象方法,就是使用MediaCodec和MediaRecorder两种编码方式的进行实现。好比同一个目的地,两条不同的路。
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上来实现收发。所以当使用MediaRecorder方式实现时,start()和stop()需要使用到createSockets()和closeSockets()。具体细节在后面的文章会详细分析。
到这里我们大致了解了MediaStream,主要采用了Android的MediaCodec和MediaRecorder两种方式实现流媒体的采集和编解码,下一步我们将详细分析音频流AudioStream和它的子类,了解音频流采集和编码的主要过程。