视频基础知识
音频编码和视频编码用的api是一样的,不一样的地方是对编码器的设置
public void init(Context context, int width,
int height,
String srcPath, String dstPath,
IHanlderCallback callback) {
mWidth = width;
mHeight = height;
mSrcFilePath = srcPath;
mDstFilePath = dstPath;
mExecutorService = Executors.newCachedThreadPool();
mCallback = callback;
mBufferSize =mWidth * mHeight * 3 / 2;
Log.e("yuanBuffer", "buffesize=" + mBufferSize + " nbsample=" + mBufferSize / 4);
try {
mEncorder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
} catch (Exception e) {
e.printStackTrace();
}
MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC,
mWidth,
mHeight);
format.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileMain);
format.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel31);
format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * 1000);
format.setInteger(MediaFormat.KEY_FRAME_RATE, 25);//帧率,一般在15至30之内,太小容易造成视频卡顿。
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10); //250帧一个关键帧
format.setInteger(MediaFormat.KEY_MAX_B_FRAMES, 3);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);//色彩格式,具体查看相关API,不同设备支持的色彩格式不尽相同
mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
}
这里的配置会根据
https://blog.csdn.net/qq_15255121/article/details/115552857
这篇文章进行配置
下面是完整的代码
package com.yuanxuzhen.androidmedia.encode;
import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Build;
import android.util.Log;
import androidx.annotation.RequiresApi;
import com.yuanxuzhen.androidmedia.IHanlderCallback;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class YuvEncoder {
private int mWidth;
private int mHeight;
private String mDstFilePath = null;
private String mSrcFilePath = null;
MediaCodec mEncorder;
ExecutorService mExecutorService;
private int mBufferSize;
private IHanlderCallback mCallback;
private boolean isEncodeing = false;
public void init(Context context, int width,
int height,
String srcPath, String dstPath,
IHanlderCallback callback) {
mWidth = width;
mHeight = height;
mSrcFilePath = srcPath;
mDstFilePath = dstPath;
mExecutorService = Executors.newCachedThreadPool();
mCallback = callback;
mBufferSize =mWidth * mHeight * 3 / 2;
Log.e("yuanBuffer", "buffesize=" + mBufferSize + " nbsample=" + mBufferSize / 4);
try {
mEncorder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
} catch (Exception e) {
e.printStackTrace();
}
MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC,
mWidth,
mHeight);
format.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileMain);
format.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel31);
format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * 1000);
format.setInteger(MediaFormat.KEY_FRAME_RATE, 25);//帧率,一般在15至30之内,太小容易造成视频卡顿。
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10); //250帧一个关键帧
format.setInteger(MediaFormat.KEY_MAX_B_FRAMES, 3);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);//色彩格式,具体查看相关API,不同设备支持的色彩格式不尽相同
mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
}
public void startEncording() {
mExecutorService.execute(new Runnable() {
@Override
public void run() {
startEncordingV1();
}
});
}
/**
* 开始编码
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void startEncordingV1() {
if (mEncorder == null) {
isEncodeing = false;
if (mCallback != null) {
mCallback.onFail();
}
return;
}
isEncodeing = true;
mEncorder.start();
try {
FileInputStream inputStream = new FileInputStream(mSrcFilePath);
FileOutputStream mFileStream = new FileOutputStream(mDstFilePath);
MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
while (true) {
long a = System.currentTimeMillis();
// 从队列中取出录音的一帧音频数据
byte[] byteArray = new byte[mBufferSize];
int readSize = inputStream.read(byteArray);
if (readSize <= 0) {
break;
}
ByteBuffer buf = ByteBuffer.wrap(byteArray);
// 取出InputBuffer,填充音频数据,然后输送到编码器进行编码
int inputBufferIndex = mEncorder.dequeueInputBuffer(0);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = mEncorder.getInputBuffer(inputBufferIndex);
inputBuffer.clear();
inputBuffer.put(buf);
mEncorder.queueInputBuffer(inputBufferIndex, 0, readSize, System.nanoTime(), 0);
}
// 取出编码好的一帧音频数据,然后给这一帧添加ADTS头
int outputBufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
while (outputBufferIndex >= 0) {
ByteBuffer outputBuffer = mEncorder.getOutputBuffer(outputBufferIndex);
int outBufferSize = outputBuffer.limit();
byte[] aacBytes = new byte[outBufferSize];
outputBuffer.get(aacBytes, 0, outputBuffer.limit());
mFileStream.write(aacBytes);
mEncorder.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
}
long b = System.currentTimeMillis() - a;
Log.i("AudioEncode", "编码耗时-毫秒==" + b);
}
if (mFileStream != null) {
try {
mFileStream.flush();
mFileStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (inputStream != null) {
try {
mFileStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (mEncorder != null) {
mEncorder.stop();
}
if (mCallback != null) {
mCallback.onSuccess();
}
} catch (FileNotFoundException e) {
if (mCallback != null) {
mCallback.onFail();
}
e.printStackTrace();
} catch (IOException e) {
if (mCallback != null) {
mCallback.onFail();
}
e.printStackTrace();
} finally {
isEncodeing = false;
}
}
public void release() {
if (mEncorder == null) {
return;
}
mEncorder.release();
mEncorder = null;
isEncodeing = false;
}
public boolean isEncodeing() {
return isEncodeing;
}
}
gitee地址:
https://gitee.com/creat151/android-media.git
|