0

I am trying to construct a UI for Onvif complaint devices. I've been beating my head against the wall for sometime now. I believe I have the custom URI correctly constructed. According to the ONVIF Programmers Guide we need to Get Profiles, GetStreamURI, Request Streaming. http://www.openipcam.com/files/ONVIF/ONVIF_WG-APG-Application_Programmer's_Guide.pdf

Using Wireshark I believe I see HTTP packets being sent (showing the appropriate requests), and what I believe are appropriate responses. The final GetStreamURI gets a successful response from the camera. Then when I try to call _mp.Play I see a few packets over HTTP and a few TCP packets back back from the camera. After this communication stops.

using System;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using LibVLCSharp.Shared;
using LibVLCSharp;
using System.ServiceModel.Channels;
using Lib_vlc_CSharp_Onvif.OnvifDevice;
using System.ServiceModel;
using Lib_vlc_CSharp_Onvif.OnvifMedia;


namespace Lib_vlc_CSharp_Onvif
{

    public partial class Form1 : Form
    {
        public LibVLC _libVLC;
        public LibVLCSharp.Shared.MediaPlayer _mp;
        public LibVLCSharp.Shared.Media media; 


        //ToDO... make screen size adjustable 
        public System.Drawing.Size VidSize;
        public System.Drawing.Size FormSize;
        public System.Drawing.Point OldVidLoc;


        //Create Onvif Media Profiles through service references 
        OnvifMedia.Media2Client Onvif_media;
        OnvifMedia.MediaProfile[] profiles;

        //Custom URI variable 
        UriBuilder deviceUri; 
        public Form1()
        {
            InitializeComponent();

            //LibVLCSharp Specific 
            Core.Initialize();
            this.KeyPreview = true;

            //Just controlling the size. TODO: Imp controls 
            VidSize = videoView.Size;
            FormSize = this.Size;
            OldVidLoc = videoView.Location;

            //Vlc Specific 
            //Set up the Vlc Lib and then connect the Form1 media window player to the media player of the library.
            //videoVew is vlcsharp item in Form1. 
            _libVLC = new LibVLC();
            _mp = new MediaPlayer(_libVLC);
            videoView.MediaPlayer = _mp;             
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //Set up device to get profiles (Onvif Specific) 

            //Required a custom URI and binding. 
            deviceUri = new UriBuilder("Http:/onvif/device_service");
            System.ServiceModel.Channels.Binding binding;
            HttpTransportBindingElement httpTransport = new HttpTransportBindingElement();
            httpTransport.AuthenticationScheme = System.Net.AuthenticationSchemes.Digest;
            binding = new CustomBinding(new TextMessageEncodingBindingElement(MessageVersion.Soap12, Encoding.UTF8), httpTransport);

            //Assign IP Address to device URI. TODO: This eventually will be pulled from user entered value in an text box. 
            deviceUri.Host = "xxx.xxx.x.x";
            DeviceClient Onvif_Device = new DeviceClient(binding, new EndpointAddress(deviceUri.ToString()));
            OnvifDevice.Service[] service = Onvif_Device.GetServices(false);

            //Check if they contain media and that we have made contact TODO wrap in a try catch block
            OnvifDevice.Service xmedia = service.FirstOrDefault(s => s.Namespace == "http://www.onvif.org/ver20/media/wsdl");
            if (xmedia != null)
            {
                Onvif_media = new Media2Client(binding, new EndpointAddress(deviceUri.ToString()));
                Onvif_media.ClientCredentials.HttpDigest.ClientCredential.UserName = "admin";
                Onvif_media.ClientCredentials.HttpDigest.ClientCredential.Password = "admin";
                Onvif_media.ClientCredentials.HttpDigest.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;

                //Get camera Profiles. 
                profiles = Onvif_media.GetProfiles(null, null);

                if (profiles != null)
                {
                    foreach (var p in profiles)
                    {
                        listBox.Items.Add(p.Name); 
                        //Profiles listed in box match profiles setup on camera. 
                    }
                }
            }

            //Eventually add a selection option on the list box. 
            //listBox.SelectedINdexChanged += OnSelectionChanged

            //If we have profiles build a custom URI to past to the vlc boject 
            if (profiles != null)
            {
                //Building URI to pass to VLCSharp VideoView Item.
                //https://www.onvif.org/ver20/media/wsdl/media.wsdl
                //GetSreamUri and define RtspUnicast. 
                //http://www.openipcam.com/files/ONVIF/ONVIF_WG-APG-Application_Programmer's_Guide.pdf on page 57&58

                UriBuilder local_uri = new UriBuilder(Onvif_media.GetStreamUri("RtspUnicast", profiles[0].token));

                //ToDO: Build list box to allow user to switch between profiles. Just past main profile for now. 

                local_uri.Host = deviceUri.Host;
                local_uri.Port = deviceUri.Port;
                local_uri.Scheme = "rtsp";
                //List full URI info. 
                infoBox.Text = local_uri.Host + local_uri.Port + local_uri.Path; 

                //Past it to VideoView and start playing video. 
                _mp.Play(new Media(_libVLC, local_uri.Uri));
            }
        }
    }
}

