/// <summary> /// Discovers device on specific IP address and port. /// </summary> /// <param name="remoteEp">Remote IP address and port.</param> /// <returns>An observable sequence containing device information.</returns> private static IObservable <DiscoverResult> DiscoverOnSinglecast(IPEndPoint remoteEp) => Observable.Create <DiscoverResult>(async(observer, cancelToken) => { var disposables = new CompositeDisposable(); var udpClient = new UdpClient(); disposables.Add(udpClient); try { udpClient.Connect(remoteEp); UdpReceiveResult received; try { // send SW_ID_FindSw var timeout = new CancellationTokenSource(InternalConfiguration.NetworkTimeoutMsec); disposables.Add(timeout); var linkedCancel = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancelToken); disposables.Add(linkedCancel); var cmd = new SwApiCommand { Cmd = SwApiId.FindSw }.ToBytes(); if (await udpClient.SendAsync(cmd, cmd.Length).WithCancellation(linkedCancel.Token) != 4) { // cannot send. observer.OnCompleted(); return; } // receive SW_ID_FindSwAck timeout = new CancellationTokenSource(InternalConfiguration.NetworkTimeoutMsec); disposables.Add(timeout); linkedCancel = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancelToken); disposables.Add(linkedCancel); received = await udpClient.ReceiveAsync().WithCancellation(linkedCancel.Token); } catch (OperationCanceledException) { // no results. observer.OnCompleted(); return; } var ack = ParseFindSwAck(received.Buffer, received.RemoteEndPoint); if (ack != null) { // found device observer.OnNext(ack); } observer.OnCompleted(); } catch (Exception ex) { observer.OnError(ex); } finally { disposables.Dispose(); } });
/// <summary> /// Discovers devices on specific network interface. /// </summary> /// <param name="remoteEp">Multicast address and port.</param> /// <param name="localAddress">Local IP address.</param> /// <returns>An observable sequence containing device information.</returns> private static IObservable <DiscoverResult> DiscoverOnMulticast(IPEndPoint remoteEp, IPAddress localAddress) => Observable.Create <DiscoverResult>(async(observer, cancelToken) => { var disposables = new CompositeDisposable(); var udpClient = new UdpClient(new IPEndPoint(localAddress, remoteEp.Port)); disposables.Add(udpClient); try { udpClient.MulticastLoopback = false; udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); udpClient.JoinMulticastGroup(remoteEp.Address, localAddress); // send SW_ID_FindSw var cmd = new SwApiCommand { Cmd = SwApiId.FindSw }.ToBytes(); await udpClient.SendAsync(cmd, cmd.Length, remoteEp); // receive SW_ID_FindSwAck while (true) { if (cancelToken.IsCancellationRequested) { return; } var timeout = new CancellationTokenSource(InternalConfiguration.NetworkTimeoutMsec); disposables.Add(timeout); var linkedCancel = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancelToken); disposables.Add(linkedCancel); try { var received = await udpClient.ReceiveAsync().WithCancellation(linkedCancel.Token); var ack = ParseFindSwAck(received.Buffer, received.RemoteEndPoint); if (ack != null) { // found device observer.OnNext(ack); } } catch (OperationCanceledException) { // no more results. break; } } observer.OnCompleted(); } catch (Exception ex) { observer.OnError(ex); } finally { udpClient.DropMulticastGroup(remoteEp.Address); disposables.Dispose(); } });
public async Task SendControlCommand(SwApiCommand cmd) { if (!_controlUdp.Client.Connected) { throw new InvalidOperationException(); } var buf = cmd.ToBytes(); await _sendControlLock.WaitAsync(); try { await _controlUdp.SendAsync(buf, buf.Length); } finally { _sendControlLock.Release(); } }
public async Task SendGeneralCommand(SwApiCommand cmd) { if (_generalTcpStream == null) { throw new InvalidOperationException(); } var payload = cmd.ToBytes(); var buf = BitConverter.GetBytes((uint)payload.Length).Concat(payload).ToArray(); await _sendGeneralLock.WaitAsync(); try { await _generalTcpStream.WriteAsync(buf, 0, buf.Length); } finally { _sendGeneralLock.Release(); } }
private static DiscoverResult ParseFindSwAck(byte[] buf, IPEndPoint remoteEp) { if (SwApiCommand.GetApiId(buf) != SwApiId.FindSwAck) { return(null); } var ack = SwApiCommand.FromBytes <SwApiFindSwAck>(buf); if (ack == null) { return(null); } var result = new DiscoverResult { FindSwAck = ack, DisplayNameString = Encoding.UTF8.GetString(ack.DisplayName).TrimEnd('\0'), Address = remoteEp.Address }; Debug.WriteLine($"Device found. Name='{result.DisplayNameString}' IP={result.Address} PreviewPort={result.FindSwAck.Preview} ControlPort={result.FindSwAck.Command} GeneralPort={result.FindSwAck.Tcp}"); return(result); }
private static SwApiCommand ParseGeneralCommand(byte[] buf) { var apiId = SwApiCommand.GetApiId(buf); switch (apiId) { case SwApiId.SwBasicInfo: // SwApi_SwBasicInfo return(SwApiCommand.FromBytes <SwApiSwBasicInfo>(buf)); case SwApiId.FileList: // SwApi_FileList return(SwApiCommand.FromBytes <SwApiFileList>(buf)); case SwApiId.ChromaPreviewResult: // SwApi_ChromaPreviewResult return(SwApiCommand.FromBytes <SwApiChromaPreviewResult>(buf)); case SwApiId.LiveBroadcastStateResult: // SwApi_LiveBroadcastState return(SwApiCommand.FromBytes <SwApiLiveBroadcastState>(buf)); case SwApiId.UploadFileResult: // SwApi_UploadFileResult return(SwApiCommand.FromBytes <SwApiUploadFileResult>(buf)); case SwApiId.ExternalStorageInputStateResult: // SwApi_ExternalStorageInputStateResult return(SwApiCommand.FromBytes <SwApiExternalStorageInputStateResult>(buf)); case SwApiId.EthernetSettingResult: case SwApiId.WiFiNetworkSettingResult: case SwApiId.ApSettingResult: // SwApi_NetworkSettingResult return(SwApiCommand.FromBytes <SwApiNetworkSettingResult>(buf)); case SwApiId.MountNotify: // SwApi_MountNotify return(SwApiCommand.FromBytes <SwApiMountNotify>(buf)); case SwApiId.RecordingResult: // SwApi_RecordingState return(SwApiCommand.FromBytes <SwApiRecordingState>(buf)); case SwApiId.RecordSetting: case SwApiId.PreviewSetting: // SwApi_CameraSetting return(SwApiCommand.FromBytes <SwApiCameraSetting>(buf)); case SwApiId.FirmwareUpdateResult: // SwApi_DoFirmwareUpdateResult return(SwApiCommand.FromBytes <SwApiDoFirmwareUpdateResult>(buf)); case SwApiId.StatusNetworkAddress: // SwApi_NetworkAddress return(SwApiCommand.FromBytes <SwApiNetworkAddress>(buf)); case SwApiId.File: case SwApiId.ChangeProgramOutSetting: case SwApiId.TcpHeartBeat: case SwApiId.SetTimeResult: case SwApiId.ChangePreviewOutOsd: case SwApiId.ChangePreviewOutFormat: case SwApiId.GetFileError: // Do nothing return(null); default: Debug.WriteLine($"Unsupported GeneralCommand({apiId})"); return(null); } }
private static SwApiCommand ParseControlCommand(byte[] buf) { var apiId = SwApiCommand.GetApiId(buf); switch (apiId) { case SwApiId.StateMode: // SwApi_State_Mode return(SwApiCommand.FromBytes <SwApiStateMode>(buf)); case SwApiId.StateRecording: // SwApi_State_Recording return(SwApiCommand.FromBytes <SwApiStateRecording>(buf)); case SwApiId.StateFadeToDefaultColor: // SwApi_State_FadeToDefaultColor return(SwApiCommand.FromBytes <SwApiStateFadeToDefaultColor>(buf)); case SwApiId.StateExternalInput: // SwApi_State_ExternalInput return(SwApiCommand.FromBytes <SwApiStateExternalInput>(buf)); case SwApiId.StateProgramOut: // SwApi_State_ProgramOut return(SwApiCommand.FromBytes <SwApiStateProgramOut>(buf)); case SwApiId.StatePreviewOut: // SwApi_State_PreviewOut return(SwApiCommand.FromBytes <SwApiStatePreviewOut>(buf)); case SwApiId.StateDefaultBackgroundColor: // SwApi_State_DefaultBackgroundColor return(SwApiCommand.FromBytes <SwApiStateDefaultBackgroundColor>(buf)); case SwApiId.StateExternalStorage: // SwApi_State_ExternalStorage return(SwApiCommand.FromBytes <SwApiStateExternalStorage>(buf)); case SwApiId.StatePreviewMode: // SwApi_State_PreviewMode return(SwApiCommand.FromBytes <SwApiStatePreviewMode>(buf)); case SwApiId.StateTcpConnected: // no payload return(SwApiCommand.FromBytes <SwApiCommand>(buf)); case SwApiId.StatusAudioMixer: // SwApi_ChangeAudioMixer return(SwApiCommand.FromBytes <SwApiChangeAudioMixer>(buf)); case SwApiId.StatusAudioMixerAll: // SwApi_ChangeAudioMixerAll return(SwApiCommand.FromBytes <SwApiChangeAudioMixerAll>(buf)); case SwApiId.StatusAudioPeak: // SwApi_Status_AudioPeak return(SwApiCommand.FromBytes <SwApiStatusAudioPeak>(buf)); case SwApiId.StatusVideoSwitcher: // SwApi_VideoSwitcherStatus return(SwApiCommand.FromBytes <SwApiVideoSwitcherStatus>(buf)); case SwApiId.StatusVideoSwitcherAuto: // SwApi_VideoSwitcherStatus return(SwApiCommand.FromBytes <SwApiVideoSwitcherStatus>(buf)); case SwApiId.StatusSetPinpGeometry: // SwApi_VideoSetPinpGeometry return(SwApiCommand.FromBytes <SwApiVideoSetPinpGeometry>(buf)); case SwApiId.StatusSetPinpBorder: // SwApi_VideoSetBorder return(SwApiCommand.FromBytes <SwApiVideoSetBorder>(buf)); case SwApiId.StatusSetChromaRange: // SwApi_VideoSetChromaRange return(SwApiCommand.FromBytes <SwApiVideoSetChromaRange>(buf)); case SwApiId.StatusSetSubMode: // SwApi_VideoSetSubMode return(SwApiCommand.FromBytes <SwApiVideoSetSubMode>(buf)); case SwApiId.StatusNetworkAddress: // SwApi_NetworkAddress return(SwApiCommand.FromBytes <SwApiNetworkAddress>(buf)); case SwApiId.StatusUpdater: // SwApi_DoFirmwareUpdateResult return(SwApiCommand.FromBytes <SwApiDoFirmwareUpdateResult>(buf)); case SwApiId.EthernetSettingResult: case SwApiId.ApSettingResult: case SwApiId.WiFiNetworkSettingResult: // SwApi_NetworkSettingResult return(SwApiCommand.FromBytes <SwApiNetworkSettingResult>(buf)); case SwApiId.MountNotify: // SwApi_MountNotify return(SwApiCommand.FromBytes <SwApiMountNotify>(buf)); case SwApiId.LiveBroadcastStateResult: // SwApi_LiveBroadcastState return(SwApiCommand.FromBytes <SwApiLiveBroadcastState>(buf)); default: Debug.WriteLine($"Unsupported ControlCommand({apiId})"); return(null); } }