asp.net - ASP.NET Core mvc 应用程序中的 FFMPEG 录制

标签 asp.net asp.net-core ffmpeg video-streaming recording

我正在使用 ASP.NET Core 和 ffmpeg 录制实时视频流。当页面收到 get 请求时,流应开始使用 ffmpeg 录制并保存到文件夹中。我想让它在访问 stop 端点时,彻底关闭 ffmpeg 进程。

不幸的是,在离开 Get 方法后我无法将“q”发送到 stdin。使用 taskkill 需要使用/F 使 ffmpeg 进程(不是窗口)强制退出并且无法正确保存视频,从而导致文件损坏。

我尝试使用Process.Kill(),但这也会导致文件损坏。另外,我尝试了 Process.CloseMainWindow() ,它有效,但仅当进程作为窗口启动时,并且我无法在我正在使用的服务器中将进程作为窗口启动。

我已经在下面包含了到目前为止的代码,所以希望有人能引导我走上正确的道路。

using System;
...
using Microsoft.Extensions.Logging;

namespace MyApp.Controllers
{
    [Route("api/[controller]")]
    [Authorize]
    public class RecordingController : Controller
    {
        private readonly ApplicationDbContext _context;
        private readonly ILogger<HomeController> _logger;

        public RecordingController(ApplicationDbContext context, ILogger<HomeController> logger)
        {
            _context = context;
            _logger = logger;
        }

        [HttpGet]
        public async Task<ActionResult> Get()
        {

            // Define the os process
            var processStartInfo = new ProcessStartInfo()
            {
                // ffmpeg arguments
                Arguments = "-f mjpeg -i \"https://urlofstream.com/video.gci\" -r 5 \"wwwroot/video.mp4\"",
                FileName = "ffmpeg.exe",
                UseShellExecute = true
            };

            var p1 = Process.Start(processStartInfo);

            // p1.StandardInput.WriteLineAsync("q"); <-- This works here but not in the Stop method

            return Ok(p1.Id);
        }


        // GET: api/Recording/stop
        [HttpGet("stop/{pid}")]
        public ActionResult Stop(int pid)
        {
            Process processes = Process.GetProcessById(pid);
            processes.StandardInput.WriteLineAsync("q");     // Does not work, is not able to redirect input
            return Ok();
        }
    }
}


最佳答案

我发现了这个问题的解决方案,希望可以帮助其他人。解决方案是使用 Singleton 与 ConcurrentDictionary 的组合。并写入标准输入。写入正在运行的进程的标准输入的唯一方法是,您仍然可以访问已启动的进程句柄,并且无法从 Controller 内部访问它,因此您需要创建一个单例并存储进程在 ConcurrentDictionary 中(这非常适合从多个线程更新)。

首先我创建了一个录音管理器类。 RecordingManager.cs

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace MyApp.Services
{
    public class RecordingManager
    {
        // Key int is the ProcessId. Value process is the running Process
        private static ConcurrentDictionary<int, Process> _processes = new ConcurrentDictionary<int, Process>();

        public Process Start()
        {
            // Define the os process
            var processStartInfo = new ProcessStartInfo()
            {
                // ffmpeg arguments
                Arguments = "-f mjpeg -i https://urlofstream.com/video.gci -r 5 wwwroot/video.mp4",
                FileName = "ffmpeg.exe",
                RedirectStandardInput = true, // Must be set to true
                UseShellExecute = false       // Must be set to false
            };

            Process p = Process.Start(processStartInfo);

            // Add the Process to the Dictionary with Key: Process ID and value as the running process
            _processes.TryAdd(p1.Id, p);

            return p;
        }

        private Process GetProcessByPid(int pid)
        {
            return _processes.FirstOrDefault(p => p.Key == pid).Value;
        }

        public void Stop(int pid)
        {
            Process p = GetProcessByPid(pid);

            // FFMPEG Expects a q written to the STDIN to stop the process
            p.StandardInput.WriteLine("q\n");
            _processes.TryRemove(pid, out p);

            // Free up resources
            p.Close()
        }
    }
}

接下来,我将单例添加到ConfigureServices 方法中的startup.cs 类中

services.AddSingleton<RecordingManager>();

最后,我将单例添加到 Controller 构造函数中并调用其中的方法。 RecordingController.cs

namespace MyApp.Controllers
{
    [Route("api/[controller]")]
    [Authorize]
    public class RecordingController : Controller
    {
        private readonly ApplicationDbContext _context;
        private readonly ILogger<HomeController> _logger;
        private readonly RecordingManager _recordingManager;

        public RecordingController(ApplicationDbContext context, ILogger<HomeController> logger, RecordingManager recordingManager)
        {
            _context = context;
            _logger = logger;
            _recordingManager = recordingManager;
        }

        [HttpGet]
        public async Task<ActionResult> Get()
        {
            Process p = _recordingManager.Start();
            return Ok(p.Id); // Wouldn't recommend simply returning an Ok Result here. Check for issues
        }


        // GET: api/Recording/stop
        [HttpGet("stop/{pid}")]
        public ActionResult Stop(int pid)
        {
            _recordingManager.Stop(pid);
            return Ok(); // Wouldn't recommend simply returning an Ok Result here. Check for issues
        }
    }
}

关于asp.net - ASP.NET Core mvc 应用程序中的 FFMPEG 录制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60955617/

相关文章:

ffmpeg - FFmpeg缩放过滤器的缩放算法是什么?

asp.net - jQuery 禁用/重新启用页面上的验证器

asp.net - 在 Azure 上预热站点之前不要处理请求

docker - 无法在 Docker 容器中为 ASP.NET Core Web App 创建开发证书

asp.net - 将命令行参数传递给 ASP.NET Core 中的 Startup 类

c# - 为 asp-for 指定自定义名称(仅需要子属性)

amazon-web-services - EBS FFMPEG 未从 ebexentsions 安装

asp.net - Windows 身份验证不会挑战域外的浏览器

mysql - ASP/MySQL 交互

java.io.IOException : error=13, 执行 .exe 库时权限被拒绝