我有一个RTP流套接字,接收来自三星网络摄像机的JPEG流。
我不太了解JPEG格式是如何工作的,但我知道这个传入的JFIF或JPEG流给了我JPEG头部信息。
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type-specific | Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Q | Width | Height |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
and then
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Restart Interval |F|L| Restart Count |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
and then in the first packet, there is this header
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| MBZ | Precision | Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Quantization Table Data |
| ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
我认为我已经正确解析了它们,这是一段代码片段,它展示了如何存储一个JPEG流数据包。
int extraOff=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);
if (width == 0) /** elphel 333 full image size more than just one byte less that < 256 **/
width = 256;
byte jpegMBZ = (byte)(data[offset + 12]);
byte jpegPrecision = (byte)(data[offset + 13]);
int jpegLength = (int)((data[offset + 14]) * 256 + data[offset + 15]);
byte[] tableData1 = new byte[64];
byte[] tableData2 = new byte[64];
for (int i = 0; i < 64; ++i)
{
tableData1[i] = data[offset + 16 + i];
tableData2[i] = data[offset + 16+64 + i];
}
byte[] tmp = new byte[1024];
_offset = Utils.MakeHeaders(tmp,jpeg_type, width, height, tableData1, tableData2, 0);
qtable = new byte[_offset];
Array.Copy(tmp, 0, _buffer, 0, _offset);
_initialized = true;
tmp = null;
GC.Collect();
extraOff = jpegLength + 4 ;
}
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);
extraOff = 0;
}
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 = 24 + extraOff; i < size; )
buffer_ptr[_offset++] = data[i++];
size -= 24+extraOff;
next_fragment_offset += size;
sync = true;
return ((data[1] >> 7) == 1);
}
else
{
_initialized = false;
_offset = qtable.Length;
next_fragment_offset = 0;
sync = false;
return false;
}
}
我遇到的问题是,通过将JPEG流连接起来成功保存到硬盘上的JPEG文件未能正确显示整个流,所有的图像预览器都只显示前两个传入的数据包,但是其余部分都是灰色的。 我认为这意味着从第三个到最后一个RTP包的数据未能正确解析或保存。
这是我得到的帧: http://rectsoft.net/ideerge/zzz.jpg
编辑:这是我调用Write函数的方式
size = rawBuffer.Length;
if (sync == true)
{
unsafe
{
fixed (byte* p = rawBuffer)
{
if (_frame.Write(p, size, out sync)) //if (_frame.Write(ref _buffer, size, out sync))
{
// i save my buffer to file here
}
}
}
}
else if ((rawBuffer[1] >> 7) == 1)
{
sync = true;
}
我的UDP接收函数填充了rawBuffer,它的行为与我处理h264流的方式完全相同,并且看起来与我在VLC上从WIRESHARK捕获的内容完全一样。
Array.Copy()
调用中提到的_buffer
很好奇--为什么有两个?您只将tmp
设置为null
,而没有将qtable
设置为null
,这是有意为之吗? - sarnold