*因为工做须要,须要利用MediaCodec实现Playback及Transcode等功能,故在学习过程当中翻译了Google官方的MediaCodec API文档,因为做者水平限制,文中不免有错误和不恰当之处,望批评指正。数组
*转载请注明出处:http://www.cnblogs.com/roger-yu/微信
Android MediaCodec能够访问底层的media codecs,咱们很容易利用MediaCodec来构建encoder或decoder来实现音视频编码和音视频解码的功能。异步
简单点儿理解,一个Codec(能够认为是一个MediaCodec的实例对象)就至关于一个“处理器”:处理输入数据,并产生输出数据。ide
以下图所示,每个Codec都维护着一组 input buffers 和 output buffers。开始时Codec拥有全部buffers的全部权,Client(能够暂且理解为MediaCodec以外写的程序)没法向 input buffer 写入数据,也没法读取 output buffer 中的数据。数据处理开始后,Client向Codec请求一个(同步模式)或者接收到(异步模式)一个空的 input buffer,将要处理的数据写入到该buffer中,而后提交给Codec处理,Codec处理完数据后会将处理的结果写入到一个空的 output buffer 中,以后Client就能够请求或接收到这个存有结果的 output buffer,Client对结果使用完毕后就能够release这个output buffer,Codec就能够再次使用这个buffer,如此过程完成整个的处理。函数
Android MediaCodec主要有3种数据处理的方式:学习
1. 使用Buffers的异步处理方式(Asynchronous Processing using Buffers)ui
2. 使用Buffers的同步处理方式(Synchronous Processing using Buffers)编码
3. 使用Buffer数组的同步处理方式(Synchronous Processing using Buffer Arrays (deprecated))spa
依据Android版本不一样能够采用不一样的方式,以下图:翻译
目前最经常使用的是前两种模式,故接下来重点讲解。
基本处理流程:
注意:
1. 在调用configure配置MediaCodec以前须要为MediaCodec设置callback,须要实现MediaCodec.Callback接口并重写其中的方法:onInputBufferAvailable 、onOutputBufferAvailable、onOutputFormatChanged、onError,工做时MediaCodec会利用 这四个回调方法来自动的通知Client何时input buffer有效,何时output buffer有效,何时media format发生变化,何时运行出错,也是在这些方法中Client向Codec送入数据并获得处理的结果及获取Codec的一些其余信息。
2. 异步模式下MediaCodec的状态转换会有些许不一样,在调用start方法后会直接进入Running状态;
异步处理模式下,调用MediaCodec.start()后Codec 当即进入Running子状态,经过设置的callback中的回调方法onInputBufferAvailable()会自动收到可用(empty)的input buffer,此时能够根据input buffer id调用getInputBuffer(id)获得这个buffer,并将须要的处理的数据写入该buffer中,最后调用queueInputBuffer(id, ...)将该buffer提交给Codec处理;Codec每处理完一帧数据就会将处理结果写入一个空的output buffer,并经过回调函数
onOutputBufferAvailable
来通知Client来读取结果,Client能够根据output bufffer id调用getOutputBuffer(id)获取该buffer并读取结果,完毕后能够调用releaseOutputBuffer(id, ...)释放该buffer给Codec再次使用。
典型的代码设计:
1 MediaCodec codec = MediaCodec.createByCodecName(name); 2 MediaFormat mOutputFormat; // member variable 3 // 异步模式下须要在configure以前设置callback 4 codec.setCallback(new MediaCodec.Callback() { 5 6 /** 7 * 在onInputBufferAvailable回调方法中,MediaCodec会通知何时input 8 * buffer有效,根据buffer id,调用getInputBuffer(id)能够得到这个buffer, 9 * 此时就能够向这个buffer中写入数据,最后调用queueInputBuffer(id, …)提交 10 * 给MediaCodec处理。 11 */ 12 @Override 13 void onInputBufferAvailable(MediaCodec mc, int inputBufferId) { 14 ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId); 15 // fill inputBuffer with valid data 16 … 17 codec.queueInputBuffer(inputBufferId, …); 18 } 19 20 /** 21 * 在onOutputBufferAvailable回调方法中,MediaCodec会通知何时output 22 * buffer有效,根据buffer id,调用getOutputBuffer(id)能够得到这个buffer, 23 * 此时就能够读取这个buffer中的数据,最后调用releaseOutputBuffer(id, …)释放 24 * 给MediaCodec再次使用。 25 */ 26 27 @Override 28 void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) { 29 ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId); 30 MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A 31 // bufferFormat is equivalent to mOutputFormat 32 // outputBuffer is ready to be processed or rendered. 33 … 34 codec.releaseOutputBuffer(outputBufferId, …); 35 } 36 /** 37 * 当MediaCodec的output format发生变化是会回调该方法,通常在start以后都会首先回调该方法 38 */ 39 @Override 40 void onOutputFormatChanged(MediaCodec mc, MediaFormat format) { 41 // Subsequent data will conform to new format. 42 // Can ignore if using getOutputFormat(outputBufferId) 43 mOutputFormat = format; // option B 44 } 45 /** 46 * MediaCodec运行发生错误时会回调该方法 47 */ 48 @Override 49 void onError(…) { 50 … 51 } 52 }); 53 codec.configure(format, …); 54 mOutputFormat = codec.getOutputFormat(); // option B 55 codec.start(); // start 以后MediaCodec当即进入Running子状态,并会回调callback中的方法 56 // wait for processing to complete 57 codec.stop(); // stop后MediaCodec进入Uninitialized子状态 58 codec.release(); //使用完毕要释放掉MediaCdoec占用的资源
基本处理流程:
同步模式下,MediaCodec调用start()方法后会进入Flushed子状态,而后第一次调用dequeueInputBuffer()后才会进入Running子状态。
这种模式下,程序须要在一个无限循环中经过调用dequeueInputBuffer(...)和dequeueOutputBuffer(...)来不断地请求Codec是否有可用的input buffer 或 output buffer:
> 若是有可用的input buffer:根据获得的buffer id,调用getInputBuffer(id)获取该buffer,并向其中写入待处理的数据,而后调用queueInputBuffer(id,..)提交到Codec进行处理
> 若是有可用的output buffer: 根据获得的buffer id,调用getOutputBuffer(id)获取该buffer,读取其中的处理结果,而后调用releaseOutputBuffer(id,..)释放该buffer供Codec再次使用
> 处理过程当中还可能受到一些特殊标记的buffer id,好比MediaCodec.INFO_OUTPUT_FORMAT_CHANGED,要做出恰当处理
典型的代码设计:
1 MediaCodec codec = MediaCodec.createByCodecName(name); 2 codec.configure(format, ...); 3 MediaFormat outputFormat = codec.getOutputFormat(); // option B 4 codec.start(); // start()方法后会进入Flushed子状态 5 /** 6 * 在一个无限循环中不断地请求Codec是否有可用的input buffer 或 output buffer 7 */ 8 for (;;) { 9 int inputBufferId = codec.dequeueInputBuffer(timeoutUs); // 请求是否有可用的input buffer 10 if (inputBufferId >= 0) { 11 ByteBuffer inputBuffer = codec.getInputBuffer(...); // 获取input buffer 12 // fill inputBuffer with valid data 13 ... 14 codec.queueInputBuffer(inputBufferId, ...); // 提交数据给Codec 15 } 16 int outputBufferId = codec.dequeueOutputBuffer(...); // 请求是否有可用的output buffer 17 if (outputBufferId >= 0) { 18 ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId); // 获取output buffer 19 MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A 20 // bufferFormat is identical to outputFormat 21 // outputBuffer is ready to be processed or rendered. 22 ... 23 codec.releaseOutputBuffer(outputBufferId, ...); // 释放output buffer供Codec再次使用 24 } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 25 // Subsequent data will conform to new format. 26 // Can ignore if using getOutputFormat(outputBufferId) 27 outputFormat = codec.getOutputFormat(); // option B 28 } 29 } 30 codec.stop(); 31 codec.release(); //释放资源
》异步模式下经过回调函数来自动的传递可用的input buffer 或 output buffer
》同步模式下须要经过dequeueInputBuffer(...)或dequeueOutputBuffer(...)来请求获取可用的input buffer 或 output buffer
微信扫一扫,关注玖零日记,获取更多相关资讯及源码 -- 虽无面朝大海,依旧春暖花开