|
|
@ -2,13 +2,16 @@ package com.yinuo.library.vlc.encoder;
|
|
|
|
|
|
|
|
|
|
|
|
import android.media.MediaCodec;
|
|
|
|
import android.media.MediaCodec;
|
|
|
|
import android.media.MediaFormat;
|
|
|
|
import android.media.MediaFormat;
|
|
|
|
|
|
|
|
import android.os.Build;
|
|
|
|
|
|
|
|
import android.os.Bundle;
|
|
|
|
import android.util.Log;
|
|
|
|
import android.util.Log;
|
|
|
|
|
|
|
|
|
|
|
|
import com.yinuo.library.vlc.PushHelper;
|
|
|
|
import com.yinuo.library.vlc.PushHelper;
|
|
|
|
import com.yinuo.library.vlc.utils.LogUtils;
|
|
|
|
import com.yinuo.library.vlc.utils.LogUtils;
|
|
|
|
|
|
|
|
|
|
|
|
import org.easydarwin.easypusher.BuildConfig;
|
|
|
|
import java.io.FileNotFoundException;
|
|
|
|
|
|
|
|
import java.io.FileOutputStream;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -51,9 +54,12 @@ public abstract class MediaEncoderCore {
|
|
|
|
* We're just using the muxer to get a .mp4 file (instead of a raw H.264 stream). We're
|
|
|
|
* We're just using the muxer to get a .mp4 file (instead of a raw H.264 stream). We're
|
|
|
|
* not recording audio.
|
|
|
|
* not recording audio.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
long timeStamp;
|
|
|
|
|
|
|
|
|
|
|
|
public void drainEncoder(boolean endOfStream) {
|
|
|
|
public void drainEncoder(boolean endOfStream) {
|
|
|
|
// LogUtils.v(String.format("%s drainEncoder: end = %b", getClass().getSimpleName(), endOfStream));
|
|
|
|
// LogUtils.v(String.format("%s drainEncoder: end = %b", getClass().getSimpleName(), endOfStream));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
buildKeyFrame();
|
|
|
|
final int TIMEOUT_USEC = 10000;
|
|
|
|
final int TIMEOUT_USEC = 10000;
|
|
|
|
if (VERBOSE) Log.d(TAG, "drainEncoder(" + endOfStream + ")");
|
|
|
|
if (VERBOSE) Log.d(TAG, "drainEncoder(" + endOfStream + ")");
|
|
|
|
|
|
|
|
|
|
|
@ -61,15 +67,13 @@ public abstract class MediaEncoderCore {
|
|
|
|
if (VERBOSE) Log.d(TAG, "sending EOS to encoder");
|
|
|
|
if (VERBOSE) Log.d(TAG, "sending EOS to encoder");
|
|
|
|
mEncoder.signalEndOfInputStream();
|
|
|
|
mEncoder.signalEndOfInputStream();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
byte[] mSpsPps = new byte[0];
|
|
|
|
ByteBuffer[] encoderOutputBuffers = mEncoder.getOutputBuffers();
|
|
|
|
ByteBuffer[] encoderOutputBuffers = mEncoder.getOutputBuffers();
|
|
|
|
while (true) {
|
|
|
|
|
|
|
|
int encoderStatus = mEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);
|
|
|
|
int encoderStatus = mEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);
|
|
|
|
|
|
|
|
|
|
|
|
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
|
|
|
|
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
|
|
|
|
// no output available yet
|
|
|
|
// no output available yet
|
|
|
|
if (!endOfStream) {
|
|
|
|
if (!endOfStream) {
|
|
|
|
break; // out of while
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
if (VERBOSE) Log.d(TAG, "no output available, spinning to await EOS");
|
|
|
|
if (VERBOSE) Log.d(TAG, "no output available, spinning to await EOS");
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -115,38 +119,26 @@ public abstract class MediaEncoderCore {
|
|
|
|
Log.d(TAG, "sent " + mBufferInfo.size + " bytes to muxer, ts=" +
|
|
|
|
Log.d(TAG, "sent " + mBufferInfo.size + " bytes to muxer, ts=" +
|
|
|
|
mBufferInfo.presentationTimeUs);
|
|
|
|
mBufferInfo.presentationTimeUs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// add
|
|
|
|
byte[] outData = new byte[mBufferInfo.size];
|
|
|
|
boolean sync = false;
|
|
|
|
//从buff中读取数据到outData中
|
|
|
|
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {// sps
|
|
|
|
encodedData.get(outData);
|
|
|
|
sync = (mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
|
|
|
|
//记录pps和sps,pps和sps数据开头是0x00 0x00 0x00 0x01 0x67,
|
|
|
|
if (!sync) {
|
|
|
|
// 0x67对应十进制103
|
|
|
|
byte[] temp = new byte[mBufferInfo.size];
|
|
|
|
if (outData[0] == 0 && outData[1] == 0 && outData[2] == 0
|
|
|
|
encodedData.get(temp);
|
|
|
|
&& outData[3] == 1 && outData[4] == 103) {
|
|
|
|
PushHelper.mPpsSps = temp;
|
|
|
|
mSpsPps = outData;
|
|
|
|
mEncoder.releaseOutputBuffer(encoderStatus, false);
|
|
|
|
} else if (outData[0] == 0 && outData[1] == 0 && outData[2] == 0
|
|
|
|
continue;
|
|
|
|
&& outData[3] == 1 && outData[4] == 101) {
|
|
|
|
} else {
|
|
|
|
//关键帧开始规则是0x00 0x00 0x00 0x01 0x65,0x65对应十进制101
|
|
|
|
PushHelper.mPpsSps = new byte[0];
|
|
|
|
//在关键帧前面加上pps和sps数据
|
|
|
|
}
|
|
|
|
byte[] iframeData = new byte[mSpsPps.length + outData.length];
|
|
|
|
|
|
|
|
System.arraycopy(mSpsPps, 0, iframeData, 0, mSpsPps.length);
|
|
|
|
|
|
|
|
System.arraycopy(outData, 0, iframeData, mSpsPps.length, outData.length);
|
|
|
|
|
|
|
|
outData = iframeData;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sync |= (mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
|
|
|
|
//save(outData, 0, outData.length, Environment.getExternalStorageDirectory() + "/easy.h264", true);
|
|
|
|
int len = PushHelper.mPpsSps.length + mBufferInfo.size;
|
|
|
|
PushHelper.INSTANCE.pushData(outData, outData.length, mBufferInfo.presentationTimeUs / 1000);
|
|
|
|
if (len > PushHelper.h264.length) {
|
|
|
|
|
|
|
|
PushHelper.h264 = new byte[len];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sync) {
|
|
|
|
|
|
|
|
System.arraycopy(PushHelper.mPpsSps, 0, PushHelper.h264, 0, PushHelper.mPpsSps.length);
|
|
|
|
|
|
|
|
encodedData.get(PushHelper.h264, PushHelper.mPpsSps.length, mBufferInfo.size);
|
|
|
|
|
|
|
|
PushHelper.INSTANCE.pushData(PushHelper.h264, PushHelper.mPpsSps.length + mBufferInfo.size, mBufferInfo.presentationTimeUs / 1000);
|
|
|
|
|
|
|
|
if (BuildConfig.DEBUG)
|
|
|
|
|
|
|
|
Log.i(TAG, String.format("push i video stamp:%d", mBufferInfo.presentationTimeUs / 1000));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
encodedData.get(PushHelper.h264, 0, mBufferInfo.size);
|
|
|
|
|
|
|
|
PushHelper.INSTANCE.pushData(PushHelper.h264, mBufferInfo.size, mBufferInfo.presentationTimeUs / 1000);
|
|
|
|
|
|
|
|
if (BuildConfig.DEBUG)
|
|
|
|
|
|
|
|
Log.i(TAG, String.format("push video stamp:%d", mBufferInfo.presentationTimeUs / 1000));
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
mEncoder.releaseOutputBuffer(encoderStatus, false);
|
|
|
|
mEncoder.releaseOutputBuffer(encoderStatus, false);
|
|
|
@ -157,10 +149,19 @@ public abstract class MediaEncoderCore {
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
if (VERBOSE) Log.d(TAG, "end of stream reached");
|
|
|
|
if (VERBOSE) Log.d(TAG, "end of stream reached");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break; // out of while
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void buildKeyFrame() {
|
|
|
|
|
|
|
|
if (System.currentTimeMillis() - timeStamp >= 1000) {//1000毫秒后,设置参数
|
|
|
|
|
|
|
|
timeStamp = System.currentTimeMillis();
|
|
|
|
|
|
|
|
if (Build.VERSION.SDK_INT >= 23) {
|
|
|
|
|
|
|
|
Bundle params = new Bundle();
|
|
|
|
|
|
|
|
params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
|
|
|
|
|
|
|
|
mEncoder.setParameters(params);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void release() {
|
|
|
|
public void release() {
|
|
|
@ -179,4 +180,35 @@ public abstract class MediaEncoderCore {
|
|
|
|
|
|
|
|
|
|
|
|
protected abstract boolean isSurfaceInput();
|
|
|
|
protected abstract boolean isSurfaceInput();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 保存数据到本地
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param buffer 要保存的数据
|
|
|
|
|
|
|
|
* @param offset 要保存数据的起始位置
|
|
|
|
|
|
|
|
* @param length 要保存数据长度
|
|
|
|
|
|
|
|
* @param path 保存路径
|
|
|
|
|
|
|
|
* @param append 是否追加
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public static void save(byte[] buffer, int offset, int length, String path, boolean append) {
|
|
|
|
|
|
|
|
FileOutputStream fos = null;
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
fos = new FileOutputStream(path, append);
|
|
|
|
|
|
|
|
fos.write(buffer, offset, length);
|
|
|
|
|
|
|
|
} catch (FileNotFoundException e) {
|
|
|
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
|
|
if (fos != null) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
fos.flush();
|
|
|
|
|
|
|
|
fos.close();
|
|
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|