#include "libavutil/stereo3d.h"
#include "avfilter.h"
+#include "filters.h"
#include "formats.h"
#include "internal.h"
#include "video.h"
typedef struct FramepackContext {
const AVClass *class;
+ int depth;
const AVPixFmtDescriptor *pix_desc; ///< agreed pixel format
enum AVStereo3DType format; ///< frame pack type output
} FramepackContext;
static const enum AVPixelFormat formats_supported[] = {
- AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P,
- AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVJ420P,
- AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
+ AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9,
+ AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14,
+ AV_PIX_FMT_GRAY16,
+ AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P,
+ AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P,
+ AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P,
+ AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P,
+ AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ444P,
+ AV_PIX_FMT_YUVJ411P,
+ AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
+ AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
+ AV_PIX_FMT_YUV440P10,
+ AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12,
+ AV_PIX_FMT_YUV440P12,
+ AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14,
+ AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
+ AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
+ AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
+ AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P,
+ AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA444P16,
+ AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA422P16,
+ AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16,
+ AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
AV_PIX_FMT_NONE
};
s->pix_desc = av_pix_fmt_desc_get(outlink->format);
if (!s->pix_desc)
return AVERROR_BUG;
+ s->depth = s->pix_desc->comp[0].depth;
// modify output properties as needed
switch (s->format) {
FramepackContext *s = ctx->priv;
int i, plane;
- if (interleaved) {
+ if (interleaved && s->depth <= 8) {
const uint8_t *leftp = s->input_views[LEFT]->data[0];
const uint8_t *rightp = s->input_views[RIGHT]->data[0];
uint8_t *dstp = out->data[0];
}
}
}
+ } else if (interleaved && s->depth > 8) {
+ const uint16_t *leftp = (const uint16_t *)s->input_views[LEFT]->data[0];
+ const uint16_t *rightp = (const uint16_t *)s->input_views[RIGHT]->data[0];
+ uint16_t *dstp = (uint16_t *)out->data[0];
+ int length = out->width / 2;
+ int lines = out->height;
+
+ for (plane = 0; plane < s->pix_desc->nb_components; plane++) {
+ if (plane == 1 || plane == 2) {
+ length = AV_CEIL_RSHIFT(out->width / 2, s->pix_desc->log2_chroma_w);
+ lines = AV_CEIL_RSHIFT(out->height, s->pix_desc->log2_chroma_h);
+ }
+ for (i = 0; i < lines; i++) {
+ int j;
+ leftp = (const uint16_t *)s->input_views[LEFT]->data[plane] +
+ s->input_views[LEFT]->linesize[plane] * i / 2;
+ rightp = (const uint16_t *)s->input_views[RIGHT]->data[plane] +
+ s->input_views[RIGHT]->linesize[plane] * i / 2;
+ dstp = (uint16_t *)out->data[plane] + out->linesize[plane] * i / 2;
+ for (j = 0; j < length; j++) {
+ // interpolate chroma as necessary
+ if ((s->pix_desc->log2_chroma_w ||
+ s->pix_desc->log2_chroma_h) &&
+ (plane == 1 || plane == 2)) {
+ *dstp++ = (*leftp + *rightp) / 2;
+ *dstp++ = (*leftp + *rightp) / 2;
+ } else {
+ *dstp++ = *leftp;
+ *dstp++ = *rightp;
+ }
+ leftp += 1;
+ rightp += 1;
+ }
+ }
+ }
} else {
for (i = 0; i < 2; i++) {
+ const int psize = 1 + (s->depth > 8);
const uint8_t *src[4];
uint8_t *dst[4];
- int sub_w = s->input_views[i]->width >> s->pix_desc->log2_chroma_w;
+ int sub_w = psize * s->input_views[i]->width >> s->pix_desc->log2_chroma_w;
src[0] = s->input_views[i]->data[0];
src[1] = s->input_views[i]->data[1];
src[2] = s->input_views[i]->data[2];
- dst[0] = out->data[0] + i * s->input_views[i]->width;
+ dst[0] = out->data[0] + i * s->input_views[i]->width * psize;
dst[1] = out->data[1] + i * sub_w;
dst[2] = out->data[2] + i * sub_w;
}
}
-static int try_push_frame(AVFilterContext *ctx);
-
-static int filter_frame_left(AVFilterLink *inlink, AVFrame *frame)
-{
- FramepackContext *s = inlink->dst->priv;
- s->input_views[LEFT] = frame;
- return try_push_frame(inlink->dst);
-}
-
-static int filter_frame_right(AVFilterLink *inlink, AVFrame *frame)
-{
- FramepackContext *s = inlink->dst->priv;
- s->input_views[RIGHT] = frame;
- return try_push_frame(inlink->dst);
-}
-
-static int request_frame(AVFilterLink *outlink)
-{
- AVFilterContext *ctx = outlink->src;
- FramepackContext *s = ctx->priv;
- int ret, i;
-
- /* get a frame on the either input, stop as soon as a video ends */
- for (i = 0; i < 2; i++) {
- if (!s->input_views[i]) {
- ret = ff_request_frame(ctx->inputs[i]);
- if (ret < 0)
- return ret;
- }
- }
- return 0;
-}
-
static int try_push_frame(AVFilterContext *ctx)
{
FramepackContext *s = ctx->priv;
for (i = 0; i < 2; i++) {
// set correct timestamps
if (pts != AV_NOPTS_VALUE)
- s->input_views[i]->pts = i == 0 ? pts * 2 : s->input_views[1]->pts + pts;
+ s->input_views[i]->pts = i == 0 ? pts * 2 : pts * 2 + av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base);
// set stereo3d side data
stereo = av_stereo3d_create_side_data(s->input_views[i]);
}
}
+static int activate(AVFilterContext *ctx)
+{
+ AVFilterLink *outlink = ctx->outputs[0];
+ FramepackContext *s = ctx->priv;
+ int ret;
+
+ FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx);
+
+ if (!s->input_views[0]) {
+ ret = ff_inlink_consume_frame(ctx->inputs[0], &s->input_views[0]);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (!s->input_views[1]) {
+ ret = ff_inlink_consume_frame(ctx->inputs[1], &s->input_views[1]);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (s->input_views[0] && s->input_views[1])
+ return try_push_frame(ctx);
+
+ FF_FILTER_FORWARD_STATUS(ctx->inputs[0], outlink);
+ FF_FILTER_FORWARD_STATUS(ctx->inputs[1], outlink);
+
+ if (ff_outlink_frame_wanted(ctx->outputs[0]) &&
+ !ff_outlink_get_status(ctx->inputs[0]) &&
+ !s->input_views[0]) {
+ ff_inlink_request_frame(ctx->inputs[0]);
+ return 0;
+ }
+
+ if (ff_outlink_frame_wanted(ctx->outputs[0]) &&
+ !ff_outlink_get_status(ctx->inputs[1]) &&
+ !s->input_views[1]) {
+ ff_inlink_request_frame(ctx->inputs[1]);
+ return 0;
+ }
+
+ return FFERROR_NOT_READY;
+}
+
#define OFFSET(x) offsetof(FramepackContext, x)
#define VF AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
static const AVOption framepack_options[] = {
{
.name = "left",
.type = AVMEDIA_TYPE_VIDEO,
- .filter_frame = filter_frame_left,
- .needs_fifo = 1,
},
{
.name = "right",
.type = AVMEDIA_TYPE_VIDEO,
- .filter_frame = filter_frame_right,
- .needs_fifo = 1,
},
{ NULL }
};
.name = "packed",
.type = AVMEDIA_TYPE_VIDEO,
.config_props = config_output,
- .request_frame = request_frame,
},
{ NULL }
};
-AVFilter ff_vf_framepack = {
+const AVFilter ff_vf_framepack = {
.name = "framepack",
.description = NULL_IF_CONFIG_SMALL("Generate a frame packed stereoscopic video."),
.priv_size = sizeof(FramepackContext),
.query_formats = query_formats,
.inputs = framepack_inputs,
.outputs = framepack_outputs,
+ .activate = activate,
.uninit = framepack_uninit,
};