|
@ -1,10 +1,6 @@ |
|
|
package com.serenegiant.usb.encoder.biz; |
|
|
package com.serenegiant.usb.encoder.biz; |
|
|
|
|
|
|
|
|
import java.io.BufferedOutputStream; |
|
|
import android.annotation.SuppressLint; |
|
|
import java.io.IOException; |
|
|
|
|
|
import java.lang.ref.WeakReference; |
|
|
|
|
|
import java.nio.ByteBuffer; |
|
|
|
|
|
|
|
|
|
|
|
import android.media.MediaCodec; |
|
|
import android.media.MediaCodec; |
|
|
import android.media.MediaCodecInfo; |
|
|
import android.media.MediaCodecInfo; |
|
|
import android.media.MediaCodecList; |
|
|
import android.media.MediaCodecList; |
|
@ -14,7 +10,13 @@ import android.os.Bundle; |
|
|
import android.os.Environment; |
|
|
import android.os.Environment; |
|
|
import android.util.Log; |
|
|
import android.util.Log; |
|
|
|
|
|
|
|
|
/** 对YUV视频流进行编码 |
|
|
import java.io.BufferedOutputStream; |
|
|
|
|
|
import java.io.IOException; |
|
|
|
|
|
import java.lang.ref.WeakReference; |
|
|
|
|
|
import java.nio.ByteBuffer; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 对YUV视频流进行编码 |
|
|
* Created by jiangdongguo on 2017/5/6. |
|
|
* Created by jiangdongguo on 2017/5/6. |
|
|
*/ |
|
|
*/ |
|
|
|
|
|
|
|
@ -54,10 +56,6 @@ public class H264EncodeConsumer extends Thread { |
|
|
this.listener = listener; |
|
|
this.listener = listener; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public H264EncodeConsumer(){ |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public H264EncodeConsumer(int width, int height) { |
|
|
public H264EncodeConsumer(int width, int height) { |
|
|
this.mWidth = width; |
|
|
this.mWidth = width; |
|
|
this.mHeight = height; |
|
|
this.mHeight = height; |
|
@ -77,9 +75,6 @@ public class H264EncodeConsumer extends Thread { |
|
|
public void setRawYuv(byte[] yuvData, int width, int height) { |
|
|
public void setRawYuv(byte[] yuvData, int width, int height) { |
|
|
if (!isEncoderStart) |
|
|
if (!isEncoderStart) |
|
|
return; |
|
|
return; |
|
|
// 根据编码器支持转换颜色空间格式
|
|
|
|
|
|
// 即 nv21 ---> YUV420sp(21)
|
|
|
|
|
|
// nv21 ---> YUV420p (19)
|
|
|
|
|
|
if (mWidth != width || mHeight != height) { |
|
|
if (mWidth != width || mHeight != height) { |
|
|
mWidth = width; |
|
|
mWidth = width; |
|
|
mHeight = height; |
|
|
mHeight = height; |
|
@ -96,7 +91,8 @@ public class H264EncodeConsumer extends Thread { |
|
|
Thread.sleep(time / 2); |
|
|
Thread.sleep(time / 2); |
|
|
} |
|
|
} |
|
|
// 将数据写入编码器
|
|
|
// 将数据写入编码器
|
|
|
feedMediaCodecData(yuvData); |
|
|
|
|
|
|
|
|
feedMediaCodecData(nv12ToNV21(yuvData, mWidth, mHeight)); |
|
|
|
|
|
|
|
|
if (time > 0) |
|
|
if (time > 0) |
|
|
Thread.sleep(time / 2); |
|
|
Thread.sleep(time / 2); |
|
@ -133,6 +129,7 @@ public class H264EncodeConsumer extends Thread { |
|
|
isExit = true; |
|
|
isExit = true; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@SuppressLint("WrongConstant") |
|
|
@Override |
|
|
@Override |
|
|
public void run() { |
|
|
public void run() { |
|
|
if (!isEncoderStart) { |
|
|
if (!isEncoderStart) { |
|
@ -261,7 +258,6 @@ public class H264EncodeConsumer extends Thread { |
|
|
mMediaCodec.start(); |
|
|
mMediaCodec.start(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
isEncoderStart = true; |
|
|
isEncoderStart = true; |
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP + 1) { |
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP + 1) { |
|
|
inputBuffers = outputBuffers = null; |
|
|
inputBuffers = outputBuffers = null; |
|
@ -288,6 +284,7 @@ public class H264EncodeConsumer extends Thread { |
|
|
|
|
|
|
|
|
private static final int FRAME_RATE = 15; |
|
|
private static final int FRAME_RATE = 15; |
|
|
private static final float BPP = 0.50f; |
|
|
private static final float BPP = 0.50f; |
|
|
|
|
|
|
|
|
private int calcBitRate() { |
|
|
private int calcBitRate() { |
|
|
final int bitrate = (int) (BPP * FRAME_RATE * mWidth * mHeight); |
|
|
final int bitrate = (int) (BPP * FRAME_RATE * mWidth * mHeight); |
|
|
Log.i(TAG, String.format("bitrate=%5.2f[Mbps]", bitrate / 1024f / 1024f)); |
|
|
Log.i(TAG, String.format("bitrate=%5.2f[Mbps]", bitrate / 1024f / 1024f)); |
|
@ -296,6 +293,7 @@ public class H264EncodeConsumer extends Thread { |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* select the first codec that match a specific MIME type |
|
|
* select the first codec that match a specific MIME type |
|
|
|
|
|
* |
|
|
* @param mimeType |
|
|
* @param mimeType |
|
|
* @return null if no codec matched |
|
|
* @return null if no codec matched |
|
|
*/ |
|
|
*/ |
|
@ -327,6 +325,7 @@ public class H264EncodeConsumer extends Thread { |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* select color format available on specific codec and we can use. |
|
|
* select color format available on specific codec and we can use. |
|
|
|
|
|
* |
|
|
* @return 0 if no colorFormat is matched |
|
|
* @return 0 if no colorFormat is matched |
|
|
*/ |
|
|
*/ |
|
|
protected static final int selectColorFormat(final MediaCodecInfo codecInfo, final String mimeType) { |
|
|
protected static final int selectColorFormat(final MediaCodecInfo codecInfo, final String mimeType) { |
|
@ -356,12 +355,14 @@ public class H264EncodeConsumer extends Thread { |
|
|
* color formats that we can use in this class |
|
|
* color formats that we can use in this class |
|
|
*/ |
|
|
*/ |
|
|
protected static int[] recognizedFormats; |
|
|
protected static int[] recognizedFormats; |
|
|
|
|
|
|
|
|
static { |
|
|
static { |
|
|
recognizedFormats = new int[]{ |
|
|
recognizedFormats = new int[]{ |
|
|
|
|
|
// MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar,
|
|
|
// MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar,
|
|
|
// MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar,
|
|
|
|
|
|
// MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar,
|
|
|
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar, |
|
|
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar, |
|
|
MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar, |
|
|
MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar, |
|
|
// MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface,
|
|
|
|
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -374,4 +375,87 @@ public class H264EncodeConsumer extends Thread { |
|
|
} |
|
|
} |
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private byte[] nv21ToI420(byte[] data, int width, int height) { |
|
|
|
|
|
byte[] ret = new byte[width * height * 3 / 2]; |
|
|
|
|
|
int total = width * height; |
|
|
|
|
|
|
|
|
|
|
|
ByteBuffer bufferY = ByteBuffer.wrap(ret, 0, total); // I420的Y分量
|
|
|
|
|
|
ByteBuffer bufferU = ByteBuffer.wrap(ret, total, total / 4); // I420的U分量
|
|
|
|
|
|
ByteBuffer bufferV = ByteBuffer.wrap(ret, total + total / 4, total / 4); // I420的V分量
|
|
|
|
|
|
// NV21 YYYYYYYY VUVU
|
|
|
|
|
|
bufferY.put(data, 0, total); |
|
|
|
|
|
for (int i = total; i < data.length; i += 2) { |
|
|
|
|
|
bufferV.put(data[i]); |
|
|
|
|
|
bufferU.put(data[i + 1]); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return ret; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private byte[] nv12ToI420(byte[] data, int width, int height) { |
|
|
|
|
|
byte[] ret = new byte[width * height * 3 / 2]; |
|
|
|
|
|
int total = width * height; |
|
|
|
|
|
|
|
|
|
|
|
ByteBuffer bufferY = ByteBuffer.wrap(ret, 0, total); // I420的Y分量
|
|
|
|
|
|
ByteBuffer bufferU = ByteBuffer.wrap(ret, total, total / 4); // I420的U分量
|
|
|
|
|
|
ByteBuffer bufferV = ByteBuffer.wrap(ret, total + total / 4, total / 4); // I420的V分量
|
|
|
|
|
|
|
|
|
|
|
|
// NV12 YYYYYYYY UVUV
|
|
|
|
|
|
bufferY.put(data, 0, total); |
|
|
|
|
|
for (int i = total; i < data.length; i += 2) { |
|
|
|
|
|
bufferU.put(data[i]); |
|
|
|
|
|
bufferV.put(data[i + 1]); |
|
|
|
|
|
} |
|
|
|
|
|
return ret; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private byte[] nv12ToNv21(byte[] data, int width, int height) { |
|
|
|
|
|
byte[] ret = new byte[width * height * 3 / 2]; |
|
|
|
|
|
int total = width * height; |
|
|
|
|
|
|
|
|
|
|
|
ByteBuffer bufferY = ByteBuffer.wrap(ret, 0, total); // I420的Y分量
|
|
|
|
|
|
ByteBuffer bufferU = ByteBuffer.wrap(ret, total, total / 4); // I420的U分量
|
|
|
|
|
|
ByteBuffer bufferV = ByteBuffer.wrap(ret, total + total / 4, total / 4); // I420的V分量
|
|
|
|
|
|
|
|
|
|
|
|
// NV12 YYYYYYYY UVUV
|
|
|
|
|
|
bufferY.put(data, 0, total); |
|
|
|
|
|
for (int i = total; i < data.length; i += 2) { |
|
|
|
|
|
bufferU.put(data[i]); |
|
|
|
|
|
bufferV.put(data[i + 1]); |
|
|
|
|
|
} |
|
|
|
|
|
return ret; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// YYYYYYYY UVUV(nv21)--> YYYYYYYY VUVU(nv12)
|
|
|
|
|
|
private byte[] nv21ToNV12(byte[] nv21, int width, int height) { |
|
|
|
|
|
byte[] ret = new byte[width * height * 3 /2]; |
|
|
|
|
|
int framesize = width * height; |
|
|
|
|
|
int i = 0, j = 0; |
|
|
|
|
|
// 拷贝Y分量
|
|
|
|
|
|
System.arraycopy(nv21, 0,ret , 0, framesize); |
|
|
|
|
|
// 拷贝UV分量
|
|
|
|
|
|
for (j = framesize; j < nv21.length; j += 2) { |
|
|
|
|
|
ret[j+1] = nv21[j+1]; |
|
|
|
|
|
ret[j] = nv21[j]; |
|
|
|
|
|
} |
|
|
|
|
|
return ret; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// YYYYYYYY UVUV(nv12)--> YYYYYYYY VUVU(nv21)
|
|
|
|
|
|
private byte[] nv12ToNV21(byte[] nv12, int width, int height) { |
|
|
|
|
|
byte[] ret = new byte[width * height * 3 /2]; |
|
|
|
|
|
int framesize = width * height; |
|
|
|
|
|
int i = 0, j = 0; |
|
|
|
|
|
// 拷贝Y分量
|
|
|
|
|
|
System.arraycopy(nv12, 0,ret , 0, framesize); |
|
|
|
|
|
// 拷贝UV分量
|
|
|
|
|
|
for (j = framesize; j < nv12.length; j += 2) { |
|
|
|
|
|
ret[j] = nv12[j+1]; |
|
|
|
|
|
ret[j+1] = nv12[j]; |
|
|
|
|
|
} |
|
|
|
|
|
return ret; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|