Coroutine вызывает cra sh в единстве - PullRequest
0 голосов
/ 23 апреля 2020

Я добавил следующую функцию в мой скрипт, и она вызывает cra sh в единстве.

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);
        }

    }

}

Я вызываю эту функцию в операторе if в функции Update. см:

void Update()
{
    _currentsframe = Time.frameCount;

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

        if (!_addFrameFunctionHasBeenCalled)
        {
            AddCurrentFrameToVideo();
        }

    }

}

Также я управлял переменной recordingButtonHasBeenPressed в другом скрипте с помощью кнопки OnClick(). см .:

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;
    }
}

Я не знаю, почему это разрушает единство Я не думаю, что это бесконечность l oop. Также я протестировал DO-While l oop вместо использования 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);
    }

это также вызывает единство cra sh.

Что мне делать? Что с ним не так?

1 Ответ

2 голосов
/ 23 апреля 2020

Это

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

является рекурсивным вызовом, когда вы снова yield return повторяете ту же процедуру (которая внутренне yield return снова повторяет ту же процедуру и c), поэтому она ожидает, пока все вложенные подпрограммы не будут закончено => Так что в какой-то момент вы получите StackOverflow!


Это определенно закрытый никогда не заканчивается, в то время как l oop

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

в пределах l oop значение 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 и флага нескольких контроллеров bool с, вы сразу же начинаете сбрасывать снова, когда здесь вызывается метод, я бы предпочел сделать весь код управляемым событиями и один раз вызвать когда кнопка вызывается. Это предотвратит случайное выполнение параллельных подпрограмм и улучшит чтение и поддержку всего этого.

Я не знаю ваш полный код, но он может выглядеть примерно так:

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;
}
...