android流媒体 视频编码格式和H264编码

android中使用previewcallback 获得的YUV数据格式一般是YV12 或者NV21.  而H264的编码格式一般是YUV420P或者YUV420SP这种格式。编码之前进行数据格式转化、程序自动检测格式设置参数。讲视频流压缩,完成推流前的准备工作. 其中转化的代码如下:

public byte[] convert(byte[] data) {


// A buffer large enough for every case
if (mBuffer == null || mBuffer.length != 3 * mSliceHeight * mStride / 2 + mYPadding) {
mBuffer = new byte[3 * mSliceHeight * mStride / 2 + mYPadding];
}


if (!mPlanar) {
if (mSliceHeight == mHeight && mStride == mWidth) {
// Swaps U and V
if (!mPanesReversed) {
for (int i = mSize; i < mSize + mSize / 2; i += 2) {
mBuffer[0] = data[i + 1];
data[i + 1] = data[i];
data[i] = mBuffer[0];
}
}
if (mYPadding > 0) {
System.arraycopy(data, 0, mBuffer, 0, mSize);
System.arraycopy(data, mSize, mBuffer, mSize + mYPadding, mSize / 2);
return mBuffer;
}
return data;
}
}
else {
if (mSliceHeight == mHeight && mStride == mWidth) {
// De-interleave U and V
if (!mPanesReversed) {
for (int i = 0; i < mSize / 4; i += 1) {
mBuffer[i] = data[mSize + 2 * i + 1];
mBuffer[mSize / 4 + i] = data[mSize + 2 * i];
}
}
else {
for (int i = 0; i < mSize / 4; i += 1) {
mBuffer[i] = data[mSize + 2 * i];
mBuffer[mSize / 4 + i] = data[mSize + 2 * i + 1];
}
}
if (mYPadding == 0) {
System.arraycopy(mBuffer, 0, data, mSize, mSize / 2);
}
else {
System.arraycopy(data, 0, mBuffer, 0, mSize);
System.arraycopy(mBuffer, 0, mBuffer, mSize + mYPadding, mSize / 2);
return mBuffer;
}
return data;
}
}


return data;
}



编码器自动检测格式:

public byte[] convert(byte[] data) {


// A buffer large enough for every case
if (mBuffer == null || mBuffer.length != 3 * mSliceHeight * mStride / 2 + mYPadding) {
mBuffer = new byte[3 * mSliceHeight * mStride / 2 + mYPadding];
}


if (!mPlanar) {
if (mSliceHeight == mHeight && mStride == mWidth) {
// Swaps U and V
if (!mPanesReversed) {
for (int i = mSize; i < mSize + mSize / 2; i += 2) {
mBuffer[0] = data[i + 1];
data[i + 1] = data[i];
data[i] = mBuffer[0];
}
}
if (mYPadding > 0) {
System.arraycopy(data, 0, mBuffer, 0, mSize);
System.arraycopy(data, mSize, mBuffer, mSize + mYPadding, mSize / 2);
return mBuffer;
}
return data;
}
}
else {
if (mSliceHeight == mHeight && mStride == mWidth) {
// De-interleave U and V
if (!mPanesReversed) {
for (int i = 0; i < mSize / 4; i += 1) {
mBuffer[i] = data[mSize + 2 * i + 1];
mBuffer[mSize / 4 + i] = data[mSize + 2 * i];
}
}
else {
for (int i = 0; i < mSize / 4; i += 1) {
mBuffer[i] = data[mSize + 2 * i];
mBuffer[mSize / 4 + i] = data[mSize + 2 * i + 1];
}
}
if (mYPadding == 0) {
System.arraycopy(mBuffer, 0, data, mSize, mSize / 2);
}
else {
System.arraycopy(data, 0, mBuffer, 0, mSize);
System.arraycopy(mBuffer, 0, mBuffer, mSize + mYPadding, mSize / 2);
return mBuffer;
}
return data;
}
}


return data;
}

检测格式配置

