目标:在UVC输出mjpeg视频流时,将图像特征一起输出,同时不能影响UVC功能
1. jpeg文件格式
jpeg通常有两种结构:
-
- jfif
-
- exif
2. 测试在jfif结构的app段中插入数据
在做UVC输出图像时,想同时将图像的特征数据一并传出。最开始考虑的是linux设备上虚拟出一个cdc设备,单独输出特征数据。在了解JPEG文件的jfif和exif格式后,发现是可以文件中是可以带上app自定义数据段的。所以可以考虑在UVC输出图像时,自定义mjpeg图像的格式,将图像的特征数据填充到mjpeg图像的app段。并做了如下测试:
#include <stdio.h>
#include <stdint.h>
/**
显示jpg图像段:test ./1080.jpg
插入数据到app段:test ./1080.jpg 123
*/
void jpg_jfif_insert(const char *path, const char *data, int data_len)
{
// 给jpg图片中插入数据,插入到appN段
char *tar_path = "./1.jpg";
FILE *fpt;
fpt = fopen(tar_path, "wb");
FILE *fp;
fp = fopen(path, "rb");
// 1. 写入头
uint8_t _head[2];
uint8_t _len[2];
fread(_head, 1, 2, fp);
printf("%x %x\r\n", _head[0], _head[1]);
fwrite(_head, 1, 2, fpt);
uint8_t _app_idx = 0;
int _read_len = 0;
// 2. 读取所有app
while (1)
{
fread(_head, 1, 2, fp);
if (_head[0] != 0xff)
{
// 读到结尾,全部写入
long _cur_idx = ftell(fp);
fseek(fp, 0, SEEK_END); // 先将指针偏移到文件尾
long _f_size = ftell(fp);
fseek(fp, _cur_idx - 2, SEEK_SET); // 先将指针偏移到文件尾
_read_len = _f_size - _cur_idx + 2;
char *_data = new char[_read_len];
fread(_data, 1, _read_len, fp);
fwrite(_data, 1, _read_len, fpt);
delete _data;
break;
}
if (_head[1] > 0xef || _head[1] < 0xe0)
{
// 插入到该处
uint8_t _head_app[2];
_head_app[0] = 0xff;
_head_app[1] = _app_idx + 1;
uint8_t _head_app_len[2];
data_len += 2;
_head_app_len[0] = data_len >> 8;
_head_app_len[1] = data_len & 0xff;
fwrite(_head_app, 1, 2, fpt);
fwrite(_head_app_len, 1, 2, fpt);
fwrite(data, 1, data_len - 2, fpt);
}
else
{
_app_idx = _head[1];
}
fread(_len, 1, 2, fp);
_read_len = (_len[0] << 8) + _len[1];
printf("%x %x length %d(%x %x)\r\n", _head[0], _head[1], _read_len, _len[0], _len[1]);
fwrite(_head, 1, 2, fpt);
fwrite(_len, 1, 2, fpt);
char *_data = new char[_read_len - 2];
fread(_data, 1, _read_len - 2, fp);
fwrite(_data, 1, _read_len - 2, fpt);
delete _data;
}
fflush(fpt);
fclose(fpt);
fclose(fp);
}
int test_insert_data_to_app_segment(int argc, char **argv)
{
/**
* 测试添加1000个数据到app段中
*/
uint8_t _data[1000];
for (int i = 0; i < sizeof(_data); i++)
{
_data[i] = i;
}
jpg_jfif_insert(argv[1], (const char *)_data, sizeof(_data));
}
int main(int argc, char **argv)
{
if (argc > 2)
return test_insert_data_to_app_segment(argc, argv);
// 输出jpg文件的段标识和长度
char *path = argv[1];
FILE *fp;
fp = fopen(path, "rb");
uint8_t _head[2];
uint8_t _len[2];
fread(_head, 1, 2, fp);
printf("%x %x\r\n", _head[0], _head[1]);
int _read_len = 0;
while (1)
{
fread(_head, 1, 2, fp);
if (_head[0] != 0xff)
break;
fread(_len, 1, 2, fp);
_read_len = (_len[0] << 8) + _len[1];
printf("%x %x length %d(%x %x)\r\n", _head[0], _head[1], _read_len, _len[0], _len[1]);
fseek(fp, _read_len - 2, SEEK_CUR);
}
}