如何用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()无效的原因),而是嵌入在逐帧的辅助数据轨道数据包里。以下是具体实现步骤:
核心思路
- 定位MP4中存储GPS数据的辅助流(通常是
AVMEDIA_TYPE_DATA类型的流,handler名称包含"GPS"标识) - 遍历读取该流的所有数据包,解析二进制数据中的GPS信息(纬度、经度、速度、时间)
- 将解析结果写入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(×tamp)); return 1; }
注意事项
- 不同设备生成的MP4,GPS数据的存储格式差异很大,需先用
ffprobe或ExifTool确认数据结构后再编写解析逻辑 - FFmpeg版本差异:4.x+版本无需
av_register_all(),部分API有调整,需适配对应版本 - 编译时需链接FFmpeg的
avformat、avutil等核心库
内容的提问来源于stack exchange,提问作者Nikolai Egipko
相关产品推荐
相关产品推荐

