/// <summary> /// Initializes a new instance of the <see cref="CallHandler"/> class. /// </summary> /// <param name="statefulCall">The stateful call.</param> public CallHandler(ICall statefulCall) { this.Call = statefulCall; this.logger = statefulCall.GraphLogger; this.Call.OnUpdated += this.CallOnUpdated; // subscribe to dominant speaker event on the audioSocket var audioSocket = this.Call.GetLocalMediaSession().AudioSocket; audioSocket.DominantSpeakerChanged += this.OnDominantSpeakerChanged; // susbscribe to the participants updates, this will inform the bot if a particpant left/joined the conference this.Call.Participants.OnUpdated += this.ParticipantsOnUpdated; // attach the botMediaStream this.BotMediaStream = new BotMediaStream(this.Call.GetLocalMediaSession(), this.logger); // initialize the timer var timer = new Timer(1000 * 60); // every 60 seconds timer.AutoReset = true; timer.Elapsed += this.OnRecordingStatusFlip; this.recordingStatusFlipTimer = timer; }
/// <summary> /// Initializes a new instance of the <see cref="BotMediaStream"/> class. /// </summary> /// <param name="mediaSession">The media session.</param> /// <param name="logger">Graph logger.</param> /// <exception cref="InvalidOperationException">Throws when no audio socket is passed in.</exception> public BotMediaStream(ILocalMediaSession mediaSession, IGraphLogger logger) { ArgumentVerifier.ThrowOnNullArgument(mediaSession, "mediaSession"); this.mediaSession = mediaSession; this.logger = logger; this.audioSendStatusActive = new TaskCompletionSource <bool>(); this.videoSendStatusActive = new TaskCompletionSource <bool>(); this.startVideoPlayerCompleted = new TaskCompletionSource <bool>(); this.audioSocket = this.mediaSession.AudioSocket; if (this.audioSocket == null) { throw new InvalidOperationException("A mediaSession needs to have at least an audioSocket"); } this.audioSocket.AudioSendStatusChanged += this.OnAudioSendStatusChanged; this.mainVideoSocket = this.mediaSession.VideoSockets?.FirstOrDefault(); if (this.mainVideoSocket != null) { this.mainVideoSocket.VideoSendStatusChanged += this.OnVideoSendStatusChanged; this.mainVideoSocket.VideoKeyFrameNeeded += this.OnVideoKeyFrameNeeded; } this.videoSockets = this.mediaSession.VideoSockets?.ToList(); this.vbssSocket = this.mediaSession.VbssSocket; if (this.vbssSocket != null) { this.vbssSocket.VideoSendStatusChanged += this.OnVbssSocketSendStatusChanged; } var ignoreTask = this.StartAudioVideoFramePlayerAsync().ForgetAndLogExceptionAsync(this.logger, "Failed to start the player"); }
/// <summary> /// Initializes a new instance of the <see cref="DemoController" /> class. /// </summary> public DemoController() { _logger = AppHost.AppHostInstance.Resolve <IGraphLogger>(); _eventPublisher = AppHost.AppHostInstance.Resolve <IEventPublisher>(); _botService = AppHost.AppHostInstance.Resolve <IBotService>(); _settings = AppHost.AppHostInstance.Resolve <IOptions <AzureSettings> >().Value; }
/// <summary> /// Initializes a new instance of the <see cref="BotMediaStream" /> class. /// </summary> /// <param name="mediaSession">he media session.</param> /// <param name="callId">The call identity</param> /// <param name="logger">The logger.</param> /// <param name="eventPublisher">Event Publisher</param> /// <param name="settings">Azure settings</param> /// <exception cref="InvalidOperationException">A mediaSession needs to have at least an audioSocket</exception> public BotMediaStream( ILocalMediaSession mediaSession, string callId, IGraphLogger logger, IEventPublisher eventPublisher, IAzureSettings settings ) : base(logger) { ArgumentVerifier.ThrowOnNullArgument(mediaSession, nameof(mediaSession)); ArgumentVerifier.ThrowOnNullArgument(logger, nameof(logger)); ArgumentVerifier.ThrowOnNullArgument(settings, nameof(settings)); this.participants = new List <IParticipant>(); _eventPublisher = eventPublisher; _callId = callId; _mediaStream = new MediaStream( settings, logger, mediaSession.MediaSessionId.ToString() ); // Subscribe to the audio media. this._audioSocket = mediaSession.AudioSocket; if (this._audioSocket == null) { throw new InvalidOperationException("A mediaSession needs to have at least an audioSocket"); } this._audioSocket.AudioMediaReceived += this.OnAudioMediaReceived; }
/// <summary> /// Initializes a new instance of the <see cref="Bot"/> class. /// </summary> /// <param name="options">The bot options.</param> /// <param name="graphLogger">The graph logger.</param> /// <param name="serviceContext">Service context.</param> public Bot(BotOptions options, IGraphLogger graphLogger, StatefulServiceContext serviceContext) { this.Options = options; this.logger = graphLogger; var name = this.GetType().Assembly.GetName().Name; var builder = new CommunicationsClientBuilder( name, options.AppId, this.logger); var authProvider = new AuthenticationProvider( name, options.AppId, options.AppSecret, this.logger); builder.SetAuthenticationProvider(authProvider); builder.SetNotificationUrl(options.BotBaseUrl.ReplacePort(options.BotBaseUrl.Port + serviceContext.NodeInstance())); builder.SetMediaPlatformSettings(this.MediaInit(options, serviceContext)); builder.SetServiceBaseUrl(options.PlaceCallEndpointUrl); this.Client = builder.Build(); this.Client.Calls().OnIncoming += this.CallsOnIncoming; this.Client.Calls().OnUpdated += this.CallsOnUpdated; this.OnlineMeetings = new OnlineMeetingHelper(authProvider, options.PlaceCallEndpointUrl); }
#pragma warning disable AvoidAsyncVoid // Avoid async void /// <summary> /// Extension for Task to execute the task in background and log any exception. /// </summary> /// <param name="task">The task.</param> /// <param name="logger">The logger.</param> /// <param name="description">The description.</param> /// <param name="memberName">Name of the member.</param> /// <param name="filePath">The file path.</param> /// <param name="lineNumber">The line number.</param> public static async void ForgetAndLogException( this Task task, IGraphLogger logger, string description = null, [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = 0) { try { await task.ConfigureAwait(false); } catch (Exception ex) { description = string.IsNullOrWhiteSpace(description) ? "Exception while executing task." : description; logger.Error( ex, description, memberName: memberName, filePath: filePath, lineNumber: lineNumber); } }
/// <summary> /// Initialize the instance. /// </summary> /// <param name="service">Service instance.</param> /// <param name="logger">Graph logger.</param> internal void Initialize(Service service, IGraphLogger logger) { Validator.IsNull(this.Logger, "Multiple initializations are not allowed."); this.Logger = logger; this.Observer = new SampleObserver(logger); var name = this.GetType().Assembly.GetName().Name; var builder = new CommunicationsClientBuilder( name, service.Configuration.AadAppId, this.Logger); var authProvider = new AuthenticationProvider( name, service.Configuration.AadAppId, service.Configuration.AadAppSecret, this.Logger); builder.SetAuthenticationProvider(authProvider); builder.SetNotificationUrl(service.Configuration.CallControlBaseUrl); builder.SetMediaPlatformSettings(service.Configuration.MediaPlatformSettings); builder.SetServiceBaseUrl(service.Configuration.PlaceCallEndpointUrl); this.Client = builder.Build(); this.Client.Calls().OnIncoming += this.CallsOnIncoming; this.Client.Calls().OnUpdated += this.CallsOnUpdated; this.OnlineMeetings = new OnlineMeetingHelper(authProvider, service.Configuration.PlaceCallEndpointUrl); }
/// <summary> /// Boots this instance. /// </summary> public void Boot() { DotNetEnv.Env.Load(new DotNetEnv.Env.LoadOptions(parseVariables: false)); var builder = new ConfigurationBuilder(); // tell the builder to look for the appsettings.json file builder .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables(); var configuration = builder.Build(); ServiceCollection = new ServiceCollection(); ServiceCollection.AddCoreServices(configuration); ServiceProvider = ServiceCollection.BuildServiceProvider(); _logger = Resolve <IGraphLogger>(); try { _settings = Resolve <IOptions <AzureSettings> >().Value; _settings.Initialize(); Resolve <IEventPublisher>(); _botService = Resolve <IBotService>(); } catch (Exception e) { _logger.Error(e, "Unhandled exception in Boot()"); } }
/// <summary> /// Initializes a new instance of the <see cref="JoinCallController" /> class. /// </summary> /// <param name="logger">The logger.</param> /// <param name="eventPublisher">The event publisher.</param> /// <param name="botService">The bot service.</param> /// <param name="settings">The settings.</param> public JoinCallController(IGraphLogger logger, IEventPublisher eventPublisher, IBotService botService, IAzureSettings settings) { _logger = logger; _botService = botService; _settings = (AzureSettings)settings; _eventPublisher = eventPublisher; }
/// <summary> /// Initializes a new instance of the <see cref="CallHandler"/> class. /// </summary> /// <param name="statefulCall">The stateful call.</param> /// <param name="logger">Logger instance.</param> public CallHandler(ICall statefulCall, IGraphLogger logger) { this.Call = statefulCall; this.logger = logger; // subscribe to call updates this.Call.OnUpdated += this.CallOnUpdated; // subscribe to dominant speaker event on the audioSocket this.Call.GetLocalMediaSession().AudioSocket.DominantSpeakerChanged += this.OnDominantSpeakerChanged; // subscribe to the VideoMediaReceived event on the main video socket this.Call.GetLocalMediaSession().VideoSockets.FirstOrDefault().VideoMediaReceived += this.OnVideoMediaReceived; // susbscribe to the participants updates, this will inform the bot if a particpant left/joined the conference this.Call.Participants.OnUpdated += this.ParticipantsOnUpdated; foreach (var videoSocket in this.Call.GetLocalMediaSession().VideoSockets) { this.availableSocketIds.Add((uint)videoSocket.SocketId); } var outcome = Serializer.SerializeObject(statefulCall.Resource); this.OutcomesLogMostRecentFirst.AddFirst("Call Created:\n" + outcome); // attach the botMediaStream this.BotMediaStream = new BotMediaStream(this.Call.GetLocalMediaSession(), this.logger); }
/// <summary> /// Initializes a new instance of the <see cref="DemoController" /> class. /// </summary> public DemoController(IBotService botService, IOptions <AzureSettings> settings, IGraphLogger logger, InMemoryObserver observer) { _logger = logger; _botService = botService; _settings = settings.Value; _observer = observer; }
/// <summary> /// Extension for Task to execute the task in background and log any exception. /// </summary> /// <param name="task">Task to execute and capture any exceptions.</param> /// <param name="logger">Graph logger.</param> /// <param name="description">Friendly description of the task for debugging purposes.</param> /// <param name="memberName">Calling function.</param> /// <param name="filePath">File name where code is located.</param> /// <param name="lineNumber">Line number where code is located.</param> /// <returns> /// A <see cref="Task" /> representing the asynchronous operation. /// </returns> public static async Task ForgetAndLogExceptionAsync( this Task task, IGraphLogger logger, string description = null, [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = 0) { try { await task.ConfigureAwait(false); logger?.Verbose( $"Completed running task successfully: {description ?? string.Empty}", memberName: memberName, filePath: filePath, lineNumber: lineNumber); } catch (Exception e) { // Log and absorb all exceptions here. logger?.Error( e, $"Caught an Exception running the task: {description ?? string.Empty}", memberName: memberName, filePath: filePath, lineNumber: lineNumber); } }
/// <summary> /// Initializes a new instance of the <see cref="BotMediaStream"/> class. /// </summary> /// <param name="mediaSession">The media session.</param> /// <param name="logger">Graph logger.</param> /// <exception cref="InvalidOperationException">Throws when no audio socket is passed in.</exception> public BotMediaStream(ILocalMediaSession mediaSession, IGraphLogger logger) : base(logger) { ArgumentVerifier.ThrowOnNullArgument(mediaSession, nameof(mediaSession)); ArgumentVerifier.ThrowOnNullArgument(logger, nameof(logger)); this.mediaSession = mediaSession; // Subscribe to the audio media. this.audioSocket = mediaSession.AudioSocket; if (this.audioSocket == null) { throw new InvalidOperationException("A mediaSession needs to have at least an audioSocket"); } this.audioSocket.AudioMediaReceived += this.OnAudioMediaReceived; // Subscribe to the video media. this.videoSockets = this.mediaSession.VideoSockets?.ToList(); if (this.videoSockets?.Any() == true) { this.videoSockets.ForEach(videoSocket => videoSocket.VideoMediaReceived += this.OnVideoMediaReceived); } // Subscribe to the VBSS media. this.vbssSocket = this.mediaSession.VbssSocket; if (this.vbssSocket != null) { this.mediaSession.VbssSocket.VideoMediaReceived += this.OnVbssMediaReceived; } }
/// <summary> /// Instantiate a custom server (e.g. for testing). /// </summary> /// <param name="config">The configuration to initialize.</param> /// <param name="logger">Graph logger instance.</param> public void Initialize(IConfiguration config, IGraphLogger logger) { this.Configuration = config; this.logger = logger; Bot.Bot.Instance.Initialize(this, logger); }
public SampleObserver(IGraphLogger logger) { // Log unhandled exceptions. AppDomain.CurrentDomain.UnhandledException += (_, e) => logger.Error(e.ExceptionObject as Exception, $"Unhandled exception"); TaskScheduler.UnobservedTaskException += (_, e) => logger.Error(e.Exception, "Unobserved task exception"); this.subscription = logger.Subscribe(this); }
/// <summary> /// Initializes a new instance of the <see cref="Startup"/> class. /// </summary> /// <param name="configuration">The configuration interface.</param> public Startup(IConfiguration configuration) { this.graphLogger = new GraphLogger( component: nameof(Startup), redirectToTrace: false); this.Configuration = configuration; }
public TextToSpeechComponent(Pipeline pipeline, AzureTextToSpeechSettings settings, IGraphLogger logger) : base(pipeline) { var speechConfig = SpeechConfig.FromSubscription(settings.SpeechSubscriptionKey, settings.SpeechRegion); speechConfig.SpeechSynthesisVoiceName = settings.SpeechSynthesisVoiceName; this.synthesizer = new SpeechSynthesizer(speechConfig, null); this.logger = logger; }
/// <summary> /// Initializes a new instance of the <see cref="BotService" /> class. /// </summary> /// <param name="logger">The logger.</param> /// <param name="eventPublisher">The event publisher.</param> /// <param name="settings">The settings.</param> public BotService( IGraphLogger logger, IOptions <AzureSettings> settings ) { _logger = logger; _settings = settings.Value; }
public HeartbeatHandler(TimeSpan frequency, IGraphLogger logger) : base(logger) { var timer = new Timer(frequency.TotalMilliseconds); timer.Enabled = true; timer.AutoReset = true; timer.Elapsed += this.HeartbeatDetected; this.heartbeatTimer = timer; }
/// <summary> /// Initializes a new instance of the <see cref="BotService" /> class. /// </summary> /// <param name="logger">The logger.</param> /// <param name="eventPublisher">The event publisher.</param> /// <param name="settings">The settings.</param> public BotService( IGraphLogger logger, IEventPublisher eventPublisher, IAzureSettings settings ) { _logger = logger; _eventPublisher = eventPublisher; _settings = (AzureSettings)settings; }
/// <summary> /// Initializes a new instance of the <see cref="HueBot"/> class. /// </summary> /// <param name="context">Stateful service context from service fabric.</param> /// <param name="logger">Global logger instance.</param> public HueBot(StatefulServiceContext context, IGraphLogger logger) : base(context) { this.logger = logger; // Set directory to where the assemblies are running from. // This is necessary for Media binaries to pick up logging configuration. var location = Assembly.GetExecutingAssembly().Location; Directory.SetCurrentDirectory(Path.GetDirectoryName(location)); }
/// <summary> /// Initializes a new instance of the <see cref="BotService" /> class. /// </summary> /// <param name="logger">The logger.</param> /// <param name="eventPublisher">The event publisher.</param> /// <param name="settings">The settings.</param> public BotService( IGraphLogger logger, IOptions <AzureSettings> settings, IOptions <AzureTextToSpeechSettings> ttsSettings, IOptions <BotSettings> botSettings ) { this._logger = logger; this._botSettings = botSettings.Value; this._settings = settings.Value; this._ttsSettings = ttsSettings.Value; }
/// <summary> /// Extension for Task to execute the task in background and log any exception. /// </summary> /// <param name="task">Task to execute and capture any exceptions.</param> /// <param name="logger">Graph logger.</param> /// <param name="description">Friendly description of the task for debugging purposes.</param> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> public static async Task ForgetAndLogExceptionAsync(this Task task, IGraphLogger logger, string description = null) { try { await task.ConfigureAwait(false); } catch (Exception e) { // ignore logger.Error(e, $"Caught an Exception running the task: {description ?? string.Empty} {e.Message}\n StackTrace: {e.StackTrace}"); } }
public ISLPipeline( IGraphLogger logger, AzureTextToSpeechSettings ttsSettings, BotSettings botSettings, Action <List <AudioMediaBuffer> > sendAudioToBot, Action <byte[]> sendScreenShareToBot) { this.logger = logger; this.ttsSettings = ttsSettings; this.botSettings = botSettings; this.sendAudioToBot = sendAudioToBot; this.sendScreenShareToBot = sendScreenShareToBot; }
/// <summary> /// Initializes a new instance of the <see cref="CallHandler"/> class. /// </summary> /// <param name="statefulCall">Stateful call instance.</param> public CallHandler(ICall statefulCall) { this.logger = statefulCall.GraphLogger; this.Call = statefulCall; this.Call.OnUpdated += this.OnCallUpdated; if (this.Call.GetLocalMediaSession() != null) { this.Call.GetLocalMediaSession().AudioSocket.DominantSpeakerChanged += this.OnDominantSpeakerChanged; this.Call.GetLocalMediaSession().VideoSocket.VideoMediaReceived += this.OnVideoMediaReceived; } this.Call.Participants.OnUpdated += this.OnParticipantsUpdated; }
/// <summary> /// Creates the video buffers from the provided h264 files. /// </summary> /// <param name="currentTick">The number of ticks that represent the current date and time.</param> /// <param name="videoFormats">The encoded video source formats.</param> /// <param name="replayed">If the video frame is being replayed.</param> /// <param name="logger">Graph logger.</param> /// <returns>The newly created list of <see cref="VideoMediaBuffer"/>.</returns> public static List <VideoMediaBuffer> CreateVideoMediaBuffers( long currentTick, List <VideoFormat> videoFormats, bool replayed, IGraphLogger logger) { List <VideoMediaBuffer> videoMediaBuffers = new List <VideoMediaBuffer>(); try { foreach (var videoFormat in videoFormats) { if (H264Frames.TryGetValue(videoFormat.GetId(), out List <H264Frame> h264Frames)) { // create the videoBuffers var packetSizeInMs = (long)((MsInOneSec / videoFormat.FrameRate) * TicksInOneMs); var referenceTime = currentTick; if (replayed) { referenceTime += packetSizeInMs; } foreach (var h264Frame in h264Frames) { var frameSize = h264Frame.Size; byte[] buffer = new byte[frameSize]; Marshal.Copy(h264Frame.Data, buffer, 0, (int)frameSize); videoMediaBuffers.Add(new VideoSendBuffer(buffer, (uint)buffer.Length, videoFormat, referenceTime)); referenceTime += packetSizeInMs; } } else { logger.Error($"h264FileReader not found for the videoFromat {videoFormat}"); } } logger.Info($"created {videoMediaBuffers.Count} VideoMediaBuffers"); return(videoMediaBuffers); } catch (Exception ex) { logger.Error(ex, $"Failed to create the videoMediaBuffers with exception"); } return(videoMediaBuffers); }
/// <summary> /// Configuration settings like Authentication, Routes for OWIN. /// </summary> /// <param name="app">Builder to configure.</param> /// <param name="logger">Graph logger.</param> public void ConfigureSettings(IAppBuilder app, IGraphLogger logger) { HttpConfiguration httpConfig = new HttpConfiguration(); httpConfig.MapHttpAttributeRoutes(); httpConfig.MessageHandlers.Add(new LoggingMessageHandler(isIncomingMessageHandler: true, logger: logger, urlIgnorers: new[] { "/logs" })); httpConfig.Services.Add(typeof(IExceptionLogger), new Common.Logging.ExceptionLogger(logger)); // TODO: Provide serializer settings hooks // httpConfig.Formatters.JsonFormatter.SerializerSettings = RealTimeMediaSerializer.GetSerializerSettings(); httpConfig.EnsureInitialized(); // Use the HTTP configuration initialized above app.UseWebApi(httpConfig); }
/// <summary> /// Initializes a new instance of the <see cref="Bot" /> class. /// </summary> /// <param name="options">The bot options.</param> /// <param name="graphLogger">The graph logger.</param> public Bot(BotOptions options, IGraphLogger graphLogger) { this.botBaseUri = options.BotBaseUrl; this.GraphLogger = graphLogger; var name = this.GetType().Assembly.GetName().Name; this.AuthenticationProvider = new AuthenticationProvider(name, options.AppId, options.AppSecret, graphLogger); this.Serializer = new CommsSerializer(); var authenticationWrapper = new AuthenticationWrapper(this.AuthenticationProvider); this.NotificationProcessor = new NotificationProcessor(authenticationWrapper, this.Serializer); this.NotificationProcessor.OnNotificationReceived += this.NotificationProcessor_OnNotificationReceived; this.RequestBuilder = new GraphServiceClient(options.PlaceCallEndpointUrl.AbsoluteUri, authenticationWrapper); // Add the default headers used by the graph client. // This will include SdkVersion. var defaultProperties = new List <IGraphProperty <IEnumerable <string> > >(); using (HttpClient tempClient = GraphClientFactory.Create(authenticationWrapper)) { defaultProperties.AddRange(tempClient.DefaultRequestHeaders.Select(header => GraphProperty.RequestProperty(header.Key, header.Value))); } // graph client var productInfo = new ProductInfoHeaderValue( typeof(Bot).Assembly.GetName().Name, typeof(Bot).Assembly.GetName().Version.ToString()); this.GraphApiClient = new GraphAuthClient( this.GraphLogger, this.Serializer.JsonSerializerSettings, new HttpClient(), this.AuthenticationProvider, productInfo, defaultProperties); // setup media prompt this.MediaMap[BotIncomingPromptName] = new MediaPrompt { MediaInfo = new MediaInfo { Uri = new Uri(options.BotBaseUrl, "audio/speech.wav").ToString(), ResourceId = Guid.NewGuid().ToString(), }, }; }
/// <summary> /// Initializes a new instance of the <see cref="CallHandler"/> class. /// </summary> /// <param name="statefulCall">Stateful call instance.</param> /// <param name="logger">Logger instance.</param> public CallHandler(ICall statefulCall, IGraphLogger logger) { this.logger = logger; this.Call = statefulCall; this.Call.OnUpdated += this.OnCallUpdated; if (this.Call.GetLocalMediaSession() != null) { this.Call.GetLocalMediaSession().AudioSocket.DominantSpeakerChanged += this.OnDominantSpeakerChanged; this.Call.GetLocalMediaSession().VideoSocket.VideoMediaReceived += this.OnVideoMediaReceived; } this.Call.Participants.OnUpdated += this.OnParticipantsUpdated; var outcome = Serializer.SerializeObject(statefulCall.Resource); this.OutcomesLogMostRecentFirst.AddFirst("Call Created:\n" + outcome); }
/// <summary> /// Initializes a new instance of the <see cref="CallHandler"/> class. /// </summary> /// <param name="statefulCall">Stateful call instance.</param> public CallHandler(ICall statefulCall) { this.logger = statefulCall.GraphLogger; this.Call = statefulCall; this.Call.OnUpdated += this.OnCallUpdated; if (this.Call.GetLocalMediaSession() != null) { this.Call.GetLocalMediaSession().AudioSocket.DominantSpeakerChanged += this.OnDominantSpeakerChanged; this.Call.GetLocalMediaSession().VideoSocket.VideoMediaReceived += this.OnVideoMediaReceived; } this.Call.Participants.OnUpdated += this.OnParticipantsUpdated; this.endCallTimer = new Timer(CallHandler.WaitForMs); this.endCallTimer.Enabled = false; this.endCallTimer.AutoReset = false; this.endCallTimer.Elapsed += this.OnTimerElapsed; }