1/* 2 * Copyright (c) 2013 Paul B Mahol 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#include "libavutil/pixdesc.h" 22#include "avfilter.h" 23#include "internal.h" 24 25typedef struct { 26 int nb_planes; 27 AVFrame *second; 28} SeparateFieldsContext; 29 30static int config_props_output(AVFilterLink *outlink) 31{ 32 AVFilterContext *ctx = outlink->src; 33 SeparateFieldsContext *sf = ctx->priv; 34 AVFilterLink *inlink = ctx->inputs[0]; 35 36 sf->nb_planes = av_pix_fmt_count_planes(inlink->format); 37 38 if (inlink->h & 1) { 39 av_log(ctx, AV_LOG_ERROR, "height must be even\n"); 40 return AVERROR_INVALIDDATA; 41 } 42 43 outlink->time_base.num = inlink->time_base.num; 44 outlink->time_base.den = inlink->time_base.den * 2; 45 outlink->frame_rate.num = inlink->frame_rate.num * 2; 46 outlink->frame_rate.den = inlink->frame_rate.den; 47 outlink->w = inlink->w; 48 outlink->h = inlink->h / 2; 49 50 return 0; 51} 52 53static void extract_field(AVFrame *frame, int nb_planes, int type) 54{ 55 int i; 56 57 for (i = 0; i < nb_planes; i++) { 58 if (type) 59 frame->data[i] = frame->data[i] + frame->linesize[i]; 60 frame->linesize[i] *= 2; 61 } 62} 63 64static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) 65{ 66 AVFilterContext *ctx = inlink->dst; 67 SeparateFieldsContext *sf = ctx->priv; 68 AVFilterLink *outlink = ctx->outputs[0]; 69 int ret; 70 71 inpicref->height = outlink->h; 72 inpicref->interlaced_frame = 0; 73 74 if (!sf->second) { 75 goto clone; 76 } else { 77 AVFrame *second = sf->second; 78 79 extract_field(second, sf->nb_planes, second->top_field_first); 80 81 if (second->pts != AV_NOPTS_VALUE && 82 inpicref->pts != AV_NOPTS_VALUE) 83 second->pts += inpicref->pts; 84 else 85 second->pts = AV_NOPTS_VALUE; 86 87 ret = ff_filter_frame(outlink, second); 88 if (ret < 0) 89 return ret; 90clone: 91 sf->second = av_frame_clone(inpicref); 92 if (!sf->second) 93 return AVERROR(ENOMEM); 94 } 95 96 extract_field(inpicref, sf->nb_planes, !inpicref->top_field_first); 97 98 if (inpicref->pts != AV_NOPTS_VALUE) 99 inpicref->pts *= 2; 100 101 return ff_filter_frame(outlink, inpicref); 102} 103 104static int request_frame(AVFilterLink *outlink) 105{ 106 AVFilterContext *ctx = outlink->src; 107 SeparateFieldsContext *sf = ctx->priv; 108 int ret; 109 110 ret = ff_request_frame(ctx->inputs[0]); 111 if (ret == AVERROR_EOF && sf->second) { 112 sf->second->pts *= 2; 113 extract_field(sf->second, sf->nb_planes, sf->second->top_field_first); 114 ret = ff_filter_frame(outlink, sf->second); 115 sf->second = 0; 116 } 117 118 return ret; 119} 120 121static const AVFilterPad separatefields_inputs[] = { 122 { 123 .name = "default", 124 .type = AVMEDIA_TYPE_VIDEO, 125 .filter_frame = filter_frame, 126 }, 127 { NULL } 128}; 129 130static const AVFilterPad separatefields_outputs[] = { 131 { 132 .name = "default", 133 .type = AVMEDIA_TYPE_VIDEO, 134 .config_props = config_props_output, 135 .request_frame = request_frame, 136 }, 137 { NULL } 138}; 139 140AVFilter ff_vf_separatefields = { 141 .name = "separatefields", 142 .description = NULL_IF_CONFIG_SMALL("Split input video frames into fields."), 143 .priv_size = sizeof(SeparateFieldsContext), 144 .inputs = separatefields_inputs, 145 .outputs = separatefields_outputs, 146}; 147