public void GotVideoRtp(IPEndPoint remoteEndPoint, uint ssrc, uint seqnum, uint timestamp, int payloadID, bool marker, byte[] payload) { //logger.LogDebug($"rtp video, seqnum {seqnum}, ts {timestamp}, marker {marker}, payload {payload.Length}."); if (_currVideoFramePosn + payload.Length >= _currVideoFrame.Length) { // Something has gone very wrong. Clear the buffer. _currVideoFramePosn = 0; } // New frames must have the VP8 Payload Descriptor Start bit set. // The tracking of the current video frame position is to deal with a VP8 frame being split across multiple RTP packets // as per https://tools.ietf.org/html/rfc7741#section-4.4. if (_currVideoFramePosn > 0 || (payload[0] & 0x10) > 0) { RtpVP8Header vp8Header = RtpVP8Header.GetVP8Header(payload); Buffer.BlockCopy(payload, vp8Header.Length, _currVideoFrame, _currVideoFramePosn, payload.Length - vp8Header.Length); _currVideoFramePosn += payload.Length - vp8Header.Length; if (marker) { DateTime startTime = DateTime.Now; List <byte[]> decodedFrames = _vp8Decoder.Decode(_currVideoFrame, _currVideoFramePosn, out var width, out var height); if (decodedFrames == null) { logger.LogWarning("VPX decode of video sample failed."); } else { foreach (var decodedFrame in decodedFrames) { byte[] rgb = PixelConverter.I420toRGB(decodedFrame, (int)width, (int)height); //Console.WriteLine($"VP8 decode took {DateTime.Now.Subtract(startTime).TotalMilliseconds}ms."); OnVideoSinkDecodedSample(rgb, width, height, (int)(width * 3)); } } _currVideoFramePosn = 0; } } else { logger.LogWarning("Discarding RTP packet, VP8 header Start bit not set."); logger.LogWarning($"rtp video, seqnum {seqnum}, ts {timestamp}, marker {marker}, payload {payload.Length}."); } }
public static RtpVP8Header GetVP8Header(byte[] rtpPayload) { RtpVP8Header vp8Header = new RtpVP8Header(); int payloadHeaderStartIndex = 1; // First byte of payload descriptor. vp8Header.ExtendedControlBitsPresent = ((rtpPayload[0] >> 7) & 0x01) == 1; vp8Header.StartOfVP8Partition = ((rtpPayload[0] >> 4) & 0x01) == 1; vp8Header._length = 1; // Is second byte being used. if (vp8Header.ExtendedControlBitsPresent) { vp8Header.IsPictureIDPresent = ((rtpPayload[1] >> 7) & 0x01) == 1; vp8Header._length = 2; payloadHeaderStartIndex = 2; } // Is the picture ID being used. if (vp8Header.IsPictureIDPresent) { if (((rtpPayload[2] >> 7) & 0x01) == 1) { // The Picture ID is using two bytes. vp8Header._length = 4; payloadHeaderStartIndex = 4; vp8Header.PictureID = BitConverter.ToUInt16(rtpPayload, 2); } else { // The picture ID is using one byte. vp8Header.PictureID = rtpPayload[2]; vp8Header._length = 3; payloadHeaderStartIndex = 3; } } vp8Header._payloadDescriptorLength = payloadHeaderStartIndex; return(vp8Header); }