Unity之VideoPlayer视频播放(二)

一、效果

全屏缩小画面、播放暂停、进度条拖拽、视频播放时长、倍速、音量等功能。 长时间不移动鼠标自动隐藏播放控制器、鼠标离开倍速、音量时隐藏倍速、音量ui

二、脚本

cs 复制代码
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Video;


public class VideoController : MonoBehaviour
{
    [Header("视频进度时间")] 
    [SerializeField] Text sumTimeTxt;        //总时间
    [SerializeField] Text currentTimeTxt;    //当前时间
    [SerializeField] Slider scheduleSlider;  //进度条

    [Header("播放按钮")] 
    [SerializeField] Sprite playSprite;      //播放
    [SerializeField] Sprite pauseSprite;     //暂停
    [SerializeField] Button playBtn;         //播放暂停
    [SerializeField] GameObject playIcon;    //播放图标

    [Header("加载视频按钮")] 
    [SerializeField] Button lastBtn;         //上一个
    [SerializeField] Button nextBtn;         //下一个
    
    [Header("视频类型切换按钮")] 
    [SerializeField] Text videoTypeTxt;      //视频类型文本
    [SerializeField] Button videoTypeBtn;    //视频类型按钮

    [Header("音量按钮")]
    // [SerializeField] Sprite volumCross;   //静音
    // [SerializeField] Sprite volumMiddle;  //未静音
    [SerializeField] Button volumBtn;                //音量按钮
    [SerializeField] Slider volumSlider;             //音量滑动条
    [SerializeField] CanvasGroup volumbg;            //音量背景(渐隐)
    [SerializeField] AudioSource audioSource;        //音量控制器
    [SerializeField] List<Transform> volumMaskList;  //音量遮罩(ui检测用)

    [Header("倍速")]
    [SerializeField] Color32 selectColor=new Color32(0, 99, 242, 255); 
    [SerializeField] Dropdown speedDropdown;        //倍速
    [SerializeField] List<Transform> speedMaskList; //倍速遮罩(ui检测用)

    [Header("全屏小屏控制器物体列表")]
    [SerializeField] List<Transform> fullControllerObjList;   //播放控制器物体(ui检测用)
    [SerializeField] List<Transform> lessenControllerObjList; //播放控制器物体(ui检测用)
    
    [Header("放大缩小按钮")] 
    [SerializeField] Button fullBtn;   //放大按钮
    [SerializeField] Button lessenBtn; //缩小按钮
    [SerializeField] Button lessenPlayBtn; //小屏播放按钮

    RenderTexture rt;
    [Header("视频参数")] 
    [SerializeField] RawImage img; //视频画面
    [SerializeField] VideoPlayer videoPlayer;

    [Header("节点")] 
    [SerializeField] GameObject root;

    [SerializeField] List<VideoClip> clipList;
    [SerializeField] List<string> urlList;

    //单例
    public static VideoController instance;

    private void Awake()
    {
        instance = this;
    }

    void Start()
    {
        // SwitchVideoType(); //默认设置视频类型

        #region 委托添加

        videoPlayer.prepareCompleted += PrepareCompleted; //准备完成

        videoPlayer.loopPointReached += LoopPointReached; //播放结束

        videoPlayer.started += Started; //Play后立即调用

        videoPlayer.frameDropped += FrameDropped; //有丢帧发生时被执行

        videoPlayer.errorReceived += ErrorReceived; //通过此回调报告 HTTP 连接问题等错误。

        videoPlayer.seekCompleted += SeekCompleted; //查询帧操作完成时被执行

        videoPlayer.frameReady += FrameReady; //新的一帧准备好时被执行

        #endregion

        //视频播放
        playBtn.onClick.AddListener(PlayVideo);
        lessenPlayBtn.onClick.AddListener(PlayVideo);
        img.GetComponent<Button>().onClick.AddListener(PlayVideo); //视频画布可控制播放暂停

        //上一个下一个视频切换
        lastBtn.onClick.AddListener(() => { SwitchVideo(-1); });
        nextBtn.onClick.AddListener(() => { SwitchVideo(1); });

        //视频类型切换
        videoTypeBtn.onClick.AddListener(SwitchVideoType);

        //音量
        volumBtn.onClick.AddListener(VolumeShowOrHide);
        volumSlider.onValueChanged.AddListener(SetSliderVolume);
        volumFade=new FadeOutData{canvasGroup=volumbg,onComplete = VolumOnComplete};
        
        //倍速playSpeedDropdownOnclike
        speedDropdown.onValueChanged.AddListener(PlayBackSpeed);
        speedDropdown.value = 3;//默认设置为1倍速

        //视频
        lessenBtn.onClick.AddListener(VideoLessen);
        fullBtn.onClick.AddListener(VideoFull);
    }

