EasyPusher安卓Android手机直播推送之MediaCodec 硬编码H264格式

本文转自Holo的博客:http://blog.csdn.net/u013758734/article/details/50834770

最近在研究EasyDarwin的Push库EasyPusher,EasyPusher可以推送H264视频到EasyDarwin服务器,终端可以通过rtsp协议访问该实时流,达到手机直播的功能,延迟基本在2秒以内。 
EasyDarwinQQ群:496258327 
本文主要记录一下最近研究的关于Android手机如何获取实时画面,并将数据编码为H264的格式的视频流,编码使用的是Android自带的MediaCodec,也就是硬解。 
本demo的下载地址:MediaCodecDemo

MediaCodec是Android在4.1中加入的新的API,目前也有很多文章介绍MediaCodec的用法,但是很多时候很多手机都失败,主要问题出现在调用dequeueOutputBuffer的时候总是返回-1,让你以为No buffer available !这里介绍一个开源项目libstreaming,我们借助此项目中封装的一个工具类EncoderDebugger,来初始化MediaCodec会很好的解决此问题,目前为止测试了几个手机都可以成功,包括小米华为Moto。 
看一下怎么使用的

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">EncoderDebugger debugger = EncoderDebugger.debug(getApplicationContext(), width, height);
MediaCodec mMediaCodec = MediaCodec.createByCodecName(debugger.getEncoderName());</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>

嗯,就这样。当然了,后面还是要根据需要对mMediaCodec设置其他参数的,看一下本demo中设置参数的过程吧

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">initMediaCodec</span>() {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> dgree = getDgree();
    framerate = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">15</span>;
    bitrate = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span> * width * height * framerate / <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">20</span>;
    EncoderDebugger debugger = EncoderDebugger.debug(getApplicationContext(), width, height);
    mConvertor = debugger.getNV21Convertor();
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
        mMediaCodec = MediaCodec.createByCodecName(debugger.getEncoderName());
        MediaFormat mediaFormat;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (dgree == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) {
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//dree==0的时候,需要将画面旋转90度,所以这里编码的时候需要将宽和高颠倒,</span>
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//否则编码后的会面会出现四重画面并且花屏</span>
            mediaFormat = MediaFormat.createVideoFormat(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"video/avc"</span>, height, width);
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
            mediaFormat = MediaFormat.createVideoFormat(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"video/avc"</span>, width, height);
        }
        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
        mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, framerate);
        mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
                debugger.getEncoderColorFormat());
        mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>);
        mMediaCodec.configure(mediaFormat, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>, MediaCodec.CONFIGURE_FLAG_ENCODE);
        mMediaCodec.start();
    } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (IOException e) {
        e.printStackTrace();
    }
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li></ul>

编码之前先看一下要编码的数据怎么获取吧,这个当然是来自Camera。 
首先是创建SurfaceView用于预览视频画面,并设置回调,来监控生命周期。

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">surfaceView = (SurfaceView) findViewById(R.id.sv_surfaceview);
surfaceView.getHolder().addCallback(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>);
surfaceView.getHolder().setFixedSize(getResources().getDisplayMetrics().widthPixels,
getResources().getDisplayMetrics().heightPixels);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>

然后是创建Camera的方法:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">ctreateCamera</span>(SurfaceHolder surfaceHolder) {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//mCameraId=Camera.CameraInfo.CAMERA_FACING_BACK</span>
        mCamera = Camera.open(mCameraId);
        Camera.Parameters parameters = mCamera.getParameters();
        Camera.CameraInfo camInfo = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Camera.CameraInfo();
        Camera.getCameraInfo(mCameraId, camInfo);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> cameraRotationOffset = camInfo.orientation;
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//设置预览格式NV21,他属于YUV420SP</span>
        parameters.setPreviewFormat(ImageFormat.NV21);
        parameters.setPreviewSize(width, height);
        mCamera.setParameters(parameters);
        mCamera.autoFocus(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>);
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//计算preview画面需要旋转的角度。目前木有做横竖屏切换的时候无缝旋转画面,后面再搞。</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>  displayRotation = (cameraRotationOffset - getDgree() + <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">360</span>) % <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">360</span>;
        mCamera.setDisplayOrientation(displayRotation);
        mCamera.setPreviewDisplay(surfaceHolder);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;
    } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Exception e) {
        destroyCamera();
        e.printStackTrace();
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;
    }
}

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">getDgree</span>() {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> rotation = getWindowManager().getDefaultDisplay().getRotation();
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> degrees = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">switch</span> (rotation) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> Surface.ROTATION_0:
            degrees = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Natural orientation</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> Surface.ROTATION_90:
            degrees = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">90</span>;
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Landscape left</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> Surface.ROTATION_180:
            degrees = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">180</span>;
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Upside down</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> Surface.ROTATION_270:
            degrees = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">270</span>;
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Landscape right</span>
    }
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> degrees;
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li></ul>

摄像头创建完毕,就是开启预览

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 * 开启预览
 */</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">synchronized</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">startPreview</span>() {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mCamera != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> && !started) {
        mCamera.startPreview();
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> previewFormat = mCamera.getParameters().getPreviewFormat();
        Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> size = previewSize.width * previewSize.height
                * ImageFormat.getBitsPerPixel(previewFormat)
                / <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">8</span>;
        mCamera.addCallbackBuffer(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[size]);
        mCamera.setPreviewCallbackWithBuffer(previewCallback);
        started = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;
        btnSwitch.setText(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"停止"</span>);
    }
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li></ul>

上面就是设置了预览回调的方式,回调中将预览画面一帧一帧的返回给我们,给我们的数据就是NV21格式的,根据需要决定是否需要对数据进行旋转,旋转之后,就是转换,将NV21数据转为YUV420P格式的数据,然后就可以编码为H264数据了。

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">Camera.PreviewCallback previewCallback = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Camera.PreviewCallback() {
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//mSpsPps用来存储sps pps数据,后面遇到关键帧(I帧),必须将spspps数据加到I帧前面</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] mSpsPps = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>];
    <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onPreviewFrame</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] data, Camera camera) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (data == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>;
        }
        ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();
        ByteBuffer[] outputBuffers = mMediaCodec.getOutputBuffers();
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] dst = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[data.length];
        Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (getDgree() == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) {
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//手机竖屏的时候要将获取的数据顺时针旋转90度,否则画面不是正着的,而是逆时针90度</span>
            dst = Util.rotateNV21Degree90(data, previewSize.width, previewSize.height);
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
            dst = data;
        }
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> bufferIndex = mMediaCodec.dequeueInputBuffer(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">5000000</span>);
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (bufferIndex >= <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) {
                inputBuffers[bufferIndex].clear();
                <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//将YUV420SP数据转换成YUV420P的格式,并将结果存入inputBuffers[bufferIndex]</span>
                mConvertor.convert(dst, inputBuffers[bufferIndex]);
                mMediaCodec.queueInputBuffer(bufferIndex, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>,
                        inputBuffers[bufferIndex].position(),
                        System.nanoTime() / <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1000</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>);
                MediaCodec.BufferInfo bufferInfo = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> MediaCodec.BufferInfo();
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>);
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">while</span> (outputBufferIndex >= <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) {
                    ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
                    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] outData = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[bufferInfo.size];
                    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//从buff中读取数据到outData中</span>
                    outputBuffer.get(outData);
                    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//记录pps和sps,pps和sps数据开头是0x00 0x00 0x00 0x01 0x67,</span>
                    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 0x67对应十进制103</span>
                    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (outData[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>] == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span> && outData[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>] == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span> && outData[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>] == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span> 
                            && outData[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">3</span>] == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span> && outData[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4</span>] == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">103</span>) {
                        mSpsPps = outData;
                    } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (outData[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>] == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span> && outData[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>] == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span> && outData[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>] == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span> 
                            && outData[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">3</span>] == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span> && outData[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4</span>] == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">101</span>) {
                        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//关键帧开始规则是0x00 0x00 0x00 0x01 0x65,0x65对应十进制101</span>
                        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//在关键帧前面加上pps和sps数据</span>
                        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] iframeData = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[mSpsPps.length + outData.length];
                        System.arraycopy(mSpsPps, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, iframeData, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, mSpsPps.length);
                        System.arraycopy(outData, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, iframeData, mSpsPps.length, outData.length);
                        outData = iframeData;
                    }
                    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//至此,这一帧的数据已经经过MediaCodec编码完毕,这个outData就是我们需要的数据了,</span>
                    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//因为EasyDarwin可以自动将H264打包为RTP,</span>
                    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//所以EasyPusher只需要负责将outData推给EasyDarwin就OK了</span>
                    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//保存H264数据到本地文件easy.h264</span>
                    Util.save(outData, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, outData.length, path, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>);
                    mMediaCodec.releaseOutputBuffer(outputBufferIndex, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>);
                    outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>);
                }
            } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
                Log.e(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"easypusher"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"No buffer available !"</span>);
            }
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Exception e) {
            StringWriter sw = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> StringWriter();
            PrintWriter pw = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> PrintWriter(sw);
            e.printStackTrace(pw);
            String stack = sw.toString();
            Log.e(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"save_log"</span>, stack);
            e.printStackTrace();
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">finally</span> {
            mCamera.addCallbackBuffer(dst);
        }
    }
};</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li><li style="box-sizing: border-box; padding: 0px 5px;">68</li><li style="box-sizing: border-box; padding: 0px 5px;">69</li><li style="box-sizing: border-box; padding: 0px 5px;">70</li><li style="box-sizing: border-box; padding: 0px 5px;">71</li></ul>

保存之后的文件easy.h264我用VLC播放器打开,截屏如下: 
EasyPusher安卓Android手机直播推送之MediaCodec 硬编码H264格式_第1张图片 
OK,基本上完毕了,该注意的地方都写在代码中了 
需要Demo的请到这里https://github.com/kidloserme/MediaCodecDemo

你可能感兴趣的:(EasyPusher安卓Android手机直播推送之MediaCodec 硬编码H264格式)