The old "API" that signaled rotation as a metadata value has been
replaced by DISPLAYMATRIX side data quite a while ago.
There is no reason to make muxers/demuxers/API users support both. In
addition, the metadata API is dangerous, as user tags could "leak" into
it, creating unintended features or bugs.
ffmpeg CLI has to be updated to use the new API. In particular, we must
not allow to leak the "rotate" tag into the muxer. Some muxers will
catch this properly (like mov), but others (like mkv) can add it as
generic tag. Note applications, which use libavformat and assume the
old rotate API, will interpret such "rotate" user tags as rotate
metadata (which it is not), and incorrectly rotate the video.
The ffmpeg/ffplay tools drop the use of the old API for muxing and
demuxing, as all muxers/demuxers support the new API. This will mean
that the tools will not mistakenly interpret per-track "rotate" user
tags as rotate metadata. It will _not_ be treated as regression.
Unfortunately, hacks have been added, that allow the user to override
rotation by setting metadata explicitly, e.g. via
-metadata:s:v:0 rotate=0
See references to trac #4560. fate-filter-meta-4560-rotate0 tests this.
It's easier to adjust the hack for supporting it than arguing for its
removal, so ffmpeg CLI now explicitly catches this case, and essentially
replaces the "rotate" value with a display matrix side data. (It would
be easier for both user and implementation to create an explicit option
for rotation.)
When the code under FF_API_OLD_ROTATE_API is disabled, one FATE
reference file has to be updated (because "rotate" is not exported
anymore).
Tested-by: Michael Niedermayer <michael@niedermayer.cc>
Reviewed-by: Michael Niedermayer <michael@niedermayer.cc>
double get_rotation(AVStream *st)
{
double get_rotation(AVStream *st)
{
- AVDictionaryEntry *rotate_tag = av_dict_get(st->metadata, "rotate", NULL, 0);
uint8_t* displaymatrix = av_stream_get_side_data(st,
AV_PKT_DATA_DISPLAYMATRIX, NULL);
double theta = 0;
uint8_t* displaymatrix = av_stream_get_side_data(st,
AV_PKT_DATA_DISPLAYMATRIX, NULL);
double theta = 0;
-
- if (rotate_tag && *rotate_tag->value && strcmp(rotate_tag->value, "0")) {
- char *tail;
- theta = av_strtod(rotate_tag->value, &tail);
- if (*tail)
- theta = 0;
- }
- if (displaymatrix && !theta)
theta = -av_display_rotation_get((int32_t*) displaymatrix);
theta -= 360*floor(theta/360 + 0.9/360);
theta = -av_display_rotation_get((int32_t*) displaymatrix);
theta -= 360*floor(theta/360 + 0.9/360);
API changes, most recent first:
API changes, most recent first:
+2017-03-xx - xxxxxxx - lavf 57.68.100 - avformat.h
+ Deprecate that demuxers export the stream rotation angle in AVStream.metadata
+ (via an entry named "rotate"). Use av_stream_get_side_data() with
+ AV_PKT_DATA_DISPLAYMATRIX instead, and read the rotation angle with
+ av_display_rotation_get(). The same is done for muxing. Instead of adding a
+ "rotate" entry to AVStream.metadata, AV_PKT_DATA_DISPLAYMATRIX side data has
+ to be added to the AVStream.
+
2017-03-xx - xxxxxxx - lavc 57.85.101 - avcodec.h
vdpau hardware accelerated decoding now supports the new hwaccel API, which
can create the decoder context and allocate hardware frame automatically.
2017-03-xx - xxxxxxx - lavc 57.85.101 - avcodec.h
vdpau hardware accelerated decoding now supports the new hwaccel API, which
can create the decoder context and allocate hardware frame automatically.
#include "libavutil/internal.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/dict.h"
#include "libavutil/internal.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/dict.h"
+#include "libavutil/display.h"
#include "libavutil/mathematics.h"
#include "libavutil/pixdesc.h"
#include "libavutil/avstring.h"
#include "libavutil/mathematics.h"
#include "libavutil/pixdesc.h"
#include "libavutil/avstring.h"
const AVPacketSideData *sd_src = &ist->st->side_data[i];
AVPacketSideData *sd_dst = &ost->st->side_data[ost->st->nb_side_data];
const AVPacketSideData *sd_src = &ist->st->side_data[i];
AVPacketSideData *sd_dst = &ost->st->side_data[ost->st->nb_side_data];
- if (ost->rotate_overridden && sd_src->type == AV_PKT_DATA_DISPLAYMATRIX)
- continue;
-
sd_dst->data = av_malloc(sd_src->size);
if (!sd_dst->data)
return AVERROR(ENOMEM);
sd_dst->data = av_malloc(sd_src->size);
if (!sd_dst->data)
return AVERROR(ENOMEM);
+ if (ost->rotate_overridden) {
+ uint8_t *sd = av_stream_new_side_data(ost->st, AV_PKT_DATA_DISPLAYMATRIX,
+ sizeof(int32_t) * 9);
+ if (sd)
+ av_display_rotation_set((int32_t *)sd, -ost->rotate_override_value);
+ }
+
ost->parser = av_parser_init(par_dst->codec_id);
ost->parser_avctx = avcodec_alloc_context3(NULL);
if (!ost->parser_avctx)
ost->parser = av_parser_init(par_dst->codec_id);
ost->parser_avctx = avcodec_alloc_context3(NULL);
if (!ost->parser_avctx)
set_encoder_id(output_files[ost->file_index], ost);
set_encoder_id(output_files[ost->file_index], ost);
+ // Muxers use AV_PKT_DATA_DISPLAYMATRIX to signal rotation. On the other
+ // hand, the legacy API makes demuxers set "rotate" metadata entries,
+ // which have to be filtered out to prevent leaking them to output files.
+ av_dict_set(&ost->st->metadata, "rotate", NULL, 0);
+
if (ist) {
ost->st->disposition = ist->st->disposition;
if (ist) {
ost->st->disposition = ist->st->disposition;
+ /*
+ * Add global input side data. For now this is naive, and copies it
+ * from the input stream's global side data. All side data should
+ * really be funneled over AVFrame and libavfilter, then added back to
+ * packet side data, and then potentially using the first packet for
+ * global side data.
+ */
+ if (ist) {
+ int i;
+ for (i = 0; i < ist->st->nb_side_data; i++) {
+ AVPacketSideData *sd = &ist->st->side_data[i];
+ uint8_t *dst = av_stream_new_side_data(ost->st, sd->type, sd->size);
+ if (!dst)
+ return AVERROR(ENOMEM);
+ memcpy(dst, sd->data, sd->size);
+ if (ist->autorotate && sd->type == AV_PKT_DATA_DISPLAYMATRIX)
+ av_display_rotation_set((uint32_t *)dst, 0);
+ }
+ }
+
// copy timebase while removing common factors
if (ost->st->time_base.num <= 0 || ost->st->time_base.den <= 0)
ost->st->time_base = av_add_q(ost->enc_ctx->time_base, (AVRational){0, 1});
// copy timebase while removing common factors
if (ost->st->time_base.num <= 0 || ost->st->time_base.den <= 0)
ost->st->time_base = av_add_q(ost->enc_ctx->time_base, (AVRational){0, 1});
AVPacketSideData *src_sd = &ist->st->side_data[i];
uint8_t *dst_data;
AVPacketSideData *src_sd = &ist->st->side_data[i];
uint8_t *dst_data;
- if (av_packet_get_side_data(&pkt, src_sd->type, NULL))
+ if (src_sd->type == AV_PKT_DATA_DISPLAYMATRIX)
- if (ist->autorotate && src_sd->type == AV_PKT_DATA_DISPLAYMATRIX)
+
+ if (av_packet_get_side_data(&pkt, src_sd->type, NULL))
continue;
dst_data = av_packet_new_side_data(&pkt, src_sd->type, src_sd->size);
continue;
dst_data = av_packet_new_side_data(&pkt, src_sd->type, src_sd->size);
int force_fps;
int top_field_first;
int rotate_overridden;
int force_fps;
int top_field_first;
int rotate_overridden;
+ double rotate_override_value;
AVRational frame_aspect_ratio;
AVRational frame_aspect_ratio;
av_dict_copy(&output_streams[i]->st->metadata, ist->st->metadata, AV_DICT_DONT_OVERWRITE);
if (!output_streams[i]->stream_copy) {
av_dict_set(&output_streams[i]->st->metadata, "encoder", NULL, 0);
av_dict_copy(&output_streams[i]->st->metadata, ist->st->metadata, AV_DICT_DONT_OVERWRITE);
if (!output_streams[i]->stream_copy) {
av_dict_set(&output_streams[i]->st->metadata, "encoder", NULL, 0);
- if (ist->autorotate)
- av_dict_set(&output_streams[i]->st->metadata, "rotate", NULL, 0);
for (j = 0; j < oc->nb_streams; j++) {
ost = output_streams[nb_output_streams - oc->nb_streams + j];
if ((ret = check_stream_specifier(oc, oc->streams[j], stream_spec)) > 0) {
for (j = 0; j < oc->nb_streams; j++) {
ost = output_streams[nb_output_streams - oc->nb_streams + j];
if ((ret = check_stream_specifier(oc, oc->streams[j], stream_spec)) > 0) {
- av_dict_set(&oc->streams[j]->metadata, o->metadata[i].u.str, *val ? val : NULL, 0);
if (!strcmp(o->metadata[i].u.str, "rotate")) {
if (!strcmp(o->metadata[i].u.str, "rotate")) {
- ost->rotate_overridden = 1;
+ char *tail;
+ double theta = av_strtod(val, &tail);
+ if (!*tail) {
+ ost->rotate_overridden = 1;
+ ost->rotate_override_value = theta;
+ }
+ } else {
+ av_dict_set(&oc->streams[j]->metadata, o->metadata[i].u.str, *val ? val : NULL, 0);
}
} else if (ret < 0)
exit_program(1);
}
} else if (ret < 0)
exit_program(1);
for (j = 0; j < 3; j++)
sc->display_matrix[i * 3 + j] = res_display_matrix[i][j];
for (j = 0; j < 3; j++)
sc->display_matrix[i * 3 + j] = res_display_matrix[i][j];
+#if FF_API_OLD_ROTATE_API
rotate = av_display_rotation_get(sc->display_matrix);
if (!isnan(rotate)) {
char rotate_buf[64];
rotate = av_display_rotation_get(sc->display_matrix);
if (!isnan(rotate)) {
char rotate_buf[64];
snprintf(rotate_buf, sizeof(rotate_buf), "%g", rotate);
av_dict_set(&st->metadata, "rotate", rotate_buf, 0);
}
snprintf(rotate_buf, sizeof(rotate_buf), "%g", rotate);
av_dict_set(&st->metadata, "rotate", rotate_buf, 0);
}
}
// transform the display width/height according to the matrix
}
// transform the display width/height according to the matrix
avio_wb16(pb, 0); /* reserved */
/* Matrix structure */
avio_wb16(pb, 0); /* reserved */
/* Matrix structure */
+#if FF_API_OLD_ROTATE_API
if (st && st->metadata) {
AVDictionaryEntry *rot = av_dict_get(st->metadata, "rotate", NULL, 0);
rotation = (rot && rot->value) ? atoi(rot->value) : 0;
}
if (st && st->metadata) {
AVDictionaryEntry *rot = av_dict_get(st->metadata, "rotate", NULL, 0);
rotation = (rot && rot->value) ? atoi(rot->value) : 0;
}
if (display_matrix) {
for (i = 0; i < 9; i++)
avio_wb32(pb, display_matrix[i]);
if (display_matrix) {
for (i = 0; i < 9; i++)
avio_wb32(pb, display_matrix[i]);
+#if FF_API_OLD_ROTATE_API
} else if (rotation == 90) {
write_matrix(pb, 0, 1, -1, 0, track->par->height, 0);
} else if (rotation == 180) {
write_matrix(pb, -1, 0, 0, -1, track->par->width, track->par->height);
} else if (rotation == 270) {
write_matrix(pb, 0, -1, 1, 0, 0, track->par->width);
} else if (rotation == 90) {
write_matrix(pb, 0, 1, -1, 0, track->par->height, 0);
} else if (rotation == 180) {
write_matrix(pb, -1, 0, 0, -1, track->par->width, track->par->height);
} else if (rotation == 270) {
write_matrix(pb, 0, -1, 1, 0, 0, track->par->width);
} else {
write_matrix(pb, 1, 0, 0, 1, 0, 0);
}
} else {
write_matrix(pb, 1, 0, 0, 1, 0, 0);
}
// Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium)
// Also please add any ticket numbers that you believe might be affected here
#define LIBAVFORMAT_VERSION_MAJOR 57
// Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium)
// Also please add any ticket numbers that you believe might be affected here
#define LIBAVFORMAT_VERSION_MAJOR 57
-#define LIBAVFORMAT_VERSION_MINOR 67
+#define LIBAVFORMAT_VERSION_MINOR 68
#define LIBAVFORMAT_VERSION_MICRO 100
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
#define LIBAVFORMAT_VERSION_MICRO 100
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
#ifndef FF_API_LAVF_KEEPSIDE_FLAG
#define FF_API_LAVF_KEEPSIDE_FLAG (LIBAVFORMAT_VERSION_MAJOR < 58)
#endif
#ifndef FF_API_LAVF_KEEPSIDE_FLAG
#define FF_API_LAVF_KEEPSIDE_FLAG (LIBAVFORMAT_VERSION_MAJOR < 58)
#endif
+#ifndef FF_API_OLD_ROTATE_API
+#define FF_API_OLD_ROTATE_API (LIBAVFORMAT_VERSION_MAJOR < 58)
+#endif
#ifndef FF_API_R_FRAME_RATE
#ifndef FF_API_R_FRAME_RATE