JPEG文件添加应用数据

文章探讨了如何在UVC设备输出mjpeg视频流时,同时传输图像的特征数据。通过分析JPEG文件的JFIF和EXIF结构,作者测试了在JFIF的APP段中插入自定义数据的方法,以不影响UVC功能的方式实现数据和图像的同步传输。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目标:在UVC输出mjpeg视频流时,将图像特征一起输出,同时不能影响UVC功能

1. jpeg文件格式

jpeg通常有两种结构:

    1. jfif
    1. exif

参考:JPEG文件格式解析(一) Exif 与 JFIF
在这里插入图片描述

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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值