Esempio n. 1
0
        public Common.SegmentStream GetSample(Container.Track track, out TimeSpan duration)
        {
            Rtp.RtpFrame result = new Rtp.RtpFrame(0);

            while (HasNext)
            {
                Rtp.RtpPacket next = new Rtp.RtpPacket(ReadNext().Data.ToArray(), 0);
                if (result.Count > 0 && next.Timestamp != result.Timestamp)
                {
                    break;
                }
                result.Add(next);
                if (next.Marker)
                {
                    break;
                }
            }

            duration = TimeSpan.FromMilliseconds(90 * result.Count);

            //return result.Assemble().ToArray();

            result.Depacketize();

            return(result.Buffer);
        }
Esempio n. 2
0
            protected internal override void ProcessPacket(Rtp.RtpPacket packet, bool ignoreForbiddenZeroBit = true, bool fullStartCodes = false)
            {
                //Determine if the forbidden bit is set and the type of nal from the first byte
                byte firstByte = packet.Payload[packet.HeaderOctets];

                byte nalUnitType = (byte)(firstByte & Common.Binary.FiveBitMaxValue);

                //Determine if extended nals are present
                switch (nalUnitType)
                {
                case Media.Codecs.Video.H264.NalUnitType.Prefix:     //Prefix Nal
                {
                    return;
                }

                case Media.Codecs.Video.H264.NalUnitType.SequenceParameterSetSubset:     // Subset sequence parameter set
                {
                    return;
                }

                case Media.Codecs.Video.H264.NalUnitType.SliceExtension:     // Coded slice in scalable extension
                {
                    return;
                }

                case Media.Codecs.Video.H264.NalUnitType.PayloadContentScalabilityInformation:     // PACSI NAL unit
                {
                    //if more than 10 bytes present containesSei = true.

                    //Skip 10 bytes

                    //read nal unit size

                    //Write nal unit size.

                    return;
                }

                case Media.Codecs.Video.H264.NalUnitType.NonInterleavedMultiTimeAggregation:     // Empty, NT-MTAP etc.
                {
                    //Get subType
                    //Read nal unit size
                    //skip TS offset (2 bytes)
                    //Skip DON if present
                    return;
                }

                default:
                {
                    //Handle as per RFC6184
                    base.ProcessPacket(packet, ignoreForbiddenZeroBit, fullStartCodes);
                    return;
                }
                }
            }
Esempio n. 3
0
        public string ToTextualConvention(FileFormat?format = null)
        {
            try
            {
                StringBuilder sb = new StringBuilder();

                var ts = Timebase.TimeOfDay.Add(TimeSpan.FromMilliseconds(Offset));

                if (IsRtcp)
                {
                    sb.Append(RtpSend.ToTextualConvention(format ?? Format, Media.Rtcp.RtcpPacket.GetPackets(Blob, Pointer + sizeOf_RD_packet_T, BlobLength - sizeOf_RD_packet_T), ts, Source));
                }
                else
                {
                    using (var rtp = new Rtp.RtpPacket(Blob, Pointer + sizeOf_RD_packet_T)) sb.Append(RtpSend.ToTextualConvention(format ?? Format, rtp, ts, Source));
                }

                return(sb.ToString());
            }
            catch { throw; }
        }
Esempio n. 4
0
        /// <summary>
        /// Writes the given RtpPacket to the stream at the current position.
        /// If written in <see cref="DumpFormat.Binary"/> or <see cref="DumpFormat.Header"/> the packet will contain an 8 Byte overhead.
        /// If written in <see cref="DumpFormat.Header"/> the packet will not contain the RtpPacket Payload or the Extension Data if present.
        /// If written in <see cref="DumpFormat.Payload"/> the Rtp Packet will only contain the RTP Payload and will not be able to be read back into RtpPackets with this class.
        /// </summary>
        /// <param name="packet">The time</param>
        /// <param name="timeOffset">The optional time the packet was recieved relative to the beginning of the file. If the packet has a Created time that will be used otherwise DateTime.UtcNow.</param>
        public void WritePacket(Rtp.RtpPacket packet, TimeSpan?timeOffset = null, System.Net.IPEndPoint source = null)
        {
            if (timeOffset < TimeSpan.Zero)
            {
                throw new ArgumentOutOfRangeException("timeOffset cannot be less than the start of the file which is defined in the header. ");
            }

            //Use the tool entry so it is disposed immediately
            using (var entry = new RtpToolEntry(m_Start, source ?? m_Source, packet))
            {
                //If given a specific timeoffset use it
                if (timeOffset.HasValue)
                {
                    entry.Offset = (int)timeOffset.Value.TotalMilliseconds;
                }
                else
                {
                    entry.Offset = (int)(DateTime.UtcNow - m_Start).TotalMilliseconds;  //otherwise calulcate it
                }
                //Write the item
                WriteToolEntry(entry);
            }
        }
Esempio n. 5
0
            //Should the ObjectType be allowed to be given...? 3 or 5 for Audio?

            //No longer needed
            /// <summary>
            /// Write the required prerequesite data to the Buffer along with the Video Frame.
            /// </summary>
            /// <param name="profileLevelId">The profileLevelId (if not 1)</param>
            //public void Depacketize(byte profileLevelId = 1)
            //{
            //    DisposeBuffer();

            //    if (profileLevelId < 1) throw new ArgumentException("Must be a valid Mpeg 4 Profile Level Id with a value >= 1", "profileLevelId");

            //    /*
            //      Example usages for the "profile-level-id" parameter are:
            //      1  : MPEG-4 Visual Simple Profile/Level 1
            //      15 : AAC?
            //      34 : MPEG-4 Visual Core Profile/Level 2
            //      145: MPEG-4 Visual Advanced Real Time Simple Profile/Level 1
            //     */

            //    m_Buffer = new MemoryStream(Media.Containers.Mpeg.StartCodes.StartCodePrefix. //00 00 01
            //        Concat(Media.Common.Extensions.Linq.LinqExtensions.Yield(Media.Codecs.Video.Mpeg4.StartCodes.VisualObjectSequence)).
            //        Concat(Media.Common.Extensions.Linq.LinqExtensions.Yield(profileLevelId)). // B0 XX (ID)
            //        Concat(Media.Containers.Mpeg.StartCodes.StartCodePrefix).Concat(Assemble()).ToArray()); // 00 00 01 XX (DATA)
            //}

            public virtual void ProcessPacket(Rtp.RtpPacket packet, byte profileLevelId = 1)
            {
                int addIndex = packet.Timestamp - packet.SequenceNumber; // Depacketized.Count > 0 ? Depacketized.Keys.Last() : 0;

                //byte[] t = new byte[] { profileLevelId, Media.Codecs.Video.Mpeg4.StartCodes.VisualObjectSequence };

                //Common.MemorySegment a = new Common.MemorySegment(t, 0, 1);

                //Common.MemorySegment b = new Common.MemorySegment(t, 1, 1);

                //Creates 4 byte array each time with the first 3 bytes being the same, would need a way to combine MemorySegments for this to be any more efficient.
                Depacketized.Add(addIndex++, CreatePrefixedStartCodeSegment(Media.Codecs.Video.Mpeg4.StartCodes.VisualObjectSequence));

                //Depacketized.Add(addIndex++, StartCodePrefixSegment);

                //Depacketized.Add(addIndex++, b);

                //Or

                //Depacketized.Add(addIndex++, StartCodePrefixSegment);

                //Depacketized.Add(addIndex++, new Common.MemorySegment(new byte[] { Media.Codecs.Video.Mpeg4.StartCodes.VisualObjectSequence }));

                //What a waste, 4 bytes + to describe 1
                Depacketized.Add(addIndex++, new Common.MemorySegment(new byte[] { profileLevelId }));

                Depacketized.Add(addIndex++, StartCodePrefixSegment);

                // or

                //Depacketized.Add(addIndex++, a);

                //Depacketized.Add(addIndex++, StartCodePrefixSegment);

                Depacketized.Add(addIndex++, packet.PayloadDataSegment);
            }
Esempio n. 6
0
        //SUpparioucly thought that the last frame would remain in a not final state so one should check that the Goodbye has not been received when processing the event...
        //Shows what happpens when packets get out of order too badely..
        public void TestFrameChangedEvents()
        {
            using (Rtp.RtpClient rtpClient = new Rtp.RtpClient())
            {
                //Fire Frames with single packets and order = 3, 1, 2.
                //Each have marker

                //What is the expected order(3 final, 1 final, 2 final)? and count (3)!

                int count = 0, fcount = 0;

                Rtp.RtpClient.TransportContext tc = new Rtp.RtpClient.TransportContext(0, 1, 0);

                //Needed to resolve packet payload and avoid exception when validating packet... :(
                tc.MediaDescription = new Sdp.MediaDescription(Sdp.MediaType.unknown, 0, "RTP", 0);

                rtpClient.AddContext(tc);

                rtpClient.FrameChangedEventsEnabled = true;

                rtpClient.RtpFrameChanged += (s, z, t, f) =>
                {
                    ++count;

                    if (f)
                    {
                        ++fcount;
                    }

                    Console.WriteLine("RtpFrameChanged=>" + z.HighestSequenceNumber + z.HasMarker + f);
                };

                Console.WriteLine("Count: " + count);

                Console.WriteLine("Final Count: " + fcount);

                int tests = 4;

                Rtp.RtpPacket rtpPacket           = new Rtp.RtpPacket(12)
                {
                    Timestamp      = tests * 1000,
                    SequenceNumber = tests,
                    Marker         = true
                };

                for (int i = tests; i >= 0; --i)
                {
                    rtpPacket.Timestamp -= tests * 1000;

                    rtpPacket.SequenceNumber = i;

                    rtpClient.HandleIncomingRtpPacket(null, rtpPacket, tc);
                }

                //8 Frame Changes only 3 are final...
                if (count != tests || fcount != tests)
                {
                    throw new Exception();
                }

                count = fcount = 0;

                rtpPacket.SequenceNumber = 4;
                rtpPacket.Timestamp      = 4000;

                rtpClient.HandleIncomingRtpPacket(null, rtpPacket, tc);

                rtpPacket.SequenceNumber = 1;
                rtpPacket.Timestamp      = 1000;

                rtpClient.HandleIncomingRtpPacket(null, rtpPacket, tc);

                rtpPacket.SequenceNumber = 3;
                rtpPacket.Timestamp      = 3000;

                rtpClient.HandleIncomingRtpPacket(null, rtpPacket, tc);

                rtpPacket.SequenceNumber = 2;
                rtpPacket.Timestamp      = 2000;

                rtpClient.HandleIncomingRtpPacket(null, rtpPacket, tc);

                if (count != tests || fcount != tests)
                {
                    throw new Exception();
                }
            }
        }
Esempio n. 7
0
        //WritePackets(RtcpPacket[])

        //WritePackets(RtpFrame frame)

        /// <summary>
        /// Writes a DumpItem to the underlying stream
        /// </summary>
        /// <param name="item">The DumpItem to write</param>
        internal void WriteToolEntry(RtpToolEntry entry)
        {
            //If already written nothing occurs
            WriteFileHeader();

            //Write non text format entry
            if (m_Format < FileFormat.Text)
            {
                //This is a header style
                if (m_Format == FileFormat.Header)
                {
                    if (entry.IsRtcp)
                    {
                        //Indicate only the header is kept
                        entry.Length = (short)(entry.BlobLength = Media.Rtcp.RtcpHeader.Length + +RtpToolEntry.sizeOf_RD_packet_T);

                        //Write the data from the blob
                        using (Rtcp.RtcpPacket rtcp = new Rtcp.RtcpPacket(entry.Blob, entry.Pointer + RtpToolEntry.sizeOf_RD_packet_T)) m_Writer.Write(entry.Blob, 0, entry.BlobLength);
                    }
                    else if (m_Format != FileFormat.Rtcp)
                    {
                        using (Rtp.RtpPacket rtp = new Rtp.RtpPacket(entry.Blob, entry.Pointer + RtpToolEntry.sizeOf_RD_packet_T))
                        {
                            //Indicate only the header is kept
                            entry.Length = (short)(entry.BlobLength = Media.Rtp.RtpHeader.Length + RtpToolEntry.sizeOf_RD_packet_T);

                            //Write the data from the blob
                            m_Writer.Write(entry.Blob, 0, entry.BlobLength);
                        }
                    }
                }
                else if (m_Format == FileFormat.Binary)
                {
                    //Nothing to change
                    m_Writer.Write(entry.Blob);
                }
                else if (m_Format == FileFormat.Payload)
                {
                    if (entry.IsRtcp)
                    {
                        using (Rtcp.RtcpPacket rtcp = new Rtcp.RtcpPacket(entry.Blob, entry.Pointer + RtpToolEntry.sizeOf_RD_packet_T))
                        {
                            entry.Length = (short)(entry.BlobLength = rtcp.Payload.Count() + +RtpToolEntry.sizeOf_RD_packet_T);
                            m_Writer.Write(rtcp.Payload.ToArray());
                        }
                    }
                    else
                    {
                        using (Rtp.RtpPacket rtp = new Rtp.RtpPacket(entry.Blob, entry.Pointer + RtpToolEntry.sizeOf_RD_packet_T))
                        {
                            entry.Length = (short)(entry.BlobLength = rtp.PayloadData.Count() + RtpToolEntry.sizeOf_RD_packet_T);
                            m_Writer.Write(entry.Blob, 0, entry.BlobLength);
                        }
                    }
                }
            }
            else
            {
                //Write the textual version of the entry
                m_Writer.Write(System.Text.Encoding.ASCII.GetBytes(entry.ToString(m_Format)));
            }

            //Increment for the entry written
            ++m_ItemsWritten;
        }
Esempio n. 8
0
        //Move to RtpSendExtensions?
        /// <summary>
        /// Parses the data contained in the given <see cref="System.IO.BinaryReader"/> for data which corresponds to a format compatible with <see href="http://www.cs.columbia.edu/irt/software/rtptools/#rtpsend">rtpsend</see>.
        /// If a Binary format is encoutered (by the presence of the "#!rtpplay1.0" file header then <see cref="RtpTools.RtpDump.RtpDumpExtensions.ReadBinaryToolEntry"/> will be called implicitly with the reader given,
        /// In such a case, unexpected will contain the data which matched the file header.
        /// </summary>
        /// <param name="reader">The <see cref="System.IO.BinaryReader"/> which should be created using <see cref="System.Text.Encoding.ASCII"/></param>
        /// <param name="format">The format found while parsing the description.</param>
        /// <param name="unexpected">Will only contain data if format was unknown, and contains the data encountered in the stream while attempting to parse.</param>
        /// <returns>The item which was created as a result of reading from the stream.</returns>
        internal static RtpToolEntry ParseText(System.IO.BinaryReader reader, System.Net.IPEndPoint source, ref FileFormat format, out byte[] unexpected)
        {
            unexpected = null;

            double timeOffset = 0;

            System.Net.IPEndPoint sourceInfo = source;

            long position = reader.BaseStream.Position;

            Common.IPacket builtPacket = null;

            //Keep track of making a Rtp or Rtcp entry.
            bool rtp = false;

            Rtp.RtpPacket rtpP = null;

            Rtcp.RtcpPacket rtcP = null;

            ///<summary>
            /// each entry starts with a time value, in seconds, relative to the beginning of the trace.
            /// The time value must appear at the beginning of a line, without white space. Within an RTP or RTCP packet description,
            /// parameters may appear in any order, without white space around the equal sign.
            /// Lines are continued with initial white space on the next line.
            /// Comment lines start with #. Strings are enclosed in quotation marks.
            /// <see cref="Tokens"/>
            ///</summary>

            //The amount of tokens consumed from the reader, where a token is defined as above
            int tokensParsed = -1,

            //The amount of bytes read
            lineBytesLength = 0,

            //Used for token parsing, the index of the recognized token in 'Tokens'
            tokenIndex = -1;

            //Indicates if in a comment
            bool parsingCommentOrWhitespace = false;

            //Contains the data read from the stream until '\n' occurs.
            byte[] lineBytes;

            //Indicates if the parsing of the entry is complete
            bool doneParsing = false, formatUnknown = format == FileFormat.Unknown, needAnyToken = true;

            //A string instance which was used to compare to known `Tokens`
            string token;

            //No bytes have actually been consumed from the stream yet, while not done parsing and not at the end of the stream
            while (!doneParsing && reader.BaseStream.Position < reader.BaseStream.Length)
            {
                //Determine the following character (ASCIIEncoding SHOULD have been specified in creation of the reader) [If not could possibly have also found out without consuming a byte here]
                int peek = reader.PeekChar();

                //If no data can be read
                if (peek == -1)
                {
                    //then indiate an unknown format and then return null
                    format = FileFormat.Unknown;
                    return null;
                }
                else if (peek == RtpDump.RtpDumpExtensions.Hash || peek == (char)Common.ASCII.Space)
                {
                    //Comment lines start with # (Hash). Strings are enclosed in quotation marks.
                    parsingCommentOrWhitespace = true;

                    //Could be a binary format however....
                }
                else if (tokensParsed > 0 && peek == 'r' || peek == Common.ASCII.R) //Don't read any further a new entry follows (Could be a malformed entry with rXXX=YYY\n)
                {
                    //doneParsing = true;
                    break;
                }

                //Read until '\n' occurs
                RtpSendExtensions.ReadLineFeed(reader.BaseStream, out lineBytes);

                //Keep track of the amount of bytes read
                lineBytesLength = lineBytes.Length;

                //If nothing was read return
                if (lineBytesLength == 0) return null;

                //If the format is unknown then
                if (formatUnknown)
                {
                    //check for the Binary format at the known ordinal, if found
                    if (lineBytesLength > 2 && lineBytes[0] == RtpDump.RtpDumpExtensions.Hash && lineBytes[1] == RtpDump.RtpDumpExtensions.Bang)
                    {
                        //Indicate a binary format so far
                        format = FileFormat.Binary;

                        //give the `unexpected` data back to the caller, which consisted of the header
                        unexpected = lineBytes;

                        //Remove the reference to the token now
                        token = null;

                        //Return the result of reading the binary entry.
                        return null;
                    }

                    //Check for the short form before parsing a token

                    //Search for '='

                    tokenIndex = Array.IndexOf<byte>(lineBytes, Common.ASCII.EqualsSign);

                    //If not found then this must be a Short entry.
                    if (tokenIndex == -1)
                    {
                        //No longer unknown because,
                        formatUnknown = false;

                        //This seems to be Short format
                        format = FileFormat.Short;
                    }
                    else //There was a '=' sign
                    {
                        //No longer still unknown because,
                        //This seems to be a Text format
                        format = FileFormat.Text;

                        //but we need to consume tokens until data in tokens as they occur to be sure
                    }
                }

                //If not found then this must be a Short entry.
                if (format == FileFormat.Short) return RtpToolEntry.CreateShortEntry(DateTime.UtcNow.Subtract(TimeSpan.FromMilliseconds(timeOffset)), sourceInfo, lineBytes, 0, position);
                else if (parsingCommentOrWhitespace)//If parsing a whitespace or a comment
                {
                    //Not parsing the comment / whitespace any more
                    parsingCommentOrWhitespace = false;

                    //Perform another iteration
                    continue;
                }
                else if (needAnyToken) //If a token is needed
                {

                    //Extract all tokens from the lineBytes
                    string[] tokens = Encoding.ASCII.GetString(lineBytes).Split((char)Common.ASCII.Space, (char)Common.ASCII.EqualsSign, '(', ')');

                    //Any hex data will need a length, and we start at offset 0.
                    int dataLen = 0, tokenOffset = 0;

                    //If nothing was tokenized then return the unexpected data.
                    if (tokens.Length == 0)
                    {
                        unexpected = lineBytes;
                        return null;
                    }

                    //The first token must be a timeOffset
                    if (timeOffset == 0) timeOffset = double.Parse(tokens[tokenOffset++]);

                    //For each token in the tokens after the timeOffset
                    for (int e = tokens.Length; tokenOffset < e; ++tokenOffset)
                    {
                        //Get the token at the index
                        token = tokens[tokenOffset];

                        //Determine what to do based on if there was a matching token in the Tokens array.
                        tokenIndex = Array.IndexOf<string>(Tokens, token);

                        //The entry must be finished.
                        if (-1 == tokenIndex)
                        {
                            unexpected = lineBytes;
                            break;
                        }

                        //Increment for a token parsed within the entry so far
                        ++tokensParsed;

                        //Switch out logic based on token
                        switch (tokenIndex)
                        {
                            //RTP
                            case 1:
                                {
                                    //The created structure will have a packetLength = 8 + dataLen
                                    rtp = true;

                                    rtpP = new Rtp.RtpPacket(0, false, false, Media.Common.MemorySegment.EmptyBytes);

                                    builtPacket = rtpP;

                                    //Do another iteration
                                    continue;
                                }
                            //RTCP
                            case 17:
                                {

                                    rtcP = new Rtcp.RtcpPacket(0, 0, 0, 0, 0, 0);

                                    //The created structure will have packetLen = 0 to indicate Rtcp.
                                    builtPacket = rtcP;

                                    //Do another iteration

                                    continue;
                                }
                            case 35: //from
                                {

                                    string[] parts = tokens[++tokenOffset].Split((char)Common.ASCII.Colon);

                                    System.Net.IPAddress ip = System.Net.IPAddress.Parse(parts[0]);

                                    int port = int.Parse(parts[1]);

                                    System.Diagnostics.Debug.WriteLine(ip + " " + port);

                                    sourceInfo = new System.Net.IPEndPoint(ip, port);

                                    continue;
                                }
                            case 2:
                                {
                                    //version

                                    int version = int.Parse(tokens[++tokenOffset]);

                                    System.Diagnostics.Debug.WriteLine(version);

                                    if (rtp) rtpP.Header.Version = version;
                                    else rtcP.Header.Version = version;

                                    continue;
                                }
                            case 3: //p
                                {
                                    int paddingCount = int.Parse(tokens[++tokenOffset]);

                                    System.Diagnostics.Debug.WriteLine(paddingCount);

                                    break;
                                }
                            case 4: //x
                                {
                                    if (rtp)
                                    {
                                        bool hasExtension = int.Parse(tokens[++tokenOffset]) == 1;

                                        System.Diagnostics.Debug.WriteLine(hasExtension);

                                        rtpP.Header.Extension = hasExtension;
                                    }

                                    break;
                                }
                            case 5: //m
                                {
                                    if (rtp)
                                    {
                                        bool hasMarker = int.Parse(tokens[++tokenOffset]) == 1;

                                        System.Diagnostics.Debug.WriteLine(hasMarker);

                                        rtpP.Header.Marker = hasMarker;
                                    }
                                    break;
                                }
                            case 6: //pt
                                {

                                    int payloadType = int.Parse(tokens[++tokenOffset]);

                                    System.Diagnostics.Debug.WriteLine(payloadType);

                                    if (rtp) rtpP.Header.PayloadType = payloadType;
                                    else rtcP.Header.PayloadType = payloadType;

                                    break;
                                }
                            case 7: //ts
                                {
                                    if (rtp)
                                    {
                                        int ts = int.Parse(tokens[++tokenOffset]);

                                        System.Diagnostics.Debug.WriteLine(ts);

                                        rtpP.Header.Timestamp = ts;
                                    }

                                    break;
                                }
                            case 8: //seq
                                {
                                    if (rtp)
                                    {
                                        int seq = int.Parse(tokens[++tokenOffset]);

                                        System.Diagnostics.Debug.WriteLine(seq);

                                        rtpP.Header.SequenceNumber = seq;
                                    }
                                    break;
                                }
                            case 9: //ssrc
                                {

                                    token = tokens[++tokenOffset];

                                    token = token.Remove(token.Length - 1).Replace(HexSpecifier, string.Empty);

                                    int ssrc = 0;

                                    if (!int.TryParse(token, out ssrc)) //plain int
                                        ssrc = int.Parse(token, System.Globalization.NumberStyles.HexNumber);//hex

                                    System.Diagnostics.Debug.WriteLine(ssrc);

                                    if (rtp) rtpP.Header.SynchronizationSourceIdentifier = ssrc;
                                    else rtcP.Header.SendersSynchronizationSourceIdentifier = ssrc;

                                    break;
                                }
                            case 10: //cc
                                {

                                    int cc = int.Parse(tokens[++tokenOffset]);

                                    System.Diagnostics.Debug.WriteLine(cc);

                                    if (rtp) rtpP.Header.ContributingSourceCount = cc;
                                    else rtcP.Header.BlockCount = cc;

                                    break;
                                }
                            case 11: //csrc
                                {

                                    int csrc = int.Parse(tokens[++tokenOffset]);

                                    System.Diagnostics.Debug.WriteLine(csrc);

                                    //Add to a list.

                                    break;
                                }
                            case 12://data
                                {
                                    //Token based reading may not be required anymore
                                    needAnyToken = false;

                                    //Not unknown anymore because,
                                    formatUnknown = false;

                                    //Definitely hex format
                                    format = FileFormat.Hex;

                                    //The next token is the string in hex format of the payload.
                                    ++tokenIndex;

                                    //If it begins with Hash then its NIL

                                    //Done parsing the entry.
                                    doneParsing = true;

                                    continue;
                                }
                            case 13: //ext_type
                                {

                                    int ext_type = int.Parse(tokens[++tokenOffset]);

                                    System.Diagnostics.Debug.WriteLine(ext_type);

                                    break;
                                }
                            case 14: //ext_len
                                {

                                    int ext_len = int.Parse(tokens[++tokenOffset]);

                                    System.Diagnostics.Debug.WriteLine(ext_len);

                                    break;
                                }
                            case 15: //ext_data
                                {

                                    //The next token is the string in hex format of the payload.

                                    ++tokenIndex;

                                    //If it begins with Hash then its NIL

                                    break;
                                }
                            case 16: //len
                                {
                                    dataLen = int.Parse(tokens[++tokenOffset]);

                                    System.Diagnostics.Debug.WriteLine(dataLen);

                                    continue;
                                }
                            default:
                                {
                                    //If the format was unknown
                                    if (formatUnknown)
                                    {
                                        //It is no longer so because,
                                        formatUnknown = false;

                                        //it is no longer unknown, it is definitely a Text format
                                        format = FileFormat.Text;
                                    }

                                    break;
                                }
                        }//Done determining what to do with a token
                    }//Done with tokens
                }//Don't need to parse any tokens

            }

            //Create the resulting entry with the data contained in memory read from the reader by the writer
            return new RtpToolEntry(DateTime.UtcNow.Subtract(TimeSpan.FromMilliseconds(timeOffset)), sourceInfo, builtPacket, (int)timeOffset, position);
        }
