FFmpegVideoWriter.cs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. using System;
  2. using System.Diagnostics;
  3. using System.IO;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using Vinno.IUS.Common.Media.FFmpeg.ArgsBuilder;
  7. using Vinno.IUS.Common.Media.FFmpeg.Settings;
  8. namespace Vinno.IUS.Common.Media.FFmpeg.Video
  9. {
  10. /// <summary>
  11. /// Encode Video using FFmpeg
  12. /// </summary>
  13. public class FFmpegVideoWriter:IDisposable
  14. {
  15. private readonly FFmpegLog _log;
  16. private ManualResetEvent _waitEvent = new ManualResetEvent(true);
  17. private readonly Process _ffmpeg;
  18. public IFFmpegLog Log => _log;
  19. /// <summary>
  20. /// Creates a new instance of <see cref="FFmpegVideoWriter"/>.
  21. /// </summary>
  22. public FFmpegVideoWriter(FFmpegVideoWriterArgs args, FFmpegSettings settings)
  23. {
  24. _log = new FFmpegLog();
  25. var width = args.Width;
  26. var height = args.Height;
  27. var argsBuilder = new FFmpegArgsBuilder();
  28. argsBuilder.AddStdIn()
  29. .AddArg("thread_queue_size", 512)
  30. .AddArg("framerate", args.FrameRate)
  31. .SetFormat("rawvideo")
  32. .AddArg("pix_fmt", "rgb32")
  33. .SetVideoSize(width, height);
  34. var output = argsBuilder.AddOutputFile(args.Output)
  35. .SetFrameRate(args.FrameRate);
  36. args.VideoCodec.Apply(settings, args, output);
  37. if (settings.Resize)
  38. {
  39. var resizeWidth = settings.ResizeWidth;
  40. var resizeHeight = settings.ResizeHeight;
  41. if (resizeWidth % 2 == 1)
  42. ++resizeWidth;
  43. if (resizeHeight % 2 == 1)
  44. ++resizeHeight;
  45. output.AddArg("vf", $"scale={resizeWidth}:{resizeHeight}");
  46. }
  47. var argument = argsBuilder.GetArgs();
  48. _log.WriteArgs(argument);
  49. FFmpegService.StartFFmpeg(out _ffmpeg, argument, _log);
  50. }
  51. /// <summary>
  52. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  53. /// </summary>
  54. public void Dispose()
  55. {
  56. _ffmpeg.StandardInput.Close();
  57. _ffmpeg.Close();
  58. }
  59. /// <summary>
  60. /// Writes an Image frame.
  61. /// </summary>
  62. public void WriteFrame(BitmapFrame frame)
  63. {
  64. if (_ffmpeg.HasExited)
  65. {
  66. throw new FFmpegException(_ffmpeg.ExitCode);
  67. }
  68. _waitEvent.Reset();
  69. bool success = true;
  70. Exception exception = null;
  71. Task.Run(()=>
  72. {
  73. try
  74. {
  75. _ffmpeg.StandardInput.BaseStream.Write(frame.Data);
  76. _ffmpeg.StandardInput.BaseStream.Flush();
  77. _waitEvent.Set();
  78. }
  79. catch (Exception e) when (_ffmpeg.HasExited)
  80. {
  81. Console.WriteLine($"{e}");
  82. success = false;
  83. exception = e;
  84. }
  85. });
  86. if(!_waitEvent.WaitOne(5000))
  87. {
  88. _waitEvent.Set();
  89. _ffmpeg.Kill();
  90. throw new Exception("Cannot connect video pipe to FFmpeg");
  91. }
  92. if(!success)
  93. {
  94. throw new FFmpegException(_ffmpeg.ExitCode, exception);
  95. }
  96. }
  97. }
  98. }