Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Saves file from takeVideoSnapshot function to gallery on Android API 29+ #1221

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ buildscript {
}

dependencies {
classpath("com.android.tools.build:gradle:7.0.3")
classpath("com.android.tools.build:gradle:7.3.1")
classpath("io.deepmedia.tools:publisher:0.6.0")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31")

Expand Down
1 change: 1 addition & 0 deletions cameraview/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ android {
"com.otaliastudios.cameraview.tools.SdkExcludeFilter," +
"com.otaliastudios.cameraview.tools.SdkIncludeFilter"
}
namespace = "com.otaliastudios.cameraview"
buildTypes["debug"].isTestCoverageEnabled = true
buildTypes["release"].isMinifyEnabled = false
}
Expand Down
3 changes: 1 addition & 2 deletions cameraview/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.otaliastudios.cameraview">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
Expand Down
3 changes: 1 addition & 2 deletions cameraview/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.otaliastudios.cameraview">
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.CAMERA" />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -595,18 +595,18 @@ public boolean mapGesture(@NonNull Gesture gesture, @NonNull GestureAction actio
mPinchGestureFinder.setActive(mGestureMap.get(Gesture.PINCH) != none);
break;
case TAP:
// case DOUBLE_TAP:
// case DOUBLE_TAP:
case LONG_TAP:
mTapGestureFinder.setActive(
mGestureMap.get(Gesture.TAP) != none ||
// mGestureMap.get(Gesture.DOUBLE_TAP) != none ||
mGestureMap.get(Gesture.LONG_TAP) != none);
// mGestureMap.get(Gesture.DOUBLE_TAP) != none ||
mGestureMap.get(Gesture.LONG_TAP) != none);
break;
case SCROLL_HORIZONTAL:
case SCROLL_VERTICAL:
mScrollGestureFinder.setActive(
mGestureMap.get(Gesture.SCROLL_HORIZONTAL) != none ||
mGestureMap.get(Gesture.SCROLL_VERTICAL) != none);
mGestureMap.get(Gesture.SCROLL_VERTICAL) != none);
break;
}

Expand Down Expand Up @@ -1780,6 +1780,25 @@ public void run() {
}
});
}
/**
* Starts recording a fast, low quality video snapshot. Video will be written to the given file,
* so callers should ensure they have appropriate permissions to write to the file.
*
* @param file a file where the video will be saved
*/
public void takeVideoSnapshot(@NonNull File file) {
takeVideoSnapshot(file, null);
}

/**
* Starts recording a fast, low quality video snapshot. Video will be written to the given file,
* so callers should ensure they have appropriate permissions to write to the file.
*
* @param fileDescriptor a file descriptor where the video will be saved
*/
public void takeVideoSnapshot(@NonNull FileDescriptor fileDescriptor) {
takeVideoSnapshot(null, fileDescriptor);
}

