[FFmpeg-devel] [PATCH] add top video filter
Stefano Sabatini
stefano.sabatini-lala at poste.it
Tue Mar 29 18:50:00 CEST 2011
On date Monday 2011-03-28 14:47:20 +0100, Mark Himsley encoded:
> Dear developers,
>
> Converting to and from interlaced PAL DV files, with their
> bottom-field-first interlace field order, can be a pain. Converting
> tff files to DV results in tff DV files, which are hard to work with
> in editing software.
>
> The attached filter can:
>
> Convert field order by either moving all of the lines in the picture
> up by 1 line (bff to tff conversion) or down by 1 line (tff to bff
> conversion). The remaining line, the bottom line in bff to tff
> transforms or the top line in tff to bff transforms, is filled by
> copying the closest line in that field.
>
> Previous to this filter I have used a filter chain like this to do
> bff to tff conversion.
>
> format=yuv422p,crop=720:575:0:1,pad=720:576:0:0:black
>
> but that chain does not fill the remaining line.
swapfields looks a good name to me, more comments follow.
[...]
> +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
> +{
> + TopContext *top = ctx->priv;
> +
> + if(args && sscanf(args, "%u", &top->dst_tff) == 1) {
Nit+++: if_(
(I'll fix it when committing if you don't care, so feel free to skip
all the nits comments).
I prefer to adopt positive logic, that is
if (!args || !sscanf(args, "%u", &top->dst_tff) != 1) {
av_log(ctx, AV_LOG_ERROR,
"Expected 1 argument '#':'%s'\n", args);
return AVERROR(EINVAL);
}
top->dst_tff = !!top->dst_tff;
av_log(ctx, AV_LOG_INFO, "ttf:%d\n", top->dst_tff);
return 0;
it should also be slightly more readable.
> +static int query_formats(AVFilterContext *ctx)
> +{
> + enum PixelFormat pix_fmts[] = {
> + PIX_FMT_YUV444P, PIX_FMT_YUV422P,
> + PIX_FMT_YUVJ444P, PIX_FMT_YUVJ422P,
> + PIX_FMT_RGB24, PIX_FMT_BGR24,
> + PIX_FMT_ARGB, PIX_FMT_ABGR,
> + PIX_FMT_RGBA, PIX_FMT_BGRA,
> + PIX_FMT_NONE
> + };
Same consideration by Michael, RGB48 (recently added) should be
already supported by this logic. Another cool idea would be to select
formats based on chroma shift.
> +static int config_input(AVFilterLink *link)
Nit: link -> inlink
here and below.
> +{
> + TopContext *top = link->dst->priv;
> + int is_packed_rgba;
> + int plane;
> +
> + is_packed_rgba = 1;
> + switch (link->format) {
> + case PIX_FMT_ARGB:
> + case PIX_FMT_ABGR:
> + case PIX_FMT_RGBA:
> + case PIX_FMT_BGRA:
> + case PIX_FMT_RGB24:
> + case PIX_FMT_BGR24: break;
> + default:
> + is_packed_rgba = 0;
> + }
> +
> + if (is_packed_rgba) {
> + top->line_step[0] = (av_get_bits_per_pixel(&av_pix_fmt_descriptors[link->format]))>>3;
Nit: unnecessary parentheses around (av_get_bits_per_pixel...)
> + } else {
> + for (plane = 0; plane < 4; plane++) {
> + top->line_step[plane] = 1;
> + }
> + }
Maybe you can use libavutil/imgutils.h:av_image_fill_max_pixsteps().
> + return 0;
> +}
> +
> +static AVFilterBufferRef *get_video_buffer(AVFilterLink *link, int perms, int w, int h)
> +{
> + return avfilter_get_video_buffer(link->dst->outputs[0], perms, w, h);
> +}
> +
> +static void start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
> +{
> + AVFilterBufferRef *outpicref = avfilter_ref_buffer(picref, ~0);
> +
> + link->dst->outputs[0]->out_buf = outpicref;
> +
> + avfilter_start_frame(link->dst->outputs[0], outpicref);
> +}
> +
> +static void end_frame(AVFilterLink *link)
> +{
> + TopContext *top = link->dst->priv;
AVFilterLink *outlink = link->dst->outputs[0];
may improve readability.
> +
> + if (link->cur_buf->video->interlaced) {
> + link->dst->outputs[0]->out_buf->video->top_field_first = top->dst_tff;
> + }
> +
> + avfilter_end_frame(link->dst->outputs[0]);
> + avfilter_unref_buffer(link->cur_buf);
> +}
> +
> +static void draw_slice(AVFilterLink *link, int y, int h, int slice_dir)
> +{
> + AVFilterContext *ctx = link->dst;
> + TopContext *top = ctx->priv;
> +
> + AVFilterBufferRef *inpic = link->cur_buf;
> + AVFilterBufferRef *outpic = link->dst->outputs[0]->out_buf;
> + int i, plane;
> + uint8_t *cpy_dst;
> +
> + if (inpic->video->interlaced) {
> + if (inpic->video->top_field_first != top->dst_tff) {
> + av_log(ctx, AV_LOG_DEBUG,
> + "picture will move %s one line\n",
> + top->dst_tff ? "up" : "down");
av_dlog(ctx, "...", ) or this will spam the log with -loglevel debug
(and put a /* #define DEBUG */ at the top of the file)
> + for (plane = 0; plane < 4 && outpic->data[plane]; plane++) {
> + cpy_dst = outpic->data[plane] + y * outpic->linesize[plane];
> + if (top->dst_tff) {
> + for (i = 0; i < h; i++) {
> + if (1 + y + i < outpic->video->h) {
> + memcpy(cpy_dst, cpy_dst + outpic->linesize[plane], outpic->linesize[plane]);
You're copying more than it is required, outpic->linesize[plane] -> line_step[plane] * w;
> + } else {
> + memcpy(cpy_dst, cpy_dst - 2 * outpic->linesize[plane], outpic->linesize[plane]);
> + }
If I'm correct this is copy the line which is tow lines up the current
one, that is:
...
line N-2
line N-1
line N-2
Can you confirm this is the standard behavior? (also it should be
possibly documented).
> + cpy_dst += outpic->linesize[plane];
> + }
> + } else {
> + cpy_dst += (h -1) * outpic->linesize[plane];
> + for (i = h -1; i >= 0 ; i--) {
> + if ( y + i > 0) {
> + memcpy( cpy_dst, cpy_dst - outpic->linesize[plane], outpic->linesize[plane] );
> + } else {
> + memcpy( cpy_dst, cpy_dst + 2 * outpic->linesize[plane], outpic->linesize[plane] );
> + }
> + cpy_dst -= outpic->linesize[plane];
> + }
> + }
> + }
> + } else {
> + av_log(ctx, AV_LOG_DEBUG,
> + "field order already correct\n");
av_dlog()
> + }
> + }
> +
> + avfilter_draw_slice(link->dst->outputs[0], y, h, slice_dir);
> +}
Is top->line_step() used?
--
FFmpeg = Faithful and Foolish Minimal Peaceful Eretic Gem
More information about the ffmpeg-devel
mailing list