You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android MediaRecorder启动失败:SYSTEM_CAMERA权限被拒

Xamarin.Forms安卓视频录制:MediaRecorder.start()失败,提示SYSTEM_CAMERA权限被拒

问题核心分析

  1. SYSTEM_CAMERA权限无效:该权限为系统级权限,第三方应用无法申请获取,你在清单中添加此权限不仅无用,还可能干扰系统权限检查逻辑,导致错误日志误导。
  2. 缺少摄像头预览Surface:Android系统要求MediaRecorder录制视频时必须绑定一个摄像头预览Surface(即使你不需要显示预览),未实现此步骤会直接导致start()调用失败。

解决步骤

1. 清理不必要的权限

修改AndroidManifest.xml,移除android.permission.SYSTEM_CAMERA权限,保留必要权限:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-feature android:name="android.hardware.camera.autofocus" />
  • Android 11及以上版本,若仅保存视频到应用私有目录,无需添加WRITE_EXTERNAL_STORAGE权限。

2. 实现摄像头预览Surface(推荐使用CameraX)

Xamarin.Forms本身无内置摄像头预览控件,需通过DependencyService调用Android原生实现,推荐使用CameraX(Google官方推出的相机API,适配性更强):

步骤1:添加CameraX NuGet包

在Android项目中安装以下NuGet包:

  • Xamarin.AndroidX.Camera.Core
  • Xamarin.AndroidX.Camera.Camera2
  • Xamarin.AndroidX.Camera.Lifecycle
  • Xamarin.AndroidX.Camera.View

步骤2:共享项目定义接口

public interface ICameraRecorderService
{
    void InitializePreview(Android.Content.Context context);
    Task StartRecording(string outputFilePath);
    void StopRecording();
}

步骤3:Android项目实现接口

using Android.App;
using Android.Content;
using Android.OS;
using Android.Views;
using AndroidX.Camera.Core;
using AndroidX.Camera.Lifecycle;
using AndroidX.Camera.View;
using AndroidX.Lifecycle;
using Xamarin.Forms;

[assembly: Dependency(typeof(AndroidCameraRecorderService))]
namespace YourAppNamespace.Droid
{
    public class AndroidCameraRecorderService : ICameraRecorderService, ILifecycleObserver
    {
        private PreviewView _cameraPreviewView;
        private VideoCapture _videoCapture;
        private Context _androidContext;

        public void InitializePreview(Context context)
        {
            _androidContext = context;
            var mainActivity = context as Activity;

            // 创建预览视图并添加到页面容器
            _cameraPreviewView = new PreviewView(mainActivity)
            {
                LayoutParameters = new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MatchParent,
                    ViewGroup.LayoutParams.MatchParent)
            };
            mainActivity.AddContentView(_cameraPreviewView, _cameraPreviewView.LayoutParameters);

            // 初始化CameraX
            var cameraProviderFuture = ProcessCameraProvider.GetInstance(mainActivity);
            cameraProviderFuture.AddListener(() =>
            {
                var cameraProvider = cameraProviderFuture.Get();
                BindCameraUseCases(cameraProvider);
            }, ContextCompat.MainExecutor(mainActivity));
        }

        private void BindCameraUseCases(ProcessCameraProvider cameraProvider)
        {
            // 配置预览用例
            var preview = new Preview.Builder().Build();
            preview.SetSurfaceProvider(_cameraPreviewView.SurfaceProvider);

            // 配置视频录制用例
            var videoCaptureConfig = new VideoCapture.Builder()
                .SetTargetRotation(_cameraPreviewView.Display.Rotation)
                .Build();
            _videoCapture = videoCaptureConfig;

            // 绑定用例到相机生命周期
            var cameraSelector = CameraSelector.DefaultBackCamera;
            try
            {
                cameraProvider.UnbindAll();
                cameraProvider.BindToLifecycle(
                    (LifecycleOwner)_androidContext,
                    cameraSelector,
                    preview,
                    _videoCapture);
            }
            catch (Exception ex)
            {
                // 处理相机绑定异常,可输出日志调试
            }
        }

        public async Task StartRecording(string outputFilePath)
        {
            if (_videoCapture == null) return;

            var outputFile = new Java.IO.File(outputFilePath);
            var outputOptions = new VideoCapture.OutputFileOptions.Builder(outputFile).Build();

            await _videoCapture.RecordAsync(
                outputOptions,
                ContextCompat.MainExecutor(_androidContext),
                new VideoSaveCallback());
        }

        public void StopRecording()
        {
            _videoCapture?.StopRecording();
        }

        // 视频保存回调类
        private class VideoSaveCallback : VideoCapture.OnVideoSavedCallback
        {
            public override void OnVideoSaved(VideoCapture.OutputFileResults outputFileResults)
            {
                // 视频保存成功逻辑
            }

            public override void OnError(int videoCaptureError, string message, Java.Lang.Exception cause)
            {
                // 视频保存失败逻辑,可输出错误信息调试
            }
        }
    }
}

步骤4:Xamarin.Forms页面调用服务

protected override void OnAppearing()
{
    base.OnAppearing();
    // 初始化预览
    var cameraService = DependencyService.Get<ICameraRecorderService>();
    cameraService.InitializePreview(Android.App.Application.Context);
}

// 开始录制按钮点击事件
private async void StartRecordBtn_Clicked(object sender, EventArgs e)
{
    // 视频保存路径(应用私有目录)
    var outputPath = Path.Combine(
        Android.OS.Environment.ExternalStorageDirectory.AbsolutePath,
        $"record_{DateTime.Now:yyyyMMddHHmmss}.mp4");
    
    var cameraService = DependencyService.Get<ICameraRecorderService>();
    await cameraService.StartRecording(outputPath);
}

// 停止录制按钮点击事件
private void StopRecordBtn_Clicked(object sender, EventArgs e)
{
    var cameraService = DependencyService.Get<ICameraRecorderService>();
    cameraService.StopRecording();
}

关键说明

  • 若你坚持使用旧的Camera+MediaRecorderAPI,同样需要创建SurfaceView并将摄像头预览输出到该Surface,再将Surface设置给MediaRecorder的SetPreviewDisplay()方法,但CameraX的适配性和稳定性远优于旧API。
  • 权限申请需确保在运行时动态申请CAMERA和RECORD_AUDIO权限,即使清单中已添加,Android 6.0及以上版本仍需动态授权。

内容的提问来源于stack exchange,提问作者TryingAgain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 15:07:02