Skip to content

Instantly share code, notes, and snippets.

@SoylentGraham
Created May 10, 2018 18:21
Show Gist options
  • Save SoylentGraham/4a08fbba268f19735f118017ead6116a to your computer and use it in GitHub Desktop.
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.
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