Skip to content

Commit 5870c95

Browse files
committed
filters: automatically flip video if displaymatrix indicates so
Currently we check AV_FRAME_DATA_DISPLAYMATRIX for its rotation factor but not for its flip factor, which could happen with some input files. This commit adds and autovflip filter, which is applied before the autorotate filter to flip vertically before rotating. It also changes the displaymatrix reading code to output to mp_image->params.vflip so it can be autoinserted as necessary. Signed-off-by: Leo Izen <[email protected]>
1 parent 52a95d9 commit 5870c95

File tree

14 files changed

+178
-7
lines changed

14 files changed

+178
-7
lines changed

filters/f_auto_filters.c

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,127 @@ struct mp_filter *mp_autorotate_create(struct mp_filter *parent)
332332
return f;
333333
}
334334

335+
struct vflip_priv {
336+
struct mp_subfilter sub;
337+
int prev_vflip;
338+
int prev_imgfmt;
339+
int target_vflip;
340+
};
341+
342+
static void vflip_process(struct mp_filter *f)
343+
{
344+
struct vflip_priv *p = f->priv;
345+
346+
if (!mp_subfilter_read(&p->sub))
347+
return;
348+
349+
struct mp_frame frame = p->sub.frame;
350+
351+
if (mp_frame_is_signaling(frame)) {
352+
mp_subfilter_continue(&p->sub);
353+
return;
354+
}
355+
356+
if (frame.type != MP_FRAME_VIDEO) {
357+
MP_ERR(f, "video input required!\n");
358+
return;
359+
}
360+
361+
struct mp_image *img = frame.data;
362+
363+
if (img->params.vflip == p->prev_vflip &&
364+
img->imgfmt == p->prev_imgfmt)
365+
{
366+
img->params.vflip = p->target_vflip;
367+
mp_subfilter_continue(&p->sub);
368+
return;
369+
}
370+
371+
if (!mp_subfilter_drain_destroy(&p->sub))
372+
return;
373+
374+
mp_assert(!p->sub.filter);
375+
376+
int vflip = p->prev_vflip = img->params.vflip;
377+
p->target_vflip = vflip;
378+
p->prev_imgfmt = img->imgfmt;
379+
380+
struct mp_stream_info *info = mp_filter_find_stream_info(f);
381+
if (!vflip || (info && info->vflip)) {
382+
mp_subfilter_continue(&p->sub);
383+
return;
384+
}
385+
386+
if (!mp_sws_supports_input(img->imgfmt)) {
387+
MP_ERR(f, "Video vflip with this format not supported\n");
388+
mp_subfilter_continue(&p->sub);
389+
return;
390+
}
391+
392+
char *args[] = {NULL};
393+
394+
p->sub.filter = mp_create_user_filter(f, MP_OUTPUT_CHAIN_VIDEO, "vflip", args);
395+
396+
if (p->sub.filter) {
397+
MP_INFO(f, "Inserting vflip filter.\n");
398+
p->target_vflip = 0;
399+
} else {
400+
MP_ERR(f, "could not create vflip filter\n");
401+
}
402+
403+
mp_subfilter_continue(&p->sub);
404+
}
405+
406+
static void vflip_reset(struct mp_filter *f)
407+
{
408+
struct vflip_priv *p = f->priv;
409+
410+
mp_subfilter_reset(&p->sub);
411+
}
412+
413+
static void vflip_destroy(struct mp_filter *f)
414+
{
415+
struct vflip_priv *p = f->priv;
416+
417+
mp_subfilter_reset(&p->sub);
418+
TA_FREEP(&p->sub.filter);
419+
}
420+
421+
static bool vflip_command(struct mp_filter *f, struct mp_filter_command *cmd)
422+
{
423+
struct vflip_priv *p = f->priv;
424+
425+
if (cmd->type == MP_FILTER_COMMAND_IS_ACTIVE) {
426+
cmd->is_active = !!p->sub.filter;
427+
return true;
428+
}
429+
return false;
430+
}
431+
432+
static const struct mp_filter_info vflip_filter = {
433+
.name = "autovflip",
434+
.priv_size = sizeof(struct vflip_priv),
435+
.command = vflip_command,
436+
.process = vflip_process,
437+
.reset = vflip_reset,
438+
.destroy = vflip_destroy,
439+
};
440+
441+
struct mp_filter *mp_autovflip_create(struct mp_filter *parent)
442+
{
443+
struct mp_filter *f = mp_filter_create(parent, &vflip_filter);
444+
if (!f)
445+
return NULL;
446+
447+
struct vflip_priv *p = f->priv;
448+
p->prev_vflip = -1;
449+
450+
p->sub.in = mp_filter_add_pin(f, MP_PIN_IN, "in");
451+
p->sub.out = mp_filter_add_pin(f, MP_PIN_OUT, "out");
452+
453+
return f;
454+
}
455+
335456
struct aspeed_priv {
336457
struct mp_subfilter sub;
337458
double cur_speed, cur_speed_drop;

filters/f_auto_filters.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
// hardware decode mode and the deinterlace user option.
77
struct mp_filter *mp_deint_create(struct mp_filter *parent);
88

9+
// Flip according to mp_image.vflip
10+
struct mp_filter *mp_autovflip_create(struct mp_filter *parent);
11+
912
// Rotate according to mp_image.rotate and VO capabilities.
1013
struct mp_filter *mp_autorotate_create(struct mp_filter *parent);
1114

filters/f_output_chain.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ void mp_output_chain_set_vo(struct mp_output_chain *c, struct vo *vo)
376376

377377
p->stream_info.hwdec_devs = vo ? vo->hwdec_devs : NULL;
378378
p->stream_info.osd = vo ? vo->osd : NULL;
379+
p->stream_info.vflip = vo ? vo->driver->caps & VO_CAP_VFLIP : false;
379380
p->stream_info.rotate90 = vo ? vo->driver->caps & VO_CAP_ROTATE90 : false;
380381
p->stream_info.dr_vo = vo;
381382
p->vo = vo;
@@ -652,6 +653,13 @@ static void create_video_things(struct chain *p)
652653
abort();
653654
MP_TARRAY_APPEND(p, p->pre_filters, p->num_pre_filters, f);
654655

656+
f = create_wrapper_filter(p);
657+
f->name = "autovflip";
658+
f->f = mp_autovflip_create(f->wrapper);
659+
if (!f->f)
660+
abort();
661+
MP_TARRAY_APPEND(p, p->post_filters, p->num_post_filters, f);
662+
655663
f = create_wrapper_filter(p);
656664
f->name = "autorotate";
657665
f->f = mp_autorotate_create(f->wrapper);

filters/filter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ struct mp_stream_info {
405405

406406
struct mp_hwdec_devices *hwdec_devs;
407407
struct osd_state *osd;
408+
bool vflip;
408409
bool rotate90;
409410
struct vo *dr_vo; // for calling vo_get_image()
410411
};

video/mp_image.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ void mp_image_copy_attributes(struct mp_image *dst, struct mp_image *src)
525525
dst->pts = src->pts;
526526
dst->dts = src->dts;
527527
dst->pkt_duration = src->pkt_duration;
528+
dst->params.vflip = src->params.vflip;
528529
dst->params.rotate = src->params.rotate;
529530
dst->params.stereo3d = src->params.stereo3d;
530531
dst->params.p_w = src->params.p_w;
@@ -851,6 +852,7 @@ bool mp_image_params_equal(const struct mp_image_params *p1,
851852
pl_color_repr_equal(&p1->repr, &p2->repr) &&
852853
p1->light == p2->light &&
853854
p1->chroma_location == p2->chroma_location &&
855+
p1->vflip == p2->vflip &&
854856
p1->rotate == p2->rotate &&
855857
p1->stereo3d == p2->stereo3d &&
856858
mp_rect_equals(&p1->crop, &p2->crop);
@@ -1104,9 +1106,15 @@ struct mp_image *mp_image_from_av_frame(struct AVFrame *src)
11041106

11051107
sd = av_frame_get_side_data(src, AV_FRAME_DATA_DISPLAYMATRIX);
11061108
if (sd) {
1107-
double r = av_display_rotation_get((int32_t *)(sd->data));
1108-
if (!isnan(r))
1109+
int32_t *matrix = (int32_t *) sd->data;
1110+
// determinant
1111+
int vflip = ((int64_t)matrix[0] * (int64_t)matrix[4]
1112+
- (int64_t)matrix[1] * (int64_t)matrix[3]) < 0;
1113+
double r = av_display_rotation_get(matrix);
1114+
if (!isnan(r)) {
11091115
dst->params.rotate = (((int)(-r) % 360) + 360) % 360;
1116+
dst->params.vflip = vflip;
1117+
}
11101118
}
11111119

11121120
sd = av_frame_get_side_data(src, AV_FRAME_DATA_ICC_PROFILE);

video/mp_image.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ struct mp_image_params {
5757

5858
enum mp_csp_light light;
5959
enum pl_chroma_location chroma_location;
60+
// The image should be flipped vertically before rotating
61+
bool vflip;
6062
// The image should be rotated clockwise (0-359 degrees).
6163
int rotate;
6264
enum mp_stereo3d_mode stereo3d; // image is encoded with this mode

video/out/gpu/libmpv_gpu.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ static int init(struct render_backend *ctx, mpv_render_param *params)
8989

9090
ctx->hwdec_devs = hwdec_devices_create();
9191
gl_video_init_hwdecs(p->renderer, p->context->ra_ctx, ctx->hwdec_devs, true);
92-
ctx->driver_caps = VO_CAP_ROTATE90;
92+
ctx->driver_caps = VO_CAP_ROTATE90 | VO_CAP_VFLIP;
9393
return 0;
9494
}
9595

video/out/gpu/video.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3737,6 +3737,7 @@ static bool pass_upload_image(struct gl_video *p, struct mp_image *mpi, uint64_t
37373737
.w = mp_image_plane_w(&layout, n),
37383738
.h = mp_image_plane_h(&layout, n),
37393739
.tex = tex[n],
3740+
.flipped = layout.params.vflip,
37403741
};
37413742
}
37423743
} else {
@@ -3750,6 +3751,10 @@ static bool pass_upload_image(struct gl_video *p, struct mp_image *mpi, uint64_t
37503751
mp_assert(mpi->num_planes == p->plane_count);
37513752

37523753
timer_pool_start(p->upload_timer);
3754+
3755+
if (mpi->params.vflip)
3756+
mp_image_vflip(mpi);
3757+
37533758
for (int n = 0; n < p->plane_count; n++) {
37543759
struct texplane *plane = &vimg->planes[n];
37553760
if (!plane->tex) {

video/out/vo.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,9 @@ static void check_vo_caps(struct vo *vo)
579579
"video output does not support this.\n", rot);
580580
}
581581
}
582+
if (vo->params->vflip && !(vo->driver->caps & VO_CAP_VFLIP))
583+
MP_WARN(vo, "Video is flagged as vertically flipped, but the "
584+
"video output does not support this.\n");
582585
}
583586

584587
static void run_reconfig(void *p)

video/out/vo.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ enum {
206206
VO_CAP_UNTIMED = 1 << 4,
207207
// VO is responsible for freeing frames.
208208
VO_CAP_FRAMEOWNER = 1 << 5,
209+
// VO does handle mp_image_params.vflip
210+
VO_CAP_VFLIP = 1 << 6,
209211
};
210212

211213
enum {

0 commit comments

Comments
 (0)