/// <summary> /// Opens output audio file for writing. This will delete any existing file. Call this before writing samples. /// </summary> /// <param name="showFFmpegOutput">Show output to terminal. Error stream will not be redirected if this is set to true.</param> public void OpenWrite(bool showFFmpegOutput = false) { if (OpenedForWriting) { throw new InvalidOperationException("File was already opened for writing!"); } var cmd = $"-f s{BitDepth}le -channels {Channels} -sample_rate {SampleRate} -i - " + $"-c:a {EncoderOptions.EncoderName} {EncoderOptions.EncoderArguments} -f {EncoderOptions.Format}"; if (UseFilename) { if (File.Exists(Filename)) { File.Delete(Filename); } InputDataStream = FFmpegWrapper.OpenInput(ffmpeg, $"{cmd} \"{Filename}\"", out ffmpegp, showFFmpegOutput); } else { csc = new CancellationTokenSource(); // using stream (InputDataStream, OutputDataStream) = FFmpegWrapper.Open(ffmpeg, $"{cmd} -", out ffmpegp, showFFmpegOutput); _ = OutputDataStream.CopyToAsync(DestinationStream, csc.Token); } OpenedForWriting = true; }
/// <summary> /// Open player for writing samples for playing. /// </summary> /// <param name="sampleRate">Sample rate</param> /// <param name="channels">Number of channels</param> /// <param name="bitDepth">Bits per sample (16, 24, 32)</param> /// <param name="showWindow">Show player graphical window</param> /// <param name="showFFplayOutput">Show FFplay output for debugging purposes.</param> public void OpenWrite(int sampleRate, int channels, int bitDepth = 16, bool showWindow = false, bool showFFplayOutput = false) { if (bitDepth != 16 && bitDepth != 24 && bitDepth != 32) { throw new InvalidOperationException("Acceptable bit depths are 16, 24 and 32"); } if (OpenedForWriting) { throw new InvalidOperationException("Player is already opened for writing samples!"); } try { if (ffplayp != null && !ffplayp.HasExited) { ffplayp.Kill(); } } catch { } InputDataStream = FFmpegWrapper.OpenInput(ffplay, $"-f s{bitDepth}le -channels {channels} -sample_rate {sampleRate} -i -" + (showWindow ? "" : " -nodisp"), out ffplayp, showFFplayOutput); OpenedForWriting = true; }
/// <summary> /// Prepares for writing. /// </summary> /// <param name="showFFmpegOutput">Show output to terminal. Error stream will not be redirected if this is set to true.</param> public void OpenWrite(bool showFFmpegOutput = false) { if (OpenedForWriting) { throw new InvalidOperationException("File was already opened for writing!"); } var cmd = $"-f rawvideo -video_size {Width}:{Height} -r {Framerate} -pixel_format rgb24 -i - " + $"-c:v {EncoderOptions.EncoderName} {EncoderOptions.EncoderArguments} -f {EncoderOptions.Format}"; if (UseFilename) { if (File.Exists(Filename)) { File.Delete(Filename); } InputDataStream = FFmpegWrapper.OpenInput(ffmpeg, $"{cmd} \"{Filename}\"", out ffmpegp, showFFmpegOutput); } else { csc = new CancellationTokenSource(); // using stream (InputDataStream, OutputDataStream) = FFmpegWrapper.Open(ffmpeg, $"{cmd} -", out ffmpegp, showFFmpegOutput); _ = OutputDataStream.CopyToAsync(DestinationStream, csc.Token); } OpenedForWriting = true; }
/// <summary> /// Opens output file for writing and returns the input stream. /// </summary> /// <param name="outputFilename">Output video file name/path</param> /// <param name="options">Output options</param> /// <param name="process">FFmpeg process</param> /// <param name="inputArguments">Input arguments (such as -f, -v:c, -video_size, -ac, -ar...)</param> /// <param name="showOutput">Show output to terminal. Error stream will not be redirected if this is set to true.</param> /// <param name="ffmpegExecutable">Name or path to the ffmpeg executable</param> public static Stream StreamToFile(string outputFilename, EncoderOptions options, out Process process, string inputArguments = "", bool showOutput = false, string ffmpegExecutable = "ffmpeg") { var input = FFmpegWrapper.OpenInput(ffmpegExecutable, $"{inputArguments} -i - " + $"-c:v {options.EncoderName} {options.EncoderArguments} -f {options.Format} \"{outputFilename}\"", out process, showOutput); return(input); }
/// <summary> /// Get stream for writing and playing video in custom format. /// </summary> /// <param name="format">Custom video format</param> /// <param name="arguments">Custom FFmpeg arguments for the specified video format</param> /// <param name="showFFplayOutput">Show FFplay output for debugging purposes.</param> public static Stream GetStreamForWriting(string format, string arguments, out Process ffplayProcess, bool showFFplayOutput = false, string ffplayExecutable = "ffplay") { var str = FFmpegWrapper.OpenInput(ffplayExecutable, $"-f {format} {arguments} -i -", out ffplayProcess, showFFplayOutput); return(str); }
/// <summary> /// Prepares for writing. /// </summary> /// <param name="showFFmpegOutput">Show output to terminal. Error stream will not be redirected if this is set to true.</param> /// <param name="thread_queue_size">Max. number of queued packets when reading from file/stream. /// Should be set to higher when dealing with high rate/low latency streams.</param> public void OpenWrite(bool showFFmpegOutput = false, int thread_queue_size = 4096) { if (OpenedForWriting) { throw new InvalidOperationException("File/Stream was already opened for writing!"); } var manual = new ManualResetEvent(false); socket = new Socket(SocketType.Stream, ProtocolType.Tcp); socket.Bind(new IPEndPoint(IPAddress.Loopback, 0)); socket.Listen(4); var port = ((IPEndPoint)socket.LocalEndPoint).Port; socket.BeginAccept(r => { connected_socket = socket.EndAccept(r); InputDataStreamAudio = new NetworkStream(connected_socket); manual.Set(); }, null); var cmd = $"-f s{AudioBitDepth}le -channels {AudioChannels} -sample_rate {AudioSampleRate} " + $"-thread_queue_size {thread_queue_size} -i \"tcp://{IPAddress.Loopback}:{port}\" " + $"-f rawvideo -video_size {VideoWidth}:{VideoHeight} -r {VideoFramerate} " + $"-thread_queue_size {thread_queue_size} -pixel_format rgb24 -i - " + $"-map 0 -c:a {AudioEncoderOptions.EncoderName} {AudioEncoderOptions.EncoderArguments} " + $"-map 1 -c:v {VideoEncoderOptions.EncoderName} {VideoEncoderOptions.EncoderArguments} " + $"-f {VideoEncoderOptions.Format}"; if (UseFilename) { if (File.Exists(Filename)) { File.Delete(Filename); } InputDataStreamVideo = FFmpegWrapper.OpenInput(ffmpeg, $"{cmd} \"{Filename}\"", out ffmpegp, showFFmpegOutput); } else { csc = new CancellationTokenSource(); // using stream (InputDataStreamVideo, OutputDataStream) = FFmpegWrapper.Open(ffmpeg, $"{cmd} -", out ffmpegp, showFFmpegOutput); _ = OutputDataStream.CopyToAsync(DestinationStream, csc.Token); } manual.WaitOne(); OpenedForWriting = true; }
/// <summary> /// Save frame as an image /// </summary> /// <param name="output">Output image</param> /// <param name="encoder">Encoder for image ('png', 'libwebp')</param> /// <param name="ffmpegExecutable">Name or path to the ffmpeg executable</param> public void Save(string output, string encoder = "png", string extraParameters = "", string ffmpegExecutable = "ffmpeg") { if (File.Exists(output)) { File.Delete(output); } using (var inp = FFmpegWrapper.OpenInput(ffmpegExecutable, $"-f rawvideo -video_size {Width}:{Height} -pixel_format rgb24 -i - " + $"-c:v {encoder} {extraParameters} -f image2pipe \"{output}\"", out _, false)) { // save it inp.Write(RawData.Span); } }
/// <summary> /// Open player for writing frames for playing. /// </summary> /// <param name="width">Video frame width</param> /// <param name="height">Video frame height</param> /// <param name="framerateFrequency">Video framerate (frequency form)</param> /// <param name="showFFplayOutput">Show FFplay output for debugging purposes.</param> public void OpenWrite(int width, int height, string framerateFrequency, bool showFFplayOutput = false) { if (OpenedForWriting) { throw new InvalidOperationException("Player is already opened for writing frames!"); } try { if (ffplayp != null && !ffplayp.HasExited) { ffplayp.Kill(); } } catch { } InputDataStream = FFmpegWrapper.OpenInput(ffplay, $"-f rawvideo -video_size {width}:{height} -framerate {framerateFrequency} -pixel_format rgb24 -i -", out ffplayp, showFFplayOutput); OpenedForWriting = true; }