You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# WPF基于VLC实现RTSP图像流失败:客户端无法打开源

问题:WPF控件渲染图像通过VLC RTSP广播失败,提示“This source does not open”

我在C# WPF项目中尝试将WPF控件渲染的图像通过VLC实现RTSP广播。目前已能将图像正确保存至文件夹,但自定义基于BlockingCollection的Stream实现后,使用VLC Player连接RTSP流时提示“This source does not open”。以下是核心代码及Debug日志,求排查帮助。


核心代码

Main类(LANViewerService)

using LibVLCSharp.Shared;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using MediaPlayer = LibVLCSharp.Shared.MediaPlayer;

namespace Presenter.Infrastructure.Services
{
    public class LANViewerService : IViewerService
    {
        private static LANViewerService instance;
        Task produceTask;
        private bool play;
        private MediaPlayer player;

        public Visual? PresentationField { get; set; }

        public event EventHandler PresentationEnded;
        public event EventHandler PresentationStarted;

        private LANViewerService() { }

        public static IViewerService GetViewerService() => instance ??= new LANViewerService();

        public void EndPresentation()
        {
            play = false;
            player.Stop();
            PresentationEnded?.Invoke(this, EventArgs.Empty);
        }

        public void StartPresentation()
        {
            Core.Initialize();
            var libvlc = new LibVLC();
            libvlc.Log += Libvlc_Log;
            player = new MediaPlayer(libvlc);
            player.Play();

            PresentationStarted?.Invoke(this, EventArgs.Empty);

            VideoStream videoStream = new VideoStream();
            var media = new Media(libvlc, new StreamMediaInput(videoStream));
            media.AddOption(":no-audio");
            media.AddOption(":sout=#transcode{vcodec=h264,fps=10,vb=1024,acodec=none}");
            media.AddOption(":sout=#rtp{sdp=rtsp://127.0.0.1:8008/test}");
            media.AddOption(":sout-keep");
            player.Media = media;
            player.Play();

            produceTask = new Task(() =>
            {
                while (play)
                {
                    if (PresentationField == null)
                        continue;
                    RenderTargetBitmap bitmap = null;
                    Application.Current.Dispatcher.Invoke(() =>
                    {
                        JpegBitmapEncoder encoder = new();
                        bitmap = ConvertToBitmapSource(PresentationField);
                        encoder.Frames.Add(BitmapFrame.Create(bitmap));
                        MemoryStream stream = new MemoryStream();
                        encoder.Save(stream);
                        videoStream.Write(stream.ToArray());
                    });

                    Thread.Sleep(30);
                }
            });
            play = true;
            produceTask.Start();
        }

        private void Libvlc_Log(object? sender, LogEventArgs e)
        {
            Debug.WriteLine(e.Module + " " + e.Message);
        }

        public RenderTargetBitmap ConvertToBitmapSource(Visual element)
        {
            var target = new RenderTargetBitmap(1920, 1080, 96, 96, PixelFormats.Default);
            target.Render(element);

            return target;
        }
    }
}

Stream类(VideoStream)

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;

namespace Presenter.Infrastructure
{
    public class VideoStream : Stream, IDisposable
    {
        BlockingCollection<byte[]> frames;
        IEnumerator<byte[]> enumerator;
        private bool _disposed;

        public override bool CanRead => true;

        public override bool CanSeek => false;
        public override bool CanWrite => true;

        public override long Length => int.MaxValue;

        public override long Position { get; set; } = 0;

        public VideoStream()
        {
            frames = new();
            enumerator = frames.GetConsumingEnumerable().GetEnumerator();
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            byte[] frame = frames.Take();
            int readed = frame.Length < count ? frame.Length : count;
            Array.Copy(frame, 0, buffer, offset, readed);  
            return readed;
        }

        public override int Read(Span<byte> buffer)
        {
            byte[] frame = frames.Take();
            frame.CopyTo(buffer);
            return frame.Length;
        }

        public void Write(byte[] frame)
        {
            frames.Add(frame);
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new InvalidOperationException();
        }

        public override void SetLength(long value)
        {
            throw new InvalidOperationException();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            
        }

