Created
May 10, 2018 18:21
-
-
Save SoylentGraham/4a08fbba268f19735f118017ead6116a to your computer and use it in GitHub Desktop.
Use pipes in c# to pull a specific frame (time) from ffmpeg into a texture in unity. SLOW.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using System.Diagnostics; | |
using System.IO; | |
using System; | |
// stolen from https://github.com/keijiro/FFmpegOut/blob/master/Assets/FFmpegOut/FFmpegPipe.cs | |
// then gave up and wrote from scratch | |
public static class FfmpegPipe | |
{ | |
const string ffmpeg_exe = "/usr/local/bin/ffmpeg"; | |
public static byte[] BlockReadVideoFrameBytes(string InputFilename, int TimeMs,int? OutputWidth,int? OutputHeight,bool ShowProgressBars) | |
{ | |
// 00:00:07.100 but can specify seconds | |
var TimeSecs = TimeMs / 1000.0f; | |
var TimeSecsString = TimeSecs.ToString("F3"); | |
var ScaleString = ""; | |
if (OutputWidth.HasValue || OutputHeight.HasValue) | |
{ | |
if (!OutputWidth.HasValue) OutputWidth = -1; | |
if (!OutputHeight.HasValue) OutputHeight = -1; | |
ScaleString = "-vf scale=" + OutputWidth.Value + ":" + OutputHeight.Value; | |
} | |
var CommandLineArgs = ""; | |
CommandLineArgs += " -i " + InputFilename; | |
CommandLineArgs += " " + ScaleString; | |
CommandLineArgs += " -ss " + TimeSecsString; | |
CommandLineArgs += " -vframes 1"; | |
CommandLineArgs += " -f image2pipe pipe:1"; | |
var info = new ProcessStartInfo(ffmpeg_exe, CommandLineArgs); | |
info.UseShellExecute = false; | |
info.CreateNoWindow = false; | |
info.RedirectStandardInput = false; | |
info.RedirectStandardOutput = true; | |
info.RedirectStandardError = false; | |
var SubProcess = Process.Start(info); | |
System.Action<BinaryReader, List<Byte>> ReadAllStream = (BinaryStream,AllBuffer) => | |
{ | |
while (true) | |
{ | |
var Buffer = new byte[1024*10]; | |
int BytesRead = BinaryStream.Read(Buffer, 0, Buffer.Length); | |
// add the whole buffer, then remove the tail chunk | |
AllBuffer.AddRange(Buffer); | |
var Cull = Buffer.Length - BytesRead; | |
AllBuffer.RemoveRange(AllBuffer.Count - Cull, Cull); | |
if (BytesRead == 0) | |
break; | |
} | |
}; | |
var OutBuffer = new List<byte>(); | |
//var ErrBuffer = new List<byte>(); | |
var StdoutReader = new BinaryReader(SubProcess.StandardOutput.BaseStream); | |
//var StderrReader = new BinaryReader(SubProcess.StandardError.BaseStream); | |
// run until done | |
try | |
{ | |
using (var Progress = new ScopedProgressBar("Running ffmpeg " + CommandLineArgs,ShowProgressBars)) | |
{ | |
while (!SubProcess.HasExited) | |
{ | |
ReadAllStream(StdoutReader, OutBuffer); | |
// ReadAllStream(StderrReader, ErrBuffer); | |
// UnityEngine.Debug.Log("Waiting for ffmpeg..."); | |
System.Threading.Thread.Sleep(1); | |
// was trying to get progress bar to wake up so we could cancel. no effect | |
//UnityEditor.EditorApplication.QueuePlayerLoopUpdate(); | |
} | |
} | |
} | |
catch(System.Exception e) | |
{ | |
UnityEngine.Debug.LogException(e); | |
SubProcess.Kill(); | |
throw; | |
} | |
/* | |
var OutBuffer = ReadAllStream(StdoutReader); | |
//var ErrBuffer = ReadAllStream(StderrReader); | |
var ErrorOutput = SubProcess.StandardError.ReadToEnd(); | |
//Debug.Log("Output " + OutBuffer.Count + " bytes, error " + ErrBuffer.Count + " bytes"); | |
UnityEngine.Debug.Log("Output " + OutBuffer.Count + " bytes, error=" + ErrorOutput); | |
*/ | |
SubProcess.Close(); | |
SubProcess.Dispose(); | |
//outputReader.Close(); | |
//outputReader.Dispose(); | |
return OutBuffer.ToArray(); | |
} | |
public static Texture2D BlockReadVideoFrame(string InputFilename, int TimeMs,int? OutputWidth, int? OutputHeight,bool ShowProgressBars) | |
{ | |
// image2pipe only comes out as jpeg apparently | |
var JpegBytes = BlockReadVideoFrameBytes(InputFilename, TimeMs, OutputWidth, OutputHeight, ShowProgressBars ); | |
var Texture = new Texture2D(1, 1); | |
using (var Progress = new ScopedProgressBar("Converting to jpeg... (x" + JpegBytes.Length + " bytes)",ShowProgressBars)) | |
{ | |
if (!Texture.LoadImage(JpegBytes)) | |
throw new System.Exception("Failed to load jpegbytes(x" + JpegBytes.Length + ") into texture"); | |
Texture.Apply(); | |
} | |
return Texture; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment