【问题标题】:Coroutine causes crash in unity协程导致统一崩溃
【发布时间】:2020-04-23 06:09:24
【问题描述】:

我在脚本中添加了以下函数,它导致统一崩溃。

public void AddCurrentFrameToVideo()
{
    _addFrameFunctionHasBeenCalled = true;

    using (var encoder = new MediaEncoder(encodedFilePath, videoAttr, audioAttr))
    using (var audioBuffer = new NativeArray<float>(sampleFramesPerVideoFrame, Allocator.Temp))
    {

        IEnumerator SetFrame()
        {
            yield return new WaitForSeconds(0.3f);
            encoder.AddFrame(tex);
            encoder.AddSamples(audioBuffer);
            if (recordingButtonHasBeenPressed)
            {
                yield return StartCoroutine(SetFrame());
            }
            else
            {
                yield return null;
                yield break;
            }

        }

        IEnumerator mycoroutine;
        mycoroutine = SetFrame();

        if (recordingButtonHasBeenPressed)
        {
            StartCoroutine(mycoroutine);
        }
        else
        {
            StopCoroutine(mycoroutine);
        }

    }

}

我在 Update 函数的 if 语句中调用此函数。见:

void Update()
{
    _currentsframe = Time.frameCount;

    if (recordingButtonHasBeenPressed)
    {
        if (!videoBasicFileHasBeenCreated)
        {
            CreateVideoBasicFile();
        }

        if (!_addFrameFunctionHasBeenCalled)
        {
            AddCurrentFrameToVideo();
        }

    }

}

我还通过按钮OnClick() 在另一个脚本中控制了recordingButtonHasBeenPressed 变量。见:

public void RecordVideo_OnClick()
{
    if (videoIsRecording)
    {
        videoIsRecording = false;
        videoRecordButton.image.sprite = videoButtonIsNotRecordingSprite;

        _myRecorderSc.recordingButtonHasBeenPressed = false;
        _myRecorderSc.videoBasicFileHasBeenCreated = false;
    }
    else
    {
        videoRecordButton.image.sprite = videoButtonIsRecordingSprite;
        _myRecorderSc.recordingButtonHasBeenPressed = true;
        _myRecorderSc.videoBasicFileHasBeenCreated = false;
        videoIsRecording = true;
    }
}

我不知道为什么它会破坏统一性。我不认为这是一个无限循环。 我还测试了DO-While 循环而不是使用Croutine。见:

    using (var encoder = new MediaEncoder(encodedFilePath, videoAttr, audioAttr))
    using (var audioBuffer = new NativeArray<float>(sampleFramesPerVideoFrame, Allocator.Temp))
    {
        do
        {
                encoder.AddFrame(tex);
                encoder.AddSamples(audioBuffer);
        } while (recordingButtonHasBeenPressed);
    }

它也会导致统一崩溃。

我该怎么办?它有什么问题?

【问题讨论】:

    标签: c# unity3d crash coroutine


    【解决方案1】:

    这个

        IEnumerator SetFrame()
        {
            yield return new WaitForSeconds(0.3f);
            encoder.AddFrame(tex);
            encoder.AddSamples(audioBuffer);
            if (recordingButtonHasBeenPressed)
            {
                yield return StartCoroutine(SetFrame());
            }
         }
    

    是一个递归调用,您再次yield return 相同的例程(在内部yield returns 再次相同的例程等)所以它一直等到所有嵌套的子例程完成=> 所以在某些时候你会得到一个 StackOverflow!


    这绝对是一个封闭的永无止境的while循环

    using (var audioBuffer = new NativeArray<float>(sampleFramesPerVideoFrame, Allocator.Temp))
    {
        do
        {
                encoder.AddFrame(tex);
                encoder.AddSamples(audioBuffer);
        } while (recordingButtonHasBeenPressed);
    }
    

    在循环内,recordingButtonHasBeenPressed 的值将永远被更改,Unity/您的应用会立即永远冻结!


    你想要做的就是像这样的协程

    IEnumerator SetFrame()
    {
        // initially wait once
        yield return new WaitForSeconds(0.3f);
    
        // simply continue to execute the routine until the record shall be stopped
        while(recordingButtonHasBeenPressed)
        {
            encoder.AddFrame(tex);
            encoder.AddSamples(audioBuffer);
    
            // yield the next frames for 0.3 seconds before checking 
            // recordingButtonHasBeenPressed again
            yield return new WaitForSeconds(0.3f);
        }
    }
    

    你甚至不需要主动阻止它。您需要做的就是启动它,然后为了中断它只需将recordingButtonHasBeenPressed 设置为false


    事件驱动

    现在一般来说,一旦在此处调用方法,您似乎会立即再次重置,而不是使用 Update 和多个控制器标志 bools 我宁愿让整个代码 事件驱动 并调用一次 在调用按钮时。这将防止并发例程意外运行,并使整个事情更好地阅读和维护。

    我不知道你的完整代码,但它可能看起来像

    public void RecordVideo_OnClick()
    {
        // invert the toggle flag
        videoIsRecording = !videoIsRecording;
    
        // depending on the new flag value chose the sprite
        videoRecordButton.image.sprite = videoIsRecording ? videoButtonIsRecordingSprite : videoButtonIsNotRecordingSprite;
    
        if (!videoIsRecording)
        {
            _myRecorderSc.StopRecording();
        }
        else
        {
            _myRecorderSc.StartRecoring();
        }
    }
    

    然后在记录器脚本中你只需要

    public void StartRecording()
    {
        if(!recording)
        {
            StartCoroutine(RecorderRoutine);
        }
    }
    
    public void StopRecording()
    {
        recording = false;
    }
    
    // flag to interrupt running record
    private bool recording;
    
    private IEnumerator RecorderRoutine()
    {
        // Just in case prevent concurrent routines
        if(recording) yield break;
        recording = true;
    
        // initialize your file
        CreateVideoBasicFile();    
    
        // initially wait once
        yield return new WaitForSeconds(0.3f);
    
        using (var encoder = new MediaEncoder(encodedFilePath, videoAttr, audioAttr))
        using (var audioBuffer = new NativeArray<float>(sampleFramesPerVideoFrame, Allocator.Temp))
        {
            // simply continue to execute the routine until the record shall be stopped
            while(recording)
            {
                encoder.AddFrame(tex);
                encoder.AddSamples(audioBuffer);
    
                // yield the next frames for 0.3 seconds before checking 
                // recordingButtonHasBeenPressed again
                yield return new WaitForSeconds(0.3f);
            }
        }
    
        recording = false;
    }
    

    【讨论】:

    • 我按照你说的做了,但统一没有为视频添加任何帧。似乎“encoder.AddFrame”不起作用。
    • 我修好了。我在调用StartRecording() 之前将recordingButtonHasBeenPressed 的值设置为“true”,在调用StopRecording 之前设置为“false”。它可以工作,但是当我收到错误消息时:“NativeArray 已被释放,不允许访问它”。这种方式统一只是在视频中添加一帧,在添加第二帧之前出现此错误。
    • 对不起,实际上是由于在 while 条件下的拼写错误^^ 它应该只是 recording
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-12
    • 2013-04-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-28
    • 2013-10-27
    • 1970-01-01
    相关资源
    最近更新 更多