MediaCodec 处理完了之后,再从输出队列中出队得到一个可用的缓冲区,这个缓冲里面的数据就是编码或者解码后的数据了,把这些数据进行相应的处理之后,还需要释放这个缓冲区,让它回到队列中去,可供下一次使用。 MediaCodec 生命周期另外,MediaCodec 也存在相应的?生命周期,如下图所示: 当创建了 MediaCodec 之后,是处于未初始化的? 在?
当一调用 start 方法之后,就进入了? 在? 在? 当不再需要 MediaCodec 时,调用 release 方法将它释放掉,进入? 当 MediaCodec 工作发生异常时,会进入到? MediaCodec 调用流程理解了 MediaCodec 的生命周期和工作流程之后,就可以上手来进行编码工作了。 以 MediaCodec 同步调用为例,使用过程如下: // 创建 MediaCodec,此时是 Uninitialized 状态 MediaCodec codec = MediaCodec.createByCodecName(name); // 调用 configure 进入 Configured 状态 codec.configure(format, …); MediaFormat outputFormat = codec.getOutputFormat(); // option B // 调用 start 进入 Executing 状态,开始编解码工作 codec.start(); for (;?? { // 从输入缓冲区队列中取出可用缓冲区,并填充数据 int inputBufferId = codec.dequeueInputBuffer(timeoutUs); if (inputBufferId >= 0) { ByteBuffer inputBuffer = codec.getInputBuffer(…); // fill inputBuffer with valid data … codec.queueInputBuffer(inputBufferId, …); } // 从输出缓冲区队列中拿到编解码后的内容,进行相应操作后释放,供下一次使用 int outputBufferId = codec.dequeueOutputBuffer(…); if (outputBufferId >= 0) { ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId); MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A // bufferFormat is identical to outputFormat // outputBuffer is ready to be processed or rendered. … codec.releaseOutputBuffer(outputBufferId, …); } elseif (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { // Subsequent data will conform to new format. // Can ignore if using getOutputFormat(outputBufferId) outputFormat = codec.getOutputFormat(); // option B } } // 调用 stop 方法进入 Unin 《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》 浏览器打开:qq.cn.hn/FTe 开源分享 itialized 状态 codec.stop(); // 调用 release 方法释放,结束操作 codec.release(); 代码解析 MediaFormat 设置首先需要创建并设置好 MediaFormat 对象,它表示媒体数据格式的相关信息,对于视频主要有以下信息要设置:
其中,码率就是指单位传输时间传送的数据位数,一般用? 其实对于码率有三种模式可以控制:
对于颜色格式,由于是将 YUV 数据编码成 H264,而 YUV 格式又有很多,这又涉及到机型兼容性问题。在对相机编码时要做好格式的处理,比如相机使用的是? 对于 I 帧间隔,也就是隔多久出现一个 H264 编码中的 I 帧。 完整 MediaFormat 设置示例: MediaFormat mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height); mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar); // 马率 mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, width * height * 5); // 调整码率的控流模式 mediaFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR); // 设置帧率 mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30); // 设置 I 帧间隔 mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); 当开始编解码操作时,开启编解码线程,处理相机预览返回的 YUV 数据。 在这里用到了相机的一个封装库: 编解码操作编解码操作代码如下: while (isEncoding) { // YUV 颜色格式转换 if (!mEncodeDataQueue.isEmpty()) { input = mEncodeDataQueue.poll(); byte[] yuv420sp = newbyte[mWidth * mHeight * 3 / 2]; NV21ToNV12(input, yuv420sp, mWidth, mHeight); input = yuv420sp; } if (input != null) { try { // 从输入缓冲区队列中拿到可用缓冲区,填充数据,再入队 ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers(); ByteBuffer[] outputBuffers = mMediaCodec.getOutputBuffers(); int inputBufferIndex = mMediaCodec.dequeueInputBuffer(-1); if (inputBufferIndex >= 0) { // 计算时间戳 pts = computePresentationTime(generateIndex); ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; inputBuffer.clear(); inputBuffer.put(input); mMediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, pts, 0); generateIndex += 1; } MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC); // 从输出缓冲区队列中拿到编码好的内容,对内容进行相应处理后在释放 while (outputBufferIndex >= 0) { ByteBuffer outputBuffer = outputBuffers[outputBufferIndex]; byte[] outData = newbyte[bufferInfo.size]; outputBuffer.get(outData); // flags 利用位操作,定义的 flag 都是 2 的倍数 if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { // 配置相关的内容,也就是 SPS,PPS mOutputStream.write(outData, 0, outData.length); } elseif ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) { // 关键帧 mOutputStream.write(outData, 0, outData.length); } else { // 非关键帧和SPS、PPS,直接写入文件,可能是B帧或者P帧 mOutputStream.write(outData, 0, outData.length); } mMediaCodec.releaseOutputBuffer(outputBufferIndex, false); outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC); } |
|
来自: 新用户6594HGU4 > 《待分类》