android 使用MediaCodec 编解码总结_android学习笔记总结
android 使用MediaCodec 编解码总结由刀豆文库小编整理,希望给你工作、学习、生活带来方便,猜你可能喜欢“android学习笔记总结”。
android 使用MediaCodec 编解码总结
本文将主要介绍在安卓中调用MediaCodec类实现视频文件的硬解码,以及如何将以byte[]类型存储的图像数据通过硬编码合成视频文件。1.MediaCodec类的编解码原理 参考链接:https://developer.Android.com/ 工作流是这样的: 以编码为例,首先要初始化硬件编码器,配置要编码的格式、视频文件的长宽、码率、帧率、关键帧间隔等等。这一步叫configure。之后开启编码器,当前编码器便是可用状态,随时准备接收数据。下一个过程便是编码的running过程,在此过程中,需要维护两个buffer队列,InputBuffer 和OutputBuffer,用户需要不断出队InputBuffer(即dequeueInputBuffer),往里边放入需要编码的图像数据之后再入队等待处理,然后硬件编码器开始异步处理,一旦处理结束,他会将数据放在OutputBuffer中,并且通知用户当前有输出数据可用了,那么用户就可以出队一个OutputBuffer,将其中的数据拿走,然后释放掉这个buffer。结束条件在于end-of-stream这个flag标志位的设定。在编码结束后,编码器调用stop函数停止编码,之后调用release函数将编码器完全释放掉,整体流程结束。
2.视频解码程序示例 代码来源于
Android: MediaCodec视频文件硬件解码以下所有代码可以在此处下载[java] view plain copy
print?
package com.example.guoheng_iri.helloworld;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.media.Image;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.util.Log;
import java.io.File;import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.LinkedBlockingQueue;
public cla VideoDecode {
private static final String TAG = “VideoToFrames”;
private static final boolean VERBOSE = true;
private static final long DEFAULT_TIMEOUT_US = 10000;
private static final int COLOR_FormatI420 = 1;
private static final int COLOR_FormatNV21 = 2;
public static final int FILE_TypeI420 = 1;
public static final int FILE_TypeNV21 = 2;
public static final int FILE_TypeJPEG = 3;
private final int decodeColorFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
private int outputImageFileType =-1;
private String OUTPUT_DIR;
public int ImageWidth=0;
public int ImageHeight=0;
MediaExtractor extractor = null;
MediaCodec decoder = null;
MediaFormat mediaFormat;
public void setSaveFrames(String dir, int fileType)throws IOException {
if(fileType!= FILE_TypeI420 && fileType!= FILE_TypeNV21 && fileType!= FILE_TypeJPEG){
throw new IllegalArgumentException(“only support FILE_TypeI420 ” + “and FILE_TypeNV21 ” + “and FILE_TypeJPEG”);
}
outputImageFileType = fileType;
File theDir = new File(dir);
if(!theDir.exists()){
theDir.mkdirs();
} else if(!theDir.isDirectory()){
throw new IOException(“Not a directory”);
}
OUTPUT_DIR = theDir.getAbsolutePath()+ “/”;
}
public void VideoDecodePrepare(String videoFilePath){
extractor = null;
decoder = null;
try {
File videoFile = new File(videoFilePath);
extractor = new MediaExtractor();
extractor.setDataSource(videoFile.toString());
int trackIndex = selectTrack(extractor);
if(trackIndex
throw new RuntimeException(“No video track found in ” + videoFilePath);
}
extractor.selectTrack(trackIndex);
mediaFormat = extractor.getTrackFormat(trackIndex);
String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
decoder = MediaCodec.createDecoderByType(mime);
showSupportedColorFormat(decoder.getCodecInfo().getCapabilitiesForType(mime));
if(isColorFormatSupported(decodeColorFormat, decoder.getCodecInfo().getCapabilitiesForType(mime))){
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, decodeColorFormat);
Log.i(TAG, “set decode color format to type ” + decodeColorFormat);
} else {
Log.i(TAG, “unable to set decode color format, color format type ” + decodeColorFormat + “ not supported”);
}
decoder.configure(mediaFormat, null, null, 0);
decoder.start();
} catch(IOException ioe){
throw new RuntimeException(“failed init encoder”, ioe);
}
}
public void close(){
decoder.stop();
decoder.release();
if(extractor!= null){
extractor.release();
extractor = null;
}
}
public void excuate()
{
try {
decodeFramesToImage(decoder, extractor, mediaFormat);
}finally {
// release encoder, muxer, and input Surface
close();
}
}
private void showSupportedColorFormat(MediaCodecInfo.CodecCapabilities caps){
System.out.print(“supported color format: ”);
for(int c : caps.colorFormats){
System.out.print(c + “t”);
}
System.out.println();
}
private boolean isColorFormatSupported(int colorFormat, MediaCodecInfo.CodecCapabilities caps){
for(int c : caps.colorFormats){
if(c == colorFormat){
return true;
}
}
return false;
}
public void decodeFramesToImage(MediaCodec decoder, MediaExtractor extractor, MediaFormat mediaFormat){
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
boolean sawInputEOS = false;
boolean sawOutputEOS = false;
final int width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
final int height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
ImageWidth=width;
ImageHeight=height;
int outputFrameCount = 0;
while(!sawOutputEOS){
if(!sawInputEOS){
int inputBufferId = decoder.dequeueInputBuffer(DEFAULT_TIMEOUT_US);
if(inputBufferId >= 0){
ByteBuffer inputBuffer = decoder.getInputBuffer(inputBufferId);
int sampleSize = extractor.readSampleData(inputBuffer, 0);//将一部分视频数据读取到inputbuffer中,大小为sampleSize
if(sampleSize
decoder.queueInputBuffer(inputBufferId, 0, 0, 0L, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
sawInputEOS = true;
} else {
long presentationTimeUs = extractor.getSampleTime();
decoder.queueInputBuffer(inputBufferId, 0, sampleSize, presentationTimeUs, 0);
extractor.advance();//移动到视频文件的下一个地址
}
}
}
int outputBufferId = decoder.dequeueOutputBuffer(info, DEFAULT_TIMEOUT_US);
if(outputBufferId >= 0){
if((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM)!= 0){
sawOutputEOS = true;
}
boolean doRender =(info.size!= 0);
if(doRender){
outputFrameCount++;
Image image = decoder.getOutputImage(outputBufferId);
System.out.println(“image format: ” + image.getFormat());
if(outputImageFileType!=-1){
String fileName;
switch(outputImageFileType){
case FILE_TypeI420:
fileName = OUTPUT_DIR + String.format(“frame_%05d_I420_%dx%d.yuv”, outputFrameCount, width, height);
dumpFile(fileName, getDataFromImage(image, COLOR_FormatI420));
break;
case FILE_TypeNV21:
fileName = OUTPUT_DIR + String.format(“frame_%05d_NV21_%dx%d.yuv”, outputFrameCount, width, height);
dumpFile(fileName, getDataFromImage(image, COLOR_FormatNV21));
break;
case FILE_TypeJPEG:
fileName = OUTPUT_DIR + String.format(“frame_%05d.jpg”, outputFrameCount);
compreToJpeg(fileName, image);
break;
}
}
image.close();
decoder.releaseOutputBuffer(outputBufferId, true);
}
}
}
}
private static int selectTrack(MediaExtractor extractor){
int numTracks = extractor.getTrackCount();
for(int i = 0;i
MediaFormat format = extractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
if(mime.startsWith(“video/”)){
if(VERBOSE){
Log.d(TAG, “Extractor selected track ” + i + “(” + mime + “): ” + format);
}
return i;
}
}
return-1;
}
private static boolean isImageFormatSupported(Image image){
int format = image.getFormat();
switch(format){
case ImageFormat.YUV_420_888:
case ImageFormat.NV21:
case ImageFormat.YV12:
return true;
}
return false;
}
public static byte[] getGrayFromData(Image image, int colorFormat){
if(colorFormat!= COLOR_FormatI420 && colorFormat!= COLOR_FormatNV21){
throw new IllegalArgumentException(“only support COLOR_FormatI420 ” + “and COLOR_FormatNV21”);
}
if(!isImageFormatSupported(image)){
throw new RuntimeException(“can't convert Image to byte array, format ” + image.getFormat());
}
Image.Plane[] planes = image.getPlanes();
int i = 0;
ByteBuffer buffer = planes[i].getBuffer();
byte[] data = new byte[buffer.remaining()];
buffer.get(data, 0, data.length);
if(VERBOSE)Log.v(TAG, “Finished reading data from plane ” + i);
return data;
}
public static byte[] getDataFromImage(Image image, int colorFormat){
if(colorFormat!= COLOR_FormatI420 && colorFormat!= COLOR_FormatNV21){
throw new IllegalArgumentException(“only support COLOR_FormatI420 ” + “and COLOR_FormatNV21”);
}
if(!isImageFormatSupported(image)){
throw new RuntimeException(“can't convert Image to byte array, format ” + image.getFormat());
}
Rect crop = image.getCropRect();
int format = image.getFormat();
int width = crop.width();
int height = crop.height();
Image.Plane[] planes = image.getPlanes();
byte[] data = new byte[width * height * ImageFormat.getBitsPerPixel(format)/ 8];
byte[] rowData = new byte[planes[0].getRowStride()];
int channelOffset = 0;
int outputStride = 1;
for(int i = 0;i
switch(i){
case 0:
channelOffset = 0;
outputStride = 1;
break;
case 1:
if(colorFormat == COLOR_FormatI420){
channelOffset = width * height;
outputStride = 1;
} else if(colorFormat == COLOR_FormatNV21){
channelOffset = width * height;
outputStride = 2;
}
break;
case 2:
if(colorFormat == COLOR_FormatI420){
channelOffset =(int)(width * height * 1.25);
outputStride = 1;
} else if(colorFormat =