void PlaybackFromJitter(uint timeNow32) { // if (_stream.Stream.Debug) _subtLocalPeer.WriteToLog($">> PlaybackFromJitter count={_jitterBuffer.Count}"); if (_jitterBuffer.Count != 0) { // simulate playback from JB var newestTimestampInSimulatedJitterBuffer = _jitterBuffer.Last.Value.timestamp32; // determine TS value of currently played frame var maxAllowedTimestampInSimulatedJitterBuffer = unchecked(newestTimestampInSimulatedJitterBuffer - SubtLogicConfiguration.JitterBufferLengthTicks); // if (_stream.Stream.Debug) Debugger.Break(); // remove oldest frames which are being played for (;;) { var oldestItem = _jitterBuffer.First; if (oldestItem == null) break; if (MiscProcedures.TimeStamp1IsLess(oldestItem.Value.timestamp32, maxAllowedTimestampInSimulatedJitterBuffer) == false) { // if (_stream.Stream.Debug) _subtLocalPeer.WriteToLog( // $"<< PlaybackFromJitter count={_jitterBuffer.Count} oldestTS={oldestItem.Value.timestamp32}, maxAllowedTimestampInSimulatedJitterBuffer={maxAllowedTimestampInSimulatedJitterBuffer}, newestTimestamp={newestTimestampInSimulatedJitterBuffer}"); break; } OnPlayed(oldestItem.Value, timeNow32); _jitterBuffer.RemoveFirst(); } } }
void SendStatusIfNeeded(uint timestamp32) { if (_lastTimeSentStatus == null || MiscProcedures.TimeStamp1IsLess(_lastTimeSentStatus.Value + SubtLogicConfiguration.RxMeasurementsTransmissionIntervalTicks, timestamp32) ) { _lastTimeSentStatus = timestamp32; var remotePeerId = SubtConnectedPeer.RemotePeerId; if (remotePeerId != null) { bool iwantToIncreaseBandwidthUntilHighPacketLoss = false; if (SubtLocalPeer.LocalPeer.Configuration.RoleAsUser) { // initially this signal comes from user iwantToIncreaseBandwidthUntilHighPacketLoss = SubtLocalPeer.Configuration.BandwidthTargetMbps == null; } else if (SubtLocalPeer.LocalPeer.Configuration.RoleAsSharedPassive) { // and then gets reflected from sharedPassive peers iwantToIncreaseBandwidthUntilHighPacketLoss = LatestRemoteStatus?.IwantToIncreaseBandwidthUntilHighPacketLoss == true; } var data = new SubtRemoteStatusPacket(_rxMeasurement.RecentBandwidth, _rxMeasurement.RecentPacketLoss, this.RecentTxBandwidth, iwantToIncreaseBandwidthUntilHighPacketLoss, SubtLocalPeer.LocalPeer.Configuration.RoleAsSharedPassive) .Encode(this); _stream.SendPacket(data, data.Length); } } }
void RetransmitBandwidthAdjustmentRequestIfNeeded(uint timestamp32) // sender thread { var p = PendingAdjustmentRequestPacketData; // save it to this (sender) thread if (p != null && _lastTimeSentBandwidthAdjustmentRequest != null && MiscProcedures.TimeStamp1IsLess(_lastTimeSentBandwidthAdjustmentRequest.Value + SubtLogicConfiguration.SubtAdjustmentRequestTransmissionIntervalTicks, timestamp32) ) { _lastTimeSentBandwidthAdjustmentRequest = timestamp32; _stream.SendPacket(p, p.Length); } }
void ThreadEntry() { var sw = Stopwatch.StartNew(); var previousTs32 = _localPeer.LocalPeer.Time32; const uint period32 = (uint)TimeSpan.TicksPerMillisecond * 10; int counter = 0; while (!_disposing) { try { _actionsQueue.ExecuteQueued(); var timeNow32 = _localPeer.LocalPeer.Time32; while (MiscProcedures.TimeStamp1IsLess(previousTs32, timeNow32) && !_disposing) { if (_debug) { Debugger.Break(); } previousTs32 = unchecked (previousTs32 + period32); foreach (var stream in _streams.Values) { stream.SendPacketsIfNeeded_10ms(); } counter++; if (counter % 10 == 0) { foreach (var stream in _streams.Values) { stream.SendPayloadPacketsIfNeeded_100ms(); } } if (counter % 100 == 0) { foreach (var stream in _streams.Values) { stream.SendPayloadPacketsIfNeeded_1s(); } } } } catch (Exception exc) { _localPeer.HandleException(exc); } Thread.Sleep(10); } }
bool TryInsertIntoJitterBuffer(JitterBufferElement jbe) { // _subtLocalPeer.WriteToLog($">> TryInsertIntoJitterBuffer strm{_stream.StreamId} count={_jitterBuffer.Count} ts={jbe.timestamp32} seq={jbe.sequence} last (newest) TS={_jitterBuffer.Last?.Value?.timestamp32}"); if (IsNewElementOutOfSequence(jbe)) return false; LinkedListNode<JitterBufferElement> insertAfter = null; for (var item = _jitterBuffer.Last; ;) { if (item == null) break; var enumeratedSequence = item.Value.sequence; if (enumeratedSequence == jbe.sequence) return false; // duplicate packet if (Sequence1IsLess(enumeratedSequence, jbe.sequence)) { insertAfter = item; break; } item = item.Previous; } if (insertAfter == null) { //if (_jitterBuffer.First != null) //{ // if (TimeStamp1IsLess(_jitterBuffer.First.Value.timestamp32, jbe.timestamp32)) // { // // throw new Exception(); // _subtLocalPeer.WriteToLog($"strm{_stream.StreamId} count={_jitterBuffer.Count}. bad item put into JB: ts={jbe.timestamp32} seq={jbe.sequence} firstTS={_jitterBuffer.First.Value.timestamp32} firstSeq={_jitterBuffer.First.Value.sequence} "); // foreach (var item in _jitterBuffer) // _subtLocalPeer.WriteToLog($"strm{_stream.StreamId} item: TS={item.timestamp32} seq={item.sequence} "); // return false; // } //} _jitterBuffer.AddFirst(jbe); } else { if (MiscProcedures.TimeStamp1IsLess(jbe.timestamp32, insertAfter.Value.timestamp32)) { _subtLocalPeer.WriteToLog_lightPain($"JB corruption check: received {jbe}; first: {_jitterBuffer.First?.Value}; last: {_jitterBuffer.Last?.Value}"); return false; //throw new Exception(); } _jitterBuffer.AddAfter(insertAfter, jbe); } // if (_stream.Stream.Debug) // CheckJitterBuffer(); return true; }
void CheckJitterBuffer() { ushort? previousSeq = null; uint? previousTs32 = null; foreach (var jbe in _jitterBuffer) { if (previousSeq.HasValue && Sequence1IsLess(jbe.sequence, previousSeq.Value)) throw new Exception(); if (previousTs32.HasValue && MiscProcedures.TimeStamp1IsLess(jbe.timestamp32, previousTs32.Value)) throw new Exception(); previousSeq = jbe.sequence; previousTs32 = jbe.timestamp32; } }
/// <summary> /// if this procedure is used - "OnTimePassed" must not be called externally /// </summary> public void OnTimeObserved(uint timeNow32) { if (_latestTimeObserved.HasValue) { if (MiscProcedures.TimeStamp1IsLess(timeNow32, _latestTimeObserved.Value)) { // can happen if 2 threads use this class instance in parallel, and each thread calls this procedure. it is normal situation, when "timeNow32" is a 'little bit' less than "_latestTimeObserved" // todo event for developer in case of "high" jumps? return; } OnTimePassed(unchecked (timeNow32 - _latestTimeObserved.Value)); } _latestTimeObserved = timeNow32; }
void SendStatusIfNeeded(uint timestamp32) // sender thread { if (_lastTimeSentStatus == null || MiscProcedures.TimeStamp1IsLess(_lastTimeSentStatus.Value + SubtLogicConfiguration.SubtRemoteStatusPacketTransmissionIntervalTicks, timestamp32) ) { _lastTimeSentStatus = timestamp32; var remotePeerId = SubtConnectedPeer.RemotePeerId; if (remotePeerId != null) { var statusPacket = new SubtRemoteStatusPacket(_rxMeasurement.RecentBandwidth, _rxMeasurement.RecentPacketLoss, this.RecentTxBandwidth, SubtLocalPeer.LocalPeer.Configuration.RoleAsSharedPassive, SubtLocalPeer.ImHealthyAndReadyFor100kbpsU2uSymbiosis ); var data = statusPacket.Encode(this); _stream.SendPacket(data, data.Length); } } }
void SendStatusIfNeeded(uint timestamp32) // sender thread { if (_lastTimeSentStatus == null || MiscProcedures.TimeStamp1IsLess(_lastTimeSentStatus.Value + SubtLogicConfiguration.SubtRemoteStatusPacketTransmissionIntervalTicks, timestamp32) ) { _lastTimeSentStatus = timestamp32; var remotePeerId = SubtConnectedPeer.RemotePeerId; if (remotePeerId != null) { bool samePacketAlreadySent = false; if (_lastSentSubtStatusPacket != null) { if (_lastSentSubtStatusPacket.ImHealthyAndReadyFor100kbpsU2uSymbiosis == SubtLocalPeer.ImHealthyAndReadyFor100kbpsU2uSymbiosis && _lastSentSubtStatusPacket.RecentTxBandwidth == this.RecentTxBandwidth && _lastSentSubtStatusPacket.RecentRxBandwidth == _rxMeasurement.RecentBandwidth) { samePacketAlreadySent = true; } } if (!samePacketAlreadySent) { var statusPacket = new SubtRemoteStatusPacket(_rxMeasurement.RecentBandwidth, _rxMeasurement.RecentPacketLoss, this.RecentTxBandwidth, SubtLocalPeer.LocalPeer.Configuration.RoleAsSharedPassive, SubtLocalPeer.ImHealthyAndReadyFor100kbpsU2uSymbiosis ); // SubtLocalPeer.WriteToLog_deepDetail($"sending SUBT status packet: {statusPacket} to peer {SubtConnectedPeer.RemotePeerId}"); var data = statusPacket.Encode(this); _stream.SendPacket(data, data.Length); _lastSentSubtStatusPacket = statusPacket; } } } }