Example #1
0
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            // Tubumu.Meeting.Server 不是 AbpModule,故手工注册。
            context.Services.AddTransient <MeetingHub>();

            var configuration = BuildConfiguration();

            // Mediasoup
            var mediasoupStartupSettings = configuration.GetSection("MediasoupStartupSettings").Get <MediasoupStartupSettings>();
            var mediasoupSettings        = configuration.GetSection("MediasoupSettings").Get <MediasoupSettings>();
            var workerSettings           = mediasoupSettings.WorkerSettings;
            var routerSettings           = mediasoupSettings.RouterSettings;
            var webRtcTransportSettings  = mediasoupSettings.WebRtcTransportSettings;
            var plainTransportSettings   = mediasoupSettings.PlainTransportSettings;

            context.Services.AddMediasoup(options =>
            {
                // MediasoupStartupSettings
                if (mediasoupStartupSettings != null)
                {
                    options.MediasoupStartupSettings.MediasoupVersion = mediasoupStartupSettings.MediasoupVersion;
                    options.MediasoupStartupSettings.WorkerPath       = mediasoupStartupSettings.WorkerPath;
                    options.MediasoupStartupSettings.NumberOfWorkers  = !mediasoupStartupSettings.NumberOfWorkers.HasValue || mediasoupStartupSettings.NumberOfWorkers <= 0 ? Environment.ProcessorCount : mediasoupStartupSettings.NumberOfWorkers;
                }

                // WorkerSettings
                if (workerSettings != null)
                {
                    options.MediasoupSettings.WorkerSettings.LogLevel            = workerSettings.LogLevel;
                    options.MediasoupSettings.WorkerSettings.LogTags             = workerSettings.LogTags;
                    options.MediasoupSettings.WorkerSettings.RtcMinPort          = workerSettings.RtcMinPort;
                    options.MediasoupSettings.WorkerSettings.RtcMaxPort          = workerSettings.RtcMaxPort;
                    options.MediasoupSettings.WorkerSettings.DtlsCertificateFile = workerSettings.DtlsCertificateFile;
                    options.MediasoupSettings.WorkerSettings.DtlsPrivateKeyFile  = workerSettings.DtlsPrivateKeyFile;
                }

                // RouteSettings
                if (routerSettings != null && !routerSettings.RtpCodecCapabilities.IsNullOrEmpty())
                {
                    options.MediasoupSettings.RouterSettings = routerSettings;

                    // Fix RtpCodecCapabilities[x].Parameters 。从配置文件反序列化时将数字转换成了字符串,这里进行修正。
                    foreach (var codec in routerSettings.RtpCodecCapabilities.Where(m => m.Parameters != null))
                    {
                        foreach (var key in codec.Parameters.Keys.ToArray())
                        {
                            var value = codec.Parameters[key];
                            if (value != null && Int32.TryParse(value.ToString(), out var intValue))
                            {
                                codec.Parameters[key] = intValue;
                            }
                        }
                    }
                }

                // WebRtcTransportSettings
                if (webRtcTransportSettings != null)
                {
                    options.MediasoupSettings.WebRtcTransportSettings.ListenIps = webRtcTransportSettings.ListenIps;
                    options.MediasoupSettings.WebRtcTransportSettings.InitialAvailableOutgoingBitrate = webRtcTransportSettings.InitialAvailableOutgoingBitrate;
                    options.MediasoupSettings.WebRtcTransportSettings.MinimumAvailableOutgoingBitrate = webRtcTransportSettings.MinimumAvailableOutgoingBitrate;
                    options.MediasoupSettings.WebRtcTransportSettings.MaxSctpMessageSize = webRtcTransportSettings.MaxSctpMessageSize;

                    // 如果没有设置 ListenIps 则获取本机所有的 IPv4 地址进行设置。
                    var listenIps = options.MediasoupSettings.WebRtcTransportSettings.ListenIps;
                    if (listenIps.IsNullOrEmpty())
                    {
                        var localIPv4IPAddresses = IPAddressExtensions.GetLocalIPAddresses(AddressFamily.InterNetwork).Where(m => m != IPAddress.Loopback);
                        if (localIPv4IPAddresses.IsNullOrEmpty())
                        {
                            throw new ArgumentException("无法获取本机 IPv4 配置 WebRtcTransport。");
                        }

                        listenIps = (from ip in localIPv4IPAddresses
                                     let ipString = ip.ToString()
                                                    select new TransportListenIp
                        {
                            Ip = ipString,
                            AnnouncedIp = ipString
                        }).ToArray();
                        options.MediasoupSettings.WebRtcTransportSettings.ListenIps = listenIps;
                    }
                    else
                    {
                        var localIPv4IPAddress = IPAddressExtensions.GetLocalIPv4IPAddress();
                        if (localIPv4IPAddress == null)
                        {
                            throw new ArgumentException("无法获取本机 IPv4 配置 WebRtcTransport。");
                        }

                        foreach (var listenIp in listenIps)
                        {
                            if (string.IsNullOrWhiteSpace(listenIp.AnnouncedIp))
                            {
                                // 如果没有设置 AnnouncedIp:
                                // 如果 Ip 属性的值不是 Any 则赋值为 Ip 属性的值,否则取本机的任意一个 IPv4 地址进行设置。(注意:可能获取的并不是正确的 IP)
                                listenIp.AnnouncedIp = listenIp.Ip == IPAddress.Any.ToString() ? localIPv4IPAddress.ToString() : listenIp.Ip;
                            }
                        }
                    }
                }

                // PlainTransportSettings
                if (plainTransportSettings != null)
                {
                    options.MediasoupSettings.PlainTransportSettings.ListenIp           = plainTransportSettings.ListenIp;
                    options.MediasoupSettings.PlainTransportSettings.MaxSctpMessageSize = plainTransportSettings.MaxSctpMessageSize;

                    var localIPv4IPAddress = IPAddressExtensions.GetLocalIPv4IPAddress();
                    if (localIPv4IPAddress == null)
                    {
                        throw new ArgumentException("无法获取本机 IPv4 配置 PlainTransport。");
                    }

                    var listenIp = options.MediasoupSettings.PlainTransportSettings.ListenIp;
                    if (listenIp == null)
                    {
                        listenIp = new TransportListenIp
                        {
                            Ip          = localIPv4IPAddress.ToString(),
                            AnnouncedIp = localIPv4IPAddress.ToString(),
                        };
                        options.MediasoupSettings.PlainTransportSettings.ListenIp = listenIp;
                    }
                    else if (string.IsNullOrWhiteSpace(listenIp.AnnouncedIp))
                    {
                        // 如果没有设置 AnnouncedIp:
                        // 如果 Ip 属性的值不是 Any 则赋值为 Ip 属性的值,否则取本机的任意一个 IPv4 地址进行设置。(注意:可能获取的并不是正确的 IP)
                        listenIp.AnnouncedIp = listenIp.Ip == IPAddress.Any.ToString() ? localIPv4IPAddress.ToString() : listenIp.Ip;
                    }
                }
            });

            // Meeting server
            context.Services.AddMeetingServer(options =>
            {
                options.ServeMode = ServeMode.Open;
            });

            // SignalR
            context.Services.AddSignalR(options =>
            {
                options.EnableDetailedErrors = true;
            })
            .AddJsonProtocol(options =>
            {
                options.PayloadSerializerOptions.Converters.Add(new JsonStringEnumMemberConverter());
                options.PayloadSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
            });
        }
