You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用FFmpeg C++库提取MP4视频的GPS位置数据(类似ExifTool)

用FFmpeg C++库提取MP4中的GPS数据(替代ExifTool命令)

要实现和exiftool -ee -n -p "$gpslatitude,$gpslongitude,$gpsspeed,$gpsdatetime" test.mp4 > gps.csv相同的功能,你需要注意:MP4中的GPS数据并非存储在全局或流的元数据字典中(这就是av_dict_get()/av_dict_iterate()无效的原因),而是嵌入在逐帧的辅助数据轨道数据包里。以下是具体实现步骤:

核心思路

  1. 定位MP4中存储GPS数据的辅助流(通常是AVMEDIA_TYPE_DATA类型的流,handler名称包含"GPS"标识)
  2. 遍历读取该流的所有数据包,解析二进制数据中的GPS信息(纬度、经度、速度、时间)
  3. 将解析结果写入CSV文件

关键代码实现

1. 初始化FFmpeg并定位GPS流

#include <iostream>
#include <cstdio>
#include <cstring>
#include <libavformat/avformat.h>
#include <time.h>

typedef struct {
    double latitude;
    double longitude;
    double speed; // 单位:m/s
    char datetime[20]; // 格式:YYYY-MM-DD HH:MM:SS
} GPSData;

int parse_gps_data(uint8_t *data, int size, GPSData *gps);

int main() {
    av_register_all(); // FFmpeg 3.x及以下需调用,4.x+可省略
    AVFormatContext *fmt_ctx = nullptr;

    // 打开输入视频文件
    if (avformat_open_input(&fmt_ctx, "test.mp4", nullptr, nullptr) != 0) {
        fprintf(stderr, "无法打开输入文件\n");
        return -1;
    }

    // 获取流信息
    if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) {
        fprintf(stderr, "无法获取流信息\n");
        avformat_close_input(&fmt_ctx);
        return -1;
    }

    // 查找GPS辅助流
    int gps_stream_idx = -1;
    for (int i = 0; i < fmt_ctx->nb_streams; i++) {
        AVStream *stream = fmt_ctx->streams[i];
        if (stream->codecpar->codec_type == AVMEDIA_TYPE_DATA) {
            AVDictionaryEntry *handler_tag = av_dict_get(stream->metadata, "handler_name", nullptr, 0);
            if (handler_tag && strstr(handler_tag->value, "GPS") != nullptr) {
                gps_stream_idx = i;
                break;
            }
        }
    }

    if (gps_stream_idx == -1) {
        fprintf(stderr, "未找到GPS数据轨道\n");
        avformat_close_input(&fmt_ctx);
        return -1;
    }

2. 读取数据包并解析GPS数据

// 打开CSV输出文件
    FILE *csv_file = fopen("gps.csv", "w");
    if (!csv_file) {
        fprintf(stderr, "无法创建CSV文件\n");
        avformat_close_input(&fmt_ctx);
        return -1;
    }
    fprintf(csv_file, "gpslatitude,gpslongitude,gpsspeed,gpsdatetime\n");

    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.data = nullptr;
    pkt.size = 0;

    // 遍历所有数据包
    while (av_read_frame(fmt_ctx, &pkt) >= 0) {
        if (pkt.stream_index == gps_stream_idx) {
            GPSData gps_data = {0};
            if (parse_gps_data(pkt.data, pkt.size, &gps_data)) {
                fprintf(csv_file, "%f,%f,%f,%s\n",
                        gps_data.latitude,
                        gps_data.longitude,
                        gps_data.speed,
                        gps_data.datetime);
            }
        }
        av_packet_unref(&pkt);
    }

    // 清理资源
    fclose(csv_file);
    avformat_close_input(&fmt_ctx);
    return 0;
}

3. 实现GPS数据解析函数

parse_gps_data需要根据你的MP4文件的GPS存储格式定制,以下是苹果设备MP4 GPS格式的示例解析逻辑(仅作参考,需根据实际数据调整):

int parse_gps_data(uint8_t *data, int size, GPSData *gps) {
    if (size < 28) return 0; // 数据长度不足,直接返回失败

    // 解析纬度(小端浮点格式)
    memcpy(&gps->latitude, data + 4, 8);
    // 解析经度(小端浮点格式)
    memcpy(&gps->longitude, data + 12, 8);
    // 解析速度(节转m/s:1节=0.514444m/s)
    float speed_knots;
    memcpy(&speed_knots, data + 20, 4);
    gps->speed = speed_knots * 0.514444;
    // 解析UTC时间戳并转换为字符串
    time_t timestamp = *(uint32_t*)(data + 24);
    strftime(gps->datetime, sizeof(gps->datetime), "%Y-%m-%d %H:%M:%S", gmtime(&timestamp));

    return 1;
}

注意事项

  • 不同设备生成的MP4,GPS数据的存储格式差异很大,需先用ffprobe或ExifTool确认数据结构后再编写解析逻辑
  • FFmpeg版本差异:4.x+版本无需av_register_all(),部分API有调整,需适配对应版本
  • 编译时需链接FFmpeg的avformat、avutil等核心库

内容的提问来源于stack exchange,提问作者Nikolai Egipko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 09:02:28