yading@10: /* yading@10: * Copyright (c) 2012 Steven Robertson yading@10: * yading@10: * This file is part of FFmpeg. yading@10: * yading@10: * FFmpeg is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU Lesser General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2.1 of the License, or (at your option) any later version. yading@10: * yading@10: * FFmpeg is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@10: * Lesser General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU Lesser General Public yading@10: * License along with FFmpeg; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: /** yading@10: * @file yading@10: * copy an alpha component from another video's luma yading@10: */ yading@10: yading@10: #include yading@10: yading@10: #include "libavutil/pixfmt.h" yading@10: #include "avfilter.h" yading@10: #include "bufferqueue.h" yading@10: #include "drawutils.h" yading@10: #include "formats.h" yading@10: #include "internal.h" yading@10: #include "video.h" yading@10: yading@10: enum { Y, U, V, A }; yading@10: yading@10: typedef struct { yading@10: int frame_requested; yading@10: int is_packed_rgb; yading@10: uint8_t rgba_map[4]; yading@10: struct FFBufQueue queue_main; yading@10: struct FFBufQueue queue_alpha; yading@10: } AlphaMergeContext; yading@10: yading@10: static av_cold void uninit(AVFilterContext *ctx) yading@10: { yading@10: AlphaMergeContext *merge = ctx->priv; yading@10: ff_bufqueue_discard_all(&merge->queue_main); yading@10: ff_bufqueue_discard_all(&merge->queue_alpha); yading@10: } yading@10: yading@10: static int query_formats(AVFilterContext *ctx) yading@10: { yading@10: static const enum AVPixelFormat main_fmts[] = { yading@10: AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA420P, yading@10: AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA, AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, yading@10: AV_PIX_FMT_NONE yading@10: }; yading@10: static const enum AVPixelFormat alpha_fmts[] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE }; yading@10: AVFilterFormats *main_formats = ff_make_format_list(main_fmts); yading@10: AVFilterFormats *alpha_formats = ff_make_format_list(alpha_fmts); yading@10: ff_formats_ref(main_formats, &ctx->inputs[0]->out_formats); yading@10: ff_formats_ref(alpha_formats, &ctx->inputs[1]->out_formats); yading@10: ff_formats_ref(main_formats, &ctx->outputs[0]->in_formats); yading@10: return 0; yading@10: } yading@10: yading@10: static int config_input_main(AVFilterLink *inlink) yading@10: { yading@10: AlphaMergeContext *merge = inlink->dst->priv; yading@10: merge->is_packed_rgb = yading@10: ff_fill_rgba_map(merge->rgba_map, inlink->format) >= 0; yading@10: return 0; yading@10: } yading@10: yading@10: static int config_output(AVFilterLink *outlink) yading@10: { yading@10: AVFilterContext *ctx = outlink->src; yading@10: AVFilterLink *mainlink = ctx->inputs[0]; yading@10: AVFilterLink *alphalink = ctx->inputs[1]; yading@10: if (mainlink->w != alphalink->w || mainlink->h != alphalink->h) { yading@10: av_log(ctx, AV_LOG_ERROR, yading@10: "Input frame sizes do not match (%dx%d vs %dx%d).\n", yading@10: mainlink->w, mainlink->h, yading@10: alphalink->w, alphalink->h); yading@10: return AVERROR(EINVAL); yading@10: } yading@10: yading@10: outlink->w = mainlink->w; yading@10: outlink->h = mainlink->h; yading@10: outlink->time_base = mainlink->time_base; yading@10: outlink->sample_aspect_ratio = mainlink->sample_aspect_ratio; yading@10: outlink->frame_rate = mainlink->frame_rate; yading@10: return 0; yading@10: } yading@10: yading@10: static void draw_frame(AVFilterContext *ctx, yading@10: AVFrame *main_buf, yading@10: AVFrame *alpha_buf) yading@10: { yading@10: AlphaMergeContext *merge = ctx->priv; yading@10: int h = main_buf->height; yading@10: yading@10: if (merge->is_packed_rgb) { yading@10: int x, y; yading@10: uint8_t *pin, *pout; yading@10: for (y = 0; y < h; y++) { yading@10: pin = alpha_buf->data[0] + y * alpha_buf->linesize[0]; yading@10: pout = main_buf->data[0] + y * main_buf->linesize[0] + merge->rgba_map[A]; yading@10: for (x = 0; x < main_buf->width; x++) { yading@10: *pout = *pin; yading@10: pin += 1; yading@10: pout += 4; yading@10: } yading@10: } yading@10: } else { yading@10: int y; yading@10: const int main_linesize = main_buf->linesize[A]; yading@10: const int alpha_linesize = alpha_buf->linesize[Y]; yading@10: for (y = 0; y < h && y < alpha_buf->height; y++) { yading@10: memcpy(main_buf->data[A] + y * main_linesize, yading@10: alpha_buf->data[Y] + y * alpha_linesize, yading@10: FFMIN(main_linesize, alpha_linesize)); yading@10: } yading@10: } yading@10: } yading@10: yading@10: static int filter_frame(AVFilterLink *inlink, AVFrame *buf) yading@10: { yading@10: AVFilterContext *ctx = inlink->dst; yading@10: AlphaMergeContext *merge = ctx->priv; yading@10: yading@10: int ret = 0; yading@10: int is_alpha = (inlink == ctx->inputs[1]); yading@10: struct FFBufQueue *queue = yading@10: (is_alpha ? &merge->queue_alpha : &merge->queue_main); yading@10: ff_bufqueue_add(ctx, queue, buf); yading@10: yading@10: do { yading@10: AVFrame *main_buf, *alpha_buf; yading@10: yading@10: if (!ff_bufqueue_peek(&merge->queue_main, 0) || yading@10: !ff_bufqueue_peek(&merge->queue_alpha, 0)) break; yading@10: yading@10: main_buf = ff_bufqueue_get(&merge->queue_main); yading@10: alpha_buf = ff_bufqueue_get(&merge->queue_alpha); yading@10: yading@10: merge->frame_requested = 0; yading@10: draw_frame(ctx, main_buf, alpha_buf); yading@10: ret = ff_filter_frame(ctx->outputs[0], main_buf); yading@10: av_frame_free(&alpha_buf); yading@10: } while (ret >= 0); yading@10: return ret; yading@10: } yading@10: yading@10: static int request_frame(AVFilterLink *outlink) yading@10: { yading@10: AVFilterContext *ctx = outlink->src; yading@10: AlphaMergeContext *merge = ctx->priv; yading@10: int in, ret; yading@10: yading@10: merge->frame_requested = 1; yading@10: while (merge->frame_requested) { yading@10: in = ff_bufqueue_peek(&merge->queue_main, 0) ? 1 : 0; yading@10: ret = ff_request_frame(ctx->inputs[in]); yading@10: if (ret < 0) yading@10: return ret; yading@10: } yading@10: return 0; yading@10: } yading@10: yading@10: static const AVFilterPad alphamerge_inputs[] = { yading@10: { yading@10: .name = "main", yading@10: .type = AVMEDIA_TYPE_VIDEO, yading@10: .config_props = config_input_main, yading@10: .get_video_buffer = ff_null_get_video_buffer, yading@10: .filter_frame = filter_frame, yading@10: .needs_writable = 1, yading@10: },{ yading@10: .name = "alpha", yading@10: .type = AVMEDIA_TYPE_VIDEO, yading@10: .filter_frame = filter_frame, yading@10: }, yading@10: { NULL } yading@10: }; yading@10: yading@10: static const AVFilterPad alphamerge_outputs[] = { yading@10: { yading@10: .name = "default", yading@10: .type = AVMEDIA_TYPE_VIDEO, yading@10: .config_props = config_output, yading@10: .request_frame = request_frame, yading@10: }, yading@10: { NULL } yading@10: }; yading@10: yading@10: AVFilter avfilter_vf_alphamerge = { yading@10: .name = "alphamerge", yading@10: .description = NULL_IF_CONFIG_SMALL("Copy the luma value of the second " yading@10: "input into the alpha channel of the first input."), yading@10: .uninit = uninit, yading@10: .priv_size = sizeof(AlphaMergeContext), yading@10: .query_formats = query_formats, yading@10: .inputs = alphamerge_inputs, yading@10: .outputs = alphamerge_outputs, yading@10: };