    void Update()
    {
        #region UI物体检测

        //全屏状态下执行
        if (isVideoFull)
        {
            //全屏模式下控制器显示隐藏检测
            if (isVideoFull)
                FullControllerShow();
            
            //音量物体显示&& 鼠标未在音量物体上时
            if (!ismute && !RayDetectionUI(Input.mousePosition, volumMaskList))
                VolumeShowOrHide(); //音量物体隐藏

            //倍速下拉框开启
            if (speedDropdown.transform.Find("Dropdown List") )
            {
                if (!RayDetectionUI(Input.mousePosition, speedMaskList))
                    speedDropdown.Hide();//鼠标不在倍速mask上时 隐藏下拉列表
            }
            else if (isSpeedRaycast)
            {
                //倍速物体射线启用 && 倍速下拉框处于关闭时 设置Mask物体不可射线检测
                isSpeedRaycast = false;
                foreach (var item in speedMaskList)
                    item.GetComponent<Image>().raycastTarget = isSpeedRaycast;
            }
        }

        #endregion
      
    }

    private void LateUpdate()
    {
        #region 视频总时间长度、视频已播放时长、滑动条设置

        // 在播放时 && 进度条没有被按下
        if (videoPlayer.isPlaying && !isSliderDrag)
        {
            SetTxtTimer((float)videoPlayer.time, currentTimeTxt); //视频时间显示
            scheduleSlider.value = (float)(videoPlayer.time / (videoPlayer.frameCount / videoPlayer.frameRate)); //进度条
        }

        #endregion

        //根据视频加载情况设置是否禁用滑动条
        // scheduleSlider.interactable = videoPlayer.isPrepared;
    }

    #region 视频小屏和大屏播放

    private bool isVideoFull;//当前视频是否是全屏 true全屏
    //小屏
    void VideoLessen()
    {
        isVideoFull = false;
        //全屏控制器隐藏
        foreach (var item in fullControllerObjList)
            item.gameObject.SetActive(false);
        
        //全屏控制器显示
        foreach (var item in lessenControllerObjList)
            item.gameObject.SetActive(true);
        
        playIcon.SetActive(false);//小屏模式下隐藏
        
        videoPlayer.isLooping=true;
        //缩小屏幕
        RectTransform rect = root.GetComponent<RectTransform>();
        rect.anchorMax = rect.anchorMin = new Vector2(0, 1f);
        rect.anchoredPosition = new Vector2(240f, -630f);
        rect.sizeDelta = new Vector2(400f, 225f);
        
        //屏幕缩小 kienct右下角画面显示、角度提示显示
        KinectManager.Instance.computeColorMap = true;
        // CalculatedAngle.instance.gameObject.SetActive(true);

    }

    //全屏
    void VideoFull()
    {
        isVideoFull = true;
        //全屏控制器显示
        foreach (var item in fullControllerObjList)
            item.gameObject.SetActive(true);
        
        //全屏控制器隐藏
        foreach (var item in lessenControllerObjList)
            item.gameObject.SetActive(false);
        
        playIcon.SetActive(isPlay);//全屏模式并且暂停状态下显示

        videoPlayer.isLooping=false;
        
        //放大屏幕
        RectTransform rect = root.GetComponent<RectTransform>();
        rect.anchorMin = new Vector2(0, 0);
        rect.anchorMax = new Vector2(1, 1);
        rect.anchoredPosition = rect.sizeDelta = new Vector2(0f, 0f);
        
        //屏幕放大 kienct右下角画面隐藏、角度提示隐藏
        KinectManager.Instance.computeColorMap = false;
        // CalculatedAngle.instance.gameObject.SetActive(false);
    }

