使用 xamarin 表单的应用程序内的录像机

Video Recorder inside a app using xamarin forms

如何使用 xamarin 表单在应用程序内实现录像机?

您可以使用 Xamarin.Plugin.Media。如果它对您来说不够灵活,那么您需要使用本机 API 来实现录像机。在这种情况下,您可能根本不应该做 Xamarin.Forms,但如果您坚持这样做。

你想达到如下GIF效果吗(超过2M,所以不能上传)?

https://imgur.com/a/kpMl2ed

我通过自定义渲染在 android 中实现了它。这是代码。

首先,我们应该MediaRecorder录制视频。

   public void startRecord(SurfaceView surfaceView)
    {

        Device.BeginInvokeOnMainThread(() => {

            string path = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath + "/test.mp4";
            recorder = new MediaRecorder();

            //If you want to rotate the video screen, you can use following code
            //Camera camera = Camera.Open();
            //Camera.Parameters parameters = camera.GetParameters();

            //parameters.SetPreviewSize(640, 480);
            //parameters.SetPictureSize(640, 480);
            //camera.SetParameters(parameters);
            //camera.SetDisplayOrientation(90);
            //camera.Unlock();
            //recorder.SetCamera(camera); 

                recorder.SetVideoSource(VideoSource.Camera);
                recorder.SetAudioSource(AudioSource.Mic);
                recorder.SetOutputFormat(OutputFormat.Default);
                recorder.SetVideoEncoder(VideoEncoder.Default);
                recorder.SetAudioEncoder(AudioEncoder.Default);
                recorder.SetOutputFile(path);
                recorder.SetPreviewDisplay(surfaceView.Holder.Surface);
                recorder.Prepare();
                recorder.Start();

        });

    }

如果你想在应用程序中实现录制视频,你应该使用自定义渲染器来查看。使用 SurfaceView 查看全部 view.First 的相机。请创建一个 CameraPreview

    public sealed class CameraPreview : ViewGroup, ISurfaceHolderCallback
{
    public SurfaceView surfaceView;
    ISurfaceHolder holder;
    Camera.Size previewSize;
    IList<Camera.Size> supportedPreviewSizes;
    Camera camera;
    IWindowManager windowManager;
    MediaRecorder recorder;

    public bool IsPreviewing { get; set; }

    public Camera Preview {
        get { return camera; }
        set {
            camera = value;
            if (camera != null) {
                supportedPreviewSizes = Preview.GetParameters().SupportedPreviewSizes;
                RequestLayout();
            }
        }
    }

    public CameraPreview (Context context)
        : base (context)
    {
        surfaceView = new SurfaceView (context);
        AddView (surfaceView);

        windowManager = Context.GetSystemService (Context.WindowService).JavaCast<IWindowManager>();

        IsPreviewing = false;
        holder = surfaceView.Holder;
        holder.AddCallback (this);


        MessagingCenter.Subscribe<string>("111", "Hi", (expense) =>
        {
            startRecord(surfaceView);
        });

        MessagingCenter.Subscribe<string>("1112", "Hi2", (expense) =>
        {
            stopRecord(surfaceView);
        });


    }

    private void stopRecord(SurfaceView surfaceView)
    {
        recorder.Stop();
        recorder.Release();
    }

    public void startRecord(SurfaceView surfaceView)
    {

        Device.BeginInvokeOnMainThread(() => {

            string path = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath + "/test.mp4";
            recorder = new MediaRecorder();

            //If you want to rotate the video screen, you can use following code
            //Camera camera = Camera.Open();
            //Camera.Parameters parameters = camera.GetParameters();

            //parameters.SetPreviewSize(640, 480);
            //parameters.SetPictureSize(640, 480);
            //camera.SetParameters(parameters);
            //camera.SetDisplayOrientation(90);
            //camera.Unlock();
            //recorder.SetCamera(camera); 

                recorder.SetVideoSource(VideoSource.Camera);
                recorder.SetAudioSource(AudioSource.Mic);
                recorder.SetOutputFormat(OutputFormat.Default);
                recorder.SetVideoEncoder(VideoEncoder.Default);
                recorder.SetAudioEncoder(AudioEncoder.Default);
                recorder.SetOutputFile(path);
                recorder.SetPreviewDisplay(surfaceView.Holder.Surface);
                recorder.Prepare();
                recorder.Start();

        });




    }


    protected override void OnMeasure (int widthMeasureSpec, int heightMeasureSpec)
    {
        int width = ResolveSize (SuggestedMinimumWidth, widthMeasureSpec);
        int height = ResolveSize (SuggestedMinimumHeight, heightMeasureSpec);
        SetMeasuredDimension (width, height);

        if (supportedPreviewSizes != null) {
            previewSize = GetOptimalPreviewSize (supportedPreviewSizes, width, height);
        }
    }

