Unity之获取Avpro视频画面并在本地创建缩略图

一、效果

获取StreamingAssets文件夹下的所有视频(包含其子文件夹),获取指定时间的视频画面,然后将图片保存到本地磁盘中。

二、关于Avpro的事件监听

当指定视频时间进度时会触发FinishedSeeking,代表加载完成这时我们在进行缩略图创建功能,否则视频帧未更新创建缩略图会出现问题。在第三步脚本中我还使用协程的方式判断视频帧是否加载完成。

cs 复制代码
//使用方法   
mediaPlayer.Events.AddListener(OnVideoEvent);


//监听
 private void OnVideoEvent(MediaPlayer mp, MediaPlayerEvent.EventType et, ErrorCode errorCode)
    {
        switch (et)
        {
            case MediaPlayerEvent.EventType.MetaDataReady:
                Debug.Log("当元数据(宽度,持续时间等)可用时触发");
                
                break;
            case MediaPlayerEvent.EventType.ReadyToPlay:
                Debug.Log("可以播放");
                break;
            case MediaPlayerEvent.EventType.Started:
                Debug.Log("播放开始时触发");
                break;
            case MediaPlayerEvent.EventType.FirstFrameReady:
                Debug.Log("第一帧渲染完成");

                break;
            case MediaPlayerEvent.EventType.FinishedPlaying:
                Debug.Log("视频结束");
                break;
            case MediaPlayerEvent.EventType.Closing:
                Debug.Log("媒体关闭时触发");
                
                break;
            case MediaPlayerEvent.EventType.Error:
                Debug.Log("发生错误时触发");
                
                break;
            case MediaPlayerEvent.EventType.SubtitleChange:
                Debug.Log("字幕改变时触发");

                break;
            case MediaPlayerEvent.EventType.Stalled:
                Debug.Log("当介质停止时触发(例如。当失去与媒体流的连接时)");

                break;
            case MediaPlayerEvent.EventType.Unstalled:
                Debug.Log("当媒体从停止状态恢复时触发(例如。当失去的连接重新建立时)");

                break;
            case MediaPlayerEvent.EventType.ResolutionChanged:
                Debug.Log("当视频的分辨率发生变化(包括加载)时触发,用于自适应流");

                break;
            case MediaPlayerEvent.EventType.StartedSeeking:
                Debug.Log("搜索开始时触发");

                break;
            case MediaPlayerEvent.EventType.FinishedSeeking:
                Debug.Log("搜索完成时触发 Seek视频指定时间跳转结束后调用");
        

                break;
            case MediaPlayerEvent.EventType.StartedBuffering:
                Debug.Log("缓冲开始时触发");

                break;
            case MediaPlayerEvent.EventType.FinishedBuffering:
                Debug.Log("缓冲完成时触发");

                break;
            case MediaPlayerEvent.EventType.PropertiesChanged:
                Debug.Log("当任何属性被触发(例如立体声包装被改变)-这必须手动触发");

                break;
            case MediaPlayerEvent.EventType.PlaylistItemChanged:
                Debug.Log("当新项目在播放列表中播放时触发");

                break;
            case MediaPlayerEvent.EventType.PlaylistFinished:
                Debug.Log("当播放列表结束时触发");

                break;
            case MediaPlayerEvent.EventType.TextTracksChanged:
                Debug.Log("当添加或删除文本轨道时触发");

                break;
        	
          
        }
    }

三、脚本

cs 复制代码
using System.IO;
using UnityEngine;
using RenderHeads.Media.AVProVideo;
using System.Collections.Generic;
using System.Collections;

public class ThumbnailGenerator : MonoBehaviour
{

    public MediaPlayer mediaPlayer;
    public float thumbnailTime = 1.0f;  //时间戳,用于生成缩略图的位置

    int index;//当前视频地址
    List<string> filesPath = new List<string>();
    void Start()
    {
        string[] videoFiles = Directory.GetFiles(Application.streamingAssetsPath, "*.*", SearchOption.AllDirectories);

        foreach (string videoFile in videoFiles)
        {
            string extension = Path.GetExtension(videoFile).ToLower();
            if (extension == ".mp4" || extension == ".avi" || extension == ".mov")  // 检查支持的视频格式
                filesPath.Add(videoFile);//添加所有视频地址
        }

        //方法一 协程创建 
        //StartCoroutine(SeekAndWaitForLoad(mediaPlayer, thumbnailTime, filesPath[0]));

        //方法二 视频状态监听 创建
        mediaPlayer.Events.AddListener(OnVideoEvent);
        mediaPlayer.OpenMedia(MediaPathType.AbsolutePathOrURL, filesPath[0], false);
        mediaPlayer.Control.Seek(thumbnailTime);// 执行Seek操作

    }


    #region 方法一 使用协程创建