        public override void Flush()
        {
            throw new InvalidOperationException();
        }

        void IDisposable.Dispose()
        {
            if (_disposed)
                return;

            enumerator.Dispose();
            _disposed = true;
        }
    }
}

Debug日志

main creating audio output
mmdevice cannot initialize COM (error 0x80010106)
main looking for audio output module matching "any": 6 candidates
main revision 3.0.18-0-ge9eceaed4d
main configured with /builds/videolan/vlc/extras/package/win32/../../../configure  '--enable-update-check' '--enable-lua' '--enable-faad' '--enable-flac' '--enable-theora' '--enable-avcodec' '--enable-merge-ffmpeg' '--enable-dca' '--enable-mpc' '--enable-libass' '--enable-schroedinger' '--enable-realrtsp' '--enable-live555' '--enable-shout' '--enable-goom' '--enable-sse' '--enable-mmx' '--enable-libcddb' '--enable-zvbi' '--disable-telx' '--enable-nls' '--host=x86_64-w64-mingw32' '--with-contrib=../contrib/x86_64-w64-mingw32' '--with-breakpad=https://win.crashes.videolan.org' '--enable-qt' '--enable-skins2' '--enable-dvdread' '--enable-caca' 'host_alias=x86_64-w64-mingw32' 'CFLAGS= ' 'CXXFLAGS= ' 'PKG_CONFIG=pkg-config' 'PKG_CONFIG_LIBDIR=/usr/x86_64-w64-mingw32/lib/pkgconfig:/usr/lib/x86_64-w64-mingw32/pkgconfig'
main using audio output module "directsound"
directsound found 3 devices
main keeping audio output
main Copyright © 1996-2022 the VideoLAN team
main VLC media player - 3.0.18 Vetinari
main Creating an input for 'imem://'
main using sout chain=`rtp{sdp=rtsp://127.0.0.1:8008/test}'
main looking for sout stream module matching "rtp": 23 candidates
main set config option: sout-rtp-sdp to rtsp://127.0.0.1:8008/test
stream_out_rtp Consider passing --rtsp-host=IP on the command line instead.
main using timeshift granularity of 50 MiB
main `imem://' gives access `imem' demux `any' path `'
stream_out_rtp RTSP stream at /test
main net: listening to 127.0.0.1 port 8008
main no access_demux modules matched
main looking for access_demux module matching "imem": 15 candidates
prefetch using 16777216 bytes buffer, 16777216 bytes read
main using stream_filter module "prefetch"
main creating access: imem://
main using access module "imem_access"
main using timeshift path: C:\Users\Konstant\AppData\Local\Temp
main using sout stream module "stream_out_rtp"
main creating demux: access='imem' demux='any' location='' file='(null)'
main stream=`rtp'
stream_out_rtp "127.0.0.1" RTSP host might be ignored in multiple-host configurations, use at your own risks.
main looking for access module matching "imem": 27 candidates
imem Invalid get/release function pointers
main looking for stream_filter module matching "prefetch,cache_read": 24 candidates
main looking for stream_filter module matching "any": 24 candidates
304219
lua Trying Lua scripts in C:\Users\Konstant\AppData\Roaming\vlc\lua\playlist
lua Trying Lua scripts in C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\playlist
lua Trying Lua playlist script C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\playlist\anevia_streams.luac
lua Trying Lua playlist script C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\playlist\anevia_xml.luac
lua Trying Lua playlist script C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\playlist\appletrailers.luac
lua Trying Lua playlist script C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\playlist\bbc_co_uk.luac
lua Trying Lua playlist script C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\playlist\cue.luac
lua Trying Lua playlist script C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\playlist\dailymotion.luac
lua Trying Lua playlist script C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\playlist\jamendo.luac
lua Trying Lua playlist script C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\playlist\koreus.luac
lua Trying Lua playlist script C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\playlist\liveleak.luac
lua Trying Lua playlist script C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\playlist\newgrounds.luac
lua Trying Lua playlist script C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\playlist\rockbox_fm_presets.luac
lua Trying Lua playlist script C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\playlist\soundcloud.luac
lua Trying Lua playlist script C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\playlist\twitch.luac
lua Trying Lua playlist script C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\playlist\vimeo.luac
lua Trying Lua playlist script C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\playlist\vocaroo.luac
lua Trying Lua playlist script C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\playlist\youtube.luac
main no stream_filter modules matched
main creating demux: access='imem' demux='any' location='' file='(null)'
main looking for demux module matching "any": 55 candidates
main using xml reader module "xml"
main using packetizer module "copy"
main using demux module "image"
main looking for meta reader module matching "any": 2 candidates
lua Trying Lua scripts in C:\Users\Konstant\AppData\Roaming\vlc\lua\meta\reader
lua Trying Lua scripts in C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\meta\reader
lua Trying Lua playlist script C:\Users\Konstant\Documents\Projects\Presenter\Presenter\bin\Debug\net6.0-windows\libvlc\win-x64\lua\meta\reader\filename.luac
main no meta reader modules matched
main `imem://' successfully opened
main EOF reached
main killing decoder fourcc `jpeg'
main removing module "copy"
main removing module "image"
main removing module "record"
main removing module "prefetch"
main starting in async mode
main selecting program id=0
main looking for packetizer module matching "any": 25 candidates
webvtt subtitle demux discarded
image Failed to load the image
image image too large (2147483647 > 134217728), rejected
image Detected image: JPEG
main using stream_filter module "record"
main no stream_directory modules matched
main looking for xml reader module matching "any": 1 candidates
main looking for stream_directory module matching "any": 1 candidates
main looking for stream_filter module matching "record": 24 candidates
main attachment of directory-extractor failed for imem://
304219
prefetch end of stream
The thread 0x2180 has exited with code 0 (0x0).
main removing module "imem_access"
main Program doesn't contain anymore ES

问题排查与修复方案

1. 核心问题分析

从日志中可定位两个关键错误:

  • image image too large (2147483647 > 134217728), rejected:VLC将你的流识别为单张超大图片(因Length返回int.MaxValue),直接拒绝处理。
  • main EOF reached:Stream的Read逻辑存在断点,VLC提前读到流结束信号,导致播放终止。
  • 此外,VLC的sout参数配置错误,两个:sout=#选项会被覆盖,转码步骤失效。

2. 具体修复步骤

(1)修正VideoStream的流属性与读取逻辑

将Length改为返回-1(表示未知长度的流),同时实现连续的帧读取逻辑,避免单次读取后触发EOF:

// 新增私有字段保存当前帧和偏移
private byte[]? _currentFrame;
private int _currentFrameOffset;

public override long Length => -1; // 标记为未知长度流

public override int Read(byte[] buffer, int offset, int count)
{
    if (_currentFrame == null || _currentFrameOffset >= _currentFrame.Length)
    {
        // 当前帧已读完,取下一帧
        _currentFrame = frames.Take();
        _currentFrameOffset = 0;
    }

    int bytesToRead = Math.Min(count, _currentFrame.Length - _currentFrameOffset);
    Array.Copy(_currentFrame, _currentFrameOffset, buffer, offset, bytesToRead);
    _currentFrameOffset += bytesToRead;

    return bytesToRead;
}

public override int Read(Span<byte> buffer)
{
    if (_currentFrame == null || _currentFrameOffset >= _currentFrame.Length)
    {
        _currentFrame = frames.Take();
        _currentFrameOffset = 0;
    }

    int bytesToRead = Math.Min(buffer.Length, _currentFrame.Length - _currentFrameOffset);
    _currentFrame.AsSpan(_currentFrameOffset, bytesToRead).CopyTo(buffer);
    _currentFrameOffset += bytesToRead;

    return bytesToRead;
}

// 完善Dispose逻辑
void IDisposable.Dispose()
{
    if (_disposed)
        return;

    frames.CompleteAdding();
    enumerator.Dispose();
    _disposed = true;
}

(2)合并VLC sout参数

将转码与RTSP输出合并为链式配置,避免参数覆盖:

media.AddOption(":sout=#transcode{vcodec=h264,fps=10,vb=1024,acodec=none}:rtp{sdp=rtsp://127.
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 09:01:01