通过FFMpeg将MOV视频转为黑白通道的mp4(可设置上下/左右)
Posted 小白球滚芝麻
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过FFMpeg将MOV视频转为黑白通道的mp4(可设置上下/左右)相关的知识,希望对你有一定的参考价值。
主要是先获取视频的码率,因为在转换过程中需要视频的码率,然后根据命令进行转换。
转完之后的mp4用Avpro去播放具体查Avpro怎么播放黑白通道视频吧。
PlaneVideoLoadTip 是提示进度预制体,自行修改吧。
这个是FFmpeg的下载要放在StreamingAssets文件夹下:
https://download.csdn.net/download/qq_42223582/87051539
using LitJson;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using UnityEngine;
using Debug = UnityEngine.Debug;
/// <summary>
/// 透明视频转换
/// </summary>
public class PlaneVideoFormatConvertMgr : MonoBehaviour
private static PlaneVideoFormatConvertMgr _Instance;
public static PlaneVideoFormatConvertMgr Instance
get
if (_Instance == null)
GameObject obj = Instantiate(Resources.Load<GameObject>("VideoFormatConvertMgr"));
_Instance = obj.GetComponent<PlaneVideoFormatConvertMgr>();
return _Instance;
/// <summary>
/// FFMpeg路径
/// </summary>
public string mFFMpegPath;
public string mFFprobePath;
/// <summary>
/// 当前转换进度
/// </summary>
public float mCurrentProcessValue;
/// <summary>
/// FFmpeg转换数据
/// </summary>
private FFmpegVideoInfo mFFmpegVideoInfo;
/// <summary>
/// 输出信息
/// </summary>
public string mDebugMessage = string.Empty;
/// <summary>
/// FFmpeg返回错误原因列表
/// </summary>
public List<string> mFFmpegErrorList = new List<string>();
/// <summary>
/// 获取视频码率
/// </summary>
private bool mIsGetVideoRate = false;
/// <summary>
/// 是否转换结束
/// </summary>
private bool mIsConvertEnd = false;
/// <summary>
/// 当前视频参数
/// </summary>
public VideoChangeData mCurrentVideoChangeData = new VideoChangeData();
/// <summary>
/// 当前转换所需数据
/// </summary>
public ConvertVideoData mCurrentConvertVideoData = new ConvertVideoData();
/// <summary>
/// FFMpeg线程
/// </summary>
private Thread mRunFFmpegThread;
/// <summary>
/// 转换视频进程
/// </summary>
private Process mFFMpegConvertVideoProcess;
/// <summary>
/// 转换结束
/// bool:是否成功
/// string:转换后的路径
/// </summary>
Action<bool, string> mActionChangeEnd;
void Awake()
mFFMpegPath = Application.streamingAssetsPath + "/FFmpeg/bin/ffmpeg.exe";
mFFprobePath = Application.streamingAssetsPath + "/FFmpeg/bin/ffprobe.exe";
/// <summary>
/// 开始转换视频
/// </summary>
/// <param name="videoChangeData"></param>
/// <param name="actionChangeEnd"></param>
public void StartChangeVideo(VideoChangeData videoChangeData,Action<bool,string> actionChangeEnd)
mFFmpegErrorList.Clear();
mDebugMessage = string.Empty;
mIsGetVideoRate = false;
mActionChangeEnd = actionChangeEnd;
StartCoroutine(StartInitVideo(videoChangeData));
/// <summary>
/// 初始化视频数据
/// </summary>
/// <param name="videoChangeData"></param>
/// <returns></returns>
IEnumerator StartInitVideo(VideoChangeData videoChangeData)
if (string.IsNullOrEmpty(videoChangeData.videoPath))
Debug.Log("mov转换视频路径为空");
yield break;
mCurrentVideoChangeData = videoChangeData;
PlaneVideoLoadTip.Instance.SetPanelState(true);
PlaneVideoLoadTip.Instance.SetCancelAction(() =>
mCurrentProcessValue = 0.0f;
DeleteElement();
);
GetcodeRate();
yield return new WaitUntil(()=>true == mIsGetVideoRate);
StartConvertVideo(videoChangeData);
/// <summary>
/// 开始转换视频
/// </summary>
/// <param name="videoChangeData"></param>
private void StartConvertVideo(VideoChangeData videoChangeData)
mFFmpegVideoInfo = null;
if (mCurrentConvertVideoData == null)
mCurrentConvertVideoData = new ConvertVideoData();
mRunFFmpegThread = new Thread(RunFFmpegExe);
mCurrentConvertVideoData.thread = mRunFFmpegThread;
mCurrentConvertVideoData.inputMovFileName = videoChangeData.videoPath;
switch (videoChangeData.splitType)
case 1:
mCurrentConvertVideoData.outVideoFileName = videoChangeData.videoPath.Replace(".mov", "UD.mp4");
break;
case 2:
mCurrentConvertVideoData.outVideoFileName = videoChangeData.videoPath.Replace(".mov", "RL.mp4");
break;
mRunFFmpegThread.Start(mCurrentConvertVideoData);
/// <summary>
/// FFmpeg执行转换
/// </summary>
/// <param name="obj"></param>
private void RunFFmpegExe(object obj)
mFFMpegConvertVideoProcess = new Process();
mFFMpegConvertVideoProcess.StartInfo.FileName = mFFMpegPath;
if (mCurrentVideoChangeData.splitType == 2)
//转换为左右分离的视频
//p.StartInfo.Arguments = $"-i data.inputMovFileName -c:v libvpx-vp9 data.outVideoFileName";
mFFMpegConvertVideoProcess.StartInfo.Arguments = $"-i \\"mCurrentConvertVideoData.inputMovFileName\\" -c:v libvpx-vp9 -vf \\"scale=trunc(iw/2)*2:trunc(ih/2)*2\\" -vf \\"split[a], pad = iw * 2:ih[b], [a] alphaextract, [b] overlay=w\\" -b mCurrentConvertVideoData.code k -r mCurrentConvertVideoData.frameRate -y \\"mCurrentConvertVideoData.outVideoFileName\\"";
else if (mCurrentVideoChangeData.splitType == 1)
//转换成上下分离的视频
//p.StartInfo.Arguments = $"-i data.inputMovFileName -c:v libvpx-vp9 data.outVideoFileName";
mFFMpegConvertVideoProcess.StartInfo.Arguments = $"-i \\"mCurrentConvertVideoData.inputMovFileName\\" -c:v libvpx-vp9 -vf \\"scale=trunc(iw/2)*2:trunc(ih/2)*2\\" -vf \\"split[a], pad = iw:ih * 2[b], [a] alphaextract, [b] overlay=0:h\\" -b mCurrentConvertVideoData.code k -r mCurrentConvertVideoData.frameRate -y \\"mCurrentConvertVideoData.outVideoFileName\\"";
mFFMpegConvertVideoProcess.StartInfo.UseShellExecute = false;
mFFMpegConvertVideoProcess.StartInfo.RedirectStandardOutput = true;
mFFMpegConvertVideoProcess.StartInfo.CreateNoWindow = true;
mFFMpegConvertVideoProcess.StartInfo.RedirectStandardInput = true;//接受来自调用程序的输入信息
mFFMpegConvertVideoProcess.StartInfo.RedirectStandardOutput = true;//由调用程序获取输出信息
mFFMpegConvertVideoProcess.StartInfo.RedirectStandardError = true;
mFFMpegConvertVideoProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
mFFMpegConvertVideoProcess.Start();
mIsConvertEnd = false;
mFFMpegConvertVideoProcess.BeginErrorReadLine();//开始异步读取
mFFMpegConvertVideoProcess.WaitForExit();
mIsConvertEnd = true;
mFFMpegConvertVideoProcess.Close();
mFFMpegConvertVideoProcess = null;
mCurrentConvertVideoData.isConvertEnd = true;
mCurrentConvertVideoData.thread.Abort();
private void Output(object sendProcess, DataReceivedEventArgs output)
if (!string.IsNullOrEmpty(output.Data))
string log = output.Data;
GetFFMpegProcessInfo(log);
if (mFFmpegVideoInfo != null && mFFmpegVideoInfo.nowProcessFrame > 20 && mFFmpegVideoInfo.videoDuration > 0)
float ConcatintProcessValue = mFFmpegVideoInfo.nowProcessTime / mFFmpegVideoInfo.videoDuration;
double j = Math.Round(ConcatintProcessValue, 2);
float tempValue = (float)j * 100.0f;
mCurrentProcessValue = tempValue;
//UnityEngine.Debug.Log("当前进度:" + tempValue);
private void Update()
if (mIsConvertEnd)
bool mIsError = false;
if (mFFmpegErrorList.Count > 0)
for (int i = 0; i < mFFmpegErrorList.Count; i++)
Debug.Log("mIsConvertEnd:"+ mFFmpegErrorList[i]);
switch (mFFmpegErrorList[i])
//case "Conversion failed!":
case "Input string was not in a correct format.":
SpringWindow.Instance.SetTipPanel("视频错误,请检查视频是否按照正确格式导出!");
break;
//case "Conversion failed!":
// SpringWindow.Instance.SetTipPanel("视频格式错误,默认转为普通视频!");
// break;
case "CreateInputBuffer failed!":
case "CreateInputBuffer failed":
case "CreateBitstreamBuffer failed!":
case "CreateBitstreamBuffer failed":
SpringWindow.Instance.SetTipPanel("视频读取失败,内存不足!");
break;
case "InitializeEncoder failed!":
case "InitializeEncoder failed":
SpringWindow.Instance.SetTipPanel("编辑器出错,请重新启动编辑器!");
break;
mIsError = true;
//如果没有错误,转换下一个
if (!mIsError)
//转换完成
PlaneVideoLoadTip.Instance.SetPanelState(false);
mIsConvertEnd = false;
mActionChangeEnd(true, mCurrentConvertVideoData.outVideoFileName);
else
//转换出错
mIsConvertEnd = false;
PlaneVideoLoadTip.Instance.SetPanelState(false);
mActionChangeEnd(false,"");
mCurrentConvertVideoData.isConvertEnd = false;
CloseGameObject();
Destroy(gameObject);
mCurrentProcessValue = 0.0f;
else
if (mCurrentProcessValue != 0.0f)
PlaneVideoLoadTip.Instance.SetProess(mCurrentProcessValue);
if (mCurrentProcessValue == 100)
mCurrentProcessValue = 0.0f;
private void DeleteElement()
Destroy(Instance.gameObject);
CloseGameObject();
#region 获取FFMpage转换数据
/// <summary>
/// 获取控制进度
/// </summary>
/// <param name="output"></param>
private void GetFFMpegProcessInfo(string output)
try
if (string.IsNullOrEmpty(output))
return;
if (mFFmpegVideoInfo == null)
mFFmpegVideoInfo = new FFmpegVideoInfo();
//UnityEngine.Debug.Log(output);
mDebugMessage = output;
//获取视频详细信息
if (output.Contains("Duration"))
// Duration: 00:00:57.30, start: 0.000000, bitrate: 107512 kb/s
var time = output.Split(',')[0].Remove(0, 11);
var data = time.Split(':');
mFFmpegVideoInfo.videoDuration = ((int.Parse(data[0].Trim()) * 60.0f * 60.0f) + (int.Parse(data[1].Trim()) * 60.0f) + float.Parse(data[2].Trim())) * 1000;
else if (output.Contains("Stream #0:0(und):Audio:"))
else if (output.Contains("Stream #0:0(und):"))
var video_info_group = output.Split(',');
if (video_info_group.Length > 7)
mFFmpegVideoInfo.resolution = video_info_group[2];
mFFmpegVideoInfo.bitRate = video_info_group[3];
mFFmpegVideoInfo.frameRate = int.Parse(video_info_group[4].Replace("fps", "").Trim());
mFFmpegVideoInfo.tbn = video_info_group[5].Replace("tbn", "");
mFFmpegVideoInfo.tbc = video_info_group[6].Replace("(default)", "");
mFFmpegVideoInfo.nowProcessTime = 0;
mFFmpegVideoInfo.nowProcessFrame = 0;
if (output.Contains("hevc"))
mFFmpegVideoInfo.videoType = "h265";
else if (output.Contains("h264"))
mFFmpegVideoInfo.videoType = "h264";
else if (output.Contains("fps") && output.Contains("bitrate") && output.Contains("time"))
int len = output.IndexOf("bitrate");
int start = output.IndexOf("time");
if (len - start - 5 > 0)
string time = output.Substring(start + 5, len - start - 5).Trim();
var data = time.Split(':');
//UnityEngine.Debug.Log("时间为" + time);
mFFmpegVideoInfo.nowProcessTime = ((int.Parse(data[0].Trim()) * 60.0f * 60.0f) + (int.Parse(data[1].Trim()) * 60.0f) + float.Parse(data[2].Trim())) * 1000;
len =<以上是关于通过FFMpeg将MOV视频转为黑白通道的mp4(可设置上下/左右)的主要内容,如果未能解决你的问题,请参考以下文章
PHP FFMPEG - 将.mov转换为.mp4(H264)