美文网首页程序员
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