Unity3D 5.6 video player and UWP
Unity3D just released version 5.6 with brand new video importer and player that I’ve been waiting for a while. In a few years we’ll be able to say “long gone are the days when you needed to install quicktime and brew an entire day’s worth of coffee to import a 360p 30 second video inside Unity”, and I find this beautiful.
Jokes aside, this means we have a lot more support and control over our video files, and dayum, that player’s good. It’s straightforward, it just works, and it still has a lot of options for the best experience. I was effectively surprised to see that video streaming worked out of the box without requiring $200+ plugins with zero support. (it’s not perfect though, we’ll come to that later)
One of the main features I was waiting for was this streaming from local or web addresses, explicitely for the case where you can’t package all your videos inside your project. I shit you not, I did a youtube video player in about 10 lines of code, and it was really fun. Internet streaming is a go, whatever the platform, it’ll just work. Local streaming is a different story. For starters, mobile operating systems’ permissions. Honestly, it’s a good thing we have them, you don’t want any app modifying your storage space on its own. But honestly, the permissions system on UWP (Windows 10 store apps, and Hololens too (and W10M, but who cares)) is b-r-o-k-e-n. I mean, okay, sandboxing complexifies storage operations, but you still have the right to offer an effective API, which UWP never has, in my humble opinion.
One of the problems I met while testing the new Unity3D video player was with these permissions, on the simple action of reading a local video file that’s not in the project, neither in the app’s local state (where everything is permitted and nothing is true). Let’s say it’s in your video library, you have a couple of things to do before you can read your video :
- add the “Videos Library” capability to your app
- put said video file in the videos library (it’s harder than you think on a Hololens)
Innocently, you’re going to want to load your file like this because that’s how the video player works with streaming :
var path = file.Path;
// next lines must be inside the main thread, somehow
// we'll see later, this is just an example
var player = GetComponent<VideoPlayer>();
player.url = "file:///" + path;
player.prepareCompleted += Player_prepareCompleted;
player.Prepare();
That’s where you realize that it doesn’t work, as usually, you’re going to get something like Access Denied
while trying to run this. The reason is that the video player prepare must be (I actually have no idea) native and it tends to fuck up permissions. So you’ve got to grant it some special permissions with a special class I haven’t heard much about, but that’ll save your life on UWP : Windows.Storage.AccessCache.StorageApplicationPermissions
and more importantly Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList
. What this does is it creates a token that acts like a shortcut inside your app context for easier file access. I honestly don’t think it was meant to be used in this particular context, but hey! for lack of a better way, this works. So the idea is that the path you will give to the video player is the token, not the direct path on your drive. Here’s a full example behavior of something that loads a video from your video library :
// use this for resync
Queue<IEnumerator> coroutines = Queue<IEnumerator>();
void Start () {
// we need async because UWP loves async
// to a point it gets old and boring
GetFiles();
}
async void GetFiles()
{
// get your file
var file = await KnownFolders.VideosLibrary.GetFileAsync("video.mp4");
// generate a token
var path = StorageApplicationPermissions.FutureAccessList.Add(file);
// resync through a coroutine
coroutines.Enqueue(LoadFile(path));
}
IEnumerator LoadFile(string path)
{
yield return WaitForEndOfFrame();
var player = GetComponent<VideoPlayer>();
// player still needs a protocol to know where to look
// here, "file:///" with three '/' because Microsoft
// seems determined to not do anything like the others
player.url = "file:///" + path;
player.prepareCompleted += Player_prepareCompleted;
player.Prepare();
}
private void Player_prepareCompleted(VideoPlayer source)
{
// we've got our video, it's loaded in memory, let's play!
source.Play();
}
// Update is called once per frame
void Update ()
{
// this is kind of lame, but quickest hack in the book to get
// things working when UWP fucks up the main thread
if (coroutines.Count > 0)
{
var action = coroutines.Dequeue();
StartCoroutine(action);
}
}
}
With that little bit of code (and correctly set permissions in your appxmanifest), you get to play 4k hw accelerated videos from your hard drive on your lovely UWP app. Hurray!
This brings me to a final note : where. is. the. documentation? For real though, at the time of writing of this article, 5.6 is RTM and we’re using it on production, but the video player documentation is still a draft gdoc lost behind a link in the official doc. WTF? I don’t mean to criticize Unity’s workflow, and I’ll admit there’s not a lot to explain, but damn, documentation is not something you should forget for a full release on a commercial product. That’ll be all.
Have fun, code safe
Tuxic