毛伊岛错误 - 屏幕录制 - StopRecording() 错误;

问题描述 投票:0回答:1

我正在尝试将 Android 实现添加到 https://github.com/jfversluis/Plugin.Maui.ScreenRecording

我想我检查了所有基础,但我在 MediaRecorder.Stop(); 上遇到了这个异常

{Java.Lang.RuntimeException: stop failed.
   at Java.Interop.JniEnvironment.InstanceMethods.CallVoidMethod(JniObjectReference instance, JniMethodInfo method, JniArgumentValue* args) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/obj/Release/net7.0/JniEnvironment.g.cs:line 20370
   at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeVirtualVoidMethod(String encodedMember, IJavaPeerable self, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods_Invoke.cs:line 66
   at Android.Media.MediaRecorder.Stop() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Android.Media.MediaRecorder.cs:line 2477
   at Plugin.Maui.ScreenRecording.ScreenRecordingImplementation.StopRecording(ScreenRecordingOptions options) in D:\pmsr\src\Plugin.Maui.ScreenRecording\ScreenRecording.android.cs:line 84
  --- End of managed Java.Lang.RuntimeException stack trace ---
java.lang.RuntimeException: stop failed.
    at android.media.MediaRecorder.stop(Native Method)
    at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.n_onClick(Native Method)
    at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.onClick(ButtonHandler_ButtonClickListener.java:31)
    at android.view.View.performClick(View.java:7448)
    at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1211)
    at android.view.View.performClickInternal(View.java:7425)
    at android.view.View.access$3600(View.java:810)
    at android.view.View$PerformClick.run(View.java:28305)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:223)
    at android.app.ActivityThread.main(ActivityThread.java:7656)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

  --- End of managed Java.Lang.RuntimeException stack trace ---
java.lang.RuntimeException: stop failed.
    at android.media.MediaRecorder.stop(Native Method)
    at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.n_onClick(Native Method)
    at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.onClick(ButtonHandler_ButtonClickListener.java:31)
    at android.view.View.performClick(View.java:7448)
    at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1211)
    at android.view.View.performClickInternal(View.java:7425)
    at android.view.View.access$3600(View.java:810)
    at android.view.View$PerformClick.run(View.java:28305)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:223)
    at android.app.ActivityThread.main(ActivityThread.java:7656)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
}

我猜我正在寻找一些愚蠢的东西。任何指导或建议将不胜感激。

ScreenRecording.android.cs

using Android.Content;
using Android.Media;
using Android.Media.Projection;
using Android.Hardware.Display;
using Android.Views;
using Microsoft.Maui.ApplicationModel;
using Android.Content.PM;
using Android.OS;
using AndroidX.Core.App;
using Android.Graphics.Drawables;
using AndroidX.Core.Graphics.Drawable;
using Microsoft.Maui.Devices;
using static Android.Provider.Telephony.Mms;
using Microsoft.Maui.Storage;



namespace Plugin.Maui.ScreenRecording;

public partial class ScreenRecordingImplementation : IScreenRecording
{
    public MediaProjectionManager ProjectionManager;
    public MediaProjection MediaProjection;
    public VirtualDisplay VirtualDisplay;
    public MediaRecorder MediaRecorder;
    public string FilePath;
    public const int REQUEST_MEDIA_PROJECTION = 1;

    public static EventHandler<ScreenRecordingEventArgs> ScreenRecordingPermissionHandler;
    public ScreenRecordingImplementation()
    {
        ProjectionManager = (MediaProjectionManager)Platform.AppContext.GetSystemService(Context.MediaProjectionService);

        
    }

    public bool IsRecording { get; private set; }

    public bool IsSupported { get { return ProjectionManager != null; } private set { }}
    

    


    public async Task StartRecording(bool enableMicrophone)
    {
        if (IsSupported)
        {

            

            MediaRecorder = new MediaRecorder();
            //FilePath = Path.Combine(Platform.AppContext.FilesDir.AbsolutePath, $"recording_{DateTime.Now:yyyyMMdd_HHmmss}.mp4");

            FilePath = Path.Combine(FileSystem.Current.CacheDirectory, "Screen.mp4");

            SetUpMediaRecorder(enableMicrophone);
            try
            {

                await Task.Delay(2000);
                MediaRecorder.Start();
                IsRecording = true;
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw new NotSupportedException("Screen recording did not start.");
            }
        }
        else
        {
            throw new NotSupportedException("Screen recording not supported on this device.");
        }
    }

    public async Task<ScreenRecordingFile?> StopRecording(ScreenRecordingOptions? options)
    {
        if (IsRecording)
        {
            IsRecording = false;
            try
            {
                MediaRecorder.Stop();
                MediaRecorder.Release();
                VirtualDisplay.Release();
                MediaProjection.Stop();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            var context = Android.App.Application.Context;

            context.StopService(new Intent(context, typeof(ScreenRecordingService)));

            return new ScreenRecordingFile(FilePath);
        }

        return null;
    }

    public async void OnScreenCapturePermissionGranted(Result resultCode, Intent? data)
    {
        if (resultCode == Result.Ok && data != null)
        {
            var context = Android.App.Application.Context;
            Intent serviceIntent = new Intent(context, typeof(ScreenRecordingService));
            if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
            {
                context.StartForegroundService(serviceIntent);
            }
            else
            {
                context.StartService(serviceIntent);
            }

            await Task.Delay(2000);

            // Handle the successful permission result
            MediaProjection mediaProjection = ProjectionManager.GetMediaProjection((int)resultCode, data);

            VirtualDisplay = mediaProjection.CreateVirtualDisplay("Plugin.Maui.ScreenRecording.ScreenRecordingImplementation.ScreenRecordingService", (int)DeviceDisplay.Current.MainDisplayInfo.Width, (int)DeviceDisplay.Current.MainDisplayInfo.Height, (int)DeviceDisplay.Current.MainDisplayInfo.Density, Android.Views.DisplayFlags.Presentation, null, null, null);

        }
        else
        {
            // Handle the case where permission was not granted
        }
        // Additional setup or start recording
    }

    public void SetUpMediaRecorder(bool enableMicrophone)
    {
        MediaRecorder = new MediaRecorder();

        if (enableMicrophone)
        {
            MediaRecorder.SetAudioSource(AudioSource.Mic);
        }

        MediaRecorder.SetVideoSource(VideoSource.Surface);
        MediaRecorder.SetOutputFormat(OutputFormat.Mpeg4);
        MediaRecorder.SetVideoEncoder(VideoEncoder.H264);

        if (enableMicrophone)
        {
            MediaRecorder.SetAudioEncoder(AudioEncoder.AmrNb);
        }

        MediaRecorder.SetOutputFile(FilePath);
        MediaRecorder.SetVideoSize((int)DeviceDisplay.Current.MainDisplayInfo.Width, (int)DeviceDisplay.Current.MainDisplayInfo.Height);
        MediaRecorder.SetVideoFrameRate(30);

        try
        {
            MediaRecorder.Prepare();
        }
        catch (Java.IO.IOException ex)
        {
            Console.WriteLine($"MediaRecorder preparation failed: {ex.Message}");
            // Handle preparation failure
            MediaRecorder.Release();
        }
    }

    public void Setup()
    {
        Intent captureIntent = ProjectionManager.CreateScreenCaptureIntent();
        Platform.CurrentActivity.StartActivityForResult(captureIntent, REQUEST_MEDIA_PROJECTION);
    }

    public class ScreenRecordingEventArgs : EventArgs
    {
        public Result ResultCode { get; set; }

        public Intent? Data { get; set; }
    }

    [Service(ForegroundServiceType = ForegroundService.TypeMediaProjection)]
    public class ScreenRecordingService : Service
    {
        public override IBinder? OnBind(Intent? intent)
        {
            return null;
        }

        public override StartCommandResult OnStartCommand(Intent? intent, StartCommandFlags flags, int startId)
        {
            string CHANNEL_ID = "ScreenRecordingService";
            int NOTIFICATION_ID = 1337;

            if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
            {
                var channelName = "Screen Recording Service";
                var channel = new NotificationChannel(CHANNEL_ID, channelName, NotificationImportance.Default)
                {
                    Description = "Notification Channel for Screen Recording Service"
                };

                var notificationManager = (NotificationManager)GetSystemService(NotificationService);
                notificationManager.CreateNotificationChannel(channel);
            }

            // Create a notification for the foreground service
            var notificationBuilder = new Notification.Builder(this, CHANNEL_ID)
                .SetContentTitle("Screen Recording")
                .SetContentText("Recording screen...")
                .SetSmallIcon(Resource.Drawable.notification_template_icon_low_bg); // Ensure you have 'ic_notification' in Resources/drawable

            var notification = notificationBuilder.Build();

            // Start the service in the foreground
            StartForeground(NOTIFICATION_ID, notification);

            return StartCommandResult.Sticky;
        }

    }
}

MainActivity.cs

using Android.App;
using Android.Content.PM;
using Android.OS;
using Plugin.Maui.ScreenRecording;

namespace ScreenRecordingSample;

[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
    public const int REQUEST_MEDIA_PROJECTION = 1;
    ScreenRecordingImplementation screenRecordingImplementation = new ScreenRecordingImplementation();


    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        // Initialize other components...

        // Initialize ScreenRecordingImplementation
        screenRecordingImplementation = new Plugin.Maui.ScreenRecording.ScreenRecordingImplementation();
    }
    protected override void OnActivityResult(int requestCode, Result resultCode, Android.Content.Intent? data)
    {
        base.OnActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_MEDIA_PROJECTION)
        {
            screenRecordingImplementation.OnScreenCapturePermissionGranted(resultCode, data);
        }
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application android:allowBackup="true" android:supportsRtl="true">
        <service android:name="Plugin.Maui.ScreenRecording.ScreenRecordingImplementation.ScreenRecordingService" android:foregroundServiceType="mediaProjection" />
    </application>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-sdk />    
</manifest>

IScreenRecording.cs

namespace Plugin.Maui.ScreenRecording;

/// <summary>
/// Provides the ability to record the screen within your app.
/// </summary>
public interface IScreenRecording
{
    /// <summary>
    /// Gets whether or not screen recording is currently happening.
    /// </summary>
    bool IsRecording { get; }

