一、效果
获取StreamingAssets文件夹下的所有视频(包含其子文件夹),获取指定时间的视频画面,然后将图片保存到本地磁盘中。
二、关于Avpro的事件监听
当指定视频时间进度时会触发FinishedSeeking,代表加载完成这时我们在进行缩略图创建功能,否则视频帧未更新创建缩略图会出现问题。在第三步脚本中我还使用协程的方式判断视频帧是否加载完成。
//使用方法
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;}}
三、脚本
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());//获取视频 RenderTextureRenderTexture renderTexture = GetVideoRenderTexture(mp);//将RenderTexture转换成texture2DTexture2D 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("搜索完成时触发");//获取视频 RenderTextureRenderTexture renderTexture = GetVideoRenderTexture(mp);//将RenderTexture转换成texture2DTexture2D 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 获取视频 RenderTextureMaterial 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转换为Texture2Dprivate 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需要将AutoOpen和AutoPlay关掉