Search Unity

Resolved Video player on Windows freezes

Discussion in 'Audio & Video' started by johannesgaebler, Sep 23, 2021.

  1. johannesgaebler

    johannesgaebler

    Joined:
    Jun 17, 2019
    Posts:
    28
    Hey there!

    I am posting a separate thread because all other issues of this type were Android specific and I think there's a big difference.

    I am using a VideoPlayer component on a couple of UI prefabs, below a canvas to render tutorial videos. My game has two parts, which are both in the same scene and there is no difference between those parts (other than gameplay). The only difference is that the second part is a bit more demanding in terms of resources.

    The videos play very smoothly in the first part and everything is fine, in the second part they play for a few frames and then stop. Always and every time without exception.

    Here's a video:


    The videos are 1280x720, ca. 2.5kbps, no audio, mp4. I am on Windows 10, Unity 2020.1.7f1 (and I have a release on Monday so I am not super into updating that haha =) ).

    This is the code:

    Code (CSharp):
    1. public class UI_VideoPlayer : UI_Element
    2.     {
    3.         private static event Action<UI_VideoPlayer> EBeforePlay;
    4.  
    5.  
    6.         [SerializeField] protected RawImage rawImage;
    7.         [SerializeField] protected Image iconLoading;
    8.      
    9.  
    10.         private VideoPlayer videoPlayer;
    11.  
    12.  
    13.         protected override void AwakeIntern()
    14.         {
    15.             base.AwakeIntern();
    16.             videoPlayer = GetComponent<VideoPlayer>();
    17.          
    18.             EBeforePlay += OnBeforePlay;
    19.         }
    20.         protected override void DestroyIntern()
    21.         {
    22.             base.DestroyIntern();
    23.             EBeforePlay -= OnBeforePlay;
    24.         }
    25.      
    26.         private void OnDisable()
    27.         {
    28.             StopAllCoroutines();
    29.             Stop();
    30.         }
    31.  
    32.         private void OnBeforePlay(UI_VideoPlayer player)
    33.         {
    34.             Stop();
    35.         }
    36.  
    37.         public void Set(VideoClip video)
    38.         {
    39.             videoPlayer.clip = video;
    40.         }
    41.  
    42.         public void Play()
    43.         {
    44.             EBeforePlay?.Invoke(this);
    45.  
    46.             if (videoPlayer.isPrepared)
    47.             {
    48.                 videoPlayer.Play();
    49.             }
    50.             else
    51.             {
    52.                 StartCoroutine(PrepareVideoAsync());
    53.             }
    54.         }
    55.         public void Stop()
    56.         {
    57.             videoPlayer.Stop();
    58.         }
    59.  
    60.  
    61.         private IEnumerator PrepareVideoAsync()
    62.         {
    63.             videoPlayer.Prepare();
    64.             iconLoading.gameObject.SetActive(true);
    65.  
    66.             while (!videoPlayer.isPrepared)
    67.             {
    68.                 iconLoading.transform.Rotate(Vector3.up, 25f * Time.deltaTime);
    69.                 yield return null;
    70.             }
    71.  
    72.             iconLoading.gameObject.SetActive(false);
    73.             videoPlayer.Play();
    74.         }
    75.     }

    What I tried so far (and obv. didn't help):

    - Added the coroutine and wait for isPrepared
    - Removed the videoPlayer.targetTexture if not used
    - Added the event to stop all players before a new one starts to not overwrite the texture
    - Checked the compiler, there are no differences and nothing suspicious going on in the working and not working part
    Edit: - Tried to load a local video using the VideoPlayer.url, the issue is the exact same as before. It plays fine first and only a few frames later.


    Thank you for any ideas or tipps!!
     
    Last edited: Sep 23, 2021
  2. mgear

    mgear

    Joined:
    Aug 3, 2010
    Posts:
    9,436
    could try the same videos in a empty project, simple videoplayer with no scripts,
    can check if editor log has any extra info (probably not, if no warnings, errors in console either..)
     
  3. johannesgaebler

    johannesgaebler

    Joined:
    Jun 17, 2019
    Posts:
    28
    Will do first thing in the morning!

    Also, I forgot to mention:
    - I checked the profiler too, nothing weird
    - I signed to all events and they are all completely normal like expected (no errors, preparing works, start fires,...)
     
  4. johannesgaebler

    johannesgaebler

    Joined:
    Jun 17, 2019
    Posts:
    28
    Ok so I tried making a new project and I also added logs to all events.
    They are firing as expected, first videoPlayer.prepareCompleted fires, shortly followed by videoPlayer.started (as expected) but the video freezes after a couple of frames. There is no other event fireing (like the errorReceived or framesDropped for example).

    That's the code (pretty straight forward tho...):

    Code (CSharp):
    1. videoPlayer.clockResyncOccurred += (p, d) => Log.Error(GetType().Name, "clockResyncOccurred");
    2.             videoPlayer.errorReceived       += (p, e) => Log.Error(GetType().Name, "errorReceived - " + e);
    3.             videoPlayer.frameDropped        += (p)    => Log.Error(GetType().Name, "frameDropped");
    4.             videoPlayer.frameReady          += (p, f) => Log.Text(GetType().Name, "frameReady - " + f);
    5.             videoPlayer.loopPointReached    += (p)    => Log.Text(GetType().Name, "loopPointReached");
    6.             videoPlayer.prepareCompleted    += (p)    => Log.Text(GetType().Name, "prepareCompleted");
    7.             videoPlayer.seekCompleted       += (p)    => Log.Text(GetType().Name, "seekCompleted");
    8.             videoPlayer.started             += (p)    => Log.Text(GetType().Name, "started");
    EDIT: I use HDRP, this is probably pretty important info, sorry to not mention earlier!

    Also, I attached a blank HDRP project with the video player stuff in it. It should play on pressing space but for some reason it doesn't work at all. Like above, all events fire as expected but the video isn't visible in game (the render texture is updated tho if you take a look in the inspector).

    Here's the project (unfortunately too large for direct upload): https://drive.google.com/file/d/1-3-zXQReqX622SXJgi_VyzcRtzzLPhz5/view?usp=sharing
    Make sure to open the sample scene. =)

    It's so weird haha! Thanks again!
     
  5. mgear

    mgear

    Joined:
    Aug 3, 2010
    Posts:
    9,436
    example scene works, if make that canvas image RawImage, and assign rendertexture to that.


    also interesting that if make new empty scene,
    add 3d plane, with unlit material, attach video player (autoplay and set clip, override material),
    its not visible until disable or delete "Sky and Fog Volume" gameobject..
    *hdrp unlit material did work.
     
  6. johannesgaebler

    johannesgaebler

    Joined:
    Jun 17, 2019
    Posts:
    28
    Oh yeah, totally forgot about that, you're right - it works for me as well!
    It works without any issue with all of these videos for me but I'll double check the raw images in my actual game.

    Edit: Other things I tried:
    - Remove the videoPlayer.Stop() entirely to not accidentally block it
    - Set "play on awake", didn't fix it BUT

    playOnAwake leads to restarting the video over and over after a few frames and triggering the events. Will look deeper into this.
     
    Last edited: Sep 24, 2021
  7. johannesgaebler

    johannesgaebler

    Joined:
    Jun 17, 2019
    Posts:
    28
    Ok so I just got it working. The problem was totally on my side (who'd have guessed lol) and the fix was the following:
    Every time a possible step-completion condition was triggered through an event, the steps were updated. Looked like that:

    Code (CSharp):
    1. steps.ForEach(s => s.gameObject.SetActive(false));
    2. var shownSteps = TutorialManager.CurrentVisibleSteps(steps.Count);
    3. for (int i = 0; i < Mathf.Min(steps.Count, shownSteps.Count()); i++)
    4. {
    5.     steps[i].gameObject.SetActive(true);
    6.     steps[i].Set(shownSteps.ElementAt(i));
    7. }
    You see this steps.ForEach()? That's the issue.

    I fixed it by replacing the code with this:

    Code (CSharp):
    1. var shownSteps = TutorialManager.CurrentVisibleSteps(steps.Count);
    2. for (int i = 0; i < shownSteps.Count(); i++)
    3. {
    4.     if (steps[i].RequireUpdate(shownSteps.ElementAt(i)))
    5.     {
    6.         steps[i].gameObject.SetActive(false);
    7.         steps[i].Set(shownSteps.ElementAt(i));
    8.         steps[i].gameObject.SetActive(true);
    9.     }
    10. }
    11. steps.ForEach(s => { if (!s.IsUsed) s.gameObject.SetActive(false); });
    I introduced RequireUpdate(step) which checks if the step actually changed and only then set the clip. SetActive() was actually the issue. And because the live mode has a lot more condition checks, that's why it was never a problem in build mode.

    It's still a bit ugly but I don't have the time to make it nice right now, will get back to it tho (or at least that's what I say now)

    Thanks for your help tho! Really appreciated!