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