/**
* Starts recording a fast, low quality video snapshot. Video will be written to the given file,
Expand All @@ -1790,9 +1809,15 @@ public void run() {
*
* @param file a file where the video will be saved
*/
public void takeVideoSnapshot(@NonNull File file) {
public void takeVideoSnapshot(@Nullable File file, @Nullable FileDescriptor fileDescriptor) {
VideoResult.Stub stub = new VideoResult.Stub();
mCameraEngine.takeVideoSnapshot(stub, file);
if (file != null) {
mCameraEngine.takeVideoSnapshot(stub, file, null);
} else if (fileDescriptor != null) {
mCameraEngine.takeVideoSnapshot(stub, null, fileDescriptor);
} else {
throw new IllegalStateException("file and fileDescriptor are both null.");
}
mUiHandler.post(new Runnable() {
@Override
public void run() {
Expand Down Expand Up @@ -1852,6 +1877,33 @@ public void onCameraError(@NonNull CameraException exception) {
takeVideo(file, fileDescriptor);
}

/**
* Starts recording a fast, low quality video snapshot. Video will be written to the given file,
* so callers should ensure they have appropriate permissions to write to the file.
* Recording will be automatically stopped after the given duration, overriding
* temporarily any duration limit set by {@link #setVideoMaxDuration(int)}.
*
* @param file a file where the video will be saved
* @param durationMillis recording max duration
*/
public void takeVideoSnapshot(@NonNull File file, int durationMillis) {
takeVideoSnapshot(file, null, durationMillis);
}

/**
* Starts recording a fast, low quality video snapshot. Video will be written to the given file,
* so callers should ensure they have appropriate permissions to write to the file.
* Recording will be automatically stopped after the given duration, overriding
* temporarily any duration limit set by {@link #setVideoMaxDuration(int)}.
*
* @param fileDescriptor a file descriptor where the video will be saved
* @param durationMillis recording max duration
*/
@SuppressWarnings("unused")
public void takeVideoSnapshot(@NonNull FileDescriptor fileDescriptor, int durationMillis) {
takeVideoSnapshot(null, fileDescriptor, durationMillis);
}

/**
* Starts recording a fast, low quality video snapshot. Video will be written to the given file,
* so callers should ensure they have appropriate permissions to write to the file.
Expand All @@ -1865,7 +1917,8 @@ public void onCameraError(@NonNull CameraException exception) {
* @param durationMillis recording max duration
*
*/
public void takeVideoSnapshot(@NonNull File file, int durationMillis) {
public void takeVideoSnapshot(@Nullable File file, @Nullable FileDescriptor fileDescriptor,
int durationMillis) {
final int old = getVideoMaxDuration();
addCameraListener(new CameraListener() {
@Override
Expand All @@ -1884,7 +1937,7 @@ public void onCameraError(@NonNull CameraException exception) {
}
});
setVideoMaxDuration(durationMillis);
takeVideoSnapshot(file);
takeVideoSnapshot(file,fileDescriptor);
}

// TODO: pauseVideo and resumeVideo? There is mediarecorder.pause(), but API 24...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,15 +352,15 @@ public final void setFacing(final @NonNull Facing facing) {
mFacing = facing;
getOrchestrator().scheduleStateful("facing", CameraState.ENGINE,
new Runnable() {
@Override
public void run() {
if (collectCameraInfo(facing)) {
restart();
} else {
mFacing = old;
}
}
});
@Override
public void run() {
if (collectCameraInfo(facing)) {
restart();
} else {
mFacing = old;
}
}
});
}
}

Expand Down Expand Up @@ -401,11 +401,11 @@ public final void setMode(@NonNull Mode mode) {
mMode = mode;
getOrchestrator().scheduleStateful("mode", CameraState.ENGINE,
new Runnable() {
@Override
public void run() {
restart();
}
});
@Override
public void run() {
restart();
}
});
}
}

Expand Down Expand Up @@ -508,20 +508,20 @@ public final boolean isTakingPicture() {
final boolean metering = mPictureMetering;
getOrchestrator().scheduleStateful("take picture", CameraState.BIND,
new Runnable() {
@Override
public void run() {
LOG.i("takePicture:", "running. isTakingPicture:", isTakingPicture());
if (isTakingPicture()) return;
if (mMode == Mode.VIDEO) {
throw new IllegalStateException("Can't take hq pictures while in VIDEO mode");
}
stub.isSnapshot = false;
stub.location = mLocation;
stub.facing = mFacing;
stub.format = mPictureFormat;
onTakePicture(stub, metering);
}
});
@Override
public void run() {
LOG.i("takePicture:", "running. isTakingPicture:", isTakingPicture());
if (isTakingPicture()) return;
if (mMode == Mode.VIDEO) {
throw new IllegalStateException("Can't take hq pictures while in VIDEO mode");
}
stub.isSnapshot = false;
stub.location = mLocation;
stub.facing = mFacing;
stub.format = mPictureFormat;
onTakePicture(stub, metering);
}
});
}

