[FFmpeg-soc] [PATCH]new avfilter vf_logo.c overlaying logo onto video
Stefano Sabatini
stefano.sabatini-lala at poste.it
Fri Oct 2 20:17:33 CEST 2009
On date Friday 2009-10-02 13:03:09 +0200, Juergen_Meissner at spiegel.de encoded:
> This is a patch which can easyly be applied to checkout svn://svn.mplayerhq.hu/soc/libavfilter.
>
> Hopefully it will find a way to the /soc/libavfilter store this time.
Hi, I'll be glad to include it in the soc, but the last time you
didn't replied to our review:
http://thread.gmane.org/gmane.comp.video.ffmpeg.soc/5897
> Best regards
>
> Jürgen Meissner
> stellv. Leiter EDV
>
> SPIEGEL-Verlag Rudolf Augstein GmbH & Co. KG
> Brandstwiete 19
> 20457 Hamburg
> Tel: +49 (40) 30 07 - 2137
> Fax: +49 (40) 30 07 - 85 - 2137
> E-Mail: juergen_meissner at spiegel.de
> http://www.spiegel.de
> Index: allfilters.c
> ===================================================================
> --- allfilters.c (Revision 5405)
> +++ allfilters.c (Arbeitskopie)
> @@ -40,6 +40,7 @@
> REGISTER_FILTER(FORMAT,format,vf);
> REGISTER_FILTER(FPS,fps,vf);
> REGISTER_FILTER(HFLIP,hflip,vf);
> + REGISTER_FILTER(LOGO,logo,vf);
> REGISTER_FILTER(NEGATE,negate,vf);
> REGISTER_FILTER(NOFORMAT,noformat,vf);
> REGISTER_FILTER(NULL,null,vf);
> Index: diffs/03_libavcodec_filters.diff
> ===================================================================
> --- diffs/03_libavcodec_filters.diff (Revision 0)
> +++ diffs/03_libavcodec_filters.diff (Revision 0)
> @@ -0,0 +1,12 @@
> +Index: libavcodec/Makefile
> +===================================================================
> +--- libavcodec/Makefile (Revision 20088)
> ++++ libavcodec/Makefile (Arbeitskopie)
> +@@ -18,6 +18,7 @@
> + opt.o \
> + options.o \
> + parser.o \
> ++ pixdesc.o \
> + raw.o \
> + resample.o \
> + resample2.o \
> Index: doc/vfilters.texi
> ===================================================================
> --- doc/vfilters.texi (Revision 5405)
> +++ doc/vfilters.texi (Arbeitskopie)
> @@ -117,6 +117,47 @@
>
> Flip the video horizontally.
>
> + at section logo
> +
> + at example
> +./ffmpeg -i inputvideofile -vfilters logo=10:20:logofile.png -y outputvideofile
> + at end example
I'm definitively not fond of positional arguments, I'd prefer rather
something like this:
logo = x= (in_w - logo_w) / 2 : y = (in_h - logo_h) / 2 : filename=logofile.png
Making x and y function of the in_w, in_h, logo_h and logo_w
expression would greatly add flexibility to the filter.
> + image of logofile.png is overlayed onto every frame of inputvideofile
> + at offset x=10 y=20 giving outputvideofile
> +
> + x <INT>
> +
> + Defines a logo (left border) offset from the left side of the video.
> + A negative value offsets (logo right border) from
> + the right side of the video.
> +
> + y <INT>
> +
> + Defines a logo (top border) offset from the top of the video.
> + A negative value offsets (logo bottom border) from
> + the bottom of the video.
> +
> + if logofile has no alpha-path You can prefix another 3 fields R,G,B
> + to select a RGB-color to be the transparent one
> +
> +
> + REMARKS: there is a lot of gymnastics with the single logo frame, we do
> + this to avoid any transformation for ALL the many
> + video frames
> +
> + at example
> +./ffmpeg -i inputvideofile -vfilters logo=0:0:0:10:20:logofile.png -y outputvideofile
> + at end example
> +
> + black is the color to be understood as transparent
> +
> + at example
> +./ffmpeg -i inputvideofile -vfilters logo_without_alpha=10:20:logofile.png -y outputvideofile
> + at end example
> +
> + force overlaying all pixels (even if no alpha-path)
> +
> @section negate
>
> @example
> Index: vf_logo.c
> ===================================================================
> --- vf_logo.c (Revision 0)
> +++ vf_logo.c (Revision 0)
> @@ -0,0 +1,852 @@
> +#define VF_LOGO_VERSION "0.9.9 2.10.2009"
No need for version.
> +/**
> + * libavfilter/vf_logo.c
> + * filter to overlay (with or without alpha) logo on top of video
> + * Copyright (c) 2009 Juergen Meissner (using parts of previous code)
> + * Copyright (c) 2008 Victor Paesa (libavfilter/vsrc_movie.c)
> + * Copyright (c) 2007 Bobby Bingham (libavfilter/vf_overlay.c)
> + * Copyright (c) 2007 Juergen Meissner (vhook/overlay.c)
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +
> +/**
> + *
> + * example of using libavfilter/vf_logo:
> + *
> + * ffmpeg -i inputvideofile -vfilters logo=10:20:logofile.png -y outputvideofile
[...]
Duplicate of the texinfo file, please remove this.
> +
> +#include <stdio.h>
> +#include "avfilter.h"
> +#include "libavcodec/pixdesc.h"
> +#include "libavformat/avformat.h"
> +#include "libswscale/swscale.h"
> +
> +
> +typedef struct {
> +
> + // pointer back to AVFilterContext
> + AVFilterContext *filter_ctx;
> +
> + // video parameters
> + int hsub, vsub; //< chroma subsampling
> + int bpp[4]; //< bytes per pixel within plane
> + int video_w, video_h, video_format;
> +
> + // logo parameters
> + int x, y; //< requested offsets of logo on video
> + int w, h, format; //< width, height, pix-format
> + int app_x, app_y, app_w, app_h; // corrected to be applied
> + char file_name[512];
> + int alpha_R, alpha_G, alpha_B;
> +
> + // Needed to load logo
> + AVFormatContext *pFormatCtx;
pFormatCtx -> fmt_ctx, please be consistent with the rest of libav*,
don't use camelCasing here and below.
> + int videoStream;
> + AVCodecContext *pCodecCtx;
> + AVCodec *pCodec;
> + AVFrame *plogo_frame;
I'd prefer:
plogo_frame -> logo_frame
again for consistency reasons.
> + AVFrame *plogo_frame_rgba32;
> + AVFrame *plogo_frame_video_format;
I don't like this name (is it a frame or a video format?).
> + uint8_t *buffer_logo_frame;
> + uint8_t *buffer_logo_frame_rgba32;
> + uint8_t *buffer_logo_frame_video_format;
> + AVPacket packet;
> + struct SwsContext *sws;
> +
> + // Needed to overlay logo onto video frame
> + uint8_t *pRuler_0;
> + uint8_t *pRuler_1_2;
> +
> +} LogoContext;
> +
> +/**
> + * RGBA pixel.
> + */
> +typedef struct {
> + uint8_t R; ///< Red.
> + uint8_t G; ///< Green.
> + uint8_t B; ///< Blue.
> + uint8_t A; ///< Alpha.
> +} RGBA;
>
> +static const char *pixdesc_name(int pix_fmt)
> +{
> + return av_pix_fmt_descriptors[pix_fmt].name;
> +}
Useless wrapper, directly use av_pix_fmt_descriptors[pix_fmt].name.
> +
> +static int load_logo_create_frames(AVFilterContext * ctx)
AVFilterContext *ctx;
> +{
> + LogoContext *logo;
> + int i, j, err, numBytes, frameFinished;
> + uint8_t *pLOADfrom_sol;
> + RGBA *pLOADfrom;
LOADfrom_sol, and LOADFrom don't look like good names for RGBA pixel.
> +
> + logo = ctx->priv;
> +
> + /*
> + * The last three parameters specify the file format, buffer size and format
> + * parameters; by simply specifying NULL or 0 we ask libavformat to
> + * auto-detect the format and use a default buffer size.
> + */
> + if ((err =
> + av_open_input_file(&logo->pFormatCtx, logo->file_name, NULL, 0,
> + NULL)) != 0) {
> + av_log(ctx, AV_LOG_ERROR,
> + " cannot open logo file %s error is %d\n", logo->file_name,
> + err);
> + return -1;
> + }
> +
> + /*
> + * This fills the streams field of the AVFormatContext with valid information.
> + */
> + if (av_find_stream_info(logo->pFormatCtx) < 0) {
> + av_log(ctx, AV_LOG_ERROR,
> + " failed to find stream info in logo file\n");
> + return -1;
> + }
> +
> + /*
> + * we simply use the first video stream
> + */
> + logo->videoStream = -1;
> + for (i = 0; i < logo->pFormatCtx->nb_streams; i++)
> + if (logo->pFormatCtx->streams[i]->codec->codec_type ==
> + CODEC_TYPE_VIDEO) {
> + logo->videoStream = i;
> + break;
> + }
> + if (logo->videoStream == -1) {
> + av_log(ctx, AV_LOG_ERROR,
> + " failed to find any video stream in logo file\n");
> + return -1;
> + }
> + // Get a pointer to the codec context for the video stream
> + logo->pCodecCtx = logo->pFormatCtx->streams[logo->videoStream]->codec;
> +
> + /*
> + * OK, so now we've got a pointer to the so-called codec context for our video
> + * stream, but we still have to find the actual codec and open it.
> + */
> + // find the decoder for the video stream
> + logo->pCodec = avcodec_find_decoder(logo->pCodecCtx->codec_id);
> + if (logo->pCodec == NULL) {
> + av_log(ctx, AV_LOG_ERROR,
> + " failed to find any codec for decoding logo frame\n");
> + return -1;
> + }
> + // Inform the codec that we can handle truncated bitstreams -- i.e.,
> + // bitstreams where frame boundaries can fall in the middle of packets
> + if (logo->pCodec->capabilities & CODEC_CAP_TRUNCATED)
> + logo->pCodecCtx->flags |= CODEC_FLAG_TRUNCATED;
> +
> + // Open codec
> + av_log(ctx, AV_LOG_DEBUG, " avcodec_open\n");
> + if (avcodec_open(logo->pCodecCtx, logo->pCodec) < 0) {
> + av_log(ctx, AV_LOG_ERROR, " failed to open codec\n");
> + return -1;
> + }
> +
> + /*
> + * the plogo_frame (of type AVFrame *) is allocated like this:
> + */
> + // Allocate an AVFrame structure
> + logo->plogo_frame = avcodec_alloc_frame();
> + if (logo->plogo_frame == NULL) {
> + av_log(ctx, AV_LOG_ERROR, " failed to alloc plogo_frame\n");
> + return -1;
> + }
> + // Determine required buffer size and allocate buffer
> + numBytes =
> + avpicture_get_size(logo->pCodecCtx->pix_fmt,
> + logo->pCodecCtx->width,
> + logo->pCodecCtx->height);
> + logo->buffer_logo_frame = av_malloc(numBytes);
> +
> + // Assign appropriate parts of buffer to image planes in plogo_frame
> + avpicture_fill((AVPicture *) logo->plogo_frame,
> + logo->buffer_logo_frame, logo->pCodecCtx->pix_fmt,
> + logo->pCodecCtx->width, logo->pCodecCtx->height);
> +
> + logo->w = logo->pCodecCtx->width;
> + logo->h = logo->pCodecCtx->height;
> +
> + av_log(ctx, AV_LOG_DEBUG, " linesizes [0]=%d [1]=%d [2]=%d [3]=%d\n",
> + ((AVPicture *) logo->plogo_frame)->linesize[0],
> + ((AVPicture *) logo->plogo_frame)->linesize[1],
> + ((AVPicture *) logo->plogo_frame)->linesize[2],
> + ((AVPicture *) logo->plogo_frame)->linesize[3]);
> +
> + av_log(ctx, AV_LOG_DEBUG, " av_read_frame\n");
> + while (av_read_frame(logo->pFormatCtx, &logo->packet) >= 0) {
> + av_log(ctx, AV_LOG_DEBUG, " got a frame\n");
> + // Is this a packet from the video stream?
> + if (logo->packet.stream_index == logo->videoStream) {
> + // Decode video frame
> + av_log(ctx, AV_LOG_DEBUG, " avcodec_decode_video\n");
> + avcodec_decode_video2(logo->pCodecCtx, logo->plogo_frame,
> + &frameFinished, &logo->packet);
> +
> + // Did we get a video frame?
> + if (frameFinished) {
> + av_log(ctx, AV_LOG_DEBUG,
> + " got a finished frame, fine!!\n");
> + }
> + }
> + av_log(ctx, AV_LOG_DEBUG,
> + " avcodec decoded logo image to pix_fmt=%s (%d)\n",
> + pixdesc_name(logo->pCodecCtx->pix_fmt),
> + logo->pCodecCtx->pix_fmt);
> + logo->format = logo->pCodecCtx->pix_fmt;
> + av_log(ctx, AV_LOG_DEBUG, " logo size is %dx%d\tpixfmt:%s\n",
> + logo->w, logo->h, pixdesc_name(logo->format));
> +
> + // Free the packet that was allocated by av_read_frame
> + av_free_packet(&logo->packet);
> + }
> +
> + if (logo->pCodecCtx->pix_fmt != PIX_FMT_RGBA) {
> + // transform it with swscaler to PIX_FMT_RGBA
> + av_log(ctx, AV_LOG_DEBUG,
> + " transform logo image from pix_fmt=%s to RGBA\n",
> + pixdesc_name(logo->pCodecCtx->pix_fmt));
> + if (logo->pCodecCtx->pix_fmt == PIX_FMT_RGB24) {
> + av_log(ctx, AV_LOG_DEBUG,
> + " image of pix_fmt=%s has no alpha path!\n",
> + pixdesc_name(logo->pCodecCtx->pix_fmt));
> + }
> + // Allocate an AVFrame structure
> + logo->plogo_frame_rgba32 = avcodec_alloc_frame();
> + if (logo->plogo_frame_rgba32 == NULL) {
> + av_log(ctx, AV_LOG_ERROR,
> + " failed to alloc plogo_frame_rgba32\n");
> + return -1;
> + }
> + logo->sws =
> + sws_getCachedContext(logo->sws, logo->w, logo->h,
> + logo->pCodecCtx->pix_fmt, logo->w,
> + logo->h, PIX_FMT_RGBA, SWS_BICUBIC, NULL,
> + NULL, NULL);
> + if (logo->sws == NULL) {
> + av_log(ctx, AV_LOG_ERROR,
> + " cannot initialize the to-RGBA conversion context\n");
> + return -1;
> + }
> + // Determine required buffer size and allocate buffer
> + numBytes = avpicture_get_size(PIX_FMT_RGBA, logo->w, logo->h);
> + logo->buffer_logo_frame_rgba32 = av_malloc(numBytes);
> + // Assign appropriate parts of buffer to image planes in plogo_frame
> + avpicture_fill((AVPicture *) logo->plogo_frame_rgba32,
> + logo->buffer_logo_frame_rgba32, PIX_FMT_RGBA,
> + logo->w, logo->h);
> +
> + // transform to RGBA pixel format
> + sws_scale(logo->sws, logo->plogo_frame->data,
> + logo->plogo_frame->linesize, 0, logo->h,
> + logo->plogo_frame_rgba32->data,
> + logo->plogo_frame_rgba32->linesize);
Random idea: as sws_scale is used here maybe we could even scale the
logo image like. We could take some params in input, for example:
logo_w = in_logo_w / 2
or
logo_w = in_w / 5
logo_h = (in_w / 5 ) * in_logo_w * in_logo_h
(scale the logo width, a scale the height preserving the original
aspect ratio).
But that may be left for a further patch.
> + av_log(ctx, AV_LOG_DEBUG,
> + " RGBA linesizes [0]=%d [1]=%d [2]=%d [3]=%d\n",
> + ((AVPicture *) logo->plogo_frame_rgba32)->linesize[0],
> + ((AVPicture *) logo->plogo_frame_rgba32)->linesize[1],
> + ((AVPicture *) logo->plogo_frame_rgba32)->linesize[2],
> + ((AVPicture *) logo->plogo_frame_rgba32)->linesize[3]);
> +
> + } else {
> + logo->plogo_frame_rgba32 = logo->plogo_frame;
> + logo->buffer_logo_frame_rgba32 = logo->buffer_logo_frame;
> + }
> +
> +
> + if (av_log_get_level() >= AV_LOG_DEBUG) {
> + pLOADfrom_sol = logo->plogo_frame_rgba32->data[0];
> + pLOADfrom = (RGBA *) pLOADfrom_sol;
> + for (i = 0; i < logo->h; i++) {
> + for (j = 0; j < logo->w; j++) {
> + av_log(ctx, AV_LOG_DEBUG,
> + " image (%3d,%3d) R=%3d G=%3d B=%3d A=%3d\n", i, j,
> + pLOADfrom->R, pLOADfrom->G, pLOADfrom->B,
> + pLOADfrom->A);
> + pLOADfrom++;
> + }
> + pLOADfrom_sol += logo->plogo_frame_rgba32->linesize[0];
> + pLOADfrom = (RGBA *) pLOADfrom_sol;
> + };
> + };
> +
> + /* eventually touch alpha if requested by name logo_without_alpha */
> + if (logo->alpha_R > -1) {
> + pLOADfrom_sol = logo->plogo_frame_rgba32->data[0];
> + pLOADfrom = (RGBA *) pLOADfrom_sol;
> + for (i = 0; i < logo->h; i++) {
> + for (j = 0; j < logo->w; j++) {
> + pLOADfrom->A = 255; // non transparent
> + if (logo->alpha_R == pLOADfrom->R &&
> + logo->alpha_G == pLOADfrom->G &&
> + logo->alpha_B == pLOADfrom->B)
> + pLOADfrom->A = 0; // transparent
> + av_log(ctx, AV_LOG_DEBUG,
> + " touched image (%3d,%3d) R=%3d G=%3d B=%3d A=%3d\n",
> + i, j, pLOADfrom->R, pLOADfrom->G, pLOADfrom->B,
> + pLOADfrom->A);
> + pLOADfrom++;
> + }
> + pLOADfrom_sol += logo->plogo_frame_rgba32->linesize[0];
> + pLOADfrom = (RGBA *) pLOADfrom_sol;
> + };
> + };
> +
> + return 0;
> +}
And now a very general question: there is some reason for which the
logo overlaying cannot be accomplished by a filterchain like this:
[IN] overlay [OVER_OUT], movie=logo.png [OVER_IN]
?
That should avoid all the complexity requested by the extraction of
the frame in the logo filter code itself and by the overlay code
duplication itself.
Also it would be more flexible (it may work for example with animated
logo, assuming that the movie filter is able to loop on the input, or
with logo which have to be showed only in a particular interval of
time or with fade-in / fade-out effects).
Alternatively could you suggest some way to factorize out the
overlaying code, we could put it in a file shared by both the overlay
and the logo filter.
Also another solution may be to create a separate filter for
extracting just the first frame of a file in input, this would allow
for scaling operation outside of the logo filter code.
> +
> +static av_cold int init(AVFilterContext * ctx, const char *args,
> + void *opaque)
> +{
> + LogoContext *logo;
> + int num_fields;
> + int arg_R, arg_G, arg_B;
> + logo = ctx->priv;
> +
> + av_log(ctx, AV_LOG_DEBUG, " version %s\n", VF_LOGO_VERSION);
> +
> + logo->filter_ctx = ctx; // remember ptr to AVFilterContext in LogoContext
> +
> + logo->alpha_R = -1; // you can pick a color to be the transparent one
> + logo->alpha_G = -1;
> + logo->alpha_B = -1;
> +
> + if (!args || strlen(args) > 1024) {
> + av_log(ctx, AV_LOG_ERROR, " Invalid arguments!\n");
> + return -1;
> + }
> +
> + num_fields = sscanf(args, "%d:%d:%d:%d:%d:%512[^:]",
> + &arg_R, &arg_G, &arg_B,
> + &logo->x, &logo->y, logo->file_name);
All the parsing may be accomplished using non positional arguments,
av_set_options(), not to mention that the color itself may be given
using the more av_parse_color(). Check libavfilter/parseutils.c.
> + if (num_fields == 6) {
> + logo->alpha_R = arg_R;
> + logo->alpha_G = arg_G;
> + logo->alpha_B = arg_B;
> + av_log(ctx, AV_LOG_INFO,
> + " RGB=(%d,%d,%d) x=%d y=%d file=%s\n",
> + logo->alpha_R, logo->alpha_G, logo->alpha_B,
> + logo->x, logo->y, logo->file_name);
> + if (logo->alpha_R < 0 || logo->alpha_R > 255 ||
> + logo->alpha_G < 0 || logo->alpha_G > 255 ||
> + logo->alpha_B < 0 || logo->alpha_B > 255) {
> + av_log(ctx, AV_LOG_ERROR,
> + " Invalid RGB values! (must be 0-255)\n");
> + return -1;
> + }
> + } else {
> + num_fields = sscanf(args, "%d:%d:%512[^:]",
> + &logo->x, &logo->y, logo->file_name);
> + if (num_fields == 3) {
> + av_log(ctx, AV_LOG_INFO,
> + " x=%d y=%d file=%s\n",
> + logo->x, logo->y, logo->file_name);
> + av_log(ctx, AV_LOG_DEBUG,
> + " RGB=(%d,%d,%d) x=%d y=%d file=%s\n",
> + logo->alpha_R, logo->alpha_G, logo->alpha_B,
> + logo->x, logo->y, logo->file_name);
> + } else {
> + av_log(ctx, AV_LOG_ERROR,
> + " expected 3 or 6 arguments\n\t\t\tlogo={R:G:B:}x:y:filename\n\t\t\toptional R,G,B selects a color to be the transparent one\n\t\t\tlogo_without_alpha=x:y:filename forces overlay of all pixels\n\t\t\tbut wrong args are given: '%s'\n",
> + args);
> + return -1;
> + }
> + }
> +
> + if (!strcmp(ctx->filter->name, "logo_without_alpha")) {
> + logo->alpha_R = logo->alpha_G = logo->alpha_B = 999;
> + av_log(ctx, AV_LOG_INFO, " processing logofile without alpha\n");
> + }
> +
> + if (!
> + (logo->sws =
> + sws_getContext(16, 16, 0, 16, 16, 0, SWS_BICUBIC, NULL, NULL,
> + NULL))) {
> + av_log(ctx, AV_LOG_ERROR, " cannot get SwsContext for swscale\n");
> + return -1;
> + }
> + // load logo image and create rgba32 and video_format frames of logo
> + return load_logo_create_frames(ctx);
> +}
> +
> +static void uninit(AVFilterContext * ctx)
> +{
> +
> + LogoContext *logo;
> +
> + logo = ctx->priv;
> +
> + if (logo->sws != NULL)
> + sws_freeContext(logo->sws);
> +}
> +
> +static AVFilterFormats *make_format_list(LogoContext * logo)
> +{
> + AVFilterFormats *ret;
> + int i;
> +
> + ret = av_mallocz(sizeof(AVFilterFormats));
> + ret->formats = av_malloc(sizeof(int) * PIX_FMT_NB);
> +
> + for (i = 0; i < PIX_FMT_NB; i++) {
> + switch (i) {
> + /* don't support these */
> + case PIX_FMT_YUYV422:
> + case PIX_FMT_MONOBLACK:
> + case PIX_FMT_UYVY422:
> + break;
> + /* support everything else (if named) */
> + default:
> + if (av_pix_fmt_descriptors[i].name)
> + ret->formats[ret->format_count++] = i;
> + }
> + }
> + return ret;
> +}
> +
> +static int query_formats_pixdesc(AVFilterContext * ctx)
> +{
> + avfilter_set_common_formats(ctx, make_format_list(ctx->priv));
> + return 0;
> +}
> +
> +static int config_props_input(AVFilterLink * link)
> +{
> + LogoContext *logo;
> + AVFilterContext *ctx;
> + int i, j, inc_i, inc_j, numBytes, r_0_numBytes, r_1_2_numBytes;
> + RGBA *pRGBA;
> + uint8_t *pRGBA_sol;
> + uint8_t *pRuler;
> + const AVPixFmtDescriptor *pixdesc;
> +
> + logo = link->dst->priv;
> + ctx = logo->filter_ctx; // get AVFilterContext ptr from LogoContext
> + pixdesc = &av_pix_fmt_descriptors[link->format];
> +
> + /* how many bits per pixel in the planes? */
> + memset(logo->bpp, 0, sizeof(logo->bpp));
> + for (i = 0; i < pixdesc->nb_channels; i++) {
> + logo->bpp[pixdesc->comp[i].plane] +=
> + 1 + pixdesc->comp[i].depth_minus1;
> + }
> + /* now in bytes per pixel in the plane */
> + for (i = 0; i < 4; i++) {
> + logo->bpp[i] >>= 3;
> + }
> +
> + logo->hsub = pixdesc->log2_chroma_h;
> + logo->vsub = pixdesc->log2_chroma_w;
> + av_log(ctx, AV_LOG_DEBUG,
> + " pixel info:bpp={%d,%d,%d,%d} hsub=%d vsub=%d\n", logo->bpp[0],
> + logo->bpp[1], logo->bpp[2], logo->bpp[3], logo->hsub,
> + logo->vsub);
> +
> + logo->video_w = link->w;
> + logo->video_h = link->h;
> + logo->video_format = link->format;
> + av_log(ctx, AV_LOG_DEBUG, " video size is %dx%d\tpixfmt:%s\n",
> + logo->video_w, logo->video_h, pixdesc_name(logo->video_format));
> +
> + /* create a copy of logo-frame in video's pixfmt (if it's different)
> + * to prepare a speedy way of overlaying the many video frames
> + * without any duplication, format translation or other expensive things
> + */
> +
> + if (link->format == logo->format) {
> + logo->plogo_frame_video_format = logo->plogo_frame;
> + logo->buffer_logo_frame_video_format = logo->buffer_logo_frame;
> + } else if (link->format != PIX_FMT_RGBA) {
> + // transform it with swscaler from PIX_FMT_RGBA to link->format
> + av_log(ctx, AV_LOG_DEBUG,
> + " transform logo image from RGBA to pix_fmt=%s\n",
> + pixdesc_name(link->format));
> +
> + // Allocate an AVFrame structure
> + logo->plogo_frame_video_format = avcodec_alloc_frame();
> + if (logo->plogo_frame_video_format == NULL) {
> + av_log(ctx, AV_LOG_ERROR,
> + " failed to alloc plogo_frame_video_format\n");
> + return -1;
> + }
> + logo->sws =
> + sws_getCachedContext(logo->sws, logo->w, logo->h, PIX_FMT_RGBA,
> + logo->w, logo->h, link->format,
> + SWS_BICUBIC, NULL, NULL, NULL);
> + if (logo->sws == NULL) {
> + av_log(ctx, AV_LOG_ERROR,
> + " cannot initialize the to-video_format conversion context\n");
> + return -1;
> + }
> + // Determine required buffer size and allocate buffer
> + numBytes = avpicture_get_size(link->format, logo->w, logo->h);
> + logo->buffer_logo_frame_video_format = av_malloc(numBytes);
> + // Assign appropriate parts of buffer to image planes in plogo_frame
> + avpicture_fill((AVPicture *) logo->plogo_frame_video_format,
> + logo->buffer_logo_frame_video_format, link->format,
> + logo->w, logo->h);
> +
> + // transform to video pixel format
> + sws_scale(logo->sws, logo->plogo_frame_rgba32->data,
> + logo->plogo_frame_rgba32->linesize, 0, logo->h,
> + logo->plogo_frame_video_format->data,
> + logo->plogo_frame_video_format->linesize);
> + av_log(ctx, AV_LOG_DEBUG,
> + " logo linesizes [0]=%d [1]=%d [2]=%d [3]=%d in videos pixfmt\n",
> + ((AVPicture *) logo->plogo_frame_video_format)->linesize[0],
> + ((AVPicture *) logo->plogo_frame_video_format)->linesize[1],
> + ((AVPicture *) logo->plogo_frame_video_format)->linesize[2],
> + ((AVPicture *) logo->plogo_frame_video_format)->
> + linesize[3]);
Why not to store the logo image in the original format, then convert
it in this function? This would avoid the unnecessary conversion
operated in load_logo_create_frames():
fmt1 -> RGBA -> fmt2
[...]
Regards.
More information about the FFmpeg-soc
mailing list