Browse Source

新增mp4文件自动保存,屏蔽声音功能

main
jiangdongguo 7 years ago
parent
commit
1a05fa7402
  1. 8
      app/app.iml
  2. 11
      app/src/main/java/com/jiangdg/usbcamera/view/USBCameraActivity.java
  3. 1
      app/src/main/res/layout/activity_usbcamera.xml
  4. 6
      libusbcamera/src/main/java/com/jiangdg/usbcamera/USBCameraManager.java
  5. 203
      libusbcamera/src/main/java/com/serenegiant/usb/common/AbstractUVCCameraHandler.java
  6. 36
      libusbcamera/src/main/java/com/serenegiant/usb/encoder/RecordParams.java
  7. 4
      libusbcamera/src/main/java/com/serenegiant/usb/encoder/biz/Mp4MediaMuxer.java

8
app/app.iml

@ -82,13 +82,21 @@
<sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" /> <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content> </content>
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" /> <orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />

11
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.CameraDialog;
import com.serenegiant.usb.USBMonitor; import com.serenegiant.usb.USBMonitor;
import com.serenegiant.usb.common.AbstractUVCCameraHandler; import com.serenegiant.usb.common.AbstractUVCCameraHandler;
import com.serenegiant.usb.encoder.RecordParams;
import com.serenegiant.usb.widget.CameraViewInterface; import com.serenegiant.usb.widget.CameraViewInterface;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
@ -166,11 +167,13 @@ public class USBCameraActivity extends AppCompatActivity implements CameraDialog
} }
if(! mUSBManager.isRecording()){ if(! mUSBManager.isRecording()){
String videoPath = USBCameraManager.ROOT_PATH+System.currentTimeMillis() String videoPath = USBCameraManager.ROOT_PATH+System.currentTimeMillis();
+USBCameraManager.SUFFIX_MP4;
FileUtils.createfile(FileUtils.ROOT_PATH+"test666.h264"); FileUtils.createfile(FileUtils.ROOT_PATH+"test666.h264");
RecordParams params = new RecordParams();
mUSBManager.startRecording(videoPath, new AbstractUVCCameraHandler.OnEncodeResultListener() { params.setRecordPath(videoPath);
params.setRecordDuration(0); // 设置为0,不分割保存
params.setVoiceClose(false); // 不屏蔽声音
mUSBManager.startRecording(params, new AbstractUVCCameraHandler.OnEncodeResultListener() {
@Override @Override
public void onEncodeResult(byte[] data, int offset, int length, long timestamp, int type) { public void onEncodeResult(byte[] data, int offset, int length, long timestamp, int type) {
// type = 0,aac格式音频流 // type = 0,aac格式音频流

1
app/src/main/res/layout/activity_usbcamera.xml

@ -35,5 +35,4 @@
android:textSize="16sp" android:textSize="16sp"
android:text="开始录制"/> android:text="开始录制"/>
</RelativeLayout> </RelativeLayout>

6
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.USBMonitor;
import com.serenegiant.usb.common.AbstractUVCCameraHandler; import com.serenegiant.usb.common.AbstractUVCCameraHandler;
import com.serenegiant.usb.common.UVCCameraHandler; import com.serenegiant.usb.common.UVCCameraHandler;
import com.serenegiant.usb.encoder.RecordParams;
import com.serenegiant.usb.widget.CameraViewInterface; import com.serenegiant.usb.widget.CameraViewInterface;
import java.io.File; 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()){ if(mCameraHandler != null && ! isRecording()){
mCameraHandler.startRecording(videoPath,listener); mCameraHandler.startRecording(params,listener);
} }
} }
public void stopRecording(){ public void stopRecording(){
if(mCameraHandler != null && isRecording()){ if(mCameraHandler != null && isRecording()){
mCameraHandler.stopRecording(); mCameraHandler.stopRecording();

203
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.MediaSurfaceEncoder;
import com.serenegiant.usb.encoder.MediaVideoBufferEncoder; import com.serenegiant.usb.encoder.MediaVideoBufferEncoder;
import com.serenegiant.usb.encoder.MediaVideoEncoder; 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.AACEncodeConsumer;
import com.serenegiant.usb.encoder.biz.H264EncodeConsumer; import com.serenegiant.usb.encoder.biz.H264EncodeConsumer;
import com.serenegiant.usb.encoder.biz.Mp4MediaMuxer; 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_CAPTURE_STOP = 6;
private static final int MSG_MEDIA_UPDATE = 7; private static final int MSG_MEDIA_UPDATE = 7;
private static final int MSG_RELEASE = 9; 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<CameraThread> mWeakThread; private final WeakReference<CameraThread> mWeakThread;
private volatile boolean mReleased; private volatile boolean mReleased;
@ -112,6 +116,11 @@ public abstract class AbstractUVCCameraHandler extends Handler {
return thread != null && thread.isRecording(); return thread != null && thread.isRecording();
} }
// public boolean isAudioThreadStart() {
// final CameraThread thread = mWeakThread.get();
// return thread != null && thread.isAudioRecording();
// }
public boolean isEqual(final UsbDevice device) { public boolean isEqual(final UsbDevice device) {
final CameraThread thread = mWeakThread.get(); final CameraThread thread = mWeakThread.get();
return (thread != null) && thread.isEqual(device); 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; AbstractUVCCameraHandler.mListener = listener;
checkReleased(); checkReleased();
// sendEmptyMessage(MSG_CAPTURE_START); // 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); sendEmptyMessage(MSG_CAPTURE_STOP);
} }
// // 启动音频线程
// public void startAudioThread(){
// sendEmptyMessage(MSG_AUDIO_START);
// }
//
// // 关闭音频线程
// public void stopAudioThread(){
// sendEmptyMessage(MSG_AUDIO_STOP);
// }
public void release() { public void release() {
mReleased = true; mReleased = true;
close(); close();
@ -297,35 +316,43 @@ public abstract class AbstractUVCCameraHandler extends Handler {
final CameraThread thread = mWeakThread.get(); final CameraThread thread = mWeakThread.get();
if (thread == null) return; if (thread == null) return;
switch (msg.what) { switch (msg.what) {
case MSG_OPEN: case MSG_OPEN:
thread.handleOpen((USBMonitor.UsbControlBlock)msg.obj); thread.handleOpen((USBMonitor.UsbControlBlock)msg.obj);
break; break;
case MSG_CLOSE: case MSG_CLOSE:
thread.handleClose(); thread.handleClose();
break; break;
case MSG_PREVIEW_START: case MSG_PREVIEW_START:
thread.handleStartPreview(msg.obj); thread.handleStartPreview(msg.obj);
break; break;
case MSG_PREVIEW_STOP: case MSG_PREVIEW_STOP:
thread.handleStopPreview(); thread.handleStopPreview();
break; break;
case MSG_CAPTURE_STILL: case MSG_CAPTURE_STILL:
thread.handleCaptureStill((String)msg.obj); thread.handleCaptureStill((String)msg.obj);
break; break;
case MSG_CAPTURE_START: case MSG_CAPTURE_START:
thread.handleStartRecording((String)msg.obj); // thread.handleStartRecording((String)msg.obj);
break; thread.handleStartRecording((RecordParams)msg.obj);
case MSG_CAPTURE_STOP: break;
thread.handleStopRecording(); case MSG_CAPTURE_STOP:
break; thread.handleStopRecording();
case MSG_MEDIA_UPDATE: break;
thread.handleUpdateMedia((String)msg.obj); case MSG_MEDIA_UPDATE:
break; thread.handleUpdateMedia((String)msg.obj);
case MSG_RELEASE: break;
thread.handleRelease(); case MSG_RELEASE:
break; thread.handleRelease();
default: break;
throw new RuntimeException("unsupported message:what=" + msg.what); // 音频线程
// 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 MediaMuxerWrapper mMuxer;
private MediaVideoBufferEncoder mVideoEncoder; private MediaVideoBufferEncoder mVideoEncoder;
private Mp4MediaMuxer mMuxer; 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) { public boolean isEqual(final UsbDevice device) {
return (mUVCCamera != null) && (mUVCCamera.getDevice() != null) && mUVCCamera.getDevice().equals(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 AACEncodeConsumer mAacConsumer;
private H264EncodeConsumer mH264Consumer; private H264EncodeConsumer mH264Consumer;
public void handleStartRecording(String path){ public void handleStartRecording(RecordParams params){
if ((mUVCCamera == null) || (mMuxer != null)) if ((mUVCCamera == null) || (mMuxer != null))
return; return;
if (params == null)
throw new NullPointerException("RecordParams can not be null!");
// 获取USB Camera预览数据 // 获取USB Camera预览数据
mUVCCamera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_NV21); 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 = new H264EncodeConsumer();
mH264Consumer.setOnH264EncodeResultListener(new H264EncodeConsumer.OnH264EncodeResultListener() { mH264Consumer.setOnH264EncodeResultListener(new H264EncodeConsumer.OnH264EncodeResultListener() {
@Override @Override
@ -599,45 +669,16 @@ public abstract class AbstractUVCCameraHandler extends Handler {
} }
}); });
mH264Consumer.start(); 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){ if(mH264Consumer != null){
mH264Consumer.setTmpuMuxer(mMuxer); mH264Consumer.setTmpuMuxer(mMuxer);
} }
if(mAacConsumer != null){
mAacConsumer.setTmpuMuxer(mMuxer);
}
callOnStartRecording();
} }
public void handleStopRecording(){ private void stopVideoRecord(){
// 停止混合器
if (mMuxer != null){
mMuxer.release();
mMuxer = null;
Log.i(TAG,TAG+"---->停止本地录制");
}
// 停止音视频编码线程
if(mH264Consumer != null){
mH264Consumer.setTmpuMuxer(null);
}
if(mAacConsumer != null){
mAacConsumer.setTmpuMuxer(null);
}
if(mH264Consumer != null){ if(mH264Consumer != null){
mH264Consumer.exit(); mH264Consumer.exit();
mH264Consumer.setTmpuMuxer(null);
try { try {
Thread t2 = mH264Consumer; Thread t2 = mH264Consumer;
mH264Consumer = null; mH264Consumer = null;
@ -649,8 +690,30 @@ public abstract class AbstractUVCCameraHandler extends Handler {
e.printStackTrace(); 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){ if(mAacConsumer != null){
mAacConsumer.exit(); mAacConsumer.exit();
mAacConsumer.setTmpuMuxer(null);
try { try {
Thread t1 = mAacConsumer; Thread t1 = mAacConsumer;
mAacConsumer = null; mAacConsumer = null;
@ -662,14 +725,8 @@ public abstract class AbstractUVCCameraHandler extends Handler {
e.printStackTrace(); e.printStackTrace();
} }
} }
// 停止捕获视频数据
if (mUVCCamera != null) { // isAudioThreadStart = false;
mUVCCamera.stopCapture();
mUVCCamera.setFrameCallback(null, 0);
}
mWeakCameraView.get().setVideoEncoder(null);
// you should not wait here
callOnStopRecording();
} }
// 停止录制视频 // 停止录制视频

36
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;
}
}

4
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) { public synchronized void addTrack(MediaFormat format, boolean isVideo) {
// now that we have the Magic Goodies, start the muxer // now that we have the Magic Goodies, start the muxer
if (mAudioTrackIndex != -1 && mVideoTrackIndex != -1) if (mAudioTrackIndex != -1 && mVideoTrackIndex != -1)
@ -102,7 +104,7 @@ public class Mp4MediaMuxer {
// Log.i(TAG, "BUFFER_FLAG_END_OF_STREAM received"); // 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 (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
// if (VERBOSE) // if (VERBOSE)
// Log.i(TAG, String.format("record file reach expiration.create new file:" + index)); // Log.i(TAG, String.format("record file reach expiration.create new file:" + index));

Loading…
Cancel
Save