/// <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="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> /// 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> /// Joins the call asynchronously. /// </summary> /// <param name="joinCallBody">The join call body.</param> /// <returns>The <see cref="ICall"/> that was requested to join.</returns> public async Task <ICall> JoinCallAsync(JoinCallController.JoinCallBody joinCallBody) { // A tracking id for logging purposes. Helps identify this call in logs. var correlationId = Guid.NewGuid(); MeetingInfo meetingInfo; ChatInfo chatInfo; if (!string.IsNullOrWhiteSpace(joinCallBody.MeetingId)) { // Meeting id is a cloud-video-interop numeric meeting id. var onlineMeeting = await this.OnlineMeetings .GetOnlineMeetingAsync(joinCallBody.TenantId, joinCallBody.MeetingId, correlationId) .ConfigureAwait(false); meetingInfo = new OrganizerMeetingInfo { Organizer = onlineMeeting.Participants.Organizer.Identity, }; chatInfo = onlineMeeting.ChatInfo; //// meetingInfo.AllowConversationWithoutHost = joinCallBody.AllowConversationWithoutHost; } else { (chatInfo, meetingInfo) = JoinInfo.ParseJoinURL(joinCallBody.JoinURL); } var tenantId = joinCallBody.TenantId ?? (meetingInfo as OrganizerMeetingInfo)?.Organizer.GetPrimaryIdentity()?.GetTenantId(); ILocalMediaSession mediaSession = this.CreateLocalMediaSession(); var joinParams = new JoinMeetingParameters(chatInfo, meetingInfo, mediaSession) { RemoveFromDefaultAudioRoutingGroup = joinCallBody.RemoveFromDefaultRoutingGroup, TenantId = tenantId, CorrelationId = correlationId, }; if (!string.IsNullOrWhiteSpace(joinCallBody.DisplayName)) { // Teams client does not allow changing of ones own display name. // If display name is specified, we join as anonymous (guest) user // with the specified display name. This will put bot into lobby // unless lobby bypass is disabled. joinParams.GuestIdentity = new Identity { Id = Guid.NewGuid().ToString(), DisplayName = joinCallBody.DisplayName, }; } var statefulCall = await this.Client.Calls().AddAsync(joinParams).ConfigureAwait(false); this.logger.Info($"Call creation complete: {statefulCall.Id}"); return(statefulCall); }