Intel RealSenseSDK提供了一种将摄像头流录制到磁盘文件并在未来进行回放的功能。这对于调试和解决应用程序中的摄像头问题非常有用。然而,将原始颜色和深度流录制到磁盘文件会对主机系统的磁盘IO带宽提出挑战。例如,如果颜色配置为RGB32 1920x1080x30fps,深度配置为640x480x30fps,SDK需要大约272MB/s的磁盘IO带宽来将样本写入磁盘。这使得大多数机械硬盘和某些慢速SSD无法胜任此类文件录制工作。
为了解决这个问题,SDK提供了一个实验性功能,即在写入磁盘之前对样本进行压缩。该功能基于颜色样本的H.264编码(仅I帧,恒定QP)和深度样本的无损Lempel-Ziv-Oberhumer(LZO)编码。颜色样本的压缩率大约为10:1,深度样本的压缩率大约为2:1。这样,对于前面的例子,磁盘IO带宽现在减少到大约32MB/s。要使用此功能,需要使用带有最新Intel Iris Graphics驱动程序的Intel® Iris™ Graphics。可以通过以下注册表设置来控制录制功能:
Windows Registry Editor Version
5.00
[HKEY_CURRENT_USER\SOFTWARE\Intel\RSSDK\FileRecording]
"DisableH264Compression"=dword:0
"H264_QPI"=dword:8
"DisableLZOCompression"=dword:0
默认情况下,颜色流启用了H.264压缩,所有其他流启用了LZO压缩。H.264 QPI(I帧量化参数)值的范围是从0(压缩最少)到51(压缩最多)。
重要提示:带有H.264压缩的录制文件只能在具有Intel Iris Graphics的系统上回放。
在C++、C#或Java代码中,使用CaptureManager实例的SetFileName函数来设置文件名和模式(录制或回放)。
C++
pxcStatus SetFileName(pxcCHAR *file, pxcBool record);
C#
pxcmStatus SetFileName(String file, Boolean record);
Java
pxcmStatus SetFileName(string file, boolean record);
参数:
录制流序列到文件的步骤:
以下是录制或回放流序列的示例代码:
C++
void RecordORPlayback(pxcCHAR *file, bool record) {
// 创建一个SenseManager实例
PXCSenseManager *sm=PXCSenseManager::CreateInstance();
// 设置文件录制或回放
sm->QueryCaptureManager()->SetFileName(file,record);
// 选择颜色流
sm->EnableStream(PXCCapture.STREAM_TYPE_COLOR, 640, 480, 0);
// 初始化并录制300帧
sm->Init();
for(int i=0; i<300; i++) {
// 此函数在颜色样本准备好时阻塞
if(sm->AcquireFrame(true) < PXC_STATUS_NO_ERROR)
break;
// 检索样本
PXCCapture::Sample *sample=sm->QuerySample();
// 处理图像sample->color
...
// 获取下一个样本
sm->ReleaseFrame();
}
// 关闭
sm->Release();
}
在录制过程中,样本会随着应用程序的处理而被记录到磁盘上。例如,如果应用程序捕获了未对齐的颜色和深度样本,磁盘上的样本就是未对齐的。如果应用程序对样本进行了对齐,磁盘上的样本就是对齐的。
录制的文件包含一个固定大小的头部,结构如下:
C++
struct Header {
pxcI32 ID; // PXC_UID('R','S','C','F')
pxcI32 fileVersion; // 文件版本
pxcI32 firstFrameOffset; // 第一帧的元数据的字节偏移量。
pxcI32 nstreams; // 流的数量。
pxcI64 frameIndexingOffset; // 可选的帧索引偏移量,如果没有则为零。
PXCSession::CoordinateSystem coordinateSystem; // 坐标系统设置。
pxcI32 reserved[26];
};
任何流的图像帧都按顺序记录在头部之后。它从ChunkFrameMetaData开始,以ChunkFrameData结束。在两者之间,可能有多个配置帧描述图像帧的元数据。元数据应该被解释为与文件头部分定义的内容的差值或变化。这减少了如果某些元数据对所有帧都通用的大小。帧结构如下:
C++
struct StreamFrame {
ChunkFrameMetaData frame_header; // 未定义顺序
ChunkImageMetaData image_meta_data; // 如果有的话。
// 这里可能有更多块。
ChunkFrameData frame_data;
} frames[];
ChunkFrameMetaData结构如下:
C++
struct ChunkFrameMetaData {
ChunkId chunkId=CHUNK_FRAME_META_DATA;
pxcI32 chunkSize=sizeof(metaData);
struct {
pxcI32 frameNumber; // 当前流中的帧号
PXCCapture::StreamType streamType;
pxcI64 timeStamp;
PXCImage::Option options;
} metaData;
};
可以将ChunkImageMetaData放入帧中,其结构如下:
C++
struct ChunkImageMetaData {
ChunkId chunkId=CHUNK_IMAGE_META_DATA;
pxcI32 chunkSize=sizeof(buffer)+sizeof(id);
pxcUID id; // 元数据标识符
pxcBYTE buffer[chunkSize-sizeof(id)];
};
未压缩流的结构如下:
C++
struct ChunkFrameDataUncompressed {
ChunkId chunkId=CHUNK_FRAME_DATA;
pxcI32 chunkSize=sizeof(imageData);
struct {
pxcI32 pitches[PXCImage::NUM_OF_PLANES];
pxcBYTE plane0[pitches[0]*height];
...
pxcBYTE planeN[pitches[PXCImage::NUM_OF_PLANES-1]*height];
} imageData
};
压缩流的结构如下:
C++
struct ChunkFrameDataCompressed {
ChunkId chunkId=CHUNK_FRAME_DATA;
pxcI32 chunkSize=sizeof(imageData);
struct {
pxcI32 pitches[PXCImage::NUM_OF_PLANES];
enum {
H264=0x343632,
LZO=0x4f5a4c,
} CompressionIdentifier;
pxcBYTE compressed_data[];
} imageData
};
RealSense™ SDK捕获模块将在文件中添加配置帧。配置帧可以包含任何由块标识符和块大小识别的任意数据。文件中块数据的呈现顺序并不重要,一般规则是,如果两个块之间存在依赖关系,依赖块应该放在文件的后面。ChunkData结构如下:
C++
struct ChunkData {
enum ChunkId {
CHUNK_DEVICEINFO = 1,
CHUNK_STREAMINFO = 2,
CHUNK_PROPERTIES = 3,
CHUNK_PROFILES = 4,
CHUNK_SERIALIZEABLE = 5,
CHUNK_FRAME_META_DATA = 6,
CHUNK_FRAME_DATA = 7,
CHUNK_IMAGE_META_DATA = 8,
CHUNK_FRAME_INDEXING = 9,
}chunkId;
pxcI32 chunkSize;
pxcBYTE chunkData[chunkSize];
} chunks[];
文件中需要一些配置帧:
播放流序列文件的步骤:
对于文件回放,SDK会立即创建Capture实例(使用QueryCapture函数),以便应用程序可以查询录制内容的功能。可以如下配置SDK文件回放行为:
函数 | 默认值 | 描述 |
---|---|---|
SetPause | false | 如果为true,则文件回放会重复返回当前帧的相同样本。 |
SetRealtime | true | 如果为true,则文件回放会根据样本的时间戳(presentation time)返回当前帧样本。如果为false,则文件回放会立即返回样本。 |
C++
PXCSenseManager* sm = PXCSenseManager::CreateInstance();
// 设置文件回放名称
sm->QueryCaptureManager()->SetFileName(filename, false);
// 启用流并初始化
sm->EnableStream(PXCCapture::STREAM_TYPE_COLOR, 0, 0);
sm->Init();
// 设置realtime=false和pause=true
sm->QueryCaptureManager()->SetRealtime(false);
sm->QueryCaptureManager()->SetPause(true);
// 流循环
for(int i = 0; i < nframes; i += 3) {
// 设置每3帧数据工作一次
sm->QueryCaptureManager()->SetFrameByIndex(i);
sm->FlushFrame();
// 准备帧就绪
pxcStatus sts = sm->AcquireFrame(true);
if(sts < PXC_STATUS_NO_ERROR)
break;
// 检索样本并处理它。图像在sample->color中。
PXCCapture::Sample* sample = sm->QuerySample();
....
// 继续处理下一帧
sm->ReleaseFrame();
}
// 清理
sm->Release();