|
|
|
@ -5,8 +5,10 @@ import android.media.MediaCodec;
|
|
|
|
|
import android.media.MediaCodecInfo;
|
|
|
|
|
import android.media.MediaFormat;
|
|
|
|
|
import android.os.Build;
|
|
|
|
|
import android.os.Bundle;
|
|
|
|
|
import android.os.Handler;
|
|
|
|
|
import android.os.HandlerThread;
|
|
|
|
|
import android.util.Log;
|
|
|
|
|
|
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
|
|
|
|
|
@ -15,7 +17,6 @@ import com.common.commonlib.utils.LogUtils;
|
|
|
|
|
import org.easydarwin.PushHelper;
|
|
|
|
|
|
|
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
|
import java.text.SimpleDateFormat;
|
|
|
|
|
import java.util.concurrent.ArrayBlockingQueue;
|
|
|
|
|
|
|
|
|
|
@TargetApi(Build.VERSION_CODES.M)
|
|
|
|
@ -42,9 +43,6 @@ public class MediaCodecManager {
|
|
|
|
|
|
|
|
|
|
private boolean isFlush = false;
|
|
|
|
|
|
|
|
|
|
private long lastPauseTime = -1;//上次暂停时间
|
|
|
|
|
private boolean isHasKeyFrame;
|
|
|
|
|
|
|
|
|
|
private Handler mHandler;
|
|
|
|
|
|
|
|
|
|
private int off_y = 50, off_x = 100;
|
|
|
|
@ -182,14 +180,14 @@ public class MediaCodecManager {
|
|
|
|
|
int videoH = rotation == 90 || rotation == 270 ? dstWidth : dstHeight;
|
|
|
|
|
mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE,//注意这里旋转后有一个大坑,就是要交换mHeight,和mWidth的位置。否则录制出来的视频时花屏的。
|
|
|
|
|
videoW, videoH);
|
|
|
|
|
int frameRate = 25; // 15fps
|
|
|
|
|
int frameRate = 24; // 15fps
|
|
|
|
|
int compressRatio = 256;
|
|
|
|
|
int bitRate = dstWidth * dstHeight * 3 * 8 * frameRate / compressRatio;
|
|
|
|
|
|
|
|
|
|
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
|
|
|
|
|
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
|
|
|
|
|
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, mColorFormat);
|
|
|
|
|
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
|
|
|
|
|
mediaFormat.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 5f);
|
|
|
|
|
LogUtils.w("prepare format: " + mediaFormat);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -197,7 +195,6 @@ public class MediaCodecManager {
|
|
|
|
|
public static final int NAL_VPS = 32;
|
|
|
|
|
|
|
|
|
|
private byte[] vps_sps_pps_buf;
|
|
|
|
|
byte[] outData;
|
|
|
|
|
|
|
|
|
|
private void start() {
|
|
|
|
|
if (!isInitCodec)
|
|
|
|
@ -244,45 +241,39 @@ public class MediaCodecManager {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index, @NonNull MediaCodec.BufferInfo info) {
|
|
|
|
|
buildKeyFrame();
|
|
|
|
|
ByteBuffer outputBuffer = codec.getOutputBuffer(index);
|
|
|
|
|
if (!isHasKeyFrame && info.flags == MediaCodec.BUFFER_FLAG_KEY_FRAME) {
|
|
|
|
|
isHasKeyFrame = true;
|
|
|
|
|
}
|
|
|
|
|
if (outData == null) {
|
|
|
|
|
outData = new byte[info.size];
|
|
|
|
|
}
|
|
|
|
|
if (outputBuffer == null)
|
|
|
|
|
if (outputBuffer == null) {
|
|
|
|
|
return;
|
|
|
|
|
else if (info.presentationTimeUs < lastPauseTime || !isHasKeyFrame) {//上一视频的数据,或者无关键帧,丢弃
|
|
|
|
|
//视频第一帧一定要是关键帧
|
|
|
|
|
outputBuffer.get(outData);
|
|
|
|
|
} else if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
|
|
|
|
|
LogUtils.w(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG");
|
|
|
|
|
} else {
|
|
|
|
|
outputBuffer.position(info.offset);
|
|
|
|
|
outputBuffer.limit(info.offset + info.size);
|
|
|
|
|
if (androidMuxer != null) {
|
|
|
|
|
if (androidMuxer != null && mTrackIndex != -1) {
|
|
|
|
|
androidMuxer.writeSampleData(mTrackIndex, outputBuffer, info);
|
|
|
|
|
}
|
|
|
|
|
byte[] outData = new byte[info.size];
|
|
|
|
|
outputBuffer.get(outData);
|
|
|
|
|
int offset = 4;
|
|
|
|
|
if (outData[2] == 0x01) {
|
|
|
|
|
offset = 3;
|
|
|
|
|
}
|
|
|
|
|
int type = (outData[offset] & 0x7E) >> 1;
|
|
|
|
|
Log.i("cyy", "type = " + type);
|
|
|
|
|
if (type == NAL_VPS) {
|
|
|
|
|
vps_sps_pps_buf = outData;
|
|
|
|
|
PushHelper.INSTANCE.pushData(outData, outData.length, info.presentationTimeUs / 1000);
|
|
|
|
|
} else if (type == NAL_I) {
|
|
|
|
|
if (vps_sps_pps_buf != null) {
|
|
|
|
|
} else {
|
|
|
|
|
if (type == NAL_I && vps_sps_pps_buf != null) {
|
|
|
|
|
byte[] newBuf = new byte[vps_sps_pps_buf.length + outData.length];
|
|
|
|
|
System.arraycopy(vps_sps_pps_buf, 0, newBuf, 0, vps_sps_pps_buf.length);
|
|
|
|
|
System.arraycopy(outData, 0, newBuf, vps_sps_pps_buf.length, outData.length);
|
|
|
|
|
outData = newBuf;
|
|
|
|
|
PushHelper.INSTANCE.pushData(outData, outData.length, info.presentationTimeUs / 1000);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
PushHelper.INSTANCE.pushData(outData, outData.length, info.presentationTimeUs / 1000);
|
|
|
|
|
codec.releaseOutputBuffer(index, false);
|
|
|
|
|
}
|
|
|
|
|
codec.releaseOutputBuffer(index, false);
|
|
|
|
|
try {
|
|
|
|
|
Thread.sleep(0);
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
@ -313,6 +304,17 @@ public class MediaCodecManager {
|
|
|
|
|
isStart = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long timeStamp = -1L;
|
|
|
|
|
|
|
|
|
|
void buildKeyFrame() {
|
|
|
|
|
if (System.currentTimeMillis() - timeStamp >= 5000) {//1000毫秒后,设置参数
|
|
|
|
|
timeStamp = System.currentTimeMillis();
|
|
|
|
|
Bundle params = new Bundle();
|
|
|
|
|
params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
|
|
|
|
|
mMediaCodec.setParameters(params);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 返回到接收数据状态
|
|
|
|
|
*/
|
|
|
|
@ -320,9 +322,9 @@ public class MediaCodecManager {
|
|
|
|
|
LogUtils.w(TAG, "flushMediaCodec");
|
|
|
|
|
frameBytes.clear();
|
|
|
|
|
isFlush = true;
|
|
|
|
|
lastPauseTime = (System.nanoTime()) / 1000;//记录
|
|
|
|
|
|
|
|
|
|
isHasKeyFrame = false;
|
|
|
|
|
// lastPauseTime = (System.nanoTime()) / 1000;//记录
|
|
|
|
|
//
|
|
|
|
|
// isHasKeyFrame = false;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -339,7 +341,7 @@ public class MediaCodecManager {
|
|
|
|
|
}
|
|
|
|
|
isStart = false;
|
|
|
|
|
isPause = true;
|
|
|
|
|
isHasKeyFrame = false;
|
|
|
|
|
// isHasKeyFrame = false;
|
|
|
|
|
LogUtils.w(TAG, "stopMediaCodec video");
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|