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变量为你自己的文件名称”,
最後再奉上我修改過的代碼,增加了選擇文件的功能.
http://download.csdn.net/detail/shawnkong/6555857
最近从事手机iOS/Android直播、视频实时美颜方面的工作。
欢迎加群交流:496010189
-
顶
- 4
-
踩
- 0
-
猜你在找
另外一篇文章的代码,也可以看看:
代码片段(1)[全屏查看所有代码]
1. [代码][Java]代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
|
private
final
String TAG =
"MediaCodeSample"
;
/** 用来解码 */
private
MediaCodec mMediaCodec;
/** 用来读取音频文件 */
private
MediaExtractor extractor;
private
MediaFormat format;
private
String mime =
null
;
private
int
sampleRate =
0
, channels =
0
, bitrate =
0
;
private
long
presentationTimeUs =
0
, duration =
0
;
public
void
decode(String url)
{
extractor =
new
MediaExtractor();
// 根据路径获取源文件
try
{
extractor.setDataSource(url);
}
catch
(Exception e)
{
Log.e(TAG,
" 设置文件路径错误"
+ e.getMessage());
}
try
{
// 音频文件信息
format = extractor.getTrackFormat(
0
);
mime = format.getString(MediaFormat.KEY_MIME);
sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
// 声道个数:单声道或双声道
channels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
// if duration is 0, we are probably playing a live stream
duration = format.getLong(MediaFormat.KEY_DURATION);
// System.out.println("歌曲总时间秒:"+duration/1000000);
bitrate = format.getInteger(MediaFormat.KEY_BIT_RATE);
}
catch
(Exception e)
{
Log.e(TAG,
"音频文件信息读取出错:"
+ e.getMessage());
// 不要退出,下面进行判断
}
Log.d(TAG,
"Track info: mime:"
+ mime +
" 采样率sampleRate:"
+ sampleRate +
" channels:"
+ channels +
" bitrate:"
+ bitrate +
" duration:"
+ duration);
// 检查是否为音频文件
if
(format ==
null
|| !mime.startsWith(
"audio/"
))
{
Log.e(TAG,
"不是音频文件 end !"
);
return
;
}
// 实例化一个指定类型的解码器,提供数据输出
// Instantiate an encoder supporting output data of the given mime type
mMediaCodec = MediaCodec.createDecoderByType(mime);
if
(mMediaCodec ==
null
)
{
Log.e(TAG,
"创建解码器失败!"
);
return
;
}
mMediaCodec.configure(format,
null
,
null
,
0
);
mMediaCodec.start();
// 用来存放目标文件的数据
ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();
// 解码后的数据
ByteBuffer[] outputBuffers = mMediaCodec.getOutputBuffers();
// 设置声道类型:AudioFormat.CHANNEL_OUT_MONO单声道,AudioFormat.CHANNEL_OUT_STEREO双声道
int
channelConfiguration = channels ==
1
? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO;
Log.i(TAG,
"channelConfiguration="
+ channelConfiguration);
extractor.selectTrack(
0
);
// ==========开始解码=============
boolean
sawInputEOS =
false
;
boolean
sawOutputEOS =
false
;
final
long
kTimeOutUs =
10
;
MediaCodec.BufferInfo info =
new
MediaCodec.BufferInfo();
while
(!sawOutputEOS)
{
try
{
if
(!sawInputEOS)
{
int
inputBufIndex = mMediaCodec.dequeueInputBuffer(kTimeOutUs);
if
(inputBufIndex >=
0
)
{
ByteBuffer dstBuf = inputBuffers[inputBufIndex];
int
sampleSize = extractor.readSampleData(dstBuf,
0
);
if
(sampleSize <
0
)
{
Log.d(TAG,
"saw input EOS. Stopping playback"
);
sawInputEOS =
true
;
sampleSize =
0
;
}
else
{
presentationTimeUs = extractor.getSampleTime();
}
mMediaCodec.queueInputBuffer(inputBufIndex,
0
, sampleSize, presentationTimeUs,
sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM :
0
);
if
(!sawInputEOS)
{
extractor.advance();
}
}
else
{
Log.e(TAG,
"inputBufIndex "
+ inputBufIndex);
}
}
// !sawInputEOS
// decode to PCM and push it to the AudioTrack player
int
res = mMediaCodec.dequeueOutputBuffer(info, kTimeOutUs);
if
(res >=
0
)
{
int
outputBufIndex = res;
ByteBuffer buf = outputBuffers[outputBufIndex];
final
byte
[] chunk =
new
byte
[info.size];
buf.get(chunk);
buf.clear();
if
(chunk.length >
0
)
{
// chunk解码后的音频流
// TODO:处理...
}
mMediaCodec.releaseOutputBuffer(outputBufIndex,
false
);
if
((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) !=
0
)
{
Log.d(TAG,
"saw output EOS."
);
sawOutputEOS =
true
;
}
}
else
if
(res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED)
{
outputBuffers = mMediaCodec.getOutputBuffers();
Log.w(TAG,
"[AudioDecoder]output buffers have changed."
);
}
else
if
(res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED)
{
MediaFormat oformat = mMediaCodec.getOutputFormat();
Log.w(TAG,
"[AudioDecoder]output format has changed to "
+ oformat);
}
else
{
Log.w(TAG,
"[AudioDecoder] dequeueOutputBuffer returned "
+ res);
}
}
catch
(RuntimeException e)
{
Log.e(TAG,
"[decodeMP3] error:"
+ e.getMessage());
}
}
// =================================================================================
if
(mMediaCodec !=
null
)
{
mMediaCodec.stop();
mMediaCodec.release();
mMediaCodec =
null
;
}
if
(extractor !=
null
)
{
extractor.release();
extractor =
null
;
}
// clear source and the other globals
duration =
0
;
mime =
null
;
sampleRate =
0
;
channels =
0
;
bitrate =
0
;
presentationTimeUs =
0
;
duration =
0
;
}
|
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:是的,这个只是单解视频,音频需另外解码。。