Click here to Skip to main content
15,893,381 members
Articles / Programming Languages / C#

C# .NET RTP MJPEG Player

Rate me:
Please Sign up or sign in to vote.
3.73/5 (11 votes)
5 Jun 2010GPL3 107.4K   8.7K   52   19
Implementation of RTP and MJPEG over RTP, supports large frames and multicasting
screen_shot2.JPG

Introduction

This is an implementation of RTP/MJPEG protocol in C#. I wrote this code back in 2005.

Background

This code was used for Elphel network cameras, multicasting RTP/MJPEG but it should be compatible with almost all RTP/MJPEG technologies. This code was written from reading the related RFCs.

Using the Code

RtpPacket.cs

C#
using System;
using System.Collections.Generic;
using System.Text;

namespace RTPLib
{
    public class RtpPacket
    {
        public static readonly int rtp_version = 2; /* the version of RTP supported */
        public static readonly int rtp_length = 12; /* fixed RTP packet header size */
        public int version; /* the version of RTP should be 2 */ 
        public byte padding; /* the padding flag **/
        public byte extension; /* the extension flag - if any extra headers 
				for higher protocol */
        public int csrc_count; /* the source count */
        public bool marker; /* the marker status */
        public int payload_type;    /* the type of payload  */
        public ushort sequence_no;  /* the sequence number of the RTP packet */
        public uint timestamp;  /* the timestamp of the RTP packet */
        public uint source_id;  /* the source id  of the RTP packet */
        public RtpPacket(byte[] _data)
        {
            decode(_data);
        }
        public void decode(byte[] data)
        {
            /*byte[] data = new byte[12];
            Array.Copy(_data, data, 12);*/ /* there should be no need 
					to copy the header */
            version = data[0] >> 6;
            padding = (byte)(0x1 & (data[0] >> 5));
            extension = (byte)(0x1 & (data[0] >> 4));
            csrc_count = 0x1F & (data[0]);
            marker = ((data[1] >> 7) == 1);
            payload_type = data[1] & 0x7f; /* we will assume it's 26 -> RTP/MJPEG */
            sequence_no = Utils.HostToNetworkOrderShort
			(System.BitConverter.ToUInt16(data, 2));
            timestamp = Utils.SwapUnsignedInt(System.BitConverter.ToUInt32(data, 4));
            source_id = Utils.SwapUnsignedInt(System.BitConverter.ToUInt32(data, 8));
        }
    }
}

JPEGFrame.cs

C#
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Drawing;
using System.Runtime.InteropServices;
using System.ComponentModel;
namespace RTPLib
{
    public unsafe class JPEGFrame : IDisposable
    {
        private byte[] _buffer;
        private int _offset;
        private MemoryStream _stream_buffer;
        private bool _alreadyDisposed;
        private Image _frame_img;
        private bool _initialized;

        public int type_specific; /** 8 bits **/
        public int fragment_offset; /** 24 bits **/
        public int next_fragment_offset;
        /** 0-63 fixed q no restart markers **/
        public int jpeg_type; /** 8 bits **/
        /** 64-127 Restart Marker header appears immediately 
	following the main JPEG header **/
        public int q;   /** 8 bits **/ /** multiply by 8 **/
        /** 128-255 qtable is present and right after the 
	restart marker header if present **/
        /** 128 to 254 q is static to be read only once**/
        public int width; /** 8 bits **/ /** multiply by 8 **/
        public int height; /** 8 bits **/ /** multiply by 8 **/

        /** Restart Marker header **/
        public ushort restart_interval; /** 16 bits **/
        public ushort f; /** 1 bit **/
        public ushort l; /** 1 bit **/
        public ushort restart_count; /** 14 bits **/ /** f and l must be set to 1 
	and 0x3FFF if set reassemble the frame before decoding **/

        /** Quantization Table header **/
        /** must be present after the restart marker header if it is present**/
        public ushort mbz; /** 8 bits **/
        public ushort precision; /** 8 bits **/
        public ushort qlength; /** 16 bits **/
        public byte[] qtable; /** qtables **/
        public static int max_size = 8192 * 250;

