古成 2024-10-31 19:48 采纳率: 0%
浏览 169
已结题

请教,如何使用C#加载本地摄像头进行逐帧推流

我想用OpenCVSharp加载本地摄像头,然后逐帧推流至RTMP,要留拉流的时候是实时的而不是每次拉流都是重头开始,应该如何写?急( ╯□╰ ),我写了一段可以执行,但是每次拉流都是重头开始播放。

    internal class Test4
    {
        private const int WIDTH = 640;
        private const int HEIGHT = 480;
        private static string rtmpUrl = "rtmp://192.168.1.35:1935/live/abc";
        static void Main()
        {
            using var capture = new VideoCapture(0);
            capture.FrameWidth = WIDTH;
            capture.FrameHeight = HEIGHT;
            capture.BufferSize = 2;

            if (!capture.IsOpened())
            {
                Console.WriteLine("Error: Unable to open the camera.");
                return;
            }

            var ffmpeg = "ffmpeg.exe";


            Console.WriteLine("Start");
            var sw = new Stopwatch();
            sw.Start();

            var inputArgs = $"-y -f image2pipe -i -";
            var outputArgs = $"-vcodec libx264 -crf 23 -pix_fmt yuv420p -preset ultrafast  -f flv {rtmpUrl}";

            var process = new Process
            {
                StartInfo =
                {
                    FileName = ffmpeg,
                    Arguments = $"{inputArgs} {outputArgs}",
                    UseShellExecute = false,
                    CreateNoWindow = true,
                    RedirectStandardInput = true,
                    RedirectStandardError = true,
                },
            };

            process.ErrorDataReceived += (sender, eventArgs) => Console.WriteLine(eventArgs.Data);
            process.Start();
            process.BeginErrorReadLine();

            var ffmpegIn = process.StandardInput.BaseStream;

            while (true)
            {
                using var frame = new Mat();
                capture.Read(frame);
                if (frame.Empty())
                    break;

                var imageByte = frame.ToBitmap().GetBytes(ImageFormat.Png);
                ffmpegIn.Write(imageByte, 0, imageByte.Length);

                Cv2.ImShow("Camera Feed", frame);
                if (Cv2.WaitKey(30) >= 0)
                    break;
            }

            Console.WriteLine("Drawing done");

            ffmpegIn.Flush();
            ffmpegIn.Close();

            process.WaitForExit();
            process.Dispose();

            sw.Stop();
            Console.WriteLine($"Video creating done {sw.Elapsed.TotalSeconds:0.00}s");
            Console.ReadLine();
        }
    }

用python是实现了,但是同样的ffmepg命令在C#中执行是花屏

ffmpeg_bin = 'ffmpeg'

# 设置摄像头分辨率
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 2)

fps = cap.get(cv2.CAP_PROP_FPS)
print("fps:", fps)
# 设置缓冲区大小为2

# 定义视频编码器
fourcc = cv2.VideoWriter_fourcc(*'X264')

# 创建FFmpeg命令行参数
ffmpeg_cmd = ['ffmpeg',
               '-y',  
               '-r','15',
               '-f', 'rawvideo',
               '-pixel_format', 'bgr24',
               '-video_size', '640x480',
               '-i', '-', 
               '-c:v', 'libx264',
               '-preset', 'ultrafast',
               '-bufsize','425984',
               '-x264opts','ref=10',
               #'-tune', 'zerolatency',
               '-pix_fmt', 'yuv420p',
               '-f', 'flv',
               'rtmp://101.34.67.44:1935/live/hahaha']
# 启动FFmpeg进程
ffmepg_process = subprocess.Popen(ffmpeg_cmd, stdin=subprocess.PIPE)
while True:
    success, img = cap.read()
    ffmepg_process.stdin.write(img.tobytes())
    cv2.imshow("Image", img)
    cv2.waitKey(1)
  • 写回答

