diff --git a/app/app.iml b/app/app.iml index dc9ae4e..e03e0af 100644 --- a/app/app.iml +++ b/app/app.iml @@ -82,13 +82,21 @@ + + + + + + + + diff --git a/app/src/main/java/com/jiangdg/usbcamera/view/USBCameraActivity.java b/app/src/main/java/com/jiangdg/usbcamera/view/USBCameraActivity.java index 5aa2b97..eed9bdd 100644 --- a/app/src/main/java/com/jiangdg/usbcamera/view/USBCameraActivity.java +++ b/app/src/main/java/com/jiangdg/usbcamera/view/USBCameraActivity.java @@ -18,6 +18,7 @@ import com.jiangdg.usbcamera.USBCameraManager; import com.serenegiant.usb.CameraDialog; import com.serenegiant.usb.USBMonitor; import com.serenegiant.usb.common.AbstractUVCCameraHandler; +import com.serenegiant.usb.encoder.RecordParams; import com.serenegiant.usb.widget.CameraViewInterface; import java.io.BufferedOutputStream; @@ -166,11 +167,13 @@ public class USBCameraActivity extends AppCompatActivity implements CameraDialog } if(! mUSBManager.isRecording()){ - String videoPath = USBCameraManager.ROOT_PATH+System.currentTimeMillis() - +USBCameraManager.SUFFIX_MP4; + String videoPath = USBCameraManager.ROOT_PATH+System.currentTimeMillis(); FileUtils.createfile(FileUtils.ROOT_PATH+"test666.h264"); - - mUSBManager.startRecording(videoPath, new AbstractUVCCameraHandler.OnEncodeResultListener() { + RecordParams params = new RecordParams(); + params.setRecordPath(videoPath); + params.setRecordDuration(0); // 设置为0,不分割保存 + params.setVoiceClose(false); // 不屏蔽声音 + mUSBManager.startRecording(params, new AbstractUVCCameraHandler.OnEncodeResultListener() { @Override public void onEncodeResult(byte[] data, int offset, int length, long timestamp, int type) { // type = 0,aac格式音频流 diff --git a/app/src/main/res/layout/activity_usbcamera.xml b/app/src/main/res/layout/activity_usbcamera.xml index 32f05b8..1873184 100644 --- a/app/src/main/res/layout/activity_usbcamera.xml +++ b/app/src/main/res/layout/activity_usbcamera.xml @@ -35,5 +35,4 @@ android:textSize="16sp" android:text="开始录制"/> - \ No newline at end of file diff --git a/libusbcamera/src/main/java/com/jiangdg/usbcamera/USBCameraManager.java b/libusbcamera/src/main/java/com/jiangdg/usbcamera/USBCameraManager.java index 3a28709..d0e131d 100644 --- a/libusbcamera/src/main/java/com/jiangdg/usbcamera/USBCameraManager.java +++ b/libusbcamera/src/main/java/com/jiangdg/usbcamera/USBCameraManager.java @@ -11,6 +11,7 @@ import com.serenegiant.usb.DeviceFilter; import com.serenegiant.usb.USBMonitor; import com.serenegiant.usb.common.AbstractUVCCameraHandler; import com.serenegiant.usb.common.UVCCameraHandler; +import com.serenegiant.usb.encoder.RecordParams; import com.serenegiant.usb.widget.CameraViewInterface; import java.io.File; @@ -182,12 +183,13 @@ public class USBCameraManager{ } } - public void startRecording(String videoPath, AbstractUVCCameraHandler.OnEncodeResultListener listener){ + public void startRecording(RecordParams params, AbstractUVCCameraHandler.OnEncodeResultListener listener){ if(mCameraHandler != null && ! isRecording()){ - mCameraHandler.startRecording(videoPath,listener); + mCameraHandler.startRecording(params,listener); } } + public void stopRecording(){ if(mCameraHandler != null && isRecording()){ mCameraHandler.stopRecording(); diff --git a/libusbcamera/src/main/java/com/serenegiant/usb/common/AbstractUVCCameraHandler.java b/libusbcamera/src/main/java/com/serenegiant/usb/common/AbstractUVCCameraHandler.java index cdb6f2d..3edfe46 100644 --- a/libusbcamera/src/main/java/com/serenegiant/usb/common/AbstractUVCCameraHandler.java +++ b/libusbcamera/src/main/java/com/serenegiant/usb/common/AbstractUVCCameraHandler.java @@ -26,6 +26,7 @@ import com.serenegiant.usb.encoder.MediaMuxerWrapper; import com.serenegiant.usb.encoder.MediaSurfaceEncoder; import com.serenegiant.usb.encoder.MediaVideoBufferEncoder; import com.serenegiant.usb.encoder.MediaVideoEncoder; +import com.serenegiant.usb.encoder.RecordParams; import com.serenegiant.usb.encoder.biz.AACEncodeConsumer; import com.serenegiant.usb.encoder.biz.H264EncodeConsumer; import com.serenegiant.usb.encoder.biz.Mp4MediaMuxer; @@ -79,6 +80,9 @@ public abstract class AbstractUVCCameraHandler extends Handler { private static final int MSG_CAPTURE_STOP = 6; private static final int MSG_MEDIA_UPDATE = 7; private static final int MSG_RELEASE = 9; + // 音频线程 +// private static final int MSG_AUDIO_START = 10; +// private static final int MSG_AUDIO_STOP = 11; private final WeakReference mWeakThread; private volatile boolean mReleased; @@ -112,6 +116,11 @@ public abstract class AbstractUVCCameraHandler extends Handler { return thread != null && thread.isRecording(); } +// public boolean isAudioThreadStart() { +// final CameraThread thread = mWeakThread.get(); +// return thread != null && thread.isAudioRecording(); +// } + public boolean isEqual(final UsbDevice device) { final CameraThread thread = mWeakThread.get(); return (thread != null) && thread.isEqual(device); @@ -198,11 +207,11 @@ public abstract class AbstractUVCCameraHandler extends Handler { } // 开始录制 - public void startRecording(final String path, OnEncodeResultListener listener) { + public void startRecording(final RecordParams params, OnEncodeResultListener listener) { AbstractUVCCameraHandler.mListener = listener; checkReleased(); // sendEmptyMessage(MSG_CAPTURE_START); - sendMessage(obtainMessage(MSG_CAPTURE_START, path)); + sendMessage(obtainMessage(MSG_CAPTURE_START, params)); } // 停止录制 @@ -210,6 +219,16 @@ public abstract class AbstractUVCCameraHandler extends Handler { sendEmptyMessage(MSG_CAPTURE_STOP); } +// // 启动音频线程 +// public void startAudioThread(){ +// sendEmptyMessage(MSG_AUDIO_START); +// } +// +// // 关闭音频线程 +// public void stopAudioThread(){ +// sendEmptyMessage(MSG_AUDIO_STOP); +// } + public void release() { mReleased = true; close(); @@ -297,35 +316,43 @@ public abstract class AbstractUVCCameraHandler extends Handler { final CameraThread thread = mWeakThread.get(); if (thread == null) return; switch (msg.what) { - case MSG_OPEN: - thread.handleOpen((USBMonitor.UsbControlBlock)msg.obj); - break; - case MSG_CLOSE: - thread.handleClose(); - break; - case MSG_PREVIEW_START: - thread.handleStartPreview(msg.obj); - break; - case MSG_PREVIEW_STOP: - thread.handleStopPreview(); - break; - case MSG_CAPTURE_STILL: - thread.handleCaptureStill((String)msg.obj); - break; - case MSG_CAPTURE_START: - thread.handleStartRecording((String)msg.obj); - break; - case MSG_CAPTURE_STOP: - thread.handleStopRecording(); - break; - case MSG_MEDIA_UPDATE: - thread.handleUpdateMedia((String)msg.obj); - break; - case MSG_RELEASE: - thread.handleRelease(); - break; - default: - throw new RuntimeException("unsupported message:what=" + msg.what); + case MSG_OPEN: + thread.handleOpen((USBMonitor.UsbControlBlock)msg.obj); + break; + case MSG_CLOSE: + thread.handleClose(); + break; + case MSG_PREVIEW_START: + thread.handleStartPreview(msg.obj); + break; + case MSG_PREVIEW_STOP: + thread.handleStopPreview(); + break; + case MSG_CAPTURE_STILL: + thread.handleCaptureStill((String)msg.obj); + break; + case MSG_CAPTURE_START: +// thread.handleStartRecording((String)msg.obj); + thread.handleStartRecording((RecordParams)msg.obj); + break; + case MSG_CAPTURE_STOP: + thread.handleStopRecording(); + break; + case MSG_MEDIA_UPDATE: + thread.handleUpdateMedia((String)msg.obj); + break; + case MSG_RELEASE: + thread.handleRelease(); + break; + // 音频线程 +// case MSG_AUDIO_START: +// thread.startAudioRecord(); +// break; +// case MSG_AUDIO_STOP: +// thread.stopAudioRecord(); +// break; + default: + throw new RuntimeException("unsupported message:what=" + msg.what); } } @@ -352,6 +379,7 @@ public abstract class AbstractUVCCameraHandler extends Handler { // private MediaMuxerWrapper mMuxer; private MediaVideoBufferEncoder mVideoEncoder; private Mp4MediaMuxer mMuxer; +// private boolean isAudioThreadStart; /** 构造方法 * @@ -429,6 +457,12 @@ public abstract class AbstractUVCCameraHandler extends Handler { } } +// public boolean isAudioRecording(){ +// synchronized (mSync){ +// return isAudioThreadStart; +// } +// } + public boolean isEqual(final UsbDevice device) { return (mUVCCamera != null) && (mUVCCamera.getDevice() != null) && mUVCCamera.getDevice().equals(device); } @@ -583,12 +617,48 @@ public abstract class AbstractUVCCameraHandler extends Handler { private AACEncodeConsumer mAacConsumer; private H264EncodeConsumer mH264Consumer; - public void handleStartRecording(String path){ + public void handleStartRecording(RecordParams params){ if ((mUVCCamera == null) || (mMuxer != null)) return; + if (params == null) + throw new NullPointerException("RecordParams can not be null!"); // 获取USB Camera预览数据 mUVCCamera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_NV21); - // 启动视频编码线程,type=1 + // 初始化混合器 + mMuxer = new Mp4MediaMuxer(params.getRecordPath(), + params.getRecordDuration() * 60 * 1000); + + // 启动视频编码线程 + startVideoRecord(); + // 启动音频编码线程 + if(! params.isVoiceClose()) { + startAudioRecord(); + } + callOnStartRecording(); + } + + + public void handleStopRecording(){ + // 停止混合器 + if (mMuxer != null){ + mMuxer.release(); + mMuxer = null; + Log.i(TAG,TAG+"---->停止本地录制"); + } + // 停止音视频编码线程 + stopAudioRecord(); + stopVideoRecord(); + // 停止捕获视频数据 + if (mUVCCamera != null) { + mUVCCamera.stopCapture(); + mUVCCamera.setFrameCallback(null, 0); + } + mWeakCameraView.get().setVideoEncoder(null); + // you should not wait here + callOnStopRecording(); + } + + private void startVideoRecord() { mH264Consumer = new H264EncodeConsumer(); mH264Consumer.setOnH264EncodeResultListener(new H264EncodeConsumer.OnH264EncodeResultListener() { @Override @@ -599,45 +669,16 @@ public abstract class AbstractUVCCameraHandler extends Handler { } }); mH264Consumer.start(); - // 启动音频编码线程,type=0 - mAacConsumer = new AACEncodeConsumer(); - mAacConsumer.setOnAACEncodeResultListener(new AACEncodeConsumer.OnAACEncodeResultListener() { - @Override - public void onEncodeResult(byte[] data, int offset, int length, long timestamp) { - if(mListener != null){ - mListener.onEncodeResult(data,offset,length,timestamp,0); - } - } - }); - mAacConsumer.start(); - // 启动混合器 - long millis = 30 * 60 * 1000; - mMuxer = new Mp4MediaMuxer(new File(FileUtils.ROOT_PATH, new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss").format(new Date())).toString(), millis); + // 添加混合器 if(mH264Consumer != null){ mH264Consumer.setTmpuMuxer(mMuxer); } - if(mAacConsumer != null){ - mAacConsumer.setTmpuMuxer(mMuxer); - } - callOnStartRecording(); } - public void handleStopRecording(){ - // 停止混合器 - if (mMuxer != null){ - mMuxer.release(); - mMuxer = null; - Log.i(TAG,TAG+"---->停止本地录制"); - } - // 停止音视频编码线程 - if(mH264Consumer != null){ - mH264Consumer.setTmpuMuxer(null); - } - if(mAacConsumer != null){ - mAacConsumer.setTmpuMuxer(null); - } + private void stopVideoRecord(){ if(mH264Consumer != null){ mH264Consumer.exit(); + mH264Consumer.setTmpuMuxer(null); try { Thread t2 = mH264Consumer; mH264Consumer = null; @@ -649,8 +690,30 @@ public abstract class AbstractUVCCameraHandler extends Handler { e.printStackTrace(); } } + } + + private void startAudioRecord(){ + mAacConsumer = new AACEncodeConsumer(); + mAacConsumer.setOnAACEncodeResultListener(new AACEncodeConsumer.OnAACEncodeResultListener() { + @Override + public void onEncodeResult(byte[] data, int offset, int length, long timestamp) { + if(mListener != null){ + mListener.onEncodeResult(data,offset,length,timestamp,0); + } + } + }); + mAacConsumer.start(); + // 添加混合器 + if(mAacConsumer != null){ + mAacConsumer.setTmpuMuxer(mMuxer); + } +// isAudioThreadStart = true; + } + + private void stopAudioRecord(){ if(mAacConsumer != null){ mAacConsumer.exit(); + mAacConsumer.setTmpuMuxer(null); try { Thread t1 = mAacConsumer; mAacConsumer = null; @@ -662,14 +725,8 @@ public abstract class AbstractUVCCameraHandler extends Handler { e.printStackTrace(); } } - // 停止捕获视频数据 - if (mUVCCamera != null) { - mUVCCamera.stopCapture(); - mUVCCamera.setFrameCallback(null, 0); - } - mWeakCameraView.get().setVideoEncoder(null); - // you should not wait here - callOnStopRecording(); + +// isAudioThreadStart = false; } // 停止录制视频 diff --git a/libusbcamera/src/main/java/com/serenegiant/usb/encoder/RecordParams.java b/libusbcamera/src/main/java/com/serenegiant/usb/encoder/RecordParams.java new file mode 100644 index 0000000..361f1fd --- /dev/null +++ b/libusbcamera/src/main/java/com/serenegiant/usb/encoder/RecordParams.java @@ -0,0 +1,36 @@ +package com.serenegiant.usb.encoder; + +/** 录制参数 + * + * Created by jiangdongguo on 2017/10/19. + */ + +public class RecordParams { + private String recordPath; + private int recordDuration; + private boolean voiceClose; + + public boolean isVoiceClose() { + return voiceClose; + } + + public void setVoiceClose(boolean voiceClose) { + this.voiceClose = voiceClose; + } + + public String getRecordPath() { + return recordPath; + } + + public void setRecordPath(String recordPath) { + this.recordPath = recordPath; + } + + public int getRecordDuration() { + return recordDuration; + } + + public void setRecordDuration(int recordDuration) { + this.recordDuration = recordDuration; + } +} diff --git a/libusbcamera/src/main/java/com/serenegiant/usb/encoder/biz/Mp4MediaMuxer.java b/libusbcamera/src/main/java/com/serenegiant/usb/encoder/biz/Mp4MediaMuxer.java index e964530..e08b7f0 100644 --- a/libusbcamera/src/main/java/com/serenegiant/usb/encoder/biz/Mp4MediaMuxer.java +++ b/libusbcamera/src/main/java/com/serenegiant/usb/encoder/biz/Mp4MediaMuxer.java @@ -44,6 +44,8 @@ public class Mp4MediaMuxer { } } + + public synchronized void addTrack(MediaFormat format, boolean isVideo) { // now that we have the Magic Goodies, start the muxer if (mAudioTrackIndex != -1 && mVideoTrackIndex != -1) @@ -102,7 +104,7 @@ public class Mp4MediaMuxer { // Log.i(TAG, "BUFFER_FLAG_END_OF_STREAM received"); } - if (System.currentTimeMillis() - mBeginMillis >= durationMillis) { + if (durationMillis!=0 && System.currentTimeMillis() - mBeginMillis >= durationMillis) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { // if (VERBOSE) // Log.i(TAG, String.format("record file reach expiration.create new file:" + index));