Esempio n. 9
0
        public static void TestAConstructor_And_Reserialization()
        {
            //Cache a bitValue
            bool bitValue = false;

            //Permute every possible bit packed value that can be valid in the first and second octet
            for (int ibitValue = 0; ibitValue < 2; ++ibitValue)
            {
                //Make a bitValue after the 0th iteration
                if (ibitValue > 0)
                {
                    bitValue = Convert.ToBoolean(bitValue);
                }

                //Permute every possible value within the 2 bit Version
                for (int VersionCounter = 0; VersionCounter <= Media.Common.Binary.TwoBitMaxValue; ++VersionCounter)
                {
                    //Permute every possible value in the 7 bit PayloadCounter
                    for (int PayloadCounter = 0; PayloadCounter <= sbyte.MaxValue; ++PayloadCounter)
                    {
                        //Permute every possible value in the 4 bit ContributingSourceCounter
                        for (byte ContributingSourceCounter = byte.MinValue; ContributingSourceCounter <= Media.Common.Binary.FourBitMaxValue; ++ContributingSourceCounter)
                        {
                            int RandomId = Utility.Random.Next(), RandomSequenceNumber = Utility.Random.Next(ushort.MinValue, ushort.MaxValue), RandomTimestamp = Utility.Random.Next();

                            //Create a RtpPacket instance using the specified options
                            using (Media.Rtp.RtpPacket p = new Rtp.RtpPacket(VersionCounter,
                                                                             bitValue, !bitValue, bitValue,
                                                                             PayloadCounter,
                                                                             ContributingSourceCounter,
                                                                             RandomId,
                                                                             RandomSequenceNumber,
                                                                             RandomTimestamp))
                            {
                                //Check the Version
                                System.Diagnostics.Debug.Assert(p.Version == VersionCounter, "Unexpected Version");

                                //Check the Padding
                                System.Diagnostics.Debug.Assert(p.Padding == bitValue, "Unexpected Padding");

                                //Check the Extension
                                System.Diagnostics.Debug.Assert(p.Extension == !bitValue, "Unexpected Extension");

                                //Check the PayloadType
                                System.Diagnostics.Debug.Assert(p.PayloadType == PayloadCounter, "Unexpected PayloadType");

                                //Check the ContributingSourceCount
                                System.Diagnostics.Debug.Assert(p.ContributingSourceCount == ContributingSourceCounter, "Unexpected ContributingSourceCounter");

                                //Check the Length
                                System.Diagnostics.Debug.Assert(p.Length == Media.Rtp.RtpHeader.Length, "Unexpected Length");

                                //Serialize, Deserialize and verify again
                                using (Media.Rtp.RtpPacket s = new Rtp.RtpPacket(p.Prepare().ToArray(), 0))
                                {
                                    if (false == s.Prepare().SequenceEqual(p.Prepare()))
                                    {
                                        throw new Exception("Unexpected Data");
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
Esempio n. 10
0
        //Could be called Depacketize
        //Virtual because the RFC6190 logic defers to this method for non SVC nal types.
        /// <summary>
        /// Depacketizes a single packet.
        /// </summary>
        /// <param name="packet"></param>
        /// <param name="containsSps"></param>
        /// <param name="containsPps"></param>
        /// <param name="containsSei"></param>
        /// <param name="containsSlice"></param>
        /// <param name="isIdr"></param>
        internal protected virtual void ProcessPacket(Rtp.RtpPacket packet, bool ignoreForbiddenZeroBit = true, bool fullStartCodes = false)
        {
            //If the packet is null or disposed then do not process it.
            if (Common.IDisposedExtensions.IsNullOrDisposed(packet))
            {
                return;
            }

            //Just put the packets into Depacketized at the end for most cases.
            int packetKey = Depacketized.Count;

            //The packets are not stored by SequenceNumber in Depacketized, they are stored in whatever Decoder Order is necessary.
            //Already contained. (Might want to wait for the Decoder Order Number to be checked.
            //if (Depacketized.ContainsKey(packetKey)) return;

            //(May need to handle re-ordering)
            //In such cases this step needs to place the packets into a seperate collection for sorting on DON / TSOFFSET before writing to the buffer.

            //From the beginning of the data in the actual payload
            int offset  = packet.Payload.Offset + packet.HeaderOctets,
                padding = packet.PaddingOctets,
                count   = (packet.Payload.Count - padding),
                end     = packet.Length - padding;

            //Must have at least 2 bytes (When nalUnitType is a FragmentUnit.. 3)
            if (count <= 2 || offset > packet.Payload.Count)
            {
                return;
            }

            //Obtain the data of the packet with respect to extensions and csrcs present.
            byte[] packetData = packet.Payload.Array;

            //Determine if the forbidden bit is set and the type of nal from the first byte
            byte firstByte = packetData[offset];

            //Should never be set... (unless decoding errors are present)
            if (false == ignoreForbiddenZeroBit && ((firstByte & 0x80) >> 7) != 0)
            {
                return;                                                                                //throw new Exception("forbiddenZeroBit");
            }
            byte nalUnitType = (byte)(firstByte & Common.Binary.FiveBitMaxValue);

            //RFC6184 @ Page 20
            //o  The F bit MUST be cleared if all F bits of the aggregated NAL units are zero; otherwise, it MUST be set.
            //if (forbiddenZeroBit && nalUnitType <= 23 && nalUnitType > 29) throw new InvalidOperationException("Forbidden Zero Bit is Set.");

            //Needs other state to check if previously F was set or not

            //Media.Codecs.Video.H264.NalUnitPriority priority = (Media.Codecs.Video.H264.NalUnitPriority)((firstByte & 0x60) >> 5);

            //Determine what to do
            switch (nalUnitType)
            {
            //Reserved - Ignore
            case Media.Codecs.Video.H264.NalUnitType.Unknown:
            case Media.Codecs.Video.H264.NalUnitType.PayloadContentScalabilityInformation:
            case Media.Codecs.Video.H264.NalUnitType.Reserved:
            {
                //May have 4 byte NAL header.
                //Do not handle
                return;
            }

            case Media.Codecs.Video.H264.NalUnitType.SingleTimeAggregationA:                     //STAP - A
            case Media.Codecs.Video.H264.NalUnitType.SingleTimeAggregationB:                     //STAP - B
            case Media.Codecs.Video.H264.NalUnitType.MultiTimeAggregation16:                     //MTAP - 16
            case Media.Codecs.Video.H264.NalUnitType.MultiTimeAggregation24:                     //MTAP - 24
            {
                //Move to Nal Data
                ++offset;

                //Todo Determine if need to Order by DON first.
                //EAT DON for ALL BUT STAP - A
                if (nalUnitType != Media.Codecs.Video.H264.NalUnitType.SingleTimeAggregationA)
                {
                    //Should check for 2 bytes.

                    //Read the DecoderOrderingNumber and add the value from the index.
                    packetKey = Common.Binary.ReadU16(packetData, ref offset, BitConverter.IsLittleEndian);

                    //If the number was already observed skip this packet.
                    //if (Depacketized.ContainsKey(packetKey)) return;
                }

                //Should check for 2 bytes.

                //Consume the rest of the data from the packet
                while (offset < count)                                 // + 2 <=
                {
                    //Determine the nal unit size which does not include the nal header
                    int tmp_nal_size = Common.Binary.Read16(packetData, ref offset, BitConverter.IsLittleEndian);

                    //Should check for tmp_nal_size > 0
                    //If the nal had data and that data is in this packet then write it
                    if (tmp_nal_size >= 0)
                    {
                        //For DOND and TSOFFSET
                        switch (nalUnitType)
                        {
                        case Media.Codecs.Video.H264.NalUnitType.MultiTimeAggregation16:                                                // MTAP - 16 (May require re-ordering)
                        {
                            //Should check for 3 bytes.

                            //DOND 1 byte

                            //Read DOND and TSOFFSET, combine the values with the existing index
                            packetKey = (int)Common.Binary.ReadU24(packetData, ref offset, BitConverter.IsLittleEndian);

                            //If the number was already observed skip this packet.
                            //if (Depacketized.ContainsKey(packetKey)) return;

                            goto default;
                        }

                        case Media.Codecs.Video.H264.NalUnitType.MultiTimeAggregation24:                                                // MTAP - 24 (May require re-ordering)
                        {
                            //Should check for 4 bytes.

                            //DOND 2 bytes

                            //Read DOND and TSOFFSET , combine the values with the existing index
                            packetKey = (int)Common.Binary.ReadU32(packetData, ref offset, BitConverter.IsLittleEndian);

                            //If the number was already observed skip this packet.
                            //if (Depacketized.ContainsKey(packetKey)) return;

                            goto default;
                        }

                        default:
                        {
                            //Should check for tmp_nal_size > 0

                            //Could check for extra bytes or emulation prevention
                            //https://github.com/raspberrypi/userland/blob/master/containers/rtp/rtp_h264.c

                            //(Stores the nalType) Write the start code
                            DepacketizeStartCode(ref packetKey, ref packetData[offset], fullStartCodes);

                            //Add the depacketized data and increase the index.

                            //Ensure the size is within the count.
                            //When tmp_nal_size is 0 packetData which is referenced by this segment which will have a 0 count.
                            Depacketized.Add(packetKey++, new Common.MemorySegment(packetData, offset, Common.Binary.Min(tmp_nal_size, count - offset)));

                            //Move the offset past the nal
                            offset += tmp_nal_size;

                            continue;
                        }
                        }
                    }
                }

                //No more data in packet.
                return;
            }

            case Media.Codecs.Video.H264.NalUnitType.FragmentationUnitA:                     //FU - A
            case Media.Codecs.Video.H264.NalUnitType.FragmentationUnitB:                     //FU - B (May require re-ordering)
            {
                /*
                 * Informative note: When an FU-A occurs in interleaved mode, it
                 * always follows an FU-B, which sets its DON.
                 * Informative note: If a transmitter wants to encapsulate a single
                 * NAL unit per packet and transmit packets out of their decoding
                 * order, STAP-B packet type can be used.
                 */
                //Needs atleast 2 bytes to reconstruct...
                //3 bytes for any valid data to follow after the header.
                if (count >= 2)
                {
                    //Offset still at the firstByte (FU Indicator) move to and read FU Header
                    byte FUHeader = packetData[++offset];

                    bool Start = ((FUHeader & 0x80) >> 7) > 0;

                    //https://tools.ietf.org/html/rfc6184 page 31...

                    //A fragmented NAL unit MUST NOT be transmitted in one FU; that is, the
                    //Start bit and End bit MUST NOT both be set to one in the same FU
                    //header.

                    //bool End = ((FUHeader & 0x40) >> 6) > 0;

                    //ignoreReservedBit

                    //bool Reserved = (FUHeader & 0x20) != 0;

                    //Should not be set
                    //if (Reserved) throw new InvalidOperationException("Reserved Bit Set");

                    //Move to data (Just read the FU Header)
                    ++offset;

                    //packet.SequenceNumber - packet.Timestamp;

                    //Store the DecoderingOrderNumber we will derive from the timestamp and sequence number.
                    //int DecodingOrderNumber = packetKey;

                    //DON Present in FU - B, add the DON to the DecodingOrderNumber
                    if (nalUnitType == Media.Codecs.Video.H264.NalUnitType.FragmentationUnitB)
                    {
                        //Needs 2 more bytes...
                        Common.Binary.ReadU16(packetData, ref offset, BitConverter.IsLittleEndian);                                        //offset += 2;
                    }

                    //Should verify count... just consumed 1 - 3 bytes and only required 2.

                    //Determine the fragment size
                    int fragment_size = count - offset;

                    //Don't emit empty fragments
                    //if (fragment_size == 0) return;

                    //If the start bit was set
                    if (Start)
                    {
                        //ignoreEndBit
                        //if (End) throw new InvalidOperationException("Start and End Bit Set in same FU");

                        //Reconstruct the nal header
                        //Use the first 3 bits of the first byte and last 5 bites of the FU Header
                        byte nalHeader = (byte)((firstByte & 0xE0) | (FUHeader & Common.Binary.FiveBitMaxValue));

                        //(Stores the nal) Write the start code
                        DepacketizeStartCode(ref packetKey, ref nalHeader, fullStartCodes);

                        //Wasteful but there is no other way to provide this byte since it is constructor from two values in the header
                        //Unless of course a FragmentHeader : MemorySegment was created, which could have a NalType property ...
                        //Could also just have an overload which writes the NalHeader
                        //Would need a CreateNalSegment static method with option for full (4 + 1) or short code ( 3 + 1)/
                        Depacketized.Add(packetKey++, new Common.MemorySegment(new byte[] { nalHeader }));
                    }

                    //Add the depacketized data
                    Depacketized.Add(packetKey, new Common.MemorySegment(packetData, offset, fragment_size));

                    //Allow If End to Write End Sequence?
                    //Should only be done if last byte is 0?
                    //if (End) Buffer.WriteByte(Media.Codecs.Video.H264.NalUnitType.EndOfSequence);
                }

                //No more data?
                return;
            }

            default:                     //Any other type excluding PayloadContentScalabilityInformation(30) and Reserved(31)
            {
                //(Stores the nalUnitType) Write the start code
                DepacketizeStartCode(ref packetKey, ref nalUnitType, fullStartCodes);

                //Add the depacketized data
                Depacketized.Add(packetKey, new Common.MemorySegment(packetData, offset, count - offset));

                return;
            }
            }
        }
Esempio n. 11
0
        //Needs to ensure api is not confused with above. could also possibly handle in Packetize by searching for 0 0 1
        //public virtual void Packetize(byte[] accessUnit, int mtu = 1500, int? DON = null)
        //{
        //    throw new NotImplementedException();
        //    //Add all data and set marker packet on last packet.
        //    //Add AUD to next packet or the end of this one?
        //}

        //Not needed since ProcessPacket can do this.
        //public void Depacketize(bool ignoreForbiddenZeroBit = true, bool fullStartCodes = false)
        //{
        //    //base.Depacketize();

        //    DisposeBuffer();

        //    m_Buffer = new MemoryStream();

        //    var packets = Packets;

        //    //Todo, check if need to
        //    //Order by DON / TSOFFSET (if any packets contains MTAP etc)

        //    //Get all packets in the frame and proces them
        //    foreach (Rtp.RtpPacket packet in packets)
        //        ProcessPacket(packet, ignoreForbiddenZeroBit, fullStartCodes);

        //    //Bring the buffer back the start. (This does not have a weird side effect of adding 0xa to the stream)
        //    m_Buffer.Seek(0, SeekOrigin.Begin);

        //    //This has a weird side effect of adding 0xa to the stream
        //    //m_Buffer.Position = 0;
        //}

        /// <summary>
        /// Depacketizes all contained packets and adds start sequences where necessary which can be though of as a H.264 RBSP
        /// </summary>
        /// <param name="packet"></param>
        public override void Depacketize(Rtp.RtpPacket packet)
        {
            ProcessPacket(packet, false, false);
        }
Esempio n. 12
0
            /// <summary>
            /// Depacketizes a single packet.
            /// </summary>
            /// <param name="packet"></param>
            /// <param name="containsSps"></param>
            /// <param name="containsPps"></param>
            /// <param name="containsSei"></param>
            /// <param name="containsSlice"></param>
            /// <param name="isIdr"></param>
            internal protected virtual void ProcessPacket(Rtp.RtpPacket packet)
            {
                //Starting at offset 0
                int offset = 0;

                //Obtain the data of the packet (without source list or padding)
                byte[] packetData = packet.PayloadData.ToArray();

                //Cache the length
                int count = packetData.Length;

                //Must have at least 2 bytes
                if (count <= 2)
                {
                    return;
                }

                //Determine if the forbidden bit is set and the type of nal from the first byte
                byte firstByte = packetData[offset];

                //bool forbiddenZeroBit = ((firstByte & 0x80) >> 7) != 0;

                byte nalUnitType = (byte)(firstByte & Common.Binary.FiveBitMaxValue);

                //TODO

                //o  The F bit MUST be cleared if all F bits of the aggregated NAL units are zero; otherwise, it MUST be set.
                //if (forbiddenZeroBit && nalUnitType <= 23 && nalUnitType > 29) throw new InvalidOperationException("Forbidden Zero Bit is Set.");

                //Optomize setting out parameters, could be done with a label or with a static function.

                //Determine what to do
                switch (nalUnitType)
                {
                //Reserved - Ignore
                case Media.Codecs.Video.H264.NalUnitType.Unknown:
                case Media.Codecs.Video.H264.NalUnitType.PayloadContentScalabilityInformation:
                case Media.Codecs.Video.H264.NalUnitType.Reserved:
                {
                    //May have 4 byte NAL header.
                    //Do not handle
                    return;
                }

                case Media.Codecs.Video.H264.NalUnitType.SingleTimeAggregationA:     //STAP - A
                case Media.Codecs.Video.H264.NalUnitType.SingleTimeAggregationB:     //STAP - B
                case Media.Codecs.Video.H264.NalUnitType.MultiTimeAggregation16:     //MTAP - 16
                case Media.Codecs.Video.H264.NalUnitType.MultiTimeAggregation24:     //MTAP - 24
                {
                    //Move to Nal Data
                    ++offset;

                    //Todo Determine if need to Order by DON first.
                    //EAT DON for ALL BUT STAP - A
                    if (nalUnitType != Media.Codecs.Video.H264.NalUnitType.SingleTimeAggregationA)
                    {
                        offset += 2;
                    }

                    //Consume the rest of the data from the packet
                    while (offset < count)
                    {
                        //Determine the nal unit size which does not include the nal header
                        int tmp_nal_size = Common.Binary.Read16(packetData, offset, BitConverter.IsLittleEndian);
                        offset += 2;

                        //If the nal had data then write it
                        if (tmp_nal_size > 0)
                        {
                            //Store the nalType contained
                            m_ContainedNalTypes.Add(nalUnitType);

                            //For DOND and TSOFFSET
                            switch (nalUnitType)
                            {
                            case Media.Codecs.Video.H264.NalUnitType.MultiTimeAggregation16:            // MTAP - 16
                            {
                                //SKIP DOND and TSOFFSET
                                offset += 3;
                                goto default;
                            }

                            case Media.Codecs.Video.H264.NalUnitType.MultiTimeAggregation24:            // MTAP - 24
                            {
                                //SKIP DOND and TSOFFSET
                                offset += 4;
                                goto default;
                            }

                            default:
                            {
                                //Read the nal header but don't move the offset
                                byte nalHeader = (byte)(packetData[offset] & Common.Binary.FiveBitMaxValue);

                                //Store the nalType contained
                                m_ContainedNalTypes.Add(nalHeader);

                                if (nalHeader == 6 || nalHeader == 7 || nalHeader == 8)
                                {
                                    Buffer.WriteByte(0);
                                }

                                //Done reading
                                break;
                            }
                            }

                            //Write the start code
                            Buffer.Write(Media.Codecs.Video.H264.NalUnitType.StartCode, 0, 3);

                            //Write the nal header and data
                            Buffer.Write(packetData, offset, tmp_nal_size);

                            //Move the offset past the nal
                            offset += tmp_nal_size;
                        }
                    }

                    return;
                }

                case Media.Codecs.Video.H264.NalUnitType.FragmentationUnitA:     //FU - A
                case Media.Codecs.Video.H264.NalUnitType.FragmentationUnitB:     //FU - B
                {
                    /*
                     * Informative note: When an FU-A occurs in interleaved mode, it
                     * always follows an FU-B, which sets its DON.
                     * Informative note: If a transmitter wants to encapsulate a single
                     * NAL unit per packet and transmit packets out of their decoding
                     * order, STAP-B packet type can be used.
                     */
                    //Need 2 bytes
                    if (count > 2)
                    {
                        //Read the Header
                        byte FUHeader = packetData[++offset];

                        bool Start = ((FUHeader & 0x80) >> 7) > 0;

                        //bool End = ((FUHeader & 0x40) >> 6) > 0;

                        //bool Receiver = (FUHeader & 0x20) != 0;

                        //if (Receiver) throw new InvalidOperationException("Receiver Bit Set");

                        //Move to data
                        ++offset;

                        //Todo Determine if need to Order by DON first.
                        //DON Present in FU - B
                        if (nalUnitType == 29)
                        {
                            offset += 2;
                        }

                        //Determine the fragment size
                        int fragment_size = count - offset;

                        //If the size was valid
                        if (fragment_size > 0)
                        {
                            //If the start bit was set
                            if (Start)
                            {
                                //Reconstruct the nal header
                                //Use the first 3 bits of the first byte and last 5 bites of the FU Header
                                byte nalHeader = (byte)((firstByte & 0xE0) | (FUHeader & Common.Binary.FiveBitMaxValue));

                                //Store the nalType contained
                                m_ContainedNalTypes.Add(nalHeader);

                                if (nalHeader == 6 || nalHeader == 7 || nalHeader == 8)
                                {
                                    Buffer.WriteByte(0);
                                }

                                //Write the start code
                                Buffer.Write(Media.Codecs.Video.H264.NalUnitType.StartCode, 0, 3);

                                //Write the re-construced header
                                Buffer.WriteByte(nalHeader);
                            }

                            //Allow If End to Write End Sequence?

                            //Write the data of the fragment.
                            Buffer.Write(packetData, offset, fragment_size);
                        }
                    }

                    return;
                }

                default:
                {
                    //Store the nalType contained
                    m_ContainedNalTypes.Add(nalUnitType);

                    if (nalUnitType == 6 || nalUnitType == 7 || nalUnitType == 8)
                    {
                        Buffer.WriteByte(0);
                    }

                    //Write the start code
                    Buffer.Write(Media.Codecs.Video.H264.NalUnitType.StartCode, 0, 3);

                    //Write the nal heaer and data data
                    Buffer.Write(packetData, offset, count - offset);

                    return;
                }
                }
            }
Esempio n. 13
0
        public string ToTextualConvention(FileFormat? format = null)
        {
            try
            {
                StringBuilder sb = new StringBuilder();

                var ts = Timebase.TimeOfDay.Add(TimeSpan.FromMilliseconds(Offset));

                if (IsRtcp) sb.Append(RtpSend.ToTextualConvention(format ?? Format, Media.Rtcp.RtcpPacket.GetPackets(Blob, Pointer + sizeOf_RD_packet_T, BlobLength - sizeOf_RD_packet_T), ts, Source));
                else using (var rtp = new Rtp.RtpPacket(Blob, Pointer + sizeOf_RD_packet_T)) sb.Append(RtpSend.ToTextualConvention(format ?? Format, rtp, ts, Source));

                return sb.ToString();
            }
            catch { throw; }
        }