LCOV - code coverage report
Current view: top level - libavfilter - vf_framepack.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 167 190 87.9 %
Date: 2017-12-16 13:57:32 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Vittorio Giovara
       3             :  *
       4             :  * This file is part of FFmpeg.
       5             :  *
       6             :  * FFmpeg is free software; you can redistribute it and/or
       7             :  * modify it under the terms of the GNU Lesser General Public
       8             :  * License as published by the Free Software Foundation; either
       9             :  * version 2.1 of the License, or (at your option) any later version.
      10             :  *
      11             :  * FFmpeg is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :  * Lesser General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU Lesser General Public
      17             :  * License along with FFmpeg; if not, write to the Free Software
      18             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      19             :  */
      20             : 
      21             : /**
      22             :  * @file
      23             :  * Generate a frame packed video, by combining two views in a single surface.
      24             :  */
      25             : 
      26             : #include <string.h>
      27             : 
      28             : #include "libavutil/common.h"
      29             : #include "libavutil/imgutils.h"
      30             : #include "libavutil/opt.h"
      31             : #include "libavutil/pixdesc.h"
      32             : #include "libavutil/rational.h"
      33             : #include "libavutil/stereo3d.h"
      34             : 
      35             : #include "avfilter.h"
      36             : #include "formats.h"
      37             : #include "internal.h"
      38             : #include "video.h"
      39             : 
      40             : #define LEFT  0
      41             : #define RIGHT 1
      42             : 
      43             : typedef struct FramepackContext {
      44             :     const AVClass *class;
      45             : 
      46             :     const AVPixFmtDescriptor *pix_desc; ///< agreed pixel format
      47             : 
      48             :     enum AVStereo3DType format;         ///< frame pack type output
      49             : 
      50             :     AVFrame *input_views[2];            ///< input frames
      51             : 
      52             :     int64_t double_pts;                 ///< new pts for frameseq mode
      53             : } FramepackContext;
      54             : 
      55             : static const enum AVPixelFormat formats_supported[] = {
      56             :     AV_PIX_FMT_YUV420P,  AV_PIX_FMT_YUV422P,  AV_PIX_FMT_YUV444P,
      57             :     AV_PIX_FMT_YUV410P,  AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVJ420P,
      58             :     AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
      59             :     AV_PIX_FMT_NONE
      60             : };
      61             : 
      62           5 : static int query_formats(AVFilterContext *ctx)
      63             : {
      64             :     // this will ensure that formats are the same on all pads
      65           5 :     AVFilterFormats *fmts_list = ff_make_format_list(formats_supported);
      66           5 :     if (!fmts_list)
      67           0 :         return AVERROR(ENOMEM);
      68           5 :     return ff_set_common_formats(ctx, fmts_list);
      69             : }
      70             : 
      71          10 : static av_cold void framepack_uninit(AVFilterContext *ctx)
      72             : {
      73          10 :     FramepackContext *s = ctx->priv;
      74             : 
      75             :     // clean any leftover frame
      76          10 :     av_frame_free(&s->input_views[LEFT]);
      77          10 :     av_frame_free(&s->input_views[RIGHT]);
      78          10 : }
      79             : 
      80           5 : static int config_output(AVFilterLink *outlink)
      81             : {
      82           5 :     AVFilterContext *ctx  = outlink->src;
      83           5 :     FramepackContext *s   = outlink->src->priv;
      84             : 
      85           5 :     int width             = ctx->inputs[LEFT]->w;
      86           5 :     int height            = ctx->inputs[LEFT]->h;
      87           5 :     AVRational time_base  = ctx->inputs[LEFT]->time_base;
      88           5 :     AVRational frame_rate = ctx->inputs[LEFT]->frame_rate;
      89             : 
      90             :     // check size and fps match on the other input
      91          10 :     if (width  != ctx->inputs[RIGHT]->w ||
      92           5 :         height != ctx->inputs[RIGHT]->h) {
      93           0 :         av_log(ctx, AV_LOG_ERROR,
      94             :                "Left and right sizes differ (%dx%d vs %dx%d).\n",
      95             :                width, height,
      96           0 :                ctx->inputs[RIGHT]->w, ctx->inputs[RIGHT]->h);
      97           0 :         return AVERROR_INVALIDDATA;
      98           5 :     } else if (av_cmp_q(time_base, ctx->inputs[RIGHT]->time_base) != 0) {
      99           0 :         av_log(ctx, AV_LOG_ERROR,
     100             :                "Left and right time bases differ (%d/%d vs %d/%d).\n",
     101             :                time_base.num, time_base.den,
     102           0 :                ctx->inputs[RIGHT]->time_base.num,
     103           0 :                ctx->inputs[RIGHT]->time_base.den);
     104           0 :         return AVERROR_INVALIDDATA;
     105           5 :     } else if (av_cmp_q(frame_rate, ctx->inputs[RIGHT]->frame_rate) != 0) {
     106           0 :         av_log(ctx, AV_LOG_ERROR,
     107             :                "Left and right framerates differ (%d/%d vs %d/%d).\n",
     108             :                frame_rate.num, frame_rate.den,
     109           0 :                ctx->inputs[RIGHT]->frame_rate.num,
     110           0 :                ctx->inputs[RIGHT]->frame_rate.den);
     111           0 :         return AVERROR_INVALIDDATA;
     112             :     }
     113             : 
     114           5 :     s->pix_desc = av_pix_fmt_desc_get(outlink->format);
     115           5 :     if (!s->pix_desc)
     116           0 :         return AVERROR_BUG;
     117             : 
     118             :     // modify output properties as needed
     119           5 :     switch (s->format) {
     120           1 :     case AV_STEREO3D_FRAMESEQUENCE:
     121           1 :         time_base.den  *= 2;
     122           1 :         frame_rate.num *= 2;
     123             : 
     124           1 :         s->double_pts = AV_NOPTS_VALUE;
     125           1 :         break;
     126           2 :     case AV_STEREO3D_COLUMNS:
     127             :     case AV_STEREO3D_SIDEBYSIDE:
     128           2 :         width *= 2;
     129           2 :         break;
     130           2 :     case AV_STEREO3D_LINES:
     131             :     case AV_STEREO3D_TOPBOTTOM:
     132           2 :         height *= 2;
     133           2 :         break;
     134           0 :     default:
     135           0 :         av_log(ctx, AV_LOG_ERROR, "Unknown packing mode.");
     136           0 :         return AVERROR_INVALIDDATA;
     137             :     }
     138             : 
     139           5 :     outlink->w          = width;
     140           5 :     outlink->h          = height;
     141           5 :     outlink->time_base  = time_base;
     142           5 :     outlink->frame_rate = frame_rate;
     143             : 
     144           5 :     return 0;
     145             : }
     146             : 
     147          30 : static void horizontal_frame_pack(AVFilterLink *outlink,
     148             :                                   AVFrame *out,
     149             :                                   int interleaved)
     150             : {
     151          30 :     AVFilterContext *ctx = outlink->src;
     152          30 :     FramepackContext *s = ctx->priv;
     153             :     int i, plane;
     154             : 
     155          30 :     if (interleaved) {
     156          15 :         const uint8_t *leftp  = s->input_views[LEFT]->data[0];
     157          15 :         const uint8_t *rightp = s->input_views[RIGHT]->data[0];
     158          15 :         uint8_t *dstp         = out->data[0];
     159          15 :         int length = out->width / 2;
     160          15 :         int lines  = out->height;
     161             : 
     162          60 :         for (plane = 0; plane < s->pix_desc->nb_components; plane++) {
     163          45 :             if (plane == 1 || plane == 2) {
     164          30 :                 length = AV_CEIL_RSHIFT(out->width / 2, s->pix_desc->log2_chroma_w);
     165          30 :                 lines  = AV_CEIL_RSHIFT(out->height,    s->pix_desc->log2_chroma_h);
     166             :             }
     167        8685 :             for (i = 0; i < lines; i++) {
     168             :                 int j;
     169       17280 :                 leftp  = s->input_views[LEFT]->data[plane] +
     170        8640 :                          s->input_views[LEFT]->linesize[plane] * i;
     171       17280 :                 rightp = s->input_views[RIGHT]->data[plane] +
     172        8640 :                          s->input_views[RIGHT]->linesize[plane] * i;
     173        8640 :                 dstp   = out->data[plane] + out->linesize[plane] * i;
     174     2289600 :                 for (j = 0; j < length; j++) {
     175             :                     // interpolate chroma as necessary
     176     2280960 :                     if ((s->pix_desc->log2_chroma_w ||
     177     2280960 :                          s->pix_desc->log2_chroma_h) &&
     178     1900800 :                         (plane == 1 || plane == 2)) {
     179      760320 :                         *dstp++ = (*leftp + *rightp) / 2;
     180      760320 :                         *dstp++ = (*leftp + *rightp) / 2;
     181             :                     } else {
     182     1520640 :                         *dstp++ = *leftp;
     183     1520640 :                         *dstp++ = *rightp;
     184             :                     }
     185     2280960 :                     leftp += 1;
     186     2280960 :                     rightp += 1;
     187             :                 }
     188             :             }
     189             :         }
     190             :     } else {
     191          45 :         for (i = 0; i < 2; i++) {
     192             :             const uint8_t *src[4];
     193             :             uint8_t *dst[4];
     194          30 :             int sub_w = s->input_views[i]->width >> s->pix_desc->log2_chroma_w;
     195             : 
     196          30 :             src[0] = s->input_views[i]->data[0];
     197          30 :             src[1] = s->input_views[i]->data[1];
     198          30 :             src[2] = s->input_views[i]->data[2];
     199             : 
     200          30 :             dst[0] = out->data[0] + i * s->input_views[i]->width;
     201          30 :             dst[1] = out->data[1] + i * sub_w;
     202          30 :             dst[2] = out->data[2] + i * sub_w;
     203             : 
     204          90 :             av_image_copy(dst, out->linesize, src, s->input_views[i]->linesize,
     205          30 :                           s->input_views[i]->format,
     206          30 :                           s->input_views[i]->width,
     207          30 :                           s->input_views[i]->height);
     208             :         }
     209             :     }
     210          30 : }
     211             : 
     212          30 : static void vertical_frame_pack(AVFilterLink *outlink,
     213             :                                 AVFrame *out,
     214             :                                 int interleaved)
     215             : {
     216          30 :     AVFilterContext *ctx = outlink->src;
     217          30 :     FramepackContext *s = ctx->priv;
     218             :     int i;
     219             : 
     220          90 :     for (i = 0; i < 2; i++) {
     221             :         const uint8_t *src[4];
     222             :         uint8_t *dst[4];
     223             :         int linesizes[4];
     224          60 :         int sub_h = s->input_views[i]->height >> s->pix_desc->log2_chroma_h;
     225             : 
     226          60 :         src[0] = s->input_views[i]->data[0];
     227          60 :         src[1] = s->input_views[i]->data[1];
     228          60 :         src[2] = s->input_views[i]->data[2];
     229             : 
     230         120 :         dst[0] = out->data[0] + i * out->linesize[0] *
     231          60 :                  (interleaved + s->input_views[i]->height * (1 - interleaved));
     232         120 :         dst[1] = out->data[1] + i * out->linesize[1] *
     233          60 :                  (interleaved + sub_h * (1 - interleaved));
     234         120 :         dst[2] = out->data[2] + i * out->linesize[2] *
     235          60 :                  (interleaved + sub_h * (1 - interleaved));
     236             : 
     237          60 :         linesizes[0] = out->linesize[0] +
     238             :                        interleaved * out->linesize[0];
     239          60 :         linesizes[1] = out->linesize[1] +
     240             :                        interleaved * out->linesize[1];
     241          60 :         linesizes[2] = out->linesize[2] +
     242             :                        interleaved * out->linesize[2];
     243             : 
     244         180 :         av_image_copy(dst, linesizes, src, s->input_views[i]->linesize,
     245          60 :                       s->input_views[i]->format,
     246          60 :                       s->input_views[i]->width,
     247          60 :                       s->input_views[i]->height);
     248             :     }
     249          30 : }
     250             : 
     251          60 : static av_always_inline void spatial_frame_pack(AVFilterLink *outlink,
     252             :                                                 AVFrame *dst)
     253             : {
     254          60 :     AVFilterContext *ctx = outlink->src;
     255          60 :     FramepackContext *s = ctx->priv;
     256          60 :     switch (s->format) {
     257          15 :     case AV_STEREO3D_SIDEBYSIDE:
     258          15 :         horizontal_frame_pack(outlink, dst, 0);
     259          15 :         break;
     260          15 :     case AV_STEREO3D_COLUMNS:
     261          15 :         horizontal_frame_pack(outlink, dst, 1);
     262          15 :         break;
     263          15 :     case AV_STEREO3D_TOPBOTTOM:
     264          15 :         vertical_frame_pack(outlink, dst, 0);
     265          15 :         break;
     266          15 :     case AV_STEREO3D_LINES:
     267          15 :         vertical_frame_pack(outlink, dst, 1);
     268          15 :         break;
     269             :     }
     270          60 : }
     271             : 
     272             : static int try_push_frame(AVFilterContext *ctx);
     273             : 
     274          68 : static int filter_frame_left(AVFilterLink *inlink, AVFrame *frame)
     275             : {
     276          68 :     FramepackContext *s = inlink->dst->priv;
     277          68 :     s->input_views[LEFT] = frame;
     278          68 :     return try_push_frame(inlink->dst);
     279             : }
     280             : 
     281          68 : static int filter_frame_right(AVFilterLink *inlink, AVFrame *frame)
     282             : {
     283          68 :     FramepackContext *s = inlink->dst->priv;
     284          68 :     s->input_views[RIGHT] = frame;
     285          68 :     return try_push_frame(inlink->dst);
     286             : }
     287             : 
     288         141 : static int request_frame(AVFilterLink *outlink)
     289             : {
     290         141 :     AVFilterContext *ctx = outlink->src;
     291         141 :     FramepackContext *s = ctx->priv;
     292             :     int ret, i;
     293             : 
     294             :     /* get a frame on the either input, stop as soon as a video ends */
     295         413 :     for (i = 0; i < 2; i++) {
     296         277 :         if (!s->input_views[i]) {
     297         209 :             ret = ff_request_frame(ctx->inputs[i]);
     298         209 :             if (ret < 0)
     299           5 :                 return ret;
     300             :         }
     301             :     }
     302         136 :     return 0;
     303             : }
     304             : 
     305         136 : static int try_push_frame(AVFilterContext *ctx)
     306             : {
     307         136 :     FramepackContext *s = ctx->priv;
     308         136 :     AVFilterLink *outlink = ctx->outputs[0];
     309             :     AVStereo3D *stereo;
     310             :     int ret, i;
     311             : 
     312         136 :     if (!(s->input_views[0] && s->input_views[1]))
     313          68 :         return 0;
     314          68 :     if (s->format == AV_STEREO3D_FRAMESEQUENCE) {
     315           8 :         if (s->double_pts == AV_NOPTS_VALUE)
     316           1 :             s->double_pts = s->input_views[LEFT]->pts;
     317             : 
     318          24 :         for (i = 0; i < 2; i++) {
     319             :             // set correct timestamps
     320          16 :             s->input_views[i]->pts = s->double_pts++;
     321             : 
     322             :             // set stereo3d side data
     323          16 :             stereo = av_stereo3d_create_side_data(s->input_views[i]);
     324          16 :             if (!stereo)
     325           0 :                 return AVERROR(ENOMEM);
     326          16 :             stereo->type = s->format;
     327          16 :             stereo->view = i == LEFT ? AV_STEREO3D_VIEW_LEFT
     328          16 :                                      : AV_STEREO3D_VIEW_RIGHT;
     329             : 
     330             :             // filter the frame and immediately relinquish its pointer
     331          16 :             ret = ff_filter_frame(outlink, s->input_views[i]);
     332          16 :             s->input_views[i] = NULL;
     333          16 :             if (ret < 0)
     334           0 :                 return ret;
     335             :         }
     336           8 :         return ret;
     337             :     } else {
     338          60 :         AVFrame *dst = ff_get_video_buffer(outlink, outlink->w, outlink->h);
     339          60 :         if (!dst)
     340           0 :             return AVERROR(ENOMEM);
     341             : 
     342          60 :         spatial_frame_pack(outlink, dst);
     343             : 
     344             :         // get any property from the original frame
     345          60 :         ret = av_frame_copy_props(dst, s->input_views[LEFT]);
     346          60 :         if (ret < 0) {
     347           0 :             av_frame_free(&dst);
     348           0 :             return ret;
     349             :         }
     350             : 
     351         180 :         for (i = 0; i < 2; i++)
     352         120 :             av_frame_free(&s->input_views[i]);
     353             : 
     354             :         // set stereo3d side data
     355          60 :         stereo = av_stereo3d_create_side_data(dst);
     356          60 :         if (!stereo) {
     357           0 :             av_frame_free(&dst);
     358           0 :             return AVERROR(ENOMEM);
     359             :         }
     360          60 :         stereo->type = s->format;
     361             : 
     362          60 :         return ff_filter_frame(outlink, dst);
     363             :     }
     364             : }
     365             : 
     366             : #define OFFSET(x) offsetof(FramepackContext, x)
     367             : #define V AV_OPT_FLAG_VIDEO_PARAM
     368             : static const AVOption framepack_options[] = {
     369             :     { "format", "Frame pack output format", OFFSET(format), AV_OPT_TYPE_INT,
     370             :         { .i64 = AV_STEREO3D_SIDEBYSIDE }, 0, INT_MAX, .flags = V, .unit = "format" },
     371             :     { "sbs", "Views are packed next to each other", 0, AV_OPT_TYPE_CONST,
     372             :         { .i64 = AV_STEREO3D_SIDEBYSIDE }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
     373             :     { "tab", "Views are packed on top of each other", 0, AV_OPT_TYPE_CONST,
     374             :         { .i64 = AV_STEREO3D_TOPBOTTOM }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
     375             :     { "frameseq", "Views are one after the other", 0, AV_OPT_TYPE_CONST,
     376             :         { .i64 = AV_STEREO3D_FRAMESEQUENCE }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
     377             :     { "lines", "Views are interleaved by lines", 0, AV_OPT_TYPE_CONST,
     378             :         { .i64 = AV_STEREO3D_LINES }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
     379             :     { "columns", "Views are interleaved by columns", 0, AV_OPT_TYPE_CONST,
     380             :         { .i64 = AV_STEREO3D_COLUMNS }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
     381             :     { NULL },
     382             : };
     383             : 
     384             : AVFILTER_DEFINE_CLASS(framepack);
     385             : 
     386             : static const AVFilterPad framepack_inputs[] = {
     387             :     {
     388             :         .name         = "left",
     389             :         .type         = AVMEDIA_TYPE_VIDEO,
     390             :         .filter_frame = filter_frame_left,
     391             :         .needs_fifo   = 1,
     392             :     },
     393             :     {
     394             :         .name         = "right",
     395             :         .type         = AVMEDIA_TYPE_VIDEO,
     396             :         .filter_frame = filter_frame_right,
     397             :         .needs_fifo   = 1,
     398             :     },
     399             :     { NULL }
     400             : };
     401             : 
     402             : static const AVFilterPad framepack_outputs[] = {
     403             :     {
     404             :         .name          = "packed",
     405             :         .type          = AVMEDIA_TYPE_VIDEO,
     406             :         .config_props  = config_output,
     407             :         .request_frame = request_frame,
     408             :     },
     409             :     { NULL }
     410             : };
     411             : 
     412             : AVFilter ff_vf_framepack = {
     413             :     .name          = "framepack",
     414             :     .description   = NULL_IF_CONFIG_SMALL("Generate a frame packed stereoscopic video."),
     415             :     .priv_size     = sizeof(FramepackContext),
     416             :     .priv_class    = &framepack_class,
     417             :     .query_formats = query_formats,
     418             :     .inputs        = framepack_inputs,
     419             :     .outputs       = framepack_outputs,
     420             :     .uninit        = framepack_uninit,
     421             : };

Generated by: LCOV version 1.13