0

My general problem is that I need to play and control the speed of a video (no sound) inside a Unity3D world and probably need to control the decoding myself and I have absolutely no idea how to do that efficiently. So any hints in the right direction are welcome.

I need to play a video projected on a Material in Unity and I need to control the speed of that video at runtime. As I target mobile devices I cannot use MovieTexture. There are alternatives like e.g. Easy Movie Texture but they do not allow me to control the speed of the video.

I found a problematic solution in this post. In short, the author breaks the video into its frames and then displays it frame by frame. This way I can control the video speed by simply changing the time until displaying the next frame. The video does not have any sound, so it's that easy. The problem is that this is a nightmare from a memory and performace point of view. The app would be GB big and inefficient.

So as far as I know a normal video player solves that by not saving and displaying every frame but just delta between them. If I could do that myself and control it frame by frame I'd solve my problem.

I imagine to decode it at runtime and then display the delta's. But I have no idea how to do that. So please point me in the right direction or maybe even give me a solution if you have.

The video format is not yet fixed, so whatever is easiest.

findusl
  • 2,454
  • 8
  • 32
  • 51

1 Answers1

1

You don't need Easy Movie Texture to do this and you don't even need to get the video frames to do this.

With the new Unity VideoPlayer API, you can check if you can set the playback speed on the platform with VideoPlayer.canSetPlaybackSpeed. If it returns true, you can then set the video playback speed by simply changing the videoPlayer.playbackSpeed property.

You can use the code in this answer to play video on RawImage then add the code below to set the playback speed.

if (videoPlayer.canSetPlaybackSpeed)
{
    videoPlayer.playbackSpeed = 1f;
}

It is as simple as that.


You mentioned that you want the image to be projected on a material. In this case, you should set the VideoPlayer.renderMode to VideoRenderMode.MaterialOverride;

The code below should project the video on any GameObject called "DisplayObject". You can also access the material from the video in the outputRenderer or outputRenderer.material variable.

It has audio for testing purposes and you can remove it if you don't want audio like you mentioned in your post.

using UnityEngine;
using UnityEngine.Video;

public class VideoSpeedControl : MonoBehaviour
{
    //The material that Video will output to
    Renderer outputRenderer;

    private VideoPlayer videoPlayer;

    //Audio
    private AudioSource audioSource;

    void Start()
    {

        outputRenderer = gameObject.AddComponent<MeshRenderer>();

        //Add VideoPlayer to the GameObject
        videoPlayer = gameObject.AddComponent<VideoPlayer>();

        //Add AudioSource
        audioSource = gameObject.AddComponent<AudioSource>();

        //Disable Play on Awake for both Video and Audio
        videoPlayer.playOnAwake = false;
        audioSource.playOnAwake = false;

        // We want to play from url
        videoPlayer.source = VideoSource.Url;
        videoPlayer.url = "http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4";

        //Set Audio Output to AudioSource
        videoPlayer.audioOutputMode = VideoAudioOutputMode.AudioSource;

        //Assign the Audio from Video to AudioSource to be played
        videoPlayer.EnableAudioTrack(0, true);
        videoPlayer.SetTargetAudioSource(0, audioSource);

        //Set the mode of output
        videoPlayer.renderMode = VideoRenderMode.MaterialOverride;

        //Set the renderer to store the images to
        //outputRenderer = videoPlayer.targetMaterialRenderer;
        videoPlayer.targetMaterialProperty = "_MainTex";

        //Prepare Video to prevent Buffering
        videoPlayer.Prepare();

        //Subscribe to prepareCompleted event
        videoPlayer.prepareCompleted += OnVideoPrepared;
    }

    void OnVideoPrepared(VideoPlayer source)
    {
        Debug.Log("Done Preparing Video");

        //Play Video
        videoPlayer.Play();

        //Play Sound
        audioSource.Play();

        //Change Play Speed
        if (videoPlayer.canSetPlaybackSpeed)
        {
            videoPlayer.playbackSpeed = 1f;
        }
    }

    bool firsrRun = true;
    void Update()
    {
        if (firsrRun)
        {
            GameObject.Find("DisplayObject").GetComponent<MeshRenderer>().material = outputRenderer.material;
            firsrRun = false;
        }
    }
}
Community
  • 1
  • 1
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • Thanks for the long and exact answer. I currently have an urgent other bugfix to do, but I will come back and test it and mark it as correct if it works :) It's especially important that it works on mobile devices (e.g. Movietexture does not work), even though you edited that part out of my question title and tags... – findusl Mar 23 '17 at 13:44
  • When people say they want to play video in Unity, you should automatically assume that the video should work on every platforms. I see no need to tag *mobile* after reading the question. This answer should work on mobile devices too. – Programmer Mar 23 '17 at 14:23
  • Thanks for your answer. It does answer the question as far as it is here, sadly it does not completely work for me. But I guess I'll do a new question on it. That video player already brought me a lot closer thanks :) – findusl Mar 24 '17 at 15:09