unity 使用自带的videoplayer获取视频缩略图
主要用到的API:
yield return new WaitUntil(() => thumbVideoPlayer.isPrepared);
yield return new WaitUntil(() => thumbVideoPlayer.frame >= frameSet);
tempRenderTex = thumbVideoPlayer.texture as RenderTexture;
RenderTexture.active = tempRenderTex;
var frameTex = new Texture2D((int)thumbVideoPlayer.width, (int)thumbVideoPlayer.height, TextureFormat.RGB24, false);//创建新的texture2D
frameTex.ReadPixels(new Rect(0, 0, tempRenderTex.width, tempRenderTex.height), 0, 0);
frameTex.Apply();
return Sprite.Create(frameTex, new Rect(0, 0, frameTex.width, frameTex.height), new Vector2(0.5f, 0.5f));//创建sprite
关键代码如下(一下使用vp代替videoplayer)
IEnumerator LoadAssets()
{
yield return null;
for (int t = 0; t < testVideoPath.Count; t++)
{
FileItem fileItem = new FileItem();
thumbVideoPlayer.url = testVideoPath[t];
yield return new WaitForEndOfFrame();
thumbVideoPlayer.Prepare();
yield return new WaitUntil(() => thumbVideoPlayer.isPrepared);//等待准备完毕
thumbVideoPlayer.Play();
yield return new WaitUntil(() => thumbVideoPlayer.frame >= frameSet);//等待播放帧数大于设置帧数
fileItem.sprite = GetThumbSprite();
fileItem.name = Path.GetFileName(testVideoPath[t]);
thumbVideoPlayer.Stop();
yield return new WaitForEndOfFrame();
fileItems.Add(fileItem);
}
yield return new WaitForEndOfFrame();
Debug.Log("加载完毕");
OnAllAssetsLoaded?.Invoke();
}
private Sprite GetThumbSprite()
{
tempRenderTex = thumbVideoPlayer.texture as RenderTexture;
RenderTexture.active = tempRenderTex;
var frameTex = new Texture2D((int)thumbVideoPlayer.width, (int)thumbVideoPlayer.height, TextureFormat.RGB24, false);
frameTex.ReadPixels(new Rect(0, 0, tempRenderTex.width, tempRenderTex.height), 0, 0);
frameTex.Apply();
return Sprite.Create(frameTex, new Rect(0, 0, frameTex.width, frameTex.height), new Vector2(0.5f, 0.5f));
}
主要就是使用vp准备(Prepare),等待准备完毕开始播放,等待vp的帧数大于预设的帧数,就可以进行下一步来获取当前帧的sprite(根据自己的需求可以只获取texture2D)。
下面是用于测试的完整代码``
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Video;
using System;
using System.IO;
using UnityEngine.UI;
public class TestLoadVideoFrameTex : MonoBehaviour
{
public string dicPath = @"F:\第二季 SIKIC#中级教程 (2015版)\视频";//视频文件夹地址,根据自己需求随意
public List<string> testVideoPath = new List<string>();//测试路径地址(原先想直接输入地址的,后来想直接代码获取文件夹内的视频地址)
private VideoPlayer thumbVideoPlayer;//临时的视频播放器,也可以直接在unity里设置好
private GameObject thumbVideoObj;
private int frameSet = 5;//预设的需要获取的第几帧;
private RenderTexture tempRenderTex = default;//这个根据自己的需求设置
public List<FileItem> fileItems = new List<FileItem>();//测试用的list
private event Action OnAllAssetsLoaded;//测试 用于资源都加载完成时的加载下图片的
public Transform Content;//测试用放置预览图片的父物体
// Start is called before the first frame update
void Start()
{
OnAllAssetsLoaded += AllAssetsLoaded;
if (Directory.Exists(dicPath))
{
string[] filePath = Directory.GetFiles(dicPath, "*.mp4");
for (int i = 0; i < filePath.Length; i++)
{
testVideoPath.Add(filePath[i]);
}
}
InitThumbVideo();
StartCoroutine(LoadAssets());
}
/// <summary>
/// 资源全部加载完成时要处理的内容
/// </summary>
private void AllAssetsLoaded()
{
for (int i = 0; i < fileItems.Count; i++)
{
var img = new GameObject("img").AddComponent<Image>();
img.transform.SetParent(Content);
#region //这部分都是不想动手拖,结果还是写了一大堆,最好还是根据自己的需求做成prefab吧
var text = new GameObject("text").AddComponent<Text>();
text.transform.SetParent(img.transform);
text.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
text.alignment = TextAnchor.MiddleCenter;
text.color = Color.white;
text.fontSize = 16;
text.horizontalOverflow = HorizontalWrapMode.Overflow;
text.verticalOverflow = VerticalWrapMode.Overflow;
text.alignment = TextAnchor.LowerCenter;
text.text = fileItems[i].name;
text.rectTransform.pivot = new Vector2(0.5f, 0);
text.rectTransform.anchorMin = new Vector2(0.5f, 0);
text.rectTransform.anchorMax = new Vector2(0.5f, 0);
text.rectTransform.anchoredPosition = new Vector2(0, -20);
#endregion
img.sprite = fileItems[i].sprite;
}
Debug.Log("赋值完成");
}
/// <summary>
/// 初始化下videoplayer,自己也可以提前在unity里就设置好
/// </summary>
private void InitThumbVideo()
{
thumbVideoObj = new GameObject("thumbVideo");
thumbVideoPlayer = thumbVideoObj.AddComponent<VideoPlayer>();
thumbVideoPlayer.source = VideoSource.Url;
thumbVideoPlayer.playOnAwake = false;
thumbVideoPlayer.audioOutputMode = VideoAudioOutputMode.None;
}
/// <summary>
/// 协程加载资源
/// </summary>
/// <returns></returns>
IEnumerator LoadAssets()
{
yield return null;
for (int t = 0; t < testVideoPath.Count; t++)
{
FileItem fileItem = new FileItem();
thumbVideoPlayer.url = testVideoPath[t];
yield return new WaitForEndOfFrame();
thumbVideoPlayer.Prepare();
yield return new WaitUntil(() => thumbVideoPlayer.isPrepared);//等待准备完毕
thumbVideoPlayer.Play();
yield return new WaitUntil(() => thumbVideoPlayer.frame >= frameSet);//等待播放帧数大于设置帧数
fileItem.sprite = GetThumbSprite();
fileItem.name = Path.GetFileName(testVideoPath[t]);
thumbVideoPlayer.Stop();
yield return new WaitForEndOfFrame();
fileItems.Add(fileItem);
}
yield return new WaitForEndOfFrame();
Debug.Log("加载完毕");
OnAllAssetsLoaded?.Invoke();
//用完不需要了,销毁吧
if (thumbVideoObj!=null )
{
Destroy(thumbVideoObj);
}
}
/// <summary>
/// 获取缩略图
/// </summary>
/// <returns></returns>
private Sprite GetThumbSprite()
{
tempRenderTex = thumbVideoPlayer.texture as RenderTexture;
RenderTexture.active = tempRenderTex;
var frameTex = new Texture2D((int)thumbVideoPlayer.width, (int)thumbVideoPlayer.height, TextureFormat.RGB24, false);//创建新的texture2D
frameTex.ReadPixels(new Rect(0, 0, tempRenderTex.width, tempRenderTex.height), 0, 0);
frameTex.Apply();
return Sprite.Create(frameTex, new Rect(0, 0, frameTex.width, frameTex.height), new Vector2(0.5f, 0.5f));//创建sprite
}
}
/// <summary>
/// 临时测试用的
/// </summary>
[Serializable]
public class FileItem
{
public Sprite sprite;
public string name;
}
网上找了很久,也没找到合适的,觉得这种方式还可以,就记录一下,
主要是感觉算是比较快的,也不需要给vp的targettexture赋值,也不需要创建RenderTexture,
也不用使用vp的那种使用事件的方式(thumbVideoPlayer.frameReady)。感觉还能优化,希望路过的大神能再给点优化意见。