        private byte[] _frag; /** hold the fragment offset number for conversion **/
        private ImageProcessing imgProceesor;
        private unsafe byte* buffer_ptr;

        public JPEGFrame()
        {
            //qtable = new byte[1024];
            //imgProceesor = new ImageProcessing();
            _buffer = new byte[max_size];
            unsafe
            {
                fixed (byte* buf_ptr = _buffer)
                    buffer_ptr = buf_ptr;
            }
            _frag = new byte[4];
            _initialized = false;
            _offset = 0;
        }
        public bool Decode(byte * data, int offset) //Decode(ref byte[] data, int offset)
        {
            if (_initialized == false)
            {
                type_specific = data[offset + 0];
                _frag[0] = data[offset + 3];
                _frag[1] = data[offset + 2];
                _frag[2] = data[offset + 1];
                _frag[3] = 0x0;
                fragment_offset = System.BitConverter.ToInt32(_frag, 0);
                jpeg_type = data[offset + 4];
                q = data[offset + 5];
                width = data[offset + 6];
                height = data[offset + 7];
                _frag[0] = data[offset + 8];
                _frag[1] = data[offset + 9];
                restart_interval = (ushort)(System.BitConverter.ToUInt16
					(_frag, 0) & 0x3FF);
                //restart_interval = (ushort)(System.BitConverter.ToUInt16
					(data, offset + 8) & 0x3FF);
                if (width == 0) /** elphel 333 full image size more than 
				just one byte less that < 256 **/
                    width = 256;
                byte[] tmp = new byte[1024];
                _offset = Utils.MakeTables(q, jpeg_type, height, width, tmp);
                qtable = new byte[_offset];
               
                Array.Copy(tmp, 0, _buffer, 0, _offset);
                Array.Copy(tmp, 0, qtable, 0, _offset); //not needed but 
                _initialized = true;
                tmp = null;
                /*Utils.StaticDispose();
                Utils.Dispose();*/
                GC.Collect();
            }
            else
            {
                _frag[0] = data[15]; //12 + 3
                _frag[1] = data[14]; //12 + 2
                _frag[2] = data[13]; //12 + 1]
                _frag[3] = 0x0;
                fragment_offset = System.BitConverter.ToInt32(_frag, 0);
                _frag[0] = data[offset + 8];
                _frag[1] = data[offset + 9];
                restart_interval = (ushort)(System.BitConverter.ToUInt16
					(_frag, 0) & 0x3FF);
                //restart_interval = (ushort)(System.BitConverter.ToUInt16
					(data, offset + 8) & 0x3FF);
            }

