You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
343 lines
12 KiB
Java
343 lines
12 KiB
Java
package org.easydarwin.push;
|
|
|
|
import android.content.Context;
|
|
import android.media.MediaCodec;
|
|
import android.media.MediaCodecInfo;
|
|
import android.media.MediaFormat;
|
|
import android.media.MediaRecorder;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.text.TextUtils;
|
|
import android.util.Log;
|
|
|
|
import com.common.commonlib.db.DBUtils;
|
|
import com.common.commonlib.db.entity.Video;
|
|
import com.yinuo.library.vlc.encoder.AndroidMuxer;
|
|
|
|
import org.easydarwin.PushHelper;
|
|
import org.easydarwin.muxer.EasyMuxer;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.nio.ByteBuffer;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.Locale;
|
|
|
|
import android.content.Context;
|
|
import android.os.storage.StorageManager;
|
|
import android.util.Log;
|
|
|
|
import java.io.File;
|
|
import java.lang.reflect.Method;
|
|
|
|
/**
|
|
* Created by apple on 2017/5/13.
|
|
*/
|
|
public class HWConsumer extends Thread implements VideoConsumer {
|
|
private static final String TAG = "HWConsumer";
|
|
private static final String MIME_TYPE = "video/hevc";
|
|
private static final long DEFAULT_RECORD_DURATION = 5 * 60 * 1000;;
|
|
private int mHeight;
|
|
private int mWidth;
|
|
private MediaCodec mMediaCodec;
|
|
private ByteBuffer[] inputBuffers;
|
|
private ByteBuffer[] outputBuffers;
|
|
private volatile boolean mVideoStarted;
|
|
private MediaFormat newFormat;
|
|
|
|
private AndroidMuxer androidMuxer;
|
|
private int mTrackIndex = -1;
|
|
private long mStartRecordTime = 0L;
|
|
private String mCurrentPath;
|
|
private Context context;
|
|
|
|
public HWConsumer(Context context) {
|
|
this.context = context;
|
|
}
|
|
|
|
@Override
|
|
public void onVideoStart(int width, int height) {
|
|
newFormat = null;
|
|
this.mWidth = width;
|
|
this.mHeight = height;
|
|
startMediaCodec();
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP + 1) {
|
|
inputBuffers = outputBuffers = null;
|
|
} else {
|
|
inputBuffers = mMediaCodec.getInputBuffers();
|
|
outputBuffers = mMediaCodec.getOutputBuffers();
|
|
}
|
|
androidMuxer = new AndroidMuxer();
|
|
start();
|
|
mVideoStarted = true;
|
|
}
|
|
|
|
/*private String getUsbStoragePath() {
|
|
String[] paths = new String[]{"/storage/78C8ED8FC8ED4BC6", "/storage/usb1"};
|
|
for (String path : paths) {
|
|
File file = new File(path);
|
|
if (file.exists() && file.isDirectory()) {
|
|
return path;
|
|
}
|
|
}
|
|
return null;
|
|
}*/
|
|
|
|
public void onUSBStart(String usbPath) {
|
|
Log.i("cyy", "usbPath = " + usbPath);
|
|
if (usbPath == null) {
|
|
//Toast.makeText(this, "USB设备未连接或不可用", Toast.LENGTH_SHORT).show();
|
|
return;
|
|
}
|
|
|
|
long time = System.currentTimeMillis();
|
|
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA);
|
|
String timeStamp = format.format(time);
|
|
String endTimeStamp = format.format(time + DEFAULT_RECORD_DURATION);
|
|
|
|
File usbStoragePath = new File(usbPath, "video");
|
|
if (!usbStoragePath.exists()) {
|
|
usbStoragePath.mkdirs();
|
|
}
|
|
|
|
File videoFile = new File(usbStoragePath, "VID_" + timeStamp + "_" + endTimeStamp + ".mp4");
|
|
|
|
mCurrentPath = videoFile.getAbsolutePath();
|
|
Log.d("mCurrentPathusbis",mCurrentPath);
|
|
MediaRecorder mediaRecorder = new MediaRecorder();
|
|
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
|
|
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
|
|
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
|
|
mediaRecorder.setOutputFile(videoFile.getAbsolutePath());
|
|
if (mStartRecordTime <= 0L) {
|
|
mStartRecordTime = System.currentTimeMillis();
|
|
}else{
|
|
long recordTime = System.currentTimeMillis() - mStartRecordTime;
|
|
if (recordTime > DEFAULT_RECORD_DURATION){
|
|
mStartRecordTime = 0L;
|
|
}
|
|
}
|
|
|
|
try {
|
|
mediaRecorder.prepare();
|
|
mediaRecorder.start();
|
|
//Toast.makeText(this, "正在录制视频...", Toast.LENGTH_SHORT).show();
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
//Toast.makeText(this, "录制失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
|
|
}
|
|
}
|
|
|
|
// final int millisPerframe = 1000 / 17;
|
|
// long lastPush = 0;
|
|
|
|
@Override
|
|
public int onVideo(byte[] nv12, int format) {
|
|
if (!mVideoStarted) return 0;
|
|
try {
|
|
byte[] data = nv12;
|
|
// if (lastPush == 0) {
|
|
// lastPush = System.currentTimeMillis();
|
|
// }
|
|
// long time = System.currentTimeMillis() - lastPush;
|
|
// if (time >= 0) {
|
|
// time = millisPerframe - time;
|
|
// if (time > 0) Thread.sleep(time / 2);
|
|
// }
|
|
|
|
|
|
int bufferIndex = mMediaCodec.dequeueInputBuffer(0);
|
|
if (bufferIndex >= 0) {
|
|
Log.d(TAG, "onVideo: " + bufferIndex);
|
|
ByteBuffer buffer = null;
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
buffer = mMediaCodec.getInputBuffer(bufferIndex);
|
|
} else {
|
|
buffer = inputBuffers[bufferIndex];
|
|
}
|
|
buffer.clear();
|
|
buffer.put(data);
|
|
buffer.clear();
|
|
mMediaCodec.queueInputBuffer(bufferIndex, 0, data.length, System.nanoTime() / 1000, MediaCodec.BUFFER_FLAG_KEY_FRAME);
|
|
}
|
|
// if (time > 0) Thread.sleep(time / 2);
|
|
// lastPush = System.currentTimeMillis();
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
@Override
|
|
public void run() {
|
|
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
|
int outputBufferIndex = 0;
|
|
int NAL_I = 19;
|
|
int NAL_VPS = 32;
|
|
byte[] vps_sps_pps_buf = null;
|
|
do {
|
|
outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 10000);
|
|
if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
|
|
// no output available yet
|
|
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
|
|
// not expected for an encoder
|
|
outputBuffers = mMediaCodec.getOutputBuffers();
|
|
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
|
|
synchronized (HWConsumer.this) {
|
|
newFormat = mMediaCodec.getOutputFormat();
|
|
if (androidMuxer != null) {
|
|
// should happen before receiving buffers, and should only happen once
|
|
mTrackIndex = androidMuxer.addTrack(newFormat);
|
|
}
|
|
}
|
|
} else if (outputBufferIndex < 0) {
|
|
// let's ignore it
|
|
} else {
|
|
ByteBuffer outputBuffer;
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
outputBuffer = mMediaCodec.getOutputBuffer(outputBufferIndex);
|
|
} else {
|
|
outputBuffer = outputBuffers[outputBufferIndex];
|
|
}
|
|
outputBuffer.position(bufferInfo.offset);
|
|
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
|
|
if (androidMuxer != null && mTrackIndex != -1) {
|
|
androidMuxer.writeSampleData(mTrackIndex, outputBuffer, bufferInfo);
|
|
}
|
|
outputBuffer.position(bufferInfo.offset);
|
|
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
|
|
byte[] outData = new byte[bufferInfo.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) {
|
|
if (vps_sps_pps_buf == null) {
|
|
vps_sps_pps_buf = outData;
|
|
}
|
|
} else if (type == NAL_I) {
|
|
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, bufferInfo.presentationTimeUs / 1000, 1);
|
|
mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
|
|
}
|
|
}
|
|
while (mVideoStarted);
|
|
}
|
|
|
|
@Override
|
|
public void onVideoStop() {
|
|
do {
|
|
newFormat = null;
|
|
mVideoStarted = false;
|
|
try {
|
|
join();
|
|
} catch (InterruptedException e) {
|
|
e.printStackTrace();
|
|
}
|
|
} while (isAlive());
|
|
if (androidMuxer != null) {
|
|
androidMuxer.release();
|
|
}
|
|
if (mMediaCodec != null) {
|
|
stopMediaCodec();
|
|
mMediaCodec = null;
|
|
}
|
|
}
|
|
|
|
private void insertToDB(long timeMillis, String filePath) {
|
|
String[] splits = filePath.split("/");
|
|
Video cacheVideo = new Video(timeMillis, splits[splits.length - 1], false, filePath);
|
|
DBUtils.INSTANCE.insertCacheVideo(cacheVideo);
|
|
}
|
|
|
|
public void onUSBStop() {
|
|
do {
|
|
newFormat = null;
|
|
mVideoStarted = false;
|
|
try {
|
|
join();
|
|
} catch (InterruptedException e) {
|
|
e.printStackTrace();
|
|
}
|
|
} while (isAlive());
|
|
if (mStartRecordTime > 0 && mStartRecordTime < System.currentTimeMillis() && !TextUtils.isEmpty(mCurrentPath)) {
|
|
Log.d("mCurrentPathiss",mCurrentPath);
|
|
insertToDB(mStartRecordTime, mCurrentPath);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public synchronized void setMuxer(EasyMuxer muxer) {
|
|
}
|
|
|
|
|
|
/**
|
|
* 初始化编码器
|
|
*/
|
|
private void startMediaCodec() {
|
|
/*
|
|
SD (Low quality) SD (High quality) HD 720p
|
|
1 HD 1080p
|
|
1
|
|
Video resolution 320 x 240 px 720 x 480 px 1280 x 720 px 1920 x 1080 px
|
|
Video frame rate 20 fps 30 fps 30 fps 30 fps
|
|
Video bitrate 384 Kbps 2 Mbps 4 Mbps 10 Mbps
|
|
*/
|
|
int framerate = 17;
|
|
// if (width == 640 || height == 640) {
|
|
// bitrate = 2000000;
|
|
// } else if (width == 1280 || height == 1280) {
|
|
// bitrate = 4000000;
|
|
// } else {
|
|
// bitrate = 2 * width * height;
|
|
// }
|
|
|
|
int bitrate = (int) (mWidth * mHeight * 20 * 2 * 0.05f);
|
|
|
|
if (mWidth >= 1920 || mHeight >= 1920)
|
|
bitrate *= 0.3;
|
|
else if (mWidth >= 1280 || mHeight >= 1280)
|
|
bitrate *= 0.4;
|
|
else if (mWidth >= 720 || mHeight >= 720)
|
|
bitrate *= 0.6;
|
|
|
|
try {
|
|
// mMediaCodec = MediaCodec.createByCodecName("0MX.rk.video_encoder.avc");
|
|
mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
throw new IllegalStateException(e);
|
|
}
|
|
|
|
MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
|
|
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate + 3000);
|
|
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, framerate);
|
|
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
|
|
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
|
|
mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
|
|
mMediaCodec.start();
|
|
|
|
Bundle params = new Bundle();
|
|
params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
|
mMediaCodec.setParameters(params);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 停止编码并释放编码资源占用
|
|
*/
|
|
private void stopMediaCodec() {
|
|
mMediaCodec.stop();
|
|
mMediaCodec.release();
|
|
}
|
|
}
|