mirror of
https://github.com/opencv/opencv.git
synced 2024-11-24 03:00:14 +08:00
Merge pull request #24592 from savuor:recorder_android
Android sample for VideoWriter #24592 This PR: * adds an Android sample for video recording with MediaNDK and built-in MJPEG. * adds a flag `--no_media_ndk` for `build_sdk.py` script to disable MediaNDK linkage. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
parent
2e37a697b9
commit
500fd453a1
@ -215,7 +215,7 @@ class Builder:
|
||||
for d in ["CMakeCache.txt", "CMakeFiles/", "bin/", "libs/", "lib/", "package/", "install/samples/"]:
|
||||
rm_one(d)
|
||||
|
||||
def build_library(self, abi, do_install):
|
||||
def build_library(self, abi, do_install, no_media_ndk):
|
||||
cmd = [self.cmake_path, "-GNinja"]
|
||||
cmake_vars = dict(
|
||||
CMAKE_TOOLCHAIN_FILE=self.get_toolchain_file(),
|
||||
@ -260,6 +260,9 @@ class Builder:
|
||||
if do_install:
|
||||
cmd.extend(["-DBUILD_TESTS=ON", "-DINSTALL_TESTS=ON"])
|
||||
|
||||
if no_media_ndk:
|
||||
cmake_vars['WITH_ANDROID_MEDIANDK'] = "OFF"
|
||||
|
||||
cmake_vars.update(abi.cmake_vars)
|
||||
cmd += [ "-D%s='%s'" % (k, v) for (k, v) in cmake_vars.items() if v is not None]
|
||||
cmd.append(self.opencvdir)
|
||||
@ -370,6 +373,7 @@ if __name__ == "__main__":
|
||||
parser.add_argument('--opencl', action="store_true", help="Enable OpenCL support")
|
||||
parser.add_argument('--no_kotlin', action="store_true", help="Disable Kotlin extensions")
|
||||
parser.add_argument('--shared', action="store_true", help="Build shared libraries")
|
||||
parser.add_argument('--no_media_ndk', action="store_true", help="Do not link Media NDK (required for video I/O support)")
|
||||
args = parser.parse_args()
|
||||
|
||||
log.basicConfig(format='%(message)s', level=log.DEBUG)
|
||||
@ -447,7 +451,7 @@ if __name__ == "__main__":
|
||||
|
||||
os.chdir(builder.libdest)
|
||||
builder.clean_library_build_dir()
|
||||
builder.build_library(abi, do_install)
|
||||
builder.build_library(abi, do_install, args.no_media_ndk)
|
||||
|
||||
builder.gather_results()
|
||||
|
||||
|
@ -13,6 +13,7 @@ add_subdirectory(image-manipulations)
|
||||
add_subdirectory(camera-calibration)
|
||||
add_subdirectory(color-blob-detection)
|
||||
add_subdirectory(mobilenet-objdetect)
|
||||
add_subdirectory(video-recorder)
|
||||
add_subdirectory(tutorial-1-camerapreview)
|
||||
add_subdirectory(tutorial-2-mixedprocessing)
|
||||
add_subdirectory(tutorial-3-cameracontrol)
|
||||
|
12
samples/android/video-recorder/CMakeLists.txt
Normal file
12
samples/android/video-recorder/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
||||
set(sample example-video-recorder)
|
||||
|
||||
if(BUILD_FAT_JAVA_LIB)
|
||||
set(native_deps opencv_java)
|
||||
else()
|
||||
set(native_deps videoio)
|
||||
endif()
|
||||
|
||||
add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS "${OPENCV_ANDROID_LIB_DIR}" SDK_TARGET 11 "${ANDROID_SDK_TARGET}" NATIVE_DEPS ${native_deps})
|
||||
if(TARGET ${sample})
|
||||
add_dependencies(opencv_android_examples ${sample})
|
||||
endif()
|
36
samples/android/video-recorder/build.gradle.in
Normal file
36
samples/android/video-recorder/build.gradle.in
Normal file
@ -0,0 +1,36 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
namespace 'org.opencv.samples.recorder'
|
||||
compileSdkVersion @ANDROID_COMPILE_SDK_VERSION@
|
||||
defaultConfig {
|
||||
applicationId "org.opencv.samples.recorder"
|
||||
minSdkVersion @ANDROID_MIN_SDK_VERSION@
|
||||
targetSdkVersion @ANDROID_TARGET_SDK_VERSION@
|
||||
versionCode 301
|
||||
versionName "3.01"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
main {
|
||||
java.srcDirs = @ANDROID_SAMPLE_JAVA_PATH@
|
||||
aidl.srcDirs = @ANDROID_SAMPLE_JAVA_PATH@
|
||||
res.srcDirs = @ANDROID_SAMPLE_RES_PATH@
|
||||
manifest.srcFile '@ANDROID_SAMPLE_MANIFEST_PATH@'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
//implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
if (gradle.opencv_source == "sdk_path") {
|
||||
implementation project(':opencv')
|
||||
} else if (gradle.opencv_source == "maven_local" || gradle.opencv_source == "maven_cenral") {
|
||||
implementation 'org.opencv:opencv:@OPENCV_VERSION_PLAIN@'
|
||||
}
|
||||
}
|
38
samples/android/video-recorder/gradle/AndroidManifest.xml
Normal file
38
samples/android/video-recorder/gradle/AndroidManifest.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.opencv.samples.recorder"
|
||||
>
|
||||
|
||||
<application
|
||||
android:label="@string/app_name"
|
||||
android:icon="@drawable/icon"
|
||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
|
||||
|
||||
<activity
|
||||
android:exported="true"
|
||||
android:name="RecorderActivity"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="landscape"
|
||||
android:configChanges="keyboardHidden|orientation">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<supports-screens android:resizeable="true"
|
||||
android:smallScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:largeScreens="true"
|
||||
android:anyDensity="true" />
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.camera.front" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>
|
||||
|
||||
</manifest>
|
BIN
samples/android/video-recorder/res/drawable/icon.png
Normal file
BIN
samples/android/video-recorder/res/drawable/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
@ -0,0 +1,38 @@
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:opencv="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:layout_margin="10dp"
|
||||
android:text="Start Camera" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textview1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right"
|
||||
android:layout_margin="10dp"
|
||||
android:text="Status: Initialized"
|
||||
android:textColor="#FF0000" />
|
||||
|
||||
<org.opencv.android.JavaCameraView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/recorder_activity_java_surface_view"
|
||||
opencv:show_fps="true"
|
||||
opencv:camera_id="any" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_view"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:visibility="gone"
|
||||
/>
|
||||
|
||||
</FrameLayout>
|
4
samples/android/video-recorder/res/values/strings.xml
Normal file
4
samples/android/video-recorder/res/values/strings.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">OpenCV Video Recorder</string>
|
||||
</resources>
|
@ -0,0 +1,340 @@
|
||||
package org.opencv.samples.recorder;
|
||||
|
||||
import org.opencv.android.CameraActivity;
|
||||
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
|
||||
import org.opencv.android.OpenCVLoader;
|
||||
import org.opencv.android.Utils;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Size;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
import org.opencv.videoio.VideoCapture;
|
||||
import org.opencv.videoio.VideoWriter;
|
||||
import org.opencv.android.CameraBridgeViewBase;
|
||||
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
|
||||
import org.opencv.videoio.Videoio;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.WindowManager;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class RecorderActivity extends CameraActivity implements CvCameraViewListener2, View.OnClickListener {
|
||||
private static final String TAG = "OCVSample::Activity";
|
||||
private static final String FILENAME_MP4 = "sample_video1.mp4";
|
||||
private static final String FILENAME_AVI = "sample_video1.avi";
|
||||
|
||||
private static final int STATUS_FINISHED_PLAYBACK = 0;
|
||||
private static final int STATUS_PREVIEW = 1;
|
||||
private static final int STATUS_RECORDING = 2;
|
||||
private static final int STATUS_PLAYING = 3;
|
||||
private static final int STATUS_ERROR = 4;
|
||||
|
||||
private String mVideoFilename;
|
||||
private boolean mUseBuiltInMJPG = false;
|
||||
|
||||
private int mStatus = STATUS_FINISHED_PLAYBACK;
|
||||
private int mFPS = 30;
|
||||
private int mWidth = 0, mHeight = 0;
|
||||
|
||||
private CameraBridgeViewBase mOpenCvCameraView;
|
||||
private ImageView mImageView;
|
||||
private Button mTriggerButton;
|
||||
private TextView mStatusTextView;
|
||||
Runnable mPlayerThread;
|
||||
|
||||
private VideoWriter mVideoWriter = null;
|
||||
private VideoCapture mVideoCapture = null;
|
||||
private Mat mVideoFrame;
|
||||
private Mat mRenderFrame;
|
||||
|
||||
public RecorderActivity() {
|
||||
Log.i(TAG, "Instantiated new " + this.getClass());
|
||||
}
|
||||
|
||||
/** Called when the activity is first created. */
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
Log.i(TAG, "called onCreate");
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
|
||||
setContentView(R.layout.recorder_surface_view);
|
||||
|
||||
mStatusTextView = (TextView) findViewById(R.id.textview1);
|
||||
mStatusTextView.bringToFront();
|
||||
|
||||
if (OpenCVLoader.initLocal()) {
|
||||
Log.i(TAG, "OpenCV loaded successfully");
|
||||
} else {
|
||||
Log.e(TAG, "OpenCV initialization failed!");
|
||||
mStatus = STATUS_ERROR;
|
||||
mStatusTextView.setText("Error: Can't initialize OpenCV");
|
||||
return;
|
||||
}
|
||||
|
||||
mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.recorder_activity_java_surface_view);
|
||||
mOpenCvCameraView.setVisibility(SurfaceView.GONE);
|
||||
mOpenCvCameraView.setCvCameraViewListener(this);
|
||||
mOpenCvCameraView.disableView();
|
||||
|
||||
mImageView = (ImageView) findViewById(R.id.image_view);
|
||||
|
||||
mTriggerButton = (Button) findViewById(R.id.btn1);
|
||||
mTriggerButton.setOnClickListener(this);
|
||||
mTriggerButton.bringToFront();
|
||||
|
||||
if (mUseBuiltInMJPG)
|
||||
mVideoFilename = getFilesDir() + "/" + FILENAME_AVI;
|
||||
else
|
||||
mVideoFilename = getFilesDir() + "/" + FILENAME_MP4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause()
|
||||
{
|
||||
Log.d(TAG, "Pause");
|
||||
super.onPause();
|
||||
if (mOpenCvCameraView != null)
|
||||
mOpenCvCameraView.disableView();
|
||||
mImageView.setVisibility(SurfaceView.GONE);
|
||||
if (mVideoWriter != null) {
|
||||
mVideoWriter.release();
|
||||
mVideoWriter = null;
|
||||
}
|
||||
if (mVideoCapture != null) {
|
||||
mVideoCapture.release();
|
||||
mVideoCapture = null;
|
||||
}
|
||||
mStatus = STATUS_FINISHED_PLAYBACK;
|
||||
mStatusTextView.setText("Status: Finished playback");
|
||||
mTriggerButton.setText("Start Camera");
|
||||
|
||||
mVideoFrame.release();
|
||||
mRenderFrame.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume()
|
||||
{
|
||||
Log.d(TAG, "onResume");
|
||||
super.onResume();
|
||||
|
||||
mVideoFrame = new Mat();
|
||||
mRenderFrame = new Mat();
|
||||
|
||||
changeStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<? extends CameraBridgeViewBase> getCameraViewList() {
|
||||
return Collections.singletonList(mOpenCvCameraView);
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
Log.d(TAG, "called onDestroy");
|
||||
super.onDestroy();
|
||||
if (mOpenCvCameraView != null)
|
||||
mOpenCvCameraView.disableView();
|
||||
if (mVideoWriter != null)
|
||||
mVideoWriter.release();
|
||||
if (mVideoCapture != null)
|
||||
mVideoCapture.release();
|
||||
}
|
||||
|
||||
public void onCameraViewStarted(int width, int height) {
|
||||
Log.d(TAG, "Camera view started " + String.valueOf(width) + "x" + String.valueOf(height));
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
}
|
||||
|
||||
public void onCameraViewStopped() {
|
||||
Log.d(TAG, "Camera view stopped");
|
||||
}
|
||||
|
||||
public Mat onCameraFrame(CvCameraViewFrame inputFrame)
|
||||
{
|
||||
Log.d(TAG, "Camera frame arrived");
|
||||
|
||||
Mat rgbMat = inputFrame.rgba();
|
||||
|
||||
Log.d(TAG, "Size: " + rgbMat.width() + "x" + rgbMat.height());
|
||||
|
||||
if (mVideoWriter != null && mVideoWriter.isOpened()) {
|
||||
Imgproc.cvtColor(rgbMat, mVideoFrame, Imgproc.COLOR_RGBA2BGR);
|
||||
mVideoWriter.write(mVideoFrame);
|
||||
}
|
||||
|
||||
return rgbMat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Log.i(TAG,"onClick event");
|
||||
changeStatus();
|
||||
}
|
||||
|
||||
public void changeStatus() {
|
||||
switch(mStatus) {
|
||||
case STATUS_ERROR:
|
||||
Toast.makeText(this, "Error", Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
case STATUS_FINISHED_PLAYBACK:
|
||||
if (!startPreview()) {
|
||||
setErrorStatus();
|
||||
break;
|
||||
}
|
||||
mStatus = STATUS_PREVIEW;
|
||||
mStatusTextView.setText("Status: Camera preview");
|
||||
mTriggerButton.setText("Start recording");
|
||||
break;
|
||||
case STATUS_PREVIEW:
|
||||
if (!startRecording()) {
|
||||
setErrorStatus();
|
||||
break;
|
||||
}
|
||||
mStatus = STATUS_RECORDING;
|
||||
mStatusTextView.setText("Status: recording video");
|
||||
mTriggerButton.setText(" Stop and play video");
|
||||
break;
|
||||
case STATUS_RECORDING:
|
||||
if (!stopRecording()) {
|
||||
setErrorStatus();
|
||||
break;
|
||||
}
|
||||
if (!startPlayback()) {
|
||||
setErrorStatus();
|
||||
break;
|
||||
}
|
||||
mStatus = STATUS_PLAYING;
|
||||
mStatusTextView.setText("Status: Playing video");
|
||||
mTriggerButton.setText("Stop playback");
|
||||
break;
|
||||
case STATUS_PLAYING:
|
||||
if (!stopPlayback()) {
|
||||
setErrorStatus();
|
||||
break;
|
||||
}
|
||||
mStatus = STATUS_FINISHED_PLAYBACK;
|
||||
mStatusTextView.setText("Status: Finished playback");
|
||||
mTriggerButton.setText("Start Camera");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void setErrorStatus() {
|
||||
mStatus = STATUS_ERROR;
|
||||
mStatusTextView.setText("Status: Error");
|
||||
}
|
||||
|
||||
public boolean startPreview() {
|
||||
mOpenCvCameraView.enableView();
|
||||
mOpenCvCameraView.setVisibility(View.VISIBLE);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean startRecording() {
|
||||
Log.i(TAG,"Starting recording");
|
||||
|
||||
File file = new File(mVideoFilename);
|
||||
file.delete();
|
||||
|
||||
mVideoWriter = new VideoWriter();
|
||||
if (!mUseBuiltInMJPG) {
|
||||
mVideoWriter.open(mVideoFilename, Videoio.CAP_ANDROID, VideoWriter.fourcc('H', '2', '6', '4'), mFPS, new Size(mWidth, mHeight));
|
||||
if (!mVideoWriter.isOpened()) {
|
||||
Log.i(TAG,"Can't record H264. Switching to MJPG");
|
||||
mUseBuiltInMJPG = true;
|
||||
mVideoFilename = getFilesDir() + "/" + FILENAME_AVI;
|
||||
}
|
||||
}
|
||||
|
||||
if (mUseBuiltInMJPG) {
|
||||
mVideoWriter.open(mVideoFilename, VideoWriter.fourcc('M', 'J', 'P', 'G'), mFPS, new Size(mWidth, mHeight));
|
||||
}
|
||||
|
||||
Log.d(TAG, "Size: " + String.valueOf(mWidth) + "x" + String.valueOf(mHeight));
|
||||
Log.d(TAG, "File: " + mVideoFilename);
|
||||
|
||||
if (mVideoWriter.isOpened()) {
|
||||
Toast.makeText(this, "Record started to file " + mVideoFilename, Toast.LENGTH_LONG).show();
|
||||
return true;
|
||||
} else {
|
||||
Toast.makeText(this, "Failed to start a record", Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean stopRecording() {
|
||||
Log.i(TAG, "Finishing recording");
|
||||
mOpenCvCameraView.disableView();
|
||||
mOpenCvCameraView.setVisibility(SurfaceView.GONE);
|
||||
mVideoWriter.release();
|
||||
mVideoWriter = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean startPlayback() {
|
||||
mImageView.setVisibility(SurfaceView.VISIBLE);
|
||||
|
||||
if (!mUseBuiltInMJPG){
|
||||
mVideoCapture = new VideoCapture(mVideoFilename, Videoio.CAP_ANDROID);
|
||||
} else {
|
||||
mVideoCapture = new VideoCapture(mVideoFilename, Videoio.CAP_OPENCV_MJPEG);
|
||||
}
|
||||
|
||||
if (!mVideoCapture.isOpened()) {
|
||||
Log.e(TAG, "Can't open video");
|
||||
Toast.makeText(this, "Can't open file " + mVideoFilename, Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
Toast.makeText(this, "Starting playback from file " + mVideoFilename, Toast.LENGTH_SHORT).show();
|
||||
|
||||
mPlayerThread = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mVideoCapture == null || !mVideoCapture.isOpened()) {
|
||||
return;
|
||||
}
|
||||
mVideoCapture.read(mVideoFrame);
|
||||
if (mVideoFrame.empty()) {
|
||||
if (mStatus == STATUS_PLAYING) {
|
||||
changeStatus();
|
||||
}
|
||||
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);
|
||||
mImageView.setImageBitmap(bmp);
|
||||
Handler h = new Handler();
|
||||
h.postDelayed(this, 33);
|
||||
}
|
||||
};
|
||||
|
||||
mPlayerThread.run();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean stopPlayback() {
|
||||
mVideoCapture.release();
|
||||
mVideoCapture = null;
|
||||
mImageView.setVisibility(SurfaceView.GONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user