I have a 1920x1080 30fps mp4 video I use as a cutscene inbetween 2 levels (scenes).
I do this by loading a scene that has only a camera, a videoplayer and a small script that loads the next scene after the video is done playing.
The videoplayer is set to PlayOnAwake and WaitForFirstFrame.
It seems to work fine in the editor, but when I make a build, the video starts slowly (stuttering), then suddenly speeds up.
Which is exactly the kind of behaviour I would expect could happen if WaitForFirstFrame was set to false, but as I said, it’s set to true.
Any idea what can cause this, and how to fix it?
I’m using Unity2019.3,
It’s for a Windows build
1 Like
Does no one else have this issue?
Hi Steven,
Are you using the .Prepare() method and waiting for isPrepared = true; on the video player before running .Play()? It could be that the video is still loading when .Play() is called, and taking a few seconds to loading before catching back up to speed.
Let me know!
Yes, I have tried that as well, even thought according to the docs I shouldn’t have to, as I’ve set WaitForFirstFrame to true.
But it makes no difference anyway, the video still stutters even if I only start playing it after waiting for isPrepared to be true.
ah that is annoying, how are you loading the video clip in? Is it loaded by Addressables or StreamingAssets, or dragged and dropped into the video player in the scene? Do you know what codec the video uses as well? We’ve had a few video issues and sometimes encoding them differently would give quite different results. I would also take a quick look at the profiler to make sure nothing’s going on that you’re not expecting during that scene load up!
It’s a local video, assigned in the inspector, so it’s part of the build, I would expect that to work better than loading it externally.
The codec is H.264, and it’s 30 fps.
I should also mention that Unity messes up the audio: only the left side remains, the right is silent, which is not the case when watching the video in any other player.
I’ll have a look at it with the profiler.
I have similar framedrop issues when I build our app for standalone. It used to run fine in unity 2018, but since we moved to unity 2020 we have this issues. Did you ever try it on a newer version of unity? And did you ever try to contact Unity about it?
Decoding a video is an expensive operation and if there are spikes it is possible that the video can’t provide the frame at the right speed. I would be curious to see the profiler.
The weird thing is in the Editor is actually works okay. Not great but still okay.
But the build (made with the unity2020) has real performance issues. Here are the profiler stats when running in the editor and controlling the video using my Playbar and setting the VideoPlayer.time property.
And by the way, thanks for getting back. I didn’t think anybody would reply here. It’s nice to see that you guys care to help.
I managed today to upgrade our project to the latest Unity 2021 version (2.8.f1) and I still have the same issue:
Poor video performance, especially in the Build. The editor is still kinda okay.
In all cases, using the Play() function, the video runs really smooth, even in the build. The only problem is when I use videoplayer.time (or frame). I tried many different video files (different resolutions, different file sizes etc.), all with the same result.
And again, it still works very well with Unity 2018, especially when building the app.
Please let me know if you think I should report this bug, and how to report it?
Are you able to record what you mean by “framedrop issues”? Because I can’t reproduce it on 2020.3.26f1.
Yes, absolutely, thanks for looking into it.
-
Unity Editor 2018 (works fine):
Dropbox
-
Standalone Mac app, made with Unity 2018 (works even better than in the Editor2018):
Dropbox
-
Unity Editor 2020 (works still okay):
Dropbox
-
Standalone Mac app, made with Unity 2020 and same results in Unity 2021 (frames are very slowly being loaded. Only in Playmode they run totally fine. But when searching with the Playbar for the frame, the framerate “of the video” is really low):
Dropbox
The only thing that comes into my mind is that some Build “quality” settings in Unity2020 might have changed and could affect the performance, but I have no idea which settings that could be. Any idea?
Hi! Sorry for the wait. I had to investigate because it is now the third post on a similar bug. I found out that we did fix a similar issue recently, but the patch has not landed on all versions. So the next thing I would recommend is upgrading your project to the latest 2022 versions. We are not suggesting that you start using 2022 in production, but this will let you know in advance if the fix we are talking about is indeed solving the issue. If it is indeed that, we will try to accelerate the process. For more details, I would recommend checking the full answer from my colleague. Video playback issue: setting VideoPlayer.time not working after update from 2020.1.16 to 2020.3.25 - Unity Engine - Unity Discussions
Hi, Sorry for my late reply. I tried our project now with the beta of Unity 2022 and here’s the result is okay. It seems to be better than Unity 2021, but still a lot worse than the performance we achieved with 2018. Here’s a recorded video of a test:
Would this fix eventually be available in Unity 2020.3 or would we have to go to Unity 2022?
Yes. It is currently getting backported to 2019, 2020, and 2021. You can see the progress in this issue tracker. https://issuetracker.unity3d.com/issues/macos-videoplayer-dot-width-and-height-returns-0-when-videoplayer-dot-texture-dot-width-and-height-returns-expected-values
As a lot of things changed between 2018 and today, I don’t know exactly what causes this. You didn’t share your script so I can’t give you pointers on your code but, things you should look at are pausing when scrubbing and not continuously seeking. Try this script and check if it improves your solution.
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;
public class ScrubbingController : MonoBehaviour
{
public VideoPlayer player;
private bool isSeeking;
private Slider slider;
void Awake()
{
slider = GetComponent<Slider>();
}
private void Player_seekCompleted(VideoPlayer source)
{
isSeeking = false;
}
void Start()
{
slider.maxValue = (float)player.length;
}
public void BeginScrub()
{
//It is recommended to pause the player when seeking as otherwise,
//you will continuously fight the VideoPlayer from playing and buffering frames.
player.Pause();
//To know when the player has finished seeking
player.seekCompleted += Player_seekCompleted;
isSeeking = false;
}
public void UpdateScrub()
{
//If you are currently seeking there is no point to seek again.
if (isSeeking)
return;
//Don't seek, if the time between the slider value and the current player time is too small.
//We will seek to the closest frame so if the delta is 0.00001f you will most likely seek the same frame.
//Change the value to fit your use case.
if(Mathf.Abs((float)player.time - slider.value) < 0.01f)
return;
player.time = slider.value;
isSeeking = true;
}
public void EndScrub()
{
//You don't want random event when you are not using this script
player.seekCompleted -= Player_seekCompleted;
player.Play();
}
}
Thanks for getting back and for showing how it’s supposed to be used. I didn’t find it this detailed in the documentary. I’m basically only using VideoPlayer.time to get the frame, but I try to make sure that I don’t do that every update by comparing the calculated frame:
public void UpdateVideoFrameTo (float time, float maxTime)
{
// maxFrames is VideoPlayer.frameCount
calculatedFrame = (long)(maxFrames / maxTime * time);
if (calculatedFrame != currentFrame)
{
currentFrame = calculatedFrame;
//vp.frame = currentFrame; // vp.frame doesn't work as good as vp.time, because sometimes it kinda just resets the video and then start from the beginning
//print(currentFrame);
vp.time = time;
}
}
What I should mention is that before I use this function in my slider, I pause the video once using VideoPlayer.Pause().
Do you see any issue with my implementation?
Do you check if you are currently seeking with player.seekCompleted?