Hi, I’m having an issue regarding the VideoPlayer component on Unity2017.1
What I want to do is that I want to seek video to a specific frame. The Play(), Pause() are not suitable for my purpose, because I need to accurately control with respect to frame index.
Here is a simple script I used.
In the editor and iOS I can set VideoPlayer.frame, and video jumps to the specified frame index properly.
But on android, it just won’t work. The video never seek to the frame.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Video;
[RequireComponent(typeof(VideoPlayer))]
public class VideoControl : MonoBehaviour {
public enum SeekMode{FrameIndex, NormalizedTime}
[SerializeField] SeekMode mode = SeekMode.NormalizedTime;
[SerializeField] ulong frameIndex;
[SerializeField, Range(0,1)] float normalizedTime;
[SerializeField, Range(0.01f, 0.5f)] float damp = 0.1f;
//=== PROPERTIES
public VideoClip Clip{ get{ return video.clip;}}
public SeekMode Mode { get{ return mode;} set{mode = value;}}
public float Damp{
get{ return damp;}
set{ damp = Mathf.Max(0.01f, Mathf.Min(value, 0.5f));}
}
public bool IsVideoPrepared{ get{ return isVideoPrepared;}}
public ulong FrameCount{ get{ return video.frameCount;}}
public float FrameRate{ get{ return video.frameRate;}}
//=== INTERNAL REFERENCES
VideoPlayer video;
//=== FLAGS
bool isVideoPrepared;
// Use this for initialization
void Awake () {
video = GetComponent<VideoPlayer>();
video.errorReceived += Video_ErrorReceived;
video.prepareCompleted += Video_PrepareCompleted;
video.frameDropped += Video_FrameDropped;
video.Prepare();
VideoCheck();
}
float t;
float vel;
// Update is called once per frame
void Update () {
if(isVideoPrepared){
switch(mode){
case SeekMode.FrameIndex :
t = Mathf.SmoothDamp(t, frameIndex, ref vel, damp);
video.frame = (long)t;
break;
case SeekMode.NormalizedTime :
t = Mathf.SmoothDamp(t, normalizedTime, ref vel, damp);
video.time = (t * video.clip.length);
break;
}
}
}
public void SetVideo(VideoClip _clip){
video.clip = _clip;
video.Prepare();
normalizedTime = 0f;
frameIndex = 0;
t=0;
VideoCheck();
}
void VideoCheck(){
Debug.Log("=== Video check ===");
Debug.Log("canStep : " + video.canStep);
Debug.Log("canSetTime : " + video.canSetTime);
Debug.Log("canSetTimeSource : " + video.canSetTimeSource);
Debug.Log("canSetSkipOnDrop " + video.canSetSkipOnDrop);
Debug.Log("canSetPlaybackSpeed : " + video.canSetPlaybackSpeed);
Debug.Log("canSetDirectAudioVolume : " + video.canSetDirectAudioVolume);
}
public void SetNormalizedTime(float _0To1){
normalizedTime = Mathf.Max(0, Mathf.Min(1, _0To1));
}
public void SetTargetFrameIndex(ulong _frameIndex){
frameIndex = _frameIndex >= video.frameCount ? video.frameCount-1 : _frameIndex;
}
void Video_ErrorReceived(VideoPlayer source, string message)
{
Debug.LogError("Video error : "+source.name+" / "+message);
}
void Video_PrepareCompleted(VideoPlayer source)
{
Debug.Log("Video preparation completed.");
isVideoPrepared = true;
}
void Video_FrameDropped(VideoPlayer source)
{
Debug.LogWarning("Video frame dropped!");
}
}
You can check the script with a simple slider using SetNormalizedTime(float) as an EventListener.
Also, I tried the VideoPlayer.Pause(). From the Unity Scripting API Reference, Pause() 'Pauses the playback and leaves the current time intact.’ On the Editor and iOS, when the Pause() is called the video frame is fixed to the current time, but on Android it goes back to the first frame (like Stop()).