LibVlcSharp SetVideoCallbacks(Lock, null, Display) 在从 IP 摄像机播放流时没有被调用

问题描述 投票:0回答:1

我正在尝试开发一个应用程序,该应用程序可以流式传输来自 IP 摄像机的视频,并处理来自这些 IP 摄像机的一些帧。我已经使用 SetVideoCallbacks(Lock, null, Display) 设置了视频回调,但 Lock 和 Display 方法没有被调用。

我尝试用 mp4 视频执行此操作,并且工作正常。但是,当我尝试使用 IP 摄像头执行此操作时,我可以在 VideoView 中看到流,但不会调用 Lock 和 Display 方法。早些时候,我认为这可能是因为帧是使用 GPU 进行解码的,但任务管理器显示,当我运行应用程序时,GPU 使用率可以忽略不计,但 CPU 使用率相当高(~40%)。我还尝试将 EnableHardwareDecoding 设置为 true 和 false。我相信存在一些格式错误。也许相机 rtsp 流未解码为 I420 格式。我的摄像机流是 H.265 编解码器,但也支持 H.264 编解码器。 我还知道,我可以看到视频源这一事实本身就很可疑,因为如果调用 Lock 函数,那么我就看不到视频(除非我复制帧)。 请帮助我解决这个问题或指导我是否有更好的方法。 谢谢。

public partial class MainWindow : Window
{
    private Camera _camera;
    private LibVLC _libVLC;
    private LibVLCSharp.Shared.MediaPlayer _mediaPlayer;

    public const uint width = 1280;
    public const uint height = 720;
    public const uint pitch = width*2;//because of I420 which uses 12 bits per pixel
    public MemoryMappedFile? CurrentMappedFile;
    public MemoryMappedViewAccessor? CurrentMappedViewAccessor;
    public ConcurrentQueue<(MemoryMappedFile? file, MemoryMappedViewAccessor? accessor)> FramesToProcess = new ConcurrentQueue<(MemoryMappedFile? file, MemoryMappedViewAccessor? accessor)>();
    public int FrameCounter = 0;
    public CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

    public MainWindow()
    {
        try
        {
            InitializeComponent();
            Core.Initialize();
            _camera = new Camera("Camera", "192.168.1.188", 554, "admin", "123456", "stream");
            _libVLC = new LibVLC("--verbose=2");
            _mediaPlayer = new LibVLCSharp.Shared.MediaPlayer(_libVLC) { EnableHardwareDecoding = true};

            //_libVLC.Log += (sender, e) => Debug.WriteLine($"[{e.Level}] {e.Module}:{e.Message}");
            var media = new Media(_libVLC, _camera.GetStreamUrl(), FromType.FromLocation);

            media.AddOption(":no-audio");
            _mediaPlayer.Media = media;

            _mediaPlayer.SetVideoFormat("I420", width, height, pitch);
            _mediaPlayer.SetVideoCallbacks(Lock, null, Display);
            _mediaPlayer.Stopped += (s, e) => cancellationTokenSource.CancelAfter(1);
            
            VideoView.MediaPlayer = _mediaPlayer;
            VideoView.MediaPlayer.Play();
            this.Closed += MainWindow_Closed;
            Task.Run(() => ProcessFrames(cancellationTokenSource.Token), cancellationTokenSource.Token);
        }
        catch(Exception ex) {
            Application.Current.Dispatcher.Invoke(() => MessageBox.Show(ex.Message));
            Debug.WriteLine(ex.Message);
        }
    }


    private IntPtr Lock(IntPtr opaque, IntPtr planes)
    {
        //Debug.WriteLine("Lock is called");

        Dispatcher.Invoke(() =>
        {
            lockTextBox.Text = $"Lock method called {FrameCounter}";
        });

        
        CurrentMappedFile = MemoryMappedFile.CreateNew(null, pitch * height);
        CurrentMappedViewAccessor = CurrentMappedFile.CreateViewAccessor();

        
        Marshal.WriteIntPtr(planes, CurrentMappedViewAccessor.SafeMemoryMappedViewHandle.DangerousGetHandle());

        return IntPtr.Zero;
    }

    private void Display(IntPtr opaque, IntPtr picture)
    {
        //Debug.WriteLine($"Debug is called with framecounter= {FrameCounter}");
        Dispatcher.Invoke(() =>
        {
            displayTextBox.Text = $"Display method called {FrameCounter}";
        });

        if (FrameCounter  == 5)
        {
            //Enqueued frames to be disposed in the processing thread
            FramesToProcess.Enqueue((CurrentMappedFile, CurrentMappedViewAccessor));
            CurrentMappedFile = null;
            CurrentMappedViewAccessor = null;
            FrameCounter = 0;//reset counter
        }
        else
        {
            if (CurrentMappedViewAccessor != null && CurrentMappedFile != null)
            {
                CurrentMappedViewAccessor.Dispose();
                CurrentMappedFile.Dispose();
            }
            
            CurrentMappedFile = null;
            CurrentMappedViewAccessor = null;
            FrameCounter++;
        }
    }
}
wpf libvlc ip-camera libvlcsharp h.265
1个回答
0
投票

在当前的 libvlc API 中,你不能同时使用 VideoView 和视频回调,所以我认为那里存在冲突。

您的选择是有限的:要么打开两个流,要么在回调中自己进行显示(比直接在 VideoView 中显示更慢、更困难且更占用 CPU 资源)。LibVLC 4 将有一个新的回调来直接获取 directX 纹理,但它仍然在预览)

© www.soinside.com 2019 - 2024. All rights reserved.