在 C# 应用程序中播放 Raspberry Pi h264 流

Posted

技术标签:

【中文标题】在 C# 应用程序中播放 Raspberry Pi h264 流【英文标题】:Play Raspberry Pi h264 stream in C# app 【发布时间】: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 完成的(已经尝试过)。

【问题讨论】:

【参考方案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

【讨论】:

以上是关于在 C# 应用程序中播放 Raspberry Pi h264 流的主要内容,如果未能解决你的问题,请参考以下文章

适用于 Raspberry Pi 的轻量级音频播放和图像显示

如何使用 Python 在 Raspberry Pi 上杀死 omxplayer 播放器

在 Raspberry Pi(Python 和 Socket)上播放 .wav 文件时遇到问题

从 ALSA Raspberry Pi 获取音频幅度

c# mono raspberry pi GPIO with Raspberry#getting Operation is not valid

Raspberry pi 上的 Alsa 问题