    /// <summary>
    /// Gets whether or not screen recording is supported on this device.
    /// </summary>
    bool IsSupported { get; }

    /// <summary>
    /// Sets up the screenrecording.
    /// </summary>
    /// <remarks>
    /// This is needed for android to request media projection.
    /// </remarks>
    void Setup();

    /// <summary>
    /// Starts the screenrecording.
    /// </summary>
    /// <param name="enableMicrophone">Determines if the microphone should be used as input during the recording.</param>
    /// <returns>A <see cref="Task"/> object with information about this operation.</returns>
    /// <remarks>
    /// A permission to access the microphone might be needed, this is not requested by this method.
    /// Make sure to add an entry in the metadata of your app and request the runtime permission
    /// before calling this method.
    /// </remarks>
    Task StartRecording(bool enableMicrophone);

    /// <summary>
    /// Stops the recording and saves the video file to the device's gallery.
    /// </summary>
    /// <returns>A <see cref="Task"/> object with information about this operation.</returns>
    Task<ScreenRecordingFile?> StopRecording(ScreenRecordingOptions? options = null);
}

其余代码与我认为的主存储库相同。

maui foreground-service maui-android
1个回答
0
投票

首先,用 try-catch 块包围

MediaRecorder.Stop()
调用,以优雅地处理任何运行时异常。这不能解决根本原因,但可以防止应用程序崩溃。

try
{
    MediaRecorder.Stop();
}
catch (Java.Lang.RuntimeException ex)
{
    Console.WriteLine("Error stopping MediaRecorder: " + ex.Message);
    // Handle the exception, e.g., by logging
}

如果录制尚未实际开始或已停止,则

MediaRecorder.Stop()
方法将失败。在调用
MediaRecorder.Start()
之前,请确保
Stop()
已成功调用。

if (IsRecording)
{
    // Proceed to stop the recording
}

MediaRecorder
对于方法调用的顺序也非常敏感。确保
Prepare()
Start()
Stop()
调用的顺序正确。您必须在
Prepare()
之前致电
Start()
,并且在致电
Start()
之前,
Stop()
必须有足够的时间启动录音。

如果录制开始后立即停止,则

MediaRecorder
可能没有足够的数据来完成视频文件,从而导致此异常。在启动和停止之间引入延迟,以确保记录一些数据。

// Wait for a minimum duration before allowing stop
if ((DateTime.UtcNow - recordingStartTime).TotalSeconds < MIN_RECORDING_DURATION)
{
    // Wait or notify the user
}

停止录制后,请确保资源已正确释放。在

MediaRecorder.Release()
之后致电
MediaRecorder.Stop()
。另外,释放
VirtualDisplay
MediaProjection
对象。

© www.soinside.com 2019 - 2024. All rights reserved.