如何在 Intel® RealSense™ SDK 中录制和回放流式传输序列
Intel® RealSense™ SDK 为开发人员提供了一项新功能,可以将摄像机流序列录制到磁盘文件以供将来播放。此功能在调试和故障排除应用程序中的摄像机问题时非常有用。
Intel® RealSense™ SDK 为开发人员提供了一项新功能,可以将摄像机流序列录制到磁盘文件以供将来播放。此功能在调试和故障排除应用程序中的摄像机问题时非常有用。
将原始彩色和深度流录制到磁盘文件会对主机系统的磁盘 IO 带宽提出挑战。例如,对于彩色配置 RGB32 1920x1080x30fps 和深度配置 640x480x30fps,SDK 需要约 272MB/s 的磁盘 I/O 带宽才能将样本写入磁盘。这使得大多数旋转硬盘和某些慢速 SSD 无法进行此类文件录制作业。
为了解决这个问题,SDK 提供了一项实验性功能,可以在将样本写入磁盘之前对其进行压缩。该功能基于 H.264 编码(仅 I 帧,恒定 QP)彩色样本和无损 Lempel–Ziv–Oberhumer (LZO) 编码深度样本。彩色样本的压缩率约为 10:1,深度样本的压缩率约为 2:1。对于前面的示例,磁盘 I/O 带宽现已降至约 32MB/s。要使用此功能,您需要使用带有最新 Intel Iris 图形驱动程序的 Intel® Iris™ 图形。您可以使用以下注册表设置来控制录制功能
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 图形的系统上播放。
在 C++、C# 或 Java 代码中,使用 `CaptureManager` 实例的 `SetFileName` 来设置文件名和模式(录制或播放)
C++ pxcStatus SetFileName(pxcCHAR *file, pxcBool record); 参数 |
将流序列录制到文件的步骤
- 使用 `CaptureManager` 实例的 `SetFileName` 函数。
- 提供文件名并将录制模式设置为 true。文件名没有限制,除了在录制模式下,文件必须是可写的。
- 如果要录制未压缩的流,请更改注册表设置
以下是录制或播放流序列的示例代码
void RecordORPlayback(pxcCHAR *file, bool record) {
// Create a SenseManager instance
PXCSenseManager *sm=PXCSenseManager::CreateInstance();
// Set file recording or playback
sm->QueryCaptureManager()->SetFileName(file,record);
// Select the color stream
sm->EnableStream(PXCCapture.STREAM_TYPE_COLOR,640,480,0);
// Initialize and Record 300 frames
sm->Init();
for (int i=0;i<300;i++) {
// This function blocks until a color sample is ready
if (sm->AcquireFrame(true)<PXC_STATUS_NO_ERROR) break;
// Retrieve the sample
PXCCapture::Sample *sample=sm->QuerySample();
// Work on the image sample->color
...
// Go fetching the next sample
sm->ReleaseFrame();
}
// close down
sm->Release();
}
在录制过程中,样本在应用程序处理后按原样录制到磁盘。例如,如果应用程序捕获未对齐的彩色和深度样本,则磁盘上的样本是未对齐的。如果应用程序对样本进行对齐,则磁盘上的样本也是对齐的。
录制的文件包含一个固定大小的头,结构如下
struct Header {
pxcI32 ID; // PXC_UID('R','S','C','F')
pxcI32 fileVersion; // file version
pxcI32 firstFrameOffset; // The byte offset to the meta data of the first frame.
pxcI32 nstreams; // The number of streams.
pxcI64 frameIndexingOffset; // Optional frame indexing offset, zero if not available.
PXCSession::CoordinateSystem coordinateSystem; // The coordinate system setting.
pxcI32 reserved[26];
};
任何流的图像帧都按顺序记录在头信息之后。它从 `ChunkFrameMetaData` 开始,以 `ChunkFrameData` 结束。在这之间,可能存在多个配置帧,用于描述图像帧的元数据。元数据应解释为相对于文件头部分定义的元数据的增量或更改。这可以减小某些元数据对所有帧通用的情况下的文件大小。帧结构如下
struct StreamFrame {
ChunkFrameMetaData frame_header;
// in no defined order
ChunkImageMetaData image_meta_data; // if any.
// there could be more chunks here.
ChunkFrameData frame_data;
} frames[]
`ChunkFrameMetaData` 结构如下
struct ChunkFrameMetaData {
ChunkId chunkId=CHUNK_FRAME_META_DATA;
pxcI32 chunkSize=sizeof(metaData);
struct {
pxcI32 frameNumber; // frame number in the current stream
PXCCapture::StreamType streamType;
pxcI64 timeStamp;
PXCImage::Option options;
} metaData;
};
您可以在帧中放置 `ChunkImageMetaData`,其结构如下
struct ChunkImageMetaData {
ChunkId chunkId=CHUNK_IMAGE_META_DATA;
pxcI32 chunkSize=sizeof(buffer)+sizeof(id);
pxcUID id; // meta data identifier
pxcBYTE buffer[chunkSize-sizeof(id)];
};
未压缩流的结构如下
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
};
压缩流的结构如下
struct ChunkFrameDataCompressed {
ChunkId chunkId=CHUNK_FRAME_DATA;
pxcI32 chunkSize=sizeof(imageData);
struct {
pxcI32 pitches[PXCImage::NUM_OF_PLANES]; // pitches of uncompressed image planes.
enum {
H264=0x343632,
LZO=0x4f5a4c,
} CompressionIdentifier;
pxcBYTE compressed_data[];
} imageData
};
RealSense™ SDK 捕获模块会将配置帧添加到文件中。配置帧可以包含由块标识符和块大小标识的任何任意数据。块数据在文件中的呈现顺序并不重要,但有一个通用规则:如果两个块之间存在依赖关系,则依赖块应放在文件后面。`ChunkData` 结构如下
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; // The chunk identifier
pxcI32 chunkSize; // The chunk size in bytes-8.
pxcBYTE chunkData[chunkSize]; // The chunk data buffer
} chunks[];
文件中需要包含一些配置帧
- `CHUNK_DEVICEINFO` 用于设备信息
- `CHUNK_STREAMINFO` 用于流信息
- `CHUNK_PROPERTIES` 用于设备属性
- `CHUNK_PROFILES` 用于流配置
- `CHUNK_SERIALIZEABLE` 用于设备校准
- `CHUNK_FRAME_INDEXING` 用于帧索引
播放流序列文件的步骤
- 使用 `CaptureManager` 实例的 `SetFileName` 函数。
- 提供 rssdk 格式的文件名并将录制模式设置为 false。
对于文件播放,SDK 会立即创建 Capture 实例(使用 `QueryCapture` 函数),以便应用程序可以查询录制内容的特性。您可以按如下方式配置 SDK 文件播放行为
函数 | 默认值 | 描述 |
|
false |
如果为 true,则文件播放会重复返回当前帧的相同样本。 |
|
true |
如果为 true,则文件播放会返回当前帧样本的显示时间(根据样本时间戳)。如果为 false,则文件播放会立即返回样本。 |
如果要准确地定位播放过程中的任何帧数据,请选择 pause=true 和 realtime=false。以下是展示如何使用 `SetRealtime` 和 `SetPause` 的示例代码
// Create a SenseManager instance
PXCSenseManager* sm = PXCSenseManager::CreateInstance();
// Set file playback name
sm->QueryCaptureManager()->SetFileName(filename, false);
// Enable stream and Initialize
sm->EnableStream(PXCCapture::STREAM_TYPE_COLOR, 0, 0);
sm->Init();
// Set realtime=false and pause=true
sm->QueryCaptureManager()->SetRealtime(false);
sm->QueryCaptureManager()->SetPause(true);
// Streaming loop
for (int i = 0; i < nframes; i+=3) {
// Set to work on every 3rd frame of data
sm->QueryCaptureManager()->SetFrameByIndex(i);
sm->FlushFrame();
// Ready for the frame to be ready
pxcStatus sts = sm->AcquireFrame(true);
if (sts < PXC_STATUS_NO_ERROR) break;
// Retrieve the sample and work on it. The image is in sample->color.
PXCCapture::Sample* sample = sm->QuerySample();
....
// Resume processing the next frame
sm->ReleaseFrame();
}
// Clean up
sm->Release();
在上述讨论中,我们可以看到通过这项新的 Intel® RealSense™ SDK 功能,开发人员可以嵌入代码将摄像机流序列录制到磁盘上的压缩文件中,并在之后进行播放。此功能在调试和故障排除应用程序问题时非常有用。