private void debug() {


        // If testing the phone again is not needed,
        // we just restore the result from the shared preferences
        if (!checkTestNeeded()) {
            String resolution = mWidth + "x" + mHeight + "-";


            boolean success = mPreferences.getBoolean(PREF_PREFIX + resolution
                    + "success", false);
            if (!success) {
                throw new RuntimeException(
                        "Phone not supported with this resolution (" + mWidth
                                + "x" + mHeight + ")");
            }


            mNV21.setSize(mWidth, mHeight);
            mNV21.setSliceHeigth(mPreferences.getInt(PREF_PREFIX + resolution
                    + "sliceHeight", 0));
            mNV21.setStride(mPreferences.getInt(PREF_PREFIX + resolution
                    + "stride", 0));
            mNV21.setYPadding(mPreferences.getInt(PREF_PREFIX + resolution
                    + "padding", 0));
            mNV21.setPlanar(mPreferences.getBoolean(PREF_PREFIX + resolution
                    + "planar", false));
            mNV21.setColorPanesReversed(mPreferences.getBoolean(PREF_PREFIX
                    + resolution + "reversed", false));
            mEncoderName = mPreferences.getString(PREF_PREFIX + resolution
                    + "encoderName", "");
            mEncoderColorFormat = mPreferences.getInt(PREF_PREFIX + resolution
                    + "colorFormat", 0);
            mB64PPS = mPreferences.getString(PREF_PREFIX + resolution + "pps",
                    "");
            mB64SPS = mPreferences.getString(PREF_PREFIX + resolution + "sps",
                    "");


            return;
        }


        if (VERBOSE)
            Log.d(TAG, ">>>> Testing the phone for resolution " + mWidth + "x"
                    + mHeight);


        // Builds a list of available encoders and decoders we may be able to
        // use
        // because they support some nice color formats
        Codec[] encoders = CodecManager.findEncodersForMimeType(MIME_TYPE);
        Codec[] decoders = CodecManager.findDecodersForMimeType(MIME_TYPE);


        int count = 0, n = 1;
        for (int i = 0; i < encoders.length; i++) {
            count += encoders[i].formats.length;
        }


        // Tries available encoders
        for (int i = 0; i < encoders.length; i++) {
            for (int j = 0; j < encoders[i].formats.length; j++) {
                reset();


                mEncoderName = encoders[i].name;
                mEncoderColorFormat = encoders[i].formats[j];


                if (VERBOSE)
                    Log.v(TAG, ">> Test " + (n++) + "/" + count + ": "
                            + mEncoderName + " with color format "
                            + mEncoderColorFormat + " at " + mWidth + "x"
                            + mHeight);


                // Converts from NV21 to YUV420 with the specified parameters
                mNV21.setSize(mWidth, mHeight);
                mNV21.setSliceHeigth(mHeight);
                mNV21.setStride(mWidth);
                mNV21.setYPadding(0);
                mNV21.setEncoderColorFormat(mEncoderColorFormat);


                // /!\ NV21Convertor can directly modify the input
                createTestImage();
                mData = mNV21.convert(mInitialImage);


                try {


                    // Starts the encoder
                    configureEncoder();
                    searchSPSandPPS();


                    saveTestResult(true);
                    Log.v(TAG, "The encoder " + mEncoderName
                            + " is usable with resolution " + mWidth + "x"
                            + mHeight);
                    return;


                } catch (Exception e) {
                    StringWriter sw = new StringWriter();
                    PrintWriter pw = new PrintWriter(sw);
                    e.printStackTrace(pw);
                    String stack = sw.toString();
                    String str = "Encoder " + mEncoderName
                            + " cannot be used with color format "
                            + mEncoderColorFormat;
                    if (VERBOSE)
                        Log.e(TAG, str, e);
                    mErrorLog += str + "\n" + stack;
                    e.printStackTrace();
                } finally {
                    releaseEncoder();
                }


            }
        }


        saveTestResult(false);
        Log.e(TAG, "No usable encoder were found on the phone for resolution "
                + mWidth + "x" + mHeight);
        throw new RuntimeException(
                "No usable encoder were found on the phone for resolution "
                        + mWidth + "x" + mHeight);


    }

         /*
        SD (Low quality) SD (High quality) HD 720p
1 HD 1080p
1
Video resolution 320 x 240 px 720 x 480 px 1280 x 720 px 1920 x 1080 px
Video frame rate 20 fps 30 fps 30 fps 30 fps
Video bitrate 384 Kbps 2 Mbps 4 Mbps 10 Mbps
        */
        int framerate = 20;
