MediaCodec是Android中媒体编解码器,可以对媒体进行编/解码
MediaCodec采用同步/异步方式处理数据,并且使用了一组输入输出缓存(ByteBuffer)。通过请求一个空的输入缓存(ByteBuffer),向其中填充满数据并将它传递给编解码器处理。编解码器处理完这些数据并将处理结果输出至一个空的输出缓存(ByteBuffer)中。使用完输出缓存的数据之后,将其释放回编解码器
MediaCodec的生命周期有三种状态:停止态-Stopped、执行态-Executing、释放态-Released。
停止状态(Stopped)包括了三种子状态:未初始化(Uninitialized)、配置(Configured)、错误(Error)。
执行状态(Executing)会经历三种子状态:刷新(Flushed)、运行(Running)、流结束(End-of-Stream)
(1)当创建了一个MediaCodec对象,此时MediaCodec处于Uninitialized状态。
MediaCodec主要提供了createEncoderByType(String type)、createDecoderByType(String type)两个方法来创建编解码器,它们均需要传入一个MIME类型多媒体格式。常见的MIME类型多媒体格式如下:
video/x-vnd.on2.vp8 - VP8 video (i.e. video in .webm)
video/x-vnd.on2.vp9 - VP9 video (i.e. video in .webm)
video/avc - H.264/AVC video
video/mp4v-es - MPEG4 video
video/3gpp - H.263 video
audio/3gpp - AMR narrowband audio
audio/amr-wb - AMR wideband audio
audio/mpeg - MPEG1/2 audio layer III
audio/mp4a-latm - AAC audio (note, this is raw AAC packets, not packaged in LATM!)
audio/vorbis - vorbis audio
audio/g711-alaw - G.711 alaw audio
audio/g711-mlaw - G.711 ulaw audio
String AUDIO_MIME = "audio/mp4a-latm";
MediaCodec mAudioEncoder = MediaCodec.createEncoderByType(AUDIO_MIME);
MediaCodec还提供了一个createByCodecName (String name)方法,支持使用组件的具体名称来创建编解码器。但是该方法使用起来有些麻烦,且官方是建议最好是配合MediaCodecList使用,因为MediaCodecList记录了所有可用的编解码器。另外,我们也可以使用该类对传入的minmeType参数进行判断,以匹配出MediaCodec对该mineType类型的编解码器是否支持。以指定MIME类型为"video/avc"为例,代码如下:
private static MediaCodecInfo selectCodec(String mimeType) {
// 获取所有支持编解码器数量
int numCodecs = MediaCodecList.getCodecCount();
for (int i = 0; i < numCodecs; i++) {
// 编解码器相关性信息存储在MediaCodecInfo中
MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
// 判断是否为编码器
if (!codecInfo.isEncoder()) {
continue;
}
// 获取编码器支持的MIME类型,并进行匹配
String[] types = codecInfo.getSupportedTypes();
for (int j = 0; j < types.length; j++) {
if (types[j].equalsIgnoreCase(mimeType)) {
return codecInfo;
}
}
}
return null;
}
(2)创建完MediaCodec之后,需要使用configure(…)方法对MediaCodec进行配置,这时MediaCodec转为Configured状态。
编解码器配置使用的是MediaCodec的configure方法,在配置时,configure方法需要传入format、surface、crypto、flags参数,其中format为MediaFormat的实例,它使用”key-value”键值对的形式存储多媒体数据格式信息;surface用于指明解码器的数据源来自于该surface;crypto用于指定一个MediaCrypto对象,以便对媒体数据进行安全解密;flags指明配置的是编码器(CONFIGURE_FLAG_ENCODE)。
MediaFormat mFormat = MediaFormat.createVideoFormat("video/avc", 640 ,480); // 创建MediaFormat
mFormat.setInteger(MediaFormat.KEY_BIT_RATE,600); // 指定比特率
mFormat.setInteger(MediaFormat.KEY_FRAME_RATE,30); // 指定帧率
mFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,mColorFormat); // 指定编码器颜色格式
mFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,10); // 指定关键帧时间间隔
mVideoEncodec.configure(mFormat,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);
(3)配置完MediaCodec之后,调用MediaCodec的start()方法使其转入Executing状态。
在调用start()方法后MediaCodec立即进入Flushed子状态,此时MediaCodec会拥有所有的缓存。一旦第一个输入缓存(input buffer)被移出队列,MediaCodec就转入Running子状态,这种状态占据了MediaCodec的大部分生命周期。当你将一个带有end-of-stream marker标记的输入缓存入队列时,MediaCodec将转入End-of-Stream子状态。在这种状态下,MediaCodec不再接收之后的输入缓存,但它仍然产生输出缓存直到end-of- stream标记输出。你可以在Executing状态的任何时候通过调用flush()方法返回到Flushed子状态。
下面看看整个过程:
调用MediaCodec的start()方法,此时MediaCodec处于Executing状态,可以通过getInputBuffers()方法和getOutputBuffers()方法获取缓存队列:
mAudioEncoder.start();
mAudioInputBuffers = mAudioEncoder.getInputBuffers();
mAudioOutputBuffers = mAudioEncoder.getOutputBuffers();
当MediaCodec处于Executing状态之后就可以对数据进行处理了。
int inputBufIndex = mAudioEncoder.dequeueInputBuffer(1000);
返回的整型变量为请求到的输入缓存的index,通过getInputBuffers()得到的是输入缓存数组,通过index和输入缓存数组可以得到当前请求的输入缓存,在使用之前要clear一下,避免之前的缓存数据影响当前数据
mInputBuffer = mAudioInputBuffers[inputBufIndex];
mInputBuffer.clear();
也可以直接通过mAudioEncoder.getInputBuffer(inputBufIndex)得到输入缓存
mInputBuffer.put(bytes, 0, BUFFER_SIZE_IN_BYTES);
mAudioEncoder.queueInputBuffer(inputBufIndex, 0, BUFFER_SIZE_IN_BYTES, (1000000 * mEncodedSize / AUDIO_BYTE_PER_SAMPLE), 0);
获取输出缓存和获取输入缓存类似
int outputBufIndex = mAudioEncoder.dequeueOutputBuffer(mOutBufferInfo, 1000);
public final static class BufferInfo {
public void set(
int newOffset, int newSize, long newTimeUs, @BufferFlag int newFlags) {
offset = newOffset;
size = newSize;
presentationTimeUs = newTimeUs;
flags = newFlags;
}
public int offset // 偏移量
public int size; // 缓存区有效数据大小
public long presentationTimeUs; // 显示时间戳
public int flags; // 缓存区标志
@NonNull
public BufferInfo dup() {
BufferInfo copy = new BufferInfo();
copy.set(offset, size, presentationTimeUs, flags);
return copy;
}
};
然后通过返回的index得到输出缓存,并通过BufferInfo获取ByteBuffer的信息,我们可以得到当前数据是否Codec-specific Data:
mOutBuffer = mAudioOutputBuffers[outputBufIndex];
if ((mOutBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
// Codec-specific Data, 这里可以从ByteBuffer中获取csd参数
// audioFormat.setByteBuffer("csd-0", mOutBuffer);
} else {
// 处理数据
}
mAudioEncoder.releaseOutputBuffer(outputBufIndex, false);
也可以直接通过mAudioEncoder.getOutputBuffer(outputBufIndex)得到输出缓存
注意一定要调用releaseOutputBuffer方法。
通过调用stop()方法使MediaCodec返回到Uninitialized状态,因此这个MediaCodec可以再次重新配置 。
当使用完MediaCodec后,必须调用release()方法释放其资源。
在极少情况下MediaCodec会遇到错误并进入Error状态。这个错误可能是在队列操作时返回一个错误的值或者有时候产生了一个异常导致的。通过调用 reset()方法使MediaCodec再次可用。你可以在任何状态调用reset()方法使MediaCodec返回到Uninitialized状态。否则,调用 release()方法进入最终的Released状态。
项目中碰到一个诡异问题,yml文件中配置了一个值为字符串的属性,用@Value注入时确发现不对应,出现了全项目中都找不到的值。研究了半天才发现,yml会把0开头的数字作为8进制对待,注入时转化为十进制注入。 datasource: 0400010001所以,字符串最好要写规范了: datasource: '0400010001' ...
Windows Mobile 开发黄金周(6):Direct 3D Mobile初体验 ---Level 200 活动日期: 2005-10-28 10:00 -- 11:30 主 讲: 马宁 ________________________________________Q:用c#的话是否可以像在PC上一样的用DLLImport来访问native代码?A:.NET CF是支持P/Invoke
任务描述本关任务:(注意:可以用3种insert语句的写法完成本题)在宠物商店数据库(PetStore)的商品类别(category)表中插入一条记录,并查询插入记录后的表。插入的记录如下:('06','昆虫',null);其中category表结构如下:相关知识为了完成本关任务,你需要掌握:插入语句插入语句语法一旦创建了数据库和表,下一步就是向表里插入数据。通过INSERT或REPLACE语句可以向表中插入一行或多行数据。语法格式:INSERT [IGNORE] [INT
https://www.cnblogs.com/fox2013/p/4553977.htmlVMP是一款很强大的加密壳,代码虚拟化技术可以很好的保护程序不被恶意修改破J但是很多人不知道怎么给自己的程序加壳,今天给大家晋级下加壳的正确方法相信很多新手都以为只要把软件直接拖到VMP里重新编译一下就是加壳了其实这样是没有达到任何的防破J效果。易语言加VPM壳少不了这两行重要代码置入...
int inet_aton(const char *cp, struct in_addr *inp);将形如“192.168.1.1"类型的点分十进制ip转换成二进制,并存放在struct in_addr中192 = 0xc0168 = 0xa81 = 0x01因此转换后的值为0xc0a811char *inet_ntoa(struct in_addr in);将二进制的ip转成点分十进制形式,转换结果作为返回值struct hostent *gethostbyadd..
nvarchar 和varchar区别 有时候设计字段的时候,碰到nvarchar和varchar时候,是有点犹豫。所以今天就来探个究竟把。(一) varchar是非Unicode可变长度类型,nvarchar是Unicode编码可变长度类型DECLARE @name AS VARCHAR(50)='我是中国人test'SELECT @name AS Name,DA...
操作步骤(参考下图)总结 显示行号 tools >> options >> Editor >> 选中show line numbers 多行注释 选中需注释的行,再按 ctrl + k 取消注释 选中需取消注释的行,再按...
http://www.lydsy.com/JudgeOnline/problem.php?id=4872一种最优解是 从大到小灯有亮的就灭掉最优解是唯一的,且关灯的顺序没有影响最优解 对每个开关至多操作1次,(连带着的灯的亮灭改变不算)设最优解 需要操作cnt次,那么就有cnt盏灯是正确的选择设 f[i] 表示 有i种正确的选择 变为 有i-1种正确的选择 的 期望次数...
常用的功能有四个元素(Elements):用于查看或修改HTML元素的属性、CSS属性、监听事件、断点等。控制台(Console):控制台一般用于执行一次性代码,查看JavaScript对象,查看调试日志信息或异常信息。源代码(Sources):该页面用于查看页面的HTML文件源代码、JavaScript源代码、CSS源代码,此外最重要的是可以调试JavaScript源代码,可以给JS代码添加断点等。网络(Network):网络页面主要用于查看header等与网络连接相关的信息。Hea
无处不在的小土-gmapping的ROS封装 (gaoyichao.com)
http://blog.chinaunix.net/uid/25014876/cid-2370-list-1.html
前两天,搞了个Linux系统想尝试一下如何搭建一个服务器,在过程中遇到了一些问题,我觉得需要做个记录。PS:阿里云服务器的购买啥的我就不细说了。 一切都准备就绪之后,首先,是Linux命令。因为之前在工作中很少接触Linux操作,所以除了常见的cd、ll、ls、./等命令符有些操作都需要通过资料查阅来确认。这些操作接下来的搭建过程中会一一说明。XSh...