前言
最近遇到一个需求,需要同步摄像机画面并在摄像机画面上叠加虚拟图像,有点虚拟演播室的需求。于是对摄像头Free-D协议传回的UDP协议进行解析。
参考文献
设备和环境
硬件:Lumens VC-H717KN
Visual Studio(数据接收)
摄像机设置
按照说明书捣鼓捣鼓(不同摄像机设置不一样)
Lumens的话,通讯笔者的话,关闭NDI才能接收到FreeD协议传输的数据。不知道为什么会冲突,官方没说明。
UDP获取PTZ数据
UDP直接采集的数据是29字节的十六进制数据。
数据包一共有
29字节
(尖括号的内容为一字节), 包括了摄像机的八个参数数据,这八个参数主要包括摄像机的六个外部参数和两个内部参数。外部的六个参数为:摄像机的空间位置( x,y,z),摄像机自身水平(pan)、垂直(tilt)和自身旋转 (roll)的数值。内部两个参数包括摄像机镜头的缩放(zoom)和对焦(focus)数值。
<D1> 消息类型 <CA> 摄像机 ID <PH><PM><PL> 水平 Pan 角度 <TH><TM><TL> 垂直 Tilt 角度 <RH><RM><RL> 旋转 Roll 角度 <XH><XM><XL> 摄像机的 X 坐标 <YH><YM><YL> 摄像机的 Y 坐标 <HH><HM><HL> 摄像机的 H 坐标 (Z 坐标) <ZH><ZM><ZL> 缩放(zoom)数值 <FH><FM><FL> 对焦时的焦点(focus)数值 <SH><SL> 由用户保留,以做它用 <CK> 校验位
我们主要解析的是第三和第四项数据。
B.2 摄像机平移角度
摄像机平移角度定义为 Y 轴与摄像机光轴在水平 (XY) 平面上的投影之间的角度。零值
对应于相机在正 Y 方向上看和一个正值
表示向右平移(即从以上)。
该值以度数表示为 24 位二进制补码有符号数,其中最高有效位(位 23)是符号位,接下来的 8 位(位 22 到 15)是
整数部分和其余位(位 14 到 0)是小数部分;或者,
这可以被认为是一个以 1/32768 度为单位的有符号整数值。值的范围是从 -180.0 度(A60000 hex)到 +180.0 度(5A0000 hex)。
以水平Pan角度解析为例:
我测试过:直接按照官方解析会存在一定的偏差。
然后我就根据寻找规律,如下:
//将三位十六进制合并成一个完整的十六进制数
//转成2进制数,第0位为符号位 1表示正数 0表示负数 1010 1010 1111 1111 1111 1111
//负数直接转十进制除于32768 0101 0101 0000 0000 0000 0000
//正数去除符号位 得补码后转十进制除于32768 0011 0001 0011 0011 0011
具体实现,默认得到UDP中的三字节Pan的十六进制数据。
static void Main(string[] args)
{
int[] num1 = DecToBin(170);
int[] num2 = DecToBin(255);
int[] num3 = DecToBin(255);
int[] binSlice = SpliceBin(num1, num2, num3);
//判断符号位正负
if (binSlice[0] == 0)
{
double num = BinToDec(binSlice);
Console.Write(-num);
}
else
{
//补码
for (int i = 0; i < 24; i++)
{
binSlice[i] = binSlice[i] == 0 ? 1 : 0;
}
int len = binSlice.Length;
binSlice[len - 1] += 1;
for (int i = 0; i < len; i++)
{
if (binSlice[len - 1 - i] > 1)
{
binSlice[len - 1 - i] = 0;
binSlice[len - i] += 1;
}
}
double num = BinToDec(binSlice);
Console.Write(num);
}
}
/// <summary>
/// 拼接三个二进制数据
/// </summary>
/// <returns>返回一个二十四位的二进制数据</returns>
static int[] SpliceBin(int[] bin1, int[] bin2, int[] bin3)
{
int[] bin = new int[24];
for (int i = 0; i < 8; i++)
{
bin[i] = bin1[i];
}
for (int i = 0; i < 8; i++)
{
bin[i + 8] = bin2[i];
}
for (int i = 0; i < 8; i++)
{
bin[i + 16] = bin3[i];
}
return bin;
}
/// <summary>
/// 十进制转八位的二进制 以二进制倒序数组返回
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
static int[] DecToBin(int value)
{
int temp = value;
int index = 0;
int[] tempInt = new int[8];
int[] result = new int[8];
while (temp > 0)
{
tempInt[index] = temp % 2;
temp /= 2;
index++;
}
for (int i = 0; i < 8; i++)
{
result[i] = tempInt[7 - i];
}
return result;
}
/// <summary>
/// 二进制转十进制
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
static double BinToDec(int[] value)
{
int num = 0;
for (int i = 0; i < value.Length; i++)
{
num += (int)(value[i] * Math.Pow(2, 23 - i));
}
return (float)num / 32768;
}