Skip to content

Commit

Permalink
修改不支持的硬编码格式报错。
Browse files Browse the repository at this point in the history
  • Loading branch information
WhalenSun committed Jul 29, 2024
1 parent 6370750 commit 6724d3e
Showing 1 changed file with 78 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

public class VideoEncoder {

private final String TAG = VideoEncoder.class.getSimpleName();
private final VideoEncodeParam videoEncodeParam;
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private MediaCodec mediaCodec;
Expand All @@ -28,6 +29,8 @@ public class VideoEncoder {
private int MAX_BITRATE_LENGTH = 1000000;
private int beginBitRate = 0;

private boolean isSupportNV21 = false;

private String firstSupportColorFormatCodecName = ""; // OMX.qcom.video.encoder.avc 和 c2.android.avc.encoder 过滤,这两个h264编码性能好一些。如果都不支持COLOR_FormatYUV420Planar,就用默认的方式。

public VideoEncoder(VideoEncodeParam param) {
Expand All @@ -38,6 +41,8 @@ public VideoEncoder(VideoEncodeParam param) {

private void initMediaCodec() {
try {
checkSupportedColorFormats("video/avc"); // H.264 编码器

if (!firstSupportColorFormatCodecName.isEmpty()) {
mediaCodec = MediaCodec.createByCodecName(firstSupportColorFormatCodecName);
} else {
Expand All @@ -55,8 +60,13 @@ private void initMediaCodec() {
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
//描述视频格式的帧速率(以帧/秒为单位)的键。帧率,一般在15至30之内,太小容易造成视频卡顿。
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, videoEncodeParam.getFrameRate());
//色彩格式,具体查看相关API,不同设备支持的色彩格式不尽相同
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
if (isSupportNV21) {
//色彩格式,具体查看相关API,不同设备支持的色彩格式不尽相同
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
} else {
//色彩格式,具体查看相关API,不同设备支持的色彩格式不尽相同
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
}
//关键帧间隔时间,单位是秒
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, videoEncodeParam.getiFrameInterval());
mediaFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR);
Expand All @@ -74,6 +84,44 @@ private void initMediaCodec() {
}
}

private void checkSupportedColorFormats(String mimeType) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
MediaCodecList codecList = null;
codecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
MediaCodecInfo[] codecInfos = codecList.getCodecInfos();

for (MediaCodecInfo codecInfo : codecInfos) {
if (!codecInfo.isEncoder()) {
continue;
}

String[] supportedTypes = codecInfo.getSupportedTypes();
for (String type : supportedTypes) {
if (type.equalsIgnoreCase(mimeType)) {
MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(type);
int[] colorFormats = capabilities.colorFormats;

for (int colorFormat : colorFormats) {
switch (colorFormat) {
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
Log.e(TAG, "Supported color format: COLOR_FormatYUV420Planar");
isSupportNV21 = false;
return;
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
Log.e(TAG, "Supported color format: COLOR_FormatYUV420SemiPlanar");
isSupportNV21 = true;
return;
default:
Log.e(TAG, "Supported color format: " + colorFormat);
break;
}
}
}
}
}
}
}

//描述平均位速率(以位/秒为单位)的键。 关联的值是一个整数
@TargetApi(Build.VERSION_CODES.KITKAT)
public void setVideoBitRate(int bitRate) {
Expand All @@ -91,7 +139,7 @@ public void setVideoBitRate(int bitRate) {
mediaCodec.setParameters(params);

} catch (IllegalStateException e) {
Log.e("TAG", "updateBitrate failed", e);
Log.e(TAG, "updateBitrate failed", e);
}
}

Expand All @@ -104,24 +152,30 @@ public int getVideoBitRate() {
public void encoderH264(byte[] data, boolean mirror) {
if (executor.isShutdown()) return;
executor.submit(() -> {
byte[] rotateBytes;
//视频顺时针旋转90度
if (mirror) {
rotateBytes = nv21Rotate270(data, videoEncodeParam.getWidth(), videoEncodeParam.getHeight());
byte[] readyToProcessBytes;
if (isSupportNV21) {
//将NV21编码成NV12
byte[] bytes = NV21ToNV12(data, videoEncodeParam.getWidth(), videoEncodeParam.getHeight());
//视频顺时针旋转90度
byte[] nv12 = rotateNV290(bytes, videoEncodeParam.getWidth(), videoEncodeParam.getHeight());

if (mirror) {
verticalMirror(nv12, videoEncodeParam.getHeight(), videoEncodeParam.getWidth());
}
readyToProcessBytes = nv12;
} else {
rotateBytes = nv21Rotate90(data, videoEncodeParam.getWidth(), videoEncodeParam.getHeight());
byte[] rotateBytes;
//视频顺时针旋转90度
if (mirror) {
rotateBytes = nv21Rotate270(data, videoEncodeParam.getWidth(), videoEncodeParam.getHeight());
} else {
rotateBytes = nv21Rotate90(data, videoEncodeParam.getWidth(), videoEncodeParam.getHeight());
}
//将NV21编码成I420
byte[] i420 = toI420(rotateBytes, videoEncodeParam.getHeight(), videoEncodeParam.getWidth());
readyToProcessBytes = i420;
}
//将NV21编码成I420
byte[] i420 = toI420(rotateBytes, videoEncodeParam.getHeight(), videoEncodeParam.getWidth());

// //将NV21编码成NV12
// byte[] bytes = NV21ToNV12(data, videoEncodeParam.getWidth(), videoEncodeParam.getHeight());
// //视频顺时针旋转90度
// byte[] nv12 = rotateNV290(bytes, videoEncodeParam.getWidth(), videoEncodeParam.getHeight());
//
// if (mirror) {
// verticalMirror(nv12, videoEncodeParam.getHeight(), videoEncodeParam.getWidth());
// }


try {
//拿到输入缓冲区,用于传送数据进行编码
Expand All @@ -134,9 +188,9 @@ public void encoderH264(byte[] data, boolean mirror) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
//往输入缓冲区写入数据
inputBuffer.put(i420);
inputBuffer.put(readyToProcessBytes);
//五个参数,第一个是输入缓冲区的索引,第二个数据是输入缓冲区起始索引,第三个是放入的数据大小,第四个是时间戳,保证递增就是
mediaCodec.queueInputBuffer(inputBufferIndex, 0, i420.length, System.nanoTime() / 1000, 0);
mediaCodec.queueInputBuffer(inputBufferIndex, 0, readyToProcessBytes.length, System.nanoTime() / 1000, 0);
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
//拿到输出缓冲区的索引
Expand All @@ -152,7 +206,7 @@ public void encoderH264(byte[] data, boolean mirror) {

if (bufferInfo.flags == MediaCodec.BUFFER_FLAG_KEY_FRAME) {
// I 帧的处理逻辑
// Log.e("TAG", "==========I帧==============="+seq);
// Log.e(TAG, "==========I帧==============="+seq);
ByteBuffer spsb = mediaCodec.getOutputFormat().getByteBuffer("csd-0");
byte[] sps = new byte[spsb.remaining()];
spsb.get(sps, 0, sps.length);
Expand All @@ -170,7 +224,7 @@ public void encoderH264(byte[] data, boolean mirror) {
}
} else {
//outData就是输出的h264数据
// Log.e("TAG", "==========P帧===============" + seq);
// Log.e(TAG, "==========P帧===============" + seq);
if (encoderListener != null) {
encoderListener.onVideoEncoded(outData, System.currentTimeMillis(), seq);
seq++;
Expand Down Expand Up @@ -356,7 +410,7 @@ private void checkSupportedColorFormats() {
for (int colorFormat : colorFormats) {

if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar) {
Log.d("TAG", "Video encoder: " + codecInfo.getName() + ", supported color format: " + colorFormat);
Log.d(TAG, "Video encoder: " + codecInfo.getName() + ", supported color format: " + colorFormat);
firstSupportColorFormatCodecName = codecInfo.getName();
return;
}
Expand Down

0 comments on commit 6724d3e

Please sign in to comment.