0

I'm working on a script where there is a video divided into 3 parts with a respective 3 audios for them that run one after the previous one is finished (both video and audio), however the video need to play only when holding a key (in my case its "space")i successfully made the video and the audio play while pressing on space:

if (Input.GetButton ("Jump")) {
                vp.Play ();
                if (!ASource.isPlaying) {
                    ASource.Play ();
                }

And pause while not :

else {
                vp.Pause ();
                ASource.Pause ();

            }

But its a hard cut, im looking for a way to make it smoothly pause/resume, i tried making a function :

public void videoPause() {
        for (i = 0; i < 10; i++) {
            vp.playbackSpeed = vp.playbackSpeed / i;
        }

    }

same for resume but with * instead of and i-- but it didint work, any idea how to make it work please ?

2 Answers2

1

See this:

for (i = 0; i < 10; i++) {
    vp.playbackSpeed = vp.playbackSpeed / i;
}

You are doing all those in one frame therefore won't be able to see the effect. Move that to a coroutine function then yield after each for loop then you will see the effect.

Something like this:

for (i = 0; i < 10; i++) {
    vp.playbackSpeed = vp.playbackSpeed / i;
    yield return null; //Wait for a frame
    //OR
    yield return new WaitForSeconds(0.1f); //Wait for 0.1 sec
}

You should now see the effect of that code but it will not be smooth.


What to do:

The lerp functions are usually used for something like this. Mathf.Lerp is appropriate for this one.

The default/normal value of VideoPlayer.playbackSpeed is 1. The value of 2 is faster and 0 is slower.

  • When you want to pause the VideoPlayer, lerp from 1 to 0 to slow it down then pause it.
  • When you want to resume the VideoPlayer, resume it slowly then lerp from 0 to 1.

Here are the two coroutines functions that should handle the pause and resume functionality slowly and smoothly.

IEnumerator SmoothlyPauseOverTimeCOR(VideoPlayer targetVp, float duration)
{
    float counter = 0;
    //Get the current playbackSpeed of the VideoPlayer 
    float startSpeed = targetVp.playbackSpeed;

    //We want to go to 0 but within duration
    float endSpeed = 0;

    //Normal speed to slow speed
    while (counter < duration)
    {
        counter += Time.deltaTime;
        targetVp.playbackSpeed = Mathf.Lerp(startSpeed, endSpeed, counter / duration);
        yield return null;
    }
    //Now, do the actual pause
    targetVp.Pause();
    executingPause = false;
}

IEnumerator SmoothlyResumeOverTimeCOR(VideoPlayer targetVp, float duration)
{
    float counter = 0;
    //Get the current playbackSpeed of the VideoPlayer 
    //float startSpeed = targetVp.playbackSpeed;
    float startSpeed = 0f;

    //We want to go to 1 but within duration
    float endSpeed = 1;


    //Do the actual resume
    targetVp.Play();

    //Slow speed to normal Speed
    while (counter < duration)
    {
        counter += Time.deltaTime;
        targetVp.playbackSpeed = Mathf.Lerp(startSpeed, endSpeed, counter / duration);
        yield return null;
    }
}

You need to make sure that the previous coroutine function is not running before starting a new one. Stop the old one then start a new one when needed. The two functions below should handle that and be able to safely call the functions above:

Coroutine pauseCoroutine;
Coroutine resumeCoroutine;

bool executingPause = false;

void SmoothlyPauseOverTime(VideoPlayer targetVp, float duration)
{
    //Stop old coroutines before starting a new one
    if (pauseCoroutine != null)
        StopCoroutine(pauseCoroutine);

    if (resumeCoroutine != null)
        StopCoroutine(resumeCoroutine);


    executingPause = true;
    pauseCoroutine = StartCoroutine(SmoothlyPauseOverTimeCOR(targetVp, duration));
}

void SmoothlyResumeOverTime(VideoPlayer targetVp, float duration)
{
    if (pauseCoroutine != null)
        StopCoroutine(pauseCoroutine);

    //Stop old coroutines before starting a new one
    if (resumeCoroutine != null)
        StopCoroutine(resumeCoroutine);

    resumeCoroutine = StartCoroutine(SmoothlyResumeOverTimeCOR(targetVp, duration));
}

Your Update function:

void Update()
{
    if (Input.GetButton("Jump"))
    {
        if (!vp.isPlaying)
        {
            Debug.Log("Resumed Playing");
            SmoothlyResumeOverTime(vp, 0.8f);
        }
    }
    else
    {
        if (!executingPause && vp.isPlaying)
        {
            Debug.Log("Paused Playing");
            SmoothlyPauseOverTime(vp, 0.8f);
        }
    }
}

Your Start function:

Based on this. It prepares the video but does not play it until Space key is pressed in the Update function above:

//Raw Image to Show Video Images [Assign from the Editor]
public RawImage image;
//Video To Play [Assign from the Editor]
public VideoClip videoToPlay;

private VideoPlayer vp;
private VideoSource videoSource;

//Audio
private AudioSource aS;

// Use this for initialization
void Start()
{
    Application.runInBackground = true;
    StartCoroutine(playVideo());
}

IEnumerator playVideo()
{
    //Add VideoPlayer to the GameObject
    vp = gameObject.AddComponent<VideoPlayer>();

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

    //Disable Play on Awake for both Video and Audio
    vp.playOnAwake = false;
    aS.playOnAwake = false;

    //We want to play from video clip not from url
    vp.source = VideoSource.VideoClip;

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

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

    //Set video To Play then prepare Audio to prevent Buffering
    vp.clip = videoToPlay;
    vp.Prepare();

    //Wait until video is prepared
    while (!vp.isPrepared)
    {
        Debug.Log("Preparing Video");
        yield return null;
    }

    Debug.Log("Done Preparing Video");

    //Assign the Texture from Video to RawImage to be displayed
    image.texture = vp.texture;
}

Usage:

public VideoPlayer vp;

.....

Will pause slowly within 0.8 second

SmoothlyPauseOverTime(vp, 0.8f);

Will resume slowly within 0.8 second

SmoothlyResumeOverTime(vp, 0.8f);
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • Not on my Unity computer to test but this should get you started. I expect it to compile. If not, fix the minor issues. – Programmer Jan 13 '18 at 14:41
  • Thanks, it works in inspector (playback speed "animate" to 0 or 1) but in the video its kinda weird, it wait for the "speed animation" to finish for the the video to resume mean it didint slowly resume it just start, for pause its okay (it slow before it pause), any idea why ? Its 360 video if this help. – Zoubaier Aouadi Jan 13 '18 at 15:54
  • See the update. Let me know if that fixes your current issue. – Programmer Jan 13 '18 at 19:57
  • Since the pressing the space is done in the update function, now the resume is kinda looping and the video didint resume. – Zoubaier Aouadi Jan 13 '18 at 20:11
  • You also have to make sure that the video is not playing before calling that function.... `if (Input.GetButton("Jump")) { if (!vp.isPlaying) { SmoothlyResumeOverTime(vp, 0.8f); } else { if (vp.isPlaying) { SmoothlyPauseOverTime(vp, 0.8f); } } }` Replace that with the code in your `Update` function. – Programmer Jan 13 '18 at 20:23
  • Its almost the same issue, pause one run smoothly but resume one it waits until PlaybackSpeed become 1 and the video resume, dosent this have something with vp.Prepare() or something ? – Zoubaier Aouadi Jan 13 '18 at 20:31
  • There was a typo in my last comment. I modified every code in the answer so replace all of them with the new code. Also , see the Update and Start function I posted. They should look like that. I tested to see if it works and it looks fine – Programmer Jan 13 '18 at 21:28
  • Thank you, now its a bit confusing for me now to replace what it needs with what i already have etc, so i just edited my original post and included the full script, you can also see my method of synchronizing the sound, can you please check and post the edited one ? i think like this its easier to understand, and thank you for going through this with me :) – Zoubaier Aouadi Jan 13 '18 at 22:31
  • Remove everything in your code then simply copy each function in my answer to your script. Stop where it says **Usage:** in my answer. You don't need those code below it. It should just compile and work. Edit your question if you have done this and it's not work. Also, please roll back the edit you made in your question and add your original code back to your question. When you want to add a modified code from my answer to your question, add **EDIT** to your question followed by the modified code. By doing that, my answer will still make sense since it addressed many mistakes in your code. – Programmer Jan 13 '18 at 22:38
  • I just tested it, its same exact problem, i think the the problem probably from my video card ? i will try and test on another machine tomorrow and see. – Zoubaier Aouadi Jan 13 '18 at 23:12
0

The for loop in videoPause method will run it all at once and not smoothly the way you want it. You could tried updating the playback speed in the update method instead, over a period of time - say 5 seconds. Something like this

// set when the pause is triggered
videoPauseTime = Time.realtimeSinceStartup;
// duration in seconds to smoothen the pause
duration = 5

public void Update() {
    float t = (Time.realtimeSinceStartup - videoPauseTime) / duration;
    if(t>1.0f)
        t = 1.0f;
    vp.playbackSpeed = Mathf.Lerp (vp.playbackSpeed,0.0f,t)
}

Please note that this is code is for illustration only and t will go beyond 1.0 which is something you don't want; hence the condition t > 1.0f. Also, I am assuming playbackSpeed of 0 makes the video to stop playing.

Ajjo
  • 234
  • 1
  • 11