+ * clazz 继承于AbstractUVCCameraHandler
+ * parent Activity子类
+ * cameraView 用于捕获静止图像
+ * encoderType 0表示使用MediaSurfaceEncoder;1表示使用MediaVideoEncoder, 2表示使用MediaVideoBufferEncoder
+ * width 分辨率的宽
+ * height 分辨率的高
+ * format 颜色格式,0为FRAME_FORMAT_YUYV;1为FRAME_FORMAT_MJPEG
+ * bandwidthFactor
+ */
+ CameraThread(final Class extends AbstractUVCCameraHandler> clazz,
+ final Activity parent, final CameraViewInterface cameraView,
+ final int encoderType, final int width, final int height, final int format,
+ final float bandwidthFactor) {
+
+ super("CameraThread");
+ mHandlerClass = clazz;
+ mEncoderType = encoderType;
+ mWidth = width;
+ mHeight = height;
+ mPreviewMode = format;
+ mBandwidthFactor = bandwidthFactor;
+ mWeakParent = new WeakReference<>(parent);
+ mWeakCameraView = new WeakReference<>(cameraView);
// loadShutterSound(parent);
- }
-
- @Override
- protected void finalize() throws Throwable {
- Log.i(TAG, "CameraThread#finalize");
- super.finalize();
- }
-
- public AbstractUVCCameraHandler getHandler() {
- if (DEBUG) Log.v(TAG_THREAD, "getHandler:");
- synchronized (mSync) {
- if (mHandler == null)
- try {
- mSync.wait();
- } catch (final InterruptedException e) {
- }
- }
- return mHandler;
- }
-
- public int getWidth() {
- synchronized (mSync) {
- return mWidth;
- }
- }
-
- public int getHeight() {
- synchronized (mSync) {
- return mHeight;
- }
- }
-
- public boolean isCameraOpened() {
- synchronized (mSync) {
- return mUVCCamera != null;
- }
- }
-
- public boolean isPreviewing() {
- synchronized (mSync) {
- return mUVCCamera != null && mIsPreviewing;
- }
- }
-
- public boolean isRecording() {
- synchronized (mSync) {
- return (mUVCCamera != null) && (mMuxer != null);
- }
- }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ Log.i(TAG, "CameraThread#finalize");
+ super.finalize();
+ }
+
+ public AbstractUVCCameraHandler getHandler() {
+ if (DEBUG) Log.v(TAG_THREAD, "getHandler:");
+ synchronized (mSync) {
+ if (mHandler == null)
+ try {
+ mSync.wait();
+ } catch (final InterruptedException e) {
+ }
+ }
+ return mHandler;
+ }
+
+ public int getWidth() {
+ synchronized (mSync) {
+ return mWidth;
+ }
+ }
+
+ public int getHeight() {
+ synchronized (mSync) {
+ return mHeight;
+ }
+ }
+
+ public boolean isCameraOpened() {
+ synchronized (mSync) {
+ return mUVCCamera != null;
+ }
+ }
+
+ public boolean isPreviewing() {
+ synchronized (mSync) {
+ return mUVCCamera != null && mIsPreviewing;
+ }
+ }
+
+ public boolean isRecording() {
+ synchronized (mSync) {
+ return (mUVCCamera != null) && (mH264Consumer != null);
+ }
+ }
// public boolean isAudioRecording(){
// synchronized (mSync){
@@ -490,124 +498,126 @@ public abstract class AbstractUVCCameraHandler extends Handler {
// }
// }
- public boolean isEqual(final UsbDevice device) {
- return (mUVCCamera != null) && (mUVCCamera.getDevice() != null) && mUVCCamera.getDevice().equals(device);
- }
-
- public void handleOpen(final USBMonitor.UsbControlBlock ctrlBlock) {
- if (DEBUG) Log.v(TAG_THREAD, "handleOpen:");
- handleClose();
- try {
- final UVCCamera camera = new UVCCamera();
- camera.open(ctrlBlock);
- synchronized (mSync) {
- mUVCCamera = camera;
- }
- callOnOpen();
- } catch (final Exception e) {
- callOnError(e);
- }
- if (DEBUG) Log.i(TAG, "supportedSize:" + (mUVCCamera != null ? mUVCCamera.getSupportedSize() : null));
- }
-
- public void handleClose() {
- if (DEBUG) Log.v(TAG_THREAD, "handleClose:");
- handleStopRecording();
- final UVCCamera camera;
- synchronized (mSync) {
- camera = mUVCCamera;
- mUVCCamera = null;
- }
- if (camera != null) {
- camera.stopPreview();
- camera.destroy();
- callOnClose();
- }
- }
-
- public void handleStartPreview(final Object surface) {
- if (DEBUG) Log.v(TAG_THREAD, "handleStartPreview:");
- if ((mUVCCamera == null) || mIsPreviewing) return;
- try {
- mUVCCamera.setPreviewSize(mWidth, mHeight, 1, 31, mPreviewMode, mBandwidthFactor);
- // 获取USB Camera预览数据,使用NV21颜色会失真
- // 无论使用YUV还是MPEG,setFrameCallback的设置效果一致
+ public boolean isEqual(final UsbDevice device) {
+ return (mUVCCamera != null) && (mUVCCamera.getDevice() != null) && mUVCCamera.getDevice().equals(device);
+ }
+
+ public void handleOpen(final USBMonitor.UsbControlBlock ctrlBlock) {
+ if (DEBUG) Log.v(TAG_THREAD, "handleOpen:");
+ handleClose();
+ try {
+ final UVCCamera camera = new UVCCamera();
+ camera.open(ctrlBlock);
+ synchronized (mSync) {
+ mUVCCamera = camera;
+ }
+ callOnOpen();
+ } catch (final Exception e) {
+ callOnError(e);
+ }
+ if (DEBUG)
+ Log.i(TAG, "supportedSize:" + (mUVCCamera != null ? mUVCCamera.getSupportedSize() : null));
+ }
+
+ public void handleClose() {
+ if (DEBUG) Log.v(TAG_THREAD, "handleClose:");
+ handleStopPusher();
+ final UVCCamera camera;
+ synchronized (mSync) {
+ camera = mUVCCamera;
+ mUVCCamera = null;
+ }
+ if (camera != null) {
+ camera.stopPreview();
+ camera.destroy();
+ callOnClose();
+ }
+ }
+
+ public void handleStartPreview(final Object surface) {
+ if (DEBUG) Log.v(TAG_THREAD, "handleStartPreview:");
+ if ((mUVCCamera == null) || mIsPreviewing) return;
+ try {
+ mUVCCamera.setPreviewSize(mWidth, mHeight, 1, 31, mPreviewMode, mBandwidthFactor);
+ // 获取USB Camera预览数据,使用NV21颜色会失真
+ // 无论使用YUV还是MPEG,setFrameCallback的设置效果一致
// mUVCCamera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_NV21);
- mUVCCamera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_YUV420SP);
- } catch (final IllegalArgumentException e) {
- try {
- // fallback to YUV mode
- mUVCCamera.setPreviewSize(mWidth, mHeight, 1, 31, UVCCamera.DEFAULT_PREVIEW_MODE, mBandwidthFactor);
- } catch (final IllegalArgumentException e1) {
- callOnError(e1);
- return;
- }
- }
- if (surface instanceof SurfaceHolder) {
- mUVCCamera.setPreviewDisplay((SurfaceHolder)surface);
- } if (surface instanceof Surface) {
- mUVCCamera.setPreviewDisplay((Surface)surface);
- } else {
- mUVCCamera.setPreviewTexture((SurfaceTexture)surface);
- }
- mUVCCamera.startPreview();
- mUVCCamera.updateCameraParams();
- synchronized (mSync) {
- mIsPreviewing = true;
- }
- callOnStartPreview();
- }
-
- public void handleStopPreview() {
- if (DEBUG) Log.v(TAG_THREAD, "handleStopPreview:");
- if (mIsPreviewing) {
- if (mUVCCamera != null) {
- mUVCCamera.stopPreview();
- mUVCCamera.setFrameCallback(null, 0);
- }
- synchronized (mSync) {
- mIsPreviewing = false;
- mSync.notifyAll();
- }
- callOnStopPreview();
- }
- if (DEBUG) Log.v(TAG_THREAD, "handleStopPreview:finished");
- }
-
- // 捕获静态图片
- public void handleCaptureStill(final String path) {
- if (DEBUG) Log.v(TAG_THREAD, "handleCaptureStill:");
- final Activity parent = mWeakParent.get();
- if (parent == null) return;
+ mUVCCamera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_YUV420SP);
+ } catch (final IllegalArgumentException e) {
+ try {
+ // fallback to YUV mode
+ mUVCCamera.setPreviewSize(mWidth, mHeight, 1, 31, UVCCamera.DEFAULT_PREVIEW_MODE, mBandwidthFactor);
+ } catch (final IllegalArgumentException e1) {
+ callOnError(e1);
+ return;
+ }
+ }
+ if (surface instanceof SurfaceHolder) {
+ mUVCCamera.setPreviewDisplay((SurfaceHolder) surface);
+ }
+ if (surface instanceof Surface) {
+ mUVCCamera.setPreviewDisplay((Surface) surface);
+ } else {
+ mUVCCamera.setPreviewTexture((SurfaceTexture) surface);
+ }
+ mUVCCamera.startPreview();
+ mUVCCamera.updateCameraParams();
+ synchronized (mSync) {
+ mIsPreviewing = true;
+ }
+ callOnStartPreview();
+ }
+
+ public void handleStopPreview() {
+ if (DEBUG) Log.v(TAG_THREAD, "handleStopPreview:");
+ if (mIsPreviewing) {
+ if (mUVCCamera != null) {
+ mUVCCamera.stopPreview();
+ mUVCCamera.setFrameCallback(null, 0);
+ }
+ synchronized (mSync) {
+ mIsPreviewing = false;
+ mSync.notifyAll();
+ }
+ callOnStopPreview();
+ }
+ if (DEBUG) Log.v(TAG_THREAD, "handleStopPreview:finished");
+ }
+
+ // 捕获静态图片
+ public void handleCaptureStill(final String path) {
+ if (DEBUG) Log.v(TAG_THREAD, "handleCaptureStill:");
+ final Activity parent = mWeakParent.get();
+ if (parent == null) return;
// mSoundPool.play(mSoundId, 0.2f, 0.2f, 0, 0, 1.0f); // play shutter sound
- try {
- final Bitmap bitmap = mWeakCameraView.get().captureStillImage(mWidth,mHeight);
- // get buffered output stream for saving a captured still image as a file on external storage.
- // the file name is came from current time.
- // You should use extension name as same as CompressFormat when calling Bitmap#compress.
- final File outputFile = TextUtils.isEmpty(path)
- ? MediaMuxerWrapper.getCaptureFile(Environment.DIRECTORY_DCIM, ".jpg")
- : new File(path);
- final BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile));
- try {
- try {
- bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
- os.flush();
- mHandler.sendMessage(mHandler.obtainMessage(MSG_MEDIA_UPDATE, outputFile.getPath()));
- } catch (final IOException e) {
- }
- } finally {
- os.close();
- }
- if(mCaptureListener != null) {
- mCaptureListener.onCaptureResult(path);
- }
- } catch (final Exception e) {
- callOnError(e);
- }
- }
-
- // 开始录制视频
+ try {
+ final Bitmap bitmap = mWeakCameraView.get().captureStillImage(mWidth, mHeight);
+ // get buffered output stream for saving a captured still image as a file on external storage.
+ // the file name is came from current time.
+ // You should use extension name as same as CompressFormat when calling Bitmap#compress.
+ final File outputFile = TextUtils.isEmpty(path)
+ ? MediaMuxerWrapper.getCaptureFile(Environment.DIRECTORY_DCIM, ".jpg")
+ : new File(path);
+ final BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile));
+ try {
+ try {
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
+ os.flush();
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_MEDIA_UPDATE, outputFile.getPath()));
+ } catch (final IOException e) {
+ }
+ } finally {
+ os.close();
+ }
+ if (mCaptureListener != null) {
+ mCaptureListener.onCaptureResult(path);
+ }
+ } catch (final Exception e) {
+ callOnError(e);
+ }
+ }
+
+ // 开始录制视频
// public void handleStartRecording2(String path) {
// if (DEBUG) Log.v(TAG_THREAD, "handleStartRecording:");
// try {
@@ -649,133 +659,136 @@ public abstract class AbstractUVCCameraHandler extends Handler {
// }
// }
- private AACEncodeConsumer mAacConsumer;
- private H264EncodeConsumer mH264Consumer;
+ private AACEncodeConsumer mAacConsumer;
+ private H264EncodeConsumer mH264Consumer;
- public void handleStartRecording(RecordParams params){
- if ((mUVCCamera == null) || (mMuxer != null))
- return;
- if (params == null)
- throw new NullPointerException("RecordParams can not be null!");
+ public void handleStartPusher(RecordParams params) {
+ if ((mUVCCamera == null) || (mH264Consumer != null))
+ return;
// // 获取USB Camera预览数据
// mUVCCamera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_NV21);
- // 初始化混合器
- videoPath = params.getRecordPath();
- mMuxer = new Mp4MediaMuxer(params.getRecordPath(),
- params.getRecordDuration() * 60 * 1000,params.isVoiceClose());
-
- // 启动视频编码线程
- startVideoRecord();
- // 启动音频编码线程
- if(! params.isVoiceClose()) {
- startAudioRecord();
- }
- callOnStartRecording();
- }
-
-
- public void handleStopRecording(){
- // 停止混合器
- if (mMuxer != null){
- mMuxer.release();
- mMuxer = null;
- Log.i(TAG,TAG+"---->停止本地录制");
- }
- // 停止音视频编码线程
- stopAudioRecord();
- stopVideoRecord();
+ // 初始化混合器
+ if (params != null) {
+ videoPath = params.getRecordPath();
+ mMuxer = new Mp4MediaMuxer(params.getRecordPath(),
+ params.getRecordDuration() * 60 * 1000, params.isVoiceClose());
+ }
+ // 启动视频编码线程
+ startVideoRecord();
+ // 启动音频编码线程
+ if (!params.isVoiceClose()) {
+ startAudioRecord();
+ }
+ callOnStartRecording();
+ }
+
+
+ public void handleStopPusher() {
+ // 停止混合器
+ if (mMuxer != null) {
+ mMuxer.release();
+ mMuxer = null;
+ Log.i(TAG, TAG + "---->停止本地录制");
+ }
+ // 停止音视频编码线程
+ stopAudioRecord();
+ stopVideoRecord();
// // 停止捕获视频数据
// if (mUVCCamera != null) {
// mUVCCamera.stopCapture();
// }
- mWeakCameraView.get().setVideoEncoder(null);
- // you should not wait here
- callOnStopRecording();
- // 返回路径
- if(mListener != null) {
- mListener.onRecordResult(videoPath+".mp4");
- }
- }
-
- private void startVideoRecord() {
- mH264Consumer = new H264EncodeConsumer(getWidth(),getHeight());
- mH264Consumer.setOnH264EncodeResultListener(new H264EncodeConsumer.OnH264EncodeResultListener() {
- @Override
- public void onEncodeResult(byte[] data, int offset, int length, long timestamp) {
- if(mListener != null){
- mListener.onEncodeResult(data,offset,length,timestamp,1);
- }
- }
- });
- mH264Consumer.start();
- // 添加混合器
- if(mH264Consumer != null){
- mH264Consumer.setTmpuMuxer(mMuxer);
- }
- }
-
- private void stopVideoRecord(){
- if(mH264Consumer != null){
- mH264Consumer.exit();
- mH264Consumer.setTmpuMuxer(null);
- try {
- Thread t2 = mH264Consumer;
- mH264Consumer = null;
- if(t2 != null){
- t2.interrupt();
- t2.join();
- }
- } catch (InterruptedException e) {
- 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);
- }
- }
-
- private void stopAudioRecord(){
- if(mAacConsumer != null){
- mAacConsumer.exit();
- mAacConsumer.setTmpuMuxer(null);
- try {
- Thread t1 = mAacConsumer;
- mAacConsumer = null;
- if(t1 != null){
- t1.interrupt();
- t1.join();
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
+ mWeakCameraView.get().setVideoEncoder(null);
+ // you should not wait here
+ callOnStopRecording();
+ // 返回路径
+ if (mListener != null) {
+ mListener.onRecordResult(videoPath + ".mp4");
+ }
+ }
+
+ private void startVideoRecord() {
+ mH264Consumer = new H264EncodeConsumer(getWidth(), getHeight());
+ mH264Consumer.setOnH264EncodeResultListener(new H264EncodeConsumer.OnH264EncodeResultListener() {
+ @Override
+ public void onEncodeResult(byte[] data, int offset, int length, long timestamp) {
+ if (mListener != null) {
+ mListener.onEncodeResult(data, offset, length, timestamp, 1);
+ }
+ }
+ });
+ mH264Consumer.start();
+ // 添加混合器
+ if (mMuxer != null) {
+ if (mH264Consumer != null) {
+ mH264Consumer.setTmpuMuxer(mMuxer);
+ }
+ }
+ }
+
+ private void stopVideoRecord() {
+ if (mH264Consumer != null) {
+ mH264Consumer.exit();
+ mH264Consumer.setTmpuMuxer(null);
+ try {
+ Thread t2 = mH264Consumer;
+ mH264Consumer = null;
+ if (t2 != null) {
+ t2.interrupt();
+ t2.join();
+ }
+ } catch (InterruptedException e) {
+ 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 (mMuxer != null) {
+ if (mAacConsumer != null) {
+ mAacConsumer.setTmpuMuxer(mMuxer);
+ }
+ }
+ }
+
+ private void stopAudioRecord() {
+ if (mAacConsumer != null) {
+ mAacConsumer.exit();
+ mAacConsumer.setTmpuMuxer(null);
+ try {
+ Thread t1 = mAacConsumer;
+ mAacConsumer = null;
+ if (t1 != null) {
+ t1.interrupt();
+ t1.join();
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
// isAudioThreadStart = false;
- }
+ }
- private String picPath = null;
+ private String picPath = null;
- public void handleStillPicture(String picPath) {
- this.picPath = picPath;
- }
+ public void handleStillPicture(String picPath) {
+ this.picPath = picPath;
+ }
- private final IFrameCallback mIFrameCallback = new IFrameCallback() {
- @Override
- public void onFrame(final ByteBuffer frame) {
+ private final IFrameCallback mIFrameCallback = new IFrameCallback() {
+ @Override
+ public void onFrame(final ByteBuffer frame) {
// final MediaVideoBufferEncoder videoEncoder;
// synchronized (mSync) {
// videoEncoder = mVideoEncoder;
@@ -784,172 +797,172 @@ public abstract class AbstractUVCCameraHandler extends Handler {
// videoEncoder.frameAvailableSoon();
// videoEncoder.encode(frame);
// }
- int len = frame.capacity();
- final byte[] yuv = new byte[len];
- frame.get(yuv);
- // nv21 yuv data callback
- if(mPreviewListener != null) {
- mPreviewListener.onPreviewResult(yuv);
- }
- // 捕获图片
- if(isCaptureStill && ! TextUtils.isEmpty(picPath)) {
- isCaptureStill = false;
- new Thread(new Runnable() {
- @Override
- public void run() {
- saveYuv2Jpeg(picPath,yuv);
- }
- }).start();
-
- isCaptureStill = false;
- }
- // 视频
- if(mH264Consumer != null){
- // 修改分辨率参数
- mH264Consumer.setRawYuv(yuv,mWidth,mHeight);
- }
- }
- };
-
- private void saveYuv2Jpeg(String path,byte[] data){
- YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, mWidth, mHeight, null);
- ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);
- boolean result = yuvImage.compressToJpeg(new Rect(0, 0, mWidth, mHeight), 100, bos);
- if(result){
- byte[] buffer = bos.toByteArray();
- Bitmap bmp = BitmapFactory.decodeByteArray(buffer, 0, buffer.length);
-
- File file = new File(path);
- FileOutputStream fos = null;
- try {
- fos = new FileOutputStream(file);
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
- bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
- try {
- fos.flush();
- fos.close();
- bmp.recycle();
- if(mCaptureListener != null) {
- mCaptureListener.onCaptureResult(path);
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- try {
- bos.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
- public void handleUpdateMedia(final String path) {
- if (DEBUG) Log.v(TAG_THREAD, "handleUpdateMedia:path=" + path);
- final Activity parent = mWeakParent.get();
- final boolean released = (mHandler == null) || mHandler.mReleased;
- if (parent != null && parent.getApplicationContext() != null) {
- try {
- if (DEBUG) Log.i(TAG, "MediaScannerConnection#scanFile");
- MediaScannerConnection.scanFile(parent.getApplicationContext(), new String[]{ path }, null, null);
- } catch (final Exception e) {
- Log.e(TAG, "handleUpdateMedia:", e);
- }
- if (released || parent.isDestroyed())
- handleRelease();
- } else {
- Log.w(TAG, "MainActivity already destroyed");
- // give up to add this movie to MediaStore now.
- // Seeing this movie on Gallery app etc. will take a lot of time.
- handleRelease();
- }
- }
-
- public void handleRelease() {
- if (DEBUG) Log.v(TAG_THREAD, "handleRelease:mIsRecording=" + mIsRecording);
- handleClose();
- mCallbacks.clear();
- if (!mIsRecording) {
- mHandler.mReleased = true;
- Looper.myLooper().quit();
- }
- if (DEBUG) Log.v(TAG_THREAD, "handleRelease:finished");
- }
-
- // 自动对焦
- public void handleCameraFoucs() {
- if (DEBUG) Log.v(TAG_THREAD, "handleStartPreview:");
- if ((mUVCCamera == null) || !mIsPreviewing)
- return;
- mUVCCamera.setAutoFocus(true);
- }
-
- // 获取支持的分辨率
- public List