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

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();
}
}