[英]CameraX Video capture not recording on physical device
你好。 我對 CameraX VideoCapture 用例有疑問。 我正在使用以下版本的庫:
implementation "androidx.camera:camera-video:1.1.0-alpha11"
我使用的用例如下:
MediaModule.kt(希爾特)
@Module @InstallIn(SingletonComponent::class) class VideoCaptureModule { @Provides fun provideMediaExecutor(): ExecutorService = Executors.newSingleThreadExecutor() @Provides fun provideCameraProviderFuture( @ApplicationContext ctx: Context ): ListenableFuture<ProcessCameraProvider> = ProcessCameraProvider.getInstance(ctx) @Provides fun provideCameraPreview(): Preview = Preview.Builder().build() @Provides fun provideCameraQualitySelector(): QualitySelector = QualitySelector.firstTry(QualitySelector.QUALITY_UHD).thenTry(QualitySelector.QUALITY_FHD).thenTry(QualitySelector.QUALITY_HD).finallyTry(QualitySelector.QUALITY_SD, QualitySelector.FALLBACK_STRATEGY_LOWER) @Provides fun provideCameraRecorder( cameraExecutor: ExecutorService, qualitySelector: QualitySelector ): Recorder = Recorder.Builder().setExecutor(cameraExecutor).setQualitySelector(qualitySelector).build() @Provides fun provideVideoCapture(recorder: Recorder): VideoCapture<Recorder> = VideoCapture.withOutput(recorder) }
記錄ViewModel(AndroidViewModel)
@HiltViewModel class RecordVideoViewModel @Inject constructor( private val preview: Preview, private val app: Application, private val cameraExecutor: ExecutorService, private val videoCapture: VideoCapture<Recorder>, private val cameraProviderFuture: ListenableFuture<ProcessCameraProvider> ): AndroidViewModel(app) { private var camera: Camera? = null private var activeRecording: ActiveRecording? = null private var cameraProvider: ProcessCameraProvider? = null private val _videoRecordingStatus = MutableLiveData<VideoRecordingStatus>() val videoRecordingStatus = _videoRecordingStatus as LiveData<VideoRecordingStatus> fun startCamera( waitForReload: Boolean, lifecycleOwner: LifecycleOwner, cameraSelector: CameraSelector, surfaceProvider: Preview.SurfaceProvider ) { cameraProviderFuture.addListener({ cameraProvider = cameraProviderFuture.get() preview.setSurfaceProvider(surfaceProvider) try { viewModelScope.launch { cameraProvider?.unbindAll() if (waitForReload) delay(CAMERA_WAITING_TIME) camera = cameraProvider?.bindToLifecycle(lifecycleOwner, cameraSelector, preview, videoCapture) _torchAvailable.value = camera?.cameraInfo?.hasFlashUnit() == true } } catch (t: Throwable) { FirebaseCrashlytics.getInstance().log(t.localizedMessage.orEmpty()) } }, ContextCompat.getMainExecutor(app)) } fun stopCamera() { cameraProvider?.unbindAll() cameraProvider = null camera = null torchEnabled = false try { activeRecording?.stop() } catch (t: IllegalStateException) { // Ignore, active recording already stopped. } activeRecording = null } fun startRecording() { val name = "${app.getString(R.string.app_name)}-${Date().time}" val contentValues = ContentValues().apply { put(MediaStore.Video.Media.DISPLAY_NAME, name) } val mediaStoreOutput = MediaStoreOutputOptions.Builder( app.contentResolver, MediaStore.Video.Media.EXTERNAL_CONTENT_URI ).setContentValues(contentValues).build() activeRecording = videoCapture.output.prepareRecording(app, mediaStoreOutput).withAudioEnabled().withEventListener(cameraExecutor, { event -> when (event) { is VideoRecordEvent.Start -> _videoRecordingStatus.postValue(VideoRecordingStatus.Recording) is VideoRecordEvent.Finalize -> _videoRecordingStatus.postValue( event.cause?.run(VideoRecordingStatus::Failed)?: VideoRecordingStatus.Succeeded(event.outputResults.outputUri.toString()) ) } }).start() } fun stopRecording() { activeRecording?.stop() } override fun onCleared() { super.onCleared() stopCamera() cameraExecutor.shutdown() } sealed class VideoRecordingStatus { object Recording: VideoRecordingStatus() data class Failed(val t: Throwable?): VideoRecordingStatus() data class Succeeded(val uri: String): VideoRecordingStatus() } }
在模擬器中一切正常。 我可以毫無問題地錄制和保存視頻,但是,當我在運行 Android 11 的真實設備(三星 Galaxy S20 Ultra 5G)中運行該應用程序時,出現以下崩潰:
2021-12-15 11:56:00.271 3758-3789/? E/DatabaseUtils: Writing exception to parcel
java.lang.IllegalArgumentException: MIME type application/octet-stream cannot be inserted into content://media/external/video/media; expected MIME type under video/*
at com.android.providers.media.MediaProvider.ensureFileColumns(MediaProvider.java:3301)
at com.android.providers.media.MediaProvider.ensureUniqueFileColumns(MediaProvider.java:3072)
at com.android.providers.media.MediaProvider.insertFile(MediaProvider.java:3826)
at com.android.providers.media.MediaProvider.insertInternal(MediaProvider.java:4386)
at com.android.providers.media.MediaProvider.insert(MediaProvider.java:4110)
at android.content.ContentProvider$Transport.insert(ContentProvider.java:336)
at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:167)
at android.os.Binder.execTransactInternal(Binder.java:1190)
at android.os.Binder.execTransact(Binder.java:1159)
2021-12-15 11:56:00.275 19974-4965/? E/SequentialExecutor: Exception while executing runnable androidx.camera.core.impl.utils.executor.SequentialExecutor$1@9d3f8d3
java.lang.IllegalArgumentException: MIME type application/octet-stream cannot be inserted into content://media/external/video/media; expected MIME type under video/*
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:172)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142)
at android.content.ContentProviderProxy.insert(ContentProviderNative.java:549)
at android.content.ContentResolver.insert(ContentResolver.java:2159)
at android.content.ContentResolver.insert(ContentResolver.java:2121)
at androidx.camera.video.Recorder$RecordingRecord.lambda$initializeRecording$1(Recorder.java:2247)
at androidx.camera.video.Recorder$RecordingRecord$$ExternalSyntheticLambda1.get(Unknown Source:4)
at androidx.camera.video.Recorder$RecordingRecord.performOneTimeMediaMuxerCreation(Recorder.java:2394)
at androidx.camera.video.Recorder.setupAndStartMediaMuxer(Recorder.java:1298)
at androidx.camera.video.Recorder$2.onEncodedData(Recorder.java:1436)
at androidx.camera.video.internal.encoder.EncoderImpl$MediaCodecCallback.lambda$sendEncodedData$3(EncoderImpl.java:937)
at androidx.camera.video.internal.encoder.EncoderImpl$MediaCodecCallback$$ExternalSyntheticLambda6.run(Unknown Source:4)
at androidx.camera.core.impl.utils.executor.SequentialExecutor$1.run(SequentialExecutor.java:111)
at androidx.camera.core.impl.utils.executor.SequentialExecutor$QueueWorker.workOnQueue(SequentialExecutor.java:231)
at androidx.camera.core.impl.utils.executor.SequentialExecutor$QueueWorker.run(SequentialExecutor.java:173)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
2021-12-15 11:56:00.328 19974-4965/? E/AndroidRuntime: FATAL EXCEPTION: pool-40-thread-1
Process: com.pt.wshhp, PID: 19974
java.lang.AssertionError: One-time media muxer creation has already occurred for recording RecordingRecord{getOutputOptions=MediaStoreOutputOptions{contentResolver=android.app.ContextImpl$ApplicationContentResolver@e6e1f6b, collectionUri=content://media/external/video/media, contentValues=_display_name=Worldstar-1639590959896, fileSizeLimit=0}, getCallbackExecutor=java.util.concurrent.Executors$FinalizableDelegatedExecutorService@850110f, getEventListener=com.pt.wshhp.viewmodels.RecordVideoViewModel$$ExternalSyntheticLambda0@fe4059c, hasAudioEnabled=true, getRecordingId=1}
at androidx.camera.video.Recorder$RecordingRecord.performOneTimeMediaMuxerCreation(Recorder.java:2391)
at androidx.camera.video.Recorder.setupAndStartMediaMuxer(Recorder.java:1298)
at androidx.camera.video.Recorder$2.onEncodedData(Recorder.java:1436)
at androidx.camera.video.internal.encoder.EncoderImpl$MediaCodecCallback.lambda$sendEncodedData$3(EncoderImpl.java:937)
at androidx.camera.video.internal.encoder.EncoderImpl$MediaCodecCallback$$ExternalSyntheticLambda6.run(Unknown Source:4)
at androidx.camera.core.impl.utils.executor.SequentialExecutor$1.run(SequentialExecutor.java:111)
at androidx.camera.core.impl.utils.executor.SequentialExecutor$QueueWorker.workOnQueue(SequentialExecutor.java:231)
at androidx.camera.core.impl.utils.executor.SequentialExecutor$QueueWorker.run(SequentialExecutor.java:173)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
2021-12-15 11:56:00.395 19974-5309/? E/AndroidRuntime: FATAL EXCEPTION: pool-40-thread-2
Process: com.pt.wshhp, PID: 19974
java.lang.AssertionError: One-time media muxer creation has already occurred for recording RecordingRecord{getOutputOptions=MediaStoreOutputOptions{contentResolver=android.app.ContextImpl$ApplicationContentResolver@e6e1f6b, collectionUri=content://media/external/video/media, contentValues=_display_name=Worldstar-1639590959896, fileSizeLimit=0}, getCallbackExecutor=java.util.concurrent.Executors$FinalizableDelegatedExecutorService@850110f, getEventListener=com.pt.wshhp.viewmodels.RecordVideoViewModel$$ExternalSyntheticLambda0@fe4059c, hasAudioEnabled=true, getRecordingId=1}
at androidx.camera.video.Recorder$RecordingRecord.performOneTimeMediaMuxerCreation(Recorder.java:2391)
at androidx.camera.video.Recorder.setupAndStartMediaMuxer(Recorder.java:1298)
at androidx.camera.video.Recorder$2.onEncodedData(Recorder.java:1436)
at androidx.camera.video.internal.encoder.EncoderImpl$MediaCodecCallback.lambda$sendEncodedData$3(EncoderImpl.java:937)
at androidx.camera.video.internal.encoder.EncoderImpl$MediaCodecCallback$$ExternalSyntheticLambda6.run(Unknown Source:4)
at androidx.camera.core.impl.utils.executor.SequentialExecutor$1.run(SequentialExecutor.java:111)
at androidx.camera.core.impl.utils.executor.SequentialExecutor$QueueWorker.workOnQueue(SequentialExecutor.java:231)
at androidx.camera.core.impl.utils.executor.SequentialExecutor$QueueWorker.run(SequentialExecutor.java:173)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
2021-12-15 11:56:00.465 19974-5313/? E/AndroidRuntime: FATAL EXCEPTION: pool-40-thread-3
Process: com.pt.wshhp, PID: 19974
java.lang.AssertionError: One-time media muxer creation has already occurred for recording RecordingRecord{getOutputOptions=MediaStoreOutputOptions{contentResolver=android.app.ContextImpl$ApplicationContentResolver@e6e1f6b, collectionUri=content://media/external/video/media, contentValues=_display_name=Worldstar-1639590959896, fileSizeLimit=0}, getCallbackExecutor=java.util.concurrent.Executors$FinalizableDelegatedExecutorService@850110f, getEventListener=com.pt.wshhp.viewmodels.RecordVideoViewModel$$ExternalSyntheticLambda0@fe4059c, hasAudioEnabled=true, getRecordingId=1}
at androidx.camera.video.Recorder$RecordingRecord.performOneTimeMediaMuxerCreation(Recorder.java:2391)
at androidx.camera.video.Recorder.setupAndStartMediaMuxer(Recorder.java:1298)
at androidx.camera.video.Recorder$2.onEncodedData(Recorder.java:1436)
at androidx.camera.video.internal.encoder.EncoderImpl$MediaCodecCallback.lambda$sendEncodedData$3(EncoderImpl.java:937)
at androidx.camera.video.internal.encoder.EncoderImpl$MediaCodecCallback$$ExternalSyntheticLambda6.run(Unknown Source:4)
at androidx.camera.core.impl.utils.executor.SequentialExecutor$1.run(SequentialExecutor.java:111)
at androidx.camera.core.impl.utils.executor.SequentialExecutor$QueueWorker.workOnQueue(SequentialExecutor.java:231)
at androidx.camera.core.impl.utils.executor.SequentialExecutor$QueueWorker.run(SequentialExecutor.java:173)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
這些是崩潰的亮點:
MIME type application/octet-stream cannot be inserted into content://media/external/video/media; expected MIME type under video/*
java.lang.AssertionError: One-time media muxer creation has already occurred for recording RecordingRecord{getOutputOptions=MediaStoreOutputOptions{contentResolver=android.app.ContextImpl$ApplicationContentResolver@e6e1f6b, collectionUri=content://media/external/video/media, contentValues=_display_name=VideoRecording-1639590959896, fileSizeLimit=0}, getCallbackExecutor=java.util.concurrent.Executors$FinalizableDelegatedExecutorService@850110f, getEventListener=com.videoapp.viewmodels.RecordVideoViewModel$$ExternalSyntheticLambda0@fe4059c, hasAudioEnabled=true, getRecordingId=1}
似乎正在嘗試錄制兩次到視頻文件源。 有沒有人遇到過這個? 在模擬器中錄制得很好,但在真實設備中卻崩潰了。
嘿,我真的不知道你的問題,但我會發給你我的 CameraX 在我的三星 A10s 中工作得非常好。 我使用的是片段而不是活動,但它是一樣的,你只改變上下文。
import android.Manifest
import android.content.ContentValues
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.video.*
import androidx.camera.video.VideoCapture
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.PermissionChecker
import androidx.fragment.app.Fragment
import com.example.splashscreenkotlin.R
import com.example.splashscreenkotlin.databinding.FragmentCameraBinding
import java.lang.Exception
import java.nio.ByteBuffer
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
typealias LumaListener = (luma: Double) -> Unit
class CameraFragment : Fragment() {
private var binding: FragmentCameraBinding? = null
private val _binding get() = binding!!
private var imageCapture: ImageCapture? = null
private var videoCapture: VideoCapture<Recorder>? = null
private var recording: Recording? = null
private lateinit var cameraExecutorService: ExecutorService
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentCameraBinding.inflate(layoutInflater,container,false)
return _binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Request camera permissions
if (allPermissionGranted()){
startCamera()
}else {
ActivityCompat.requestPermissions(requireActivity(), REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
}
// Set up the listeners for take photo and video capture buttons
_binding.imageCaptureButton.setOnClickListener { takePhoto() }
_binding.videoCaptureButton.setOnClickListener { captureVideo() }
cameraExecutorService = Executors.newSingleThreadExecutor()
}
private fun takePhoto(){
//First, get a reference to the ImageCapture use case.
// If the use case is null, exit out of the function.
// This will be null If we tap the photo button before image capture is set up.
// Without the return statement, the app would crash if it was null.
val imageCapture = imageCapture ?: return
//create a MediaStore content value to hold the image.
// Use a timestamp so the display name in MediaStore will be unique.
val name = SimpleDateFormat(FILENAME_FORMAT, Locale.getDefault()).format(System.currentTimeMillis())
//This class is used to store a set of values that the ContentResolver can process.
//ContentResolver class provides applications access to the content model.
val contentValues = ContentValues().apply {
//MediaStore is contract between the media provider and applications.
// Contains definitions for the supported URIs and columns.
put(MediaStore.MediaColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P){
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/SplashApp-Images")
}
}
//Create an OutputFileOptions object.
// This object is where we can specify things about how we want our output to be.
// We want the output saved in the MediaStore so other apps could display it,
// so add our MediaStore entry.
val outputOptions = ImageCapture.OutputFileOptions
.Builder(requireActivity().contentResolver,MediaStore.Images.Media.EXTERNAL_CONTENT_URI,contentValues)
.build()
//Call takePicture() on the imageCapture object.
// Pass in outputOptions, the executor, and a callback for when the image is saved
imageCapture.takePicture(outputOptions,ContextCompat.getMainExecutor(requireContext()),
object : ImageCapture.OnImageSavedCallback{
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
val msg = "Photo capture succeeded: ${outputFileResults.savedUri}"
Toast.makeText(requireContext(),msg,Toast.LENGTH_SHORT).show()
Log.d(TAG,msg)
}
//In the case that the image capture fails or saving the image capture fails,
// add in an error case to log that it failed.
override fun onError(exception: ImageCaptureException) {
Log.e(TAG,"Photo Captured failed: ${exception.message}",exception)
}
})
}
private fun captureVideo(){
//Check if the VideoCapture use case has been created: if not, do nothing.7
val videoCapture = videoCapture ?: return
//Disable the UI until the request action is completed by CameraX
_binding.videoCaptureButton.isEnabled = false
//If there is an active recording in progress, stop it and release the current recording.
// We will be notified when the captured video file is ready to be used by our application.
val currentRecording = recording
if (currentRecording != null){
currentRecording.stop()
recording = null
return
}
// we create our intended MediaStore video content object,
// with system timestamp as the display name(so we could capture multiple videos).
val name = SimpleDateFormat(FILENAME_FORMAT, Locale.getDefault()).format(System.currentTimeMillis())
//ContentValue class is used to store a set of values that the ContentResolver can process.
// ContentResolver class provides applications access to the content model.
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME,name)
put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P ) {
put(MediaStore.Video.Media.RELATIVE_PATH,"Movies/SplashApp-Videos")
}
}
//Create a MediaStoreOutputOptions.Builder with the external content option.
//MediaStoreOutputOptions class providing options for storing output to MediaStore.
val mediaStoreOutputOptions = MediaStoreOutputOptions.Builder(
requireActivity().contentResolver,MediaStore.Video.Media.EXTERNAL_CONTENT_URI
)//Set the created video contentValues to the MediaStoreOutputOptions.Builder, and build our MediaStoreOutputOptions instance.
.setContentValues(contentValues).build()
//Configure the output option to the Recorder of VideoCapture<Recorder> and enable audio recording
recording = videoCapture.output
.prepareRecording(requireContext(),mediaStoreOutputOptions).apply {
if (PermissionChecker.checkSelfPermission(requireContext(),Manifest.permission.RECORD_AUDIO)
== PermissionChecker.PERMISSION_GRANTED) {
withAudioEnabled()
}
}//Start this new recording, and register a lambda VideoRecordEvent listener.
.start(ContextCompat.getMainExecutor(requireContext())){ recordEvent ->
when(recordEvent) {
//When the request recording is started by the camera device,
// toggle the "Start Capture" button text to say "Stop Capture".
is VideoRecordEvent.Start -> {
_binding.videoCaptureButton.apply {
text = getString(R.string.stop_capture)
isEnabled = true
}
}
//When the active recording is complete, notify the user with a toast,
// and toggle the "Stop Capture" button back to "Start Capture", and re-enable it
is VideoRecordEvent.Finalize -> {
_binding.videoCaptureButton.apply {
text = getString(R.string.start_capture)
isEnabled = true
}
if(!recordEvent.hasError()){
val msg = "Video Capture Succeed: " + "${recordEvent.outputResults.outputUri}"
Toast.makeText(requireContext(),msg,Toast.LENGTH_LONG).show()
Log.d(TAG,msg)
}else {
recording?.close()
recording = null
Log.e(TAG,"Video Capture Ends with an Error: " + "${recordEvent.error}")
}
}
}
}
}
private fun startCamera(){
// User ProcessCameraProvider Bcs A singleton which can be used to bind the lifecycle of cameras
// to any LifecycleOwner within an application's process.
// Retrieve it with getInstance
// This eliminates the task of opening and closing the camera since CameraX is lifecycle-aware.
val cameraFutureProvider = ProcessCameraProvider.getInstance(requireContext())
//Registers a listener to be run on the given executor.
// The listener will run when the Future's computation is complete or,
// if the computation is already complete, immediately.
cameraFutureProvider.addListener({
// Used to bind the lifecycle of cameras to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraFutureProvider.get()
// Preview use case that provides a camera preview stream for displaying on-screen.
val preview = Preview.Builder().build().also {
// Decide how the surface we using
// get a surface provider from viewfinder, and then set it on the preview.
it.setSurfaceProvider(_binding.viewFinder.surfaceProvider)
}
val recorder = Recorder.Builder()
.setQualitySelector(QualitySelector.from(Quality.HIGHEST,
//Add a FallbackStrategy to the existing QualitySelector creation.
// This allows CameraX to pick up a supported resolution
// if the required Quality.HIGHEST is not supportable with the imageCapture use case.
FallbackStrategy.higherQualityOrLowerThan(Quality.SD)))
.build()
videoCapture = VideoCapture.withOutput(recorder)
imageCapture = ImageCapture.Builder().build()
/*
//ImageAnalysis use case providing CPU accessible images for an app to perform image analysis on.
//ImageAnalysis acquires images from the camera via an ImageReader.
// Each image is provided to an ImageAnalysis.Analyzer function which can be implemented by application code
//where it can access image data for application analysis via an ImageProxy.
val imageAnalyzer = ImageAnalysis.Builder().build().also {
it.setAnalyzer(cameraExecutorService,LuminosityAnalyzer { luma ->
Log.d(TAG,"The Average luminosity: $luma")
})
}
*/
// Select back camera as a default
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
// Unbind use case before rebinding
// This will initiate a close of every currently open camera.
cameraProvider.unbindAll()
// Bind use cases to camera
cameraProvider.bindToLifecycle(
requireActivity(),cameraSelector,preview,imageCapture,videoCapture
)
}catch (e:Exception){
Log.e(TAG, "Use case binding failed",e)
}
}// This returns an Executor that runs on the main thread.
,ContextCompat.getMainExecutor(requireContext()))
}
//The analyzer logs the average luminosity of the image.
// To create an analyzer, we override the analyze function in a class that implements the ImageAnalysis.Analyzer interface.
private class LuminosityAnalyzer(private val listener: LumaListener) : ImageAnalysis.Analyzer{
private fun ByteBuffer.toByteArray(): ByteArray{
rewind() //Rewind the Buffer to zero
val data = ByteArray(remaining())
get(data) //Copy the buffer into a byte array
return data //Return byte array
}
override fun analyze(image: ImageProxy) {
val buffer = image.planes[0].buffer
val data = buffer.toByteArray()
val pixels = data.map { it.toInt() and 0xFF }
val luma = pixels.average()
listener(luma)
//The application is responsible for calling ImageProxy.close() to close the image.
// Failing to close the image will cause future images to be stalled or dropped depending on the backpressure strategy.
image.close()
}
}
// Send Permission To access to camera
private fun allPermissionGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(requireContext(),it) ==
PackageManager.PERMISSION_GRANTED
}
//Callback for the result from requesting permissions.
// This method is invoked for every call on requestPermissions(String[], int).
@Deprecated("Deprecated in Java")
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == REQUEST_CODE_PERMISSIONS){
if (allPermissionGranted()){
startCamera()
}else {
Toast.makeText(requireContext(),"Permission not granted by the user.",Toast.LENGTH_SHORT).show()
activity?.finish()
}
}
}
override fun onDestroyView() {
super.onDestroyView()
binding = null
cameraExecutorService.shutdown()
}
companion object {
private const val TAG = "CAMERAX"
private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SS"
private const val REQUEST_CODE_PERMISSIONS = 10
private val REQUIRED_PERMISSIONS =
mutableListOf(
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
).apply {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P){
add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
}.toTypedArray()
}
}
對於我的 FragmentLayout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.CameraFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:id="@+id/camera_container"
android:background="@android:color/black"
android:layout_height="match_parent">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:id="@+id/image_capture_button"
android:layout_width="110dp"
android:layout_height="110dp"
android:layout_marginBottom="50dp"
android:layout_marginEnd="50dp"
android:elevation="2dp"
android:text="@string/take_photo"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintEnd_toStartOf="@id/vertical_centerline" />
<Button
android:id="@+id/video_capture_button"
android:layout_width="110dp"
android:layout_height="110dp"
android:layout_marginBottom="50dp"
android:layout_marginStart="50dp"
android:elevation="2dp"
android:text="@string/start_capture"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/vertical_centerline" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/vertical_centerline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent=".50" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
順便說一句,與 bottomNavigation 一起工作很棒 :) 即使我更改片段,我的相機片段也不會崩潰
我在華為p30和華為p10有同樣的問題。我嘗試將相機切換到前置並且它可以工作,但是當我切換到后置攝像頭時,它
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.