            return (next_fragment_offset == fragment_offset);
        }
        public unsafe bool Write(byte * data, 
	int size, out bool sync) //Write(ref byte[] data, int size,out bool sync)
        {
            if (Decode(data, 12))
            {
                    for (int i = 20; i < size;)
                    {
                        buffer_ptr[_offset] = data[i++];
                        ++_offset;
                        buffer_ptr[_offset] = data[i++];
                        ++_offset;
                    }
                size -= 20;
                next_fragment_offset += size;
                sync = true;
                return ((data[1] >> 7) == 1); 
            }
            /*
            if (Decode(data, 12))
            {
                size -= 20;
                Array.Copy(data, 20, buffer, _offset, size);
                next_fragment_offset += size;
                _offset += size;
                sync = true;
                return ((data[1] >> 7) == 1);
            }*/
            else
            {
                _offset = qtable.Length;
                next_fragment_offset = 0;
                sync = false;
                return false;
            }
        }
        public Image GetFrame(out int motion_level)
        {
            if (_initialized == false)
                throw new Exception();
            _stream_buffer = new MemoryStream(_buffer, 0, _offset, false);
            _frame_img = Image.FromStream(_stream_buffer, false, true);
            _offset = qtable.Length;
            motion_level = 0;
            next_fragment_offset = 0;
            _stream_buffer.Close();
            _stream_buffer.Dispose();
            _stream_buffer = null;
            return _frame_img;

            /*stream_buffer = new MemoryStream(_offset);
            stream_buffer.Write(buffer, 0, _offset); */
            //stream_buffer = new MemoryStream(10);
           /* GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Normal);
            IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
            frame_img = new Bitmap(width * 8, height * 8, 
		(width * 8), System.Drawing.Imaging.PixelFormat.Format24bppRgb, ptr);
            */
             //Console.WriteLine(frame_img.PixelFormat.ToString() + 
             //" " + Image.GetPixelFormatSize(frame_img.PixelFormat) / 8);
           
            /*GC.ReRegisterForFinalize(stream_buffer);*/
            //Bitmap map = frame_img as Bitmap;    
            //imgProceesor.CompareUnsafeFaster(out motion_level, ref frame_img);
            //motion_level = imgProceesor.MotionLevel;
            //Console.WriteLine("motion " + motion_level);   
        }
        ~JPEGFrame()
        {
            Dispose(true);
           
        }
       protected virtual void Dispose(bool isDisposing)
       {
           if (_alreadyDisposed)
               return;
           // Don't dispose more than once.          
           if (isDisposing)
           {
               // TODO: free managed resources here.
               if(_frame_img != null)
                    _frame_img.Dispose();
               if(_stream_buffer != null)
                    _stream_buffer.Close();
               //stream_buffer.Dispose();
                _initialized = false;
               _frame_img = null;
               _stream_buffer = null;
               qtable = null;
               _buffer = null;
               _alreadyDisposed = true;
               
           }
           // TODO: free unmanaged resources here.
           // Set disposed flag:          
       }
       public void Dispose()
       {
           Dispose(true);
           GC.SuppressFinalize(true);
       }
        public override string ToString()
        {
            string ret = "type_specific= " + type_specific + "\r\n";
            ret += "fragment_offset= " + fragment_offset + "\r\n";
            ret += "type=" + jpeg_type + "\r\n";
            ret += "q=" + q + "\r\n";
            ret += "width=" + width + "\r\n";
            ret += "height=" + height;
            if (jpeg_type > 63)
            {
                ret += "\r\n" + "restart_interval=" + restart_interval + "\r\n";
                ret += "f=" + f + " l=" + l + "\r\n";
                ret += "restart_count=" + restart_count;
            }
            return ret;
        }
    }
}

Notes

Please understand that I wrote this code back in 2005 and I haven't made any updates. If you think you can rearrange the code and provide comments, please let me know. I also hope for RTP/H.264 managed API implementation of existing open source projects.

Points of Interest

I am interested in creating Internet Exchange Points.

History

  • First release 2005
screen_shot1.JPG

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Engineer TECO GROUP - Jundi Electrical Industry
United States United States
if you have any questions please send an email :

ojundi @ [%s].info

Oz [Jundi]

