【问题标题】:How to Parse an MPEG Video Stream using .NET Core如何使用 .NET Core 解析 MPEG 视频流
【发布时间】:2019-02-02 19:22:36
【问题描述】:

对于个人项目,我正在尝试从 IP 摄像机的 MPEG 流中读取数据,并对收到的各个帧执行一些计算机视觉任务(使用 .NET Core 2.2)。

我向相机的 MPEG 端点执行 GET 请求,我收到了一个 multipart/x-mixed-replace 响应,该响应不断将看似单独的帧作为 JPEG 图像流式传输。

我无法理解的是从多部分响应中解析出帧的正确方法 - 如果有更好的解析方法,如何检索帧对我来说并不重要这种流,我当然愿意改变 - 视频检索/处理对我来说是一个全新的世界:)

供参考:

MPEG 端点:

GET http://192.168.0.14/mjpeg.cgi

示例响应标头:

Content-Type: multipart/x-mixed-replace;boundary=--video boundary--

示例响应正文:

Content-length: 41142
Date: 02-02-2019 12:43:19 AM
Content-type: image/jpeg

[payload]

--video boundary--
Content-length: 41220
Date: 02-02-2019 12:43:19 AM
Content-type: image/jpeg

[payload]

--video boundary--

我目前所拥有的:

var client = new HttpClient() {
    BaseAddress = new Uri(streamUrl)
};

var stream = await client.GetStreamAsync(resource);

using (var streamReader = new StreamReader(stream)) {                
    while(true) {
        await GetFrameStart(streamReader);
        var frame = await ReadFrame(streamReader);

        // Get byte[] from returned frame above and construct image
    };
}

// ---

// Fast forward to the start of the Frame's bytes
static async Task GetFrameStart(StreamReader reader) {
    string buffer = null;
    while (buffer != string.Empty) {
        buffer = await reader.ReadLineAsync();
    }
}

// Read entire byte array until the video boundary is met
static async Task<String> ReadFrame(StreamReader reader) {
    string result = string.Empty;
    string line = null;

    while(!isBoundary(line)) {
        line = await reader.ReadLineAsync();
        if (!isBoundary(line)) result += line;
    };

    return result;
}

上面的内容似乎将每个帧的单个 [payload] 返回给我,但是如果我将字符串转换为字节并写入磁盘时,图像不是有效的 jpeg:

var bytes = Encoding.UTF8.GetBytes(frame);

using (var fs = new FileStream("test.jpeg", FileMode.Create, FileAccess.Write)) {
    fs.Write(bytes, 0, bytes.Length);
}

【问题讨论】:

标签: c# .net .net-core


【解决方案1】:

你可以使用Microsoft.AspNetCore.WebUtilities.MultipartReader

 var boundary = GetBoundary(context.Request.ContentType);
 var reader = new MultipartReader(boundary, context.Request.Body);
 var section = await reader.ReadNextSectionAsync();

请参阅此Answer 了解更多信息:

希望这会有所帮助。

【讨论】:

  • 鼓励链接到外部资源,但请在链接周围添加上下文,以便您的其他用户了解它是什么以及为什么存在。始终引用重要链接中最相关的部分,以防目标站点无法访问或永久离线。请参阅:How to anwser
【解决方案2】:

你可以在一行中使用 ffmpeg:

ffmpeg -i http://192.168.0.14/mjpeg.cgi -vcodec copy frame%d.jpg

这会提取所有带有编号名称的帧。

如果您安装了 ffmpeg,您可以使用 System.Diagnostics.Process 从您的应用程序中调用它,如下所示:

var process = new Process()
{
    StartInfo = new ProcessStartInfo()
    {
        FileName = "ffmpeg",
        Arguments = "-i http://192.168.0.14/mjpeg.cgi -vcodec copy frame%d.jpg",
        UseShellExecute = false,
        CreateNoWindow = true,
        RedirectStandardOutput = true,
        RedirectStandardError = true
    },
    EnableRaisingEvents = true
};

process.ErrorDataReceived += (sender, data) => Console.WriteLine(data.Data);

process.Start();
process.BeginErrorReadLine();

【讨论】:

  • 感谢您的回复!这看起来可能会起作用,因为它将图像写入磁盘 - 但是我希望尽可能多地将帧直接读取到内存中,以便我可以在写入之前对其进行操作。
  • 如何将其转换为 http?我正在尝试使用 ffmpeg 将 rstp 转换为 http,这就是我要问的原因
猜你喜欢
  • 2016-07-29
  • 2015-02-23
  • 2017-11-19
  • 2018-06-28
  • 2012-01-11
  • 1970-01-01
  • 2021-07-12
  • 1970-01-01
  • 2012-01-29
相关资源
最近更新 更多