spydroid-ipcamera源码分析(二):MediaStream流媒体基类分析

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和它的子类,了解音频流采集和编码的主要过程。

你可能感兴趣的:(spydroid-ipcamera源码分析(二):MediaStream流媒体基类分析)