【发布时间】:2022-04-12 02:41:53
【问题描述】:
你好。我对 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) } -
记录视图模型(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 的真实设备(Samsung 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}
这是我迄今为止尝试过的(没有成功):
- 在 ViewModel 中从 MediaModule.kt 创建对象,而不是注入它们
- 使用 Activity 上下文而不是 Application 对象
- 发生此崩溃时停止摄像头并重新启动(使用 try-catch)
似乎正在尝试向视频文件源录制两次。有没有人遇到过这个问题?在模拟器中录制很好,但在真实设备中却崩溃了。
【问题讨论】:
标签: android kotlin video android-camera android-camerax