【问题标题】:Play Raspberry Pi h264 stream in C# app在 C# 应用程序中播放 Raspberry Pi h264 流
【发布时间】:2014-08-19 12:00:06
【问题描述】:

我有一个带有专用摄像头的 Raspberry Pi 板,仅在 h264 中录制视频。我正在寻找在 c# windows forms 应用程序中实时流式传输和播放录制视频的最佳方法(如延迟小于 1 秒)。附加要求是此类流可以在显示之前轻松处理,例如用于搜索图像上的对象。

我尝试过的东西:

- raspi 上的 VLC 服务器和 c# 表单应用程序中的 VLC 控制

- 使用 nc 在 raspi 上创建一个套接字,在 c# 中接收原始 h264 数据并将其传递给 mplayer 前端

- 使用https://github.com/cisco/openh264

h264dec.exe vid.h264 out.yuv

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

h264dec.exe  vid.h264

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

- ffmpeg

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

编辑 这是我试图在 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 完成的(已经尝试过)。

【问题讨论】:

    标签: c# stream ffmpeg raspberry-pi h.264


    【解决方案1】:

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

    就像我之前所说的 -fps 120 选项可以让播放器跳过缓冲区中的内容并在收到流时立即播放。 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 是 rasp 正在侦听的端口号。

    #!/bin/bash
    
    raspivid --width 1280 --height 720 -t 9999999 --framerate 25 --output - | nc -l PORTNUMBER
    

    【讨论】:

      【解决方案2】:

      我没有使用 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 -
      

      注意:写摄像头连接的树莓派的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 脚本并关闭 mplayer 子进程,我们在 End 函数中调用 pkill。

      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 安装单声道:

      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
      

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

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

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

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

      这是它的样子:

      开发愉快:D

      【讨论】:

        猜你喜欢
        • 2013-05-26
        • 1970-01-01
        • 2015-03-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-30
        • 1970-01-01
        相关资源
        最近更新 更多