    #endregion
    
    #region 全屏模式下 播放控制器显示隐藏

    private Vector3 oldMousePos;
    void FullControllerShow()
    {
        // 当全屏状态下当前鼠标位置与上次鼠标位置不同时显示播放控制器
        if (Input.mousePosition!=oldMousePos)
        {
            oldMousePos = Input.mousePosition;
            foreach (var VARIABLE in fullControllerObjList)
                VARIABLE.gameObject.SetActive(true);

            //取消invoke
            if (IsInvoking("InvokeControllerHide"))
                CancelInvoke("InvokeControllerHide");
            
            //不在控制器ui下时
            if (!RayDetectionUI(Input.mousePosition,fullControllerObjList))
                Invoke("InvokeControllerHide", 3f);//开启3秒控制器隐藏
           
        }
    }

    //全屏控制器3秒隐藏
    void InvokeControllerHide()
    {
        foreach (var VARIABLE in fullControllerObjList)
            VARIABLE.gameObject.SetActive(false);
    }

    #endregion
    
    #region 修改指定文本时长

    /// <summary>
    /// 修改指定文本时长
    /// </summary>
    void SetTxtTimer(float time, Text timeTxt)
    {
        TimeData timeData = VideoTimeConversion(time);

        //时:分:秒
        // timeTxt.text = timeData == null
        //     ? "00:00:00"
        //     : $"{timeData.h.ToString("00")}:{timeData.m.ToString("00")}:{timeData.s.ToString("00")}";

        //分:秒
        int m = timeData.h * 60 + timeData.m;
        timeTxt.text = timeData == null
            ? "00:00"
            : $"{m.ToString("00")}:{timeData.s.ToString("00")}";
    }

    //时间数据
    class TimeData
    {
        public int h;
        public int m;
        public int s;
    }

    /// <summary>
    /// video时间换算
    /// </summary>
    /// <param name="totalTime">总时长</param>
    /// <returns></returns>
    TimeData VideoTimeConversion(float totalTime)
    {
        TimeData data = new TimeData();

        /*1.VideoPlayer.time / 3600 通过时间获取(其中Time表示当前可用时间)*/
        data.h = (int)totalTime / 3600;
        data.m = (int)(totalTime - data.h * 3600) / 60;
        data.s = (int)totalTime % 60;

        #region 使用帧方式换算(暂不用)

        /*2.VideoPlayer.frameCount / VideoPlayer.frameRate 通过帧获取(总帧数/1秒多少帧)*/

        //int timer = (int)(videoPlayer.frameCount / videoPlayer.frameRate);
        //int h = timer / (60 * 60);//时
        //int m = (timer - (h * 3600)) / 60;//分
        //int s = timer % 60;//秒

        #endregion

        return data;
    }

    #endregion

    #region 视频委托

    //视频准备完成
    void PrepareCompleted(VideoPlayer _player)
    {
        // Debug.Log("视频准备完成" + _player.length);

        //设置视频总时长
        SetTxtTimer((float)_player.length, sumTimeTxt);

        //设置RT
        rt = CrteateRT((int)_player.clip.width, (int)_player.clip.height);
        _player.targetTexture = rt;
        img.texture = rt;

        //播放视频
        isPlay = true;
        PlayVideo(); //播放视频
    }

    private bool isVideoEnd;
    //视频播放结束
    void LoopPointReached(VideoPlayer _player)
    {
        Debug.Log("视频播放结束");
        _player.Stop();

        isVideoEnd = true;

        //视频播放按钮状态修改
        isPlay = true;
        lessenPlayBtn.gameObject.SetActive(true);
        playBtn.image.sprite = playSprite;
    }

