1. 概述
FFmpeg 在处理视频旋转信息方面经历了重要的架构变化。本文档详细对比了 FFmpeg 3.4 和 7.0.2 在封装(muxing)和解封装(demuxing)视频旋转信息时的差异,并提供兼容性解决方案。文档内容由Claude Sonnet 4辅助撰写,如有疏漏还请斧正。
- FFmpeg 3.4:支持 metadata 和 side data 双重机制
- FFmpeg 7.0.2:主要依赖 side data 机制,弱化 metadata 支持
2. 旋转信息存储机制
存储方式 | FFmpeg 3.4 | FFmpeg 7.0.2 | 说明 |
---|---|---|---|
Stream Metadata | ✅ 读写支持 | ❌ 仅读取(兼容性) | stream->metadata["rotate"] |
Side Data | ✅ 读写支持 | ✅ 主要方式 | AV_PKT_DATA_DISPLAYMATRIX |
容器原生格式 | ✅ 支持 | ✅ 支持 | MP4 tkhd atom 等 |
c
// Metadata 格式(字符串)
stream->metadata["rotate"] = "90" // 90度旋转
// Side Data 格式(3x3 变换矩阵)
int32_t displaymatrix[9] = {
cos(θ), -sin(θ), 0,
sin(θ), cos(θ), 0,
0, 0, 1
};
3. 解封装(Demuxing)对比
3.1 FFmpeg 3.4 解封装行为
c
// FFmpeg 3.4 读取流程
void ffmpeg34_demux_rotation() {
// 1. 从容器读取 displaymatrix
uint8_t *displaymatrix = read_container_displaymatrix();
// 2. 设置 side data
av_stream_add_side_data(stream, AV_PKT_DATA_DISPLAYMATRIX, displaymatrix, size);
// 3. 同时设置 metadata(便于应用程序使用)
if (displaymatrix) {
double rotation = av_display_rotation_get((int32_t*)displaymatrix);
av_dict_set(&stream->metadata, "rotate",
av_asprintf("%.0f", -rotation), AV_DICT_DONT_STRDUP_VAL);
}
}
// 应用程序读取(推荐方式)
int getRotation_v34(AVStream *stream) {
// 方式1:从 metadata 读取(简单)
AVDictionaryEntry *t = av_dict_get(stream->metadata, "rotate", NULL, 0);
if (t) return atoi(t->value);
// 方式2:从 side data 读取(精确)
uint8_t *displaymatrix = av_stream_get_side_data(stream, AV_PKT_DATA_DISPLAYMATRIX, NULL);
if (displaymatrix) {
return (int)(-av_display_rotation_get((int32_t*)displaymatrix));
}
return 0;
}
3.2 FFmpeg 7.0.2 解封装行为
c
// FFmpeg 7.0.2 读取流程
void ffmpeg70_demux_rotation() {
// 1. 从容器读取 displaymatrix
uint8_t *displaymatrix = read_container_displaymatrix();
// 2. 只设置 side data
av_packet_side_data_add(&stream->codecpar->coded_side_data,
&stream->codecpar->nb_coded_side_data,
AV_PKT_DATA_DISPLAYMATRIX, displaymatrix, size, 0);
// 3. 不再自动设置 metadata
// stream->metadata["rotate"] 为空!
}
// 应用程序读取(新方式)
int getRotation_v70(AVStream *stream) {
// 只能从 side data 读取
AVPacketSideData *side_data = av_packet_side_data_get(
stream->codecpar->coded_side_data,
stream->codecpar->nb_coded_side_data,
AV_PKT_DATA_DISPLAYMATRIX
);
if (side_data && side_data->size >= 9 * sizeof(int32_t)) {
double rotation = av_display_rotation_get((int32_t*)side_data->data);
return (int)(-rotation);
}
return 0;
}
4. 封装(Muxing)对比
4.1 FFmpeg 3.4 封装行为
c
// FFmpeg 3.4 支持多种设置方式
// 方式1:通过 metadata 设置(推荐)
int setRotation_v34_metadata(AVStream *stream, int degrees) {
char rotation_str[16];
snprintf(rotation_str, sizeof(rotation_str), "%d", degrees);
// 设置 metadata,muxer 会自动转换为 displaymatrix
return av_dict_set(&stream->metadata, "rotate", rotation_str, 0);
}
// 方式2:直接设置 side data
int setRotation_v34_sidedata(AVStream *stream, int degrees) {
uint8_t *displaymatrix = av_stream_new_side_data(stream,
AV_PKT_DATA_DISPLAYMATRIX,
9 * sizeof(int32_t));
if (!displaymatrix) return -1;
av_display_rotation_set((int32_t*)displaymatrix, -(double)degrees);
return 0;
}
4.2 FFmpeg 7.0.2 封装行为
c
// FFmpeg 7.0.2 主要通过 side data 设置
int setRotation_v70(AVStream *stream, int degrees) {
// metadata 方式可能无效,必须使用 side data
int32_t *displaymatrix = (int32_t*)av_packet_side_data_new(
&stream->codecpar->coded_side_data,
&stream->codecpar->nb_coded_side_data,
AV_PKT_DATA_DISPLAYMATRIX,
9 * sizeof(int32_t),
0
);
if (!displaymatrix) return -1;
av_display_rotation_set(displaymatrix, -(double)degrees);
return 0;
}
5. 总结
FFmpeg 在处理视频旋转信息方面的变化反映了其向更标准化、更精确方向的演进:
- FFmpeg 3.4:过渡期版本,同时支持 metadata 和 side data
- FFmpeg 7.0.2:现代化版本,主要依赖标准化的 side data
开发者应该:
- 在新项目中优先使用 side data API
- 保持对旧版本的兼容性支持
- 进行充分的跨版本测试
- 关注 FFmpeg 的未来发展趋势
这种变化虽然增加了开发复杂度,但提供了更好的标准兼容性和更精确的旋转信息处理能力。