使用 xamarin 表单的应用程序内的录像机
Video Recorder inside a app using xamarin forms
如何使用 xamarin 表单在应用程序内实现录像机?
您可以使用 Xamarin.Plugin.Media。如果它对您来说不够灵活,那么您需要使用本机 API 来实现录像机。在这种情况下,您可能根本不应该做 Xamarin.Forms,但如果您坚持这样做。
你想达到如下GIF效果吗(超过2M,所以不能上传)?
我通过自定义渲染在 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,所以不能上传)。
如何使用 xamarin 表单在应用程序内实现录像机?
您可以使用 Xamarin.Plugin.Media。如果它对您来说不够灵活,那么您需要使用本机 API 来实现录像机。在这种情况下,您可能根本不应该做 Xamarin.Forms,但如果您坚持这样做。
你想达到如下GIF效果吗(超过2M,所以不能上传)?
我通过自定义渲染在 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,所以不能上传)。