    //Play开始播放
    void Started(VideoPlayer _player)
    {
        Debug.Log("Play开始播放");
    }

    //有丢帧发生时被执行
    void FrameDropped(VideoPlayer _player)
    {
        Debug.Log("有丢帧发生时被执行");
    }

    //查询帧操作完成时被执行
    void SeekCompleted(VideoPlayer _player)
    {
        Debug.Log("查询帧操作完成时被执行");
        isSliderDrag = false;
        
        //播放视频
        isPlay = true;
        PlayVideo();
    }

    //通过此回调报告 HTTP 连接问题等错误。
    void ErrorReceived(VideoPlayer _player, string message)
    {
        Debug.Log("通过此回调报告 HTTP 连接问题等错误。" + message);
    }

    //新的一帧准备好时被执行
    void FrameReady(VideoPlayer _player, long frameIdx)
    {
        Debug.Log("新的一帧准备好时被执行。" + frameIdx);
    }

    #endregion

    #region 创建RenderTexture

    public RenderTexture CrteateRT(int width, int hight)
    {
        RenderTexture rt = new RenderTexture(width, hight, 0, RenderTextureFormat.ARGB32);
        rt.Create();
        return rt;
    }

    #endregion

    #region 滑动条控制

    bool isSliderDrag;

    //滑动条按下
    public void ScheduleSliderDown()
    {
        Debug.Log("slider按下");
        isSliderDrag = true;
    }

    private float DragTime; //滑动条拖动的时间

    // 滑动条拖动
    public void ScheduleSliderDrag()
    {
        isSliderDrag = true;
        DragTime = scheduleSlider.value * (videoPlayer.frameCount / videoPlayer.frameRate);
        SetTxtTimer(DragTime, currentTimeTxt); //修改视频已播放时长
    }

    //滑动条抬起
    public void ScheduleSliderUP()
    {
        Debug.Log("slider抬起");
        videoPlayer.time = DragTime;           //修改播放进度
        
        if (isVideoEnd)
        {
            isVideoEnd = false;
            isSliderDrag = false;
            isPlay = true;
            PlayVideo();
        }
    }
    
    #endregion

    #region 播放控制

    bool isPlay = true; //默认下次播放

    private void PlayVideo()
    {
        Debug.Log($"调用Play方法:{isPlay}");
        
        if (isPlay)
        {

            videoPlayer.Play(); //播放
            lessenPlayBtn.gameObject.SetActive(false);
        }
        else
        {
            videoPlayer.Pause(); //暂停
            lessenPlayBtn.gameObject.SetActive(true);
        }

        playIcon.SetActive(isVideoFull&&!isPlay);//全屏模式并且暂停状态下显示

        playBtn.image.sprite = isPlay ? pauseSprite : playSprite;
        isPlay = !isPlay;
    }

    #endregion

    #region 视频类型切换

    void SwitchVideoType()
    {
        currentVideoIndex = -1; //视频当前下标

        switch (videoPlayer.source)
        {
            case VideoSource.VideoClip:
                videoTypeTxt.text = "URL";
                videoPlayer.source = VideoSource.Url;

                break;
            case VideoSource.Url:
                videoTypeTxt.text = "Clip";
                videoPlayer.source = VideoSource.VideoClip;
                break;
        }

        SwitchVideo(1); //播放视频
    }

    #endregion

    #region 上一个下一个视频

    int currentVideoIndex;

    void SwitchVideo(int index)
    {
        isPlay = true; //默认下一次播放

        currentVideoIndex += index;

        switch (videoPlayer.source)
        {
            case VideoSource.VideoClip:

                if (currentVideoIndex > clipList.Count - 1)
                    currentVideoIndex = clipList.Count - 1;

                if (currentVideoIndex < 0)
                    currentVideoIndex = 0;

                videoPlayer.clip = clipList[currentVideoIndex];

                break;
            case VideoSource.Url:

                if (currentVideoIndex > urlList.Count - 1)
                    currentVideoIndex = urlList.Count - 1;

                if (currentVideoIndex < 0)
                    currentVideoIndex = 0;

                videoPlayer.url = urlList[currentVideoIndex];

                break;
        }

        videoPlayer.Prepare(); //视频预先加载准备
    }