27条回答 默认 最新

  • 问答-莫得感情 2024-10-31 20:02
    关注

    ️题主您好️,目前由我为您解答👇🏼
    🧡参考谷歌智能助手,由吾为汝整理提供🧡

    C# 使用 OpenCVSharp 推流到 RTMP 服务器,并保证实时性,需要避免几个关键问题:

    1. 图像格式转换: 确保推流的图像格式是FFmpeg支持的,例如YUV420P。 你之前的代码使用 PNG 格式通过管道传输,效率低,而且容易导致花屏。 应该直接将Mat转换为YUV格式。

    2. 帧率控制: 你需要控制推流的帧率,使其与摄像头采集的帧率匹配,或者设定一个合适的固定帧率。 之前的代码缺少帧率控制,可能导致FFmpeg处理速度跟不上,或者输出的视频帧率不稳定。

    3. 关键帧间隔 (Keyframe Interval): 设置合适的关键帧间隔,以便播放器可以快速seek和开始播放。 缺少关键帧会导致每次拉流都从头开始。

    4. 缓冲区管理: 合理设置缓冲区大小,避免数据拥塞或延迟。

    以下是一个改进的代码示例,使用了FFmpegPipe类来简化FFmpeg流程的管理,并实现了帧率控制和关键帧间隔设置:

    using OpenCvSharp;
    using OpenCvSharp.Extensions;
    using System;
    using System.Diagnostics;
    using System.Drawing.Imaging;
    using System.Threading;
    
    internal class RTMPStreamer
    {
        private const int WIDTH = 640;
        private const int HEIGHT = 480;
        private const int FPS = 15; // 设置目标帧率
        private const int KEYFRAME_INTERVAL = 2 * FPS; // 2秒一个关键帧
        private static string rtmpUrl = "rtmp://192.168.1.35:1935/live/abc";
    
        static void Main()
        {
            using var capture = new VideoCapture(0);
            capture.FrameWidth = WIDTH;
            capture.FrameHeight = HEIGHT;
            capture.Fps = FPS; // 设置摄像头采集帧率
    
            if (!capture.IsOpened())
            {
                Console.WriteLine("Error: Unable to open the camera.");
                return;
            }
    
            var ffmpegArgs = $"-y -f rawvideo -pix_fmt yuv420p -video_size {WIDTH}x{HEIGHT} -framerate {FPS} -i - -c:v libx264 -preset ultrafast -g {KEYFRAME_INTERVAL} -f flv {rtmpUrl}";
            using var ffmpegPipe = new FFmpegPipe(ffmpegArgs);
    
            var frameCount = 0;
            var sw = new Stopwatch();
            sw.Start();
    
            while (true)
            {
                using var frame = new Mat();
                capture.Read(frame);
                if (frame.Empty())
                    break;
    
                // 转换到YUV420p
                using var yuvFrame = new Mat();
                Cv2.CvtColor(frame, yuvFrame, ColorConversionCodes.BGR2YUV_I420);
    
    
                ffmpegPipe.Write(yuvFrame.Data, yuvFrame.DataEnd.ToInt64() - yuvFrame.DataStart.ToInt64());
    
    
                Cv2.ImShow("Camera Feed", frame);
                if (Cv2.WaitKey(1) >= 0) 
                    break;
    
    
                frameCount++;
                Thread.Sleep(Math.Max(0, (int)(1000.0 / FPS - sw.ElapsedMilliseconds))); // 控制帧率
                sw.Restart();
    
    
            }
    
            ffmpegPipe.Close(); 
            Console.WriteLine("Streaming finished.");
            Console.ReadKey();
        }
    }
    
    
    
    // FFmpegPipe 类,简化FFmpeg进程管理
    public class FFmpegPipe : IDisposable
    {
        private Process ffmpegProcess;
        public FFmpegPipe(string arguments)
        {
            ffmpegProcess = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = "ffmpeg.exe", // 或者 ffmpeg 的完整路径
                    Arguments = arguments,
                    UseShellExecute = false,
                    RedirectStandardInput = true,
                    RedirectStandardError = true,
                    CreateNoWindow = true
                }
            };
    
    
            ffmpegProcess.ErrorDataReceived += (sender, e) => Console.WriteLine(e.Data);
            ffmpegProcess.Start();
            ffmpegProcess.BeginErrorReadLine();
        }
    
    
        public void Write(byte[] data, long length)
        {
            ffmpegProcess.StandardInput.BaseStream.Write(data, 0, (int)length);
    
        }
    
    
    
        public void Close()
        {
    
            ffmpegProcess.StandardInput.Close(); // 关闭输入流,通知FFmpeg结束
            ffmpegProcess.WaitForExit();
    
    
            ffmpegProcess.Close();
    
        }
    
    
        public void Dispose()
        {
            Close();
        }
    }
    
    

    关键改进点:

    • YUV420P 格式: 使用 Cv2.CvtColor 将图像转换为 YUV420P 格式,这是FFmpeg推荐的格式,效率更高。
    • 帧率控制: 使用 Thread.Sleep 控制帧率,使其稳定在设定的 FPS。
    • 关键帧间隔: 使用 -g 参数设置关键帧间隔。
    • FFmpegPipe 类: 封装了FFmpeg进程的创建、写入和关闭,使代码更简洁。
    • 关闭输入流: 使用 ffmpegProcess.StandardInput.Close() 显式关闭FFmpeg的输入流,这对于让FFmpeg正确结束编码至关重要。

    使用方法:

    1. 确保你已经安装了 FFmpeg,并将 ffmpeg.exe 添加到系统环境变量 PATH 中,或者在代码中指定FFmpeg的完整路径。
    2. 安装 OpenCvSharp4OpenCvSharp4.runtime.win NuGet包。
    3. 编译并运行代码。

    这个改进后的版本应该能够解决花屏和实时性的问题。 如果还有问题,请检查FFmpeg的输出日志,以便进行更精确的调试。 记住根据你的实际网络情况和硬件性能调整 FPSKEYFRAME_INTERVAL-preset 参数。 -preset ultrafast 速度最快,但质量最低,可以根据需要调整为 superfastveryfast 等。

    评论

报告相同问题?

问题事件

  • 系统已结题 11月8日
  • 赞助了问题酬金15元 10月31日
  • 创建了问题 10月31日