赞
踩
简单的介绍一下MediaCodec:本文主要讲述的是博主自己在用MediaCodec进行编解码过程中分别用同步和异步两种方式实现了硬编解码的过程,因为之前自己在用异步实现的过程中经常会出现解码黑屏的情况,百度和博客上也没有相关的实例,找到的只有一个示例代码,并没有提供什么帮助。所以写了代码去实现,Mediacodec的简单使用我先介绍一下,并且把Mediacodec的异步实现的流程介绍一下。
这是谷歌官网给出的Mediacodec的编解码数据流向,Mediacodec类可用于访问底层的媒体编解码器,即编码器/解码器组件。这是Android底层的多媒体支持基础设施的一部分(通常与MediaExtractor,MediaSync,mediamuxer,mediacrypto,mediadrm)。
从广义上讲,一个编解码器处理输入数据来生成输出数据。它处理异步数据和使用一组输入和输出缓冲器。在一个简单的级别上,您请求(或接收)一个空的输入缓冲区,用数据填充它并将其发送到编解码器进行处理。该编解码器使用的数据并将其转换为它的一个空的输出缓冲器。最后,你请求(或接收)一个填充的输出缓冲区,消耗它的内容并将其释放回编解码器。
Mediacodec的基本使用介绍
MediaCodec mediaCodec = MediaCodec.createDecoderByType("video/avc");
//实例化的首选解码器支持的MIME类型的输入数据,最好使用finddecoderforformat(mediaformat)和createbycodecname(字符串)来保证解码器能够处理一个给定的格式.
MediaFormat mediaFormat = android.media.MediaFormat.createVideoFormat("video/avc", 1280, 720);
mediaFormat.setInteger(android.media.MediaFormat.KEY_BIT_RATE, 512 * 2 * 1000);
//设置码率
mediaFormat.setInteger(android.media.MediaFormat.KEY_FRAME_RATE, 20);
//设置帧率
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
//关键帧间隔时间 单位s
mediaCodec.configure(mediaFormat, new Surface(mSurfaceView.getSurfaceTexture()), null, 0);
//配置mediaformat并且传入需要显示解码画面的SurfaceView
mediaCodec.start();
// start();
MediaCodec codec = MediaCodec.createByCodecName(name);
MediaFormat mOutputFormat; // member variable
codec.setCallback(new MediaCodec.Callback() {
@Override
void onInputBufferAvailable(MediaCodec mc, int inputBufferId) {
ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId);
// fill inputBuffer with valid data
…
codec.queueInputBuffer(inputBufferId, …);
}
@Override
void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) {
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
// bufferFormat is equivalent to mOutputFormat
// outputBuffer is ready to be processed or rendered.
…
codec.releaseOutputBuffer(outputBufferId, …);
}
@Override
void onOutputFormatChanged(MediaCodec mc, MediaFormat format) {
// Subsequent data will conform to new format.
// Can ignore if using getOutputFormat(outputBufferId)
mOutputFormat = format; // option B
}
@Override
void onError(…) {
…
}
});
codec.configure(format, …);
mOutputFormat = codec.getOutputFormat(); // option B
codec.start();
// wait for processing to complete
codec.stop();
codec.release();
MediaCodec codec = MediaCodec.createByCodecName(name);
codec.configure(format, …);
MediaFormat outputFormat = codec.getOutputFormat(); // option B
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, …);
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// Subsequent data will conform to new format.
// Can ignore if using getOutputFormat(outputBufferId)
outputFormat = codec.getOutputFormat(); // option B
}
}
codec.stop();
codec.release();
xml配置
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<TextView
android:textSize="18sp"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="预览图像:" />
<TextureView android:id="@+id/v1"
android:layout_marginTop="5dp"
android:layout_width="match_parent"
android:layout_height="200dp"/>
<TextView
android:textSize="18sp"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="解码后渲染图像:" />
<TextureView android:id="@+id/v2"
android:layout_marginTop="5dp"
android:layout_width="match_parent"
android:layout_height="200dp"/>
</LinearLayout>
把Camera获取到的数据呈现在TextureView上
camera = Camera.open();
camera.setPreviewTexture(surfaceView1);
//显示camera的画面 surfaceView1 是xml声明的第一个TextureView 需要在前面绑定
Camera.Parameters cp= cam.getParameters();
cp.setFlashMode("off");
// 无闪光灯
cp.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
//设置聚焦模式
cp.setSceneMode(Camera.Parameters.SCENE_MODE_AUTO);
cp.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
cp.setPreviewFormat(ImageFormat.YV12);
cp.setPictureSize(camWidth, camHeight);
cp.setPreviewSize(camWidth, camHeight);
camera.setParameters(cp);
camera.startPreview();
asynccodec = MediaCodec.createEncoderByType("video/avc");
MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc",mWidth,mHeight);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 125000);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
asynccodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
asynccodec.setCallback(new MediaCodec.Callback() {
@Override
public void onInputBufferAvailable(MediaCodec mc, int index) {
ByteBuffer inputBuffer = asynccodec.getInputBuffer(index);
byte[] tmpBuffer = camera.getInstance().getDequeueEncode();
// tmpBuffer 主要用户获取camera中获取的到的一帧画面的数据
// 博主通过一个encodeBuffer缓存队列进行数据的存储,这里是从缓存里面出一帧的数据
//getDequeueEncode()函数是当缓存队列有的时候,会进行数据的出队列
inputBuffer.clear();
int length = 0;
if(tmpBuffer != null) {
inputBuffer.put(tmpBuffer);
//将获取到的数据放入通过index获取的ByteBuffer中
length = tmpBuffer.length;
encodedFrameCount++;
}
asynccodec.queueInputBuffer(index, 0, length, 0, 0);
//通过index获取ByteBuffer然后放入编码器中
}
@Override
public void onOutputBufferAvailable(MediaCodec codec, int outputBufferId, MediaCodec.BufferInfo info) {
//异步实现的关键!!!!
//onOutputBufferAvailable这个回调函数网上找不到一些实现的例子,主要的问题就出现在TextureView在呈现画面的时候出现掉帧和模糊的的情况。
//通过异步实现,当mediacodec有数据的时候,就会自动调用这个函数,把编码后的数据进行出队列操作
ByteBuffer outputBuffer = asynccodec.getOutputBuffer(outputBufferId);
MediaFormat bufferFormat = asynccodec.getOutputFormat(outputBufferId);
if (outputBuffer != null && info.size > 0) {
byte[] tmpBuffer = new byte[info.size];
tmpBuffer = new byte[outputBuffer.remaining()];
//ByteBuffer 转化为byte[]数组
outputBuffer.get(tmpBuffer);
mFrameBuffer.enqueue(tmpBuffer,tmpBuffer.length);
//将异步编码后的数据存放到了自己定义的一个缓存队列中,后续TextureView进行显示也是通过从这个缓存队列中获取相应数据然后再进行解码然后呈现出来的
}
asynccodec.releaseOutputBuffer(outputBufferId, true);
//释放当前outputBufferId上的数据
}
@Override
public void onError(MediaCodec codec, MediaCodec.CodecException e) {
//错误回调
Log.d("error", e.getDiagnosticInfo());
}
@Override
public void onOutputFormatChanged(MediaCodec codec, android.media.MediaFormat format) {
}
});
} catch (IOException e) {
e.printStackTrace();
}
asynccodec.start();
缓存队列FrameBuffer的介绍
缓存队列的实现是用的java的数组,自己封装了出队列和进队列的方法。同时加入了ReentrantLock机制提供保证。还有对摄像头数据获取的前几个关键帧进行判断的操作,这里就不贴代码了。 其实不使用队列的话,数据量小的情况下也是可以流畅运行的。
Thanks.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。