    protected override void OnLayout (bool changed, int l, int t, int r, int b)
    {
        var msw = MeasureSpec.MakeMeasureSpec (r - l, MeasureSpecMode.Exactly);
        var msh = MeasureSpec.MakeMeasureSpec (b - t, MeasureSpecMode.Exactly);

        surfaceView.Measure (msw, msh);
        surfaceView.Layout (0, 0, r - l, b - t);
    }

    public void SurfaceCreated (ISurfaceHolder holder)
    {
        try {
            if (Preview != null) {
                Preview.SetPreviewDisplay (holder);
             }
        } catch (Exception ex) {
            System.Diagnostics.Debug.WriteLine (@"          ERROR: ", ex.Message);
        }
    }




    public void SurfaceDestroyed (ISurfaceHolder holder)
    {
        if (Preview != null) {
            Preview.StopPreview ();
        }
    }

    public void SurfaceChanged (ISurfaceHolder holder, Android.Graphics.Format format, int width, int height)
    {
        var parameters = Preview.GetParameters ();
        parameters.SetPreviewSize (previewSize.Width, previewSize.Height);
        RequestLayout ();
        //If you want to rotate the video screen, you can use following code
        //switch (windowManager.DefaultDisplay.Rotation) {
        //case SurfaceOrientation.Rotation0:
        //  camera.SetDisplayOrientation (90);
        //  break;
        //case SurfaceOrientation.Rotation90:
        //  camera.SetDisplayOrientation (0);
        //  break;
        //case SurfaceOrientation.Rotation270:
        //  camera.SetDisplayOrientation (180);
        //  break;
        //}

        Preview.SetParameters (parameters);
        Preview.StartPreview ();
        IsPreviewing = true;
    }

    Camera.Size GetOptimalPreviewSize (IList<Camera.Size> sizes, int w, int h)
    {
        const double AspectTolerance = 0.1;
        double targetRatio = (double)w / h;

        if (sizes == null) {
            return null;
        }

        Camera.Size optimalSize = null;
        double minDiff = double.MaxValue;

        int targetHeight = h;
        foreach (Camera.Size size in sizes) {
            double ratio = (double)size.Width / size.Height;

            if (Math.Abs (ratio - targetRatio) > AspectTolerance)
                continue;
            if (Math.Abs (size.Height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.Abs (size.Height - targetHeight);
            }
        }

        if (optimalSize == null) {
            minDiff = double.MaxValue;
            foreach (Camera.Size size in sizes) {
                if (Math.Abs (size.Height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.Abs (size.Height - targetHeight);
                }
            }
        }

        return optimalSize;
    }
}

然后,这是自定义渲染器。

[assembly: ExportRenderer(typeof(CustomRenderer.CameraPreview), typeof(CameraPreviewRenderer))]

命名空间CustomRenderer.Droid { public class CameraPreviewRenderer : ViewRenderer { CameraPreview cameraPreview;

    public CameraPreviewRenderer(Context context) : base(context)
    {
    }



protected override void OnElementChanged(ElementChangedEventArgs<CustomRenderer.CameraPreview> e)
    {
        base.OnElementChanged(e);

        if (e.OldElement != null)
        {
            // Unsubscribe
            cameraPreview.Click -= OnCameraPreviewClicked;
        }
        if (e.NewElement != null)
        {
            if (Control == null)
            {
                cameraPreview = new CameraPreview(Context);


                SetNativeControl(cameraPreview);
            }
            Control.Preview = Camera.Open((int)e.NewElement.Camera);

            // Subscribe
            cameraPreview.Click += OnCameraPreviewClicked;
        }




    }

    void OnCameraPreviewClicked(object sender, EventArgs e)
    {
        if (cameraPreview.IsPreviewing)
        {
            cameraPreview.Preview.StopPreview();
            cameraPreview.IsPreviewing = false;
        }
        else
        {
            cameraPreview.Preview.StartPreview();
            cameraPreview.IsPreviewing = true;
        }
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            Control.Preview.Release();
        }
        base.Dispose(disposing);
    }


 }
 }

如果想知道如何在IOS或UWP中实现Camera Preview,可以参考这篇link。 https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/view

这是 github.

中的代码

https://github.com/851265601/FormsRecordVideoInside

以上实现方式,有点复杂。我建议你使用 Xam.Plugin.Media 来实现它。这是 运行 GIF。它无法在应用程序中看到视频预览,但您不需要使用自定义渲染器来实现它只需几行代码。

  private async void PlayStopButtonText_Clicked(object sender, EventArgs e)
    {
        //  throw new NotImplementedException();

        var file = await CrossMedia.Current.TakeVideoAsync(new StoreVideoOptions
        {
            SaveToAlbum = true,Directory = "Sample"


        });

        //Get the public album path

        if (file == null)
            return;

        await DisplayAlert("File Location", file.AlbumPath, "OK");
    }

在android中添加以下权限。

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

这里是运行 GIF(超过2M,所以不能上传)。

https://imgur.com/a/9WE4szZ