private const byte PAYLOAD_TYPE_MASK = 0x7F; // 01111111b /// <summary> /// Буфер для полезной нагрузки RTP пакетов /// </summary> //private byte[] buffer = new byte[ushort.MaxValue]; /// <summary> /// Формирует структуру RtpPacket и вызывает событие CreatedRtpPacket /// </summary> /// <param name="data">Указатель на область памяти /// в неуправляемой куче, где содержится RTP пакет</param> /// <param name="count">Размер области памяти в байтах</param> unsafe public void HandleRtpPacket(IPEndPoint remoteEndPoint, IntPtr data, int count) //IntPtr data, int count)// { RtpPacket packet = new RtpPacket(); int offset = 0; byte firstByte = *(byte *)(data + offset); offset++; byte secondByte = *(byte *)(data + offset); packet.Version = (byte)(firstByte >> 6); packet.Padding = (firstByte & PADDING_MASK) == PADDING_MASK; packet.HasExtension = (firstByte & EXTENSION_MASK) == EXTENSION_MASK; packet.CSRCCount = (byte)(firstByte & CSRCCOUNT_MASK); packet.IsMarker = (secondByte & IS_MARKER_MASK) == IS_MARKER_MASK; packet.PayloadType = (byte)(secondByte & PAYLOAD_TYPE_MASK); offset++; packet.SequenceNumber = BigEndian.ReadUInt16((void *)(data + offset)); offset += 2; packet.Timestamp = BigEndian.ReadUInt32((void *)(data + offset)); offset += 4; packet.SSRC = BigEndian.ReadUInt32((void *)(data + offset)); if (packet.SSRC != SSRC) { return; // пакеты другой сессии } if (packet.HasExtension) { offset = 12 + packet.CSRCCount * 4 + 0; packet.HeaderExtensionProfile = BigEndian.ReadUInt16((void *)(data + offset)); offset += 2; packet.HeaderExtensionLength = BigEndian.ReadUInt16((void *)(data + offset)); offset += 2; packet.HeaderExtension = data + offset; offset += packet.HeaderExtensionLength; packet.PayloadLength = count - offset; packet.Payload = data + offset; } else { offset = 12 + packet.CSRCCount * 4 + 0; packet.HeaderExtensionProfile = 0; packet.HeaderExtensionLength = 0; packet.HeaderExtension = IntPtr.Zero; packet.PayloadLength = count - offset; packet.Payload = data + offset; } RtpPacketRecieved(packet); }
/// <summary> /// Обрабатывает пришедшие данные и вызывает всякие события /// </summary> /// <param name="data">Указатель область памяти, /// где располагаются данные RTCP пакета</param> /// <param name="count">Размер области памяти в байтах</param> public void CreateRtcpPacket(IntPtr data, int count) { Marshal.Copy(data, buffer, 0, count); // сейчас в массиве buffer содержится один или более RTCP пакетов // их нужно обработать в цикле // сначала обрабатывается первый пакет, который находится в начале // буфера, потом данные в буфере сдвигаются к началу, и т. д., // пока не будут обработаны все пакеты while (count > 0) { int version = buffer[0] >> 6; if (version != 2) { return; } bool padding = (buffer[0] & _00100000) == _00100000; RtcpPacketType packetType = (RtcpPacketType)buffer[1]; int length = (BigEndian.ReadUInt16(buffer, 2) + 1) * 4; switch (packetType) { case RtcpPacketType.SenderReport: RtcpSenderReportPacket packet = new RtcpSenderReportPacket(); packet.Version = (byte)(buffer[0] >> 6); packet.Padding = ((buffer[0] & _00100000) == _00100000); packet.ReceptionReportCount = (byte)(buffer[0] & _00011111); packet.PayloadType = RtcpPacketType.SenderReport; packet.SenderSSRC = BigEndian.ReadUInt32(buffer, 4); packet.TimestampMSW = BigEndian.ReadUInt32(buffer, 8); packet.TimestampLSW = BigEndian.ReadUInt32(buffer, 12); packet.RTPTimestamp = BigEndian.ReadUInt32(buffer, 16); packet.SenderPacketCount = BigEndian.ReadUInt32(buffer, 20); packet.SenderOctetCount = BigEndian.ReadUInt32(buffer, 24); CreatedRtcpSenderReport(packet); break; case RtcpPacketType.ReceiverReport: // ничего не делать, т. к. нам не будут приходить пакеты такого типа break; case RtcpPacketType.SourceDescription: // прочитать количество источников int sourceCount = buffer[0] & _00011111; int pos = 4; RtcpSourceDescription[] result = new RtcpSourceDescription[sourceCount]; for (int i = 0; i < sourceCount; i++) { result[i] = new RtcpSourceDescription(); result[i].SSRC = BigEndian.ReadUInt32(buffer, pos); pos += 4; List <RtcpSourceDescriptionItem> items = new List <RtcpSourceDescriptionItem>(); while (buffer[pos] != 0) { RtcpSourceDescriptionType type = (RtcpSourceDescriptionType)buffer[pos]; pos += 1; int itemLength = buffer[pos]; pos += 1; string itemText = Encoding.UTF8.GetString(buffer, pos, itemLength); pos += itemLength; items.Add(new RtcpSourceDescriptionItem() { Type = type, Text = itemText }); } result[i].Items = items.ToArray(); // выравниваем по 4 байтам while (pos % 4 != 0) { pos++; } } CreatedRtcpSourceDescription(result); break; case RtcpPacketType.Goodbye: // ничего не делать break; case RtcpPacketType.ApplicationDefined: // ничего не делать break; } // смещаем оставшиеся данные в буфере к началу for (int i = length; i < count; i++) { buffer[i - length] = buffer[i]; buffer[i] = 0; } count -= length; } }
/// <summary> /// RTP Payload Format for JPEG-compressed Video /// http://tools.ietf.org/html/rfc2435 /// </summary> /// <param name="packet">RTP пакет для обработки</param> unsafe override public void HandleRtpPacket(RtpPacket packet) { int payloadLength = packet.PayloadLength; int offset = 0; byte typeSpecific = *(byte *)(packet.Payload + offset); offset++; int fragmentOffset = BigEndian.ReadInt24((byte *)(packet.Payload + offset)); offset += 3; if (expectingOffset != fragmentOffset) { corrupted = true; } byte type = *(byte *)(packet.Payload + offset); offset++; byte q = *(byte *)(packet.Payload + offset); offset++; ushort width = (ushort)((*(byte *)(packet.Payload + offset)) * 8); offset++; ushort height = (ushort)((*(byte *)(packet.Payload + offset)) * 8); offset++; if (type >= 64 && type <= 127) { offset += 4; } byte qmbz = 0, qprecizion = 0; ushort qlength = 0; if (q >= 128 && q <= 255 && fragmentOffset == 0) { qmbz = *(byte *)(packet.Payload + offset); offset++; qprecizion = *(byte *)(packet.Payload + offset); offset++; qlength = BigEndian.ReadUInt16((void *)(packet.Payload + offset)); offset += 2; if (qlength == 128) { BigEndian.WriteUInt16(buffer, HEIGHT_OFFSET, height); BigEndian.WriteUInt16(buffer, WIDTH_OFFSET, width); Marshal.Copy(packet.Payload + offset, buffer, QUANT_1_OFFSET, 64); offset += 64; Marshal.Copy(packet.Payload + offset, buffer, QUANT_2_OFFSET, 64); offset += 64; } } int jpgLength = payloadLength - offset; Marshal.Copy(packet.Payload + offset, buffer, DATA_OFFSET + fragmentOffset, jpgLength); bufferOffset = DATA_OFFSET + fragmentOffset + jpgLength; expectingOffset = fragmentOffset + jpgLength; if (packet.IsMarker) { if (!corrupted) { IntPtr jpg = Marshal.AllocHGlobal(bufferOffset); Marshal.Copy(buffer, 0, jpg, bufferOffset); currentPacketTimeStamp = packet.Timestamp; if (currentPacketTimeStamp <= lastPacketTimeStamp) { Logger.Write(String.Format("{0}<={1}", currentPacketTimeStamp, lastPacketTimeStamp), EnumLoggerType.DebugLog); } OnFrameReceived(jpg, bufferOffset, true, packet.Timestamp); lastPacketTimeStamp = currentPacketTimeStamp; Marshal.FreeHGlobal(jpg); } corrupted = false; expectingOffset = 0; } }