视频编码
说明:
使用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
网友评论