Comments and Discussions

 
QuestionI cannot run it Pin
wind snail10-Jun-14 15:37
professionalwind snail10-Jun-14 15:37 
QuestionNew Library - Manged Media Aggregation Pin
jfriedman11-Dec-12 10:59
jfriedman11-Dec-12 10:59 
QuestionHere is a readable implementation Pin
jfriedman24-Nov-12 9:35
jfriedman24-Nov-12 9:35 
Here is the same thing only readable...

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Media.Rtp
{
    /// <summary>
    /// Implements RFC2435
    /// </summary>
    public class JpegFrame : RtpFrame
    {

        #region Statics

        static byte[] CreateStartOfInformation()
        {
            return new byte[] { 0xff, 0xd8 };
        }

        static byte[] CreateJFIFHeader(uint type, uint width, uint height, ArraySegment<byte> tables, uint dri)
        {
            List<byte> result = new List<byte>();
            result.AddRange(CreateStartOfInformation());

            result.Add(0xff);
            result.Add(0xe0);//AppFirst
            result.Add(0x00);
            result.Add(0x10);//length
            result.Add((byte)'J');
            result.Add((byte)'F');
            result.Add((byte)'I');
            result.Add((byte)'F');
            result.Add(0x00);

            result.Add(0x01);//Version Major
            result.Add(0x01);//Version Minor

            result.Add(0x00);//Units

            result.Add(0x00);//Horizontal
            result.Add(0x01);

            result.Add(0x00);//Vertical
            result.Add(0x01);

            result.Add(0x00);//No thumb
            result.Add(0x00);

            if (dri > 0)
            {
                result.AddRange(CreateDataRestartIntervalMarker(dri));
            }

            result.AddRange(CreateQuantizationTablesMarker(tables));

            result.Add(0xff);
            result.Add(0xc0);//SOF
            result.Add(0x00);
            result.Add(0x11);
            result.Add(0x08);
            result.Add((byte)(height >> 8));
            result.Add((byte)height);
            result.Add((byte)(width >> 8));
            result.Add((byte)width);
            
            result.Add(0x03);
            result.Add(0x01);
            result.Add((byte)(type > 0 ? 0x22 : 0x21));
            result.Add(0x00);

            result.Add(0x02);
            result.Add(0x11);
            result.Add(0x01);

            result.Add(0x03);
            result.Add(0x11);
            result.Add(0x01);

            //Huffman Tables
            result.AddRange(CreateHuffmanTableMarker(lum_dc_codelens, lum_dc_symbols, 0, 0));
            result.AddRange(CreateHuffmanTableMarker(lum_ac_codelens, lum_ac_symbols, 0, 1));
            result.AddRange(CreateHuffmanTableMarker(chm_dc_codelens, chm_dc_symbols, 1, 0));
            result.AddRange(CreateHuffmanTableMarker(chm_ac_codelens, chm_ac_symbols, 1, 1));
            
            result.Add(0xff);
            result.Add(0xda);//Marker SOS
            result.Add(0x00);
            result.Add(0x0c);
            result.Add(0x03);
            result.Add(0x01);
            result.Add(0x00);
            result.Add(0x02);
            result.Add(0x11);
            result.Add(0x03);
            result.Add(0x11);
            result.Add(0x00);
            result.Add(0x3f);
            result.Add(0x00);

            return result.ToArray();
        }

        // The default 'luma' and 'chroma' quantizer tables, in zigzag order:
        static byte[] defaultQuantizers = new byte[]
        {
           // luma table:
           16, 11, 12, 14, 12, 10, 16, 14,
           13, 14, 18, 17, 16, 19, 24, 40,
           26, 24, 22, 22, 24, 49, 35, 37,
           29, 40, 58, 51, 61, 60, 57, 51,
           56, 55, 64, 72, 92, 78, 64, 68,
           87, 69, 55, 56, 80, 109, 81, 87,
           95, 98, 103, 104, 103, 62, 77, 113,
           121, 112, 100, 120, 92, 101, 103, 99,
           // chroma table:
           17, 18, 18, 24, 21, 24, 47, 26,
           26, 47, 99, 66, 56, 66, 99, 99,
           99, 99, 99, 99, 99, 99, 99, 99,
           99, 99, 99, 99, 99, 99, 99, 99,
           99, 99, 99, 99, 99, 99, 99, 99,
           99, 99, 99, 99, 99, 99, 99, 99,
           99, 99, 99, 99, 99, 99, 99, 99,
           99, 99, 99, 99, 99, 99, 99, 99
        };     

        /// <summary>
        /// Creates a Luma and Chroma Table using the default quantizer
        /// </summary>
        /// <param name="Q">The quality</param>
        /// <returns>64 luma bytes and 64 chroma</returns>
        static byte[] CreateQuantizationTables(uint Q)
        {
            int factor = (int)Q;
            int q;

            if (Q < 1) factor = 1;
            else if (Q > 99) factor = 99;

            if (Q < 50)
            {
                q = 5000 / factor;
            }
            else
            {
                q = 200 - factor * 2;
            }
            byte[] resultTables = new byte[128];
            for (int i = 0; i < 128; ++i)
            {
                int newVal = (defaultQuantizers[i] * q + 50) / 100;
                if (newVal < 1) newVal = 1;
                else if (newVal > 255) newVal = 255;
                resultTables[i] = (byte)newVal;
            }
            return resultTables;
        }

        static byte[] CreateQuantizationTablesMarker(ArraySegment<byte> tables)
        {
            List<byte> result = new List<byte>();

            int tableSize = tables.Count / 2;

            //Luma

            result.Add(0xff);
            result.Add(0xdb);

            result.Add(0x00);
            result.Add((byte)(tableSize + 3));
            result.Add(0x00);

            for (int i = 0, e = tableSize; i < e; ++i)
            {
                result.Add(tables.Array[tables.Offset + i]);
            }

            //Chroma

            result.Add(0xff);
            result.Add(0xdb);

            result.Add(0x00);
            result.Add((byte)(tableSize + 3));
            result.Add(0x01);

            for (int i = tableSize, e = tables.Count; i < e; ++i)
            {
                result.Add(tables.Array[tables.Offset + i]);
            }

            return result.ToArray();

        }

        static byte[] lum_dc_codelens = {
        0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
        };

        static byte[] lum_dc_symbols = {
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
        };

        static byte[] lum_ac_codelens = {
        0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d,
        };

        static byte[] lum_ac_symbols = {
        0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
        0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
        0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
        0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
        0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
        0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
        0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
        0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
        0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
        0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
        0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
        0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
        0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
        0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
        0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
        0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
        0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
        0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
        0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
        0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
        0xf9, 0xfa,
        };

        static byte[] chm_dc_codelens = {
        0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
        };

        static byte[] chm_dc_symbols = {
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 
        };

        static byte[] chm_ac_codelens = {
        0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77,
        };

        static byte[] chm_ac_symbols = {
        0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
        0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
        0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
        0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
        0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
        0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
        0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
        0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
        0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
        0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
        0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
        0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
        0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
        0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
        0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
        0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
        0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
        0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
        0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
        0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
        0xf9, 0xfa,
        };

        static byte[] CreateHuffmanTableMarker(byte[] codeLens, byte[] symbols, int tableNo, int tableClass)
        {
            List<byte> result = new List<byte>();
            result.Add(0xff);
            result.Add(0xc4);
            result.Add(0x00);
            result.Add((byte)(3 + codeLens.Length + symbols.Length));
            result.Add((byte)((tableClass << 4) | tableNo));
            result.AddRange(codeLens);
            result.AddRange(symbols);
            return result.ToArray();
        }

        static byte[] CreateDataRestartIntervalMarker(uint dri)
        {
            return new byte[] { 0xff, 0xdd, 0x00, 0x04, (byte)(dri >> 8), (byte)(dri) };
        }

        #endregion

        internal System.IO.MemoryStream Buffer = new System.IO.MemoryStream();

        internal System.Drawing.Image Image;

        public JpegFrame() : base(26) { }

        public JpegFrame(RtpFrame f) : base(f) { if (PayloadType != 26) throw new ArgumentException("Expected the payload type 26, Found type: " + f.PayloadType); }

        /// <summary>
        /// //Writes the packets to a memory stream and created the default header and quantization tables if necessary.
        /// </summary>
        internal void ProcessPackets()
        {
            ArraySegment<byte> tables = default(ArraySegment<byte>);            
            uint FragmentOffset, TypeSpecific, Type, Quality, Width, Height, DataRestartInterval = 0;
            for (int i = 0, e = this.Packets.Count, last = e - 1; i < e; ++i)
            {
                int offset = 0;

                RtpPacket packet = this.Packets[i];

                TypeSpecific = (uint)(packet.Payload[offset++]);

                //FragmentOffset = (uint)(packet.Payload[offset++] | packet.Payload[offset++] << 8 | packet.Payload[offset++] << 16);
                FragmentOffset = (uint)(packet.Payload[offset++] << 16 | packet.Payload[offset++] << 8 | packet.Payload[offset++]);

                //Ensure start packet
                if (i == 0 && FragmentOffset != 0) continue;

                Type = (uint)(packet.Payload[offset++]);
                Quality = (uint)packet.Payload[offset++];
                Width = (uint)(packet.Payload[offset++] * 8);
                Height = (uint)(packet.Payload[offset++] * 8);                

                //Only occur in the first packet
                if (FragmentOffset == 0)
                {
                    //If There is already a StartOfInformation present just copy
                    if (packet.Payload[offset] == 0xff && packet.Payload[offset + 1] == 0xd8)
                    {
                        goto WritePacket;
                    }

                    //Get the restart interval is present
                    if (Type > 63)
                    {
                        DataRestartInterval = (uint)(packet.Payload[offset++] << 8 | packet.Payload[offset++]);
                    }

                    //Get the Q Tables if present
                    if (Quality > 127)
                    {
                        uint MBZ = (uint)(packet.Payload[offset++]);//Should only proceed if MBZ > 0?
                        uint Precision = (uint)(packet.Payload[offset++]);//Wasted read
                        uint Length = (uint)(packet.Payload[offset++] << 8 | packet.Payload[offset++]);
                        if(Length > 0)
                        {
                            tables = new ArraySegment<byte>(packet.Payload, offset, (int)Length);
                            offset += (int)Length;
                        }
                        else
                        {
                            tables = new ArraySegment<byte>(CreateQuantizationTables(Quality));
                        }
                    }

                    //Write the header to the buffer
                    byte[] header = CreateJFIFHeader(Type, Width, Height, tables, DataRestartInterval);
                    Buffer.Write(header, 0, header.Length);

                }
                
            WritePacket:    //Copy rest of data to buffer
                Buffer.Write(packet.Payload, offset, packet.Payload.Length - offset);

                //Check to see if EOI is present or needs to be added on the last packet
                if (i == last && packet.Payload[packet.Payload.Length - 2] != 0xff && packet.Payload[packet.Payload.Length - 1] != 0xd9)
                {
                    Buffer.WriteByte(0xff);
                    Buffer.WriteByte(0xd9);
                }

            }
            
            //Go back to the beginning
            Buffer.Seek(0, System.IO.SeekOrigin.Begin);            
        }

        /// <summary>
        /// Creates a image from the processed packets in the memory stream
        /// </summary>
        /// <returns>The image created from the packets</returns>
        public System.Drawing.Image ToImage()
        {
            try
            {
                if (Image != null) return Image;
                ProcessPackets();
                return Image = System.Drawing.Image.FromStream(Buffer, false, true);
            }
            catch { throw; }
            finally
            {
                Buffer.Dispose();
                Buffer = null;
            }

        }
    }
}

