internal float RecentPacketLoss => Math.Min(_recentPacketLoss.Output, 1); // 0..1 void OnPlayed(JitterBufferElement jbe, uint timeNow32) { if (_lastPlayedJBE != null) { var missingPacketsCountAtThisPlaybackTime = SubtractSequences(jbe.sequence, _lastPlayedJBE.sequence) - 1; // var timePassed32AtSender = SubtractTimestamps(jbe.timestamp32, _lastPlayedJBE.timestamp32);////todo better use local time // if (missingPacketsCountAtThisPlaybackTime > 100) // _subtLocalPeer.WriteToLog($"missingPacketsCountAtThisPlaybackTime = {missingPacketsCountAtThisPlaybackTime}"); _recentPacketLoss.Input(missingPacketsCountAtThisPlaybackTime // , timePassed32AtSender ); _recentPacketLoss.OnTimeObserved(timeNow32); lock (_recentBandwidth) { _recentBandwidth.Input(jbe.bandwidthSizeBits//, timePassed32 ); _recentBandwidth.OnTimeObserved(timeNow32); } } _lastPlayedJBE = jbe; }
/// <summary> /// general algorithm: /// find a place where to insert this packet into jitter buffer /// if it is duplicate, ignore the packet /// insert into jitter buffer /// simulate playback: remove elements from head of the JB /// calculate packet loss out of sequence fields /// </summary> internal void OnReceivedPacket(ushort bandwidthSizeBits, ushort sequence, uint timestamp32, uint timeNow32) { var jbe = new JitterBufferElement { timestamp32 = timestamp32, sequence = sequence, bandwidthSizeBits = bandwidthSizeBits }; if (TryInsertIntoJitterBuffer(jbe)) { PlaybackFromJitter(timeNow32); } }
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; }
bool IsNewElementOutOfSequence(JitterBufferElement jbe) { if (_jitterBuffer.Count != 0) { var firstSeq = _jitterBuffer.First.Value.sequence; var lastSeq = _jitterBuffer.Last.Value.sequence; const ushort maxDistance = 1000; // TODO why 1000?? var minSequence = unchecked((ushort)(firstSeq - maxDistance)); if (Sequence1IsLess(jbe.sequence, minSequence)) return true; var maxSequence = unchecked((ushort)(lastSeq + maxDistance)); if (Sequence1IsLess(maxSequence, jbe.sequence)) return true; return false; } return false; }