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;
public static readonly int rtp_length = 12;
public int version;
public byte padding;
public byte extension;
public int csrc_count;
public bool marker;
public int payload_type;
public ushort sequence_no;
public uint timestamp;
public uint source_id;
public RtpPacket(byte[] _data)
{
decode(_data);
}
public void decode(byte[] data)
{
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;
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;
public int fragment_offset;
public int next_fragment_offset;
public int jpeg_type;
public int q;
public int width;
public int height;
public ushort restart_interval;
public ushort f;
public ushort l;
public ushort restart_count;
public ushort mbz;
public ushort precision;
public ushort qlength;
public byte[] qtable;
public static int max_size = 8192 * 250;
private byte[] _frag;
private ImageProcessing imgProceesor;
private unsafe byte* buffer_ptr;
public JPEGFrame()
{
_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)
{
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);
(data, offset + 8) & 0x3FF);
if (width == 0)
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);
_initialized = true;
tmp = null;
GC.Collect();
}
else
{
_frag[0] = data[15];
_frag[1] = data[14];
_frag[2] = data[13];
_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);
(data, offset + 8) & 0x3FF);
}
return (next_fragment_offset == fragment_offset);
}
public unsafe bool Write(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);
}
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;
}
~JPEGFrame()
{
Dispose(true);
}
protected virtual void Dispose(bool isDisposing)
{
if (_alreadyDisposed)
return;
if (isDisposing)
{
if(_frame_img != null)
_frame_img.Dispose();
if(_stream_buffer != null)
_stream_buffer.Close();
_initialized = false;
_frame_img = null;
_stream_buffer = null;
qtable = null;
_buffer = null;
_alreadyDisposed = true;
}
}
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