//        if (width == 640 || height == 640) {
//            bitrate = 2000000;
//        } else if (width == 1280 || height == 1280) {
//            bitrate = 4000000;
//        } else {
//            bitrate = 2 * width * height;
//        }
        int bitrate = (int) (mWidth*mHeight*20*2*0.07f);
       // int bitrate = 2 * mWidth * mHeight / 3;
        
        EncoderDebugger debugger = EncoderDebugger.debug(mContext, mWidth, mHeight);
        mVideoConverter = debugger.getNV21Convertor();
        mMediaCodec = MediaCodec.createByCodecName(debugger.getEncoderName());
        MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", mWidth, mHeight);
        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, 1);
        mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        mMediaCodec.start();


        Bundle params = new Bundle();
        params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            mMediaCodec.setParameters(params);
        }
    }

上传之前进行数据转化,然后编码推送数据到流媒体服务器:

public int onVideo(byte[] data, int format) {
        if (!mVideoStarted)return 0;


        
        data = mVideoConverter.convert(data);
inputBuffers = mMediaCodec.getInputBuffers();
outputBuffers = mMediaCodec.getOutputBuffers();
        int bufferIndex = mMediaCodec.dequeueInputBuffer(0);        
       // MainService.mEasyPusher.addwatermarkScale(data, Scaler, data.length,Constants.RECORD_VIDEO_WIDTH,Constants.RECORD_VIDEO_HEIGHT,Constants.UPLOAD_VIDEO_WIDTH,Constants.UPLOAD_VIDEO_HEIGHT);
if (bufferIndex >= 0) {
   ByteBuffer buffer = null;
   buffer = inputBuffers[bufferIndex];   
   buffer.clear();
   buffer.put(data);
   buffer.clear();
   mMediaCodec.queueInputBuffer(bufferIndex, 0, data.length, System.nanoTime() / 1000, 0);
}
        
        return 0;
    }
    @Override
    public void run(){
        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        int outputBufferIndex = 0;
        byte[] mPpsSps = new byte[0];
        byte[]h264 = new byte[mWidth*mHeight*3/2];        


        do {
       
            outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 30000);
            if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
                // no output available yet
            } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                // not expected for an encoder
            } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
               /* EasyMuxer muxer = mMuxer;
                if (muxer != null) {
                    // should happen before receiving buffers, and should only happen once
                    MediaFormat newFormat = mMediaCodec.getOutputFormat();
                    muxer.addTrack(newFormat, true);
                }*/
            } else if (outputBufferIndex < 0) {
                // let's ignore it
            } else {
                ByteBuffer outputBuffer;
                
                outputBuffer = outputBuffers[outputBufferIndex];        
                Muxer muxer = mMuxer;
                if (muxer != null) {
                    muxer.pumpStream(outputBuffer, bufferInfo, true);
                }
                boolean sync = false;
                if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {// sps
                    sync = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
                    if (!sync) {
                        byte[] temp = new byte[bufferInfo.size];
                        outputBuffer.get(temp);
                        mPpsSps = temp;
                        mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                        continue;
                    } else {
                        mPpsSps = new byte[0];
                    }
                }
                sync |= (bufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
                int len = mPpsSps.length + bufferInfo.size;
                if (len > h264.length){
                    h264 = new byte[len];
                }
                
                if (sync) {
                    System.arraycopy(mPpsSps, 0, h264, 0, mPpsSps.length);
                    outputBuffer.get(h264, mPpsSps.length, bufferInfo.size);
                    mPusher.SendBuffer_org( h264,  mPpsSps.length + bufferInfo.size, (int)(bufferInfo.presentationTimeUs / 1000),1, m_index);
                   
                }else{
               
                    outputBuffer.get(h264, 0, bufferInfo.size);
                    mPusher.SendBuffer_org( h264,  bufferInfo.size,  (int)(bufferInfo.presentationTimeUs / 1000), 1, m_index);
                  
                }
                
                mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
            }
        }
        while (mVideoStarted);
    }



相关代码下载:https://github.com/Car-eye-team/Car-eye-device  欢迎加入QQ技术讨论群:590411159






你可能感兴趣的:(流媒体)