Commit 45bb80dc authored by Paul B Mahol's avatar Paul B Mahol

avfilter/vf_v360: implement stereo 3D support

parent 451cee66
...@@ -18080,6 +18080,21 @@ Set the output video resolution. ...@@ -18080,6 +18080,21 @@ Set the output video resolution.
Default resolution depends on formats. Default resolution depends on formats.
@item in_stereo
@item out_stereo
Set the input/output stereo format.
@table @samp
@item 2d
2D mono
@item sbs
Side by side
@item tb
Top bottom
@end table
Default value is @b{@samp{2d}} for input and output format.
@item yaw @item yaw
@item pitch @item pitch
@item roll @item roll
......
...@@ -22,6 +22,13 @@ ...@@ -22,6 +22,13 @@
#define AVFILTER_V360_H #define AVFILTER_V360_H
#include "avfilter.h" #include "avfilter.h"
enum StereoFormats {
STEREO_2D,
STEREO_SBS,
STEREO_TB,
NB_STEREO_FMTS,
};
enum Projections { enum Projections {
EQUIRECTANGULAR, EQUIRECTANGULAR,
CUBEMAP_3_2, CUBEMAP_3_2,
...@@ -95,6 +102,8 @@ typedef struct V360Context { ...@@ -95,6 +102,8 @@ typedef struct V360Context {
int out_cubemap_face_rotation[6]; int out_cubemap_face_rotation[6];
int rotation_order[3]; int rotation_order[3];
int in_stereo, out_stereo;
float in_pad, out_pad; float in_pad, out_pad;
float yaw, pitch, roll; float yaw, pitch, roll;
...@@ -108,6 +117,11 @@ typedef struct V360Context { ...@@ -108,6 +117,11 @@ typedef struct V360Context {
float input_mirror_modifier[2]; float input_mirror_modifier[2];
int pr_width[4], pr_height[4];
int in_offset_w[4], in_offset_h[4];
int out_offset_w[4], out_offset_h[4];
int planewidth[4], planeheight[4]; int planewidth[4], planeheight[4];
int inplanewidth[4], inplaneheight[4]; int inplanewidth[4], inplaneheight[4];
int uv_linesize[4]; int uv_linesize[4];
......
...@@ -90,6 +90,11 @@ static const AVOption v360_options[] = { ...@@ -90,6 +90,11 @@ static const AVOption v360_options[] = {
{ "lanczos", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interp" }, { "lanczos", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interp" },
{ "w", "output width", OFFSET(width), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "w"}, { "w", "output width", OFFSET(width), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "w"},
{ "h", "output height", OFFSET(height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "h"}, { "h", "output height", OFFSET(height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "h"},
{ "in_stereo", "input stereo format", OFFSET(in_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" },
{"out_stereo", "output stereo format", OFFSET(out_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" },
{ "2d", "2d mono", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_2D}, 0, 0, FLAGS, "stereo" },
{ "sbs", "side by side", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_SBS}, 0, 0, FLAGS, "stereo" },
{ "tb", "top bottom", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_TB}, 0, 0, FLAGS, "stereo" },
{ "in_forder", "input cubemap face order", OFFSET(in_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "in_forder"}, { "in_forder", "input cubemap face order", OFFSET(in_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "in_forder"},
{"out_forder", "output cubemap face order", OFFSET(out_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "out_forder"}, {"out_forder", "output cubemap face order", OFFSET(out_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "out_forder"},
{ "in_frot", "input cubemap face rotation", OFFSET(in_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, "in_frot"}, { "in_frot", "input cubemap face rotation", OFFSET(in_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, "in_frot"},
...@@ -222,14 +227,19 @@ static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jo ...@@ -222,14 +227,19 @@ static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jo
const AVFrame *in = td->in; \ const AVFrame *in = td->in; \
AVFrame *out = td->out; \ AVFrame *out = td->out; \
\ \
for (int stereo = 0; stereo < 1 + s->out_stereo > STEREO_2D; stereo++) { \
for (int plane = 0; plane < s->nb_planes; plane++) { \ for (int plane = 0; plane < s->nb_planes; plane++) { \
const int in_linesize = in->linesize[plane]; \ const int in_linesize = in->linesize[plane]; \
const int out_linesize = out->linesize[plane]; \ const int out_linesize = out->linesize[plane]; \
const int uv_linesize = s->uv_linesize[plane]; \ const int uv_linesize = s->uv_linesize[plane]; \
const uint8_t *src = in->data[plane]; \ const int in_offset_w = stereo ? s->in_offset_w[plane] : 0; \
uint8_t *dst = out->data[plane]; \ const int in_offset_h = stereo ? s->in_offset_h[plane] : 0; \
const int width = s->planewidth[plane]; \ const int out_offset_w = stereo ? s->out_offset_w[plane] : 0; \
const int height = s->planeheight[plane]; \ const int out_offset_h = stereo ? s->out_offset_h[plane] : 0; \
const uint8_t *src = in->data[plane] + in_offset_h * in_linesize + in_offset_w * (bits >> 3); \
uint8_t *dst = out->data[plane] + out_offset_h * out_linesize + out_offset_w * (bits >> 3); \
const int width = s->pr_width[plane]; \
const int height = s->pr_height[plane]; \
\ \
const int slice_start = (height * jobnr ) / nb_jobs; \ const int slice_start = (height * jobnr ) / nb_jobs; \
const int slice_end = (height * (jobnr + 1)) / nb_jobs; \ const int slice_end = (height * (jobnr + 1)) / nb_jobs; \
...@@ -242,6 +252,7 @@ static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jo ...@@ -242,6 +252,7 @@ static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jo
\ \
s->remap_line(dst + y * out_linesize, width, src, in_linesize, u, v, ker); \ s->remap_line(dst + y * out_linesize, width, src, in_linesize, u, v, ker); \
} \ } \
} \
} \ } \
\ \
return 0; \ return 0; \
...@@ -2119,12 +2130,12 @@ static inline void mirror(const float *modifier, float *vec) ...@@ -2119,12 +2130,12 @@ static inline void mirror(const float *modifier, float *vec)
static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int p) static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int p)
{ {
s->u[p] = av_calloc(s->uv_linesize[p] * s->planeheight[p], sizeof_uv); s->u[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
s->v[p] = av_calloc(s->uv_linesize[p] * s->planeheight[p], sizeof_uv); s->v[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
if (!s->u[p] || !s->v[p]) if (!s->u[p] || !s->v[p])
return AVERROR(ENOMEM); return AVERROR(ENOMEM);
if (sizeof_ker) { if (sizeof_ker) {
s->ker[p] = av_calloc(s->uv_linesize[p] * s->planeheight[p], sizeof_ker); s->ker[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_ker);
if (!s->ker[p]) if (!s->ker[p])
return AVERROR(ENOMEM); return AVERROR(ENOMEM);
} }
...@@ -2158,6 +2169,8 @@ static int config_output(AVFilterLink *outlink) ...@@ -2158,6 +2169,8 @@ static int config_output(AVFilterLink *outlink)
int elements; int elements;
int err; int err;
int h, w; int h, w;
int in_offset_h, in_offset_w;
int out_offset_h, out_offset_w;
float hf, wf; float hf, wf;
float output_mirror_modifier[3]; float output_mirror_modifier[3];
void (*in_transform)(const V360Context *s, void (*in_transform)(const V360Context *s,
...@@ -2229,36 +2242,68 @@ static int config_output(AVFilterLink *outlink) ...@@ -2229,36 +2242,68 @@ static int config_output(AVFilterLink *outlink)
s->rotation_order[order] = rorder; s->rotation_order[order] = rorder;
} }
switch (s->in_stereo) {
case STEREO_2D:
w = inlink->w;
h = inlink->h;
in_offset_w = in_offset_h = 0;
break;
case STEREO_SBS:
w = inlink->w / 2;
h = inlink->h;
in_offset_w = w;
in_offset_h = 0;
break;
case STEREO_TB:
w = inlink->w;
h = inlink->h / 2;
in_offset_w = 0;
in_offset_h = h;
break;
default:
av_assert0(0);
}
s->inplaneheight[1] = s->inplaneheight[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
s->inplaneheight[0] = s->inplaneheight[3] = h;
s->inplanewidth[1] = s->inplanewidth[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
s->inplanewidth[0] = s->inplanewidth[3] = w;
s->in_offset_h[1] = s->in_offset_h[2] = FF_CEIL_RSHIFT(in_offset_h, desc->log2_chroma_h);
s->in_offset_h[0] = s->in_offset_h[3] = in_offset_h;
s->in_offset_w[1] = s->in_offset_w[2] = FF_CEIL_RSHIFT(in_offset_w, desc->log2_chroma_w);
s->in_offset_w[0] = s->in_offset_w[3] = in_offset_w;
switch (s->in) { switch (s->in) {
case EQUIRECTANGULAR: case EQUIRECTANGULAR:
in_transform = xyz_to_equirect; in_transform = xyz_to_equirect;
err = 0; err = 0;
wf = inlink->w; wf = w;
hf = inlink->h; hf = h;
break; break;
case CUBEMAP_3_2: case CUBEMAP_3_2:
in_transform = xyz_to_cube3x2; in_transform = xyz_to_cube3x2;
err = prepare_cube_in(ctx); err = prepare_cube_in(ctx);
wf = inlink->w / 3.f * 4.f; wf = w / 3.f * 4.f;
hf = inlink->h; hf = h;
break; break;
case CUBEMAP_1_6: case CUBEMAP_1_6:
in_transform = xyz_to_cube1x6; in_transform = xyz_to_cube1x6;
err = prepare_cube_in(ctx); err = prepare_cube_in(ctx);
wf = inlink->w * 4.f; wf = w * 4.f;
hf = inlink->h / 3.f; hf = h / 3.f;
break; break;
case CUBEMAP_6_1: case CUBEMAP_6_1:
in_transform = xyz_to_cube6x1; in_transform = xyz_to_cube6x1;
err = prepare_cube_in(ctx); err = prepare_cube_in(ctx);
wf = inlink->w / 3.f * 2.f; wf = w / 3.f * 2.f;
hf = inlink->h * 2.f; hf = h * 2.f;
break; break;
case EQUIANGULAR: case EQUIANGULAR:
in_transform = xyz_to_eac; in_transform = xyz_to_eac;
err = prepare_eac_in(ctx); err = prepare_eac_in(ctx);
wf = inlink->w; wf = w;
hf = inlink->h / 9.f * 8.f; hf = h / 9.f * 8.f;
break; break;
case FLAT: case FLAT:
av_log(ctx, AV_LOG_ERROR, "Flat format is not accepted as input.\n"); av_log(ctx, AV_LOG_ERROR, "Flat format is not accepted as input.\n");
...@@ -2266,20 +2311,20 @@ static int config_output(AVFilterLink *outlink) ...@@ -2266,20 +2311,20 @@ static int config_output(AVFilterLink *outlink)
case DUAL_FISHEYE: case DUAL_FISHEYE:
in_transform = xyz_to_dfisheye; in_transform = xyz_to_dfisheye;
err = 0; err = 0;
wf = inlink->w; wf = w;
hf = inlink->h; hf = h;
break; break;
case BARREL: case BARREL:
in_transform = xyz_to_barrel; in_transform = xyz_to_barrel;
err = 0; err = 0;
wf = inlink->w / 5.f * 4.f; wf = w / 5.f * 4.f;
hf = inlink->h; hf = h;
break; break;
case STEREOGRAPHIC: case STEREOGRAPHIC:
in_transform = xyz_to_stereographic; in_transform = xyz_to_stereographic;
err = 0; err = 0;
wf = inlink->w; wf = w;
hf = inlink->h / 2.f; hf = h / 2.f;
break; break;
default: default:
av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n"); av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n");
...@@ -2374,21 +2419,45 @@ static int config_output(AVFilterLink *outlink) ...@@ -2374,21 +2419,45 @@ static int config_output(AVFilterLink *outlink)
return err; return err;
} }
s->pr_height[1] = s->pr_height[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
s->pr_height[0] = s->pr_height[3] = h;
s->pr_width[1] = s->pr_width[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
s->pr_width[0] = s->pr_width[3] = w;
switch (s->out_stereo) {
case STEREO_2D:
out_offset_w = out_offset_h = 0;
break;
case STEREO_SBS:
out_offset_w = w;
out_offset_h = 0;
w *= 2;
break;
case STEREO_TB:
out_offset_w = 0;
out_offset_h = h;
h *= 2;
break;
default:
av_assert0(0);
}
s->out_offset_h[1] = s->out_offset_h[2] = FF_CEIL_RSHIFT(out_offset_h, desc->log2_chroma_h);
s->out_offset_h[0] = s->out_offset_h[3] = out_offset_h;
s->out_offset_w[1] = s->out_offset_w[2] = FF_CEIL_RSHIFT(out_offset_w, desc->log2_chroma_w);
s->out_offset_w[0] = s->out_offset_w[3] = out_offset_w;
s->planeheight[1] = s->planeheight[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h); s->planeheight[1] = s->planeheight[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
s->planeheight[0] = s->planeheight[3] = h; s->planeheight[0] = s->planeheight[3] = h;
s->planewidth[1] = s->planewidth[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w); s->planewidth[1] = s->planewidth[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
s->planewidth[0] = s->planewidth[3] = w; s->planewidth[0] = s->planewidth[3] = w;
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
s->uv_linesize[i] = FFALIGN(s->planewidth[i], 8); s->uv_linesize[i] = FFALIGN(s->pr_width[i], 8);
outlink->h = h; outlink->h = h;
outlink->w = w; outlink->w = w;
s->inplaneheight[1] = s->inplaneheight[2] = FF_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
s->inplaneheight[0] = s->inplaneheight[3] = inlink->h;
s->inplanewidth[1] = s->inplanewidth[2] = FF_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
s->inplanewidth[0] = s->inplanewidth[3] = inlink->w;
s->nb_planes = av_pix_fmt_count_planes(inlink->format); s->nb_planes = av_pix_fmt_count_planes(inlink->format);
if (desc->log2_chroma_h == desc->log2_chroma_w && desc->log2_chroma_h == 0) { if (desc->log2_chroma_h == desc->log2_chroma_w && desc->log2_chroma_h == 0) {
...@@ -2409,9 +2478,9 @@ static int config_output(AVFilterLink *outlink) ...@@ -2409,9 +2478,9 @@ static int config_output(AVFilterLink *outlink)
// Calculate remap data // Calculate remap data
for (int p = 0; p < s->nb_allocated; p++) { for (int p = 0; p < s->nb_allocated; p++) {
const int width = s->planewidth[p]; const int width = s->pr_width[p];
const int uv_linesize = s->uv_linesize[p]; const int uv_linesize = s->uv_linesize[p];
const int height = s->planeheight[p]; const int height = s->pr_height[p];
const int in_width = s->inplanewidth[p]; const int in_width = s->inplanewidth[p];
const int in_height = s->inplaneheight[p]; const int in_height = s->inplaneheight[p];
float du, dv; float du, dv;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment