大家好,欢迎来到IT知识分享网。
1 前言
MediaCodec 主要用于视频解码和编码操作,可以实现视频倍速播放、全关键帧转换、视频倒放等功能。
MediaCodec 的工作原理图如下:
MediaCodec 的主要接口如下:
//创建解码器(type为mime或name)
public static MediaCodec createDecoderByType(String type)
//创建编码器(type为mime或name)
public static MediaCodec createEncoderByType(String type)
//配置解码器和编码器(flag为0表示解码器,1表示编码器)
public void configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags)
//启动编码器或解码器
public final void start()
//获取输入队列的一个空闲索引(timeoutUs:最多等待时间,-1表示一直等待,单位:微秒us)
public final int dequeueInputBuffer(long timeoutUs)
//获取输入队列的一个空闲缓存区(index:dequeueInputBuffer方法的返回值)
public ByteBuffer getInputBuffer(int index)
//提醒解码器或编码器处理数据(index:dequeueInputBuffer方法的返回值)
public final void queueInputBuffer(int index, int offset, int size, long presentationTimeUs, int flags)
//创建BufferInfo类,用于存储解码或编码后的缓存数据的格式信息
public final static class BufferInfo
//获取输出队列的一个缓存区的索引,并将格式信息保存在info中(timeoutUs:最多等待时间,-1表示一直等待,单位:微秒us)
public final int dequeueOutputBuffer(BufferInfo info, long timeoutUs)
//获取输出队列的一个缓存区(index:dequeueOutputBuffer方法的返回值)
public ByteBuffer getOutputBuffer(int index)
//清除index指向的缓存区中的数据
public final void releaseOutputBuffer(int index, boolean render)
//结束解码或编码会话
public final void stop()
//释放资源
public final void release()
本文将以全关键帧转换为例,讲解 MediaCodec 的应用。
2 项目目录
3 代码
在 AndroidManifest.xml 中配置权限,如下:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
MainActivity.java
package com.example.codec;
import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
String[] permissions = {
"android.permission.WRITE_EXTERNAL_STORAGE",
"android.permission.READ_EXTERNAL_STORAGE"};
if(Build.VERSION.SDK_INT>=23){
requestPermissions(permissions,1);
}
}
public void onclick(View v) {
Manager manager = new Manager("/sdcard/Pictures/WeiXin/a.mp4", "/sdcard/a.mp4");
manager.prepare();
manager.start();
manager.transform(); //转换为全关键帧
}
}
Manager.java
package com.example.codec;
import android.media.MediaFormat;
import android.os.Handler;
import android.os.Message;
import java.util.LinkedList;
public class Manager {
private Decoder decoder;
private Encoder encoder;
private LinkedList<byte[]> de2EnQue;
private String input_path;
private String output_path;
public static final int DECODE_ONE_FRAME = 1; //decoder解码了1帧
public static final int DECODER_RELEASE = 2; //decoder释放了资源
public static final int ENCODE_ONE_FRAME = 3; //encoder编码了1帧
public static final int ENCODER_RELEASE = 4; //encoder释放了资源
public Manager(String input_path, String output_path) {
this.input_path = input_path;
this.output_path = output_path;
de2EnQue = new LinkedList<>();
decoder = new Decoder(mainHandler, de2EnQue);
encoder = new Encoder(mainHandler, de2EnQue);
}
public void prepare() {
decoder.setPath(input_path);
encoder.setPath(output_path);
MediaFormat format = decoder.config();
encoder.config(format);
}
public void start() {
decoder.start();
encoder.start();
}
public void transform() {
decoder.decode();
}
private Handler mainHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DECODE_ONE_FRAME:
encoder.encode(); //通知编码器编码
break;
case DECODER_RELEASE:
encoder.release(); //通知编码器释放资源
break;
case ENCODE_ONE_FRAME:
break;
case ENCODER_RELEASE:
break;
}
}
};
}
Decoder.java
package com.example.codec;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Handler;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.LinkedList;
public class Decoder {
private MediaCodec decoder;
private MediaExtractor extractor;
private MediaFormat format;
private LinkedList<byte[]> de2EnQue;
private Handler mainHandler;
private String input_path;
private int outputSize;
private final int timeout = 500;
public Decoder(Handler mainHandler, LinkedList<byte[]> de2EnQue) {
this.mainHandler = mainHandler;
this.de2EnQue = de2EnQue;
extractor = new MediaExtractor();
}
public void setPath(String input_path) {
this.input_path = input_path;
}
public MediaFormat config() {
try {
extractor.setDataSource(input_path);
} catch (IOException e) {
e.printStackTrace();
}
String mime = "";
int count = extractor.getTrackCount(); //获取轨道数
for (int i = 0; i < count; i++) {
format = extractor.getTrackFormat(i);
mime = format.getString(MediaFormat.KEY_MIME);
if (mime.startsWith("video/")) { // mp4为“video/avc”
extractor.selectTrack(i);
break;
}
}
int width = format.getInteger(MediaFormat.KEY_WIDTH);
int height = format.getInteger(MediaFormat.KEY_HEIGHT);
outputSize = width*height*3/2;
try {
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
decoder = MediaCodec.createDecoderByType(mime);
decoder.configure(format,null,null,0);
} catch (IOException e) {
e.printStackTrace();
}
return format;
}
public void start() {
decoder.start();
}
public void decode() {
new Thread() {
@Override
public void run() {
execute();
release();
}
}.start();
}
public void execute() {
boolean isInputFinish = false; //输入结束标志
boolean isOutPutFinish = false; //输出结束标志
while (!isOutPutFinish) {
if (!isInputFinish) {
int inputIndex = decoder.dequeueInputBuffer(timeout);
if (inputIndex>=0) {
ByteBuffer inputBuffer = decoder.getInputBuffer(inputIndex);
inputBuffer.clear(); //清除以前数据
int sampleSize = extractor.readSampleData(inputBuffer, 0);
if (sampleSize>0) {
decoder.queueInputBuffer(inputIndex, 0, inputBuffer.limit(), 0, 0);
extractor.advance();
} else {
isInputFinish = true;
decoder.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
}
}
}
while (!isOutPutFinish) {
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputIndex = decoder.dequeueOutputBuffer(bufferInfo, timeout);
if (outputIndex<0) {
break;
}
if (bufferInfo.flags==MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
if (bufferInfo.size==0) {
if (isInputFinish) {
isOutPutFinish = true;
}
break;
}
bufferInfo.flags = 0;
}
if (bufferInfo.size>0) {
ByteBuffer outputBuffer = decoder.getOutputBuffer(outputIndex);
byte[] tempBuffer = new byte[outputSize];
outputBuffer.get(tempBuffer, 0, outputSize);
de2EnQue.addLast(tempBuffer);
mainHandler.sendEmptyMessage(Manager.DECODE_ONE_FRAME);
decoder.releaseOutputBuffer(outputIndex, false);
}
}
}
}
public void release() {
decoder.stop();
decoder.release();
mainHandler.sendEmptyMessage(Manager.DECODER_RELEASE);
}
}
Encoder.java
package com.example.codec;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.os.Handler;
import android.os.HandlerThread;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.LinkedList;
public class Encoder {
private MediaCodec encoder;
private MediaMuxer muxer;
private LinkedList<byte[]> de2EnQue;
private Handler mainHandler;
private Handler handler;
private String output_path;
private int interval = 34483;
private volatile long pts = 0;
private final int timeout = 500;
private int trackIndex;
public Encoder(Handler mainHandler, LinkedList<byte[]> de2EnQue) {
this.mainHandler = mainHandler;
this.de2EnQue = de2EnQue;
}
public void setPath(String output_path) {
this.output_path = output_path;
}
public void config(MediaFormat mediaFormat) {
int width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
int height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
int frame_rate = mediaFormat.getInteger(MediaFormat.KEY_FRAME_RATE);
MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 0); //全I帧
format.setInteger(MediaFormat.KEY_FRAME_RATE, frame_rate);
format.setInteger(MediaFormat.KEY_BIT_RATE, 1500000);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
try {
encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
encoder.configure(format, null,null, MediaCodec.CONFIGURE_FLAG_ENCODE);
muxer = new MediaMuxer(output_path,MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
} catch (IOException e) {
e.printStackTrace();
}
}
public void start() {
encoder.start();
HandlerThread handlerThread = new HandlerThread("encoder");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
}
public void encode() {
handler.post(new Runnable() {
@Override
public void run() {
execute();
}
});
}
private void execute() {
int inputIndex = encoder.dequeueInputBuffer(timeout);
if (inputIndex>=0) {
if (!de2EnQue.isEmpty()) {
ByteBuffer inputBuffer = encoder.getInputBuffer(inputIndex);
inputBuffer.clear(); //清除以前数据
byte[] tempBuffer = de2EnQue.removeFirst();
inputBuffer.put(tempBuffer, 0, tempBuffer.length);
encoder.queueInputBuffer(inputIndex, 0, inputBuffer.limit(), pts, 0);
pts += interval;
}
}
while(true) {
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputIndex = encoder.dequeueOutputBuffer(bufferInfo, timeout);
if (outputIndex==MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
MediaFormat outputFormat = encoder.getOutputFormat();
trackIndex = muxer.addTrack(outputFormat);
muxer.start();
break;
}
if (outputIndex<0) {
break;
}
if (bufferInfo.size>0) {
ByteBuffer outputBuffer = encoder.getOutputBuffer(outputIndex);
//这里可以通过修改 bufferInfo.presentationTimeUs 实现倍速播放
muxer.writeSampleData(trackIndex, outputBuffer, bufferInfo);
mainHandler.sendEmptyMessage(Manager.ENCODE_ONE_FRAME);
encoder.releaseOutputBuffer(outputIndex, false);
}
}
}
public void release() {
handler.post(new Runnable() {
@Override
public void run() {
encoder.stop();
encoder.release();
handler.getLooper().quit();
mainHandler.sendEmptyMessage(Manager.ENCODER_RELEASE);
}
});
}
}
4 拓展
可以通过修改 bufferInfo.presentationTimeUs 实现倍速播放,如下:
float speed = 3.0f; //播放速度
bufferInfo.presentationTimeUs = (long)(bufferInfo.presentationTimeUs/speed)
muxer.writeSampleData(trackIndex, outputBuffer, bufferInfo);
详见 → 使用MediaExtractor、MediaMuxer去掉视频文件中的音频数据 中拓展。
另外,也可以通过以下方式实现倍速播放视频:
float speed = 3.0f; //播放速度
encoder.queueInputBuffer(inputIndex, 0, inputBuffer.limit(), (long)(pts/speed), 0);
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/25831.html