On Thu, 12 Dec 2013 18:00:00 +0100, Vittorio Giovara
<[email protected]> wrote:
> ---
> This new iteration removes quincunx support (was the only format that
> forcifully reduced resolution), adds a check for a valid input and completes
> documentation with some examples.
> Cheers,
> Vittorio
>
> Changelog | 1 +
> doc/filters.texi | 28 +++
> libavfilter/Makefile | 1 +
> libavfilter/allfilters.c | 1 +
> libavfilter/version.h | 4 +-
> libavfilter/vf_framepack.c | 406
> ++++++++++++++++++++++++++++++++++++++++++++
> 6 files changed, 439 insertions(+), 2 deletions(-)
> create mode 100644 libavfilter/vf_framepack.c
>
> diff --git a/Changelog b/Changelog
> index e7a8e44..b21954a 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -51,6 +51,7 @@ version 10:
> - support for decoding through VDPAU in avconv (the -hwaccel option)
> - remove mp3_header_(de)compress bitstream filters
> - stereoscopic 3d metadata handling
> +- framepack filter
>
>
> version 9:
> diff --git a/doc/filters.texi b/doc/filters.texi
> index b32aad1..af9b06a 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -1252,6 +1252,34 @@ frames with a negative PTS.
>
> @end table
>
> +@section framepack
> +
> +Packs two different video streams into a stereoscopic video. The two videos
> +should have the same size and framerate; you may conveniently adjust those
> +values with the @ref{scale} and @ref{fps} filters. If one video lasts longer
> +than the other, the last frame is repeated.
> +
> +This filter also sets proper signalling on supported codecs.
> +
> +This filter accepts the following named parameters:
> +@table @option
> +
> +@item format
> +Desired framepacking format. Supported values are @var{sbs} (default),
> + @var{tab}, @var{lines}, @var{columns} and @var{frameseq}.
> +See a detailed format description in @file{libavutil/stereo3d.h}.
> +
> +@end table
> +
> +@example
> +#convert left and right views into a frame sequential video
> +avconv -i LEFT -i RIGHT -filter_complex framepack=frameseq OUTPUT
> +
> +#convert views into a side-by-side video with same output resolution as input
> +avconv -i LEFT -i RIGHT -filter_complex
> [0:v]scale=w=iw/2[left],[1:v]scale=w=iw/2[right],[left][right]framepack=sbs
> OUTPUT
> +
> +@end example
> +
> @anchor{frei0r}
> @section frei0r
>
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index 96fa8c0..92c1561 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -54,6 +54,7 @@ OBJS-$(CONFIG_FADE_FILTER) += vf_fade.o
> OBJS-$(CONFIG_FIELDORDER_FILTER) += vf_fieldorder.o
> OBJS-$(CONFIG_FORMAT_FILTER) += vf_format.o
> OBJS-$(CONFIG_FPS_FILTER) += vf_fps.o
> +OBJS-$(CONFIG_FRAMEPACK_FILTER) += vf_framepack.o
> OBJS-$(CONFIG_FREI0R_FILTER) += vf_frei0r.o
> OBJS-$(CONFIG_GRADFUN_FILTER) += vf_gradfun.o
> OBJS-$(CONFIG_HFLIP_FILTER) += vf_hflip.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index f041f5c..9702a0a 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -74,6 +74,7 @@ void avfilter_register_all(void)
> REGISTER_FILTER(FIELDORDER, fieldorder, vf);
> REGISTER_FILTER(FORMAT, format, vf);
> REGISTER_FILTER(FPS, fps, vf);
> + REGISTER_FILTER(FRAMEPACK, framepack, vf);
> REGISTER_FILTER(FREI0R, frei0r, vf);
> REGISTER_FILTER(GRADFUN, gradfun, vf);
> REGISTER_FILTER(HFLIP, hflip, vf);
> diff --git a/libavfilter/version.h b/libavfilter/version.h
> index bc079b4..3f7d9dc 100644
> --- a/libavfilter/version.h
> +++ b/libavfilter/version.h
> @@ -30,8 +30,8 @@
> #include "libavutil/avutil.h"
>
> #define LIBAVFILTER_VERSION_MAJOR 3
> -#define LIBAVFILTER_VERSION_MINOR 11
> -#define LIBAVFILTER_VERSION_MICRO 1
> +#define LIBAVFILTER_VERSION_MINOR 12
> +#define LIBAVFILTER_VERSION_MICRO 0
>
> #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
> LIBAVFILTER_VERSION_MINOR, \
> diff --git a/libavfilter/vf_framepack.c b/libavfilter/vf_framepack.c
> new file mode 100644
> index 0000000..5a8b306
> --- /dev/null
> +++ b/libavfilter/vf_framepack.c
> @@ -0,0 +1,406 @@
> +/*
> + * Copyright (c) 2013 Vittorio Giovara
> + *
> + * This file is part of Libav.
> + *
> + * Libav is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * Libav is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with Libav; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
> USA
> + */
> +
> +/**
> + * @file
> + * Generate a frame packed video, by combining two views in a single surface.
> + */
> +
> +#include <string.h>
> +
> +#include "libavutil/imgutils.h"
> +#include "libavutil/opt.h"
> +#include "libavutil/pixdesc.h"
> +#include "libavutil/rational.h"
> +#include "libavutil/stereo3d.h"
> +
> +#include "avfilter.h"
> +#include "formats.h"
> +#include "internal.h"
> +#include "video.h"
> +
> +#define LEFT 0
> +#define RIGHT 1
> +
> +typedef struct FramepackContext {
> + const AVClass *class;
> +
> + const AVPixFmtDescriptor *pix_desc; ///< agreed pixel format
> +
> + enum AVStereo3DType format; ///< frame packed output
> +
> + AVFrame *input_views[2]; ///< input frames
> +
> + int64_t double_pts; ///< new pts for frameseq mode
> +} 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_NONE
> +};
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> + // this will ensure that formats are the same on all pads
> + ff_set_common_formats(ctx, ff_make_format_list(formats_supported));
> + return 0;
> +}
> +
> +static av_cold void framepack_uninit(AVFilterContext *ctx)
> +{
> + FramepackContext *s = ctx->priv;
> +
> + // clean any leftover frame
> + av_frame_free(&s->input_views[LEFT]);
> + av_frame_free(&s->input_views[RIGHT]);
> +}
> +
> +static int config_input_left(AVFilterLink *inlink)
> +{
> + FramepackContext *s = inlink->dst->priv;
> +
> + s->pix_desc = av_pix_fmt_desc_get(inlink->format);
> + if (!s->pix_desc)
> + return AVERROR_BUG;
> +
> + return 0;
> +}
> +
> +static int config_input_right(AVFilterLink *inlink)
> +{
> + FramepackContext *s = inlink->dst->priv;
> +
> + s->pix_desc = av_pix_fmt_desc_get(inlink->format);
> + if (!s->pix_desc)
> + return AVERROR_BUG;
> +
> + return 0;
> +}
You can just set pix_desc in config_output() and remove those two identical
functions.
> +
> +static int config_output(AVFilterLink *outlink)
> +{
> + AVFilterContext *ctx = outlink->src;
> + FramepackContext *s = outlink->src->priv;
> +
> + int width = ctx->inputs[LEFT]->w;
> + int height = ctx->inputs[LEFT]->h;
> + AVRational time_base = ctx->inputs[LEFT]->time_base;
> +
> + // check size and fps match on the other input
> + if (width != ctx->inputs[RIGHT]->w ||
> + height != ctx->inputs[RIGHT]->h) {
> + av_log(ctx, AV_LOG_ERROR,
> + "Left and right sizes differ (%dx%d vs %dx%d).\n",
> + width, height,
> + ctx->inputs[RIGHT]->w, ctx->inputs[RIGHT]->h);
> + return AVERROR_INVALIDDATA;
> + } else if (av_cmp_q(time_base, ctx->inputs[RIGHT]->time_base) != 0) {
> + av_log(ctx, AV_LOG_ERROR,
> + "Left and right framerates differ (%d/%d vs %d/%d).\n",
> + time_base.num, time_base.den,
> + ctx->inputs[RIGHT]->time_base.num,
> + ctx->inputs[RIGHT]->time_base.den);
> + return AVERROR_INVALIDDATA;
> + }
> +
> + // modify output properties as needed
> + switch (s->format) {
> + case AV_STEREO3D_FRAMESEQUENCE:
> + time_base.den *= 2;
> + s->double_pts = AV_NOPTS_VALUE;
> + break;
> + case AV_STEREO3D_COLUMNS:
> + case AV_STEREO3D_SIDEBYSIDE:
> + width *= 2;
> + break;
> + case AV_STEREO3D_LINES:
> + case AV_STEREO3D_TOPBOTTOM:
> + height *= 2;
> + break;
> + default:
> + av_log(ctx, AV_LOG_ERROR, "Unknown packing mode.");
> + return AVERROR_INVALIDDATA;
> + break;
pointless break after return
> + }
> +
> + outlink->w = width;
> + outlink->h = height;
> + outlink->time_base = time_base;
> +
> + return 0;
> +}
> +
> +static void pack_sidebyside_frame(FramepackContext *s,
> + AVFrame *dst,
> + int interleaved)
> +{
> + int plane, i;
> + int length = dst->width / 2;
> + int lines = dst->height;
> +
> + for (plane = 0; plane < s->pix_desc->nb_components; plane++) {
> + const uint8_t *leftp = s->input_views[LEFT]->data[plane];
> + const uint8_t *rightp = s->input_views[RIGHT]->data[plane];
> + uint8_t *dstp = dst->data[plane];
> +
> + if (plane == 1 || plane == 2) {
> + length = -(-dst->width / 2 >> s->pix_desc->log2_chroma_w);
> + lines = -(-dst->height >> s->pix_desc->log2_chroma_h);
> + }
> +
> + if (interleaved) {
> + for (i = 0; i < lines; i++) {
> + int j;
> + int k = 0;
> +
> + for (j = 0; j < length; j++) {
> + dstp[k++] = leftp[j];
> + dstp[k++] = rightp[j];
> + }
> +
> + dstp += dst->linesize[plane];
> + leftp += s->input_views[LEFT]->linesize[plane];
> + rightp += s->input_views[RIGHT]->linesize[plane];
> + }
> + } else {
> + av_image_copy_plane(dst->data[plane], dst->linesize[plane],
> + leftp, s->input_views[LEFT]->linesize[plane],
> + length, lines);
> + av_image_copy_plane(dst->data[plane] + length,
> dst->linesize[plane],
> + rightp,
> s->input_views[RIGHT]->linesize[plane],
> + length, lines);
> + }
> + }
> +
> + return;
> +}
> +
> +static void pack_topbottom_frame(FramepackContext *s,
> + AVFrame *dst,
> + int interleaved)
> +{
> + int plane, i;
> + int length = dst->width;
> + int lines = dst->height / 2;
> +
> + for (plane = 0; plane < s->pix_desc->nb_components; plane++) {
> + const uint8_t *leftp = s->input_views[LEFT]->data[plane];
> + const uint8_t *rightp = s->input_views[RIGHT]->data[plane];
> + uint8_t *dstp = dst->data[plane];
> +
> + if (plane == 1 || plane == 2) {
> + length = -(-dst->width >> s->pix_desc->log2_chroma_w);
> + lines = -(-dst->height / 2 >> s->pix_desc->log2_chroma_h);
> + }
> +
> + if (interleaved) {
> + for (i = 0; i < lines; i ++) {
> + memcpy(dstp, leftp, length);
> + leftp += s->input_views[LEFT]->linesize[plane];
> + dstp += dst->linesize[plane];
> +
> + memcpy(dstp, rightp, length);
> + rightp += s->input_views[RIGHT]->linesize[plane];
> + dstp += dst->linesize[plane];
> + }
> + } else {
> + av_image_copy_plane(dst->data[plane],
> + dst->linesize[plane],
> + leftp, s->input_views[LEFT]->linesize[plane],
> + length, lines);
> + av_image_copy_plane(dst->data[plane] + dst->linesize[plane] *
> lines,
> + dst->linesize[plane],
> + rightp,
> s->input_views[RIGHT]->linesize[plane],
> + length, lines);
> + }
> + }
> +
> + return;
> +}
> +
> +static av_always_inline void pack_frame(FramepackContext *s, AVFrame *dst)
> +{
> + switch (s->format) {
> + case AV_STEREO3D_COLUMNS:
> + pack_sidebyside_frame(s, dst, 1);
> + break;
> + case AV_STEREO3D_SIDEBYSIDE:
> + pack_sidebyside_frame(s, dst, 0);
> + break;
> + case AV_STEREO3D_LINES:
> + pack_topbottom_frame(s, dst, 1);
> + break;
> + case AV_STEREO3D_TOPBOTTOM:
> + pack_topbottom_frame(s, dst, 0);
> + break;
> + }
> +}
> +
> +static int filter_frame_left(AVFilterLink *inlink, AVFrame *frame)
> +{
> + FramepackContext *s = inlink->dst->priv;
> + s->input_views[LEFT] = frame;
> + return 0;
> +}
> +
> +static int filter_frame_right(AVFilterLink *inlink, AVFrame *frame)
> +{
> + FramepackContext *s = inlink->dst->priv;
> + s->input_views[RIGHT] = frame;
> + return 0;
> +}
> +
> +static int request_frame(AVFilterLink *outlink)
> +{
> + AVFilterContext *ctx = outlink->src;
> + FramepackContext *s = ctx->priv;
> + AVFrame *out;
> + AVStereo3D *stereo;
> + int ret, ret_eof[2], i;
> +
> + /* get a frame on the either input, on EOF reuse previous */
> + for (i = 0; i < 2; i++) {
> + if (!s->input_views[i]) {
> + ret_eof[i] = ff_request_frame(ctx->inputs[i]);
> + if (ret_eof[i] < 0 && ret_eof[i] != AVERROR_EOF)
> + return ret_eof[i];
> + }
> + }
> + /* when both return values are EOF, it means that
> + * videos are fully processed and we can stop */
> + if (ret_eof[LEFT] == ret_eof[RIGHT] && ret_eof[LEFT] == AVERROR_EOF)
> + return AVERROR_EOF;
> +
> + if (s->format == AV_STEREO3D_FRAMESEQUENCE) {
> + if (s->double_pts == AV_NOPTS_VALUE)
> + s->double_pts = s->input_views[LEFT]->pts;
> +
> + for (i = 0; i < 2; i++) {
> + out = av_frame_clone(s->input_views[i]);
> + if (!out)
> + return AVERROR(ENOMEM);
> +
> + out->pts = s->double_pts++;
> +
> + stereo = av_stereo3d_create_side_data(out);
> + if (!stereo)
> + return AVERROR(ENOMEM);
> + stereo->type = s->format;
> +
> + if (ret_eof[i] != AVERROR_EOF)
> + av_frame_free(&s->input_views[i]);
> +
> + ret = ff_filter_frame(outlink, out);
> + if (ret < 0)
> + return ret;
> + }
> + return ret;
> + } else {
> + out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
> + if (!out)
> + return AVERROR(ENOMEM);
> +
> + pack_frame(s, out);
> + }
> +
> + // get any property from the original frame
> + ret = av_frame_copy_props(out, s->input_views[LEFT]);
> + if (ret < 0)
> + return ret;
Leaking out here
> +
> + // set stereo3d side data
> + stereo = av_stereo3d_create_side_data(out);
> + if (!stereo)
> + return AVERROR(ENOMEM);
and here
> + stereo->type = s->format;
> +
> + // cleanup keeping a valid frame on EOF
> + for (i = 0; i < 2; i++)
> + if (ret_eof[i] != AVERROR_EOF)
> + av_frame_free(&s->input_views[i]);
How is that supposed to work? If you free the frames here and then get an EOF on
the next call then you have nothing to work with.
And anyway, is this special EOF handling even useful here? It is useful in
overlay to allow overlaying a single image over a long video, but IIUC it would
break horribly here if the two inputs are not exactly matched.
--
Anton Khirnov
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel