首页| 论坛| 消息
主题:Qt音视频开发技巧/推流带旋转角度/rtsprtmp推流/保存文件到MP4/拉流解析旋转角度
liudianwu发表于 2025-07-02 11:30
## 一、前言说明
手机上拍摄的视频一般都是带有旋转角度的,前置后置旋转的角度还不一样,一个是90度一个是270度,在早期的vlc等播放器播放这种视频的视频,无法解析旋转角度,所以看起来视频是倒过来的,很不舒服,后面慢慢的手机时代流行后,各大主流播放器都支持了旋转角度的解析。视频文件本身是1280x720的分辨率,并不是720x1280的分辨率,然后视频文件带有旋转角度,ffmpeg中可以用对应流的stream->metadata获取到对应的旋转角度,从ffmpeg5开始换成了用函数av_stream_get_side_data获取,获取到这个旋转角度后,可以通过运算方式将frame转换后再显示,也可以通过滤镜来实现,个人更偏爱用滤镜,比较方便,而且性能比较高。
上面只是拉流解码识别旋转角度,这个还是非常容易实现的,如果是保存到MP4文件,只需要用对应的设置旋转角度函数设置对应的流即可。那是不是就完成了,推流试试之后发现,拉流那边居然无法识别到设置的旋转角度信息,查阅了大量资料得知,RTSP等协议本身不直接支持 在协议层面传递视频的旋转角度信息。RTSP的核心功能是媒体会话控制(如播放、暂停等),并不涉及元数据或视频属性的描述。测试了很多流媒体服务程序,都是不支持的,哪怕用ffmpeg命令行推流带旋转角度信息的文件再拉流,还是不行,无论如何设置旋转角度信息,那怎么办呢?突然发现可不可以通过设置自定义的元数据来设置呢,这样的话尽管只能ffmpeg端解析自定义的元数据识别,还是可以接受的。不然每个旋转的视频文件要重新编码旋转后再推流,很费CPU资源不划算。
之前以为ffmpeg可以强制写入自定义键值的元数据,试下来不行的,必须是该封装格式支持的元数据才行,比如MP4可以设置title和artist等,如果是不支持的,就算设置成功了,最后也不会写入进去,尤其是推流,不断尝试打印av_dict_get(xxx->metadata)最后发现rtsp有个title元数据可以设置数据,于是利用这个元数据把旋转角度信息设置,解析端除了用常规函数获取旋转信息,还要读取这个元数据,也认识是对应的旋转角度,这样就完美了,测试下来效果非常好,目前来说是市面上唯一一款支持非编码实现的带旋转角度的推流播放器了。
## 二、效果图

## 三、相关代码
```cpp
int FFmpegHelper::getRotate(AVStream *stream)
{
int rotate = 0;
//测试发现ffmpeg2不支持旋转滤镜
#if (FFMPEG_VERSION_MAJOR < 3)
return rotate;
#endif
#if (FFMPEG_VERSION_MAJOR < 5)
AVDictionaryEntry *tag = NULL;
tag = av_dict_get(stream->metadata, "rotate", NULL, 0);
if (tag) {
rotate = atoi(tag->value);
}
#else
//从ffplay源码中找到的方法
double theta = 0;
uint8_t *sidedata = NULL;
#if (FFMPEG_VERSION_MAJOR < 7)
sidedata = av_stream_get_side_data(stream, AV_PKT_DATA_DISPLAYMATRIX, NULL);
#else
const AVPacketSideData *psidedata = av_packet_side_data_get(stream->codecpar->coded_side_data, stream->codecpar->nb_coded_side_data, AV_PKT_DATA_DISPLAYMATRIX);
if (psidedata) {
sidedata = psidedata->data;
}
#endif
if (sidedata) {
theta = -av_display_rotation_get((int32_t *)sidedata);
theta -= 360 * floor(theta / 360 + 0.9 / 360);
rotate = theta;
}
#endif
return rotate;
}
void FFmpegHelper::setRotate(AVStream *stream, int rotate)
{
if (rotate metadata, "rotate", rotate);
#else
//从ffplay源码中找到的方法
uint8_t *sidedata = NULL;
#if (FFMPEG_VERSION_MAJOR < 6)
sidedata = av_stream_new_side_data(stream, AV_PKT_DATA_DISPLAYMATRIX, sizeof(qint32) * 9);
#else
AVPacketSideData *psidedata = av_packet_side_data_new(&stream->codecpar->coded_side_data, &stream->codecpar->nb_coded_side_data, AV_PKT_DATA_DISPLAYMATRIX, sizeof(qint32) * 9, 0);
if (psidedata) {
sidedata = psidedata->data;
}
#endif
if (sidedata) {
av_display_rotation_set((int32_t *)sidedata, rotate);
}
#endif
}
void FFmpegHelper::getRotateByMetaData(AVFormatContext *formatCtx, int &rotate)
{
//取出自定义旋转信息/放在元数据中/方便推流拉流使用
if (rotate == 0) {
AVDictionaryEntry *entry = av_dict_get(formatCtx->metadata, "title", NULL, 0);
if (entry) {
QString value = entry->value;
if (value.startsWith("rotate:")) {
rotate = value.split(":").last().toInt();
}
}
}
}
void FFmpegHelper::setRot

浏览大图

浏览大图
下一页 (1/5)
回帖(0):

全部回帖(0)»
最新回帖
收藏本帖
发新帖