Click here to Skip to main content
12,626,508 members (39,071 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

64.2K views
7K downloads
51 bookmarked
Posted

C# .NET RTP MJPEG Player

, 6 Jun 2010 GPL3
Rate this:
Please Sign up or sign in to vote.
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

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

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)

Share

About the Author

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

ojundi @ [%s].info

Oz [Jundi]

You may also be interested in...

Comments and Discussions

 
QuestionI cannot run it Pin
Member 1045579510-Jun-14 16:37
memberMember 1045579510-Jun-14 16:37 
QuestionNew Library - Manged Media Aggregation Pin
jfriedman11-Dec-12 11:59
memberjfriedman11-Dec-12 11:59 
QuestionHere is a readable implementation Pin
jfriedman24-Nov-12 10:35
memberjfriedman24-Nov-12 10:35 
GeneralMy vote of 1 Pin
jfriedman24-Nov-12 10:30
memberjfriedman24-Nov-12 10:30 
QuestionWhat player can be used for play the rtp/mjpeg stream? The VLC seems dosen't wrok very well. Pin
zhoushuai24-Oct-12 16:35
memberzhoushuai24-Oct-12 16: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 4:03
memberjfriedman1-Dec-12 4: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 12:00
memberjfriedman11-Dec-12 12:00 
NewsMissing file Pin
wustafae10-May-11 23:53
memberwustafae10-May-11 23:53 
GeneralRe: Missing file Pin
ldussan3-Aug-11 18:20
memberldussan3-Aug-11 18:20 
Generaltwo files are missing Pin
dnumitz10-May-11 22:47
memberdnumitz10-May-11 22:47 
GeneralThis code is compatible to run on Linux using Mono Pin
Oz Jundi19-Jun-10 13:19
memberOz Jundi19-Jun-10 13:19 
GeneralMissing source files Pin
Ozan Yasin Dogan19-Jun-10 11:10
memberOzan Yasin Dogan19-Jun-10 11:10 
GeneralRe: Missing source files Pin
Oz Jundi19-Jun-10 12:31
memberOz Jundi19-Jun-10 12:31 
GeneralRe: Missing source files Pin
KarstenK13-May-14 4:46
memberKarstenK13-May-14 4:46 
GeneralMy vote of 1 Pin
Roey C8-Jun-10 1:11
memberRoey C8-Jun-10 1:11 
GeneralRe: My vote of 1 Pin
defconhaya8-Jun-10 1:28
memberdefconhaya8-Jun-10 1:28 
GeneralRe: My vote of 1 Pin
Oz Jundi19-Jun-10 13:17
memberOz Jundi19-Jun-10 13:17 
GeneralRe: My vote of 1 Pin
wustafae10-May-11 23:52
memberwustafae10-May-11 23:52 
GeneralRe: My vote of 1 Pin
KarstenK13-May-14 4:47
memberKarstenK13-May-14 4: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.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.161205.3 | Last Updated 6 Jun 2010
Article Copyright 2010 by Oz Jundi
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid