Esempio n. 1
0
        /// <summary>
        /// Create a Router.
        /// </summary>
        public async Task <Router?> CreateRouterAsync(RouterOptions routerOptions)
        {
            if (_closed)
            {
                return(null);
            }

            await _closeLock.WaitAsync();

            try
            {
                if (_closed)
                {
                    return(null);
                }

                _logger.LogDebug("CreateRouterAsync()");

                // This may throw.
                var rtpCapabilities = ORTC.GenerateRouterRtpCapabilities(routerOptions.MediaCodecs);

                var @internal = new { RouterId = Guid.NewGuid().ToString() };

                await _channel.RequestAsync(MethodId.WORKER_CREATE_ROUTER, @internal);

                var router = new Router(_loggerFactory, @internal.RouterId, rtpCapabilities, _channel, _payloadChannel, AppData);

                lock (_routersLock)
                {
                    _routers.Add(router);
                }

                router.On("@close", _ =>
                {
                    lock (_routersLock)
                    {
                        _routers.Remove(router);
                    }
                    return(Task.CompletedTask);
                });

                // Emit observer event.
                Observer.Emit("newrouter", router);

                return(router);
            }
            finally
            {
                _closeLock.Set();
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Check whether the given RTP capabilities can consume the given Producer.
        /// </summary>
        public bool CanConsume(string producerId, RtpCapabilities rtpCapabilities)
        {
            if (!_producers.TryGetValue(producerId, out Producer producer))
            {
                _logger.LogError($"CanConsume() | Producer with id {producerId} not found");

                return(false);
            }

            try
            {
                return(ORTC.CanConsume(producer.ConsumableRtpParameters, rtpCapabilities));
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "CanConsume() | unexpected error");

                return(false);
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Create a Router.
        /// </summary>
        public async Task <Router> CreateRouter(RouterOptions routerOptions)
        {
            _logger.LogDebug("CreateRouter()");

            // This may throw.
            var rtpCapabilities = ORTC.GenerateRouterRtpCapabilities(routerOptions.MediaCodecs);

            var @internal = new { RouterId = Guid.NewGuid().ToString() };

            await _channel.RequestAsync(MethodId.WORKER_CREATE_ROUTER, @internal);

            var router = new Router(_loggerFactory, @internal.RouterId, rtpCapabilities, _channel, AppData);

            _routers.Add(router);

            router.On("@close", _ => _routers.Remove(router));

            // Emit observer event.
            Observer.Emit("newrouter", router);

            return(router);
        }
Esempio n. 4
0
        /// <summary>
        /// Create a Consumer.
        /// </summary>
        /// <param name="consumerOptions"></param>
        /// <returns></returns>
        public override async Task <Consumer> ConsumeAsync(ConsumerOptions consumerOptions)
        {
            _logger.LogDebug("ConsumeAsync()");

            if (consumerOptions.ProducerId.IsNullOrWhiteSpace())
            {
                throw new Exception("missing producerId");
            }

            var producer = GetProducerById(consumerOptions.ProducerId);

            if (producer == null)
            {
                throw new Exception($"Producer with id {consumerOptions.ProducerId} not found");
            }

            // This may throw.
            var rtpParameters = ORTC.GetPipeConsumerRtpParameters(producer.ConsumableRtpParameters, Rtx);

            var @internal = new ConsumerInternalData
                            (
                Internal.RouterId,
                Internal.TransportId,
                consumerOptions.ProducerId,
                Guid.NewGuid().ToString()
                            );

            var reqData = new
            {
                producer.Kind,
                RtpParameters          = rtpParameters,
                Type                   = ConsumerType.Pipe,
                ConsumableRtpEncodings = producer.ConsumableRtpParameters.Encodings,
            };

            var status = await Channel.RequestAsync(MethodId.TRANSPORT_CONSUME, @internal, reqData);

            var responseData = JsonConvert.DeserializeObject <TransportConsumeResponseData>(status !);

            var data = new
            {
                producer.Kind,
                RtpParameters = rtpParameters,
                Type          = ConsumerType.Pipe,
            };

            // 在 Node.js 实现中, 创建 Consumer 对象时没提供 score 和 preferredLayers 参数,且 score = { score: 10, producerScore: 10 }。
            var consumer = new Consumer(_loggerFactory,
                                        @internal,
                                        data.Kind,
                                        data.RtpParameters,
                                        data.Type,
                                        Channel,
                                        AppData,
                                        responseData.Paused,
                                        responseData.ProducerPaused,
                                        responseData.Score,
                                        responseData.PreferredLayers);

            Consumers[consumer.Internal.ConsumerId] = consumer;

            consumer.On("@close", _ => Consumers.Remove(consumer.Internal.ConsumerId));
            consumer.On("@producerclose", _ => Consumers.Remove(consumer.Internal.ConsumerId));

            // Emit observer event.
            Observer.Emit("newconsumer", consumer);

            return(consumer);
        }
Esempio n. 5
0
        /// <summary>
        /// Create a DataProducer.
        /// </summary>
        /// <returns></returns>
        public async Task <DataProducer> ProduceDataAsync(DataProducerOptions dataProducerOptions)
        {
            _logger.LogDebug("ProduceDataAsync()");

            if (!dataProducerOptions.Id.IsNullOrWhiteSpace() && DataProducers.ContainsKey(dataProducerOptions.Id !))
            {
                throw new Exception($"a DataProducer with same id {dataProducerOptions.Id} already exists");
            }

            if (dataProducerOptions.Label.IsNullOrWhiteSpace())
            {
                dataProducerOptions.Label = string.Empty;
            }
            if (dataProducerOptions.Protocol.IsNullOrWhiteSpace())
            {
                dataProducerOptions.Protocol = string.Empty;
            }

            // This may throw.
            ORTC.ValidateSctpStreamParameters(dataProducerOptions.SctpStreamParameters);

            var @internal = new DataProducerInternalData
                            (
                Internal.RouterId,
                Internal.TransportId,
                !dataProducerOptions.Id.IsNullOrWhiteSpace() ? dataProducerOptions.Id : Guid.NewGuid().ToString()
                            );

            var reqData = new
            {
                dataProducerOptions.SctpStreamParameters,
                Label    = dataProducerOptions.Label !,
                Protocol = dataProducerOptions.Protocol !
            };

            var status = await Channel.RequestAsync(MethodId.TRANSPORT_PRODUCE_DATA, @internal, reqData);

            var responseData = JsonConvert.DeserializeObject <TransportDataProduceResponseData>(status);
            var dataProducer = new DataProducer(_loggerFactory,
                                                @internal,
                                                responseData.SctpStreamParameters,
                                                responseData.Label,
                                                responseData.Protocol,
                                                Channel,
                                                AppData);

            DataProducers[dataProducer.Internal.DataProducerId] = dataProducer;

            dataProducer.On("@close", _ =>
            {
                DataProducers.Remove(dataProducer.Internal.DataProducerId);
                Emit("@dataproducerclose", dataProducer);
            });

            Emit("@newdataproducer", dataProducer);

            // Emit observer event.
            Observer.Emit("newdataproducer", dataProducer);

            return(dataProducer);
        }
Esempio n. 6
0
        /// <summary>
        /// Create a Consumer.
        /// </summary>
        /// <param name="consumerOptions"></param>
        /// <returns></returns>
        public virtual async Task <Consumer> ConsumeAsync(ConsumerOptions consumerOptions)
        {
            _logger.LogDebug("ConsumeAsync()");

            if (consumerOptions.ProducerId.IsNullOrWhiteSpace())
            {
                throw new ArgumentException("missing producerId");
            }

            if (consumerOptions.RtpCapabilities == null)
            {
                throw new ArgumentException(nameof(consumerOptions.RtpCapabilities));
            }

            if (!consumerOptions.Paused.HasValue)
            {
                consumerOptions.Paused = false;
            }

            // This may throw.
            ORTC.ValidateRtpCapabilities(consumerOptions.RtpCapabilities);

            var producer = GetProducerById(consumerOptions.ProducerId);

            if (producer == null)
            {
                throw new NullReferenceException($"Producer with id {consumerOptions.ProducerId} not found");
            }

            // This may throw.
            var rtpParameters = ORTC.GetConsumerRtpParameters(producer.ConsumableRtpParameters, consumerOptions.RtpCapabilities);

            // Set MID.
            rtpParameters.Mid = $"{_nextMidForConsumers++}";

            // We use up to 8 bytes for MID (string).
            if (_nextMidForConsumers == 100000000)
            {
                _logger.LogDebug($"ConsumeAsync() | reaching max MID value {_nextMidForConsumers}");

                _nextMidForConsumers = 0;
            }

            var @internal = new ConsumerInternalData
                            (
                Internal.RouterId,
                Internal.TransportId,
                consumerOptions.ProducerId,
                Guid.NewGuid().ToString()
                            );

            var reqData = new
            {
                producer.Kind,
                RtpParameters = rtpParameters,
                producer.Type,
                ConsumableRtpEncodings = producer.ConsumableRtpParameters.Encodings,
                consumerOptions.Paused,
                consumerOptions.PreferredLayers
            };

            var status = await Channel.RequestAsync(MethodId.TRANSPORT_CONSUME, @internal, reqData);

            var responseData = JsonConvert.DeserializeObject <TransportConsumeResponseData>(status);

            var data = new
            {
                producer.Kind,
                RtpParameters = rtpParameters,
                Type          = (ConsumerType)producer.Type, // 注意:类型转换
            };

            var consumer = new Consumer(_loggerFactory,
                                        @internal,
                                        data.Kind,
                                        data.RtpParameters,
                                        data.Type,
                                        Channel,
                                        AppData,
                                        responseData.Paused,
                                        responseData.ProducerPaused,
                                        responseData.Score,
                                        responseData.PreferredLayers);

            Consumers[consumer.Internal.ConsumerId] = consumer;

            consumer.On("@close", _ => Consumers.Remove(consumer.Internal.ConsumerId));
            consumer.On("@producerclose", _ => Consumers.Remove(consumer.Internal.ConsumerId));

            // Emit observer event.
            Observer.Emit("newconsumer", consumer);

            return(consumer);
        }
Esempio n. 7
0
        /// <summary>
        /// Create a Producer.
        /// </summary>
        public async Task <Producer> ProduceAsync(ProducerOptions producerOptions)
        {
            _logger.LogDebug("ProduceAsync()");

            if (!producerOptions.Id.IsNullOrWhiteSpace() && Producers.ContainsKey(producerOptions.Id !))
            {
                throw new Exception($"a Producer with same id \"{ producerOptions.Id }\" already exists");
            }

            // This may throw.
            ORTC.ValidateRtpParameters(producerOptions.RtpParameters);

            // If missing or empty encodings, add one.
            // TODO: (alby)注意检查这样做是否合适
            // 在 mediasoup-worker 中,要求 Encodings 至少要有一个元素。
            if (producerOptions.RtpParameters.Encodings.IsNullOrEmpty())
            {
                producerOptions.RtpParameters.Encodings = new List <RtpEncodingParameters>
                {
                    new RtpEncodingParameters()
                };
            }

            // Don't do this in PipeTransports since there we must keep CNAME value in
            // each Producer.
            // TODO: (alby)不好的模式
            if (GetType() != typeof(PipeTransport))
            {
                // If CNAME is given and we don't have yet a CNAME for Producers in this
                // Transport, take it.
                if (_cnameForProducers.IsNullOrWhiteSpace() && producerOptions.RtpParameters.Rtcp != null && !producerOptions.RtpParameters.Rtcp.CNAME.IsNullOrWhiteSpace())
                {
                    _cnameForProducers = producerOptions.RtpParameters.Rtcp.CNAME;
                }
                // Otherwise if we don't have yet a CNAME for Producers and the RTP parameters
                // do not include CNAME, create a random one.
                else if (_cnameForProducers.IsNullOrWhiteSpace())
                {
                    _cnameForProducers = Guid.NewGuid().ToString().Substring(0, 8);
                }

                // Override Producer's CNAME.
                // TODO: (alby)注意检查这样做是否合适
                producerOptions.RtpParameters.Rtcp       = producerOptions.RtpParameters.Rtcp ?? new RtcpParameters();
                producerOptions.RtpParameters.Rtcp.CNAME = _cnameForProducers;
            }

            var routerRtpCapabilities = GetRouterRtpCapabilities();

            // This may throw.
            var rtpMapping = ORTC.GetProducerRtpParametersMapping(producerOptions.RtpParameters, routerRtpCapabilities);

            // This may throw.
            var consumableRtpParameters = ORTC.GetConsumableRtpParameters(producerOptions.Kind, producerOptions.RtpParameters, routerRtpCapabilities, rtpMapping);

            var @internal = new ProducerInternalData
                            (
                Internal.RouterId,
                Internal.TransportId,
                producerOptions.Id.IsNullOrWhiteSpace() ? Guid.NewGuid().ToString() : producerOptions.Id !
                            );
            var reqData = new
            {
                producerOptions.Kind,
                producerOptions.RtpParameters,
                RtpMapping = rtpMapping,
                producerOptions.KeyFrameRequestDelay,
                producerOptions.Paused,
            };

            var status = await Channel.RequestAsync(MethodId.TRANSPORT_PRODUCE, @internal, reqData);

            var responseData = JsonConvert.DeserializeObject <TransportProduceResponseData>(status);
            var data         = new
            {
                producerOptions.Kind,
                producerOptions.RtpParameters,
                responseData.Type,
                ConsumableRtpParameters = consumableRtpParameters
            };

            var producer = new Producer(_loggerFactory,
                                        @internal,
                                        data.Kind,
                                        data.RtpParameters,
                                        data.Type,
                                        data.ConsumableRtpParameters,
                                        Channel,
                                        AppData,
                                        producerOptions.Paused !.Value);

            Producers[producer.Internal.ProducerId] = producer;

            producer.On("@close", _ =>
            {
                Producers.Remove(producer.Internal.ProducerId);
                Emit("@producerclose", producer);
            });

            Emit("@newproducer", producer);

            // Emit observer event.
            Observer.Emit("newproducer", producer);

            return(producer);
        }