diff --git a/modules/java/generator/android-24/java/org/opencv/android/NativeCameraView.java b/modules/java/generator/android-24/java/org/opencv/android/NativeCameraView.java index b28c2121cd..0053a25a07 100644 --- a/modules/java/generator/android-24/java/org/opencv/android/NativeCameraView.java +++ b/modules/java/generator/android-24/java/org/opencv/android/NativeCameraView.java @@ -2,6 +2,7 @@ package org.opencv.android; import org.opencv.core.Mat; import org.opencv.core.Size; +import org.opencv.core.MatOfInt; import org.opencv.imgproc.Imgproc; @@ -123,8 +124,11 @@ public class NativeCameraView extends CameraBridgeViewBase { return false; } + MatOfInt params = new MatOfInt(Videoio.CAP_PROP_FRAME_WIDTH, width, + Videoio.CAP_PROP_FRAME_HEIGHT, height); + Log.d(TAG, "Try to open camera with index " + localCameraIndex); - mCamera = new VideoCapture(localCameraIndex, Videoio.CAP_ANDROID); + mCamera = new VideoCapture(localCameraIndex, Videoio.CAP_ANDROID, params); if (mCamera == null) return false; @@ -139,9 +143,6 @@ public class NativeCameraView extends CameraBridgeViewBase { mFrame = new RotatedCameraFrame(new NativeCameraFrame(mCamera), frameRotation); - mCamera.set(Videoio.CAP_PROP_FRAME_WIDTH, width); - mCamera.set(Videoio.CAP_PROP_FRAME_HEIGHT, height); - if (frameRotation % 180 == 0) { mFrameWidth = (int) mCamera.get(Videoio.CAP_PROP_FRAME_WIDTH); mFrameHeight = (int) mCamera.get(Videoio.CAP_PROP_FRAME_HEIGHT); @@ -181,10 +182,9 @@ public class NativeCameraView extends CameraBridgeViewBase { @Override public Mat rgba() { - mCapture.set(Videoio.CAP_PROP_FOURCC, VideoWriter.fourcc('R','G','B','3')); - mCapture.retrieve(mBgr); - Log.d(TAG, "Retrived frame with size " + mBgr.cols() + "x" + mBgr.rows() + " and channels: " + mBgr.channels()); - Imgproc.cvtColor(mBgr, mRgba, Imgproc.COLOR_RGB2RGBA); + mCapture.set(Videoio.CAP_PROP_FOURCC, VideoWriter.fourcc('R','G','B','4')); + mCapture.retrieve(mRgba); + Log.d(TAG, "Retrieved frame with size " + mRgba.cols() + "x" + mRgba.rows() + " and channels: " + mRgba.channels()); return mRgba; } @@ -192,7 +192,7 @@ public class NativeCameraView extends CameraBridgeViewBase { public Mat gray() { mCapture.set(Videoio.CAP_PROP_FOURCC, VideoWriter.fourcc('G','R','E','Y')); mCapture.retrieve(mGray); - Log.d(TAG, "Retrived frame with size " + mGray.cols() + "x" + mGray.rows() + " and channels: " + mGray.channels()); + Log.d(TAG, "Retrieved frame with size " + mGray.cols() + "x" + mGray.rows() + " and channels: " + mGray.channels()); return mGray; } @@ -200,20 +200,17 @@ public class NativeCameraView extends CameraBridgeViewBase { mCapture = capture; mGray = new Mat(); mRgba = new Mat(); - mBgr = new Mat(); } @Override public void release() { if (mGray != null) mGray.release(); if (mRgba != null) mRgba.release(); - if (mBgr != null) mBgr.release(); } private VideoCapture mCapture; private Mat mRgba; private Mat mGray; - private Mat mBgr; }; private class CameraWorker implements Runnable { diff --git a/modules/videoio/src/cap_android_mediandk.cpp b/modules/videoio/src/cap_android_mediandk.cpp index 98f9eea3f9..5242a17d25 100644 --- a/modules/videoio/src/cap_android_mediandk.cpp +++ b/modules/videoio/src/cap_android_mediandk.cpp @@ -21,10 +21,20 @@ #define INPUT_TIMEOUT_MS 2000 +#define COLOR_FormatUnknown -1 #define COLOR_FormatYUV420Planar 19 #define COLOR_FormatYUV420SemiPlanar 21 #define COLOR_FormatSurface 0x7f000789 //See https://developer.android.com/reference/android/media/MediaCodecInfo.CodecCapabilities for codes +#define FOURCC_BGR CV_FOURCC_MACRO('B','G','R','3') +#define FOURCC_RGB CV_FOURCC_MACRO('R','G','B','3') +#define FOURCC_BGRA CV_FOURCC_MACRO('B','G','R','4') +#define FOURCC_RGBA CV_FOURCC_MACRO('R','G','B','4') +#define FOURCC_GRAY CV_FOURCC_MACRO('G','R','E','Y') +#define FOURCC_NV12 CV_FOURCC_MACRO('N','V','1','2') +#define FOURCC_YV12 CV_FOURCC_MACRO('Y','V','1','2') +#define FOURCC_UNKNOWN 0xFFFFFFFF + using namespace cv; #define TAG "NativeCodec" @@ -51,9 +61,9 @@ class AndroidMediaNdkCapture : public IVideoCapture public: AndroidMediaNdkCapture(): sawInputEOS(false), sawOutputEOS(false), - frameStride(0), frameWidth(0), frameHeight(0), colorFormat(0), - videoWidth(0), videoHeight(0), - videoFrameCount(0), + frameStride(0), frameWidth(0), frameHeight(0), + colorFormat(COLOR_FormatUnknown), fourCC(FOURCC_BGR), + videoWidth(0), videoHeight(0), videoFrameCount(0), videoRotation(0), videoRotationCode(-1), videoOrientationAuto(false) {} @@ -65,6 +75,7 @@ public: int32_t frameWidth; int32_t frameHeight; int32_t colorFormat; + uint32_t fourCC; int32_t videoWidth; int32_t videoHeight; float videoFrameRate; @@ -73,7 +84,6 @@ public: int32_t videoRotationCode; bool videoOrientationAuto; std::vector buffer; - Mat frame; ~AndroidMediaNdkCapture() { cleanUp(); } @@ -157,23 +167,51 @@ public: return false; } - Mat yuv(frameHeight + frameHeight/2, frameStride, CV_8UC1, buffer.data()); + ColorConversionCodes ccCode; + const Mat yuv(frameHeight + frameHeight/2, + frameWidth, CV_8UC1, buffer.data(), frameStride); if (colorFormat == COLOR_FormatYUV420Planar) { - cv::cvtColor(yuv, frame, cv::COLOR_YUV2BGR_YV12); + switch(fourCC) + { + case FOURCC_BGR: ccCode = COLOR_YUV2BGR_YV12; break; + case FOURCC_RGB: ccCode = COLOR_YUV2RGB_YV12; break; + case FOURCC_BGRA: ccCode = COLOR_YUV2BGRA_YV12; break; + case FOURCC_RGBA: ccCode = COLOR_YUV2RGBA_YV12; break; + case FOURCC_GRAY: ccCode = COLOR_YUV2GRAY_YV12; break; + case FOURCC_YV12: break; + case FOURCC_UNKNOWN: fourCC = FOURCC_YV12; break; + default: LOGE("Unexpected FOURCC value: %d", fourCC); + return false; + } } else if (colorFormat == COLOR_FormatYUV420SemiPlanar) { - cv::cvtColor(yuv, frame, cv::COLOR_YUV2BGR_NV21); + // Attention: COLOR_FormatYUV420SemiPlanar seems to correspond to NV12. + // This is different from the Camera2 interface, where NV21 + // is used in this situation. + switch(fourCC) + { + case FOURCC_BGR: ccCode = COLOR_YUV2BGR_NV12; break; + case FOURCC_RGB: ccCode = COLOR_YUV2RGB_NV12; break; + case FOURCC_BGRA: ccCode = COLOR_YUV2BGRA_NV12; break; + case FOURCC_RGBA: ccCode = COLOR_YUV2RGBA_NV12; break; + case FOURCC_GRAY: ccCode = COLOR_YUV2GRAY_NV12; break; + case FOURCC_NV12: break; + case FOURCC_UNKNOWN: fourCC = FOURCC_NV12; break; + default: LOGE("Unexpected FOURCC value: %d", fourCC); + return false; + } } else { LOGE("Unsupported video format: %d", colorFormat); return false; } - Mat croppedFrame = frame(Rect(0, 0, videoWidth, videoHeight)); - out.assign(croppedFrame); + if (fourCC == FOURCC_YV12 || fourCC == FOURCC_NV12) + yuv.copyTo(out); + else + cvtColor(yuv, out, ccCode); - if (videoOrientationAuto && -1 != videoRotationCode) { - cv::rotate(out, out, videoRotationCode); - } + if (videoOrientationAuto && -1 != videoRotationCode) + rotate(out, out, videoRotationCode); return true; } @@ -194,8 +232,11 @@ public: case CAP_PROP_FRAME_COUNT: return videoFrameCount; case CAP_PROP_ORIENTATION_META: return videoRotation; case CAP_PROP_ORIENTATION_AUTO: return videoOrientationAuto ? 1 : 0; + case CAP_PROP_FOURCC: return fourCC; } - return 0; + + // unknown parameter or value not available + return -1; } bool setProperty(int property_id, double value) CV_OVERRIDE @@ -206,6 +247,31 @@ public: videoOrientationAuto = value != 0 ? true : false; return true; } + case CAP_PROP_FOURCC: { + uint32_t newFourCC = cvRound(value); + switch (newFourCC) + { + case FOURCC_BGR: + case FOURCC_RGB: + case FOURCC_BGRA: + case FOURCC_RGBA: + case FOURCC_GRAY: + fourCC = newFourCC; + return true; + case FOURCC_YV12: + if (colorFormat != COLOR_FormatYUV420SemiPlanar) { + fourCC = (colorFormat == COLOR_FormatUnknown) ? FOURCC_UNKNOWN : FOURCC_YV12; + return true; + } + break; + case FOURCC_NV12: + if (colorFormat != COLOR_FormatYUV420Planar) { + fourCC = (colorFormat == COLOR_FormatUnknown) ? FOURCC_UNKNOWN : FOURCC_NV12; + return true; + } + break; + } + } } return false; diff --git a/samples/android/video-recorder/src/org/opencv/samples/recorder/RecorderActivity.java b/samples/android/video-recorder/src/org/opencv/samples/recorder/RecorderActivity.java index d4faea51b2..f7f1af77cf 100644 --- a/samples/android/video-recorder/src/org/opencv/samples/recorder/RecorderActivity.java +++ b/samples/android/video-recorder/src/org/opencv/samples/recorder/RecorderActivity.java @@ -57,7 +57,6 @@ public class RecorderActivity extends CameraActivity implements CvCameraViewList private VideoWriter mVideoWriter = null; private VideoCapture mVideoCapture = null; private Mat mVideoFrame; - private Mat mRenderFrame; public RecorderActivity() { Log.i(TAG, "Instantiated new " + this.getClass()); @@ -122,7 +121,6 @@ public class RecorderActivity extends CameraActivity implements CvCameraViewList mTriggerButton.setText("Start Camera"); mVideoFrame.release(); - mRenderFrame.release(); } @Override @@ -132,7 +130,6 @@ public class RecorderActivity extends CameraActivity implements CvCameraViewList super.onResume(); mVideoFrame = new Mat(); - mRenderFrame = new Mat(); changeStatus(); } @@ -294,12 +291,16 @@ public class RecorderActivity extends CameraActivity implements CvCameraViewList mVideoCapture = new VideoCapture(mVideoFilename, Videoio.CAP_OPENCV_MJPEG); } - if (!mVideoCapture.isOpened()) { + if (mVideoCapture == null || !mVideoCapture.isOpened()) { Log.e(TAG, "Can't open video"); Toast.makeText(this, "Can't open file " + mVideoFilename, Toast.LENGTH_SHORT).show(); return false; } + if (!mUseBuiltInMJPG){ + mVideoCapture.set(Videoio.CAP_PROP_FOURCC, VideoWriter.fourcc('R','G','B','4')); + } + Toast.makeText(this, "Starting playback from file " + mVideoFilename, Toast.LENGTH_SHORT).show(); mPlayerThread = new Runnable() { @@ -315,11 +316,14 @@ public class RecorderActivity extends CameraActivity implements CvCameraViewList } return; } - // VideoCapture with CAP_ANDROID generates RGB frames instead of BGR - // https://github.com/opencv/opencv/issues/24687 - Imgproc.cvtColor(mVideoFrame, mRenderFrame, mUseBuiltInMJPG ? Imgproc.COLOR_BGR2RGBA: Imgproc.COLOR_RGB2RGBA); - Bitmap bmp = Bitmap.createBitmap(mRenderFrame.cols(), mRenderFrame.rows(), Bitmap.Config.ARGB_8888); - Utils.matToBitmap(mRenderFrame, bmp); + + // MJPEG codec will output BGR only. So we need to convert to RGBA. + if (mUseBuiltInMJPG) { + Imgproc.cvtColor(mVideoFrame, mVideoFrame, Imgproc.COLOR_BGR2RGBA); + } + + Bitmap bmp = Bitmap.createBitmap(mVideoFrame.cols(), mVideoFrame.rows(), Bitmap.Config.ARGB_8888); + Utils.matToBitmap(mVideoFrame, bmp); mImageView.setImageBitmap(bmp); Handler h = new Handler(); h.postDelayed(this, 33);