/// <summary> /// <para>Events:</para> /// <para>@emits transportclose</para> /// <para>@emits producerclose</para> /// <para>@emits producerpause</para> /// <para>@emits producerresume</para> /// <para>@emits score - (score: ConsumerScore)</para> /// <para>@emits layerschange - (layers: ConsumerLayers | undefined)</para> /// <para>@emits trace - (trace: ConsumerTraceEventData)</para> /// <para>@emits @close</para> /// <para>@emits @producerclose</para> /// <para>Observer events:</para> /// <para>@emits close</para> /// <para>@emits pause</para> /// <para>@emits resume</para> /// <para>@emits score - (score: ConsumerScore)</para> /// <para>@emits layerschange - (layers: ConsumerLayers | undefined)</para> /// <para>@emits rtp - (packet: Buffer)</para> /// <para>@emits trace - (trace: ConsumerTraceEventData)</para> /// </summary> /// <param name="loggerFactory"></param> /// <param name="consumerInternalData"></param> /// <param name="kind"></param> /// <param name="rtpParameters"></param> /// <param name="type"></param> /// <param name="channel"></param> /// <param name="appData"></param> /// <param name="paused"></param> /// <param name="producerPaused"></param> /// <param name="score"></param> /// <param name="preferredLayers"></param> public Consumer(ILoggerFactory loggerFactory, ConsumerInternalData consumerInternalData, MediaKind kind, RtpParameters rtpParameters, ConsumerType type, Channel channel, PayloadChannel payloadChannel, Dictionary <string, object>?appData, bool paused, bool producerPaused, ConsumerScore?score, ConsumerLayers?preferredLayers ) { _logger = loggerFactory.CreateLogger <Consumer>(); // Internal _internal = consumerInternalData; // Data Kind = kind; RtpParameters = rtpParameters; Type = type; _channel = channel; _payloadChannel = payloadChannel; AppData = appData; _paused = paused; ProducerPaused = producerPaused; Score = score; PreferredLayers = preferredLayers; HandleWorkerNotifications(); }
/// <summary> /// Create a Consumer. /// </summary> /// <param name="consumerOptions">注意:由于强类型的原因,这里使用的是 ConsumerOptions 类而不是 PipConsumerOptions 类</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 resData = await Channel.RequestAsync(MethodId.TRANSPORT_CONSUME, @internal, reqData); var responseData = JsonSerializer.Deserialize <TransportConsumeResponseData>(resData !, ObjectExtensions.DefaultJsonSerializerOptions) !; 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, PayloadChannel, AppData, responseData.Paused, responseData.ProducerPaused, responseData.Score, responseData.PreferredLayers); consumer.On("@close", async(_, _) => { await ConsumersLock.WaitAsync(); try { Consumers.Remove(consumer.ConsumerId); } catch (Exception ex) { _logger.LogError(ex, "@close"); } finally { ConsumersLock.Set(); } }); consumer.On("@producerclose", async(_, _) => { await ConsumersLock.WaitAsync(); try { Consumers.Remove(consumer.ConsumerId); } catch (Exception ex) { _logger.LogError(ex, "@producerclose"); } finally { ConsumersLock.Set(); } }); await ConsumersLock.WaitAsync(); try { Consumers[consumer.ConsumerId] = consumer; } catch (Exception ex) { _logger.LogError(ex, "ConsumeAsync()"); } finally { ConsumersLock.Set(); } // Emit observer event. Observer.Emit("newconsumer", consumer); return(consumer); }
/// <summary> /// Create a Consumer. /// </summary> /// <param name="consumerOptions"></param> /// <returns></returns> public virtual async Task <Consumer> ConsumeAsync(ConsumerOptions consumerOptions) { _logger.LogDebug($"ConsumeAsync() | Transport:{TransportId}"); if (consumerOptions.ProducerId.IsNullOrWhiteSpace()) { throw new ArgumentException("missing producerId"); } if (consumerOptions.RtpCapabilities == null) { throw new ArgumentException(nameof(consumerOptions.RtpCapabilities)); } if (consumerOptions.Mid != null && consumerOptions.Mid.Length == 0) { throw new ArgumentException(nameof(consumerOptions.Mid)); } 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"); } var pipe = consumerOptions.Pipe.HasValue && consumerOptions.Pipe.Value; // This may throw. var rtpParameters = ORTC.GetConsumerRtpParameters(producer.ConsumableRtpParameters, consumerOptions.RtpCapabilities, pipe); if (!pipe) { if (consumerOptions.Mid != null) { rtpParameters.Mid = consumerOptions.Mid; } else { lock (_nextMidForConsumersLock) { // Set MID. rtpParameters.Mid = (_nextMidForConsumers++).ToString(); // We use up to 8 bytes for MID (string). if (_nextMidForConsumers == 100_000_000) { _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, Type = pipe ? ProducerType.Pipe : producer.Type, ConsumableRtpEncodings = producer.ConsumableRtpParameters.Encodings, consumerOptions.Paused, consumerOptions.PreferredLayers }; var resData = await Channel.RequestAsync(MethodId.TRANSPORT_CONSUME, @internal, reqData); var responseData = JsonSerializer.Deserialize <TransportConsumeResponseData>(resData !, ObjectExtensions.DefaultJsonSerializerOptions) !; var data = new { producer.Kind, RtpParameters = rtpParameters, Type = (ConsumerType)(pipe ? ProducerType.Pipe : producer.Type), // 注意:类型转换。ProducerType 的每一种值在 ConsumerType 都有对应且相同的值。 }; var consumer = new Consumer(_loggerFactory, @internal, data.Kind, data.RtpParameters, data.Type, Channel, PayloadChannel, AppData, responseData.Paused, responseData.ProducerPaused, responseData.Score, responseData.PreferredLayers); consumer.On("@close", async(_, _) => { await ConsumersLock.WaitAsync(); try { Consumers.Remove(consumer.ConsumerId); } catch (Exception ex) { _logger.LogError(ex, "@close"); } finally { ConsumersLock.Set(); } }); consumer.On("@producerclose", async(_, _) => { await ConsumersLock.WaitAsync(); try { Consumers.Remove(consumer.ConsumerId); } catch (Exception ex) { _logger.LogError(ex, "@producerclose"); } finally { ConsumersLock.Set(); } }); await ConsumersLock.WaitAsync(); try { Consumers[consumer.ConsumerId] = consumer; } catch (Exception ex) { _logger.LogError(ex, "ConsumeAsync()"); } finally { ConsumersLock.Set(); } // Emit observer event. Observer.Emit("newconsumer", consumer); return(consumer); }