unity5.6开始增加了videoPlayer,使得视频播放相对比较简单,项目需求进行了一下研究应用,也遇到很多坑,Google 百度一下发现确实有这些问题,一些简单问题如下:
1)播放无声音
2)通过slider控制播放进度
3)视频截图(texture->texture2d)
4)视频结束时事件激活
基本上无太大问题,以上四个问题解决方案在下文红色文字区域,先介绍一下video Player应用,后续对这四个问题进行解决。
(一)新建video Player 可以在ui下田间video Play组建,也可以直接右键-video-videoplayer,添加后可以看到如下图所示的组件
本文主要重点说一下一下参数:source有两种模式clip模式和url模式,clip则可以直接通过videoClip进行播放,url则可以通过url进行播放。renderMode为渲染模式,既可以为camera,material等,如果是使用ui播放的选用render texture,本文采用此模式。audioOutputMode有三种,none模式,direct模式(没尝试)和audiosource模式,本文采用audiosource模式,采用此模式时只需要将audiosource组建拖入上图中videoPlayer中的audiosource参数槽中即可,不需要任何处理,但有时会出现拖入后videoPlayer中的audiosource参数槽消失,且无声音播放,所以一般采用代码添加,如下所示:
//代码添加 videoPlayer = gameObject.AddComponent<VideoPlayer>(); //videoPlayer = gameObject.GetComponent<VideoPlayer>(); audioSource = gameObject.AddComponent<AudioSource>(); //audioSource = gameObject.GetComponent<AudioSource>(); videoPlayer.playOnAwake = false; audioSource.playOnAwake = false; audioSource.Pause();
(二)视频播放的控制与音频/动画播放类似,videoPlayer有play/pause等方法,具体可以参见后面完整代码。
在调用视频播放结束时事件loopPointReached(此处为借鉴别人称呼,此事件其实并不是视频播放结束时的事件),顾名思义,此事件为达到视频播放循环点时的事件,即当videoplay 的isLooping属性为true(即循环播放视频)时,视频结束时调用此方法,所以当视频非循环播放时,此事件在视频结束时调用不到。要想调用此方法可以把视频设置为循环播放,在loopPointReached指定的事件中停止播放视频
(三)关于视频播放的ui选择问题,选用render texture时需要指定target texture。
1)在project面板上create-renderTexture,并把新建的renderTexture拖到videoplayer相应的参数槽上
2)在Hierarchy面板上新建ui-RawImage,并把上一步新建的renderTexture拖到RawImage的texture上即可。
其实可以不用这么处理,videoPlayer有texture变量,直接在update里面把texture值赋给RawImage的texture即可,代码如下
rawImage.texture = videoPlayer.texture;
视频截图时可以通过videoPlayer.texture,把图像保存下来不过需要把texture转变为texture2d,虽然后者继承在前者,但是无法强制转货回去,转换以及存储图片代码如下:
private void SaveRenderTextureToPNG(Texture inputTex, string file) { RenderTexture temp = RenderTexture.GetTemporary(inputTex.width, inputTex.height, 0, RenderTextureFormat.ARGB32); Graphics.Blit(inputTex, temp); Texture2D tex2D = GetRTPixels(temp); RenderTexture.ReleaseTemporary(temp); File.WriteAllBytes(file, tex2D.EncodeToPNG()); } private Texture2D GetRTPixels(RenderTexture rt) { RenderTexture currentActiveRT = RenderTexture.active; RenderTexture.active = rt; Texture2D tex = new Texture2D(rt.width, rt.height); tex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0); RenderTexture.active = currentActiveRT; return tex; }
最后说一下通过slider控制视频播放进度的问题,
通过slider控制视频播放存在两个问题,一方面在update实时把videoPlayer.time 赋值给slider,一方面需要把slider的value反馈给time,如果用slider的OnValueChanged(float value) 方法则存在矛盾,导致问题。所以可以通过UI事件的BeginDrag和EndDrag事件
事件进行,即当BeginDrag时,停止给slider赋值,当EndDrag时重新开始赋值。如下图所示
全代码
using System; using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEngine.UI; using UnityEngine.Video; public class VideoController : MonoBehaviour { public GameObject screen; public Text videoLength; public Text currentLength; public Slider volumeSlider; public Slider videoSlider; private string video1Url; private string video2Url; private VideoPlayer videoPlayer; private AudioSource audioSource; private RawImage videoScreen; private float lastCountTime = 0; private float totalPlayTime = 0; private float totalVideoLength = 0; private bool b_firstVideo = true; private bool b_adjustVideo = false; private bool b_skip = false; private bool b_capture = false; private string imageDir =@"D:\test\Test\bwadmRe"; // Use this for initialization void Start () { videoScreen = screen.GetComponent<RawImage>(); string dir = Path.Combine(Application.streamingAssetsPath,"Test"); video1Url = Path.Combine(dir, "01.mp4"); video2Url = Path.Combine(dir, "02.mp4"); //代码添加 videoPlayer = gameObject.AddComponent<VideoPlayer>(); //videoPlayer = gameObject.GetComponent<VideoPlayer>(); audioSource = gameObject.AddComponent<AudioSource>(); //audioSource = gameObject.GetComponent<AudioSource>(); videoPlayer.playOnAwake = false; audioSource.playOnAwake = false; audioSource.Pause(); videoPlayer.audioOutputMode = VideoAudioOutputMode.AudioSource; videoPlayer.SetTargetAudioSource(0, audioSource); VideoInfoInit(video1Url); videoPlayer.loopPointReached += OnFinish; } #region private method private void VideoInfoInit(string url) { videoPlayer.source = VideoSource.Url; videoPlayer.url = url; videoPlayer.prepareCompleted += OnPrepared; videoPlayer.isLooping = true; videoPlayer.Prepare(); } private void OnPrepared(VideoPlayer player) { player.Play(); totalVideoLength = videoPlayer.frameCount / videoPlayer.frameRate; videoSlider.maxValue = totalVideoLength; videoLength.text = FloatToTime(totalVideoLength); lastCountTime = 0; totalPlayTime = 0; } private string FloatToTime(float time) { int hour = (int)time / 3600; int min = (int)(time - hour * 3600) / 60; int sec = (int)(int)(time - hour * 3600) % 60; string text = string.Format("{0:D2}:{1:D2}:{2:D2}", hour, min, sec); return text; } private IEnumerator PlayTime(int count) { for(int i=0;i<count;i++) { yield return null; } videoSlider.value = (float)videoPlayer.time; //videoSlider.value = videoSlider.maxValue * (time / totalVideoLength); } private void OnFinish(VideoPlayer player) { Debug.Log("finished"); } private void SaveRenderTextureToPNG(Texture inputTex, string file) { RenderTexture temp = RenderTexture.GetTemporary(inputTex.width, inputTex.height, 0, RenderTextureFormat.ARGB32); Graphics.Blit(inputTex, temp); Texture2D tex2D = GetRTPixels(temp); RenderTexture.ReleaseTemporary(temp); File.WriteAllBytes(file, tex2D.EncodeToPNG()); } private Texture2D GetRTPixels(RenderTexture rt) { RenderTexture currentActiveRT = RenderTexture.active; RenderTexture.active = rt; Texture2D tex = new Texture2D(rt.width, rt.height); tex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0); RenderTexture.active = currentActiveRT; return tex; } #endregion #region public method //开始 public void OnStart() { videoPlayer.Play(); } //暂停 public void OnPause() { videoPlayer.Pause(); } //下一个 public void OnNext() { string nextUrl = b_firstVideo ? video2Url : video1Url; b_firstVideo = !b_firstVideo; videoSlider.value = 0; VideoInfoInit(nextUrl); } //音量控制 public void OnVolumeChanged(float value) { audioSource.volume = value; } //视频控制 public void OnVideoChanged(float value) { //videoPlayer.time = value; //print(value); //print(value); } public void OnPointerDown() { b_adjustVideo = true; b_skip = true; videoPlayer.Pause(); //OnVideoChanged(); //print("down"); } public void OnPointerUp() { videoPlayer.time = videoSlider.value; videoPlayer.Play(); b_adjustVideo = false; //print("up"); } public void OnCapture() { b_capture = true; } #endregion // Update is called once per frame void Update () { if (videoPlayer.isPlaying) { videoScreen.texture = videoPlayer.texture; float time = (float)videoPlayer.time; currentLength.text = FloatToTime(time); if(b_capture) { string name = DateTime.Now.Minute.ToString() + "_" + DateTime.Now.Second.ToString() + ".png"; SaveRenderTextureToPNG(videoPlayer.texture,Path.Combine(imageDir,name)); b_capture = false; } if(!b_adjustVideo) { totalPlayTime += Time.deltaTime; if (!b_skip) { videoSlider.value = (float)videoPlayer.time; lastCountTime = totalPlayTime; } if (totalPlayTime - lastCountTime >= 0.8f) { b_skip = false; } } //StartCoroutine(PlayTime(15)); } } }
如果使用插件AVPro Video所有问题不是问题