    #endregion

    #region 播放指定视频

    public void PlayAssignedVideo(string videoName)
    {
        foreach (var clip in clipList)
        {
            if (clip.name==videoName)
            {
                videoPlayer.clip = clip;
                videoPlayer.Prepare(); //视频预先加载准备
                return;
            }
        }
        
        Debug.Log($"{videoName}");
    }
    
    public void PlayAssignedVideo(VideoClip clip)
    {
        OpenPanel(); //开启面板
        videoPlayer.clip = clip;
        videoPlayer.Prepare(); //视频预先加载准备
    }

    #endregion

    #region 音量控制

    bool ismute = true;
    FadeOutData volumFade;
    IEnumerator volumeFadeOut; //当前渐隐协程
    /// <summary>
    /// 音量显示隐藏
    /// </summary>
    void VolumeShowOrHide()
    {
        ismute = !ismute;
        //设置Mask物体的射线检测效果
        foreach (var item in volumMaskList)
            item.GetComponent<Image>().raycastTarget = !ismute;
        
        //判断是否有音量协程在执行
        if (volumFade.isFading) StopCoroutine(volumeFadeOut);
        
        //渐隐
        volumFade.fadeOutValue = ismute ? 0 : 1;
         
        //设置当前执行的音量协程
        volumeFadeOut = FadeOut(volumFade);
        StartCoroutine(volumeFadeOut);

        // 点击按钮静音并存储当前音量
        // if (ismute)
        //     volumSlider.value = oldVolumeValue;//不静音 还原音量
        // else
        // {
        //     //静音
        //     oldVolumeValue = audioSource.volume; //记录原先音频数值
        //     volumSlider.value = 0;
        // }
    }

    //音量渐隐回调
    void VolumOnComplete()
    {
        // 渐隐结束 音量背景显示隐藏
        volumbg.gameObject.SetActive(!ismute);
    }

    private float oldVolumeValue; //静音前的原始音频

    //音量滑动条监听
    void SetSliderVolume(float value)
    {
        // Debug.Log("滑动条value");
        audioSource.volume = value;   //设置音频音量

        // 按钮静音
        // ismute = value > 0;
        // volumBtn.image.sprite = ismute ? volumMiddle:volumCross;//设置静音图片效果
    }

    #endregion

    #region 倍速控制

    private bool isSpeedRaycast;//倍速物体射线是否启用
    //Dropdown下的挂载的Event Trigger调用
    public void playSpeedDropdownOnclike()
    {
        isSpeedRaycast = true;
        
        //设置Mask物体可以射线检测
        foreach (var item in speedMaskList)
            item.GetComponent<Image>().raycastTarget = isSpeedRaycast;
        
        // 遍历所有选项
        for (int i = 0; i < speedDropdown.options.Count; i++)
        {
            Text optionText = speedDropdown.transform.Find("Dropdown List/Viewport/Content").GetChild(i).GetComponentInChildren<Text>();

            if (i == speedDropdown.value+1)
            {
                // 设置选中选项颜色
                optionText.color = selectColor;
            }
            else
            {
                // 设置默认选项颜色
            }
        }
    }

    public void PlayBackSpeed(int value)
    {
        videoPlayer.playbackSpeed = float.Parse(speedDropdown.options[value].text.Split('x')[0]);
        //Debug.Log(videoPlayer.playbackSpeed);
    }

    #endregion

    #region 开启关闭视频

    public void OpenPanel()
    {
        root.SetActive(true);
    }

    public void ClosePanel()
    {
        root.SetActive(false);
    }

    #endregion

    #region UI渐隐
    
    public class FadeOutData
    {
        public bool isFading;           //是否正在渐隐
        public float duration = 0.15f;  //渐隐时间
        public float fadeOutValue;      //渐隐值
        public CanvasGroup canvasGroup; //渐隐物体

