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