vf_logo.c implements avfilters "logo" and "logo_without_alpha"
logo=10:20:logo.jpg overlays the video at x=10 y=20 with the logo (is
respecting the alpha-path of the logo)
logo=-1:1:logo.png overlays the video at 1 pixel from the right and 1
pixel from the top border
logo_without_alpha=10:20:logo.gif overlays the video at 10,20 for all
pixels of the logo, doesn't look for an alpha-path in the logo
logo=0:0:0:10:20:logo.gif overlays video at 10,20 and assumes black
RGB=(0,0,0) is the transparent color in the logo
vf_logo.c is relying on the new pixdesc.c rather than the old pix-fmt.c
and therefor needs pixdesc.o to be included into the libavcodec.a archive.
03_libavcodec_Makefile.diff adds the pixdesc.o to the OBS list in the
libavcodec/Makefile
mfG J.Meissner
Index: allfilters.c
===================================================================
--- allfilters.c (revision 4238)
+++ allfilters.c (working copy)
@@ -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_Makefile.diff
===================================================================
--- diffs/03_libavcodec_Makefile.diff (revision 0)
+++ diffs/03_libavcodec_Makefile.diff (revision 0)
@@ -0,0 +1,12 @@
+Index: libavcodec/Makefile
+===================================================================
+--- libavcodec/Makefile (revision 18682)
++++ libavcodec/Makefile (working copy)
+@@ -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 4238)
+++ doc/vfilters.texi (working copy)
@@ -117,6 +117,47 @@
Flip the video horizontally.
+...@section logo
+
+...@example
+./ffmpeg -i inputvideofile -vfilters logo=10:20:logofile.png -y outputvideofile
+...@end example
+
+ 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
+
+...@example
+./ffmpeg -i inputvideofile -vfilters logo=0:0:0:10:20:logofile.png -y
outputvideofile
+...@end example
+
+ black is the color to be understood as transparent
+
+...@example
+./ffmpeg -i inputvideofile -vfilters logo_without_alpha=10:20:logofile.png -y
outputvideofile
+...@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,859 @@
+#define VF_LOGO_VERSION "0.9.8 26.4.2009"
+/**
+ * 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
+ *
+ * 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
+ *
+ *
+ * ffmpeg -i inputvideofile -vfilters logo=0:0:0:10:20:logofile.png -y
outputvideofile
+ *
+ * black is the color to be understood as transparent
+ *
+ *
+ * ffmpeg -i inputvideofile -vfilters logo_without_alpha=10:20:logofile.png -y
outputvideofile
+ *
+ * force overlaying all pixels (even if no alpha-path)
+ *
+ *
+ * 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
+ *
+ */
+
+/* TODO:
+ *
+ *
+ */
+
+#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;
+ int videoStream;
+ AVCodecContext *pCodecCtx;
+ AVCodec *pCodec;
+ AVFrame *plogo_frame;
+ AVFrame *plogo_frame_rgba32;
+ AVFrame *plogo_frame_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;
+}
+
+static int load_logo_create_frames(AVFilterContext * ctx)
+{
+ LogoContext *logo;
+ int i, j, err, numBytes, frameFinished;
+ uint8_t *pLOADfrom_sol;
+ RGBA *pLOADfrom;
+
+ 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);
+ 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;
+}
+
+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);
+ 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);
+ av_free(&logo->plogo_frame);
+ av_free(&logo->plogo_frame_rgba32);
+ av_free(&logo->plogo_frame_video_format);
+ av_free(&logo->buffer_logo_frame);
+ av_free(&logo->buffer_logo_frame_rgba32);
+ av_free(&logo->buffer_logo_frame_video_format);
+ av_free(&logo->pRuler_0);
+ av_free(&logo->pRuler_1_2);
+}
+
+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]);
+
+ } else {
+ logo->plogo_frame_video_format = logo->plogo_frame_rgba32;
+ logo->buffer_logo_frame_video_format =
+ logo->buffer_logo_frame_rgba32;
+ }
+
+ logo->app_x = logo->x;
+ logo->app_y = logo->y;
+ logo->app_w = logo->w;
+ logo->app_h = logo->h;
+
+ /* if negative, it is based on right/bottom border of logo and video */
+ if (logo->x < 0)
+ logo->app_x = logo->video_w - 1 - logo->w + logo->x;
+ if (logo->y < 0)
+ logo->app_y = logo->video_h - 1 - logo->h + logo->y;
+
+ /* never cross left border or top of video, just shift it */
+ if (logo->app_x < 0)
+ logo->app_x = 0;
+ if (logo->app_y < 0)
+ logo->app_y = 0;
+
+ /* fit overlay into video if overlapping borders */
+ if (logo->app_x > logo->video_w - 1)
+ logo->app_x = logo->video_w - 1;
+ if (logo->app_y > logo->video_h - 1)
+ logo->app_y = logo->video_h - 1;
+ if (logo->app_w > logo->video_w - 1 - logo->app_x)
+ logo->app_w = logo->video_w - 1 - logo->app_x;
+ if (logo->app_h > logo->video_h - 1 - logo->app_y)
+ logo->app_h = logo->video_h - 1 - logo->app_y;
+
+ av_log(ctx, AV_LOG_INFO,
+ " overlay logo of size (%d,%d) at (%d,%d) w=%d h=%d onto video of
size (%d,%d) pixfmt=%s\n",
+ logo->w, logo->h, logo->app_x, logo->app_y, logo->app_w,
+ logo->app_h, logo->video_w, logo->video_h,
+ pixdesc_name(logo->video_format));
+
+ /* get memory for the alpha rulers for plane 0 and plane 1 and 2 */
+ r_0_numBytes = logo->app_w * logo->app_h;
+ logo->pRuler_0 = NULL;
+ logo->pRuler_0 = av_mallocz(r_0_numBytes);
+
+ if (logo->hsub == 0 && logo->vsub == 0) {
+ r_1_2_numBytes = r_0_numBytes;
+ logo->pRuler_1_2 = logo->pRuler_0;
+ } else {
+ r_1_2_numBytes =
+ ((logo->app_w + (1 << logo->hsub) -
+ 1) >> logo->hsub) * ((logo->app_h + (1 << logo->vsub) -
+ 1) >> logo->vsub);
+ logo->pRuler_1_2 = NULL;
+ logo->pRuler_1_2 = av_mallocz(r_1_2_numBytes);
+ }
+
+ if (logo->pRuler_0 == 0 || logo->pRuler_1_2 == 0) {
+ av_log(ctx, AV_LOG_ERROR, " av_mallocz for Rulers failed\n");
+ return -1;
+ }
+
+ /* fill the rulers from RGBA alpha information */
+ /* Ruler_0 */
+ pRuler = logo->pRuler_0;
+ pRGBA_sol = logo->plogo_frame_rgba32->data[0];
+ for (i = 0; i < logo->app_h; i++) {
+ pRGBA = (RGBA *) pRGBA_sol;
+ for (j = 0; j < logo->app_w; j++) {
+ *pRuler = pRGBA->A;
+ av_log(ctx, AV_LOG_DEBUG,
+ " Ruler_0:(%d,%d)=(%3d,%3d,%3d) \talpha=%3d\t%p\n", i,
+ j, pRGBA->R, pRGBA->G, pRGBA->B, *pRuler, pRGBA);
+ pRuler++;
+ pRGBA++;
+ }
+ pRGBA_sol += logo->plogo_frame_rgba32->linesize[0]; // jump to
next start of line
+ }
+
+ /* Ruler_1_2 */
+ if (logo->pRuler_0 != logo->pRuler_1_2) {
+ pRuler = logo->pRuler_1_2;
+ pRGBA_sol = logo->plogo_frame_rgba32->data[0];
+ inc_i = 1 << logo->vsub;
+ inc_j = 1 << logo->hsub;
+ for (i = 0; i < logo->app_h; i += inc_i) {
+ pRGBA = (RGBA *) pRGBA_sol;
+ for (j = 0; j < logo->app_w; j += inc_j) {
+ *pRuler = pRGBA->A;
+ av_log(ctx, AV_LOG_DEBUG,
+ " Ruler_1_2:(%d,%d)=(%3d,%3d,%3d) \talpha=%3d\t%p\n",
+ i, j, pRGBA->R, pRGBA->G, pRGBA->B, *pRuler, pRGBA);
+ pRuler++;
+ pRGBA += inc_j;
+ }
+ pRGBA_sol += inc_i * logo->plogo_frame_rgba32->linesize[0]; //
jump to next start of line
+ }
+ }
+
+ return 0;
+}
+
+static void overlay_one_plane(uint8_t * dst, int dst_wrap,
+ const uint8_t * src, int src_wrap,
+ int bwidth, int bpp, int h, uint8_t * pRuler)
+{
+ int i, j;
+
+ if ((!dst) || (!src))
+ return;
+
+ for (i = 0; i < h; i++) {
+ for (j = 0; j < bwidth; j += bpp) {
+ if (*pRuler != 0)
+ memcpy(dst + j, src + j, bpp);
+ pRuler++;
+ }
+ dst += dst_wrap;
+ src += src_wrap;
+ }
+}
+
+static void overlay_planes(LogoContext * logo,
+ AVPicture * dst, const AVPicture * src,
+ int pix_fmt, int app_w, int app_h)
+{
+ int i, h, bwidth;
+ const AVPixFmtDescriptor *pixdesc;
+ uint8_t *pRuler;
+ AVFilterContext *ctx;
+
+ ctx = logo->filter_ctx; // get AVFilterContext pointer from LogoContext
+ pixdesc = &av_pix_fmt_descriptors[pix_fmt];
+
+ pRuler = logo->pRuler_0;
+
+ switch (pixdesc->flags & PIX_FMT_PAL) {
+
+ case PIX_FMT_PAL:
+ overlay_one_plane(dst->data[0], dst->linesize[0],
+ src->data[0], src->linesize[0],
+ app_w, logo->bpp[0], app_h, pRuler);
+ /* do not copy the palette in data[1]. image must! use same palette */
+ break;
+
+ default:
+ for (i = 0; i < pixdesc->nb_channels; i++) {
+ h = app_h;
+ bwidth = logo->bpp[i] * app_w;
+ if (i == 1 || i == 2) {
+ h = h >> pixdesc->log2_chroma_h;
+ bwidth = bwidth >> pixdesc->log2_chroma_w;
+ pRuler = logo->pRuler_1_2;
+ }
+ overlay_one_plane(dst->data[i], dst->linesize[i],
+ src->data[i], src->linesize[i],
+ bwidth, logo->bpp[i], h, pRuler);
+ }
+ }
+}
+
+static void overlay_with_logo(LogoContext * logo,
+ AVFilterPicRef * dst, int app_x, int app_y,
+ AVPicture * src,
+ int app_w, int app_h, int hsub, int vsub)
+{
+ AVPicture pic;
+ int i;
+
+ /* create AVPicture struct from AVFilterPicRef given by link */
+ memcpy(&pic, &dst->data, sizeof(AVPicture));
+
+ /* let this AVPicture point to target point in dst picture
+ * so we have to loop only around the smaller logo sized rectangle
+ */
+ pic.data[0] += app_x * logo->bpp[0];
+ pic.data[0] += app_y * pic.linesize[0];
+ for (i = 1; i < 4; i++) {
+ if (pic.data[i] == NULL)
+ continue;
+ if (pic.data[i]) {
+ pic.data[i] += (app_x * logo->bpp[i]) >> hsub;
+ pic.data[i] += (app_y >> vsub) * pic.linesize[i];
+ }
+ }
+
+ /* overlay video pic in size w=width h=heigth ruled by RGBA Alpha-path
+ * video pic logo pic video pixfmt width
+ * I I I I
+ * I I I I
height
+ * vvvv vvvvvvvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvvv v
v */
+ overlay_planes(logo, &pic, (AVPicture *) src->data, dst->pic->format,
+ app_w, app_h);
+}
+
+static void start_frame(AVFilterLink * link, AVFilterPicRef * picref)
+{
+ avfilter_start_frame(link->dst->outputs[0], picref);
+}
+
+static void draw_slice(AVFilterLink * link, int y, int h)
+{
+ LogoContext *logo = link->dst->priv;
+ AVFilterPicRef *pic = link->dst->outputs[0]->cur_pic;
+ AVFilterContext *ctx;
+
+ ctx = logo->filter_ctx; // get AVFilterContext pointer from LogoContext
+
+ /* overlay the logo into the output frame */
+ overlay_with_logo(logo, pic,
+ logo->app_x, logo->app_y,
+ (AVPicture *) logo->plogo_frame_video_format,
+ logo->app_w, logo->app_h, logo->hsub, logo->vsub);
+
+ avfilter_draw_slice(link->dst->outputs[0], y, h);
+}
+
+static void end_frame(AVFilterLink * link)
+{
+ AVFilterLink *out = link->dst->outputs[0];
+
+ avfilter_end_frame(out);
+
+ /* unreference the source picture if we're feeding the destination filter
+ * a copied version dues to permission issues */
+ if (link->srcpic) {
+ avfilter_unref_pic(link->srcpic);
+ link->srcpic = NULL;
+ }
+}
+
+AVFilter avfilter_vf_logo_without_alpha = {
+ .name = "logo_without_alpha",
+
+ .init = init,
+ .uninit = uninit,
+
+ .query_formats = query_formats_pixdesc,
+
+ .priv_size = sizeof(LogoContext),
+
+ .inputs = (AVFilterPad[]){{.name = "default",
+ .type = CODEC_TYPE_VIDEO,
+ .config_props = config_props_input,
+ .start_frame = start_frame,
+ .draw_slice = draw_slice,
+ .end_frame = end_frame,
+ .min_perms = AV_PERM_WRITE | AV_PERM_READ,
+ .rej_perms = AV_PERM_REUSE | AV_PERM_REUSE2}
+ ,
+ {.name = NULL}
+ }
+ ,
+ .outputs = (AVFilterPad[]){{.name = "default",
+ .type = CODEC_TYPE_VIDEO,}
+ ,
+ {.name = NULL}
+ }
+ ,
+};
+
+AVFilter avfilter_vf_logo = {
+ .name = "logo",
+
+ .init = init,
+ .uninit = uninit,
+
+ .query_formats = query_formats_pixdesc,
+
+ .priv_size = sizeof(LogoContext),
+
+ .inputs = (AVFilterPad[]){{.name = "default",
+ .type = CODEC_TYPE_VIDEO,
+ .config_props = config_props_input,
+ .start_frame = start_frame,
+ .draw_slice = draw_slice,
+ .end_frame = end_frame,
+ .min_perms = AV_PERM_WRITE | AV_PERM_READ,
+ .rej_perms = AV_PERM_REUSE | AV_PERM_REUSE2}
+ ,
+ {.name = NULL}
+ }
+ ,
+ .outputs = (AVFilterPad[]){{.name = "default",
+ .type = CODEC_TYPE_VIDEO,}
+ ,
+ {.name = NULL}
+ }
+ ,
+};
Property changes on: vf_logo.c
___________________________________________________________________
Added: svn:executable
+ *
Index: Makefile
===================================================================
--- Makefile (revision 4238)
+++ Makefile (working copy)
@@ -17,6 +17,7 @@
OBJS-$(CONFIG_DRAWBOX_FILTER) += vf_drawbox.o
OBJS-$(CONFIG_FPS_FILTER) += vf_fps.o
OBJS-$(CONFIG_HFLIP_FILTER) += vf_hflip.o
+OBJS-$(CONFIG_LOGO_FILTER) += vf_logo.o
OBJS-$(CONFIG_NEGATE_FILTER) += vf_negate.o
OBJS-$(CONFIG_FIFO_FILTER) += vf_fifo.o
OBJS-$(CONFIG_FORMAT_FILTER) += vf_format.o
_______________________________________________
FFmpeg-soc mailing list
[email protected]
https://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-soc