Unit 高效开发系列 - 第六章 Unity 视频播放、压缩、图片与视频互转

前言

Uinty高效开发系列主要包含一系列的Unity开发实例,主要来源于笔者的一个3D游戏项目和一个手机APP项目,适合利用Unity开发游戏或手机APP场景。

一、Unity 视频播放、压缩、图片与视频互转实现

  • Unity 视频播放、压缩、图片与视频互转,支持windows、Android平台,Iphone没有测试过,理论上可以。

1.安卓平台文件访问权限设置

  • 安卓平台访问手机平台需要有相应权限,安卓平台需要进行一些特殊处理,在“ProjectSettings\ProjectSettings.asset”文件设置ForceSDCardPermission 为1;
  • 程序启动时执行RequestPermission请求存储读写权限:
public static void RequestPermission()
    {
#if UNITY_ANDROID && !UNITY_EDITOR
       string permissionr = UnityEngine.Android.Permission.ExternalStorageRead; 
       if (!UnityEngine.Android.Permission.HasUserAuthorizedPermission(permissionr)) 
       { 
           UnityEngine.Android.Permission.RequestUserPermission(permissionr); 
       } 

       string permissionw = UnityEngine.Android.Permission.ExternalStorageWrite; 
       if (!UnityEngine.Android.Permission.HasUserAuthorizedPermission(permissionw)) 
       { 
           UnityEngine.Android.Permission.RequestUserPermission(permissionw); 
       } 
#endif
    }

2.安卓平台文件单选、多选,图片、视频文件加载

public struct ParaStrc
{
    public string path;
    public GameObject go;
    public ParaStrc(string _path, GameObject _go)
    {
        path = _path;
        go = _go;
    }
}

public void GetImagePath(string imagePath, Image img,ref List<string> lstPath)
{
    Debug.Log("GetImagePath");
    if (imagePath == null)
        return;
    if (img != null)
    {
        string strpath = imagePath;
        if (strpath.IndexOf(".jpg") > 0 || strpath.IndexOf(".JPG") > 0)
        {
            ChangeJpgToPng(strpath);
        }
        strpath = strpath.Replace(".jpg", ".png");
        strpath = strpath.Replace(".JPG", ".png");

        lstPath.Add(strpath);    

        ParaStrc para = new ParaStrc(strpath, img.gameObject);
        StartCoroutine("LoadImage", (object)para);           
    }        
}
public void GetImagePaths(string[] imagePaths,Image[] lstImg, ref List<string> lstPath)
{
    Debug.Log("GetImagePath");
    if (imagePaths == null)
        return;
    int idx = 0;
    foreach (string strpath in imagePaths)
    {
        if (lstPath.Count < 5)
        {
            GetImagePath(strpath, lstImg[lstPath.Count],ref lstPath);
        }
        else
        {
            break;
        }           
    }
}
 private IEnumerator LoadImage(object paraIn)
 {        
     ParaStrc para =(ParaStrc)paraIn;
     Debug.Log("LoadImage:" + para.path);
     WWW www = new WWW("file://" + para.path);
     yield return www;
     if (www.error == null)
     {          
         Image img = para.go.transform.GetComponent<Image>();
         if(img != null)
         {
             Debug.Log("img.sprite != null");
             img.sprite = Texture2D2Sprite( www.texture);  
         }   
         else {
             Debug.Log("img == null");
         }
     }
     else
     {
         Debug.Log("LoadImage>>>www.error:");
         Debug.LogError("LoadImage>>>www.error:" + www.error);
     }
 }
 private void selectPic
 _Android()
 {            
      g_androidFileSystem.OpenAndroidFile((filePath) => {
          if (filePath.IndexOf("|") > 0)
          {
              string[] paths = filePath.Split('|');                 
              GetImagePaths(paths,g_picImages, ref g_picPaths);
          }
          else
          {
              string strpath = filePath;
              Debug.Log("strpath:" + strpath);
              if(g_picPaths.Count < 5)
              {
                  GetImagePath(strpath, g_picImages[g_picPaths.Count],ref g_picPaths);
              }
             
          }      
     },AndriodFileTypeEnum.AFT_Picture,true );
 }

