繁体   English   中英

在C#app中播放Raspberry Pi h264流

[英]Play Raspberry Pi h264 stream in C# app

我有一个带有专用摄像头的Raspberry Pi板,只能在h264中录制视频。 我正在寻找在c#windows forms app中实时流式传输和播放录制视频的最佳方法(例如,延迟不到1秒)。 附加要求是在显示之前可以容易地处理这样的流,例如用于搜索图像上的对象。

我试过的东西:

- 使用radi上的VLC服务器和c#中的VLC控制形成app < - 简单的解决方案,使用RTSP,但是有一个严重的缺陷,即显示的图像延迟约3秒。 我无法用buffor size / options等修复它。

- 使用nc在raspi上创建套接字,在c#中接收原始h264数据并将其传递给mplayer前端 < - 如果我只是启动raspotion | nc和笔记本电脑上的nc | mplayer,我得到我想要的结果,我得到的视频几乎是实时的,但是当我尝试在c#中创建mplayer前端并模拟nc.exe时出现问题。 也许我正在传递h264数据错误(只是将它们写入stdin)或者其他东西。

- 使用https://github.com/cisco/openh264 < - 我编译了所有内容,但我甚至无法解码使用h264dec.exe在raspi上录制的样本vid.h264,更不用说在c#中使用它了。

h264dec.exe vid.h264 out.yuv

这会产生0bytes out.yuv文件,而:

h264dec.exe  vid.h264

给我错误消息:“配置文件中没有指定输入文件。”

- ffmpeg < - 我在c#app中实现了ffplay.exe播放,但缺少简单的方法来获取screencaps等,这使我无法进一步调查和开发。

我甚至不确定我是否正确地接近这个主题,所以我真的很感谢我能得到的每一条建议。

编辑这是我试图在c#中实现的“工作”解决方案

raspivid --width 400 --height 300 -t 9999999 --framerate 25 --output - | nc -l 5884

nc ip_addr 5884 | mplayer -nosound -fps 100 -demuxer +h264es -cache 1024 -

这里的关键是FPS 100,因为mplayer跳过滞后并以正常速度播放它立即收到的视频。 这里的问题是我不知道如何通过c#将视频数据从socket传递到mplayer,因为我猜它不是通过stdin完成的(已经尝试过了)。

好的,实际上我设法解决了这个问题:

就像我之前说的那样-fps 120选项是让玩家在收到它后立即跳过buffor和播放流中的内容。 PanelId是一个面板的句柄,其中mplayer是嵌套的。

class Mplayer
{
    Process mplayer;

    public Mplayer(string path, string pipeName, int panelId)
    {
        String args = "";
        mplayer = new Process();
        mplayer.StartInfo.UseShellExecute = false;
        mplayer.StartInfo.RedirectStandardInput = true;
        mplayer.StartInfo.FileName = path;
        args = @"\\.\pipe\" + pipeName + " -demuxer +h264es -fps 120 -nosound -cache 512";
        args += " -nofs -noquiet -identify -slave ";
        args += " -nomouseinput -sub-fuzziness 1 ";
        args += " -vo direct3d, -ao dsound  -wid ";
        args += panelId;
        mplayer.StartInfo.Arguments = args;
    }

    public void Start()
    {
        mplayer.Start();
    }

    public void End()
    {
        mplayer.Kill();
    }
}

后台工作者从套接字读取东西:

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        try
        {
            pipeServ.WaitForConnection(); //opcjonalne?
            StreamWriter sw = new StreamWriter(pipeServ);
            sw.AutoFlush = true;

            tcpCamera = new TcpClient();
            tcpCamera.Connect(ipAddress, camPort);
            NetworkStream camStream = tcpCamera.GetStream();

            int read = 0;
            byte[] bytes = new byte[tcpCamera.ReceiveBufferSize];
            while (tcpCamera.Connected)
            {
                read = camStream.Read(bytes, 0, tcpCamera.ReceiveBufferSize);
                if (read > 0)
                    pipeServ.Write(bytes, 0, read);
            }
        }
        catch (IOException ex)
        {
            //Broken pipe - result of Mplayer exit
            //MessageBox.Show(ex.Message);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

在RaspberryPi上运行的脚本。 Portnumber是锉刀正在监听的端口号。

#!/bin/bash

raspivid --width 1280 --height 720 -t 9999999 --framerate 25 --output - | nc -l PORTNUMBER

我在不使用NamedPipes的情况下解决了它。 方法如下:

注意:此解决方案仅适用于Linux。

首先创建一个bash脚本VideoStreamRecv.bash $1是WindowID的参数,我们将WinForm面板ID传递给此bash脚本。

#!/bin/bash -e
nc 127.0.0.1 5000 | mplayer -nosound -fps 120 -demuxer h264es -cache 1024 -wid $1 -

注意:写入相机所连接的Raspberry Pi的IP代替127.0.0.1

创建您的C#项目。 我在Visual Studio中创建了一个简单的Windows窗体应用程序项目。 这是整体外观。

在此输入图像描述

以下是课程:

Base.cs

public partial class Base : Form
{
    private MPlayer Player;
    private StreamWriter PlayerInput;
    public Base()
    {
        InitializeComponent();
    }

    private void Base_Load(object sender, EventArgs e)
    {
        Player = new MPlayer((int)Video.Handle);
        Player.Start();
    }

    private void Stop_Click(object sender, EventArgs e)
    {
        Player.End();
    }
}

MPlayer.cs

它就像@CoreMeltdown但在这里我们正在调用bash脚本并关闭我们在End函数中调用pkill的mplayer子进程。

class MPlayer
{
    Process mplayer;

    public MPlayer(int PanelID)
    {
        mplayer = new Process();
        mplayer.StartInfo.UseShellExecute = false;
        mplayer.StartInfo.RedirectStandardInput = true;
        mplayer.StartInfo.FileName = "VideoStreamRecv.bash";
        mplayer.StartInfo.Arguments = PanelID.ToString();
    }

    public void Start()
    {
        mplayer.Start();
    }

    public void End()
    {
        mplayer.Kill();
        Process.Start("pkill mplayer");
    }
}

编译项目并将所有二进制文件和VideoStreamRecv.bash复制到与Raspberry Pi中的二进制文件相同的目录中。

使用以下命令为Raspberry Pi安装mono:

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb http://download.mono-project.com/repo/debian raspbianstretch main" | sudo tee /etc/apt/sources.list.d/mono-official.list
sudo apt-get update
sudo apt-get install mono-devel --yes --allow-unauthenticated

使用此命令在Raspberry Pi上启动相机流,并使用此命令连接相机(与@CoreMeltdown相同)。

raspivid --width 400 --height 300 -t 9999999 --framerate 25 --output - | nc -l 5000

在接收器Raspberry Pi(带有编译的二进制文件的那个)上,打开终端,用二进制文件转到dir并执行:

mono MplayerFrontEnd.exe # MplayerFrontEnd is the name of my project, use your own name here.

这是它的样子:

在此输入图像描述

快乐开发:D

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM