一、效果
获取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;
using System;
using System.Linq;
public struct ThumbnailData
{
public Action OnComplete; //回调函数
public float thumbnailTime; //时间戳,用于生成缩略图的位置
public MediaPlayer mediaPlayer;
}
public class ThumbnailGenerator : MonoBehaviour
{
public MediaPlayer mediaPlayer;
ThumbnailData thumbnailData;//缩略图数据
List<string> videoPaths = new List<string>();//所有视频路径
void Start()
{
//获取指定文件夹下的所有视频
GetAllVideoFile($"{Application.streamingAssetsPath}");
//创建缩略图数据
thumbnailData = new ThumbnailData()
{
OnComplete = () => { Debug.Log("********创建结束*******"); },
thumbnailTime = 5,//缩略图时间
mediaPlayer = mediaPlayer,
};
//创建缩略图
GenThumbnail(thumbnailData);
}
#region 获取指定文件夹所有视频文件
void GetAllVideoFile(string path)
{
//获取文件夹下指定类型视频
videoPaths = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories)
.Where(s => IsAssignTypeFile(s)).ToList();
}
/// <summary>
/// 判断是否是指定类型文件
/// </summary>
/// <param name="suffix">文件后缀</param>
/// <returns></returns>
bool IsAssignTypeFile(string suffix)
{
string[] types = ".vid|.mp4|.wav".Split('|');//设置指定后缀
//是指定类型
for (int i = 0; i < types.Length; i++)
if (suffix.EndsWith(types[i])) return true;
return false;
}
#endregion
#region 创建缩略图
public void GenThumbnail(ThumbnailData _thumbnailData)
{
thumbnailData = _thumbnailData;
//视频状态监听 创建
thumbnailData.mediaPlayer.Events.AddListener(OnVideoEvent);
thumbnailData.mediaPlayer.OpenMedia(MediaPathType.AbsolutePathOrURL, videoPaths[0], false);
thumbnailData.mediaPlayer.Control.Seek(thumbnailData.thumbnailTime);// 执行Seek操作
}
#endregion
#region 视频状态监听
int index;//当前视频地址
private void OnVideoEvent(MediaPlayer mp, MediaPlayerEvent.EventType et, ErrorCode errorCode)
{
switch (et)
{
case MediaPlayerEvent.EventType.FinishedSeeking:
Debug.Log("搜索完成时触发");
//本地创建缩略图
CreateThumbnailsLocally(mp);
break;
}
}
#endregion
#region 在本地创建缩略图
void CreateThumbnailsLocally(MediaPlayer mp)
{
string path = mp.MediaPath.Path;
//获取视频 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 < videoPaths.Count)
{
Debug.Log("加载下一个缩略图:" + videoPaths[index]);
//播放视频
mp.OpenMedia(MediaPathType.AbsolutePathOrURL, videoPaths[index], false);
mp.Control.Seek(thumbnailData.thumbnailTime);// 执行Seek操作
}
else
thumbnailData.OnComplete?.Invoke();//执行回调
}
#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需要将AutoOpen和AutoPlay关掉