您的位置:首页 > 移动开发 > Android开发

Android 新API 之 MediaCodec使用笔记 <一>

2016-12-16 11:06 417 查看
http://blog.csdn.net/shawnkong/article/details/16337381

版权声明:本文为博主Shawn原创文章©️未经博主允许不得转载。

Android的视频编解码一直让人有点纠结,SDK竟然不提供硬件编解码的API,如果你想自己做,只能通过JNI借助第三方编解码器,其都是使用的软解码,效率很难保证,这对想做视频通话的是一个不小的打击。

好了,说到google 新提供的SDK中出现的类MediaCodec,这个api限制在API 16后,也就是Android 4.1.2后才可以使用,如果你的系统低于这个版本,是不可以使用这个类的。MediaCodec这家伙能提供给你硬件编解码功能,当然得厂商支持在下层已经,如果厂商没做好,系统会提供给你软件编解码器,反正不用你操心,可以直接就拿来用的。

使用网上有人提供的Demo,可以解码mp4文件,具体情况还没研究,先放出Github链接,直接自己抓出来就可以用,

如果懒得去抓,下面贴出代码,就一个简单的Activity,一切搞定,

“记得修改文件名,SAMPLE变量为你自己的文件名称”,

[java] view
plain copy







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) {

// We shouldn't stop the playback at this point, just pass the EOS

// flag to decoder, we will get it again from the

// dequeueOutputBuffer

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);

// We use a very simple clock to keep the video FPS, or the video

// playback will be too fast

while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {

try {

sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

break;

}

}

decoder.releaseOutputBuffer(outIndex, true);

break;

}

// All decoded frames have been rendered, we can stop playing now

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();

}

}

}

最後再奉上我修改過的代碼,增加了選擇文件的功能.

http://download.csdn.net/detail/shawnkong/6555857

最近从事手机iOS/Android直播、视频实时美颜方面的工作。

欢迎加群交流:496010189



顶4

踩0

上一篇Ubuntu
android 开发环境搭建之eclipse篇

下一篇linux
v4l2 开发笔记

我的同类文章

Android(20)

c++中局部变量未初始化引发的离奇惨案~~2014-09-09阅读2534
•Ubuntu
14.04 Linux系统安装Subversion结合Eclipse的一些问题2014-04-22阅读1650
•Android
NDK 开发进阶之 mk文件写法2014-01-13阅读1005
•Android
不能生产 R.java2013-11-27阅读904
•Android
Studio配置2013-10-30阅读2586

•Android
NDK开发之配置 adb-bundle、eclipse 支持C/C++ 语言的JNI开发2014-05-27阅读1016
•Linphone
杂记2014-02-10阅读1841
•编译x264
出现No working C compiler found.2013-12-23阅读4420
•Ubuntu
android 开发环境搭建之eclipse篇2013-11-12阅读1609
•关于64位
Ubuntu 13.04 安装Android Studio的一些问题及ADB驱动配置2013-10-30阅读2298

更多文章

参考知识库




iOS知识库

3444关注|1442收录




Android知识库

27913关注|2411收录




.NET知识库

2412关注|815收录




Java
SE知识库

18288关注|509收录




Java
EE知识库

12515关注|820收录




Java
知识库

20865关注|1436收录




Swift知识库

3486关注|1164收录

猜你在找

FFmpeg音视频高级开发实战 iOS&Android;
Android 5.x顶级视频课程
Android SQLite 性能优化——显示使用事务
Android底层技术:HAL驱动开发
Android基础视频教程

查看评论

15楼 IT_Transformers 2016-10-19 18:14发表 [回复]


你好,我解码以后从哪个地方可以得到每一帧的数据呢?

14楼 邪恶的鱼蛋 2016-06-27 09:14发表 [回复]


您好,我想请问一下MediaCodec 如何暂停解码,就是我点暂停,他就停留在这里,点击继续他就继续开始解码

Re: Shawn4com 2016-07-25 18:15发表 [回复]


回复邪恶的鱼蛋:把解码线程挂起,这样解码就不会继续了

13楼 ai454121 2015-11-10 20:26发表 [回复]


LZ,您好,我发现decoder.configure(format, surface, null, 0); 中如果surface设置为null,那解码速度会比现在慢很多,这边测试差不多慢2倍,这个可能是什么原因引起的,如果不配置surface,有什么办法改进吗?

Re: Shawn4com 2016-07-25 18:21发表 [回复]


回复ai454121:不设置surface会解码变慢么?你是从哪里看出来解码变慢了么?检查一下sleep那个地方,看是不是调用次数增多了

12楼 he_wen_jian 2015-03-05 13:13发表 [回复]


请问如何播放网络视频?

Re: Shawn4com 2016-07-25 18:16发表 [回复]


回复he_wen_jian:这个只是简单演示解码video,如果需要播放网络视频,你还需要有接收、音频解码播放功能

11楼 amsh10421 2014-09-15 17:46发表 [回复]


extractor = new MediaExtractor();

extractor.setDataSource(SAMPLE);

for (int i = 0; i < extractor.getTrackCount(); i++)

extractor.getTrackCount(); 返回值是0 请问这个怎么修改?是我的文件没读进去么?

Re: Shawn4com 2014-09-16 11:06发表 [回复]


回复amsh10421: 加文件读写权限了么?

10楼 sanbo_xyz 2014-06-19 18:51发表 [回复]


上传的不对。。。

Re: Shawn4com 2014-06-20 14:54发表 [回复]


回复sanbo_xyz:什么不对?

Re: sanbo_xyz 2014-06-23 11:21发表 [回复]


回复Shawn4com:不能正常使用。

Re: Shawn4com 2014-06-23 16:09发表 [回复]


回复sanbo_xyz:有什么问题,能说的具体点么?我这里跑的好好的。。

9楼 newplumage 2014-04-03 15:54发表 [回复]


感谢这个例子,我能正常运行了。有两个问题

1.音频需另外解码,怎么处理?

2.视频和音频怎么同步?

请指教!!

Re: Shawn4com 2014-04-15 10:18发表 [回复]


回复newplumage:sorry,我暂时只用来解码视频,没有测试音频,因为现在我主要不是用来做播放器。同步的话使用时间戳。

8楼 yqymmzby 2014-04-01 13:45发表 [回复]


请问音频使用MediaPlayer同步解吗?还是什么?

7楼 yubestming_job 2014-03-20 10:51发表 [回复]


你好,我想问下,我做的实时视频通话,c++那边给我传递过来的是一个Byte[]的字节数组,我怎么显示出来,在android端,求指教。QQ512573717

Re: Shawn4com 2014-03-21 11:01发表 [回复]


回复yubestming_job: 实时流好像不支持的,我试过,没弄出来,要不你试试,如果成功了,回头跟出来?

6楼 xhcw1011 2014-02-19 09:17发表 [回复]


你好,请问我想解实时的h264的流该怎么解,我现在按照网上一些例子做出来了,但是效果很差,很模糊。基本看不到东西。

Re: chenqingfei 2014-10-11 09:05发表 [回复]


回复xhcw1011:同感,我也是在网上搜集了一些信息,写出来的,效果比较差,

如果是非高清的,流畅度还能保证,如果是高清的,网络根本就支撑不了,而且我还做了分包,如果不分包,高清的一帧数据UDP都发不过去。这个问题真的很棘手啊。不知道微信是怎么做的。

Re: 紫枫wrongs 2014-02-28 14:36发表 [回复]


回复xhcw1011:你好,对于实时的视频,在接收端,如何解析解包呢,是不是需要将包组起来,然后解码, 请问下你解码是使用的啥方式,求助!!!

Re: 紫枫wrongs 2014-02-19 14:16发表 [回复]


回复xhcw1011:你好,请问你对h264如何进行解码的呢,我使用的是.264文件解码,可在extractor.setDataSource(fileString); 一直报异常呢

Re: xhcw1011 2014-02-19 14:33发表 [回复]


回复紫枫wrongs:你先看下是报什么错。

Re: 紫枫wrongs 2014-02-20 10:35发表 [回复]


回复xhcw1011:播放H264错误是我的文件有问题,现在换了文件解决了,对于实时流的接收端,你是使用RTP 接收的吗 可以给个Demo参考下吗

5楼 紫枫wrongs 2014-02-17 16:48发表 [回复]


你好,下载的demo,怎么在extractor.setDataSource(SAMPLE);

一直报错呢,是不是不能支持.h264吗 ?

Re: Shawn4com 2014-02-17 22:21发表 [回复]


回复紫枫wrongs:SAMPLE 要制定一个文件。。

Re: 紫枫wrongs 2014-02-18 10:03发表 [回复]


回复Shawn4com:sample 这个路径下的文件,是我自己编码的,使用h264的播放器都可以播放的,

Re: Shawn4com 2014-04-14 14:43发表 [回复]


回复紫枫wrongs:sdk里面的extractor只支持mp4,avi和mkv格式的,h264的裸码解析不了的

4楼 紫枫wrongs 2014-02-13 16:48发表 [回复]


下载csdn的实例,extractor = new MediaExtractor();

extractor.setDataSource(SAMPLE);

执行到这的时候一直报错,求指点啊,

3楼 zuchgi 2014-02-11 00:46发表 [回复]


String SAMPLE = Environment.getExternalStorageDirectory() + "/encoder.h264";

extractor.setDataSource(SAMPLE);

运行到这里的时候显示找不到extractor,请问楼主要如何处理?

Re: Shawn4com 2014-02-25 12:14发表 [回复]


回复zuchgi:贴错误信息看下。

2楼 chen7yang 2014-01-08 15:03发表 [回复] [引用] [举报]


求助:

我的代码 执行到这句时

outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,0);

就会抛 IllegalStateException ,

Re: Shawn4com 2014-01-09 16:17发表 [回复] [引用] [举报]


回复chen7yang:据说这个api还不稳定,实际使用可能有问题。。

1楼 temofil2009 2013-12-24 11:17发表 [回复]


这样的话送到surface播出来的视频是没有audio的吧?

Re: Shawn4com 2013-12-25 10:40发表 [回复]


回复temofil2009:是的,这个只是单解视频,音频需另外解码。。

另外一篇文章的代码,也可以看看:


代码片段(1)[全屏查看所有代码]

1. [代码][Java]代码

?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: