vf_fieldorder.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2011 Mark Himsley
3  *
4  * This file is part of Libav.
5  *
6  * Libav 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  * Libav 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 Libav; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
26 /* #define DEBUG */
27 
28 #include "libavutil/imgutils.h"
29 #include "libavutil/pixdesc.h"
30 #include "avfilter.h"
31 
32 typedef struct
33 {
34  unsigned int dst_tff;
35  int line_size[4];
37 
38 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
39 {
40  FieldOrderContext *fieldorder = ctx->priv;
41 
42  const char *tff = "tff";
43  const char *bff = "bff";
44 
45  if (!args) {
46  fieldorder->dst_tff = 1;
47  } else if (sscanf(args, "%u", &fieldorder->dst_tff) == 1) {
48  fieldorder->dst_tff = !!fieldorder->dst_tff;
49  } else if (!strcmp(tff, args)) {
50  fieldorder->dst_tff = 1;
51  } else if (!strcmp(bff, args)) {
52  fieldorder->dst_tff = 0;
53  } else {
54  av_log(ctx, AV_LOG_ERROR, "Invalid argument '%s'.\n", args);
55  return AVERROR(EINVAL);
56  }
57 
58  av_log(ctx, AV_LOG_INFO, "output field order: %s\n",
59  fieldorder->dst_tff ? tff : bff);
60 
61  return 0;
62 }
63 
65 {
66  AVFilterFormats *formats;
67  enum PixelFormat pix_fmt;
68  int ret;
69 
72  if (ctx->inputs[0]) {
73  formats = NULL;
74  for (pix_fmt = 0; pix_fmt < PIX_FMT_NB; pix_fmt++)
77  && av_pix_fmt_descriptors[pix_fmt].nb_components
78  && !av_pix_fmt_descriptors[pix_fmt].log2_chroma_h
79  && (ret = avfilter_add_format(&formats, pix_fmt)) < 0) {
80  avfilter_formats_unref(&formats);
81  return ret;
82  }
83  avfilter_formats_ref(formats, &ctx->inputs[0]->out_formats);
84  avfilter_formats_ref(formats, &ctx->outputs[0]->in_formats);
85  }
86 
87  return 0;
88 }
89 
90 static int config_input(AVFilterLink *inlink)
91 {
92  AVFilterContext *ctx = inlink->dst;
93  FieldOrderContext *fieldorder = ctx->priv;
94  int plane;
95 
98  for (plane = 0; plane < 4; plane++) {
99  fieldorder->line_size[plane] = av_image_get_linesize(
100  inlink->format,
101  inlink->w,
102  plane);
103  }
104 
105  return 0;
106 }
107 
108 static AVFilterBufferRef *get_video_buffer(AVFilterLink *inlink, int perms, int w, int h)
109 {
110  AVFilterContext *ctx = inlink->dst;
111  AVFilterLink *outlink = ctx->outputs[0];
112 
113  return avfilter_get_video_buffer(outlink, perms, w, h);
114 }
115 
116 static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *inpicref)
117 {
118  AVFilterContext *ctx = inlink->dst;
119  AVFilterLink *outlink = ctx->outputs[0];
120 
121  AVFilterBufferRef *outpicref;
122 
123  outpicref = avfilter_ref_buffer(inpicref, ~0);
124  outlink->out_buf = outpicref;
125 
126  avfilter_start_frame(outlink, outpicref);
127 }
128 
129 static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
130 {
131  AVFilterContext *ctx = inlink->dst;
132  FieldOrderContext *fieldorder = ctx->priv;
133  AVFilterLink *outlink = ctx->outputs[0];
134 
135  AVFilterBufferRef *inpicref = inlink->cur_buf;
136 
141  if ( !inpicref->video->interlaced
142  || inpicref->video->top_field_first == fieldorder->dst_tff) {
143  avfilter_draw_slice(outlink, y, h, slice_dir);
144  }
145 }
146 
147 static void end_frame(AVFilterLink *inlink)
148 {
149  AVFilterContext *ctx = inlink->dst;
150  FieldOrderContext *fieldorder = ctx->priv;
151  AVFilterLink *outlink = ctx->outputs[0];
152 
153  AVFilterBufferRef *inpicref = inlink->cur_buf;
154  AVFilterBufferRef *outpicref = outlink->out_buf;
155 
156  int h, plane, line_step, line_size, line;
157  uint8_t *cpy_src, *cpy_dst;
158 
159  if ( inpicref->video->interlaced
160  && inpicref->video->top_field_first != fieldorder->dst_tff) {
161  av_dlog(ctx,
162  "picture will move %s one line\n",
163  fieldorder->dst_tff ? "up" : "down");
164  h = inpicref->video->h;
165  for (plane = 0; plane < 4 && inpicref->data[plane]; plane++) {
166  line_step = inpicref->linesize[plane];
167  line_size = fieldorder->line_size[plane];
168  cpy_src = inpicref->data[plane];
169  cpy_dst = outpicref->data[plane];
170  if (fieldorder->dst_tff) {
176  for (line = 0; line < h; line++) {
177  if (1 + line < outpicref->video->h) {
178  memcpy(cpy_dst, cpy_src + line_step, line_size);
179  } else {
180  memcpy(cpy_dst, cpy_src - line_step - line_step, line_size);
181  }
182  cpy_src += line_step;
183  cpy_dst += line_step;
184  }
185  } else {
191  cpy_src += (h - 1) * line_step;
192  cpy_dst += (h - 1) * line_step;
193  for (line = h - 1; line >= 0 ; line--) {
194  if (line > 0) {
195  memcpy(cpy_dst, cpy_src - line_step, line_size);
196  } else {
197  memcpy(cpy_dst, cpy_src + line_step + line_step, line_size);
198  }
199  cpy_src -= line_step;
200  cpy_dst -= line_step;
201  }
202  }
203  }
204  outpicref->video->top_field_first = fieldorder->dst_tff;
205  avfilter_draw_slice(outlink, 0, h, 1);
206  } else {
207  av_dlog(ctx,
208  "not interlaced or field order already correct\n");
209  }
210 
211  avfilter_end_frame(outlink);
212  avfilter_unref_buffer(inpicref);
213 }
214 
216  .name = "fieldorder",
217  .description = NULL_IF_CONFIG_SMALL("Set the field order."),
218  .init = init,
219  .priv_size = sizeof(FieldOrderContext),
221  .inputs = (AVFilterPad[]) {{ .name = "default",
222  .type = AVMEDIA_TYPE_VIDEO,
223  .config_props = config_input,
224  .start_frame = start_frame,
225  .get_video_buffer = get_video_buffer,
226  .draw_slice = draw_slice,
227  .end_frame = end_frame,
228  .min_perms = AV_PERM_READ,
229  .rej_perms = AV_PERM_REUSE2|AV_PERM_PRESERVE,},
230  { .name = NULL}},
231  .outputs = (AVFilterPad[]) {{ .name = "default",
232  .type = AVMEDIA_TYPE_VIDEO, },
233  { .name = NULL}},
234 };