/**
Expand All @@ -535,20 +535,20 @@ public void run() {
final boolean metering = mPictureSnapshotMetering;
getOrchestrator().scheduleStateful("take picture snapshot", CameraState.BIND,
new Runnable() {
@Override
public void run() {
LOG.i("takePictureSnapshot:", "running. isTakingPicture:", isTakingPicture());
if (isTakingPicture()) return;
stub.location = mLocation;
stub.isSnapshot = true;
stub.facing = mFacing;
stub.format = PictureFormat.JPEG;
// Leave the other parameters to subclasses.
//noinspection ConstantConditions
AspectRatio ratio = AspectRatio.of(getPreviewSurfaceSize(Reference.OUTPUT));
onTakePictureSnapshot(stub, ratio, metering);
}
});
@Override
public void run() {
LOG.i("takePictureSnapshot:", "running. isTakingPicture:", isTakingPicture());
if (isTakingPicture()) return;
stub.location = mLocation;
stub.isSnapshot = true;
stub.facing = mFacing;
stub.format = PictureFormat.JPEG;
// Leave the other parameters to subclasses.
//noinspection ConstantConditions
AspectRatio ratio = AspectRatio.of(getPreviewSurfaceSize(Reference.OUTPUT));
onTakePictureSnapshot(stub, ratio, metering);
}
});
}

@Override
Expand Down Expand Up @@ -612,29 +612,36 @@ public void run() {
* @param file the output file
*/
@Override
public final void takeVideoSnapshot(@NonNull final VideoResult.Stub stub,
@NonNull final File file) {
public final void takeVideoSnapshot(final @NonNull VideoResult.Stub stub,
final @Nullable File file,
final @Nullable FileDescriptor fileDescriptor) {
getOrchestrator().scheduleStateful("take video snapshot", CameraState.BIND,
new Runnable() {
@Override
public void run() {
LOG.i("takeVideoSnapshot:", "running. isTakingVideo:", isTakingVideo());
stub.file = file;
stub.isSnapshot = true;
stub.videoCodec = mVideoCodec;
stub.audioCodec = mAudioCodec;
stub.location = mLocation;
stub.facing = mFacing;
stub.videoBitRate = mVideoBitRate;
stub.audioBitRate = mAudioBitRate;
stub.audio = mAudio;
stub.maxSize = mVideoMaxSize;
stub.maxDuration = mVideoMaxDuration;
//noinspection ConstantConditions
AspectRatio ratio = AspectRatio.of(getPreviewSurfaceSize(Reference.OUTPUT));
onTakeVideoSnapshot(stub, ratio);
}
});
@Override
public void run() {
LOG.i("takeVideoSnapshot:", "running. isTakingVideo:", isTakingVideo());
if (file != null) {
stub.file = file;
} else if (fileDescriptor != null) {
stub.fileDescriptor = fileDescriptor;
} else {
throw new IllegalStateException("file and fileDescriptor are both null.");
}
stub.isSnapshot = true;
stub.videoCodec = mVideoCodec;
stub.audioCodec = mAudioCodec;
stub.location = mLocation;
stub.facing = mFacing;
stub.videoBitRate = mVideoBitRate;
stub.audioBitRate = mAudioBitRate;
stub.audio = mAudio;
stub.maxSize = mVideoMaxSize;
stub.maxDuration = mVideoMaxDuration;
//noinspection ConstantConditions
AspectRatio ratio = AspectRatio.of(getPreviewSurfaceSize(Reference.OUTPUT));
onTakeVideoSnapshot(stub, ratio);
}
});
}

@Override
Expand Down Expand Up @@ -706,21 +713,21 @@ public final void onSurfaceChanged() {
LOG.i("onSurfaceChanged:", "Size is", getPreviewSurfaceSize(Reference.VIEW));
getOrchestrator().scheduleStateful("surface changed", CameraState.BIND,
new Runnable() {
@Override
public void run() {
// Compute a new camera preview size and apply.
Size newSize = computePreviewStreamSize();
if (newSize.equals(mPreviewStreamSize)) {
LOG.i("onSurfaceChanged:",
"The computed preview size is identical. No op.");
} else {
LOG.i("onSurfaceChanged:",
"Computed a new preview size. Calling onPreviewStreamSizeChanged().");
mPreviewStreamSize = newSize;
onPreviewStreamSizeChanged();
}
}
});
@Override
public void run() {
// Compute a new camera preview size and apply.
Size newSize = computePreviewStreamSize();
if (newSize.equals(mPreviewStreamSize)) {
LOG.i("onSurfaceChanged:",
"The computed preview size is identical. No op.");
} else {
LOG.i("onSurfaceChanged:",
"Computed a new preview size. Calling onPreviewStreamSizeChanged().");
mPreviewStreamSize = newSize;
onPreviewStreamSizeChanged();
}
}
});
}

/**
Expand Down
Loading