Example #2
0
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc()
            .AddJsonOptions(options =>
            {
                options.JsonSerializerOptions.Converters.Add(new JsonStringEnumMemberConverter());
                options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
            });

            // Cache
            services.AddDistributedRedisCache(options =>
            {
                options.Configuration = "localhost";
                options.InstanceName  = "Meeting:";
            });
            services.AddMemoryCache();

            // Cors
            var corsSettings = Configuration.GetSection("CorsSettings").Get <CorsSettings>();

            services.AddCors(options => options.AddPolicy("DefaultPolicy",
                                                          builder => builder.WithOrigins(corsSettings.Origins).AllowAnyMethod().AllowAnyHeader().AllowCredentials())
                             );

            // Authentication
            services.AddSingleton <ITokenService, TokenService>();
            var tokenValidationSettings = Configuration.GetSection("TokenValidationSettings").Get <TokenValidationSettings>();

            services.AddSingleton(tokenValidationSettings);
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidIssuer    = tokenValidationSettings.ValidIssuer,
                    ValidateIssuer = true,

                    ValidAudience    = tokenValidationSettings.ValidAudience,
                    ValidateAudience = true,

                    IssuerSigningKey         = SignatureHelper.GenerateSigningKey(tokenValidationSettings.IssuerSigningKey),
                    ValidateIssuerSigningKey = true,

                    ValidateLifetime = tokenValidationSettings.ValidateLifetime,
                    ClockSkew        = TimeSpan.FromSeconds(tokenValidationSettings.ClockSkewSeconds),
                };

                // We have to hook the OnMessageReceived event in order to
                // allow the JWT authentication handler to read the access
                // token from the query string when a WebSocket or
                // Server-Sent Events request comes in.
                options.Events = new JwtBearerEvents
                {
                    OnMessageReceived = context =>
                    {
                        var accessToken = context.Request.Query["access_token"];

                        // If the request is for our hub...
                        var path = context.HttpContext.Request.Path;
                        if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
                        {
                            // Read the token out of the query string
                            context.Token = accessToken;
                        }
                        return(Task.CompletedTask);
                    },
                    OnAuthenticationFailed = context =>
                    {
                        //_logger.LogError($"Authentication Failed(OnAuthenticationFailed): {context.Request.Path} Error: {context.Exception}");
                        if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                        {
                            context.Response.Headers.Add("Token-Expired", "true");
                        }
                        return(Task.CompletedTask);
                    },
                    OnChallenge = async context =>
                    {
                        //_logger.LogError($"Authentication Challenge(OnChallenge): {context.Request.Path}");
                        var body = Encoding.UTF8.GetBytes("{\"code\": 400, \"message\": \"Authentication Challenge\"}");
                        context.Response.StatusCode  = StatusCodes.Status401Unauthorized;
                        context.Response.ContentType = "application/json";
                        await context.Response.Body.WriteAsync(body, 0, body.Length);
                        context.HandleResponse();
                    }
                };
            });

            // SignalR
            services.AddSignalR(options =>
            {
                options.EnableDetailedErrors = true;
            })
            .AddJsonProtocol(options => {
                options.PayloadSerializerOptions.Converters.Add(new JsonStringEnumMemberConverter());
                options.PayloadSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
            });
            services.Replace(ServiceDescriptor.Singleton(typeof(IUserIdProvider), typeof(NameUserIdProvider)));

            // Consul
            var consulSettings = Configuration.GetSection("ConsulSettings").Get <ConsulSettings>();

            services.AddSingleton(consulSettings);

            // Mediasoup
            var mediasoupStartupSettings = Configuration.GetSection("MediasoupStartupSettings").Get <MediasoupStartupSettings>();
            var mediasoupSettings        = Configuration.GetSection("MediasoupSettings").Get <MediasoupSettings>();
            var workerSettings           = mediasoupSettings.WorkerSettings;
            var routerSettings           = mediasoupSettings.RouterSettings;
            var webRtcTransportSettings  = mediasoupSettings.WebRtcTransportSettings;
            var plainTransportSettings   = mediasoupSettings.PlainTransportSettings;
            var meetingServerSettings    = Configuration.GetSection("MeetingServerSettings").Get <MeetingServerSettings>();

            services.AddMediasoup(options =>
            {
                // MediasoupStartupSettings
                if (mediasoupStartupSettings != null)
                {
                    options.MediasoupStartupSettings.MediasoupVersion = mediasoupStartupSettings.MediasoupVersion;
                    options.MediasoupStartupSettings.WorkerPath       = mediasoupStartupSettings.WorkerPath;
                    options.MediasoupStartupSettings.NumberOfWorkers  = !mediasoupStartupSettings.NumberOfWorkers.HasValue || mediasoupStartupSettings.NumberOfWorkers <= 0 ? Environment.ProcessorCount : mediasoupStartupSettings.NumberOfWorkers;
                }

                // WorkerSettings
                if (workerSettings != null)
                {
                    options.MediasoupSettings.WorkerSettings.LogLevel            = workerSettings.LogLevel;
                    options.MediasoupSettings.WorkerSettings.LogTags             = workerSettings.LogTags;
                    options.MediasoupSettings.WorkerSettings.RtcMinPort          = workerSettings.RtcMinPort;
                    options.MediasoupSettings.WorkerSettings.RtcMaxPort          = workerSettings.RtcMaxPort;
                    options.MediasoupSettings.WorkerSettings.DtlsCertificateFile = workerSettings.DtlsCertificateFile;
                    options.MediasoupSettings.WorkerSettings.DtlsPrivateKeyFile  = workerSettings.DtlsPrivateKeyFile;
                }

                // RouteSettings
                if (routerSettings != null && !routerSettings.RtpCodecCapabilities.IsNullOrEmpty())
                {
                    options.MediasoupSettings.RouterSettings = routerSettings;

                    // Fix RtpCodecCapabilities[x].Parameters 。从配置文件反序列化时将数字转换成了字符串,这里进行修正。
                    foreach (var codec in routerSettings.RtpCodecCapabilities.Where(m => m.Parameters != null))
                    {
                        foreach (var key in codec.Parameters.Keys.ToArray())
                        {
                            var value = codec.Parameters[key];
                            if (value != null && Int32.TryParse(value.ToString(), out var intValue))
                            {
                                codec.Parameters[key] = intValue;
                            }
                        }
                    }
                }

                // WebRtcTransportSettings
                if (webRtcTransportSettings != null)
                {
                    options.MediasoupSettings.WebRtcTransportSettings.ListenIps = webRtcTransportSettings.ListenIps;
                    options.MediasoupSettings.WebRtcTransportSettings.InitialAvailableOutgoingBitrate = webRtcTransportSettings.InitialAvailableOutgoingBitrate;
                    options.MediasoupSettings.WebRtcTransportSettings.MinimumAvailableOutgoingBitrate = webRtcTransportSettings.MinimumAvailableOutgoingBitrate;
                    options.MediasoupSettings.WebRtcTransportSettings.MaxSctpMessageSize = webRtcTransportSettings.MaxSctpMessageSize;

                    // 如果没有设置 ListenIps 则获取本机所有的 IPv4 地址进行设置。
                    var listenIps = options.MediasoupSettings.WebRtcTransportSettings.ListenIps;
                    if (listenIps.IsNullOrEmpty())
                    {
                        var localIPv4IPAddresses = IPAddressExtensions.GetLocalIPAddresses(AddressFamily.InterNetwork).Where(m => m != IPAddress.Loopback);
                        if (EnumerableExtensions.IsNullOrEmpty(localIPv4IPAddresses))
                        {
                            throw new ArgumentException("无法获取本机 IPv4 配置 WebRtcTransport。");
                        }

                        listenIps = (from ip in localIPv4IPAddresses
                                     let ipString = ip.ToString()
                                                    select new TransportListenIp
                        {
                            Ip = ipString,
                            AnnouncedIp = ipString
                        }).ToArray();
                        options.MediasoupSettings.WebRtcTransportSettings.ListenIps = listenIps;
                    }
                    else
                    {
                        var localIPv4IPAddress = IPAddressExtensions.GetLocalIPv4IPAddress();
                        if (localIPv4IPAddress == null)
                        {
                            throw new ArgumentException("无法获取本机 IPv4 配置 WebRtcTransport。");
                        }

                        foreach (var listenIp in listenIps)
                        {
                            if (listenIp.AnnouncedIp.IsNullOrWhiteSpace())
                            {
                                // 如果没有设置 AnnouncedIp:
                                // 如果 Ip 属性的值不是 Any 则赋值为 Ip 属性的值,否则取本机的任意一个 IPv4 地址进行设置。(注意:可能获取的并不是正确的 IP)
                                listenIp.AnnouncedIp = listenIp.Ip == IPAddress.Any.ToString() ? localIPv4IPAddress.ToString() : listenIp.Ip;
                            }
                        }
                    }
                }

                // PlainTransportSettings
                if (plainTransportSettings != null)
                {
                    options.MediasoupSettings.PlainTransportSettings.ListenIp           = plainTransportSettings.ListenIp;
                    options.MediasoupSettings.PlainTransportSettings.MaxSctpMessageSize = plainTransportSettings.MaxSctpMessageSize;

                    var localIPv4IPAddress = IPAddressExtensions.GetLocalIPv4IPAddress();
                    if (localIPv4IPAddress == null)
                    {
                        throw new ArgumentException("无法获取本机 IPv4 配置 PlainTransport。");
                    }

                    var listenIp = options.MediasoupSettings.PlainTransportSettings.ListenIp;
                    if (listenIp == null)
                    {
                        listenIp = new TransportListenIp
                        {
                            Ip          = localIPv4IPAddress.ToString(),
                            AnnouncedIp = localIPv4IPAddress.ToString(),
                        };
                        options.MediasoupSettings.PlainTransportSettings.ListenIp = listenIp;
                    }
                    else if (listenIp.AnnouncedIp.IsNullOrWhiteSpace())
                    {
                        // 如果没有设置 AnnouncedIp:
                        // 如果 Ip 属性的值不是 Any 则赋值为 Ip 属性的值,否则取本机的任意一个 IPv4 地址进行设置。(注意:可能获取的并不是正确的 IP)
                        listenIp.AnnouncedIp = listenIp.Ip == IPAddress.Any.ToString() ? localIPv4IPAddress.ToString() : listenIp.Ip;
                    }
                }
            });

            // Meeting server
            services.AddMeetingServer(options =>
            {
                options.ServeMode = meetingServerSettings.ServeMode;
            });

            // Swagger
            services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc("v1", new OpenApiInfo {
                    Title   = "Meeting API",
                    Version = "v1"
                });
            });
        }