MediaCodec的介绍及实例
来源:互联网 发布:奥林巴斯25mm1.8知乎 编辑:程序博客网 时间:2024/06/07 18:17
MediaCodec
MediaCodec的介绍
MediaCodec是Android平台提供的硬件编解码器,它由一组API构成。这样说还是太抽象了,下面用一个很简单的开源例子MediaCodecDemo来说明MediaCodec怎么使用。
MediaCodecDemo:https://github.com/vecio/MediaCodecDemo
MediaCodec的实例
MediaCodecDemo的源代码文件只有一个,比较简单,主要的功能是读取一个本地的MP4文件,然后对它进行解码,最后显示在屏幕上
1、创建显示区域SurfaceView
2、把显示区域绑定到播放线程上
3、创建解复用器MediaExtractor,它的功能是分离音频和视频
4、设置解复用器的数据源(即MP4文件的路径)
5、遍历数据源的track的信息,track是MP4文件中的数据的集合,分析该track的格式,因为我们的目的是处理视频,因此,我们需要找到视频所在的track
6、找到视频所在的track后,让解复用器选择它,表示接下来我们要处理这个track;然后根据格式信息创建解码器MediaCodec
7、对解码器进行配置
8、启动解码器
9、得到解码器的输入队列和输出队列;队列里面存放的是一个一个的缓冲区;把缓冲区组成队列的目的是为了复用这里缓冲区,例如,一个缓冲区用完之后,可以把它放进队列尾部,让解码器重新使用;输入队列中的缓冲区用于存放从MP4文件读取的数据;输出队列的缓冲区存放的是已经解码的数据
10、进入循环
(1)获取一个空闲的输入缓冲区,然后使用解复用器读取数据,放进缓冲区中,再把这个缓冲区添加到解码器的输入队列的尾部,让解码器进行解码
(2)获取一个有效的输出缓冲区,这个缓冲区存放的是已经解码完成的数据
(3)解码器内部会把解码的数据显示在SurfaceView上
完整的代码如下:
- package io.vec.demo.mediacodec;
-
- import java.nio.ByteBuffer;
-
- import android.app.Activity;
- import android.media.MediaCodec;
- import android.media.MediaCodec.BufferInfo;
- import android.media.MediaExtractor;
- import android.media.MediaFormat;
- import android.os.Bundle;
- import android.os.Environment;
- import android.util.Log;
- import android.view.Surface;
- import android.view.SurfaceHolder;
- import android.view.SurfaceView;
-
- public class DecodeActivity extends Activity implements SurfaceHolder.Callback {
-
-
- private static final String SAMPLE = Environment.getExternalStorageDirectory() + "/video.mp4";
-
-
- private PlayerThread mPlayer = null;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- SurfaceView sv = new SurfaceView(this);
- sv.getHolder().addCallback(this);
- setContentView(sv);
- }
-
- protected void onDestroy() {
- super.onDestroy();
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- if (mPlayer == null) {
- mPlayer = new PlayerThread(holder.getSurface());
- mPlayer.start();
- }
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- if (mPlayer != null) {
- mPlayer.interrupt();
- }
- }
-
-
-
-
-
- private class PlayerThread extends Thread {
- private MediaExtractor extractor;
- private MediaCodec decoder;
- private Surface surface;
-
- public PlayerThread(Surface surface) {
- this.surface = surface;
- }
-
- @Override
- public void run() {
- extractor = new MediaExtractor();
-
-
- extractor.setDataSource(SAMPLE);
-
-
- for (int i = 0; i < extractor.getTrackCount(); i++) {
-
- MediaFormat format = extractor.getTrackFormat(i);
- String mime = format.getString(MediaFormat.KEY_MIME);
-
-
- if (mime.startsWith("video/")) {
- extractor.selectTrack(i);
-
- decoder = MediaCodec.createDecoderByType(mime);
-
-
- decoder.configure(format, surface, null, 0);
- break;
- }
- }
-
- if (decoder == null) {
- Log.e("DecodeActivity", "Can't find video info!");
- return;
- }
-
-
- decoder.start();
-
-
- ByteBuffer[] inputBuffers = decoder.getInputBuffers();
-
- ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
- BufferInfo info = new BufferInfo();
- boolean isEOS = false;
- long startMs = System.currentTimeMillis();
-
- while (!Thread.interrupted()) {
- if (!isEOS) {
-
- int inIndex = decoder.dequeueInputBuffer(10000);
- if (inIndex >= 0) {
-
- ByteBuffer buffer = inputBuffers[inIndex];
-
- int sampleSize = extractor.readSampleData(buffer, 0);
- if (sampleSize < 0) {
-
-
-
- Log.d("DecodeActivity", "InputBuffer BUFFER_FLAG_END_OF_STREAM");
- decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
- isEOS = true;
- } else {
-
- decoder.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0);
-
-
- extractor.advance();
- }
- }
- }
-
-
- int outIndex = decoder.dequeueOutputBuffer(info, 10000);
- switch (outIndex) {
- case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
- Log.d("DecodeActivity", "INFO_OUTPUT_BUFFERS_CHANGED");
- outputBuffers = decoder.getOutputBuffers();
- break;
- case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
- Log.d("DecodeActivity", "New format " + decoder.getOutputFormat());
- break;
- case MediaCodec.INFO_TRY_AGAIN_LATER:
- Log.d("DecodeActivity", "dequeueOutputBuffer timed out!");
- break;
- default:
-
-
- ByteBuffer buffer = outputBuffers[outIndex];
- Log.v("DecodeActivity", "We can't use this buffer but render it due to the API limit, " + buffer);
-
-
-
-
-
- while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {
- try {
- sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- break;
- }
- }
-
-
- decoder.releaseOutputBuffer(outIndex, true);
- break;
- }
-
-
- if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
- Log.d("DecodeActivity", "OutputBuffer BUFFER_FLAG_END_OF_STREAM");
- break;
- }
- }
-
-
- decoder.stop();
-
- decoder.release();
-
- extractor.release();
- }
- }
- }
下面按照步骤进行详细讲解。
创建显示区域SurfaceView
-
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- SurfaceView sv = new SurfaceView(this);
- sv.getHolder().addCallback(this);
- setContentView(sv);
- }
把显示区域绑定到播放线程上
-
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- if (mPlayer == null) {
- mPlayer = new PlayerThread(holder.getSurface());
- mPlayer.start();
- }
- }
创建解复用器
解复用器的作用是分离音频和视频
-
- extractor = new MediaExtractor();
-
-
- extractor.setDataSource(SAMPLE);
根据视频媒体的格式创建解码器
遍历解复用器的Track(Track是数据样本的集合),选择视频样本,然后取得媒体格式,根据媒体格式创建解码器
-
- for (int i = 0; i < extractor.getTrackCount(); i++) {
-
- MediaFormat format = extractor.getTrackFormat(i);
- String mime = format.getString(MediaFormat.KEY_MIME);
-
-
- if (mime.startsWith("video/")) {
- extractor.selectTrack(i);
-
- decoder = MediaCodec.createDecoderByType(mime);
-
-
- decoder.configure(format, surface, null, 0);
- break;
- }
- }
启动解码器
获取空闲缓冲区然后读取MP4数据
这些数据是压缩的数据
- if (!isEOS) {
-
- int inIndex = decoder.dequeueInputBuffer(10000);
- if (inIndex >= 0) {
-
- ByteBuffer buffer = inputBuffers[inIndex];
-
- int sampleSize = extractor.readSampleData(buffer, 0);
- if (sampleSize < 0) {
-
-
-
- Log.d("DecodeActivity", "InputBuffer BUFFER_FLAG_END_OF_STREAM");
- decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
- isEOS = true;
- } else {
-
- decoder.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0);
-
-
- extractor.advance();
- }
- }
- }
取得解码器的输出
这些数据是解压后的数据,可以用来显示
-
- int outIndex = decoder.dequeueOutputBuffer(info, 10000);
- switch (outIndex) {
- case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
- Log.d("DecodeActivity", "INFO_OUTPUT_BUFFERS_CHANGED");
- outputBuffers = decoder.getOutputBuffers();
- break;
- case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
- Log.d("DecodeActivity", "New format " + decoder.getOutputFormat());
- break;
- case MediaCodec.INFO_TRY_AGAIN_LATER:
- Log.d("DecodeActivity", "dequeueOutputBuffer timed out!");
- break;
- default:
-
-
- ByteBuffer buffer = outputBuffers[outIndex];
- Log.v("DecodeActivity", "We can't use this buffer but render it due to the API limit, " + buffer);
-
-
-
-
-
- while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {
- try {
- sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- break;
- }
- }
-
-
- decoder.releaseOutputBuffer(outIndex, true);
- break;
- }