Update: I believe my issue is the URI I have built requires validation. When I take this URI and put it into a web browser I get a 401 Error. I'm not sure why I don't see this error on wire-shark. I assign the user name and password into URI object but when I check "IsWellFormedURIString" I get an "invalid Port Error."

BBridges
  • 130
  • 14
  • Note: I have confirmed that profiles for the Camera are configured correctly using Onvif Device Manager. I have compared the profiles that each program pulls in and I believe them to be the same. – BBridges Sep 05 '20 at 19:35

2 Answers2

2

Did you try --rtsp-user and --rtsp-pwd ? you could also set the RTSP password with the dialog API.

If that doesn't work, please share your full logs.

You should be able to copy/paste the URL from Onvif Device Manager right into VLC and see it play (provided that you entered the correct credentials). If it doesn't, that's already an issue on its own.

cube45
  • 3,429
  • 2
  • 24
  • 35
  • You got me part of the way there. I didn't think about comparing the URL in ODM to what i was doing. Turns out I have two issues, not one. First, I was using the wrong port. This explains why the the communication was going dead when the stream URI request was sent. Now with the correct port I am seeing RTSP packets sent back and forth but with a 401 Authentication Error. Building my PW and Username into the URI doesn't work. I get an invalid formatting error even though this appears to be correct when looking at valid formatting for RTSP. – BBridges Sep 06 '20 at 15:12
  • Interesting: Using the VLC program, I can copy and paste my URI with the correct port and it pops up asking for user name and password. Put that in and it streams my video. Try copying and pasting a URI with a user name and password VLC errors out. – BBridges Sep 06 '20 at 15:13
  • I'm marking this as complete and might open up a new question if I can't figure it out. According to the Onvif specifications I can only authenticate using Digest over HTTP or WSS. I chose Digest over HTTP (Obviously). I must be doing something wrong with the authentication. – BBridges Sep 06 '20 at 15:58
  • 1
    Authenticating with the ONVIF protocol is not enough, you still need to tell VLC to use RTSP authentication. For your use case, the onvif protocol is only a programmatic way to discover the RTSP URL to use. – cube45 Sep 06 '20 at 19:21
  • 1
    Either use the --rtsp options as suggested, or use the Dialog API to respond when libvlc is asking for the username/password, as in the VLC interface. I think there is a sample for that. – cube45 Sep 06 '20 at 19:22
  • I was wondering if that was what it was. Because using Digest is see the correct response... first a 401Error and then a 200okay. I wouldn't know where to start with the Dialog API and giving authorization for the VLC interface. I'll see if I can find an example. – BBridges Sep 06 '20 at 19:28
  • https://code.videolan.org/videolan/LibVLCSharp/-/issues/377 – BBridges Sep 06 '20 at 19:40
  • 1
    Totally unrelated to your issue, it's about some issues with the UWP package. 401 (digest challenge) then 200 is the correct workflow for HTTP digest authentication. I don't know what you think is wrong, but you're probably not looking in the correct direction. – cube45 Sep 07 '20 at 05:18
  • I understand that! That is how I am getting the uri in the first place. authentication is required for the call getprofiles. I first see a 401 then a 200 second time around. Yet, when I pass the uri (That was acquired by get profiles) to the Vic object It gives a 401 error and doesn’t ever stop. It made me think that I’d need additional authorization for the Vic to access the stream? Not sure! – BBridges Sep 07 '20 at 05:27
  • 1
    Yes, the RTSP 401 means that you're not authenticated. See my answer, all is inside. Do a google search for how to pass the options to the libvlc instance – cube45 Sep 07 '20 at 10:08
  • Yes, I am been trying to figure out how to do it but am totally lost. Google isn't finding me anything useful. I'll keep on digging. – BBridges Sep 07 '20 at 14:02
  • Okay! I finally was able to figure out a solution. I couldn't ever get the API to work. But it turns out if you have special characters in your password you have to do something special when assigning it to the URI Object. – BBridges Sep 07 '20 at 15:49
0

You can still assign the Password and Username password.

MyUri.UserName = MyName
MyUri.Password = password 

You run into problems when your password has characters like "#" While I haven't figured out a workaround on this, for the time being keeping the password simple like "MyPassword" instead of "MyPassword#1234" will allow you to use the URI with the name and password built into the string.

BBridges
  • 130
  • 14