    /// <summary>
    /// 创建视频缩略图
    /// </summary>
    /// <param name="mp">视频播放器</param>
    /// <param name="time">视频指定时间</param>
    /// <param name="path">视频地址</param>
    /// <returns></returns>
    private IEnumerator SeekAndWaitForLoad(MediaPlayer mp, float time, string path)
    {
        mp.OpenMedia(MediaPathType.AbsolutePathOrURL, path, false);
        // 执行Seek操作
        mp.Control.Seek(time);

        // 等待寻址完成
        yield return new WaitUntil(() => !mp.Control.IsSeeking());

        // 确保播放时间已经跳转到期望的时间点
        yield return new WaitUntil(() => Mathf.Abs((float)mp.Control.GetCurrentTime() - time) < 0.1f);

        // 可选:等待视频帧更新完成 (确保画面已经渲染)
        yield return new WaitForEndOfFrame();

        Debug.Log("寻找完整的和视频帧加载时间: " + mp.Control.GetCurrentTime());

        //获取视频 RenderTexture
        RenderTexture renderTexture = GetVideoRenderTexture(mp);

        //将RenderTexture转换成texture2D
        Texture2D texture2D = RenderTexture2Texture2D(renderTexture);

        //将Texture2D写入本地
        string previewPath = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path)) + ".png";
        Texture2dWriteLocal(texture2D, previewPath);

        //创建下一个缩略图
        index++;
        if (index < filesPath.Count)
            StartCoroutine(SeekAndWaitForLoad(mediaPlayer, thumbnailTime, filesPath[0]));
    }
    #endregion

    #region 方法二 视频状态监听
    private void OnVideoEvent(MediaPlayer mp, MediaPlayerEvent.EventType et, ErrorCode errorCode)
    {
        string path = mp.MediaPath.Path;
        switch (et)
        {
            case MediaPlayerEvent.EventType.FinishedSeeking:
                Debug.Log("搜索完成时触发");

                //获取视频 RenderTexture
                RenderTexture renderTexture = GetVideoRenderTexture(mp);

                //将RenderTexture转换成texture2D
                Texture2D texture2D = RenderTexture2Texture2D(renderTexture);

                //将Texture2D写入本地
                string previewPath = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path)) + ".png";
                Texture2dWriteLocal(texture2D, previewPath);

                //创建下一个缩略图
                index++;
                if (index < filesPath.Count)
                {
                    mp.OpenMedia(MediaPathType.AbsolutePathOrURL, filesPath[index], false);
                    mp.Control.Seek(thumbnailTime);// 执行Seek操作
                }
                break;
        }

    }

    #endregion



    #region 获取视频 RenderTexture
    Material mt;
    RenderTexture GetVideoRenderTexture(MediaPlayer mp)
    {
        if (mt == null)
        {
            mt = new Material(Shader.Find("AVProVideo/Internal/Resolve"));
            mt.color = Color.white;//颜色设置
            mt.DisableKeyword("USE_HSBC");//禁用USE_HSBC关键字
        }
        VideoRender.SetupMaterialForMedia(mt, mp, -1); //设置材质贴图等

        VideoRender.ResolveFlags resolveFlags = (VideoRender.ResolveFlags.ColorspaceSRGB | VideoRender.ResolveFlags.Mipmaps | VideoRender.ResolveFlags.PackedAlpha | VideoRender.ResolveFlags.StereoLeft);//播放器标志

        return VideoRender.ResolveVideoToRenderTexture(mt, null, mp.TextureProducer, resolveFlags);//将视频解析为RenderTexture
    }
    #endregion

    #region 将RenderTexture转换为Texture2D
    private static Texture2D RenderTexture2Texture2D(RenderTexture renderTexture)
    {
        int width = renderTexture.width;
        int height = renderTexture.height;
        Texture2D texture2D = new Texture2D(width, height, TextureFormat.ARGB32, false);
        RenderTexture.active = renderTexture;
        texture2D.ReadPixels(new Rect(0, 0, width, height), 0, 0);
        texture2D.wrapMode = TextureWrapMode.Clamp;
        texture2D.Apply();
        return texture2D;
    }
    #endregion

    #region 将Texture2d写入本地
    void Texture2dWriteLocal(Texture2D texture2D, string localPath)
    {
        File.WriteAllBytes(localPath, texture2D.EncodeToPNG());
    }
    #endregion

}

在使用avpro制作缩略图时,我尝试使用mediaPlayer.TextureProducer.GetTexture();方法获取画面Texture,然后将其写入RenderTexture,在转换Texture2D写入本地会发现缩略图颜色泛白,经过排查MediaPlayer原脚本,发现在转换RenderTexture时需要使用avpro的指定shader处理才会显示正确的材质,感兴趣的朋友可以打开MediaPlayer脚本进行查看
改版

MediaPlayer脚本

原脚本查看方法:

注意:添加的MediaPlayer需要将AutoOpen和AutoPlay关掉

相关推荐
中关村科金26 分钟前
中关村科金推出得助音视频鸿蒙SDK,助力金融业务系统鸿蒙化提速
华为·音视频·harmonyos
谢泽浩43 分钟前
Unity 给模型贴上照片
unity·游戏引擎
z2014z43 分钟前
Unity Resource System 优化笔记
unity·游戏引擎
王维志1 小时前
Unity 高亮插件HighlightPlus介绍
unity·游戏引擎
DisonTangor1 小时前
上海人工智能实验室开源视频生成模型Vchitect 2.0 可生成20秒高清视频
人工智能·音视频
美狐美颜sdk1 小时前
探索视频美颜SDK与直播美颜工具的开发实践方案
人工智能·计算机视觉·音视频·直播美颜sdk·视频美颜sdk
Mr数据杨2 小时前
我的AI工具箱Tauri版-FasterWhisper音频转文本
音视频
zaizai10072 小时前
我的demo保卫萝卜中的技术要点
unity
Mr数据杨2 小时前
我的AI工具箱Tauri版-FunAsr音频转文本
音视频
菌菌巧乐兹3 小时前
Unity 百度AI实现无绿幕拍照抠像功能(详解版)
人工智能·百度·unity