        public Action onComplete;       //回调函数

    }
    /// <summary>
    /// 音量渐隐
    /// </summary>
    private IEnumerator FadeOut(FadeOutData data)
    {
        //显示物体
        data.canvasGroup.gameObject.SetActive(true);

        data.isFading = true;
        float elapsedTime = 0f;

        // 设置当前的 alpha 值
        float startAlpha = data.canvasGroup.alpha ;
        // canvasGroup.alpha = startAlpha;
        
        //N秒渐隐
        while (elapsedTime < data.duration)
        {
            elapsedTime += Time.deltaTime;

            // 计算新的 alpha 值
            float newAlpha = Mathf.Lerp(startAlpha, data.fadeOutValue, elapsedTime / 1);
            data.canvasGroup.alpha = newAlpha;

            yield return null; // 等待下一帧
        }

        // 确保最终 alpha 值为 最终值
        data.canvasGroup.alpha = data.fadeOutValue;

        //结束渐隐
        data.isFading = false;
        
        //执行回调
        data.onComplete?.Invoke();
       
    }
    #endregion
    
    #region UI触发检测

    /// <summary>
    /// Ui触发检测
    /// </summary>
    /// <param name="position">点击屏幕坐标</param>
    /// <param name="detectionObjs">识别物体</param>
    /// <returns></returns>
    private bool RayDetectionUI(Vector2 position, List<Transform> detectionObjs)
    {
        //识别物体为空直接返回false
        if (detectionObjs == null)
        {
            Debug.Log("识别物体为空");
            return false;
        }
        
        EventSystem eventSystem = EventSystem.current;
        PointerEventData pointerEventData = new PointerEventData(eventSystem);
        pointerEventData.position = position;
        //射线检测ui
        List<RaycastResult> uiRaycastResultCache = new List<RaycastResult>();
        eventSystem.RaycastAll(pointerEventData, uiRaycastResultCache);
        //检测鼠标是否在 指定UI上
        foreach (var result in uiRaycastResultCache)
        {
            if (detectionObjs.IndexOf(result.gameObject.transform)!=-1)
                return true;
        }

        return false;
    }

    #endregion

}

三、下载链接

https://pan.baidu.com/s/1ShS1de7X7JWzSdT4PRicyQ?pwd=syq1

相关推荐
TechVoyager_82467 小时前
多形态输入与12 通道音频:IT6615 这颗 HDMI 发射器的接口宽度
音视频·音频芯片·芯片选型·视频发射
Raymond16887 小时前
AI配音工具长文本合成架构技术解析:全局音色锚定与声学状态跟踪
人工智能·架构·实时互动·视觉检测·音视频·语音识别
hflmwl813 小时前
Wise Data Recovery找回误删的文档照片音乐和视频
音视频·datarecovery·找回误删的文档照片音乐和视频·找回误删的文档照片音乐视频
技灵AI14 小时前
AI 视频转场提示词怎么写?让前后画面衔接更自然
人工智能·prompt·aigc·音视频
youandyouyou16 小时前
跨区培训的屏幕共享怎么做才稳?同屏同步与弱网优化实践
音视频·网络通信
开开心心就好16 小时前
课堂随机点名工具,绿色版免安装双击即用
java·服务器·前端·ffmpeg·音视频·perl·symfony
美狐美颜SDK开放平台16 小时前
直播APP集成美颜SDK,如何梳理第三方组件的数据权限?
开发语言·人工智能·深度学习·音视频·第三方美颜sdk
l143723326717 小时前
批量视频硬字幕擦除多少钱?影响报价的关键因素全解析
音视频
重生六道18 小时前
B站视频时长统计:一段JS脚本搞定分P/合集总时长(附倍速计算)
开发语言·javascript·音视频
音视频牛哥18 小时前
不换摄像机,也能接入 WebRTC?SmartMediaKit RTSP/RTMP 转 WHIP 技术实践
音视频·webrtc·smartmediakit·rtsp转whip·rtmp转whip·whip推流·rtsp转webrtc播放