3.播放视频

unity 播放视频通常需要引入插件,笔者用的AVPro Video是在商城买的,时间有点久,忘记多少钱了。

unity 如何判断timeline播放完毕 unity视频播放_Android

public void OnMuteChange()
{
    if (PlayingPlayer)
    {
        PlayingPlayer.Control.MuteAudio(g_MuteToggle.isOn);
    }       
}

public void OnPlayButton()
{
    if (PlayingPlayer)
    {
        PlayingPlayer.Control.Play();          
    }
}
public void OnPauseButton()
{
    if (PlayingPlayer)
    {
        PlayingPlayer.Control.Pause();            
    }
}

public void OnVideoSeekSlider()
{
    if (PlayingPlayer && g_videoSeekSlider && g_videoSeekSlider.value != g_setVideoSeekSliderValue)
    {
        PlayingPlayer.Control.Seek(g_videoSeekSlider.value * PlayingPlayer.Info.GetDurationMs());
    }
}

public void OnVideoSliderDown()
{
    if (PlayingPlayer)
    {
        g_wasPlayingOnScrub = PlayingPlayer.Control.IsPlaying();
        if (g_wasPlayingOnScrub)
        {
            PlayingPlayer.Control.Pause();                
        }
        OnVideoSeekSlider();
    }
}
public void OnVideoSliderUp()
{
    if (PlayingPlayer && g_wasPlayingOnScrub)
    {
        PlayingPlayer.Control.Play();
        g_wasPlayingOnScrub = false;           
    }
}

public void OnRewindButton()
{
    if (PlayingPlayer)
    {
        PlayingPlayer.Control.Rewind();
    }
}

3.图片压缩成Mp4

笔者在一个项目中需要客户端上传大量图片到服务器存储,花了大量时间研究图片高保真压缩算法,最后研究发现,把图片压缩成MP4是压缩比最高的方案。如下图所示,5张图片总大小2.8M,压缩成MP4后大小为174k,压缩比达16.25倍。

unity 如何判断timeline播放完毕 unity视频播放_Android_02

unity 如何判断timeline播放完毕 unity视频播放_unity_03

  • 需要特别说明一下,FFmpeg在Android平台压缩多张jpg图片时,只能保存一张图,windows平台下每问题,为解决Android平台多张Jgp压缩问题,示例工程会先将JPG转换为PNG。
public static void ChangeJpgToPng(string path)
{
    List<string> cmds = new List<string>();
    cmds.Add("-y");
    cmds.Add("-i");
    cmds.Add(path);
    string outpath = path.Replace(".jpg", ".png");
    cmds.Add(outpath);
    Wrapper.Execute(cmds.ToArray());


    int cnt = 0;
    while (FFmpegWrapper.isFinish == false && cnt < 20)
    {
        Thread.Sleep(500);
        cnt++;
    }
    if (File.Exists(outpath))
    {
        Debug.Log("!!!!!!!!! File.Exists,cnt:" + cnt);          
    }
}

4.Mp4解压成图片

步骤3中压缩的MP4解压还原成图片,总大小1.7M,占原始图片的60%大小。压缩前后图片效果无明显差异:

unity 如何判断timeline播放完毕 unity视频播放_ide_04

unity 如何判断timeline播放完毕 unity视频播放_Android_05


unity 如何判断timeline播放完毕 unity视频播放_视频编解码_06

4.视频文件压缩

视频文件压缩也采用FFmpeg进行处理,视频压缩可调整压缩参数调整压缩比:

unity 如何判断timeline播放完毕 unity视频播放_unity_07

二、示例工程编译及运行

  • 解压VedioPro.zip文件,使用unity 2019.2.2f1版本打开VedioPro文件夹。

三、资源下载