美文网首页程序员
Android音视频编码(二)

Android音视频编码(二)

作者: dodo128 | 来源:发表于2020-04-23 14:31 被阅读0次

    视频编码

    说明
    使用MediaCodec对由Camera采集的数据进行H264编码

    1.采集视频数据
    参数设置:
    帧率:30fps
    视频size:1280 * 720
    视频比特率:1280 * 720 * 2

    mCameraId = if(cameraType == TYPE_FRONT) Camera.CameraInfo.CAMERA_FACING_FRONT else Camera.CameraInfo.CAMERA_FACING_BACK
    mCamera = Camera.open(mCameraId)
    val params = mCamera?.parameters
    //设置nv21格式
    params?.previewFormat = ImageFormat.NV21
    
    val videoSizes = params?.supportedVideoSizes
    val videoSize = videoSizes?.let { chooseVideoSize(it) }
    mVideoSize = videoSize
    //设置预览视频size
    params?.setPreviewSize(videoSize?.width!!, videoSize.height)
    //设置帧率,此处需要乘以1000
    params?.setPreviewFpsRange(mFrameRate * 1000, mFrameRate * 1000)
    mCamera?.parameters = params
    
    mCamera?.setPreviewCallback(mPreviewCallback)
    mCamera?.setErrorCallback(mErrorCallback)
    
    //设置camera预览角度
    val orientation = mContext.resources.configuration.orientation
    if(orientation == Configuration.ORIENTATION_LANDSCAPE){
        mCamera?.setDisplayOrientation(0)
    }else{
        mCamera?.setDisplayOrientation(90)
    }
    
    //设置textureview
    val surface = mTextureView?.surfaceTexture
    mCamera?.setPreviewTexture(surface)
    mCamera?.startPreview()
    
    private val mPreviewCallback = object : Camera.PreviewCallback{
        override fun onPreviewFrame(data: ByteArray?, camera: Camera?) {
    //            Log.i(TAG, "onPreviewFrame:$mCameraType")
            if (data != null) {
                encodeVideo(data)
            }
        }
    }
    

    2.编码数据

    try {
        //mimetype: MediaFormat.MIMETYPE_VIDEO_AVC
        //h264编码
        mMediaCodec = MediaCodec.createEncoderByType(mMimeType)
    } catch (e: Exception) {
        Log.i(TAG, "init error:$e")
        return
    }
    
    val format = MediaFormat.createVideoFormat(mMimeType, params.videoWidth, params.videoHeight)
    //设置nv12格式
    format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar)
    format.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR)
    //设置比特率
    format.setInteger(MediaFormat.KEY_BIT_RATE, params.videoBitRate)
    //设置帧率
    format.setInteger(MediaFormat.KEY_FRAME_RATE, params.videoFrameRate)
    //设置I帧间隔
    //h264编码包含 I帧,P帧,B帧
    format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1)
    
    mMediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
    mMediaCodec.start()
    
    //负责将数据加入输入缓冲区
    mInputTask = InputTask(mMediaCodec)
    //负责从输出缓冲区取出编码好的数据
    mOutputTask = OutputTask(mMediaCodec)
    mInputTask.start()
    mOutputTask.start()
    
    fun encode(data: ByteArray){
        ++frameIndex
        //设置pts显示时间,此处很关键
        val pts = frameIndex * 1000000 / mRecorderParams.videoFrameRate
        mMediaMuxer.updateVideoPts(pts)
        mInputTask.writeData(FrameData(data, pts))
    }
    

    输入数据:

    while(true){
        var frame = inputQueue.peek()
        if(frame != null){
            val inputIndex = mediaCodec.dequeueInputBuffer(5000)//5ms
            if(inputIndex >= 0){
                //nv21数据转nv12,放入缓冲区
                val data = nv21toNV12(inputQueue.poll())
                val inputBuffer = mediaCodec.getInputBuffer(inputIndex)
                inputBuffer.clear()
                inputBuffer.put(data)
        
                mediaCodec.queueInputBuffer(inputIndex, 0, data.size, frame.pts, 0)
            }
        }
        
        if(!isRun && inputQueue.size < 1){
            isInputEnd = true
            break
        }
        }
    }
    

    输出数据:

    while(true){
        val bufferInfo = MediaCodec.BufferInfo()
        val outputIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 5000)//5ms
        
        if(outputIndex >= 0){
            val outputBuffer = mediaCodec.getOutputBuffer(outputIndex)
            if(outputBuffer != null){
            val data = ByteArray(bufferInfo.size)
            outputBuffer.get(data)
            outputBuffer.position(bufferInfo.offset)
            outputBuffer.limit(bufferInfo.offset + bufferInfo.size)
            //由MediaMuxer合入视频数据
            mMediaMuxer.writeData(outputBuffer, bufferInfo, true)
            
            mediaCodec.releaseOutputBuffer(outputIndex, false)
            }
        }else if(outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
            //添加音轨
            mMediaMuxer.addTrack(mediaCodec.outputFormat, true)
        }
        
        val isEnd = mInputTask.isInputEnd()
        if(isEnd == null || isEnd){
            mMediaMuxer.stop()
            break
        }
    }
    

    nv21数据转nv12数据

    fun nv21toNV12(frame: FrameData): ByteArray{
        val nv21 = frame.data
        val size = nv21.size
        val nv12 = ByteArray(size)
        val len = size * 4 / 6
        System.arraycopy(nv21, 0, nv12, 0, len)
        
        var i = len
        while(i < size - 1){
            nv12[i] = nv21[i + 1]
            nv12[i + 1] = nv21[i]
            i += 2
        }
        
        return nv12
    }
    

    格式说明:
    其中:y 亮度,u v 色度饱和度
    nv21格式:
    yyyyyy
    yyyyyy
    yyyyyy
    yyyyyy
    vuvuvu
    vuvuvu

    nv12格式:
    yyyyyy
    yyyyyy
    yyyyyy
    yyyyyy
    uvuvuv
    uvuvuv

    相关文章

      网友评论

        本文标题:Android音视频编码(二)

        本文链接:https://www.haomeiwen.com/subject/seqaihtx.html