GeneralMy vote of 1 Pin
jfriedman24-Nov-12 9:30
jfriedman24-Nov-12 9:30 
QuestionWhat player can be used for play the rtp/mjpeg stream? The VLC seems dosen't wrok very well. Pin
zhoushuai24-Oct-12 15:35
zhoushuai24-Oct-12 15:35 
AnswerRe: What player can be used for play the rtp/mjpeg stream? The VLC seems dosen't wrok very well. Pin
jfriedman1-Dec-12 3:03
jfriedman1-Dec-12 3:03 
AnswerRe: What player can be used for play the rtp/mjpeg stream? The VLC seems dosen't wrok very well. Pin
jfriedman11-Dec-12 11:00
jfriedman11-Dec-12 11:00 
NewsMissing file Pin
wustafae10-May-11 22:53
wustafae10-May-11 22:53 
GeneralRe: Missing file Pin
ldussan3-Aug-11 17:20
ldussan3-Aug-11 17:20 
Generaltwo files are missing Pin
dnumitz10-May-11 21:47
dnumitz10-May-11 21:47 
GeneralThis code is compatible to run on Linux using Mono Pin
Oz Jundi19-Jun-10 12:19
Oz Jundi19-Jun-10 12:19 
GeneralMissing source files Pin
Ozan Yasin Dogan19-Jun-10 10:10
Ozan Yasin Dogan19-Jun-10 10:10 
GeneralRe: Missing source files Pin
Oz Jundi19-Jun-10 11:31
Oz Jundi19-Jun-10 11:31 
GeneralRe: Missing source files Pin
KarstenK13-May-14 3:46
mveKarstenK13-May-14 3:46 
GeneralMy vote of 1 Pin
Roey C8-Jun-10 0:11
Roey C8-Jun-10 0:11 
GeneralRe: My vote of 1 Pin
defconhaya8-Jun-10 0:28
defconhaya8-Jun-10 0:28 
GeneralRe: My vote of 1 Pin
Oz Jundi19-Jun-10 12:17
Oz Jundi19-Jun-10 12:17 
GeneralRe: My vote of 1 Pin
wustafae10-May-11 22:52
wustafae10-May-11 22:52 
GeneralRe: My vote of 1 Pin
KarstenK13-May-14 3:47
mveKarstenK13-May-14 3:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.